fix(replay,maintainability): thread the recorded decision clock into three unrecorded reads, delete a dead wire, and add a reachability check (#9492) - #9538
Conversation
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-28 08:17:41 UTC
Review summary Blockers
Nits — 6 non-blocking
Concerns raised — review before merging
📋 Copy for AI agents — paste into your coding agentDecision 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. 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.
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Logic backtestReplayed 0 historical case(s) for Backtest comparison:
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | 208948d | Commit Preview URL Branch Preview URL |
Jul 28 2026, 08:00 AM |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9538 +/- ##
=======================================
Coverage 89.58% 89.58%
=======================================
Files 846 846
Lines 110384 110388 +4
Branches 26295 26297 +2
=======================================
+ Hits 98889 98893 +4
Misses 10231 10231
Partials 1264 1264
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Bundle ReportBundle size has no change ✅ |
a49ab77 to
6147e30
Compare
|
CI failure fixed and pushed.
That is a fair hit on me. The irony is not lost — this PR adds a reachability check for silently dead code, and I introduced a silently uncategorized tool doing it. The lesson is the same one #9540 encodes: a registry with no compile-time totality (this map is a plain I also checked whether the stdio MCP server ( Rebased onto current |
6147e30 to
c351acd
Compare
c351acd to
208948d
Compare
Summary
Two of #9492's three findings, both structural rather than cosmetic: the recorded decision clock reached exactly one consumer while three other clock reads still decided outcomes, and a dead feature file sat green in CI for months with no mechanism that could have noticed.
Closes #9539
Closes #9540
#9539 — replay determinism: three unrecorded reads decided outcomes
#9028/#9256 shipped "replayable time": one
Date.now()per decision pass, recorded into the replay input. Its value is destroyed entirely by a second, unrecorded read in the same pass — the recorded instant is then not the instant that decided.Worse, this failed silently in the direction that matters.
replayDecisionpinsactionand only re-derivesevaluateGateCheck, so replaying a decision made on an unrecorded read reportsverdict: "match"— a false certification, not a caught divergence. The feature would have reported the pipeline deterministic precisely where it wasn't.Three reads now take the recorded instant:
activeMergeBlockedSha— fed the plan and the recorded reason code, from a secondDate.now()in the very function whose caller capturesdecisionClock. Threaded as a requireddecisionNowMsfield, deliberately: optionality is how this class drifts (the approval-queue: accept path builds a thinner executor context — #9159 mutex, moderation ladder and #9055 base guard all silently inert #9482 lesson), and an optional clock lets a future call site reintroduce the gap with no type error.isNewAccount— halves the contributor cap and can therefore flip a cap close.decisionClockwas already in scope, unused. The twin helperisBelowAccountAgeThresholdhad nonowMsseam at all; it has one now.resolveUnlinkedIssueMatchDisposition— its velocity-exception gap chooses close vs hold. All three of its clock reads now share one instant, so the DB lookback windows and the gap agree.The honest-scope note in
decision-replay.tsis updated to name what is still live-clock — the threeisBelowAccountAgeThresholdcallers outside this pass (each its own pass, no captured instant, nothing to be consistent with) and the SQL-sidedatetime('now', ?)insubmitter-reputation.ts, a class no JS-side seam can cover. A scope claim that silently widens is worse than a narrow one.#9540 — a dead wire, and the missing check for the class
src/review/issue-rag-wire.tshad zero production importers: both real consumers imported the engine module it re-exported directly, and only its own test imported the wire. Coverage cannot catch this — a dead module's own test exercises it perfectly well.scripts/check-dead-source-files.ts(new, intest:ciand CI): everysrc/**file must have at least one importer outside its own test. Deliberately narrow — no re-export chasing, no unused-export analysis — it answers only the question maintainability: processors.ts (16.2k lines, 437 touches) is the structural cause of this audit's drift and determinism gaps; plus a confirmed dead wire #9492 actually found, cheaply enough to run per-PR.src/review/content-lane/index.ts— a barrel with no importers, and already drifted: it omitssurvivingExistingEntries, which predates this PR. Deleted.src/miner/soft-claim.tswas a shipped-but-unregistered feature.buildSoftClaimSpeclanded in feat(miner-plan): soft-claim local-write action-spec builder (#2315) #2813 and no MCP tool ever exposed it, so the capability simply did not exist at runtime. Wired up asloopover_post_soft_claim, following the identicallocal-write-toolspattern its nine siblings already use.scripts/**is a legitimate importer root (check-migrations.tsanddraft-issue.tsare the sole consumers of two livesrc/**modules), andserver.tsplus the two esbuild alias targets are declared entry points with reasons.Scope
#9492 originally bundled three findings in one issue, which is why this PR had nothing it could close in full. It is now an epic over three sub-issues — #9539 and #9540, both closed here in their entirety, and #9541, the
processors.tssplits.#9541 is deliberately not in this PR. Extracting ~9 command handlers behind a shared dispatch, three advisory spend gates behind one precondition struct, and the plan-and-execute pass into its own typed-context module is a large behaviour-preserving refactor of a 16.2k-line file. Bundling it with behavioural fixes would make both unreviewable, and the risk profiles are entirely different.
Both fixes here reduce that refactor's risk: the required
decisionNowMsfield is a first instance of exactly the "typed decision-pass context both paths must construct" #9541 calls for, and the reachability check makes a module accidentally orphaned during a split fail CI instead of silently dying.Validation
npx tsc --noEmit,dead-source-files:check,db:migrations:check,selfhost:env-reference:check,actionlint,git diff --check— all cleanRegressions, verified to fail against the unfixed code: the velocity exception evaluated at a supplied instant diverges from the live clock (using a confirmed miner, the only configuration where the gap is decisive — an earlier draft of this test passed for the wrong reason and was fixed); the repeat lookback shares that instant; account age judged at a supplied instant flips
isNewAccount; and the checker flags both the "only its own test imports it" and "no importer at all" shapes.Invariants: omitting
nowMspreserves live-clock behaviour byte-identically at every new seam, so the callers outside the decision pass are untouched; one production importer is always enough; a non-own test still counts as a real importer (a shared helper is alive);scripts/**rescues a module that is otherwise reported dead; declared entry points are never flagged; barrels keep their members alive viaexport ... from; specifiers reaching outsidesrc/**are ignored rather than miscounted; and the pass still captures exactly one clock instant.The two
processors.ts-internal sites have no export seam, so they are pinned by a source-parity guard — the same conventiondb-parsers.test.tsalready uses forSTALE_RECHECK_DENIAL_DETAIL_PATTERN, and the honest alternative to exporting internals purely for a test.