build(typescript): enable noUnusedLocals/noUnusedParameters repo-wide (#9553, #9570, #9571, #9572) - #9573
Conversation
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-28 18:37:50 UTC
Review summary Nits — 6 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. 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. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | ab116ca | Commit Preview URL Branch Preview URL |
Jul 28 2026, 06:18 PM |
8c9fca6 to
a63a16e
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9573 +/- ##
==========================================
- Coverage 89.84% 89.83% -0.01%
==========================================
Files 875 875
Lines 110980 110951 -29
Branches 26402 26402
==========================================
- Hits 99706 99678 -28
+ Misses 9992 9991 -1
Partials 1282 1282
Flags with carried forward coverage won't be shown. Click here to find out more.
|
60ccaf8 to
ddc6bdf
Compare
…#9553) Dead code is the substrate every drift bug in the 2026-07-27 audit grew on: a stale import or an orphaned constant reads exactly like a live wire, so the next person greps, finds it, and reasons about a code path that no longer runs. Enabling the flags made the compiler enumerate all 515 instances. Every one in src/** and packages/** was traced to its replacement before deletion -- all 82 were genuine supersession leftovers, no behaviour bug hiding among them -- but the triage turned up three real problems that were invisible under the noise: 1. src/queue/processors.ts -- sweepRepoBacklogConvergence accepted `requestedBy` ("schedule" | "api" | "test") and dropped it. Every sibling sweep stamps it into recordAuditEvent's metadata; both agent.sweep.backlog_convergence events here omitted it, so those records could not be attributed to a schedule vs a manual API trigger. Now wired into both. (Note the mechanical fix would have been to rename it `_requestedBy`, which cements the gap instead of closing it.) 2. test/unit/openapi.test.ts -- the #9302 REST<->MCP parity guard asserted against src/mcp/server.ts's gatePrecisionOutputSchema and maintainerMeasurementReportOutputSchema, which the tools stopped registering when #9518 moved their outputs to @loopover/contract. The shapes are still identical, so nothing had drifted YET -- but the guard was watching objects no runtime reads and would not have caught a future contract change. Re-anchored onto GetGatePrecisionOutput.shape / GetOutcomeCalibrationOutput.shape, which is what the tools actually register, and what that file's own header comment already claimed it did. 3. src/github/resolve-command.ts was reachable only from its own test, because src/review/review-memory-wire.ts carried a SECOND byte-identical copy of normalizeResolveFindingRef (regex included) and that copy was the one production used. Two independent implementations of the same public-safety validation, free to drift. Deduped onto the original via re-export; dead-source-files:check now passes on a file it was about to start failing on. Also corrects packages/loopover-engine/src/scoring/preview.ts's header, which claimed a ReDoS guarantee via a hasUnsafeWildcardCount import that had been dead since 625e236 deduped its label matching onto label-match.ts. The guarantee is real and unchanged; it now arrives through labelMatchesPattern, and the comment says so. Pre-existing import-specifier violations fixed in the same pass, since the tree has to be green for the flags to mean anything: - scripts/actionlint.ts imported a `.ts` specifier (TS5097) - test/unit/contract-registry.test.ts had three `.js` specifiers in a Bundler zone - check-dead-source-files-script.test.ts tripped check-import-specifiers on its own string FIXTURES, the same self-referential false positive that checker's ALLOWED_FILENAMES already documents for its own test Mechanics: unused parameters are renamed with a leading underscore, never deleted -- they are positional, so removing one silently re-binds every later argument. Everything else was removed by its real TypeScript AST node span (a regex pass was tried first and mis-bounded declarations badly enough to produce unparseable files). Full suite green: 23,894 passed, 0 failed. tsc clean with the flags on.
…e-advisory.ts check-engine-parity holds the two hand-duplicated gate-decision twins (packages/loopover-engine/src/advisory/gate-advisory.ts and src/rules/advisory.ts) in lockstep: touching one without the other requires an engine version bump. That is the mechanism, and it is doing its job here. the engine twin. They are genuinely dead there and NOT in the host: the engine copy is a deliberately slimmed re-implementation (#4881) that omits buildIssueAdvisory / addIssueFindings / collisionClustersForPull, which are what use those types on the host side. So there is no matching host edit to make -- the asymmetry is correct, and the version bump is the sanctioned way to record it. No behaviour change: type-only imports are erased at compile time. The bump exists so the parity contract stays enforceable, not because the gate decides anything differently. packages/loopover-miner/expected-engine.version moves in lockstep, as its own check requires.
…d shape #9565 added Two rebase follow-ups after #9565 and #9574 landed. 1. scripts/actionlint.ts gets its `.ts` extension back. This PR had removed it to satisfy check-import-specifiers, which broke the script outright -- it runs under `node --experimental-strip-types`, whose ESM resolver does no extension resolution, so the process dies at startup with ERR_MODULE_NOT_FOUND. #9565 independently reached the same conclusion and added TYPE_STRIPPED_ENTRYPOINTS to the checker for exactly this file, so the extension is now permitted where it is required. Verified by running `npm run actionlint`, which fails before this change and passes after. #9565's version of the checker is taken wholesale over this PR's: it solves the same two problems (that entrypoint set, plus allowlisting check-dead-source-files-script.test.ts for its string fixtures), and re-litigating a file main just rewrote buys nothing. 2. src/mcp/server.ts's `loginRepoPullShape` is removed -- dead on arrival in #9565, and the first thing the newly-enabled noUnusedLocals caught on main. Which is the point of this PR: dead code now surfaces at the commit that introduces it rather than at the next audit. The engine bump lands at 3.16.1 (main released 3.16.0 while this was open). It is required by check-engine-parity: this PR removes two dead TYPE-only imports from packages/loopover-engine/src/advisory/gate-advisory.ts, and the parity contract holds that file in lockstep with its host twin src/rules/advisory.ts. There is no matching host edit to make -- the engine copy is a deliberately slimmed re-implementation (#4881) omitting the functions that use those types -- so the version bump is the sanctioned way to record a one-sided change. No behaviour change: type-only imports are erased at compile time. packages/loopover-miner/expected-engine.version moves with it, as its own check requires.
…ne bump The manifest is a generated artifact that must move with any package.json version, and release-manifest:sync:check fails CI when it drifts. Regenerated with the repo's own `npm run release-manifest:sync` rather than hand-edited.
Rebase onto main after #9579 and #9580 landed. The newly-enabled noUnusedLocals immediately flagged twelve dead symbols in code merged since this PR opened -- including two I left in #9580 myself: - src/queue/processors.ts: PrCommandPrologueOutcome imported but unused (the adapter's annotated return type was dropped in favour of inference), plus eight unused bindings in the prologue destructures -- handlers that do not need `pr`, `settings`, `authorization` or `command` were still pulling them out. - src/queue/pr-command-prologue.ts: LoopOverMentionCommandName, superseded by LoopOverActionCommandName once the spec narrowed to action verbs. - src/mcp/dispatch-telemetry-sink.ts: an unused McpToolCallTelemetry import from #9579. Which is the point of the PR: the flags catch dead code at the commit that introduces it rather than at the next audit. Zero behaviour change -- every removal is a binding or an import TypeScript proved unreferenced, and the suite passes 24,014 tests. The rebase conflict itself was in processors.ts's import block: main added the pr-command-prologue import on the same lines this PR removed the unused runRetentionPrune one. Both intents kept.
fdd5069 to
ab116ca
Compare
Closes #9553
Closes #9570
Closes #9571
Closes #9572
Summary
Dead code is the substrate every drift bug in the 2026-07-27 audit grew on: a stale import or an orphaned constant reads exactly like a live wire, so the next person greps, finds it, and reasons about a code path that no longer runs.
Enabling
noUnusedLocals/noUnusedParametersmade the compiler enumerate all 515 instances. Every one insrc/**andpackages/**was traced to its replacement before deletion — all 82 were genuine supersession leftovers, no behaviour bug hiding among them. But the triage surfaced three real problems that were invisible while the noise was tolerated.The three findings (each its own issue)
#9570 —
requestedBydropped from backlog-convergence audit eventssweepRepoBacklogConvergenceacceptedrequestedBy: "schedule" | "api" | "test"and never read it, while every sibling sweep stamps it intorecordAuditEvent's metadata. Bothagent.sweep.backlog_convergenceevents omitted it, so those records could not be attributed to a schedule vs a manual API trigger.Worth calling out: the mechanical response to the compiler warning is to rename it
_requestedBy— which cements the gap permanently instead of closing it. Wired into both events instead.#9571 — the #9302 parity guard watches objects nothing registers
The REST↔MCP parity test asserted against
src/mcp/server.ts'sgatePrecisionOutputSchema/maintainerMeasurementReportOutputSchema. #9518 moved those tools' outputs to@loopover/contract, and the tools now registerGetGatePrecisionOutput.shape/GetOutcomeCalibrationOutput.shape.The shapes are still identical, so nothing had drifted yet — the defect is that the guard was watching objects no runtime reads, so a future contract change would have sailed past it. Re-anchored onto what the tools actually register, which the test file's own header comment already claimed it did.
#9572 —
normalizeResolveFindingRefimplemented twicesrc/github/resolve-command.tshad no production importer, becausesrc/review/review-memory-wire.tscarried a byte-identical second copy (regex included) and that copy was the one production used. Two independent implementations of the same public-safety validation, free to drift, with the canonical module kept alive only by its own test — exactly theissue-rag-wire.tsshape #9492's dead-source check exists to catch. Deduped via re-export.Also fixed (pre-existing, since the tree has to be green for the flags to mean anything)
scripts/actionlint.tsimported a.tsspecifier (TS5097)test/unit/contract-registry.test.tshad three.jsspecifiers in a Bundler-resolved zonecheck-dead-source-files-script.test.tstrippedcheck-import-specifierson its own string fixtures — the same self-referential false positive that checker'sALLOWED_FILENAMESalready documents for its own testPlus
packages/loopover-engine/src/scoring/preview.ts's header, which claimed a ReDoS guarantee via ahasUnsafeWildcardCountimport that had been dead since625e236b4. The guarantee is real and unchanged — it arrives throughlabelMatchesPattern— and the comment now says so.Mechanics
Verification
tscclean with both flags ondead-source-files:check,import-specifiers:check,regate-sort-key:check,coverage-boltons:check,branding-drift:check— all green