Skip to content

fix: scope agent gate applicability to real dispatch evidence - #1154

Closed
groupthinking wants to merge 1 commit into
mainfrom
groupthinking-fix-agent-gate-applicability
Closed

fix: scope agent gate applicability to real dispatch evidence#1154
groupthinking wants to merge 1 commit into
mainfrom
groupthinking-fix-agent-gate-applicability

Conversation

@groupthinking

@groupthinking groupthinking commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Canonical issue

Fixes #1130

Outcome

agent-completion/truth-gate stops issuing verdicts that no author can act on.

Today the check is permanently unsatisfiable for any PR whose linked issue
carries the agent-task label without having declared the agent contract.
agentTaskApplicable(pull, selectedIssue) in .github/workflows/pr-checks.yml
unions the PR's labels with the linked issue's labels. linear-code[bot]
applies agent-task to [aw] issues as a topic tag; those issues were never
created from .github/ISSUE_TEMPLATE/agent-task.yml, so they declare no
Agent Run ID and no Agent Login.

Any PR closing such an issue is therefore judged an agent completion and then
required to satisfy a contract the issue never declared:

verdict: blocked
reasons: ["invalid_payload"]
invalid_fields: ["policy.agent_login", "policy.run_id"]

Observed on #1123. There is no honest escape: fabricating a run ID, or adding an
agent-lock-manifest to a human PR, defeats precisely what the gate protects.

After this change, applicability is a function of dispatch evidence rather
than of an incidental label, and a mislabelled issue surfaces as an actionable
mislabelled_agent_task notice instead of an unresolvable invalid_payload.

Scope

  • Included: agentTaskApplicable in .github/workflows/pr-checks.yml (both
    byte-identical copies), the new mislabelled_agent_task diagnostic, and the
    tests covering them.
  • Explicitly excluded: scripts/ci/agent_completion_gate.py, the payload
    schema, the label taxonomy, and linear-code[bot]'s labelling behaviour. The
    gate stays fail-closed for genuine agent work; nothing about real agent
    enforcement is relaxed.

Risk

  • Risk level: medium. This is enforcement logic, so a mistake either blocks
    honest work or lets unverified agent work through.
  • Failure mode: the credible failure is under-application — an agent
    completion judged not applicable and waved through. It is bounded by keeping
    every existing PR-side provenance signal sufficient on its own: agent bot
    author, agent branch prefix, agent label on the PR, or an agent-lock-manifest
    in the PR body. Only the transitive issue-label path is narrowed, and only
    by additionally requiring the contract the label already claims exists.
  • Rollback: revert this commit. The change is confined to one function plus its
    tests, touches no persisted state, and no other job reads the new notice.

Verification

Run on the current head.

  • PYTHONPATH=. python3 -m unittest tests.unit.test_agent_completion_gate
    106/106 pass, including three new tests.
  • The new truth table executes the real function under node across 13 rows,
    among them the exact fix(aw): require an explicit noop terminal state in agentic workflows #1123 scenario (issue labelled mcp/agent, body declares
    no contract, human author, non-agent branch → not applicable).
  • Two pre-existing assertions in
    test_scheduled_scanner_detects_frozen_intent_changes had encoded the old
    behaviour. Both were updated to the corrected contract while preserving their
    original intent: one split into with-contract and without-contract cases, the
    other kept its "applicability follows the selected issue" purpose by giving
    the selected issue a declared contract.
  • test_agent_applicability_copies_stay_identical pins the two copies of the
    function together so they cannot drift.

Two implementation constraints worth flagging for review, both discovered by
failing tests rather than assumed:

  1. declaresAgentContract is nested inside agentTaskApplicable. The test
    harness extracts each JS function individually and runs it under node -e,
    so an extracted function must be self-contained; a sibling helper breaks the
    scheduled-scanner test with ReferenceError.
  2. The mislabel diagnostic uses core.notice(), never collectionErrors.push().
    scripts/ci/agent_completion_gate.py short-circuits to not_applicable when
    applicable is False and discards collection errors, so a pushed error would
    be silently swallowed — the diagnostic would look implemented and do nothing.
  • Focused tests
  • Required CI — see below
  • Review threads resolved

