Skip to content

fix(ci): arm the truth gate only on an issue that declares a contract - #1426

Closed
groupthinking wants to merge 1 commit into
mainfrom
claude/clever-heisenberg-8k227t
Closed

fix(ci): arm the truth gate only on an issue that declares a contract#1426
groupthinking wants to merge 1 commit into
mainfrom
claude/clever-heisenberg-8k227t

Conversation

@groupthinking

Copy link
Copy Markdown
Owner

Canonical issue

Closes #1401

Outcome

agent-completion/truth-gate stops going permanently red on agent-branch pull requests that close an ordinary issue. #1377 fixed the case where the gate armed with no linked issue; this fixes the same defect one level down — arming on a linked issue that declares no agent contract.

 return login !== 'dependabot[bot]' &&
-  (issueDispatch || (pullProvenance && Boolean(selectedIssue)));
+  (issueDispatch ||
+    (pullProvenance && declaresAgentContract(selectedIssue)));

pullProvenance is true for any branch matching /^(?:agent|claude|codex|copilot|jules)[/-]/, and Boolean(selectedIssue) is true for any linked issue. So a claude/… branch closing an ordinary issue armed the gate, which then demanded policy.agent_login and policy.run_id — only ever populated from the frozen intent snapshot snapshot-agent-task-intent writes for dispatched agent tasks. An ordinary issue has no snapshot, so the verdict was permanently invalid_payload regardless of what the author did.

The comment above the return already stated the correct rule — provenance arms the gate once a linked issue exists "to verify against" — but Boolean(selectedIssue) tests only that an issue exists, not that it carries anything to verify against. declaresAgentContract is the same predicate issueDispatch already relies on, so both arms of the disjunction now require the thing the gate goes on to measure.

The collector was already diagnosing this correctly, reporting linked_issue_not_agent_task and missing_intent_snapshot. Only the applicability predicate ignored it.

Scope

  • Included:
    • .github/workflows/pr-checks.yml — the predicate in both copies of agentTaskApplicable() (lines 725 and 2051), plus a comment recording why presence is not the right test. The two copies remain byte-identical, which an existing test asserts.
    • tests/unit/test_agent_completion_gate.py — both assertion blocks.
  • Explicitly excluded:

Risk

  • Risk level: low
  • Failure mode: the gate resolves not_applicable for a PR that should be gated — specifically an agent PR linking an issue that declares no contract. That state was previously blocked-with-no-remedy rather than enforcing anything, so this narrows a permanently-failing check, not a working one. A PR linking a genuinely dispatched issue is gated exactly as before, because declaresAgentContract is the same function issueDispatch already uses. Binding a PR to a focused issue stays enforced by Canonical issue and evidence and PR Governance.
  • Rollback: revert this single commit. Workflow and test changes only — no runtime code path, no schema, no data migration.

Verification

Current head 39729d2.

  • Focused teststests/unit/test_agent_completion_gate.py: 112 passed, 89 subtests.
  • Fail-test — with pr-checks.yml reverted and the tests left as fixed, both assertion blocks fail (test_agent_applicability_requires_provenance_or_declared_contract and test_scheduled_scanner_detects_frozen_intent_changes). 2 failed / 110 passed. They pass with the change.
  • Full unit suitetests/unit: 8114 passed, 5 xpassed, 89 subtests, 0 failed (261s).
  • Workflow validityyaml.safe_load parses pr-checks.yml (5 jobs); Boolean(selectedIssue) now appears 0 times, declaresAgentContract(selectedIssue) 4 times (2 per copy), and the byte-identical-copies assertion passes.
  • Required CI — pending on this head.
  • Review threads resolved — no threads yet.

Test changes

Both blocks previously encoded the defect — a plain issue plus any provenance signal asserted true:

['branch + issue', {...base, head: {ref: 'codex/fix'}}, PLAIN, true],   // was

Each provenance signal (PR label, branch prefix, lock manifest, known agent author) now pins both directions: a contract-less issue must not arm, a contract-bearing one must. Added the exact shape that was red on #1400 — an agent-prefixed branch closing an ordinary issue.

Production evidence

Not applicable — CI configuration and tests only; no runtime surface, HTTP contract, or deployed artifact changes.

This PR cannot green its own gate. pull_request_target runs the workflow from the base branch, so the fix takes effect for everything else once merged, not here. #1377 hit exactly this and merged with the same status red. Evidence is the fail-test above.

Agent handoff


Generated by Claude Code

