fix(evidence): decide the dead combined-haystack binding — it stays removed, and why - #66
Conversation
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 13 minutes Limit details: You’ve used the included review currently available. Your 75 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. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
Comment |
Workflow source detectedPR #66 now has valid workflow source context (origin=local_request). No linked GitHub issue is required for this PR. |
Automated Status SummaryHead SHA: 13408e3
Updated automatically; will refresh on subsequent CI/Docker completions. Keepalive checklistScopeNo scope information available Tasks
Acceptance criteria
|
|
Runner dispatch state for autofix on PR #66. Do not edit. |
|
Runner dispatch state for codex on PR #66. Do not edit. |
|
Workflow state fingerprint for Agents Gate Followups. Do not edit. |
|
Workflow state fingerprint for Keepalive Loop Reporter. Do not edit. |
…y it stays dead
`_dedupe_candidate` built `haystack = f"#{issue_number} {issue_title}"` and then searched
`issue_title` and `candidate["seed"]` only. ruff F841 flagged the unused local; PR #58 removed
the binding as lint. This decides the question that removal left open — was the combined form
the intent? No, and it is inert rather than merely unused:
* Substituting `haystack` for `issue_title` in the first `_contains_pr_ref` adds EXACTLY one
condition, `issue_number == pr_number`. It does not widen the text searched. Checked by brute
force over 200k random (issue_number, pr_number, title) triples: 0 mismatches against
`ref(title, pr) or issue_number == pr`.
* That condition cannot occur: GitHub draws issue and PR numbers from one per-repo sequence.
Over recorded history — 202 dedupe-eligible candidates (reverted/abandoned keepalive rows
carrying a PR number, 2026-01-08..2026-08-22, 11 repos) against 4,031 issues in those same
repos — 0 number collisions.
* And it is already covered even if it could: every seed `evidence_for_repo` builds opens with
"PR #{pr_number}", so the adjacent `_contains_pr_ref(candidate["seed"], issue_number)`
disjunct is already true in exactly that case. Differential run of the real
`_dedupe_candidate` against a haystack-wired copy over 120k real-shaped candidates, with
`issue_number == pr_number` forced in ~40% of search hits: 0 behaviour differences.
So the effect on matching is +0 candidates, not merely over history but under any input. The one
way it could ever fire is as a false positive claiming duplication from a numeric coincidence.
Also corrects the premise behind the question: `_contains_pr_ref` ALREADY matches a bare "#N" in
an issue title — the "#" in `#?{n}` is optional and `(?<!\d)` excludes only a preceding digit.
Nothing here changes what the path matches, so CLAUDE.md §2 is satisfied by the change being a
true no-op rather than by deferring it. The genuine widening, if ever wanted, is the sibling
pattern in durability_sweep.py — title -> title+body, which widens the TEXT.
The explanation sits ABOVE the matching loop rather than in the deleted line's slot, so an
identical deletion on both sides merges cleanly with PR #58 instead of conflicting.
Selftest pins the decision with two assertions and a deliberate-break -> revert demonstration:
wiring the haystack in fails assertion 1 (exit 1); removing "PR #<pr>" from the reversal seed
fails assertion 2 (exit 1); revert restores byte-for-byte and both pass (exit 0).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…is redundant PR #58's entire change to keepalive_evidence.py is two deletions: the dead `haystack` binding (decided in the previous commit) and this unused `repo = target.split("#", 1)[0]` in the selftest loop — `record_run` is passed `target`, never `repo`. Taking both makes this branch's copy of the file a superset of #58's, and since both are now identical deletions on both sides, the file merges with zero conflicts in either order — verified with a 3-way merge against origin/claude/ci-conform-format-lint. `ruff check --select E4,E7,E9,F` is clean for this file and `black --line-length 100 --check` leaves it unchanged. #58 is separately CONFLICTING against main (it predates #59 and is a tree-wide black pass), so it needs a rebase regardless — that is not caused by this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3566ffc to
b29de06
Compare
main modified langsmith-fleet-worker-attempt.json again (pr_number 68, an eleventh rewrite), producing the modify/delete conflict this change exists to end. Resolved by keeping the deletion. Floor comes from main at 407; this branch adds no tests, so the merge result is main's count unchanged.
`langsmith-fleet-worker-attempt.json` was TRACKED at the repo root, holding one run's execution telemetry (`operation_role: worker`, a resolved `selected_model`, and the `pr_number` of whichever PR last merged). `stranske/Workflows` `reusable-codex-run.yml` rewrites it into the checkout root on every agent round, purely to stage the `actions/upload-artifact` step on the next line — so the autofix bot re-emitted it on the next PR and #66 hit an add/add conflict on a path neither side had a reason to author. That was dropped there; the cause was not. It recurred during this branch's own review: #68's merge re-added the file, turning the rebase into a modify/delete conflict, which is the defect demonstrating itself. Wrong in this repo's own terms, not merely noisy. CLAUDE.md §1's tool-vs-evidence split puts one run's telemetry outside the tree. §2 requires execution provenance to reach the learner through `feedback.py`'s tables, and a git-churned `worker` record with a resolved model is a second, unmanaged store of exactly that evidence. Nothing here reads the path — this tree's emitters are `langsmith_*.py` and they read `langsmith-fleet.ndjson` from `$ORCH_STATE_DIR`. Both steps are needed, and the order is not interchangeable. Verified against a scratch repository: `git add -A` SKIPS an ignored UNTRACKED path but STAGES an ignored TRACKED one. So ignoring alone would have been inert while the path stayed in the index, and untracking alone would have let the next round re-add it. It also means the runner's `git reset HEAD --` denylist is only load-bearing for paths that are already tracked. A PATTERN, not the literal name: the runner names the file after the ROLE it recorded, so a verifier or evaluator attempt arrives as a sibling and escapes a literal. ROOT-ANCHORED, and the leading slash is load-bearing. The first draft bounded the patterns by extension and treated that as sufficient; it is not. Unanchored, a gitignore pattern matches at EVERY depth, so `langsmith-fleet*.json` also swallowed a contract schema at `docs/contracts/schemas/langsmith-fleet-v1.schema.json` — a path `stranske/Workflows` really tracks, whose sibling `artifact-manifest-v1.schema.json` this tree already keeps. A schema IS a `.json`, so extension-bounding cannot save it. Found by a concurrent session reviewing the upstream half of this fix. The nested CI artifact directory is named separately as `artifacts/langsmith/` for the same reason: anchoring it to the one directory that holds it cannot reach a schema elsewhere. Same near-miss the vendored-`node_modules` exception records, same conclusion. Placed ABOVE the managed `BEGIN/END WORKFLOWS STATUS FILES` block on purpose: a Template-Version bump rewrites that block and would silently drop the line. `scripts/sync_status_file_ignores.py --check` still reports all canonical patterns present. Guarded by `test_repo_artifact_hygiene.py` (11 tests), which puts every question to git rather than reimplementing gitignore precedence — `.gitignore`'s own header forbids the alternative, recording that trailing comments once made every pattern inert and staged 795 files instead of 141. The new prerequisite is `env_prereq.git_repo_absent()`, gated on whether git can answer here and never on `.gitignore` itself, since gating on the measured file would leave the check unable to fail once the file went missing. Three break->revert directions are held, each demonstrated and reverted byte-identical: too narrow (the bare literal fails the four sibling cases), too broad by KIND (`*langsmith-fleet*` fails on the contract doc), too broad by DEPTH (dropping the slash fails on the schema). Floor 410 -> 421, re-measured on each merge result as main moved twice underneath (402+10, then 407+10, then 410+11). No ceiling moved — the tests run on any checkout and skip only in the exec-mirror layout, which is a file copy, not a `.git`. Upstream, handled separately: Workflows PR #2856 (2026-07-31) diagnosed this verbatim but fixed only its own repo, never `templates/consumer-repo/.gitignore`, so consumers stayed exposed and this repo repeated the defect 23 days later.
`langsmith-fleet-worker-attempt.json` was TRACKED at the repo root, holding one run's execution telemetry (`operation_role: worker`, a resolved `selected_model`, and the `pr_number` of whichever PR last merged). `stranske/Workflows` `reusable-codex-run.yml` rewrites it into the checkout root on every agent round, purely to stage the `actions/upload-artifact` step on the next line — so the autofix bot re-emitted it on the next PR and #66 hit an add/add conflict on a path neither side had a reason to author. That was dropped there; the cause was not. It recurred during this branch's own review: #68's merge re-added the file, turning the rebase into a modify/delete conflict, which is the defect demonstrating itself. Wrong in this repo's own terms, not merely noisy. CLAUDE.md §1's tool-vs-evidence split puts one run's telemetry outside the tree. §2 requires execution provenance to reach the learner through `feedback.py`'s tables, and a git-churned `worker` record with a resolved model is a second, unmanaged store of exactly that evidence. Nothing here reads the path — this tree's emitters are `langsmith_*.py` and they read `langsmith-fleet.ndjson` from `$ORCH_STATE_DIR`. Both steps are needed, and the order is not interchangeable. Verified against a scratch repository: `git add -A` SKIPS an ignored UNTRACKED path but STAGES an ignored TRACKED one. So ignoring alone would have been inert while the path stayed in the index, and untracking alone would have let the next round re-add it. It also means the runner's `git reset HEAD --` denylist is only load-bearing for paths that are already tracked. A PATTERN, not the literal name: the runner names the file after the ROLE it recorded, so a verifier or evaluator attempt arrives as a sibling and escapes a literal. ROOT-ANCHORED, and the leading slash is load-bearing. The first draft bounded the patterns by extension and treated that as sufficient; it is not. Unanchored, a gitignore pattern matches at EVERY depth, so `langsmith-fleet*.json` also swallowed a contract schema at `docs/contracts/schemas/langsmith-fleet-v1.schema.json` — a path `stranske/Workflows` really tracks, whose sibling `artifact-manifest-v1.schema.json` this tree already keeps. A schema IS a `.json`, so extension-bounding cannot save it. Found by a concurrent session reviewing the upstream half of this fix. The nested CI artifact directory is named separately as `artifacts/langsmith/` for the same reason: anchoring it to the one directory that holds it cannot reach a schema elsewhere. Same near-miss the vendored-`node_modules` exception records, same conclusion. Placed ABOVE the managed `BEGIN/END WORKFLOWS STATUS FILES` block on purpose: a Template-Version bump rewrites that block and would silently drop the line. `scripts/sync_status_file_ignores.py --check` still reports all canonical patterns present. Guarded by `test_repo_artifact_hygiene.py` (11 tests), which puts every question to git rather than reimplementing gitignore precedence — `.gitignore`'s own header forbids the alternative, recording that trailing comments once made every pattern inert and staged 795 files instead of 141. The new prerequisite is `env_prereq.git_repo_absent()`, gated on whether git can answer here and never on `.gitignore` itself, since gating on the measured file would leave the check unable to fail once the file went missing. Three break->revert directions are held, each demonstrated and reverted byte-identical: too narrow (the bare literal fails the four sibling cases), too broad by KIND (`*langsmith-fleet*` fails on the contract doc), too broad by DEPTH (dropping the slash fails on the schema). Floor 410 -> 421, re-measured on each merge result as main moved twice underneath (402+10, then 407+10, then 410+11). No ceiling moved — the tests run on any checkout and skip only in the exec-mirror layout, which is a file copy, not a `.git`. Upstream, handled separately: Workflows PR #2856 (2026-07-31) diagnosed this verbatim but fixed only its own repo, never `templates/consumer-repo/.gitignore`, so consumers stayed exposed and this repo repeated the defect 23 days later.
`langsmith-fleet-worker-attempt.json` was TRACKED at the repo root, holding one run's execution telemetry (`operation_role: worker`, a resolved `selected_model`, and the `pr_number` of whichever PR last merged). `stranske/Workflows` `reusable-codex-run.yml` rewrites it into the checkout root on every agent round, purely to stage the `actions/upload-artifact` step on the next line — so the autofix bot re-emitted it on the next PR and #66 hit an add/add conflict on a path neither side had a reason to author. That was dropped there; the cause was not. It recurred during this branch's own review: #68's merge re-added the file, turning the rebase into a modify/delete conflict, which is the defect demonstrating itself. Wrong in this repo's own terms, not merely noisy. CLAUDE.md §1's tool-vs-evidence split puts one run's telemetry outside the tree. §2 requires execution provenance to reach the learner through `feedback.py`'s tables, and a git-churned `worker` record with a resolved model is a second, unmanaged store of exactly that evidence. Nothing here reads the path — this tree's emitters are `langsmith_*.py` and they read `langsmith-fleet.ndjson` from `$ORCH_STATE_DIR`. Both steps are needed, and the order is not interchangeable. Verified against a scratch repository: `git add -A` SKIPS an ignored UNTRACKED path but STAGES an ignored TRACKED one. So ignoring alone would have been inert while the path stayed in the index, and untracking alone would have let the next round re-add it. It also means the runner's `git reset HEAD --` denylist is only load-bearing for paths that are already tracked. A PATTERN, not the literal name: the runner names the file after the ROLE it recorded, so a verifier or evaluator attempt arrives as a sibling and escapes a literal. ROOT-ANCHORED, and the leading slash is load-bearing. The first draft bounded the patterns by extension and treated that as sufficient; it is not. Unanchored, a gitignore pattern matches at EVERY depth, so `langsmith-fleet*.json` also swallowed a contract schema at `docs/contracts/schemas/langsmith-fleet-v1.schema.json` — a path `stranske/Workflows` really tracks, whose sibling `artifact-manifest-v1.schema.json` this tree already keeps. A schema IS a `.json`, so extension-bounding cannot save it. Found by a concurrent session reviewing the upstream half of this fix. The nested CI artifact directory is named separately as `artifacts/langsmith/` for the same reason: anchoring it to the one directory that holds it cannot reach a schema elsewhere. Same near-miss the vendored-`node_modules` exception records, same conclusion. Placed ABOVE the managed `BEGIN/END WORKFLOWS STATUS FILES` block on purpose: a Template-Version bump rewrites that block and would silently drop the line. `scripts/sync_status_file_ignores.py --check` still reports all canonical patterns present. Guarded by `test_repo_artifact_hygiene.py` (11 tests), which puts every question to git rather than reimplementing gitignore precedence — `.gitignore`'s own header forbids the alternative, recording that trailing comments once made every pattern inert and staged 795 files instead of 141. The new prerequisite is `env_prereq.git_repo_absent()`, gated on whether git can answer here and never on `.gitignore` itself, since gating on the measured file would leave the check unable to fail once the file went missing. Three break->revert directions are held, each demonstrated and reverted byte-identical: too narrow (the bare literal fails the four sibling cases), too broad by KIND (`*langsmith-fleet*` fails on the contract doc), too broad by DEPTH (dropping the slash fails on the schema). Floor 410 -> 421, re-measured on each merge result as main moved twice underneath (402+10, then 407+10, then 410+11). No ceiling moved — the tests run on any checkout and skip only in the exec-mirror layout, which is a file copy, not a `.git`. Upstream, handled separately: Workflows PR #2856 (2026-07-31) diagnosed this verbatim but fixed only its own repo, never `templates/consumer-repo/.gitignore`, so consumers stayed exposed and this repo repeated the defect 23 days later.
`langsmith-fleet-worker-attempt.json` was TRACKED at the repo root, holding one run's execution telemetry (`operation_role: worker`, a resolved `selected_model`, and the `pr_number` of whichever PR last merged). `stranske/Workflows` `reusable-codex-run.yml` rewrites it into the checkout root on every agent round, purely to stage the `actions/upload-artifact` step on the next line — so the autofix bot re-emitted it on the next PR and #66 hit an add/add conflict on a path neither side had a reason to author. That was dropped there; the cause was not. It recurred during this branch's own review: #68's merge re-added the file, turning the rebase into a modify/delete conflict, which is the defect demonstrating itself. Wrong in this repo's own terms, not merely noisy. CLAUDE.md §1's tool-vs-evidence split puts one run's telemetry outside the tree. §2 requires execution provenance to reach the learner through `feedback.py`'s tables, and a git-churned `worker` record with a resolved model is a second, unmanaged store of exactly that evidence. Nothing here reads the path — this tree's emitters are `langsmith_*.py` and they read `langsmith-fleet.ndjson` from `$ORCH_STATE_DIR`. Both steps are needed, and the order is not interchangeable. Verified against a scratch repository: `git add -A` SKIPS an ignored UNTRACKED path but STAGES an ignored TRACKED one. So ignoring alone would have been inert while the path stayed in the index, and untracking alone would have let the next round re-add it. It also means the runner's `git reset HEAD --` denylist is only load-bearing for paths that are already tracked. A PATTERN, not the literal name: the runner names the file after the ROLE it recorded, so a verifier or evaluator attempt arrives as a sibling and escapes a literal. ROOT-ANCHORED, and the leading slash is load-bearing. The first draft bounded the patterns by extension and treated that as sufficient; it is not. Unanchored, a gitignore pattern matches at EVERY depth, so `langsmith-fleet*.json` also swallowed a contract schema at `docs/contracts/schemas/langsmith-fleet-v1.schema.json` — a path `stranske/Workflows` really tracks, whose sibling `artifact-manifest-v1.schema.json` this tree already keeps. A schema IS a `.json`, so extension-bounding cannot save it. Found by a concurrent session reviewing the upstream half of this fix. The nested CI artifact directory is named separately as `artifacts/langsmith/` for the same reason: anchoring it to the one directory that holds it cannot reach a schema elsewhere. Same near-miss the vendored-`node_modules` exception records, same conclusion. Placed ABOVE the managed `BEGIN/END WORKFLOWS STATUS FILES` block on purpose: a Template-Version bump rewrites that block and would silently drop the line. `scripts/sync_status_file_ignores.py --check` still reports all canonical patterns present. Guarded by `test_repo_artifact_hygiene.py` (11 tests), which puts every question to git rather than reimplementing gitignore precedence — `.gitignore`'s own header forbids the alternative, recording that trailing comments once made every pattern inert and staged 795 files instead of 141. The new prerequisite is `env_prereq.git_repo_absent()`, gated on whether git can answer here and never on `.gitignore` itself, since gating on the measured file would leave the check unable to fail once the file went missing. Three break->revert directions are held, each demonstrated and reverted byte-identical: too narrow (the bare literal fails the four sibling cases), too broad by KIND (`*langsmith-fleet*` fails on the contract doc), too broad by DEPTH (dropping the slash fails on the schema). Floor 410 -> 421, re-measured on each merge result as main moved twice underneath (402+10, then 407+10, then 410+11). No ceiling moved — the tests run on any checkout and skip only in the exec-mirror layout, which is a file copy, not a `.git`. Upstream, handled separately: Workflows PR #2856 (2026-07-31) diagnosed this verbatim but fixed only its own repo, never `templates/consumer-repo/.gitignore`, so consumers stayed exposed and this repo repeated the defect 23 days later. Co-authored-by: Cursor <cursoragent@cursor.com>
Decides a question a lint pass left open, rather than answering it silently.
_dedupe_candidatebuilthaystack = f"#{issue_number} {issue_title}"and then searchedissue_titleandcandidate["seed"]only — the combined form was constructed and discarded.ruff F841 flagged the unused local. Removing it as lint is a no-op; the open question was whether
matching against the combined form had been the intent.
keepalive_evidencefeeds run/outcomeevidence, so changing what this path matches is a change to Brain input (CLAUDE.md §2) and does
not belong in a lint pass.
Determination: no — and it is inert, not merely unused
issue_number == pr_number. It does not widen the text searched. Brute force over 200k random(issue_number, pr_number, title)triples: 0 mismatches againstref(title, pr) or issue_number == previdence_for_repobuilds opens withPR #{pr_number}, so the adjacent_contains_pr_ref(candidate["seed"], issue_number)disjunct already fires in exactly that case. Differential over 120k real-shaped candidates with the collision forced in ~40% of search hits: 0 behaviour differencesEffect on matching: +0 candidates — not merely over history, but under any possible input. A dead
disjunct by construction. The only way it could ever fire is as a false positive claiming
duplication from a bare numeric coincidence.
This also corrects the premise behind the question:
_contains_pr_refalready matches a bare#Nin an issue title — the#in#?{n}is optional and(?<!\d)excludes only a precedingdigit. So the case the combined form was supposedly for was already covered.
Nothing here changes what the path matches, so §2 is satisfied by the change being a true no-op
rather than by deferring it. The genuine widening, if ever wanted, is the sibling pattern in
durability_sweep.py—title->title+body, which widens the TEXT. That direction is recordedin the comment.
Changes
Dead binding removed; the reasoning recorded as a comment above the matching loop (not in
the deleted line's slot, so an identical deletion on both sides merges cleanly rather than
conflicting).
Also took the other F841 in this file — an unused
repo = target.split("#", 1)[0]in theselftest loop (
record_runis passedtarget, neverrepo).Since superseded: main took both deletions itself when the lint debt was drained, so this
PR's diff against main is now 46 insertions, 0 deletions — purely the decision record and
the test gate, which is the part that could not be derived from a lint pass.
Test gate
Two selftest assertions pin the decision, with a deliberate-break -> revert demonstration:
PR #<pr>from the reversal seed, which is the precondition that makes it a no-op ->assertion 2 fails (exit 1)
CI note
lint-format,lint-ruff,typecheck-mypy,python 3.12andpython 3.13were red on thisbranch for a repo-wide reason unrelated to it:
.github/workflows/autofix-versions.envwasmissing, so all five died at the tooling-install step before running any code. That is now fixed
by #60, and all checks on this PR are green.