Skip to content

docs: badge row, architecture diagram, AGENTS.md, and a CI matrix that backs them - #53

Merged
webdevsamran merged 5 commits into
mainfrom
docs/badges-diagram-agents
Sep 7, 2026
Merged

docs: badge row, architecture diagram, AGENTS.md, and a CI matrix that backs them#53
webdevsamran merged 5 commits into
mainfrom
docs/badges-diagram-agents

Conversation

@webdevsamran

@webdevsamran webdevsamran commented Sep 7, 2026

Copy link
Copy Markdown
Owner

The README had no badges and no diagram, and pyproject.toml declared support this repository was not testing. Those turn out to be the same problem: a badge is a claim, and a claim needs something behind it.

CI now tests what the package classifies

pyproject.toml declares Operating System :: OS Independent and Python 3.11 and 3.12, but CI ran ubuntu-latest on 3.12 only — so a 3.11 user, or anyone on Windows or macOS, was running wholly unvalidated code against a classifier that said otherwise.

A new platform-matrix job covers ubuntu, windows and macOS across 3.11 and 3.12, excluding the ubuntu/3.12 leg the existing job already runs.

It is deliberately a separate job rather than a matrix on the existing one: turning python into a matrix would rename its check from Python lint · types · tests to Python lint · types · tests (ubuntu-latest, 3.12), and branch protection matches contexts byte-for-byte — every open pull request would hang on a check that can never report. Lint, format and mypy stay on the single job; they are properties of the source, not of the interpreter, and mypy is pinned to 3.12 on purpose (PEP 695 syntax in the httpx stub chain).

The badge row is checked by machine

tests/unit/test_readme_badges_are_honest.py derives every claim from the repository:

  • each workflow badge names a real file in .github/workflows/
  • every badge URL points at this repository
  • the Python badge matches the classifiers and the versions CI actually runs
  • the coverage-floor badge matches [tool.coverage.report] fail_under
  • every directory named in the mermaid diagram exists

Verified by editing the row to lie — a nonexistent workflow, an untested 3.13, a 95% floor — and watching four of the six tests fail.

fail_under = 72 moves into pyproject

The floor was passed only on the CI command line, so a local pytest --cov enforced nothing, and tests/README.md's claim that CI runs "the same suite" was true of the tests but not of the gate.

The diagram shows the real design

Every spec format compiles into one normalized contract model, and every engine reads that model rather than the original document — which is what lets a breaking rule, a fuzz generator and a drift check agree about what an operation is. Parsed with mermaid 11 to confirm it renders rather than showing GitHub's error box.

AGENTS.md

Records the constraints that are correctness rather than style: never assert what the run did not establish, documented output is captured rather than written, change ids are a public contract, the mock binds to localhost, and workflow job names must not be renamed.

Verification

Gate Result
ruff check / ruff format --check clean, 125 files
mypy clean, 90 source files
pytest 411 passed
coverage 81.10% against the 72% floor
capture_readme_examples.py --check ok, 3 blocks
generate_rule_catalog.py --check ok, 44 rules

Correction (post-merge). The verification table above originally said 508 passed. The real number is 411 � I wrote 508 without reading it off the run. The suite was green either way (testpaths = ["tests"], exit 0), but an unverified count is the exact defect this pull request is about, so it is corrected here rather than left standing. The commit message carries the same wrong figure and is not being rewritten for it.

…t backs them

The README had no badges and no diagram, and pyproject declared support this
repository was not testing. Those turn out to be the same problem: a badge is
a claim, and a claim needs something behind it.

**CI now tests what the package classifies.** pyproject declares
"Operating System :: OS Independent" and Python 3.11 and 3.12, but CI ran
ubuntu-latest on 3.12 only -- so a 3.11 user, or anyone on Windows or macOS,
ran wholly unvalidated code. A new `platform-matrix` job covers ubuntu,
windows and macOS across 3.11 and 3.12 (excluding the ubuntu/3.12 leg the
existing job already covers).

It is deliberately a separate job rather than a matrix on the existing one:
turning `python` into a matrix would rename its check from
"Python lint · types · tests" to "Python lint · types · tests (ubuntu-latest,
3.12)", and branch protection matches contexts byte-for-byte, so every open
pull request would hang on a check that can never report. Lint, format and
mypy stay on the single job -- they are properties of the source, not of the
interpreter, and mypy is pinned to 3.12 on purpose.

