test: extend TestErrorDisplaySync to build_site.py and AGENTS.md mirrors - #951
Conversation
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.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (6)
🔗 Linked repositories identifiedCodeRabbit considers these linked repositories for cross-repo context during reviews:
📝 WalkthroughWalkthroughThis PR expands E001 synchronisation tests to cover ChangesE001 mirror test coverage
Project status metric updates
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related issues
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 8✅ Passed checks (8 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
`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>
|
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 assumedCLAUDE.md's bar is "a test that is green both before and after the change proves nothing." Your tests clear it. Repointing the canonical Some things I want to call out specifically, because they're the reasoning this project asks for and rarely gets:
One real gap the mutation pass foundAGENTS.md's The comment read "the other fields are JSON-escaped, so only Mutation-proven: 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 What I pushed to your branchMaintainer-edit was enabled, so rather than send you round again I pushed two commits:
Both new guards are mutation-validated the same way yours were: drifting AGENTS.md's Two notes in fairnessThe review caught my mistake, not just yours. In Not blocking, and not on you: 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. |
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>
… 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>
…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>
Summary
Extends
TestErrorDisplaySync(tests/test_errors.py) to guard the two E001 (missing_contract_block) spec-ref mirrors that were previously unguarded. The canonicalE001diagnostic is duplicated across six places; four were already test-guarded (README.md,docs/index.html,spec/00-introduction.md, plus the canonicalvera/errors.py), butscripts/build_site.pyandAGENTS.mdwere not — which is how the §5.4 → §5.2spec_refrepoint 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 thatscripts/build_site.pyrenders intodocs/index.mdand un-doubles the f-string's literal braces, then assertserror_code/description/rationale/fix/spec_ref/ header format (6 assertions). The extractor anchors on the closing code fence rather than thespec_reftext, so a futurespec_refdrift surfaces the precise field mismatch instead of an opaque "block not found"._extract_agents_diagnostic— parses eachjsonfenced block inAGENTS.md, finds the diagnostic whoseerror_code == "E001", and assertserror_code+spec_ref(the two fields that appear verbatim there; description/rationale are ellipsis-truncated). Assertions usedict.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
E001change that misses a mirror now fails a test rather than shipping.Related Issues
Closes #829
Type of Change
How verified
python -m pytest tests/test_errors.py::TestErrorDisplaySync -q→ 26 passed (18 → 26; +8 new drift-guard assertions). Touches onlytests/, so the CHANGELOG gate does not apply.Checklist
Summary by CodeRabbit