fix(ci): unblock the merge gate, consolidate its competing fixes, and audit 337 branches - #1377
Conversation
test_cleanup_is_total_for_non_oserror_failures failed on Python 3.11.15: its premise asserted that shutil.rmtree(path, ignore_errors=True) raises ValueError on a NUL-byte path. That is an implementation detail which has changed -- ignore_errors now absorbs the non-OSError as well, so the assertion no longer holds. The contract under test is unaffected: _cleanup_download_artifacts must swallow non-OSError failures because it runs from a finally block and would otherwise replace the in-flight exception. Establish that premise against the unguarded rmtree call, which still raises, so the test proves the helper's own defensiveness rather than the stdlib's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YcHjCZ6pGn6A5BeeoZ6eZi
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Scanned FilesNone |
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
Agent Completion Truth Gate: BLOCKEDReasons: Machine-readable verdict{
"details": {
"collection_errors": [
"incomplete_linked_issue_contract",
"missing_intent_snapshot",
"missing_agent_run_id",
"missing_agent_login"
],
"invalid_fields": [
"policy.agent_login",
"policy.run_id"
]
},
"reasons": [
"invalid_payload"
],
"verdict": "blocked"
} |
|
agentTaskApplicable() unioned PR labels with linked-issue labels, so a bare agent-task/mcp-agent label applied by label automation to an issue that never declared a contract judged any closing PR an agent completion. The gate then required an Agent Run ID / Agent Login the issue never declared, yielding a permanent blocked/invalid_payload verdict no author could satisfy. Applicability now comes from PR provenance (known agent author, agent branch prefix, agent label on the PR, lock manifest) or from a genuine issue-side dispatch: an agent-task/mcp-agent label AND declared Agent Run ID + Agent Login headings. The generic agent label remains a PR-side signal only, since the snapshot job and collector never recognise it issue-side — arming from it would block permanently as linked_issue_not_agent_task with no snapshot to satisfy. The collector now emits a mislabelled_agent_task core.notice when a linked issue carries a contract label without declaring the contract, keyed on the missing contract itself (not inapplicability) so Dependabot PRs linked to valid contracts never get a false notice. Generated with [Linear](https://linear.app/myxstack/issue/GRV-196/agent-completiontruth-gate-is-permanently-unsatisfiable-for-any-pr#agent-session-8e5d62f0) Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
The truth gate scores a pull request against the frozen intent snapshot on its linked issue. That snapshot is only ever written by snapshot-agent-task-intent, which runs on `issues` events alone -- it never runs on pull_request_target. So a pull request with no linked issue has no snapshot, no declared agent_login and no declared run_id, which means policy.agent_login, policy.run_id and issue.number can never be populated and the verdict is permanently `invalid_payload` no matter what the author does. Arming that unsatisfiable state from a branch-name prefix made the check red on pull requests that never had a contract to satisfy. It is red on merged commits too: #1368, the current tip of main, merged with agent-completion/truth-gate/pr-1368 failing on exactly this. A check that is red on everything gates nothing and buries real failures, which is the failure mode agent-completion-enforcement.yml already warns about in its own comments. Pull-side provenance now arms the gate only when a linked issue exists to verify against. With none there is nothing to measure, so the verdict is not_applicable rather than blocked. This is not an escape hatch: a pull request that links a dispatched issue is gated exactly as before, and the requirement to bind a pull request to a focused issue at all is separately owned by the `Canonical issue and evidence` check, which states a requirement an author can actually meet. Consolidates the two competing open implementations of this fix. #1364's commit is cherry-picked here with authorship intact; #1154 carried the same intent but had drifted to 117 files and 14k lines of unrelated changes. Both should close in favour of this. Full unit suite: 8079 passed, 0 failed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YcHjCZ6pGn6A5BeeoZ6eZi
Classifies every remote branch using the one signal that stays honest after
the secret-purge force-push: whether it shares any ancestry with `main`.
$ git merge-base origin/main origin/<branch>
(empty)
275 of 337 branches return empty -- they predate the rewrite and no rebase
recovers them. The signals the branch-cleanup harness normally leans on all
mislead here, so they are deliberately not used:
* `git merge-tree` calls these orphans a CLEAN merge; unrelated trees do
not textually conflict, they would clobber.
* A two-dot diff against an empty merge base silently degrades to a
working-tree diff, which is why a two-line Dependabot bump measures as
111 files / 15,650 lines.
* The purge rewrote committer dates, so every branch reads as under 30
days old and no staleness threshold ever fires.
Running the stock harness on this repo produced 263 REVIEW off those bad
signals. The ancestry test resolves the same set into:
KEEP-OPEN-PR 28
REVIEW-SHARED 29 real shared ancestry, no open PR -- not pruned
CLOSE-MERGED 1 tip is an ancestor of main
CLOSE-ORPHANED 275
The script prunes only the last two groups (276 branches) and archive-tags
each one first, verifying every tag is on the remote before deleting
anything. It defaults to a dry run.
That dry run caught the audit classifying `main` itself as CLOSE-MERGED --
`git merge-base --is-ancestor origin/main origin/main` is trivially true.
The row is removed, and a protected-ref guard plus a default-branch check
now abort regardless of what the CSV contains.
Not executed here: this session's credentials are scoped to one branch and
tag creation fails with HTTP 403, so the archive tags cannot be written.
Deleting without them would remove the only durable recovery path, so
nothing was deleted. Run the script with tag-write credentials.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YcHjCZ6pGn6A5BeeoZ6eZi
🔍 PR Validation |
The header summary read "CLOSE-MERGED 2 / 277 branches" while the shipped docs/branch-audit-2026-08-05.csv has 1 CLOSE-MERGED and 276 prunable. The header was written before the `main` row was dropped from the CSV and was never updated. Comment only -- the selection logic already read from the CSV, so the script was correctly selecting 276 the whole time. Verified: header, CSV tally, and the script's own dry-run count now all agree at 276. Reported by the Vercel review bot on #1377. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YcHjCZ6pGn6A5BeeoZ6eZi
Truth-gate verdict update — and why I am stopping here rather than satisfying itLinking #1384 moved the verdict. Before: "collection_errors": ["incomplete_linked_issue_contract", "missing_linked_issue",
"missing_closing_issue_reference", "missing_agent_run_id",
"missing_agent_login"],
"invalid_fields": ["issue.number", "policy.agent_login", "policy.run_id"]Now: "collection_errors": ["incomplete_linked_issue_contract", "missing_intent_snapshot",
"missing_agent_run_id", "missing_agent_login"],
"invalid_fields": ["policy.agent_login", "policy.run_id"]
I am not going to clear those, deliberately. Doing so would require:
#1384 was written after this PR, as a description of a defect I had already diagnosed. There was no dispatch — no provider run issued this work against a pre-registered contract. Back-filling one would mean inventing a This is the Recommendation stands: land this to quiet the check, then retire the apparatus in a separate PR. Generated by Claude Code |
Gate 2 listed `CI`, `Coverage`, `CodeQL`, `Security`, `Secret Scan`, `Dependency Review` -- inherited verbatim from v1. Those are workflow names. Branch protection matches check-run names (a job's `name:`, or its job id), so of that list only `CodeQL` can actually be selected. Anyone following adoption step 3 would find the strings absent from GitHub's picker, which is plausibly why MG-2 was never done. That is the v1 failure mode reproduced inside its replacement: a gate naming things that cannot be satisfied. Replaced with the contexts actually observed on live pull requests. Also splits out conditionally-required checks. `Generate and Upload Coverage` does not run on documentation-only changes -- it ran on #1377 and is absent from #1408. Requiring it unconditionally would leave docs pull requests permanently pending, which blocks harder than failing, since a check that never reports never resolves. Same for `E2E Pipeline Tests` and the JavaScript security scan. Adds the rule that would have caught this: before adding a check to branch protection, confirm the exact string appears in the picker and that it reports on a documentation-only pull request. Raised by the CodeRabbit review on #1408. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YcHjCZ6pGn6A5BeeoZ6eZi
Canonical issue
Closes #1384
Outcome
agent-completion/truth-gatestops being red on pull requests that never had a dispatch contract to satisfy, so CI failures become visible again instead of being buried under a check that fails on everything. Alongside it, the one failing test onmainis fixed and the PR backlog is drained from 61 open to 28.The gate scores a PR against the frozen intent snapshot on its linked issue. That snapshot is written only by
snapshot-agent-task-intent, which runs onissuesevents and never onpull_request_target. With no linked issue there is no snapshot, sopolicy.agent_login,policy.run_idandissue.numbercan never be populated and the verdict isinvalid_payloadregardless of what the author does. Arming that from a branch-name prefix made it red on merged commits too — #1368, the current tip ofmain, merged with this status failing.Provenance now arms the gate only when a linked issue exists to verify against; with none the verdict is
not_applicable. A PR that links a dispatched issue is gated exactly as before.Scope
.github/workflows/pr-checks.yml—agentTaskApplicable()(both copies) requires a linked issue before pull-side provenance arms the gate. Includes fix(ci): decide truth-gate applicability from PR provenance or a declared issue contract #1364's commit, cherry-picked with authorship intact.tests/unit/test_agent_completion_gate.py— both directions of the linked-issue rule, in both assertion blocks.tests/unit/test_transcript_action_workflow.py— retargets a test premise that asserted a CPython-version-specificshutil.rmtreedetail.docs/branch-audit-2026-08-05.csv,scripts/maintenance/archive-and-prune-branches.sh— branch audit and prune script (dry-run by default).Risk
not_applicablefor a PR that genuinely should be gated — specifically an agent PR that links no issue. That state was previouslyblocked-with-no-remedy rather than enforcing anything, so this narrows a permanently-failing check rather than a working one. Binding a PR to a focused issue stays enforced byCanonical issue and evidenceandPR Governance.pr-checks.ymland the gate tests. No data migration, no infrastructure change, no runtime code path affected. The branch audit files are additive and inert.Verification
Current head
10113efe27fad21e5a73b20225805d7d2208b453.tests/unit/test_agent_completion_gate.py: 112 passed, 89 subtests.tests/unit/test_transcript_action_workflow.py: 115 passed.main).yaml.safe_loadparsespr-checks.yml; the twoagentTaskApplicable()copies remain byte-identical, which the test asserts.bash -nclean; dry run selects 276, matching the CSV; guard verified by injectingmainback into the CSV and confirming it is still excluded.validate,guards,lint-python,lint-frontend,bandit,python-safety,npm-audit,gitleaks,dependency-reviewall green.10113efand the bot confirmedISSUE_RESOLVED. CodeRabbit has not reviewed: it skips on a label rule, and labels applied to this PR do not survive thelabeljob.agent-completion/truth-gateis still failing on this PR and is expected to.pull_request_targetruns the workflow from the base branch, so this fix cannot affect its own PR — it takes effect for everything else once merged. This is the same failure #1368 merged with.Production evidence
2KiJcUNFe5PyurhENWSWzAdadaby--execute.HTTP 403, so the archive tags could not be written. Deleting 276 branches without them would remove the only durable recovery path. Runscripts/maintenance/archive-and-prune-branches.sh --executewith tag-write credentials; it archive-tags, verifies every tag reached the remote, and aborts before deleting if any is missing.Agent handoff
agent-completion/truth-gate, which cannot pass on its own PR (see Verification), andPR Governance/Canonical issue and evidence, which this description is written to satisfyBacklog drained
61 open PRs → 28. Every remaining PR shares ancestry with
mainand is genuinely rebaseable.mainmainwas force-pushed for a secret purge, so 275 of 337 branches share no ancestry with it —git merge-basereturns empty and no rebase recovers them. The usual signals mislead here:merge-treecalls orphans a clean merge, a diff against an empty merge base degrades to a working-tree diff (a 2-line Dependabot bump measures as 111 files / 15,650 lines), and the purge rewrote committer dates so nothing reads as stale. The stock harness produced 263 REVIEW off those signals; ancestry resolves it to 28 KEEP-OPEN-PR · 29 REVIEW-SHARED · 1 CLOSE-MERGED · 275 CLOSE-ORPHANED.The prune script's dry run caught the audit classifying
mainitself as CLOSE-MERGED (--is-ancestor origin/main origin/mainis trivially true). That row is removed and a protected-ref guard now aborts regardless of CSV contents.#1378 lists the genuinely unlanded work needing a fresh cut, including two security items (#1118 proxy credential leakage, #1156 the
ecdsaadvisory) and #1075's transcript-on-timeout bug. Every branch is retained for archive-tagging.