fix(review): an incomplete secret scan holds the gate instead of closing the PR (#9082) - #9103
Conversation
…ing the PR (#9082) incompletePatchLessSecretScanFinding emitted code "secret_leak" whenever a patch-less file's content couldn't be fetched or verified within the scan cap (a Contents API 404/5xx, a >4MB file, a rate limit, or the fetch budget being exhausted) -- sharing the code with a REAL, matched credential. secret_leak is unconditional and breaker-exempt by design (CONCRETE_EVIDENCE_BLOCKER_CODES), so an unreadable file auto-closed a legitimate PR with no live re-check ever getting a chance to clear it. Confirmed live: 4 contributor PRs were closed this way (metagraphed#4600, loopover#7155/#7431/#8376). Gives the incomplete case its own code, secret_scan_incomplete, and routes it through evaluateGateCheckCore's no-deterministic-blocker branch -- mirroring ai_review_inconclusive's existing pattern exactly, and deliberately NOT the unconditional isEvaluationBlocker check: it only holds when nothing else already hard-blocked, so a real secret_leak on a DIFFERENT file in the same PR still fails the gate rather than being buried in this new hold. Re-evaluates automatically on the next sync/push, same as pre_merge_check_unresolved. secret_leak itself is completely unchanged: a genuine match still hard-blocks unconditionally, with no opt-in. Tests: a gate-level regression pinning the neutral-vs-failure split (including the real-leak-still-fails case with both finding codes present on one PR), the end-to-end maybeAddSecretLeakFinding wiring test rewritten to assert the new code and that secret_leak is absent, and the unit-level code assertion in patchless-secret-scan.test.ts. Closes #9082
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Logic backtestReplayed 0 historical case(s) for Backtest comparison:
|
❌ 3 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-26 17:51:44 UTC
Review summary Nits — 5 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
…et_scan_incomplete change #9082/#9103 (merged) added the secret_scan_incomplete neutral-hold branch to src/rules/advisory.ts's evaluateGateCheckCore, plus an updated secret_leak provenance comment, but never touched the hand-duplicated engine-package twin (packages/loopover-engine/src/advisory/gate-advisory.ts) -- the two are supposed to stay logically identical (see check-engine-parity.ts's GATE_DECISION_TWIN_PAIR). Confirmed the drift is live on main right now (the engine copy is missing the whole branch), which is exactly what stranded PR #9103's own CI: the version-bump-or-co-edit guard (checkGateDecisionVersionBump) correctly flagged the host-only edit at merge time. Mirrors both pieces into the engine twin and bumps @loopover/engine 3.15.0 -> 3.15.1 (the guard's own documented escape hatch for a one-sided fix landing after the fact), syncing package-lock.json and the miner's expected-engine.version pin alongside it -- same shape as the precedent manual bump in bc9e81a.
…c engine gate-decision twin (#9107) * fix(review): auto-clear manual-review label when AI-review lock contention resolves manual-review has no removal path today, so once applied it sticks forever even when its underlying cause was a purely transient infra artifact. Narrow scope: only auto-clears for the one case with live evidence of being transient -- aiReviewLockContendedResult's "AI review already in progress for this PR head" finding (a pass losing the AI-review lock race, #8999). Every other hold reason (guardrail, migration collision, breaker downgrades, a human's own hold, ...) still requires a maintainer to lift it. A (repo, PR)-keyed transient marker (24h TTL) records when contention fires; the next pass clears the marker once contention no longer shows up in that pass's gate warnings, and planAgentMaintenanceActions removes the label only when nothing else currently justifies the hold. Closes #9009 * fix(engine): sync the gate-decision twin after #9082's host-only secret_scan_incomplete change #9082/#9103 (merged) added the secret_scan_incomplete neutral-hold branch to src/rules/advisory.ts's evaluateGateCheckCore, plus an updated secret_leak provenance comment, but never touched the hand-duplicated engine-package twin (packages/loopover-engine/src/advisory/gate-advisory.ts) -- the two are supposed to stay logically identical (see check-engine-parity.ts's GATE_DECISION_TWIN_PAIR). Confirmed the drift is live on main right now (the engine copy is missing the whole branch), which is exactly what stranded PR #9103's own CI: the version-bump-or-co-edit guard (checkGateDecisionVersionBump) correctly flagged the host-only edit at merge time. Mirrors both pieces into the engine twin and bumps @loopover/engine 3.15.0 -> 3.15.1 (the guard's own documented escape hatch for a one-sided fix landing after the fact), syncing package-lock.json and the miner's expected-engine.version pin alongside it -- same shape as the precedent manual bump in bc9e81a.
Problem
incompletePatchLessSecretScanFinding(src/queue/patchless-secret-scan.ts) emitted codesecret_leakwhenever a patch-less file's content couldn't be fetched or fully verified within the scan cap — a Contents API 404/5xx, a file over 4MB, a rate limit, or the fetch concurrency budget being exhausted marks every eligible patch-less file this way. It shared the code with a genuinely matched credential.secret_leakis designed to be unconditional and breaker-exempt (CONCRETE_EVIDENCE_BLOCKER_CODESinsrc/settings/agent-actions.ts) — correct for a real leak, catastrophic for "we couldn't read the file." An unreadable file therefore auto-closed a legitimate contributor PR with zero live re-check ever getting a chance to clear it.Confirmed live (edge-nl-01 audit ledger): 4 contributor PRs were closed this exact way —
metagraphed#4600,loopover#7155,#7431,#8376.Fix
Gives the incomplete case its own code,
secret_scan_incomplete, routed throughevaluateGateCheckCore's no-deterministic-blocker branch — mirroring the existingai_review_inconclusivepattern exactly.Important correctness detail caught while building this: the obvious first approach — adding the new code to
isEvaluationBlocker's unconditional first check (same aspre_merge_check_unresolved) — is wrong. That check runs before any deterministic blockers are computed, so it would let one unreadable file'ssecret_scan_incompletefinding silently mask a real, confirmedsecret_leakon a different file in the same PR, downgrading a genuine credential leak to "held for a human" instead of hard-blocking it.ai_review_inconclusivealready solves exactly this class of problem by being checked only inside theblockers.length === 0branch — i.e. only relevant once nothing else has already hard-blocked. The new code follows that same shape, verified with a dedicated regression test (secret_leak+secret_scan_incompletetogether on one PR → stillfailure).secret_leakitself is completely unchanged — a genuine match still hard-blocks unconditionally, no opt-in, no downgrade.Also corrects a stale comment claiming
secret_leakis "produced ONLY by the flag-gated safety scan" — it's also produced unconditionally bymaybeAddSecretLeakFinding(#audit-3.4) regardless ofLOOPOVER_REVIEW_SAFETY.Tests
test/unit/gate-check-policy.test.ts):secret_scan_incompletealone → neutral hold;secret_leak+secret_scan_incompletetogether → stillfailurewithsecret_leakin blockers (the exact masking bug this PR avoids).test/unit/patchless-secret-scan.test.ts): themaybeAddSecretLeakFindingfetcher-rejection test rewritten to assert the new code is present andsecret_leakis absent.incompletePatchLessSecretScanFindingcode assertion updated.Validation
npx vitest run test/unit/gate-check-policy.test.ts test/unit/patchless-secret-scan.test.ts test/unit/rules.test.ts— 282/282 passed.secret_leakacross the repo (configured-gate-blocker-signals,agent-actions,safety-wiring,outcomes-wire,salvageability,backtest-logic-check-core,content-lane-wire) — 670/670 passed, no regressions.patchless-secret-scan.tsconfirmed directly; the other two sit well outside their files' only uncovered ranges).tsc --noEmit --incremental falseclean.main(pre-existing repo-wide state) — left unreformatted.Closes #9082