Skip to content

fix(tooling): honour diag-fields-exempt for unresolvable fields in all 3 passes - #965

Merged
aallan merged 3 commits into
aallan:mainfrom
chethanuk:fix/issue-955-diag-exempt-three-passes
Jul 9, 2026
Merged

fix(tooling): honour diag-fields-exempt for unresolvable fields in all 3 passes#965
aallan merged 3 commits into
aallan:mainfrom
chethanuk:fix/issue-955-diag-exempt-three-passes

Conversation

@chethanuk

@chethanuk chethanuk commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Fixes #955

Depends on #956 (#964) — stacked, will show a combined diff until #956 merges. This branch is cut from the tip of `fix/issue-956-plumbing-skip-reachability`, since #955's diff assumes #956's reachability predicate already exists.

Summary

  • The field-presence pass (`check_source`) appended its non-literal-severity violation and `continue`d before its own opt-out lookup ran, so a `# diag-fields-exempt` marker on that exact call was never consulted even in the one pass that otherwise honours it.
  • The `spec_ref`-validity and `error_code`-registration passes never consulted the opt-out at all.
  • Fixed for the unresolvable (non-literal) sub-case only: a non-literal severity or spec_ref can't be checked statically and so is opt-out-able, same as a missing field.
  • A `spec_ref` that resolves but cites the wrong/nonexistent section, or an `error_code` not in `ERROR_CODES`, is a content error, not a tagging gap — the opt-out never suppresses either, marker or not (rejected the blanket-honour alternative: it would let a one-line comment ship a diagnostic citing a nonexistent spec section).
  • Convention census: on `vera/`, all 3 existing `# diag-fields-exempt` markers are missing-field cases — none waive a content-wrong value, so this change doesn't affect any real site.
  • `main()`'s remedy text now states precisely what the opt-out does and doesn't waive.

Test plan

  • New `TestOptOutHonouredAcrossAllThreePasses` class (5 tests): each run RED against pre-fix code first, confirmed failing for the expected reason, then GREEN after the fix.
  • Mutation-kill: hand-applied the rejected blanket-honour (Option 1) patch, confirmed the content-wrong test goes RED, restored.
  • `python scripts/check_diagnostic_fields.py` exits 0 on `vera/` — exempt set unaffected.
  • `pytest tests/test_diagnostic_fields.py -v` — all passing.
  • `mypy vera/` clean.
  • Full `pre-commit run --all-files` green, including doc-count sync.

Summary by CodeRabbit

  • Bug Fixes
    • Corrected how # diag-fields-exempt is applied for Diagnostic(...) checks: it now reliably suppresses only genuinely unresolvable, non-literal severity/spec_ref issues, while still reporting incorrect spec_ref content and unregistered error_code.
  • Tests
    • Added coverage to ensure the opt-out is honoured consistently across all relevant validation passes, including multi-line spec_ref call spans and empty opt-out reasons.
  • Documentation
    • Updated changelog, roadmap, and testing guidance to reflect the clarified validation rules and refreshed test-suite totals.

@chethanuk
chethanuk requested a review from aallan as a code owner July 9, 2026 02:49
@coderabbitai

coderabbitai Bot commented Jul 9, 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: 366b0cce-47bc-4113-ace0-2104156e5059

📥 Commits

Reviewing files that changed from the base of the PR and between 5ef1b03 and 00d6808.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • CLAUDE.md
  • ROADMAP.md
  • TESTING.md
  • scripts/check_diagnostic_fields.py
  • tests/test_diagnostic_fields.py
🔗 Linked repositories identified

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

  • aallan/vera-bench (manual)

📝 Walkthrough

Walkthrough

This PR tightens the diagnostic-fields validation script so helper-owned Diagnostic(...) constructors are only exempt when reachable as the helper result, and makes # diag-fields-exempt apply consistently across the validation passes for unresolvable/non-literal cases. Documentation and test metrics are updated.

Changes

Diagnostic-fields gate fixes

Layer / File(s) Summary
Plumbing-skip reachability and tests
scripts/check_diagnostic_fields.py, tests/test_diagnostic_fields.py
Requires the sole own-scope Diagnostic(...) constructor to be reachable as the helper’s result before skipping it, and adds regression tests for dispatched, appended, reassigned, and rebound cases.
Opt-out handling across validation passes
scripts/check_diagnostic_fields.py, tests/test_diagnostic_fields.py
Defers non-literal severity emission until after opt-out lookup, extends spec_ref handling with call-span data, applies opt-out consistently to unresolvable spec_ref cases, updates CLI wording, and adds cross-pass regression tests.
Changelog, roadmap, and testing updates
CHANGELOG.md, ROADMAP.md, TESTING.md, CLAUDE.md
Records the fixes in the changelog, updates roadmap test counts and resolved issue entries, and revises the testing and command documentation counts and descriptions.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • aallan/vera#826: Shares the same diagnostic-field gate and test coverage area, with this PR refining the earlier enforcement.