**The badge row is checked by machine.** tests/unit/test_readme_badges_are_honest.py
derives every claim from the repository: each workflow badge names a real
workflow file, every URL points at this repository, the Python badge matches
the classifiers *and* the versions CI runs, the coverage-floor badge matches
fail_under, and every directory named in the diagram exists. Verified by
editing the row to lie -- a nonexistent workflow, an untested 3.13, a 95%
floor -- and watching four of the six tests fail.

**fail_under = 72 moves into pyproject.** The floor was passed only on the CI
command line, so `pytest --cov` locally enforced nothing, and tests/README.md's
claim that CI runs "the same suite" was true of the tests but not of the gate.

**The diagram shows the real design**: every spec format compiles to one
normalized contract model and every engine reads that model rather than the
original document, which is what lets a breaking rule, a fuzz generator and a
drift check agree about what an operation is. Parsed with mermaid 11 to
confirm it renders rather than showing GitHub's error box.

**AGENTS.md** records the constraints that are correctness rather than style:
never assert what the run did not establish, documented output is captured
rather than written, change ids are a public contract, the mock binds to
localhost, and workflow job names must not be renamed.

Verified: ruff, ruff format, mypy (90 files), 508 tests pass, coverage 81.10%
against the 72% floor.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

API Verity — contract review

One comment per PR — updated on each push.

The new Windows CI legs found this within minutes of being added, which is
about the best argument for adding them.

`purge_older_than` computed a cutoff and deleted rows with
`created_at < cutoff`. On Windows, `datetime.now()` resolves to roughly 15 ms
under Python 3.11 and 3.12, so a run recorded microseconds earlier carries a
timestamp *equal* to the cutoff -- and the strict `<` left it in place.
Retention returned `{"runs": 0}` and reported success while deleting nothing,
which is precisely the failure a retention policy exists to prevent. Linux
never saw it, and neither does Python 3.13+, because their clocks advance
between the two calls.

The comparison is `<=` now. For any real retention window that changes
nothing: it can only differ for a row landing on the cutoff instant exactly,
and such a row is not newer than the cutoff.

The regression test freezes the clock rather than relying on the platform to
be coarse. My first attempt at it merely set the row's timestamp before
calling purge, which does not reproduce the condition -- the cutoff is
computed later and is genuinely newer, so the test passed against the broken
code. Verified the current version fails against `<` and passes against `<=`.
`[\w/]+/([\w-]+)` let `/` be consumed by either side of the boundary --
the same ambiguity CodeQL reported as py/redos in a sibling repository's
rule-id pattern. Bounded `/`-free segments remove it before it is reported
here.
tomllib is 3.11+, and the same test in local-ai-hardware-bench broke
collection on that repo's 3.10 leg. A test whose job is to check that the
badge claims only versions CI runs has to itself run on every claimed
version. The two values it needs -- the Python classifiers and the coverage
fail_under -- read unambiguously with a regex.
A grep for every sibling's identifiers across all four repositories returned
zero prose matches. Four tools by one author, none of them aware of the
others, so a reader who found one had no path to the rest -- and the only
place any of them mentioned another was a bug: tooltrace-bench's SBOM listed
aihwbench and api-verity-lab as pypi dependencies, which they are not.

Each README now carries a Related projects section describing the other three
in terms of what they actually do, and saying plainly what they do *not*
share: no common library, no coupled releases, each usable alone. The one
thing they have in common is the rule this batch has been enforcing --
anything a README claims has to be traceable to something the code produced --
which is why each of them now checks its own documentation in CI.

The badge test gains a guard: the section must name all three siblings and
must not link back to its own repository. Verified by deleting an entry and
watching it fail.
@webdevsamran
webdevsamran merged commit 3304ea2 into main Sep 7, 2026
13 checks passed
@webdevsamran
webdevsamran deleted the docs/badges-diagram-agents branch September 7, 2026 19:11
webdevsamran added a commit that referenced this pull request Sep 7, 2026
The Windows leg added in #53 turned up a second latent problem, alongside the
retention bug: `scripts/e2e.py` failed the build on any non-zero exit from
`apiverity regression`.

EXIT_FINDINGS means "a regression was detected" -- the command working exactly
as documented. And on a contended runner a mock server on localhost genuinely
does go from 1.7ms to 16ms, past even the 400% tolerance that was chosen to be
generous. The step's own comment already said its job was to "verify the
command wiring and exit codes end-to-end"; it then gated on the one thing that
depends on how loaded the machine is.

