Skip to content

test: extend TestErrorDisplaySync to build_site.py and AGENTS.md mirrors - #951

Merged
aallan merged 3 commits into
aallan:mainfrom
chethanuk:fix/issue-829-errordisplay-mirrors
Jul 8, 2026
Merged

test: extend TestErrorDisplaySync to build_site.py and AGENTS.md mirrors#951
aallan merged 3 commits into
aallan:mainfrom
chethanuk:fix/issue-829-errordisplay-mirrors

Conversation

@chethanuk

@chethanuk chethanuk commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Extends TestErrorDisplaySync (tests/test_errors.py) to guard the two E001 (missing_contract_block) spec-ref mirrors that were previously unguarded. The canonical E001 diagnostic is duplicated across six places; four were already test-guarded (README.md, docs/index.html, spec/00-introduction.md, plus the canonical vera/errors.py), but scripts/build_site.py and AGENTS.md were not — which is how the §5.4 → §5.2 spec_ref repoint in #826 drifted those two copies before review caught it.

Two extractors and eight assertions close the gap:

  • _extract_build_site_error_block — reads the E001 example that scripts/build_site.py renders into docs/index.md and un-doubles the f-string's literal braces, then asserts error_code / description / rationale / fix / spec_ref / header format (6 assertions). The extractor anchors on the closing code fence rather than the spec_ref text, so a future spec_ref drift surfaces the precise field mismatch instead of an opaque "block not found".
  • _extract_agents_diagnostic — parses each json fenced block in AGENTS.md, finds the diagnostic whose error_code == "E001", and asserts error_code + spec_ref (the two fields that appear verbatim there; description/rationale are ellipsis-truncated). Assertions use dict.get() so a missing field still shows the helpful message.

Test-only. No drift exists today, so this is a regression guard, not a fix: a future E001 change that misses a mirror now fails a test rather than shipping.

Related Issues

Closes #829

Type of Change

  • Tests

How verified

python -m pytest tests/test_errors.py::TestErrorDisplaySync -q26 passed (18 → 26; +8 new drift-guard assertions). Touches only tests/, so the CHANGELOG gate does not apply.

Checklist

  • I have read CONTRIBUTING.md
  • My changes follow the project's coding standards
  • I have added/updated tests as appropriate
  • I have updated relevant documentation
  • All tests pass locally

Summary by CodeRabbit

  • Tests
    • Expanded canonical E001 diagnostic validation to cross-check additional rendered and documented outputs, including both formatted text and JSON variants.
    • Added assertions for key fields (error code, description, rationale, fix, and spec reference) and for the expected header formatting.
  • Documentation
    • Updated project status and test metrics across README, FAQ, HISTORY, ROADMAP, and TESTING to reflect the latest totals (6,789).
    • Refreshed the TESTING “Test Files” coverage notes for expanded error-display synchronisation.

Guard the two previously unguarded E001 spec_ref mirrors against drift:
scripts/build_site.py (rendered plaintext block, braces un-doubled) and
AGENTS.md (embedded --json example). The build_site extractor anchors on the
closing code fence rather than the spec_ref text so a drift surfaces the
precise field mismatch, and AGENTS.md assertions use dict.get() for clear
messages. Test-only; 18 -> 26 tests pass.
@chethanuk
chethanuk requested a review from aallan as a code owner July 6, 2026 11:20
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6b754d60-64af-441a-9789-a5a296558796

📥 Commits

Reviewing files that changed from the base of the PR and between 471ff1f and 57a7e06.