#1377 stopped `agentTaskApplicable()` arming on a branch-name prefix with no
linked issue. The same defect survived one level down: it armed on a linked
issue that declares no agent contract, and was just as unsatisfiable there.

`issueDispatch` was already correct — it requires the agent-task label *and*
`declaresAgentContract(selectedIssue)`. The second disjunct was not:

    (issueDispatch || (pullProvenance && Boolean(selectedIssue)))

`pullProvenance` is true for any branch matching `/^(?:agent|claude|codex|
copilot|jules)[/-]/`, and `Boolean(selectedIssue)` is true for any linked
issue at all. So a `claude/...` branch closing an ordinary issue armed the
gate, which then demanded `policy.agent_login` and `policy.run_id`. Those are
only ever populated from the frozen intent snapshot written by
`snapshot-agent-task-intent`, which runs on `issues` events for dispatched
tasks. An ordinary issue has no snapshot, so the verdict was permanently
`invalid_payload` no matter what the author did.

The comment above the return already stated the correct rule — provenance
arms the gate once a linked issue exists "to verify against" — but
`Boolean(selectedIssue)` tests only that an issue exists, not that it carries
anything to verify against. Use `declaresAgentContract(selectedIssue)`, the
same predicate `issueDispatch` relies on, so both arms require the thing the
gate goes on to measure. The collector already diagnosed this correctly,
reporting `linked_issue_not_agent_task` and `missing_intent_snapshot`; only
the applicability predicate ignored it.

This narrows a permanently-failing check rather than a working one. A PR
linking a genuinely dispatched issue is gated exactly as before. Binding a PR
to a focused issue stays enforced by `Canonical issue and evidence` and
`PR Governance`.

Both assertion blocks in the gate tests encoded the old behaviour — a plain
issue plus any provenance signal asserted `true`. Both now pin both
directions for every signal (PR label, branch prefix, lock manifest, known
agent author): a contract-less issue must not arm, a contract-bearing one
must. Added the exact shape that was red on #1400, an agent-prefixed branch
closing an ordinary issue. Reverting the workflow fails both blocks.

Closes #1401

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019baCDT5aP5Z66pLGBCE2Y6
@vercel

vercel Bot commented Aug 7, 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 Ready Ready Preview, v0 Aug 7, 2026 6:12pm

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Pull-request validation now requires linked issues to include both an agent run ID and agent login when agent-task checks are enabled.
    • Scheduled refreshes and evidence collection now apply the same validation requirements for more consistent results.

Walkthrough

The workflow now arms the agent-task truth gate from pull-request provenance only when the linked issue declares both required agent contract fields. The same rule applies in scheduled refresh and truth-gate evidence collection.

Changes

Agent contract gate

Layer / File(s) Summary
Require agent contract declarations
.github/workflows/pr-checks.yml
Both agentTaskApplicable checks now call declaresAgentContract(selectedIssue) instead of checking only for a linked issue.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • GRV-332 — Covers the same pr-checks.yml applicability change for linked agent contract fields.
  • GRV-319 — Addresses the same unsatisfiable truth-gate applicability condition.
  • GRV-84 — Relates to agent applicability and truth-gate evidence enforcement.
  • groupthinking/EventRelay#1130 — Earlier related work on strengthening agent applicability checks.

Possibly related PRs

Poem

A linked issue joins the queue,
But contracts must now come through.
Run ID, login, side by side,
Arm the gate only when supplied.
False alarms fade from view.

🚥 Pre-merge checks | ✅ 4 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Enforce Copilot Verification ⚠️ Warning GitHub activity for #1426 shows no submitted reviews and no Copilot actor with state APPROVED. Obtain and verify an explicit GitHub Copilot review with state APPROVED on this pull request; comments and human approvals do not satisfy this check.
Require Ai Unit Tests ⚠️ Warning PR #1426 lacks the required copilot-rabbit label. AI-related unit tests are committed in tests/unit/test_agent_completion_gate.py. Add the copilot-rabbit label to PR #1426, then rerun the required checks before merging.
Linked Issues check ❓ Inconclusive The workflow change matches #1401, but test coverage cannot be verified because tests/unit/test_agent_completion_gate.py is excluded by !tests/**. Include the excluded test file in review or provide reviewable evidence that both assertion blocks cover contract-less and contract-bearing issues.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The reported workflow and test updates directly support #1401, with no unrelated code or runtime changes identified.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly and concisely describes the primary truth-gate applicability fix.
Description check ✅ Passed The description covers the core template sections with clear scope, risk, verification, and handoff details, but omits Agent provenance despite the Claude Code attribution.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/clever-heisenberg-8k227t
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/clever-heisenberg-8k227t

Warning

Review ran into problems

🔥 Problems

These MCP integrations need to be re-authenticated in the Integrations settings: Sentry


Linked repositories: Public OSS repositories can only analyze public repositories installed in this organization. No linked repositories were analyzed; skipped groupthinking/uvai-skills.


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 7, 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 39729d2.
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 7, 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

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Same label-gate skip as #1381 and #1400: the auto-labeler runs after the PR opens, so the required-label gate evaluates an unlabelled PR every time. This is the documented retry.

Note for reviewers: agent-completion/truth-gate will go red on this PR and that is expected. pull_request_target runs the workflow from the base branch, so this fix cannot green its own gate — it takes effect for everything else once merged. The fail-test in the description is the evidence instead: reverting pr-checks.yml fails both assertion blocks in tests/unit/test_agent_completion_gate.py.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

CI status on 39729d2 — the one red check is the bug this PR fixes

agent-completion/truth-gate is ❌ invalid_payload. Expected, and not fixable here. pull_request_target runs the workflow from the base branch, so this PR is scored by the old predicate on main. The fix takes effect for every PR opened after it merges, never for itself. #1377 merged with this identical status red for the same structural reason.

Note the verdict has already narrowed compared to #1400's:

  "collection_errors": [
    "incomplete_linked_issue_contract",
-   "linked_issue_not_agent_task",
    "missing_intent_snapshot",
    "missing_agent_run_id",
    "missing_agent_login"
  ]

Same invalid_payload outcome either way — the point stands that an ordinary linked issue has no intent snapshot, so policy.agent_login and policy.run_id can never be populated.

Since the gate cannot demonstrate its own fix, the fail-test is the evidence:

pr-checks.yml reverted with the fix
test_agent_applicability_requires_provenance_or_declared_contract ❌ FAIL
test_scheduled_scanner_detects_frozen_intent_changes ❌ FAIL
suite total 110 passed / 2 failed 112 passed, 89 subtests

Both assertion blocks catch the regression independently.

Everything else on this head

Check Result
lint-python, guards, validate, bandit, gitleaks (working tree), dependency-review
Canonical issue and evidence, Agent completion enforcement
build, test, lint-frontend, trivy, npm-audit, python-safety, Security Scans, Coverage ⏳ running
agent-completion/truth-gate ❌ structurally cannot pass here (above)

Local on this head: tests/unit 8114 passed, 5 xpassed, 89 subtests, 0 failed; yaml.safe_load parses the workflow; the two agentTaskApplicable() copies remain byte-identical.

No fix pushed for the red check, by design. Will update if any still-running check fails for a real reason.


Separate, unresolved: CodeRabbit cannot review anything in this repo

Third PR in a row where auto-review skipped on the required-label gate, and here the explicit @coderabbitai review retry did not take either (two more skip runs, no acknowledgement). #1381 and #1400 needed manual retries, and #1400's then hit the account review rate limit.

The cause is a timing race: the label job applies labels after the PR opens, so CodeRabbit's gate always evaluates an unlabelled PR. Net effect is that a security PR like #1381 sat unreviewed until someone manually poked it.

Not changing it here — the gate is org/repository UI config (Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)), so it is a review-policy decision, not a code fix. Worth deciding between: dropping the required-label filter, or having the labeler run on pull_request.opened before CodeRabbit evaluates. Happy to open a separate issue if you want it tracked.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- PR diff summary ---'
git diff --stat HEAD^ HEAD
echo '--- Changed files ---'
git diff --name-only HEAD^ HEAD

echo '--- Workflow outline ---'
ast-grep outline .github/workflows/pr-checks.yml --items all 2>/dev/null || true

echo '--- agentTaskApplicable occurrences and context ---'
rg -n -C 35 'agentTaskApplicable|declaresAgentContract|Boolean\(selectedIssue\)' .github/workflows/pr-checks.yml

echo '--- Test outline ---'
ast-grep outline tests/unit/test_agent_completion_gate.py --items all 2>/dev/null || true

echo '--- Relevant test cases ---'
rg -n -C 25 'test_agent_applicability_requires_provenance_or_declared_contract|test_scheduled_scanner_detects_frozen_intent_changes|agentTaskApplicable|linked_issue_not_agent_task' tests/unit/test_agent_completion_gate.py

echo '--- Evaluator applicability contract ---'
fd -a '^agent_completion_gate\.py$' . -x sh -c 'echo "--- $1 ---"; rg -n -C 12 "def evaluate|applicable|not_applicable" "$1"' sh {}

Length of output: 32192


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- Locate relevant files ---'
fd -a -i 'test_agent_completion_gate\.py|agent_completion_gate\.py' . || true

echo '--- Full PR diff ---'
git diff --find-renames --unified=80 HEAD^ HEAD -- \
  .github/workflows/pr-checks.yml \
  ':(glob)**/test_agent_completion_gate.py' \
  ':(glob)**/agent_completion_gate.py'

echo '--- Verify agentTaskApplicable copy equivalence ---'
python3 - <<'PY'
from pathlib import Path
text = Path(".github/workflows/pr-checks.yml").read_text()
needle = "function agentTaskApplicable(pull, selectedIssue) {"
starts = [i for i in range(len(text)) if text.startswith(needle, i)]
print(f"copies={len(starts)}")
if len(starts) == 2:
    def extract(start):
        depth = 0
        began = False
        for i in range(start, len(text)):
            if text[i] == "{":
                depth += 1
                began = True
            elif text[i] == "}" and began:
                depth -= 1
                if depth == 0:
                    return text[start:i + 1]
    first, second = map(extract, starts)
    print(f"byte_identical={first == second}")
    print(f"first_length={len(first)} second_length={len(second)}")
PY

echo '--- Test definitions and applicability fixtures ---'
fd -a '^test_agent_completion_gate\.py$' . -x sh -c '
  echo "--- $1 ---"
  rg -n -C 45 \
    "test_agent_applicability_requires_provenance_or_declared_contract|test_scheduled_scanner_detects_frozen_intent_changes|agentTaskApplicable|PLAIN|CONTRACT" \
    "$1"
' sh {}

echo '--- Evaluator applicability behavior ---'
fd -a '^agent_completion_gate\.py$' . -x sh -c '
  echo "--- $1 ---"
  rg -n -C 18 "def evaluate|not_applicable|policy.*applicable" "$1"
' sh {}

Length of output: 40459


@groupthinking Review complete. I found no blocking defects.

The predicate now requires an issue contract for both applicability paths. Both embedded agentTaskApplicable() copies are byte-identical. The unit tests cover every pull-side provenance signal with both contract-less and contract-bearing issues. The evaluator returns not_applicable when the collector sets policy.applicable to false.

I also noted the expected base-workflow behavior of pull_request_target for this self-fixing PR.


✏️ Learnings added
Learnt from: groupthinking
URL: https://github.com/groupthinking/EventRelay/pull/1426

Timestamp: 2026-08-07T18:12:58.905Z
Learning: In `.github/workflows/pr-checks.yml`, `agent-completion/truth-gate` runs through `pull_request_target`, so it evaluates the workflow from the base branch. A PR that changes this workflow cannot make its own gate result reflect the change. Validate such a change with focused fail-tests and verify the effect after merge.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@groupthinking The agent-completion/truth-gate failure is expected for this PR. The base-branch pull_request_target workflow evaluates the old applicability predicate. The focused fail-tests provide valid evidence for this workflow-only change.

The explicit review command did complete. The review found no blocking defects.

The required-label configuration has a real ordering problem. A labeler that runs on pull_request.opened cannot guarantee that CodeRabbit evaluates after the label exists. The reliable fix is to remove the required-label filter, or to use a review trigger that occurs only after the label event. This should be tracked as a separate repository or organization configuration issue.

🐇


✏️ Learnings added
Learnt from: groupthinking
URL: https://github.com/groupthinking/EventRelay/pull/1426

Timestamp: 2026-08-07T18:13:13.508Z
Learning: In the EventRelay repository, CodeRabbit's required-label review filter can race with the PR auto-labeler because the label is applied after the PR opens. Do not assume a `pull_request.opened` labeler runs before CodeRabbit evaluates. If reviews must run reliably, remove the required-label filter or trigger review after the label event.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

Copy link
Copy Markdown
Owner Author

Correction + review outcome

CodeRabbit review is complete and clean: "Review complete. I found no blocking defects."

It independently verified the three things that matter here — the predicate requires an issue contract on both applicability paths, the two embedded agentTaskApplicable() copies are byte-identical (it recomputed this with its own brace-matching script), and the tests cover every pull-side provenance signal in both the contract-less and contract-bearing directions. It also independently confirmed the pull_request_target base-branch behaviour that makes this PR's own gate unfixable.

Correcting my previous comment: I wrote that the explicit @coderabbitai review retry "did not take either." That was wrong — it did take, it was just slower to acknowledge than the two skip-runs that landed in between. Apologies for the noise.

The underlying point stands and is unchanged: auto-review still skipped on the required-label gate, as it did on #1381 and #1400. Every PR in this repo needs a manual retry to get reviewed, because the label job runs after the PR opens. That is what left the #1381 security PR unreviewed until it was poked by hand, and it remains a repo/org UI setting worth fixing.

So the review coverage question on this PR is resolved; only the merge decision remains.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@groupthinking Acknowledged. The explicit review command completed successfully, and the review found no blocking defects.

The review coverage for this PR is complete. The expected agent-completion/truth-gate result does not change the review outcome because pull_request_target evaluates the base-branch workflow.

The required-label timing race remains a separate repository or organization configuration issue. It does not block this PR's code change.

🐇

You are interacting with an AI system.

@linear-code
linear-code Bot marked this pull request as ready for review August 7, 2026 18:16

Copy link
Copy Markdown
Owner Author

Closing — #1409 already implements #1401, and implements it better

PR Governance caught this correctly: "Issue #1401 already has another open implementation PR: #1409". #1409 was opened at 14:55; I opened this at 18:11 without checking for an existing implementation first. That's my error — #1409 has priority on age alone.

More importantly, it is the better fix, and it identifies a real flaw in the approach I specified on #1401. I verified both of its claims against the workflow rather than taking them on trust:

1. My fix leaves an unsatisfiable case standing. It reduces the predicate to declaresAgentContract && (agentTaskLabel || pullProvenance). An issue that declares a run id and login but carries no agent-task/mcp-agent label still arms the gate whenever the PR has provenance. But pr-checks.yml:144 is:

if (disposition !== 'snapshot' || !hasAgentTaskLabel) {
  return;
}

No label → no snapshot → missing_intent_snapshot → permanently invalid_payload. Exactly the defect I was fixing, one branch over. My test suite did not catch it: ['unlabelled issue with contract', …] uses a human PR, so it passes under my change while the agent-branch equivalent would not. #1409's return login !== 'dependabot[bot]' && issueDispatch; closes it.

2. declaresAgentContract was itself too weak. It checked only login and run id, while the snapshot job's complete predicate (pr-checks.yml:223-229) additionally requires objective, acceptance criteria, a declared file scope or approved unrestricted scope, and a checked pre-dispatch confirmation. So an issue with the label and login+run_id but nothing else arms the gate and still gets no snapshot — the same livelock a third level down. #1409 mirrors the full predicate and tests each missing field.

3. #1409 also closes a hole in the opposite direction that neither I nor #1401 spotted: hasResponse in the snapshot job did not strip backticks while the gate's declared did, so a field left unfilled inside a code span (`_No response_`) read as a real answer to the snapshot job and as absent to the gate. The snapshot gets written, the gate stays not_applicable, and a malformed dispatch skips the check entirely. That is the more serious of the two directions.

So this PR is superseded on every axis. Closing it rather than leaving two competing implementations to block each other's governance check.

#1409 is the one to review and merge. Its Deviation from the fix proposed on #1401 section is correct and I endorse it — the issue's proposed one-line change was insufficient, and #1409 was right not to follow it literally.

Nothing from this branch needs carrying over; #1409's tests are a strict superset of mine.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blocked by a competing PR — and #1409's predicate is the stronger one

PR Governance and Canonical issue and evidence are both red here for one reason: Issue #1401 already has another open implementation PR: #1409. Two sessions independently implemented #1401. That collision needs a human pick, so this is a decision note, not a fix.

Having read both against pr-checks.yml at this head, #1409's predicate is correct and this one is not. The argument #1409 makes in its "Deviation" section holds up against the source.

The two predicates

// #1426 (this PR), pr-checks.yml:736
issueDispatch || (pullProvenance && declaresAgentContract(selectedIssue))

// #1409
issueDispatch

with issueDispatch = carriesLabel(issue, ['agenttask','mcpagent']) && declaresAgentContract(issue) (line 698).

Factoring this PR's disjunction:

declaresAgentContract && (agentTaskLabel || pullProvenance)

So relative to #1409, the marginal cases this PR arms on are exactly:

declaresAgentContract(issue) and pullProvenance and not agentTaskLabel

Every one of those marginal cases is unsatisfiable

declaresAgentContract (line 628) parses only the issue body — it looks for non-empty Agent Run ID / Run ID and Agent Login sections. It never inspects labels.

The snapshot the gate scores against is written by snapshot-agent-task-intent, which returns early at line 144:

if (disposition !== 'snapshot' || !hasAgentTaskLabel) {
  return;
}

An issue that declares a run id and login in its body but does not carry agent-task/mcp-agent therefore never gets a snapshot. Arm the gate on it and the collector demands policy.agent_login and policy.run_id that were never written → missing_intent_snapshotinvalid_payload, permanently, with no action available to the author.

That is the same defect class #1401 was filed for, narrowed by one level rather than closed. The added disjunct's entire marginal contribution is the configuration where the gate can never be satisfied — it arms only where arming is guaranteed to fail.

The PR body here argues that declaresAgentContract is "the same predicate issueDispatch already relies on, so both arms now require the thing the gate goes on to measure." That's half the requirement: the gate measures against the snapshot, and the snapshot's precondition is the label, not the body declaration. Requiring the body declaration without the label requires the evidence's description while dropping the condition under which the evidence is produced.

Recommendation

Close this in favour of #1409, whose issueDispatch-only rule resolves the marginal cases to not_applicable and closes #1401 completely. #1409 also carries the operator-doc update this PR omits.

The one thing this PR has that #1409 does not is being out of draft. If #1409 is preferred, it needs marking ready for review; its mergeable_state is currently blocked.

Either way one of the two must close before Canonical issue and evidence can go green on the survivor — the check fails both by construction while both are open.

No code was changed by this run; the choice between two of your own PRs is yours.


Generated by Claude Code

Copy link
Copy Markdown
Owner Author

Blocked: duplicate canonical issue with #1409

Canonical issue and evidence is red on head 39729d2, and it is not a flake:

##[error]Issue #1401 already has another open implementation PR: #1409

#1409 and this PR both declare Closes #1401 and both fix the same defect in agentTaskApplicable(). The gate's competing-PR scan uses state: "open" with no draft filter, so #1409 being a draft does not exempt it — this PR cannot go green while #1409 stays open, and #1409 cannot merge while it is a draft. Two required checks are red as a result (Canonical issue and evidence, and PR Governance downstream of it).

The handoff checkbox here — "No competing PR implements the same issue" — is incorrect. #1409 was opened at 14:55Z, this PR at 18:11Z. #1409's own identical checkbox was accurate when written; the collision was introduced by this PR.

The two fixes are not equivalent

#1409 (14:55Z, draft) #1426 (18:11Z, ready)
Rule issueDispatch alone — agentTaskLabel && declaresAgentContract issueDispatch || (pullProvenance && declaresAgentContract(selectedIssue))
Diff 3 files, +412/−135 (workflow, tests, operator doc) 2 files, +77/−23 (workflow, tests)

#1409 is the stricter of the two and documents why it deviates from the fix #1401 literally proposed — the one this PR implements. Its argument survives checking against the workflow source: pr-checks.yml:144 returns early unless the issue carries agent-task/mcp-agent, so an issue that declares a run id and login in its body but lacks that label gets no snapshot written. Under this PR's rule such an issue still arms the gate via pullProvenance, and the verdict is then permanently missing_intent_snapshotinvalid_payload — the same unsatisfiable state both PRs exist to remove, just reached by a narrower path. #1409's rule closes it; this one leaves it standing.

That is a residual hole, not a regression: this PR is a strict improvement on today's Boolean(selectedIssue). But it does not fully retire the defect.

Recommendation

Keep #1409, close this PR as a duplicate. It is older, strictly stronger, and additionally updates the operator doc's Applicability section, which this PR does not touch.

Staged, not executed — picking between two live implementations is a maintainer call, and #1409's own handoff raises a third option (retire the truth-gate apparatus wholesale per #1408) that would moot both:

gh pr close 1426 --comment "Duplicate of #1409 on canonical issue #1401"
gh pr ready 1409

If #1426's smaller diff is preferred instead, invert it — close #1409 first, then re-run Canonical issue and evidence here; it should go green with no code change, since the only finding was the collision.

Neither PR can green its own agent-completion/truth-gate: pull_request_target runs the workflow from the base branch. Both bodies note this, and it does not block — the check is absent from the required list in MERGE_POLICY.md (gate 2).


Generated by Claude Code

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.

fix(ci): truth gate arms on any linked issue, not one that declares an agent contract — still unsatisfiable for agent-branch PRs

2 participants