Skip to content

chore(stub-debt): resolve 6 fork-stub typing mismatches in agent-runner e2e — baseline 6 → 0#2458

Merged
alexey-pelykh merged 1 commit intomainfrom
fix/2352-fork-stub-typing-mismatches
Apr 22, 2026
Merged

chore(stub-debt): resolve 6 fork-stub typing mismatches in agent-runner e2e — baseline 6 → 0#2458
alexey-pelykh merged 1 commit intomainfrom
fix/2352-fork-stub-typing-mismatches

Conversation

@alexey-pelykh
Copy link
Copy Markdown

Summary

Bite B of the #2354 umbrella (drive stub-debt baseline to zero and remove it). Resolves the 6 remaining @ts-expect-error -- fork stub typing mismatch suppressions in src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts — the gate inventory drops from 6 to 0.

Sibling PR #2457 (Bite A, merged d75ea3da45) took the baseline from 12 to 6. This PR closes the remaining gap. A future follow-up (Bite C) will delete .stub-debt-baseline and simplify scripts/check-stub-debt.mjs to fail on any @ts-expect-error — that's what closes #2354.

Root cause

All 6 suppressions guard vi.spyOn(modelFallbackModule, "runWithModelFallback").mockImplementation(...) callbacks. The host object was declared as:

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const modelFallbackModule = { runWithModelFallback: vi.fn() } as any;

When the host is any, vi.spyOn(obj, key) resolves to the untyped overload (...args: unknown[]) => any. Each callback's strictly-typed { run } / { provider, model, run } parameter is contravariantly incompatible with unknown[] — TS errors, hence the per-callback @ts-expect-error. Empirically verified: removing one suppression produced TS2345: ... Type 'unknown' is not assignable to type '{ run: ... }'.

Fix

Bind the mock's function signature with a local ModelFallbackFn type so vi.spyOn picks the typed overload. The as any + eslint-disable-next-line @typescript-eslint/no-explicit-any go away, and the 6 callback @ts-expect-error comments are no longer needed.

Changes

File What How
src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts:41 Type the mock host Replace const modelFallbackModule = { runWithModelFallback: vi.fn() } as any; (+ its eslint-disable) with a local ModelFallbackFn function type and vi.fn<ModelFallbackFn>() — matches the 5+ existing vi.fn<T>() usages elsewhere in the repo.
src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts × 6 Drop suppressions Remove // @ts-expect-error -- fork stub typing mismatch at the 6 previous callsites. Callback bodies unchanged. oxfmt reflowed 5 of 6 vi.spyOn(...).mockImplementation(...) chains into multi-line builder-chain style — cosmetic only, no behavior change.
.stub-debt-baseline Decrement to zero 60.

The fork-boundary-mock counter (H8) is unaffected (still 132 == baseline 132).

Verification

  • node scripts/check-stub-debt.mjsstub-debt check passed: 0 == baseline 0.
  • pnpm tsgo → exit 0
  • pnpm lintFound 0 warnings and 0 errors. (3932 files)
  • pnpm format:checkAll matched files use the correct format. (5115 files)
  • pnpm check (full: format + tsgo + lint + lint:tmp + lint:no-remoteclaw-ai) → exit 0
  • E2E file runtime parity: pnpm vitest run --config vitest.e2e.config.ts src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts37 failed / 5 passed / 42 total — IDENTICAL to pre-change. The 37 failures are pre-existing (resolveFallbackTransition gutted per MBP, agent-runner-execution.ts:225-226); orthogonal to fix(test): resolve 6 fork-stub typing mismatches in agent-runner e2e test #2352 and not run by CI's pnpm test (uses scripts/test-parallel.mjs — unit/extensions/gateway only).
  • Adversarial validation (fresh-context subclaude): CLEAN verdict on 7 AC + 9 adversarial checks (no bypass patterns, no stealth normalization, H8 counter unchanged, no formatter side effects, Bite C gate-simplification ready — zero @ts-expect-error remain in src/, extensions/, ui/).

Test plan

  • node scripts/check-stub-debt.mjs passes at baseline 0
  • pnpm check (format + tsgo + lint + project-specific lints) passes
  • Modified test file runtime parity vs pre-change (37/5/42, all 37 failures pre-existing)
  • No production function signatures changed; no production code touched
  • No new @ts-ignore, @ts-nocheck, or as any introduced (grep-verified)
  • CI green on build, test, lint, docs, rebrand-gate, zombie-import-gate, stub-debt-gate, throwing-stub-callers-gate, obsolescence-audit-gate

Context