agent-completion/truth-gate is red on this PR, by the bug this PR fixes.
The workflow triggers on pull_request_target, which always evaluates the
workflow file from main, so the corrected function cannot run on its own PR.
This PR closes #1130, #1130 carries the mcp/agent label, and #1130 declares no
agent contract — the exact input class described above. The check turns green
for subsequent PRs once this merges. gitleaks (working tree) is a separate
repo-wide false positive, tracked and fixed in #1159.
Agent completion enforcement fails on every open PR in the repository,
human and agent alike, because its trust policy is unprovisioned — surveyed
and tracked in #1160. None of the three is caused by this diff.

Production evidence

Not applicable: no runtime surface changes. This PR modifies CI enforcement
logic and its tests only — no application code, no dependency, no
infrastructure, and nothing that reaches a deployed environment. The observable
evidence for this change is the gate verdict itself, which is why the
verification above exercises the real workflow function rather than a
reimplementation of it.

Agent handoff

  • One canonical issue is linked
  • No competing PR implements the same issue
  • Acceptance criteria are satisfied
  • Required checks pass on the current head — blocked by the bug under fix, explained above
  • Human decision is requested only for product, security, irreversible infrastructure, or production approval

`agentTaskApplicable` unioned the pull request's labels with the linked
issue's labels, so a linked issue carrying `agent-task` made any pull
request an agent completion. Label automation applies `agent-task` as a
topic tag to issues that were never created from the agent task template
and therefore never declared an Agent Run ID or Agent Login. Those pull
requests were then measured against a contract that did not exist,
producing `blocked` / `invalid_payload` with `policy.agent_login` and
`policy.run_id` invalid -- a state no author could reach, because the
missing fields live on an issue that was never dispatched.

Applicability is now a function of dispatch evidence:

- Pull request provenance (known agent author, agent branch prefix, an
  agent label on the pull request, or a lock manifest in its body) is
  sufficient on its own, so fail-closed behaviour is preserved for real
  agent work regardless of how the issue is labelled.
- The linked issue's agent label counts only when the issue body actually
  declares the Agent Run ID and Agent Login the gate goes on to require,
  ignoring the `_No response_` placeholder left by unfilled issue forms.

The contract check is nested inside `agentTaskApplicable` so the function
stays self-contained for the workflow's `node -e` extraction harness, and
both copies remain byte-identical. A mislabelled issue is now surfaced
with `core.notice` as `mislabelled_agent_task`; it cannot be reported as
a collection error because the gate short-circuits to `not_applicable`
before reading them.

Two pre-existing assertions encoded the old behaviour and are updated to
the corrected contract while preserving their original intent: the row
proving a linked issue's labels are consulted now covers both the
contract-less and contract-bearing cases, and the textual-link fallback
check keeps its contrast by declaring a contract on the selected issue.

Fixes #1130

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 1, 2026 19:43
@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v0-uvai Canceled Canceled v0 Aug 1, 2026 7:43pm

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • [‘architecture-gap’, ‘bug’, ‘ci-cd’, ‘ci/cd’, ‘copilot-rabbit’, ‘documentation’, ‘duplicate’, ‘enhancement’, ‘frontend’, ‘github_actions’, ‘good first issue’, ‘help wanted’, ‘high-priority’, ‘invalid’, ‘javascript’, ‘ml-model’, ‘needs-triage’, ‘pipeline-critical’, ‘placeholder-code’, ‘priority:high’, ‘python’, ‘python:uv’, ‘question’, ‘styling’, ‘tests’, ‘v0’]

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 136db6a1-87d6-4bff-90da-1cffd32a295b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA c8106c7.
Ensure 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 Files

None

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Agent Completion Truth Gate: BLOCKED

Reasons: invalid_payload

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"
}