Suggested labels: tests, ci, docs

🚥 Pre-merge checks | ✅ 7 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely states the main tooling change: honouring diag-fields-exempt for unresolvable fields across all three passes.
Linked Issues check ✅ Passed Issue #955's opt-out semantics are now consistent across the three passes and the report, with tests covering the chosen behaviour.
Out of Scope Changes check ✅ Passed All doc, roadmap, and test updates directly support the diagnostic-fields gate fix; no unrelated code paths were introduced.
Changelog Covers Public-Surface Changes ✅ Passed The diff only changes docs, scripts, CI, and tests; none of the listed public-surface paths changed, so no extra changelog coverage was required.
Spec And Implementation Move Together ✅ Passed No files under vera/ or spec/ changed; the diff is limited to docs, scripts, tests and workflows, so there’s no spec/implementation drift here.
Diagnostics Carry An Error Code ✅ Passed The PR only changes checker logic/docs; the added diagnostic examples in tests all use E/W codes, and no user-facing diagnostic text was introduced without one.
✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_diagnostic_fields.py`:
- Around line 709-753: Add fixture coverage in test_diagnostic_fields for the
remaining rebind paths handled by _rebound_names: with ... as d, walrus d :=
..., and except ... as d. Create small source snippets that mirror
REASSIGNED_BEFORE_RETURN and REBOUND_VIA_FOR_LOOP, then assert
spec_ref_violations_in_source returns one violation and still reports the
missing §99.1 reference so regressions in the With, NamedExpr, or ExceptHandler
branches are caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 30bb3cb8-3f9d-4193-a907-255c1624476b

📥 Commits

Reviewing files that changed from the base of the PR and between 5de983d and d26f2ce.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • ROADMAP.md
  • TESTING.md
  • scripts/check_diagnostic_fields.py
  • tests/test_diagnostic_fields.py
🔗 Linked repositories identified

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

  • aallan/vera-bench (manual)

Comment thread tests/test_diagnostic_fields.py
chethanuk and others added 2 commits July 9, 2026 05:01
…he helper's result

_plumbing_ctors already required a genuine helper method (aallan#827's fix) whose
sole own-scope Diagnostic() construction is unambiguous, but never confirmed
that construction was actually the helper's output. A helper that builds its
one ctor and hands it to something other than return/append — e.g.
self.dispatch(d) instead of self.errors.append(d) — still had it elected as
plumbing and skipped by all three passes, so a bogus spec_ref or unregistered
error_code on it would ship silently.

_ctor_is_reachable_as_result now requires the sole ctor be return-ed,
appended, or bound to a local that is later return-ed/appended, gated after
the existing single-ctor check so aallan#827's ambiguity handling is untouched. On
vera/ the exempt set is unchanged: all five real helpers return or append
their sole ctor directly, so the gap was latent, closed defensively.

The name-based return/append match in the second half of the check has no
real data-flow: a local rebound after the ctor-binding assign would still
match on the name alone and be wrongly treated as reachable. Fixed
conservatively via _rebound_names, which counts every name-binding form in
own scope — plain and augmented assignment, for/with targets, walrus, and
except-as names — not just plain Assign. A name rebound more than once by
any of these forms is unreliable, so the ctor is inspected rather than
exempted.

Fixes aallan#956

Co-Authored-By: Claude <noreply@anthropic.invalid>
…l 3 passes

The field-presence pass (check_source) appended its non-literal-severity
violation and continue'd before its own opt-out lookup ran, so a marker on
that exact call was never consulted even in the one pass that otherwise
honours it. The spec_ref-validity and error_code-registration passes never
consulted the opt-out at all.

Fixed for the unresolvable (non-literal) sub-case only: a non-literal
severity or spec_ref can't be checked statically and so is opt-out-able,
same as a missing field. A spec_ref that resolves but cites the
wrong/nonexistent section, or an error_code not in ERROR_CODES, is a content
error, not a tagging gap - the opt-out never suppresses either, marker or
not. On vera/ the exempt set is unaffected: the three existing real markers
are all missing-field cases. main()'s remedy text now states this precisely
instead of implying the opt-out waives everything it lists.

Fixes aallan#955

Co-Authored-By: Claude <noreply@anthropic.invalid>
@chethanuk
chethanuk force-pushed the fix/issue-955-diag-exempt-three-passes branch from d26f2ce to 5ef1b03 Compare July 9, 2026 04:39
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.29%. Comparing base (a835975) to head (00d6808).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #965   +/-   ##
=======================================
  Coverage   93.29%   93.29%           
=======================================
  Files          96       96           
  Lines       31448    31448           
  Branches      456      456           
=======================================
  Hits        29339    29339           
  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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/check_diagnostic_fields.py`:
- Around line 287-342: The reachability logic in _rebound_names and
_ctor_is_reachable_as_result does not account for annotated assignments, so
AnnAssign can hide or miss rebinding of the local ctor holder. Update the
binding-site counting in _rebound_names to include ast.AnnAssign targets, and
adjust _ctor_is_reachable_as_result so a ctor bound via an annotated assignment
is treated the same as a plain Assign when checking whether the local name is
still reliably reachable. Add a regression fixture covering both the annotated
rebind case and the annotated ctor-binding case.

In `@TESTING.md`:
- Line 123: Update the `test_diagnostic_fields.py` entry in `TESTING.md` so the
`# diag-fields-exempt` opt-out summary explicitly includes the `error_code` pass
alongside severity and spec_ref. The current description understates the
behavior pinned by the tests; revise the row text to reflect that the opt-out is
honored consistently across all three passes for an unresolvable non-literal
value, while still excluding cases that resolve but are factually wrong.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ae8862b7-a737-4f2f-8c44-1b4e660103ea

📥 Commits

Reviewing files that changed from the base of the PR and between d26f2ce and 5ef1b03.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • ROADMAP.md
  • TESTING.md
  • scripts/check_diagnostic_fields.py
  • tests/test_diagnostic_fields.py
🔗 Linked repositories identified

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

  • aallan/vera-bench (manual)

Comment thread scripts/check_diagnostic_fields.py
Comment thread TESTING.md Outdated
aallan added a commit to chethanuk/vera that referenced this pull request Jul 9, 2026
…(PR aallan#964 review)

A value-carrying annotated assignment (d: object = self.template)
rebinds the local without being an ast.Assign, so a bogus ctor
followed by an AnnAssign rebind was still elected as plumbing and
skipped by all three passes (probe-confirmed EXEMPT). Dually,
d: Diagnostic = Diagnostic(...) was never recognised as the initial
binding, permanently inspecting genuine plumbing. Both arms fixed:
_rebound_names counts value-carrying AnnAssign targets (a bare
annotation does not assign and is not counted), and the binding
recognition accepts an AnnAssign whose value is the ctor.

Six new fixtures: the two AnnAssign directions (RED-confirmed pre-fix,
each arm mutation-killed), the bare-annotation distinction, and one
pin each for the with/walrus/except rebind arms so a dropped arm ships
RED (PR aallan#965 review ask, pinned here where the machinery lives).
Exempt set on vera/ unchanged (the same five plumbing ctors).
TESTING.md/ROADMAP counts follow (6,827 -> 6,833).
# Conflicts:
#	CHANGELOG.md
#	ROADMAP.md
#	TESTING.md
#	scripts/check_diagnostic_fields.py
#	tests/test_diagnostic_fields.py
@aallan
aallan merged commit 9bfd195 into aallan:main Jul 9, 2026
27 checks passed
@aallan

aallan commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Merged — thanks @chethanuk. That's the #955/#956 gate-honesty pair complete, and with it all four of this batch. The design call you made here — honouring the opt-out for unresolvable fields only, and explicitly rejecting the blanket-honour alternative so a one-line comment can never ship a diagnostic citing a nonexistent spec section — was exactly right, and the mutation-kill you ran against your own rejected alternative is the house discipline in miniature.

What the maintainer loop did on this one: the reconcile after #964's merge (your branch was cut from a pre-fix tip of that stack, so the narrow self.<attr>.append sink and its regression test came back via the rebase rather than needing anything from you); a mutation-pin for the multi-line span lookup (narrowing it back to the argument's line now goes RED); and precision on one claim — "honoured across all three passes" became "honoured wherever an unresolvable field can arise," since the error_code pass skips non-literal codes entirely and so has nothing to waive. CHANGELOG/TESTING.md/CLAUDE.md all state that scoping the same way now.

Four PRs, four merges, three issues closed (#852, #702, #955+#956). The whole batch lands in the next release. Genuinely good run — the tracker's Stage 20 table is noticeably shorter for it.

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.

Diagnostic-fields gate: the # diag-fields-exempt opt-out is honoured by only one of three passes

2 participants