Refs: #2354, #2457, ADR 0005 H5

🤖 Generated with Claude Code

…er e2e — baseline 6 → 0 (#2352)

Bite B of #2354 umbrella (drive stub-debt baseline to zero). Resolves the
6 `@ts-expect-error -- fork stub typing mismatch` suppressions in
src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts by properly
typing the local `modelFallbackModule` spy host with `vi.fn<ModelFallbackFn>()`
instead of `{ runWithModelFallback: vi.fn() } as any`. When the host was
`any`, `vi.spyOn` resolved to the untyped overload `(...args: unknown[]) => any`
and the callback impls' strictly-typed `{ run }` parameters were contravariantly
incompatible — removing `as any` and binding the mock's function signature
makes the spies accept the existing callback shapes without suppression.

Changes:
- `src/auto-reply/reply/agent-runner.runreplyagent.e2e.test.ts`:
  - Replace `const modelFallbackModule = { runWithModelFallback: vi.fn() } as any;`
    (with `eslint-disable-next-line @typescript-eslint/no-explicit-any`) with a
    local `ModelFallbackFn` function type and `vi.fn<ModelFallbackFn>()`.
  - Delete 6 `// @ts-expect-error -- fork stub typing mismatch` comments.
  - Formatter (oxfmt) reflows 5/6 `vi.spyOn(...).mockImplementation(...)`
    chains into multi-line builder-chain style — cosmetic only, no behavior
    change. Callback bodies (mock return shape, `expect()` assertions) are
    byte-identical modulo whitespace.
- `.stub-debt-baseline`: `6` → `0`.

The baseline file and `scripts/check-stub-debt.mjs` gate simplification
(Bite C) remain the next step to close #2354 — a separate follow-up PR.

Verification:
- `node scripts/check-stub-debt.mjs` → `stub-debt check passed: 0 == baseline 0.`
  (H8 fork-boundary-mock counter unchanged: `132 == baseline 132`).
- `pnpm check` (format + tsgo + lint + project-specific lints) → exit 0.
- E2E file runtime parity: 37 failed / 5 passed / 42 total — identical to
  pre-change. The 37 failures are pre-existing (resolveFallbackTransition
  gutted per Middleware Boundary Principle); not touched by this PR and
  not run by CI's `pnpm test` (which excludes e2e via test-parallel.mjs).
- Adversarial validation (fresh-context subclaude): CLEAN verdict on 7
  AC + 9 adversarial checks (no bypass patterns, no stealth normalization,
  no H8 regression, no formatter side effects, Bite C gate-simplification
  ready).

Refs: #2354, #2457, ADR 0005 H5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alexey-pelykh alexey-pelykh merged commit 5ec00ef into main Apr 22, 2026
15 checks passed
@alexey-pelykh alexey-pelykh deleted the fix/2352-fork-stub-typing-mismatches branch April 22, 2026 07:19
alexey-pelykh added a commit that referenced this pull request Apr 22, 2026
… — Bite C (#2354)

**Bite C** of the #2354 umbrella (drive stub-debt baseline to zero and
remove it). Bites A (#2457, baseline 12 → 6) and B (#2458, baseline 6 → 0)
drove the H5 `@ts-expect-error` count to zero across `src/`, `extensions/`,
and `ui/`. This PR retires the H5 baseline entirely and flips the gate to
zero-tolerance.

The H8 fork-boundary-mock counter (`.fork-boundary-mock-baseline = 132`)
is untouched — still baselined, still ratchetable.

Changes:
- `scripts/check-stub-debt.mjs`: inline the H5 counter as a zero-tolerance
  check (was baseline-gated via `readBaseline`+`reportCounter`). Any
  `@ts-expect-error` in a gated file fails the script with an inventory
  and a remediation pointer to Bite A (`as unknown as T` cast pattern,
  PR #2457) and Bite B (`vi.fn<Fn>()` typed-mock pattern, PR #2458). The
  H8 branch — `readBaseline` + `reportCounter` for `.fork-boundary-mock-baseline`
  — is byte-identical to pre-change; inline-for-H5 / helper-for-H8
  asymmetry is intentional since the two counters are now fundamentally
  different patterns.
- `.stub-debt-baseline`: DELETED (last value was `0`).
- `CLAUDE.md:121`: rewrote the `stub-debt-gate` bullet to reflect
  zero-tolerance H5 + link to `CONTRIBUTING.md § Fork-boundary mocks`
  for H8 detail.
- `CONTRIBUTING.md:217`: row label for the sync-pr-audit composite
  summary updated from `H8 stub-debt + fork-boundary-mock baselines`
  to `H5 stub-debt (strict) + H8 fork-boundary-mock` — also corrects
  pre-existing mislabel of H5 as H8. Script reference unchanged.
- `.github/workflows/sync-pr-audit.yml:116`: same row-label update;
  script invocation and exit-code variable `H8` intentionally unchanged
  to minimize churn.

Verification:
- `node scripts/check-stub-debt.mjs` → `stub-debt check passed: 0
  @ts-expect-error suppressions.` + `fork-boundary-mock check passed:
  132 == baseline 132.` (exit 0).
- Failure-path probe (temp `@ts-expect-error` under `src/`) →
  script prints inventory, remediation guide, and exits 1. Confirms
  the zero-tolerance branch.
- `pnpm check` (format + tsgo + lint + project-specific lints) →
  exit 0.
- `pnpm test` (full unit+extensions+gateway suite) → 7010 passed /
  3 skipped / 7013 total.
- Rescan: `git ls-files | xargs grep -l "\.stub-debt-baseline"` →
  zero hits. No stale references remain.
- Adversarial validation (fresh-context subclaude): 5 AC PASS +
  13 adversarial checks PASS; 1 MINOR cross-repo finding (HQ ADR
  0005 H5 staleness) tracked as post-merge HQ follow-up, does NOT
  block this PR.

Closes #2354

Refs: #2457, #2458, ADR 0005 H5

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
alexey-pelykh added a commit that referenced this pull request Apr 22, 2026
… — Bite C (#2354) (#2459)

**Bite C** of the #2354 umbrella (drive stub-debt baseline to zero and
remove it). Bites A (#2457, baseline 12 → 6) and B (#2458, baseline 6 → 0)
drove the H5 `@ts-expect-error` count to zero across `src/`, `extensions/`,
and `ui/`. This PR retires the H5 baseline entirely and flips the gate to
zero-tolerance.

The H8 fork-boundary-mock counter (`.fork-boundary-mock-baseline = 132`)
is untouched — still baselined, still ratchetable.

Changes:
- `scripts/check-stub-debt.mjs`: inline the H5 counter as a zero-tolerance
  check (was baseline-gated via `readBaseline`+`reportCounter`). Any
  `@ts-expect-error` in a gated file fails the script with an inventory
  and a remediation pointer to Bite A (`as unknown as T` cast pattern,
  PR #2457) and Bite B (`vi.fn<Fn>()` typed-mock pattern, PR #2458). The
  H8 branch — `readBaseline` + `reportCounter` for `.fork-boundary-mock-baseline`
  — is byte-identical to pre-change; inline-for-H5 / helper-for-H8
  asymmetry is intentional since the two counters are now fundamentally
  different patterns.
- `.stub-debt-baseline`: DELETED (last value was `0`).
- `CLAUDE.md:121`: rewrote the `stub-debt-gate` bullet to reflect
  zero-tolerance H5 + link to `CONTRIBUTING.md § Fork-boundary mocks`
  for H8 detail.
- `CONTRIBUTING.md:217`: row label for the sync-pr-audit composite
  summary updated from `H8 stub-debt + fork-boundary-mock baselines`
  to `H5 stub-debt (strict) + H8 fork-boundary-mock` — also corrects
  pre-existing mislabel of H5 as H8. Script reference unchanged.
- `.github/workflows/sync-pr-audit.yml:116`: same row-label update;
  script invocation and exit-code variable `H8` intentionally unchanged
  to minimize churn.

Verification:
- `node scripts/check-stub-debt.mjs` → `stub-debt check passed: 0
  @ts-expect-error suppressions.` + `fork-boundary-mock check passed:
  132 == baseline 132.` (exit 0).
- Failure-path probe (temp `@ts-expect-error` under `src/`) →
  script prints inventory, remediation guide, and exits 1. Confirms
  the zero-tolerance branch.
- `pnpm check` (format + tsgo + lint + project-specific lints) →
  exit 0.
- `pnpm test` (full unit+extensions+gateway suite) → 7010 passed /
  3 skipped / 7013 total.
- Rescan: `git ls-files | xargs grep -l "\.stub-debt-baseline"` →
  zero hits. No stale references remain.
- Adversarial validation (fresh-context subclaude): 5 AC PASS +
  13 adversarial checks PASS; 1 MINOR cross-repo finding (HQ ADR
  0005 H5 staleness) tracked as post-merge HQ follow-up, does NOT
  block this PR.

Closes #2354

Refs: #2457, #2458, ADR 0005 H5

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(ci): drive stub debt baseline to zero and remove it

1 participant