Skip to content

feat(orb): gate determinism — deterministic test-evidence facts, decision confidence, and replayable time (#8833, #8834, #9028)#9256

Merged
JSONbored merged 4 commits into
mainfrom
fix/8833-8834-9028-gate-determinism
Jul 27, 2026
Merged

feat(orb): gate determinism — deterministic test-evidence facts, decision confidence, and replayable time (#8833, #8834, #9028)#9256
JSONbored merged 4 commits into
mainfrom
fix/8833-8834-9028-gate-determinism

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

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'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 — 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:

  • It fires only where the claim is provably a fact error. A genuine test-absence blocker on a genuinely test-free PR keeps its severity — the rule never weakens a true finding.
  • Coverage-depth claims are deliberately excluded. "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_VERSION v2 credits #8834 for aiConfidence). What was missing is the agreement signal.

scoreJudgmentAgreement scores inter-run agreement across the reviewer stances the engine already produces (#8229's reviewerVotes) 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.

  • 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 corroborated one. Zero samples refuses to invent a score at all. (Same "silence is not certainty" discipline fix(orb): enforce the model/fact boundary — CI claims demoted at parse, silence is not certainty #8845 established.)
  • 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 → hold" already happens: differing stances are the ai_review_split finding, 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.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 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; isBaseStaleByAheadBy is stated as the commit-count comparison it is.
  • 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. 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 as replay-decision.ts --at <epoch ms>.

Honest finding on the second named rule: staleBaseAheadByThreshold reads 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

Issue Claimed Deliberately deferred, and why
#8833 Advances The issue's own comment splits the remainder into five items, "each its own PR". This lands the test-evidence half of item 2. Deferred: item 1 (structured verifiable blocker schema — needs a REVIEW_PROMPT_VERSION bump 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-text promptSection today, 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.
#8834 Advances The paid rotated-exemplar sampling is NOT implemented. The issue asks for N=2–3 runs of the same judge with exemplars rotated out of the golden corpus. That is a strictly better signal than two different models voting once each — it isolates the judge's own reproducibility instead of confounding it with two models' differing priors — but every extra run is a real per-review AI charge on every reviewed PR, and this engine pays that bill in production. A default-on N-sample pass would multiply AI spend per review. What landed is the half that costs nothing: scoring the samples the dual review already runs, which the issue's own citation says captures most of the benefit ("two samples capture most of the benefit"). The extra-sampling half needs a budget decision and a flag defaulting OFF; it is not stubbed, so there is no unreachable code — see the closing note in src/review/judgment-agreement.ts.
#9028 Advances Requirement 1 (--requery) is blocked by the issue's own text on persisting full prompt text alongside promptDigest — 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 — clean
  • npx tsc --noEmit -p tsconfig.json --incremental false0 errors
  • npm 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)
  • Targeted suites, all green: 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-core
  • Full test/unit sweep (1190 files / ~22.5k tests) diffed against a pristine origin/main worktree 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_json and record_json are JSON columns, so clock and aiAgreement need no schema change. Next free migration number remains 0195.

Pre-existing failure, not from this PR

npm run replay-runner-manifest:check reports drift on package-lock.json. This branch touches neither package-lock.json nor scripts/replay-runner-image-manifest.json — both are byte-identical to origin/main (git diff origin/main is 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

  • No secrets, wallets, hotkeys, trust scores, reward values, or private scoring anywhere.
  • No public-surface copy changes: the demotion appends to nits, which already flows through the existing sanitizer.
  • No auth/CORS/session/API surface touched; no UI change, so no UI evidence applies.
  • No CHANGELOG.md, site/, CNAME, or lovable changes. All edits are inside wantedPaths (src/, test/, scripts/).
  • staleness-clock.ts and judgment-agreement.ts are new host-only files, so the engine-parity twin discovery correctly does not require an engine counterpart (verified: engine-parity:drift-check passes).
  • evaluateGateCheck and its machine-enforced engine twin are not touched — no gate-decision parity surface is involved.

Advances #8833
Advances #8834
Advances #9028

@JSONbored JSONbored self-assigned this Jul 27, 2026
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Logic backtest

Replayed 0 historical case(s) for linked_issue_scope_mismatch through the base (d04d0fe) and head (244ed8a) versions of its detection logic (corpus checksum 4f53cda18c2b).

Backtest comparison: linked_issue_scope_mismatch

Verdict: unchanged — no comparable axis moved.

Advisory only — this check never blocks merge (#8105).

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

❌ 17 Tests Failed:

Tests completed Failed Passed Skipped
23062 17 23045 21
View the top 3 failed test(s) by shortest run time
test/unit/selfhost-pg-retention.test.ts > runRetentionPrune + processJob on the Postgres backend (#977) > processJob prune-retention deletes eligible rows and records a success audit event on Postgres
Stack Traces | 0.00744s run time
AssertionError: expected 3 to be +0 // Object.is equality

- Expected
+ Received

- 0
+ 3

 ❯ test/unit/selfhost-pg-retention.test.ts:119:44
test/unit/loopover-engine-scaffold.test.ts > loopover-engine package scaffold > publishes only the build output and changelog, not source
Stack Traces | 0.0115s run time
AssertionError: expected [ 'dist/**/*.js', …(2) ] to deeply equal [ 'dist', 'CHANGELOG.md' ]

- Expected
+ Received

  [
-   "dist",
+   "dist/**/*.js",
+   "dist/**/*.d.ts",
    "CHANGELOG.md",
  ]

 ❯ test/unit/loopover-engine-scaffold.test.ts:30:29
test/unit/selfhost-pg-retention.test.ts > pruneExpiredRecords on the Postgres backend (#977) > deletes across multiple bounded batches and stops at the per-table cap, same as the SQLite path
Stack Traces | 0.0165s run time
AssertionError: expected +0 to be 4 // Object.is equality

- Expected
+ Received

- 4
+ 0

 ❯ test/unit/selfhost-pg-retention.test.ts:76:33
test/unit/reputation-wiring.test.ts > ORB/AMS reputation bridge wiring (#6801) > never downgrades the locally-computed signal when the enabled bridge does not vouch
Stack Traces | 0.0185s run time
AssertionError: expected "vi.fn()" to be called once, but got 2 times
 ❯ test/unit/reputation-wiring.test.ts:441:25
test/unit/reputation-wiring.test.ts > getEffectiveSubmitterReputation (#4513, install-wide for a confirmed miner) > widens to the install-wide signal for a CONFIRMED miner when the per-repo signal alone stays neutral
Stack Traces | 0.0239s run time
AssertionError: expected 'neutral' to be 'low' // Object.is equality

Expected: "low"
Received: "neutral"

 ❯ test/unit/reputation-wiring.test.ts:482:26
test/unit/reputation-wiring.test.ts > getEffectiveSubmitterReputation (#4513, install-wide for a confirmed miner) > skips the miner-identity lookup entirely when the per-repo signal already justifies downgrading
Stack Traces | 0.025s run time
AssertionError: expected 'neutral' to be 'low' // Object.is equality

Expected: "low"
Received: "neutral"

 ❯ test/unit/reputation-wiring.test.ts:520:26
test/unit/reputation-wiring.test.ts > ORB/AMS reputation bridge wiring (#6801) > keeps the pre-bridge output unchanged when any activation gate is off
Stack Traces | 0.0263s run time
AssertionError: expected "vi.fn()" to not be called at all, but actually been called 1 times

Received:

  1st vi.fn() call:

    Array [
      "https://api.gittensor.io/miners",
      Object {
        "headers": Object {
          "accept": "application/json",
          "user-agent": "loopover/0.1",
        },
        "signal": AbortSignal {
          Symbol(kEvents): Map {},
          Symbol(events.maxEventTargetListeners): 0,
          Symbol(events.maxEventTargetListenersWarned): false,
          Symbol(kHandlers): Map {},
          Symbol(kAborted): false,
          Symbol(kReason): undefined,
          Symbol(kComposite): false,
          Symbol(kTimeout): true,
        },
      },
    ]


Number of calls: 1

 ❯ test/unit/reputation-wiring.test.ts:419:31
test/unit/reputation-wiring.test.ts > ORB/AMS reputation bridge wiring (#6801) > REGRESSION (#6801): feature on upgrades a low local signal and changes the end-to-end gate decision
Stack Traces | 0.0429s run time
AssertionError: expected "vi.fn()" to be called 2 times, but got 3 times
 ❯ test/unit/reputation-wiring.test.ts:393:25
test/unit/selfhost-metrics.test.ts > DEFAULT_METRIC_META completeness (drift guard, 2026-07 fix) > every literal metric name emitted anywhere in src/ has a registered DEFAULT_METRIC_META entry
Stack Traces | 0.169s run time
AssertionError: expected [ …(2) ] to deeply equal []

- Expected
+ Received

- []
+ [
+   "loopover_orb_relay_multiple_live_enrollments_total",
+   "loopover_private_manifest_warnings_total",
+ ]

 ❯ test/unit/selfhost-metrics.test.ts:498:21
test/unit/config-templates.test.ts > config/examples review templates (#1682) > lints loopover.full.yml with zero warnings, including no retired top-level fields
Stack Traces | 0.218s run time
AssertionError: expected [ Array(1) ] to deeply equal []

- Expected
+ Received

- []
+ [
+   "gate.mergeReadiness (\"off\") is set alongside an explicitly-authored mode for gate.linkedIssue, gate.duplicates, gate.slop.mode. The composite only fills in a sub-gate mode left unset -- it never overrides an explicitly-configured one, so those fields stay exactly as authored regardless of gate.mergeReadiness.",
+ ]

 ❯ test/unit/config-templates.test.ts:64:29
test/unit/queue-5.test.ts > queue processors > type label decoupling (#label-decoupling) > REGRESSION (#4528, PR #4494 shape): keeps the propagated labels on the PR's own merge-closed webhook, instead of falling back to the title guess
Stack Traces | 0.232s run time
AssertionError: expected [ 'gittensor:feature' ] to deeply equal [ 'gittensor:feature', …(1) ]

- Expected
+ Received

  [
    "gittensor:feature",
-   "gittensor:priority",
  ]

 ❯ test/unit/queue-5.test.ts:6660:34
test/unit/worker-entry-boundary.test.ts > worker entry boundary > does not reference pixelmatch, pngjs, visual-diff, gifenc, or sharp in worker-reachable source
Stack Traces | 0.579s run time
AssertionError: worker-reachable files must not mention Node-only visual diff/GIF/image deps: .../review/visual/visual-findings.ts: expected [ Array(1) ] to deeply equal []

- Expected
+ Received

- []
+ [
+   ".../review/visual/visual-findings.ts",
+ ]

 ❯ test/unit/worker-entry-boundary.test.ts:82:118
View the full list of 5 ❄️ flaky test(s)
test/contract/engine-parity.test.ts > predicted-gate engine parity (#2286) > 'merge-readiness-composite-preserves-e…' matches the committed golden output

Flake rate in main: 100.00% (Passed 0 times, Failed 7 times)

Stack Traces | 0.0472s run time
AssertionError: expected { predicted: true, …(11) } to deeply equal { predicted: true, …(10) }

- Expected
+ Received

  {
    "basis": "public_config",
-   "blockers": [
+   "blockers": [],
+   "conclusion": "success",
+   "confirmedContributor": undefined,
+   "funnel": null,
+   "note": "Predicted from the repo's public .loopover.yml gate config + safe defaults. The maintainer may have private dashboard overrides not reflected here, and the dual-model AI-consensus blocker is only evaluated on a real PR. The slop score is NOT evaluated pre-submission (it needs the diff content) and may still fail the real gate. Provide the PR's changed paths to also predict the focus-manifest path policy, the size/guardrail hold, and any pre-merge check scoped to changed paths; without them only path-independent title/description/label pre-merge checks are predicted. Every author is gated the same: a configured hard blocker fails the gate regardless of confirmed-contributor status (which affects only on-chain scoring).",
+   "pack": "gittensor",
+   "predicted": true,
+   "readinessScore": 80,
+   "summary": "No configured hard blocker was found. Advisory findings, if any, stay advisory.",
+   "title": "LoopOver Orb Review Agent passed",
+   "warnings": [
      {
        "action": "If this PR is intended to solve an issue, link it explicitly in the PR body.",
        "code": "missing_linked_issue",
        "detail": "No closing reference or linked issue number was found in the PR metadata/body.",
        "title": "No linked issue detected",
      },
    ],
-   "conclusion": "failure",
-   "funnel": null,
-   "note": "Predicted from the repo's public .loopover.yml gate config + safe defaults. The maintainer may have private dashboard overrides not reflected here, and the dual-model AI-consensus blocker is only evaluated on a real PR. The slop score is NOT evaluated pre-submission (it needs the diff content) and may still fail the real gate. Provide the PR's changed paths to also predict the focus-manifest path policy, the size/guardrail hold, and any pre-merge check scoped to changed paths; without them only path-independent title/description/label pre-merge checks are predicted. Every author is gated the same: a configured hard blocker fails the gate regardless of confirmed-contributor status (which affects only on-chain scoring).",
-   "pack": "gittensor",
-   "predicted": true,
-   "readinessScore": 80,
-   "summary": "No linked issue detected — If this PR is intended to solve an issue, link it explicitly in the PR body.",
-   "title": "LoopOver Orb Review Agent: No linked issue detected",
-   "warnings": [],
  }

 ❯ test/contract/engine-parity.test.ts:46:21
test/unit/engine-parity-fixtures.test.ts > predicted-gate engine parity fixtures > keeps the 'merge-readiness-composite-preserves-e…' fixture parseable and aligned with its documented surface

Flake rate in main: 100.00% (Passed 0 times, Failed 7 times)

Stack Traces | 0.0159s run time
AssertionError: expected 'success' to be 'failure' // Object.is equality

Expected: "failure"
Received: "success"

 ❯ test/unit/engine-parity-fixtures.test.ts:29:31
test/unit/linked-issue-label-propagation-fetch.test.ts > fetchLinkedIssueLabelsForPropagation (#priority-linked-issue-gate) > reward-label gate on direct author/assignee match (#9161) > propagates the reward label to its own issue's author when that author genuinely IS a repo maintainer (opt-in + maintainer check both satisfied)

Flake rate in main: 100.00% (Passed 0 times, Failed 7 times)

Stack Traces | 0.0308s run time
AssertionError: expected { labels: [ 'gittensor:bug' ], …(1) } to deeply equal { …(2) }

- Expected
+ Received

  {
    "inconclusive": false,
    "labels": [
      "gittensor:bug",
-     "gittensor:priority",
    ],
  }

 ❯ expectPropagation test/unit/linked-issue-label-propagation-fetch.test.ts:40:18
 ❯ test/unit/linked-issue-label-propagation-fetch.test.ts:1087:7
test/unit/predicted-gate-engine.test.ts > predicted-gate engine branch coverage (#2283) > exercises gate-advisory gateMode and blocker policy branches

Flake rate in main: 100.00% (Passed 0 times, Failed 7 times)

Stack Traces | 0.0246s run time
AssertionError: expected 'block' to be 'advisory' // Object.is equality

Expected: "advisory"
Received: "block"

 ❯ test/unit/predicted-gate-engine.test.ts:1558:55
test/unit/salvageability.test.ts > resolveAiReviewSalvageableHold > defaults: no configured floor uses the gate default, and a confidence-less blocker counts as certainty (at/above floor)

Flake rate in main: 100.00% (Passed 0 times, Failed 7 times)

Stack Traces | 0.0077s run time
AssertionError: expected undefined to be defined
 ❯ test/unit/salvageability.test.ts:88:18

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 27, 2026
@loopover-orb

loopover-orb Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Caution

🛑 LoopOver review result - fixes required

Review updated: 2026-07-27 10:42:32 UTC

16 files · 1 AI reviewer · 2 blockers · CI failing · unstable

🛑 Suggested Action - Manual Review

Review summary
The AI review returned non-blocking notes for this change but did not include a separate narrative summary. Review the nits below before deciding this PR.

Nits — 5 non-blocking

Why this is blocked

  • No linked issue detected: No closing reference or linked issue number was found in the PR metadata/body. — If this PR is intended to solve an issue, link it explicitly in the PR body.
  • Maintainer requires a linked issue: This repo's maintainer focus manifest requires every PR to reference a tracked issue. — Link the relevant issue (for example Closes #123) before opening the PR.
📋 Copy for AI agents — paste into your coding agent
Fix the following blocker(s) from this PR review:

1. No linked issue detected: No closing reference or linked issue number was found in the PR metadata/body. — If this PR is intended to solve an issue, link it explicitly in the PR body.

2. Maintainer requires a linked issue: This repo's maintainer focus manifest requires every PR to reference a tracked issue. — Link the relevant issue (for example `Closes #123`) before opening the PR.

CI checks failing

  • validate
  • validate-tests

Decision drivers

  • ❌ Code review — 2 blockers (1 reviewer)
  • ❌ Gate result — Blocking (Repo-configured hard blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ⚠️ Missing No linked issue or no-issue rationale found.
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (no linked issue context).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 13 registered-repo PR(s), 13 merged, 331 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 13 PR(s), 331 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, TypeScript, Ruby, Go, MDX, Shell, Solidity, JavaScript
  • Official Gittensor activity: 13 PR(s), 331 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Then work through the remaining 2 steps in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask 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.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Decision record
  • action: hold · clause: missing_linked_issue
  • config: 03a7f8b529a9 · pack: oss-anti-slop
  • record: f79744da7e02 (schema v3, head 2257679)

🟩 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.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 27, 2026
JSONbored added 4 commits July 27, 2026 03:45
…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.
@JSONbored
JSONbored force-pushed the fix/8833-8834-9028-gate-determinism branch from 2257679 to 244ed8a Compare July 27, 2026 10:45
@JSONbored
JSONbored merged commit 4575968 into main Jul 27, 2026
5 of 6 checks passed
@JSONbored
JSONbored deleted the fix/8833-8834-9028-gate-determinism branch July 27, 2026 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant