Skip to content

fix(local_verify): say so when the overlay carried the FIX into the base - #125

Merged
stranske merged 1 commit into
mainfrom
claude/local-verify-overlay-scope
Aug 25, 2026
Merged

stranske merged 1 commit into
mainfrom
claude/local-verify-overlay-scope

Conversation

@stranske

Copy link
Copy Markdown
Owner

The defect

The overlay is meant to add the candidate tests to the base tree and nothing else. When the fix itself lives in test files, the default --test-path scope — "every changed test file" — is every changed file, so the base tree after the overlay is identical to the worktree in every file that differs. RED and GREEN then run the same code, and neither verdict means anything.

What it reported instead, captured from the new fixture with the guard disabled:

verdict: FAIL_HOLLOW
reason:  candidate tests still pass against the base implementation; 2 of 2 candidate nodes do
         not discriminate: tests/test_guard.py::TestGuard::test_guard,
         tests/test_regression.py::TestRegression::test_regression

A confident statement about the tests, and a false one — the tests were fine, the run was misconfigured. Worse, the per-node pass corroborates it by naming both nodes as tautologies, so the diagnosis reads well-evidenced and its implied fix (rewrite the tests) is the opposite of the real one (scope --test-path to the new module). Measured on Counter_Risk #964, where getting that scope right was load-bearing and had to be known in advance.

What changed

  • overlay_covers_every_change() — the exact condition (every path that differs from the base was copied into it), not a heuristic about which files look like tests. Conservative by construction: it fires only when nothing is left uncovered, so the correct usage — overlay the new test, leave the fix behind in the base — can never trip it, and a deletion cannot be copied so a removing worktree is never flagged.
  • One predicate, two causes. invalid_demonstration() folds this together with the node-id case fix(capabilities): say what the concept IS, and ship the break proof in the shape an issue body takes #118 added, so a consumer has a single question to ask.
  • It reaches the result dict, not only the transcript. The JSON consumer is the one being misled; a finding only the renderer can see is the delivery defect how_to_use already paid for once. It rides reason (which record_verdict writes to outcomes.notes), with the computed verdict preserved inside the sentence.
  • verify() reads _changed_paths once and filters it for the default overlay — the whole changed list is what answers the coverage question, and two git calls could disagree about what changed.

Reported, never gated. verdict, ok and the exit code are computed above this and untouched, matching --transcript's rule that a reporting feature which could move a gate would make the artifact and the gate two answers to one question. A selftest pins the verdict unchanged.

Break → revert (four; two fire on the previously-silent path, which is the direction that matters most for a new warning)

break result
overlay_covers_every_change returns False AssertionError: overlay_covers_every_change(degenerate) — with the FAIL_HOLLOW result quoted above
the finding stops riding reason AssertionError: candidate tests still pass against the base implementation; 2 of 2 candidate nodes do not discriminate...
changed_paths no longer carried (guard goes blind) fires on the sound run: AssertionError: "THIS IS NOT A VALID DEMONSTRATION" not in sound_text
all(...)any(...) the same false positive, from the other side

Reverted; selftest green. The fixture asserts both directions on one repo: the default scope is flagged, and the same fix with --test-path scoped to the new module alone is silent — a warning that fires on every run is a warning nobody reads.

Verification

python3 src/verify.py458 passed, 85 selftests, 5 of 5 gates green. Collection unchanged at 458, no .verify-floor.json move.

🤖 Generated with Claude Code

The overlay is meant to add the candidate tests to the base tree and nothing else. When the fix
itself lives IN TEST FILES, the default `--test-path` scope — "every changed test file" — is every
changed file, so the base tree after the overlay is identical to the worktree in every file that
differs. RED and GREEN then run the same code and neither verdict means anything.

WHAT IT REPORTED INSTEAD, captured from the fixture with the guard disabled:

    verdict: FAIL_HOLLOW
    reason:  candidate tests still pass against the base implementation; 2 of 2 candidate nodes do
             not discriminate: tests/test_guard.py::TestGuard::test_guard,
             tests/test_regression.py::TestRegression::test_regression

A confident statement about the TESTS, and a false one — the tests were fine and the RUN was
misconfigured. Worse, the per-node pass corroborates it by naming both nodes as tautologies, so the
diagnosis reads well-evidenced and its implied fix (rewrite the tests) is the opposite of the real
one (scope `--test-path` to the new module). Measured on Counter_Risk #964, where getting the scope
right was load-bearing and had to be known in advance.

`overlay_covers_every_change()` is the EXACT condition — every path that differs from the base was
copied into it — not a heuristic about which files look like tests. Conservative by construction: it
fires only when NOTHING is left uncovered, so the correct usage (overlay the new test, leave the fix
behind in the base) can never trip it, and a deletion cannot be copied so a removing worktree is
never flagged.

ONE PREDICATE, TWO CAUSES. `invalid_demonstration()` folds this together with the node-id case #118
added, so a consumer has a single question to ask. And it reaches the RESULT DICT, not only the
transcript: the JSON consumer is the one being misled, and a finding that only the renderer can see
is the delivery defect `how_to_use` already paid for once. It rides `reason`, which `record_verdict`
writes to `outcomes.notes`, so the evidence stream sees it too — with the computed verdict preserved
inside the sentence.

REPORTED, NEVER GATED. `verdict`, `ok` and the exit code are computed ABOVE this and are untouched,
matching `--transcript`'s rule that a reporting feature which could move a gate would make the
artifact and the gate two answers to one question. A selftest pins the verdict unchanged.

`verify()` now reads `_changed_paths` ONCE and filters it for the default overlay, because the whole
changed list is what answers the coverage question; two git calls could disagree about what changed.

BREAK -> REVERT (four; two of them fire on the previously-silent path, which is the direction that
matters most for a new warning)

* `overlay_covers_every_change` returns False -> `AssertionError: overlay_covers_every_change(
  degenerate)` with the full FAIL_HOLLOW result quoted above.
* the finding stops riding `reason` -> `AssertionError: candidate tests still pass against the base
  implementation; 2 of 2 candidate nodes do not discriminate...`
* `changed_paths` no longer carried -> the guard goes blind and fires on the SOUND run:
  `AssertionError: "THIS IS NOT A VALID DEMONSTRATION" not in sound_text`
* `all(...)` -> `any(...)` -> the same false positive, from the other side.

Reverted; selftest green. The fixture asserts BOTH directions on one repo: the default scope is
flagged, and the same fix with `--test-path` scoped to the new module alone is silent — a warning
that fires on every run is a warning nobody reads.

verify.py: 458 passed, 85 selftests, 5 of 5 gates green. Collection unchanged (458), no floor move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 49 minutes.

View limit details

Limit details: You’ve used the included review currently available. Your 72 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6f062b24-ca54-4900-a2b1-27ccfcc104f9

📥 Commits

Reviewing files that changed from the base of the PR and between 2953dba and 52a44e3.

📒 Files selected for processing (2)
  • ARCHITECTURE.md
  • src/local_verify.py

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

@agents-workflows-bot

Copy link
Copy Markdown
Contributor

Workflow source needed

PR #125 needs either a linked GitHub issue or one valid non-issue Workflow Source before PR metadata automation can manage it safely.

Please do one of:

  • Add <!-- meta:issue:123 --> or a normal Closes #123 / Related to #123 line.
  • Check one Workflow Source option in the PR body.
  • Add a hidden marker such as <!-- workflow-source:local_request -->, <!-- workflow-source:manual_remote -->, <!-- workflow-source:review_followup -->, <!-- workflow-source:sync_campaign -->, or <!-- workflow-source:dependabot -->.
  • Add a workflow source label such as workflow:source-direct-pr, workflow:source-local-request, workflow:source-review-followup, workflow:source-sync, or workflow:no-automation.

Once a valid source is present, this warning will not be reposted.

@stranske-keepalive

Copy link
Copy Markdown

Automated Status Summary

Head SHA: aabfee6
Latest Runs: ⏳ pending — Gate
Required: core tests (3.12): ⏳ pending, core tests (3.13): ⏳ pending, docker smoke: ⏳ pending, gate: ⏳ pending

Workflow / Job Result Logs
(no jobs reported) ⏳ pending

Coverage Overview

  • Coverage history entries: 1

Coverage Trend

Metric Value
Current 34.12%
Baseline 0.00%
Delta +34.12%
Minimum 70.00%
Status ❌ Below minimum

Top Coverage Hotspots (lowest coverage)

File Coverage Missing
src/capability_effectiveness.py 0.0% 154
src/capability_firing_monitor.py 0.0% 192
src/capability_matcher_proposals.py 0.0% 111
src/capability_opportunity.py 0.0% 143
src/capability_propensity.py 0.0% 1710
src/ccusage_reconcile.py 0.0% 286
src/codemod_lane.py 0.0% 351
src/evidence_acquisition.py 0.0% 103
src/exploration_collection.py 0.0% 331
src/feature_scan.py 0.0% 118
src/frontend_verify.py 0.0% 255
src/improvement_log.py 0.0% 248
src/issue_readiness.py 0.0% 507
src/keepalive_evidence.py 0.0% 378
src/keepalive_supervisor.py 0.0% 322

Low Coverage Files (<50.0%)

File Coverage Missing
src/capability_effectiveness.py 0.0% 154
src/capability_firing_monitor.py 0.0% 192
src/capability_matcher_proposals.py 0.0% 111
src/capability_opportunity.py 0.0% 143
src/capability_propensity.py 0.0% 1710
src/ccusage_reconcile.py 0.0% 286
src/codemod_lane.py 0.0% 351
src/evidence_acquisition.py 0.0% 103
src/exploration_collection.py 0.0% 331
src/feature_scan.py 0.0% 118
src/frontend_verify.py 0.0% 255
src/improvement_log.py 0.0% 248
src/issue_readiness.py 0.0% 507
src/keepalive_evidence.py 0.0% 378
src/keepalive_supervisor.py 0.0% 322

Updated automatically; will refresh on subsequent CI/Docker completions.


Keepalive checklist

Scope

No scope information available

Tasks

  • No tasks defined

Acceptance criteria

  • No acceptance criteria defined

@stranske
stranske merged commit 14c42f3 into main Aug 25, 2026
37 checks passed
@stranske
stranske deleted the claude/local-verify-overlay-scope branch August 25, 2026 04:23
@github-actions

Copy link
Copy Markdown
Contributor

Workflow state fingerprint for Agents Gate Followups. Do not edit.

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