⛔ Files ignored due to path filters (1)
  • docs/llms-full.txt is excluded by !docs/**
📒 Files selected for processing (6)
  • FAQ.md
  • HISTORY.md
  • README.md
  • ROADMAP.md
  • TESTING.md
  • tests/test_errors.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • aallan/vera-bench (manual)

📝 Walkthrough

Walkthrough

This PR expands E001 synchronisation tests to cover scripts/build_site.py and AGENTS.md, and updates project status documents to report the newer test totals and revised E001 roadmap note.

Changes

E001 mirror test coverage

Layer / File(s) Summary
E001 mirror extraction and assertions
tests/test_errors.py
Adds helpers for extracting the generated E001 block and the AGENTS.md JSON example, then extends TestErrorDisplaySync to compare the canonical diagnostic fields against both mirrors.

Project status metric updates

Layer / File(s) Summary
Project status metric updates
README.md, FAQ.md, ROADMAP.md, HISTORY.md, TESTING.md
Updates the reported test-count figures and the ROADMAP.md E001 tracking note across the documentation set.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

Possibly related PRs

  • aallan/vera#327: Introduces scripts/build_site.py, which is one of the E001 mirrors now validated by tests/test_errors.py.
  • aallan/vera#526: Affects the generated docs/index.md path that the new build_site.py extraction logic is checking for E001 drift.

Suggested labels: tests, docs

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly states the main test coverage expansion and names both newly covered mirrors.
Linked Issues check ✅ Passed The PR adds TestErrorDisplaySync coverage for both AGENTS.md and scripts/build_site.py as requested by #829.
Out of Scope Changes check ✅ Passed The ancillary documentation metric updates appear consistent with the added test count and do not show unrelated functional changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Changelog Covers Public-Surface Changes ✅ Passed PASS: the diff only touches docs/tests; no vera/cli.py, vera/errors.py, spec/, vera/lsp/, or vera/codegen/api.py changed, and CHANGELOG.md is untouched.
Spec And Implementation Move Together ✅ Passed No files under vera/ or spec/ changed; the PR only updates tests and documentation metrics, so there is no implementation/spec drift.
Diagnostics Carry An Error Code ✅ Passed PR only adds tests/docs; no diagnostic definitions changed. The canonical E001 constructor already carries stable code E001, and W001 is stable too.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.29%. Comparing base (e87a996) to head (57a7e06).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #951   +/-   ##
=======================================
  Coverage   93.29%   93.29%           
=======================================
  Files          96       96           
  Lines       31440    31440           
  Branches      456      456           
=======================================
  Hits        29331    29331           
  Misses       2096     2096           
  Partials       13       13           
Flag Coverage Δ
javascript 78.41% <ø> (ø)
python 95.08% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

`check_doc_counts.py` gates the total test count and the per-file TESTING.md
row, so adding 8 tests to test_errors.py requires the counts move in lockstep:

  - TESTING.md: total 6,779 -> 6,787 (6,690 -> 6,698 passed); the
    test_errors.py row 52 -> 60 tests, 525 -> 621 lines, and its description
    now names the two newly-guarded mirrors (AGENTS.md + build_site.py).
  - README.md / ROADMAP.md / FAQ.md / HISTORY.md: 6,779 -> 6,787.
  - docs/llms-full.txt: regenerated via scripts/build_site.py.

Maintainer-applied so the CI doc-count gate passes. No change to the
contributor's test code.

Co-Authored-By: Claude <noreply@anthropic.invalid>
Applied after an adversarial review of this PR.

1. AGENTS.md's `fix` deserialises byte-identically to `canonical.fix`, but
   nothing compared it. Mutation-proven: drifting AGENTS.md's `fix` left the
   suite GREEN (60 passed), while the same drift in the CANONICAL text reddened
   the README / HTML / spec / build_site guards -- i.e. AGENTS.md was the one
   mirror that could go stale while every other was updated in lockstep. That is
   precisely the aallan#829 drift class this PR exists to close. Adds
   `test_agents_has_fix`.

   The comment justifying the omission ('the other fields are JSON-escaped, so
   only error_code and spec_ref appear verbatim') was false: the extractor runs
   `json.loads`, which resolves the escaping, so every field is comparable.
   Replaced with an accurate one.

2. `description` / `rationale` are ellipsis-truncated in the example, so they
   are now prefix-compared (`test_agents_truncated_fields_are_prefixes`),
   bringing AGENTS.md to parity with the other four mirrors.

3. Refreshes the stale `TestErrorDisplaySync` docstring (it named 3 of 5 mirrors).

Both new guards are mutation-validated: drifting AGENTS.md's `fix` or
`description` turns each RED; reverting returns 62/62 green.

Maintainer-side, in the same commit:
  - ROADMAP.md: aallan#829's row removed (this PR closes it, per CLAUDE.md). Its
    untracked 'deeper option' -- single-source the example so nothing can drift
    -- is captured as aallan#954 and carried forward as a new row.
  - TESTING.md: the test_errors.py row now enumerates the five mirrors instead of
    asserting a contested count; counts reconciled 6,787 -> 6,789.

Co-Authored-By: Claude <noreply@anthropic.invalid>
@aallan

aallan commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Thank you for this — and welcome. It's a genuinely well-made first contribution, and it closes #829 properly.

Because this touches the project's own drift guards, I put it through an adversarial review: several independent passes over the diff, each finding then attacked by separate reviewers instructed to refute it by default, plus a mutation pass in an isolated worktree that actually broke each mirror to check the new tests notice.

The tests are not vacuous — that was verified, not assumed

CLAUDE.md's bar is "a test that is green both before and after the change proves nothing." Your tests clear it. Repointing the canonical spec_ref turns both new mirrors RED alongside the three existing ones, and deleting or corrupting either source fails loudly (assert match / raise AssertionError) rather than silently returning a degenerate value. That last property is the one most sync tests get wrong.

Some things I want to call out specifically, because they're the reasoning this project asks for and rarely gets:

  • You anticipated the self-defeating guard. Anchoring _extract_build_site_error_block() on the closing code fence rather than the spec_ref text — with the comment explaining that anchoring on spec_ref would make extraction fail opaquely and hide the very mismatch test_build_site_has_spec_ref exists to surface. I verified this: a spec_ref drift now produces a precise Expected/Got diff, not a "block not found."
  • _extract_agents_diagnostic() keys on the parsed payload, not a raw substring — more robust than the three older extractors sitting next to it. For contrast, our own _extract_readme_error_block silently returns '' on a heading drift; both of yours raise with the filename.
  • Scope discipline: +96 lines, tests/ only, no compiler surface.

One real gap the mutation pass found

AGENTS.md's fix was unguarded, and the comment explaining why was incorrect.

The comment read "the other fields are JSON-escaped, so only error_code and spec_ref appear verbatim." But _extract_agents_diagnostic() calls json.loads, which resolves the escaping — every field is directly comparable afterwards. (spec_ref is itself escaped in the raw markdown, yet test_agents_has_spec_ref compares it fine, so the reasoning doesn't hold under either reading.) Measured against HEAD, fix deserialises byte-identically to canonical.fix.

Mutation-proven:

Drift AGENTS.md's fix  ('>= 0' -> '>= 999'):   60 passed   <-- GREEN, undetected
Drift the CANONICAL fix ('>= 0' -> '>= 1'):
    FAILED test_readme_has_fix
    FAILED test_html_has_fix
    FAILED test_spec_has_fix
    FAILED test_build_site_has_fix     <-- yours, working
    (no test_agents_has_fix existed)

So AGENTS.md was the one mirror that could go stale while every other was updated in lockstep — exactly the #829 drift class. The wrong comment was the more harmful half: it's the artifact that would have stopped the next person from closing the gap.

Note this is a gap in an area you added coverage to — you guarded fix for build_site.py correctly.

What I pushed to your branch

Maintainer-edit was enabled, so rather than send you round again I pushed two commits:

471ff1f — doc-count lockstep. check_doc_counts.py gates the total test count and the per-file TESTING.md row, so adding tests requires the counts move together. Nothing to do with your code; it's a gate that's easy to miss from outside.

57a7e06 — the review fixes:

  • test_agents_has_fix (exact comparison), and the comment replaced with an accurate one.
  • test_agents_truncated_fields_are_prefixesdescription / rationale are ellipsis-truncated in the example, so they're prefix-compared. This brings AGENTS.md to parity with the other four mirrors.
  • Refreshed the TestErrorDisplaySync docstring (it still named 3 of the now-5 mirrors).
  • ROADMAP.md: removed Extend TestErrorDisplaySync to cover all E001 spec_ref mirrors (AGENTS.md + the build_site.py generator) #829's row, since this PR closes it.
  • TESTING.md: reworded the row (see below).

Both new guards are mutation-validated the same way yours were: drifting AGENTS.md's fix or description turns each RED; reverting returns 62/62 green.

Two notes in fairness

The review caught my mistake, not just yours. In 471ff1f I described the suite as covering "all six E001 spec_ref mirrors" — wrong: #829 says "six places — the diagnostic itself plus five mirrors." The row now enumerates the five instead of asserting a count. I also nearly "corrected" a HISTORY.md snapshot column that turned out to follow a long-standing house convention. Fresh eyes cut both ways.

Not blocking, and not on you: _extract_build_site_error_block() matches the first [E001] in the whole file. There's exactly one today, so it guards the right block and has teeth. A second, earlier example would silently redirect it — but our own pre-existing _extract_html_error_block has the identical shape, so you simply followed house style. Worth hardening both, or neither.

Your ROADMAP row also carried an untracked idea — "single-source the example so nothing can drift" — which I didn't want to delete silently. It's now #954, and the ROADMAP row points there. That's the structural version of what you've built the detection for.

CI is green. Merging. Thanks again — this is exactly the kind of contribution that makes the guardrails worth having, and I hope you'll send more.

@aallan
aallan merged commit 4cd140d into aallan:main Jul 8, 2026
26 checks passed
aallan added a commit to chethanuk/vera that referenced this pull request Jul 8, 2026
Maintainer-applied so the PR is mergeable; no change to the gate's behaviour.

- Merged origin/main first (this branch predated aallan#951), so the doc-count
  baseline is the post-aallan#951 6,789 rather than 6,779.
- check_doc_counts.py gates the total test count and the per-file TESTING.md
  row: 6,789 -> 6,797 (+8); test_diagnostic_fields.py 41 -> 49 tests,
  479 -> 622 lines. Its row now describes the aallan#827 narrowing. README /
  ROADMAP / FAQ / HISTORY totals and docs/llms-full.txt regenerated.
- ROADMAP.md: aallan#827's row removed (this PR closes it, per CLAUDE.md).
- Three comments referenced the contributor's own fork PR and third-party
  review bots ('PR #2 review, gemini + greptile', 'greptile P2',
  'PR #2 review (gemini/greptile)'). Those are meaningless upstream, and a
  bare '#2' invites confusion with this repo's own issue #2. Removed the
  provenance, kept every word of the technical reasoning.

Co-Authored-By: Claude <noreply@anthropic.invalid>
aallan added a commit to chethanuk/vera that referenced this pull request Jul 8, 2026
… and aallan#827 under [Unreleased]

check_changelog_updated.py only BLOCKS a PR touching vera/ or spec/ —
tests/, scripts/, docs/ sit in EXEMPT_PREFIXES. That is a merge gate, not a
statement of what merits an entry: every comparable PR carries its own bullet
(aallan#420 / aallan#419 / aallan#839 are pure tests/ splits; aallan#682 and aallan#645 are scripts/+tests/
gates). [Unreleased] is also the section the next release's notes are extracted
from at tag time, so a PR that closes an issue but leaves it empty vanishes from
the release record.

aallan#951 (Closes aallan#829) merged with an empty [Unreleased]; its bullet is added here
retroactively rather than in a separate PR. aallan#952's own bullet (Closes aallan#827)
lands with it. aallan#953 already carries one, since it touches vera/smt.py and the
gate required it.

Both credit @chethanuk, the external contributor.

Co-Authored-By: Claude <noreply@anthropic.invalid>
aallan pushed a commit that referenced this pull request Jul 8, 2026
…own-scope ctor (#952)

Narrows the diagnostic-fields gate's plumbing-skip, which exempted every Diagnostic(...) lexically inside any function *named* `_error` / `_warning`. A stray second ctor in the same helper, or a module-level look-alike, escaped all three passes (field presence, spec_ref validity, error_code registration).

A ctor is now skipped only when its enclosing function is a genuine helper method -- a direct class member with a `self` receiver, not a @staticmethod, @classmethod, module-level or nested look-alike -- and it is that method's sole own-scope construction. Own-scope means the body: decorators, parameter defaults and annotations evaluate in the enclosing scope and are always inspected. A helper holding two constructions is ambiguous; neither is skipped. Every exemption therefore lies inside a function span the old rule already exempted, so the exempt set can only shrink and the change cannot open an escape; on vera/ it resolves to the same five plumbing ctors as before.

Adversarial review drove four further changes. Counting every own-scope ctor rather than only the return/append one closes a residual escape where a helper's real ctor is bound to a local and a stray direct ctor wins the election, and removes a false positive on an ordinary hoist-to-a-local refactor. Seeding the own-scope walk from `fn.body` rather than `ast.iter_child_nodes(fn)` closes an escape introduced by the first of those fixes, where a Diagnostic in a decorator argument could be elected as the helper's plumbing -- caught by neither the whole-vera/ exempt-set differential (no real helper is decorated) nor any test. The three consumers of the skip are now individually mutation-pinned: two non-equivalent mutants previously survived the whole suite. And the two conjuncts of `_is_helper_method` are pinned separately, after a mutant that broke both at once reported "killed" while the `self`-receiver check went unexercised.

Fifteen mutants of the rule, all RED, each by a named test.

Retroactive CHANGELOG entry for #829 (merged in #951 without one) rides along. Two pre-existing gate defects surfaced by the review are filed rather than fixed here: #955 (the `# diag-fields-exempt` opt-out is honoured by one of four sites) and #956 (the skip never checks its own premise, so a delegating helper's hardcoded fields go unvalidated).

Closes #827

Co-Authored-By: Claude <noreply@anthropic.invalid>
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.

Extend TestErrorDisplaySync to cover all E001 spec_ref mirrors (AGENTS.md + the build_site.py generator)

2 participants