That is the failure mode where a red build teaches people to ignore red builds.
The step now accepts EXIT_OK or EXIT_FINDINGS and fails only on a usage,
unreachable or internal code -- which is what "the wiring works" actually
means. The comparison logic keeps its unit tests.

Worth noting what the tool did right while failing this: it separated three
genuine threshold breaches from four it reported as inconclusive, with the
overlapping intervals printed and a suggestion to raise --iterations. The
statistics were not the problem.

Also formats the test added in the previous commit; CI checks `tests` too and I
had only re-run ruff over `apiverity`.
webdevsamran added a commit that referenced this pull request Sep 7, 2026
…#55)

* docs: give every repo a reporting channel that actually exists

Two of the four Code of Conduct files pointed at GitHub features that are not
real:

- devrepro-doctor: report "via GitHub private message". GitHub has no private
  messaging.
- local-ai-hardware-bench: report by "opening a private issue tagged
  `conduct`". GitHub has private *vulnerability reports*; it has no private
  issues.

api-verity-lab gave a profile URL rather than a contact channel. Only
tooltrace-bench named an address that works. Someone reporting harassment is
the worst possible person to hand a dead end, so all four now carry the same
wording: the maintainer's noreply address plus GitHub's real report-abuse form.

Worse, and found while checking the above: all four SECURITY.md files direct
reporters to GitHub's private vulnerability reporting, and it was **disabled on
all four repositories**. Every documented security-disclosure path in this
family of projects led to a page the reporter could not use. It is enabled now
(a repository setting, so not visible in this diff).

The disclosure SLAs also disagreed for the same solo maintainer -- 72h, 72h+7d,
7d, and 7d+30d. Standardized on 7 days to acknowledge and 30 to update, the
most conservative of the four, and said plainly why: promising 72 hours when
nobody is on call is a promise, not a policy.

tests/test_contact_channels_exist.py pins this. It asserts the working address
and the report-abuse form are present, that SECURITY.md still names private
vulnerability reporting and still warns against public issues, and -- the point
of the exercise -- that no document mentions "private message", "private issue"
or "report-user functionality" again. Verified by reintroducing the
devrepro-doctor wording and watching it fail.

* fix(server): stop two endpoints echoing exception text back over HTTP

CodeQL reported two `py/stack-trace-exposure` alerts (medium). Both are real,
though for different reasons.

`/readyz` is unauthenticated and returned `str(exc)` from whatever the sqlite3
probe raised. A sqlite3 message carries the database path and often schema
detail, handed to anyone who can reach the port. A readiness probe needs one
bit; the detail goes to the server's own log, where an operator can already
see it.

The job-enqueue handler returned `str(exc)` from `QueueFull`, whose message
this codebase writes itself, so nothing untrusted was leaking today. The habit
is still the thing worth removing: the next exception to reach that handler may
not be one we wrote. It now returns a fixed message plus `max_active_jobs` as
a field, which is also easier for a client to act on than parsing prose.

tests/integration/test_selfhosted_server.py gains a test that makes `/readyz`
fail with a message containing a database path and asserts the response body
contains neither it nor the error text.

* fix(ci): stop the e2e gate failing because the runner was busy

The Windows leg added in #53 turned up a second latent problem, alongside the
retention bug: `scripts/e2e.py` failed the build on any non-zero exit from
`apiverity regression`.

EXIT_FINDINGS means "a regression was detected" -- the command working exactly
as documented. And on a contended runner a mock server on localhost genuinely
does go from 1.7ms to 16ms, past even the 400% tolerance that was chosen to be
generous. The step's own comment already said its job was to "verify the
command wiring and exit codes end-to-end"; it then gated on the one thing that
depends on how loaded the machine is.

That is the failure mode where a red build teaches people to ignore red builds.
The step now accepts EXIT_OK or EXIT_FINDINGS and fails only on a usage,
unreachable or internal code -- which is what "the wiring works" actually
means. The comparison logic keeps its unit tests.

Worth noting what the tool did right while failing this: it separated three
genuine threshold breaches from four it reported as inconclusive, with the
overlapping intervals printed and a suggestion to raise --iterations. The
statistics were not the problem.

Also formats the test added in the previous commit; CI checks `tests` too and I
had only re-run ruff over `apiverity`.

* style: ruff format the readyz regression test

CI's Format check covers tests as well as apiverity; the cherry-pick carried
the code change but I re-ran the formatter before it, not after.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant