Summary
The pre-submission gate predictor (buildPredictedGateVerdict, src/rules/predicted-gate.ts) tells a contributor a PR will pass, while the live gate one-shot auto-closes it, on a repo whose public .gittensory.yml sets gate.mergeReadiness: block and leaves gate.linkedIssue unset. The predictor's whole contract is to compute the same verdict the live gate would, so this false optimism directly causes the loss it exists to prevent.
Area
MCP
Expected behavior
The composite merge-readiness gate forces the linked-issue sub-gate to block — applyMergeReadinessGate (src/rules/advisory.ts) sets linkedIssueGateMode = composite when mergeReadinessGateMode is on. The live review path collects linked-issue evidence whenever merge-readiness is enabled:
// src/queue/processors.ts — shouldCollectLinkedIssueEvidence
return settings.requireLinkedIssue || settings.linkedIssueGateMode !== "off" || mergeReadinessGateEnabled(settings);
so the live gate creates the missing_linked_issue finding and FAILS the PR. The predictor must surface the same finding under mergeReadiness so its verdict matches.
Actual behavior
The predictor derives the flag from gate.linkedIssue alone:
const requireLinkedIssue = gate.linkedIssue !== null && gate.linkedIssue !== "off";
On a mergeReadiness:block repo with linkedIssue unset (→ null), requireLinkedIssue is false, so addPullRequestFindings never creates the missing_linked_issue finding (advisory.ts: if (pr.linkedIssues.length === 0 && requireLinkedIssue)). evaluateGateCheck then runs with linkedIssueGateMode forced to block by the merge-readiness override but has no finding to act on, and the predictor returns success. The contributor sees green via gittensory_predict_gate, submits, and the live gate auto-closes the PR.
Reproduction
Predict a gate for a repo manifest { gate: { mergeReadiness: "block" } } with a PR body that links no issue (linkedIssues: []) and no matching issue in the snapshot → the predictor returns conclusion: "success" with no missing_linked_issue blocker, whereas the live gate fails on exactly that blocker.
Validation
Fix derives requireLinkedIssue from the merge-readiness term as well, mirroring shouldCollectLinkedIssueEvidence. Covered by test/unit/predicted-gate.test.ts; the new regression test fails before the fix (predicts success) and passes after, and the no-gate arm pins the false/false branch. Full local npm run test:ci green.
Summary
The pre-submission gate predictor (
buildPredictedGateVerdict,src/rules/predicted-gate.ts) tells a contributor a PR will pass, while the live gate one-shot auto-closes it, on a repo whose public.gittensory.ymlsetsgate.mergeReadiness: blockand leavesgate.linkedIssueunset. The predictor's whole contract is to compute the same verdict the live gate would, so this false optimism directly causes the loss it exists to prevent.Area
MCP
Expected behavior
The composite merge-readiness gate forces the linked-issue sub-gate to block —
applyMergeReadinessGate(src/rules/advisory.ts) setslinkedIssueGateMode = compositewhenmergeReadinessGateModeis on. The live review path collects linked-issue evidence whenever merge-readiness is enabled:so the live gate creates the
missing_linked_issuefinding and FAILS the PR. The predictor must surface the same finding undermergeReadinessso its verdict matches.Actual behavior
The predictor derives the flag from
gate.linkedIssuealone:On a
mergeReadiness:blockrepo withlinkedIssueunset (→null),requireLinkedIssueisfalse, soaddPullRequestFindingsnever creates themissing_linked_issuefinding (advisory.ts:if (pr.linkedIssues.length === 0 && requireLinkedIssue)).evaluateGateCheckthen runs withlinkedIssueGateModeforced toblockby the merge-readiness override but has no finding to act on, and the predictor returnssuccess. The contributor sees green viagittensory_predict_gate, submits, and the live gate auto-closes the PR.Reproduction
Predict a gate for a repo manifest
{ gate: { mergeReadiness: "block" } }with a PR body that links no issue (linkedIssues: []) and no matching issue in the snapshot → the predictor returnsconclusion: "success"with nomissing_linked_issueblocker, whereas the live gate fails on exactly that blocker.Validation
Fix derives
requireLinkedIssuefrom the merge-readiness term as well, mirroringshouldCollectLinkedIssueEvidence. Covered bytest/unit/predicted-gate.test.ts; the new regression test fails before the fix (predicts success) and passes after, and the no-gate arm pins the false/false branch. Full localnpm run test:cigreen.