Summary
Add a CI-enforced check that fails when any symbol defined as a "stub" (unconditional-throw or silent-neuter return) has live non-test callers. Prevents recurrence of the class documented in #2408 and #2337.
Rationale
Both #2408 (unconditional-throw regression) and #2337 (silent-neuter regressions — Telegram vision, node-host skills, TUI listModels) shipped because:
- TypeScript accepts
never-return functions and empty-return stubs anywhere a compatible signature is expected.
- Unit tests mock the stub, so the integration-level error is invisible.
- No lint rule forbids the pattern.
A CI gate closes this hole without relying on reviewer vigilance or memory of the anti-pattern.
Detection scope
Minimum target: (..._args: unknown[]): never { throw ... } with live non-test callers.
Stretch target: silent-neuter forms (=> false, => [], => {}, => undefined) explicitly marked as upstream-compat stubs (e.g., via a // Gutted in RemoteClaw fork comment convention, a @upstream-stub JSDoc tag, or similar).
Implementation options (choose during design phase)
- oxlint custom rule — AST-based detection. Check whether the repo's oxlint setup supports custom rules.
- Script in
scripts/ — invoked from pnpm check or a dedicated pnpm lint:stubs step in CI. Grep for pattern + caller graph, fail on live non-test callers.
- Brand-type phantom parameter — give stubs a
never-typed phantom parameter callers cannot satisfy. Most invasive; fallback only.
Acceptance criteria
Related
Dependencies
Summary
Add a CI-enforced check that fails when any symbol defined as a "stub" (unconditional-throw or silent-neuter return) has live non-test callers. Prevents recurrence of the class documented in #2408 and #2337.
Rationale
Both #2408 (unconditional-throw regression) and #2337 (silent-neuter regressions — Telegram vision, node-host skills, TUI
listModels) shipped because:never-return functions and empty-return stubs anywhere a compatible signature is expected.A CI gate closes this hole without relying on reviewer vigilance or memory of the anti-pattern.
Detection scope
Minimum target:
(..._args: unknown[]): never { throw ... }with live non-test callers.Stretch target: silent-neuter forms (
=> false,=> [],=> {},=> undefined) explicitly marked as upstream-compat stubs (e.g., via a// Gutted in RemoteClaw forkcomment convention, a@upstream-stubJSDoc tag, or similar).Implementation options (choose during design phase)
scripts/— invoked frompnpm checkor a dedicatedpnpm lint:stubsstep in CI. Grep for pattern + caller graph, fail on live non-test callers.never-typed phantom parameter callers cannot satisfy. Most invasive; fallback only.Acceptance criteria
.github/workflows/ci.ymlrequired check orpnpm checkpipeline).mainafter fix(agents): restore resolveAgentRuntimeOrThrow body — regression stub crashed every agent dispatch #2408 and audit(agents): inventory throwing-stubs-with-live-callers across src/ #2409 findings resolved.Related
Dependencies