Workflow evidence

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Refines the agent-completion gate so linked issue labels only trigger it when backed by an agent contract.

Changes:

  • Separates PR provenance from issue-side dispatch evidence.
  • Emits notices for mislabelled agent-task issues.
  • Adds regression and applicability tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
.github/workflows/pr-checks.yml Updates applicability and diagnostic logic.
tests/unit/test_agent_completion_gate.py Adds contract and provenance test cases.
Suppressed comments (1)

.github/workflows/pr-checks.yml:2001

  • carriesAgentLabel also accepts the generic agent label, but issue dispatch labels are limited to agenttask/mcpagent by the snapshot job (lines 54-56) and the collector (lines 2188-2190). An issue with agent plus these headings therefore makes a human PR applicable, only to be permanently blocked as linked_issue_not_agent_task and without a snapshot. Restrict this second copy identically so scheduled and per-PR applicability remain aligned.
              const issueDispatch = carriesAgentLabel(issueLabelSource) &&
                declaresAgentContract(selectedIssue);

Comment on lines +698 to +699
const issueDispatch = carriesAgentLabel(issueLabelSource) &&
declaresAgentContract(selectedIssue);
Comment on lines +2173 to +2174
if (!applicable && issue &&
['agenttask', 'mcpagent'].some(label => issueLabels.has(label))) {

Copy link
Copy Markdown
Owner Author

Independent verification — ready to merge on the test evidence

Checked out this PR's head (c8106c70b34317f632295da030f99598ff84c2d1) and ran the gate suite:

PYTHONPATH=. python3 -m unittest tests.unit.test_agent_completion_gate
Ran 106 tests in 1.377s
OK

All 106 pass, including the regression cases that pin the corrected contract:

Why agent-completion/truth-gate/pr-1154 is red, and why that's expected

That last test names the reason: the gate deliberately runs from the trusted default branch, not the PR head (the job checks out .trusted). So the check on this PR executes main's pre-fix agentTaskApplicable, which unions the linked issue's labels — and #1130 carries mcp/agent (normalises to mcpagent) while declaring no Agent Run ID/Agent Login. Old logic → applicable: trueinvalid_payload. This fix cannot green its own gate by design; that's the bootstrap #1130 anticipated ("silently relaxing a governance gate to green a PR of my own is the failure mode the gate exists to catch").

Once merged, the trusted logic becomes the fixed logic and the recurring red on [aw]-closing PRs clears. Per #1130 this context is not merge-blocking today (main has no required status checks). The Vercel failure is a dashboard cancellation, not a build failure.

Recommendation: safe to merge on the strength of the suite above; it needs your sign-off since it rewrites a governance gate on protected main. I did not auto-merge — an unattended run doesn't clear a protected-branch publish gate, least of all on a change to the gate itself.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Automated review — verified locally against head c8106c70.

Verdict: correct and ready. This faithfully implements both remediations designed in #1130:

  • applicability now keys off PR provenance, with the linked-issue agent-task label counting only when the issue actually declares Agent Run ID and Agent Login (parsed from the issue-form headings, _No response_ treated as undeclared);
  • a mislabelled issue surfaces as a mislabelled_agent_task notice — correctly via core.notice(), since agent_completion_gate.py short-circuits to not_applicable and discards collection_errors once applicable is false.

Verification: PYTHONPATH=. python3 -m unittest tests.unit.test_agent_completion_gate106/106 pass on this exact head SHA. Both agentTaskApplicable copies are byte-identical (enforced by test_agent_applicability_copies_stay_identical), and the #1123/#1130 regression case (mislabelled linked issue → false) is covered by the new truth table.

On the red agent-completion/truth-gate/pr-1154 check — that's the bug fixing itself, not a defect. The gate that ran still evaluated this PR under the old label-union logic: #1130 carries an mcp/agent label, so the old code judged this a completion and returned invalid_payload against a contract the issue never declared. Under the corrected logic this PR's own shape (human author, non-agent branch, no PR agent label/manifest, linked issue with no declared contract) resolves to not_applicable — the check self-corrects once this merges. The Vercel line is a manual "Canceled from the Dashboard," unrelated to the diff.

No code defects found. I'm not merging autonomously — this targets protected main and is left for your merge approval.


Generated by Claude Code

@groupthinking groupthinking left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Automated review — review requested on #1154

Verdict: sound fix, mergeable, with one narrow non-blocking follow-up. Ran the full suite locally at c8106c7:
PYTHONPATH=. python3 -m unittest tests.unit.test_agent_completion_gate106/106 pass. The change correctly dissolves the #1130/#1123 deadlock — applicability now follows real dispatch evidence, and a mislabelled agent-task issue surfaces as a core.notice() instead of an unresolvable invalid_payload.


1. Non-blocking — issueDispatch accepts the generic agent label, but the rest of the issue side doesn't (seconds Copilot's suppressed note, verified).

carriesAgentLabel matches ['agent','agenttask','mcpagent'] and is reused for issue-side dispatch (issueDispatch, ≈L698 / ≈L2000). But every other issue-side path is restricted to ['agenttask','mcpagent']: the snapshot trigger (L54–56), linked_issue_not_agent_task (L711–714 / L2188–2190), and the new mislabel notice (L2174). So an issue carrying a bare agent label plus a declared contract goes applicable=true → the collector then sees no agent-task/mcp-agent label → pushes linked_issue_not_agent_task → the PR blocks. That's a narrower recurrence of the exact #1130 deadlock this PR removes. Verified against the head workflow's own agentTaskApplicable:

true  <= issue generic `agent` label + contract   (edge: becomes applicable, then blocks downstream)
true  <= issue `agent-task` label + contract       (intended)
false <= issue `bug` label + contract              (control)

Fix: gate issueDispatch on the restricted set (only agenttask/mcpagent), keeping the generic agent label as PR-side provenance only. Both copies (≈L698 and ≈L2000) must change identically — test_agent_applicability_copies_stay_identical will enforce that.

2. Merge-process — this PR structurally cannot green its own agent-completion/truth-gate; that red is expected, not a defect.

pr-checks.yml (which emits the truth-gate status) triggers on pull_request_target / workflow_run / schedule — all of which run the workflow definition from main, never the PR head. So the failing agent-completion/truth-gate (and the related Agent completion enforcement / Canonical issue and evidence / PR Governance) are executing main's old buggy applicability logic; this diff only takes effect once merged. Requiring these to go green before merging is the same self-referential trap the PR fixes, one level up. Recommend an admin/bypass merge rather than waiting for them. Separately, the red gitleaks (working tree) is the unrelated repo-wide uv.lock checksum false-positive (open fix in #1141/#1142) — this diff touches only pr-checks.yml and one test file, so it isn't implicated.


I have not merged (protected main, unattended scheduled run) and not pushed to the branch (not my designated branch — no authorization to write here). Once you accept #2, the staged action is a squash-merge of #1154; #1 can land as a small follow-up.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Automated triage — check status consolidated

Ran the remediation loop against head c8106c7. Summary of the red checks so a reviewer doesn't have to chase each one:

Substantive checks all greentest, build, lint-python, lint-frontend, CodeQL, bandit, trivy, npm-audit, python-safety, dependency-review.

The reds are all non-blocking / expected:

Check Verdict Why
agent-completion/truth-gate invalid_payload (policy.agent_login, policy.run_id null) The exact signature this PR fixes (#1130). The gate posts commit statuses with elevated permissions, so it is evaluated from main's pre-fix logic — it cannot reflect this branch's fix and will stay red until the fix lands on main. Self-referential deadlock; per #1130, main has no required status checks, so this is not merge-blocking.
PR Governance / Agent completion enforcement failure Same agent-completion machinery, same root cause.
Canonical issue and evidence failure Author-fixable, unrelated to the code. The check wants the mandated body sections ## Canonical issue, ## Outcome, ## Risk, ## Production evidence; this PR's body uses ## Problem / ## Fix / ## Verification. Adding those headings clears this one check.
gitleaks (working tree) failure Pre-existing false positive, not introduced here. square-access-token matched uv.lock:5129, which is a sha256: package hash (size = 401824), not a credential. This PR doesn't touch uv.lock; the finding exists on main.

Net: mergeable_state = unstable (mergeable, no required checks failing). Every red is either the self-referential gate this PR is written to break or pre-existing/format noise.

I have not merged or pushed: I'm limited to my own working branch and won't merge to protected main autonomously. Maintainer decision needed — this is the classic chicken-and-egg where the gate fix is blocked by the gate. Given main has no required checks and the substantive suite is green, the intended resolution is an admin/override merge (the fix's own logic then applies to subsequent PRs). Worth a second look at the truth table in the PR body before merging, since this CI run exercised main's logic rather than this branch's new declaresAgentContract path.


Generated by Claude Code

@groupthinking

Copy link
Copy Markdown
Owner Author

Flagging overlap so these get sequenced rather than colliding: this and #1151 both target the Agent Lock gate, but on different axes and different files, so they're complementary rather than competing.

Root cause for #1151, for context: the workflow greps head-SHA check runs for a check named "Agent Lock trusted publication", and nothing in the repo publishes it (verified via gh search code). Even with a publisher, .github/agent-lock/trusted-publishers.json has all three allowlists empty with custom_role_policy: "fail_closed", so the verifier returns trust_policy_unprovisioned. The gate shipped ahead of its dependency.

No conflict between the two — the +167 lines of tests here are a genuine improvement over the current coverage. Both can land in either order.

groupthinking pushed a commit that referenced this pull request Aug 5, 2026
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

Copy link
Copy Markdown
Owner Author

Closing in favour of #1377

This PR and #1364 were competing implementations of the same fix — scoping truth-gate applicability to real dispatch evidence. Measured against current main:

#1154 (this) #1364
Files changed 117 2
Lines +14,266 / −4,056 +335 / −44
Commits ahead 1,462 1
Commits behind main 55 0

The focused commit here (c8106c70b, "fix: scope agent gate applicability to real dispatch evidence") had the right diagnosis — its title names exactly the correct fix. But the branch also carries uv.lock, ~14k lines of test files, and merge commits from main, so the intended change is not separable for review.

#1364 implemented the same idea cleanly, and its commit is now cherry-picked into #1377, which additionally requires a linked issue before pull-side provenance arms the gate — completing the "real dispatch evidence" principle this PR set out to establish.

Closing this; #1377 is canonical. Branch retained and will be archive-tagged before any deletion.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Correction to the numbers above. The close stands, but I gave the wrong mechanism.

The "117 files / +14,266" figure came from a diff against an empty merge base, not a real measurement. The actual state:

$ git merge-base origin/main origin/groupthinking-fix-agent-gate-applicability
(empty — no common ancestor)

This branch shares no ancestry with current main — it predates the secret-purge force-push, so it is orphaned rather than drifted, and cannot be rebased onto main.

So the comparison I drew with #1364 was not really "focused vs. bloated" — it was "rooted in current main" vs. "not rooted in it at all." Your diagnosis in c8106c70b was correct either way, and it is the principle #1377 implements.

Disposition unchanged: #1377 is canonical.


Generated by Claude Code

groupthinking added a commit that referenced this pull request Aug 5, 2026
… audit 337 branches (#1377)

* test: stop asserting a CPython-version-specific rmtree detail

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

* fix(ci): stop arming truth gate from bare agent labels on linked issues

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>

* fix(ci): require a linked issue before arming the truth gate

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

* chore(maintenance): audit all 337 remote branches and add a prune script

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

* docs(maintenance): correct prune-script header counts to match the CSV

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

agent-completion/truth-gate is permanently unsatisfiable for any PR closing an [aw] issue

2 participants