feat(orb): gate determinism — deterministic test-evidence facts, decision confidence, and replayable time (#8833, #8834, #9028)#9256
Conversation
Logic backtestReplayed 0 historical case(s) for Backtest comparison:
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
❌ 17 Tests Failed:
View the top 3 failed test(s) by shortest run time
View the full list of 5 ❄️ flaky test(s)
To view more test analytics, go to the Test Analytics Dashboard |
|
Caution 🛑 LoopOver review result - fixes requiredReview updated: 2026-07-27 10:42:32 UTC
Review summary Nits — 5 non-blocking
Why this is blocked
📋 Copy for AI agents — paste into your coding agentCI checks failing
Decision drivers
Context & advisory signals — never blocks the verdict
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. Decision record
🟩 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.
|
…r contradicts (#8833) Whether a PR carries test-path evidence is a deterministic fact — signals/test-evidence.ts's isTestPath owns it, and slop.ts's missing_test_evidence finding already decides its severity. The reviewer prompt nonetheless fed the same fact as free text and let the model re-decide blocker-vs-nit, so a model could close a PR on a claim the engine had already answered the other way. Extends the existing parse-time fact-authority pattern (demoteCiClaimBlockers #8845, demoteEvidenceAbsenceBlockers #8961) to this criterion: a whole-PR "no tests were added" blocker is demoted to a nit when the classifier confirms the PR does change test paths. Fires only in the arm where the claim is provably a fact error — a genuine test-absence blocker on a genuinely test-free PR is untouched — and demotes rather than drops, so the observation still reaches the human. Coverage-DEPTH claims that narrow to a specific target ("no tests for the nullish branch") are deliberately excluded: the classifier cannot check those, so they remain the model's judgment to make and keep blocking.
…e replayable (#9028) gate.requireFreshRebaseWindow compared the base branch's tip against an inline Date.now() read inside maybeForceFreshRebase. Time is a decision INPUT, and nothing recorded which instant the comparison used — so re-deriving such a decision later could silently reach the opposite answer purely because the wall clock had moved, and report it as a match. The decision pass now takes ONE Date.now() reading, records it into decision_replay_inputs.replay_json as `clock`, and passes it to every clock-dependent rule instead of each calling the clock itself. Both staleness rules move to a pure, clock-injected module: isWithinFreshRebaseWindow takes the instant explicitly, and isBaseStaleByAheadBy is stated as the commit-count comparison it is — provably instant-independent, not merely assumed so. replayDecision gains a stage-0 `clock` check: replaying at the recorded instant (the CLI default) is bit-exact, while naming a different instant reports a `clock` divergence rather than silently certifying a re-derivation that never reproduced the original evaluation. Records written before this change carry no instant, so the stage is skipped rather than guessed. The CLI exposes it as `--at <epoch ms>`. Also corrects the decision_replay_inputs migration reference in two doc comments (0181 is alert_dedup_claims; the table is created in 0182).
…eement (#8834) Verbalized confidence alone is poorly calibrated — #8845 already had to stop reading an absent confidence as certainty. Sampling-based consistency is the better-behaved signal, and the risk-control literature this epic builds on finds two samples capture most of the benefit. Scores inter-run agreement across the reviewer stances the engine ALREADY produces (#8229's reviewerVotes) and folds it into the verbalized confidence, at zero additional AI spend. The combined score multiplies the two so it is monotonically below either input — a judgment is only as trustworthy as both how sure the judge said it was and how reproducibly the judges reached it, which is the property an abstention threshold depends on. A lone run is recorded as UNCORROBORATED at a 0.5 agreement floor rather than fabricated unanimity, so a single-reviewer or budget-degraded review records a strictly lower confidence than a genuinely corroborated one. Zero samples refuses to invent a score at all. The signal rides to DecisionRecord.aiAgreement (schema v5) so every decision joins the risk-control calibration set with its reproducibility attached. Deliberately ADDITIVE: it does not re-route the gate. Disagreement already routes to a hold today — differing stances ARE the ai_review_split finding, which blocks or holds via the existing confidence floor — so a second parallel route would double-count the same evidence instead of measuring it.
…ent field (#8834) The DECISION_RECORD_SCHEMA_VERSION bump to v5 moves the pinned version assertions in the backfill bundle and queue decision-record suites. The #9124 record test additionally carries the agreement on its cached finding and asserts it threads through to the record, mirroring exactly how that test already proves modelIds/promptDigest pass through rather than being re-derived.
2257679 to
244ed8a
Compare
Summary
Three related pieces of the Phase-3/4 determinism work: shrink what the LLM is allowed to decide (#8833), measure how reproducibly it decided the rest (#8834), and make the replay harness cover time (#9028).
Two of the three are deliberately scoped down — see Scoping decisions below. Only #9028's requirement 2 and #8833's next extraction step are claimed; both parent issues stay open with the remainder named explicitly.
#8833 — one more objectively-checkable criterion leaves LLM judgment
Whether a PR carries test-path evidence is a fact:
signals/test-evidence.ts'sisTestPathowns it, andslop.ts'smissing_test_evidencefinding already decides its severity. The reviewer prompt nonetheless fed the same fact as free text and let the model re-decide blocker-vs-nit — the exact double-judging the issue's own input inventory flagged ("still fed as free text, model re-decides blocker-vs-nit").Extends the established parse-time fact-authority pattern (
demoteCiClaimBlockers#8845,demoteEvidenceAbsenceBlockers#8961) to this criterion. A whole-PR "no tests were added" blocker is demoted to a nit when the classifier confirms the PR does change test paths.Two properties worth calling out, both mirroring the existing demotions:
"no tests for the nullish branch"/"no test covers the error path"narrow to a specific target; the path classifier cannot check those, so they remain the model's judgment to make and keep blocking. Only existence claims are demoted.#8834 — a per-decision confidence signal
The persistence half of this issue already shipped (
DECISION_RECORD_SCHEMA_VERSIONv2 credits #8834 foraiConfidence). What was missing is the agreement signal.scoreJudgmentAgreementscores inter-run agreement across the reviewer stances the engine already produces (#8229'sreviewerVotes) and folds it into the verbalized confidence. The combined score multiplies the two, so it is monotonically below either input — a judgment is only as trustworthy as both how sure the judge said it was and how reproducibly the judges reached it, which is the property an abstention threshold depends on.DecisionRecord.aiAgreement(schema v5) so every decision joins the risk-control calibration set with its reproducibility attached.ai_review_splitfinding, which blocks or holds via the existing confidence floor. A second parallel route would double-count the same evidence rather than measure it.#9028 — wall-clock capture (requirement 2)
gate.requireFreshRebaseWindowcompared the base branch's tip against an inlineDate.now()read insidemaybeForceFreshRebase. Time is a decision input, and nothing recorded which instant the comparison used — so re-deriving such a decision later could reach the opposite answer purely because the wall clock had moved, and report it as a match.Date.now()reading, records it intodecision_replay_inputs.replay_jsonasclock, and passes it to every clock-dependent rule instead of each calling the clock itself.isWithinFreshRebaseWindowtakes the instant explicitly;isBaseStaleByAheadByis stated as the commit-count comparison it is.replayDecisiongains a stage-0clockcheck: replaying at the recorded instant (the CLI default) is bit-exact, while naming a different instant reports aclockdivergence rather than silently certifying a re-derivation that never reproduced the original evaluation. Pre-Replay v2: re-query action-match mode + wall-clock capture for staleness rules #9028 records carry no instant, so the stage is skipped rather than guessed. Exposed asreplay-decision.ts --at <epoch ms>.Honest finding on the second named rule:
staleBaseAheadByThresholdreads no clock at all — it is a commit-count comparison (aheadBy >= threshold), so wall-clock capture does not apply to it. Rather than pretend otherwise, it is stated as a pure function and the suite pins its instant-independence (same inputs, wildly different instants, identical answer). That is the correct analogue of "bit-exact at the recorded instant" for a rule that has no instant. Note this also means neither staleness rule is fully replayable yet for a second reason outside this issue's scope: both make live GitHub reads (fetchLiveBaseBranchAdvancedAt,fetchBaseAheadBy) that are not captured either.Scoping decisions
REVIEW_PROMPT_VERSIONbump and is the largest single piece), the size-vs-norm and base-staleness halves of item 2, item 3 (scanner-confirmed CVE/secret/lockfile facts by rule — REES returns only free-textpromptSectiontoday, so this needs a structured findings channel across a separately-deployed service), item 4 (explicit"unknown"in the finding schema), item 5 (screenshot-vision laundering). The full rubric-shrink + per-dimension isolated judge calls the issue describes is a multi-PR refactor; a speculative sweep here would have been unbounded.src/review/judgment-agreement.ts.--requery) is blocked by the issue's own text on persisting full prompt text alongsidepromptDigest— an unmade privacy/row-size decision (prompt text embeds the diff; it likely needs another private sibling table with retention bounds). The current CLI also documents no-requery as a structural invariant, so inverting it is a deliberate contract change that should not ride along with an unrelated fix. Requirement 2 is fully implemented.Validation
Run from a clean worktree at
origin/main(32c7d5a):git diff --check— cleannpx tsc --noEmit -p tsconfig.json --incremental false— 0 errorsnpm run db:migrations:check/db:schema-drift:check/cf-typegen/selfhost:env-reference/ui:openapi:check/ui:openapi:settings-parity/engine-parity:drift-check/docs:drift-check/manifest:drift-check/command-reference:check/coverage-boltons:check/import-specifiers:check/branding-drift:check— all pass, no generated-artifact drift (regenerating produced an empty diff)judgment-agreement(7 new),decision-replay(19, 7 new),ai-review(247),ai-review-advisory(60, 1 new),decision-record(32),queue-2(125),gate-golden-corpus(17),advisory-live-parity,risk-control,counterfactual-replay-coretest/unitsweep (1190 files / ~22.5k tests) diffed against a pristineorigin/mainworktree via the JSON reporter: zero regressions. The 16 remaining failures are byte-identical to the main baseline (config-templates,engine-parity-fixtures,linked-issue-label-propagation-fetch,loopover-engine-scaffold,predicted-gate-engine,queue-5,reputation-wiring×4,salvageability,selfhost-metrics,selfhost-pg-retention×2,worker-entry-boundary).Not run (per maintainer instruction to keep this to targeted tests + typecheck): full
npm run test:ci,test:coverage,test:workers,ui:*,npm audit. CI covers these.No migration is added —
replay_jsonandrecord_jsonare JSON columns, soclockandaiAgreementneed no schema change. Next free migration number remains 0195.Pre-existing failure, not from this PR
npm run replay-runner-manifest:checkreports drift onpackage-lock.json. This branch touches neitherpackage-lock.jsonnorscripts/replay-runner-image-manifest.json— both are byte-identical toorigin/main(git diff origin/mainis empty for both), so the check fails identically on main today. Deliberately not regenerated here: it is a maintainer-owned attestation manifest (#9214) and refreshing it is an unrelated change that should not ride along.Safety
nits, which already flows through the existing sanitizer.CHANGELOG.md,site/,CNAME, orlovablechanges. All edits are insidewantedPaths(src/,test/,scripts/).staleness-clock.tsandjudgment-agreement.tsare new host-only files, so the engine-parity twin discovery correctly does not require an engine counterpart (verified:engine-parity:drift-checkpasses).evaluateGateCheckand its machine-enforced engine twin are not touched — no gate-decision parity surface is involved.Advances #8833
Advances #8834
Advances #9028