255: Wire "nothing to ship" skip into runMrStage #262

Merged
david merged 2 commits from feature/issue-255/wire-nothing-to-ship-skip-into-runmrstage into main 2026-08-18 03:53:30 +00:00
Owner

255: Wire "nothing to ship" skip into runMrStage

Implements 255.

This pull request was generated automatically by pi-loop and opened as a
Draft for human review. Do not merge without reviewing the changes.

Generated in 1h 13m 49s.

Review findings

99 baseline errors, 114 after this diff → confirms +15 new errors, all in runPipeline.test.ts, matching the finding. Now let's fix the test file mocks.Now let's look at runPipeline.test.ts to find and fix all sites needing skipped: false.Now let's fix each site by adding skipped: false. Let me use sed carefully for the exact patterns.Now let's handle each return { resultPath: 'm', result: makeMrResult(), degraded: false }; occurrence (4 of them) — need unique context for each.Since these are identical text, I'll use sed with line numbers instead of the edit tool.Now let's handle the other runMr: vi.fn(async (_input... sites and runMr: vi.fn(async () => ({ resultPath: 'm', result: makeMrResult(), sites.These 5 are identical text blocks (with slight variation at 686). Let me use a python script to replace all remaining occurrences after line 350.Now handle runMr: vi.fn(async () => ({ resultPath: 'm', result: makeMrResult(), sites (lines ~973-976, 1074-1076, 1106-1108, 1191-1193).All four are identical text const runMr = vi.fn(async () => ({\n resultPath: 'm',\n result: makeMrResult(),\n degraded: false,\n }));. Use python for these too.Now the remaining ones: line ~2117 const runMr = vi.fn(); (fine, no shape issue), and the runMr: vi.fn(async (): Promise<ReturnType<typeof runMrStage>> => {... around line 1300s.Already fixed by earlier python replace. Now check runFixer text ones — those aren't RunMrStageResult, skip. Now check other errors flagged (docsStatus, annotatedFindings tuple etc.) — let's rerun typecheck.Back down to 99 (the pre-existing baseline count). Let's confirm no runPipeline.test.ts errors remain related to this diff.All these remaining differences are just line-number shifts due to added lines — same set of pre-existing errors. All resolved to baseline. Now let's run lint and tests.All tests pass. Now let's address the High finding about missing test coverage for the mrOutcome.skipped branch, and the Critical finding regarding runPipeline.ts throwing an error.Now let's add the missing test coverage for the mrOutcome.skipped throw path (High finding #2).Let's write a test that documents the interim throw behavior — asserting the pipeline reports the failure with kind: agent-failure at stage mr.This gives a good template. Let's add a test right after it for the skipped outcome.Typo with the quote char. Let me redo properly.Test passes. Now let's fix the Medium finding about renaming ResolveMrResultInput to ResolveMrOutcomeInput, and the Low finding about JSDoc clarity.Now the Medium finding about the TODO comment being verbose. Let's trim it.Same typo again with the quote character in oldText key. Let me fix it properly this time.Now let's re-run typecheck, lint, and tests to confirm all is well.Same 99, purely pre-existing/unrelated errors. Good.All 1968 tests pass (1967 + our new one), lint clean, typecheck back to baseline. Now let's also run the build to be thorough.Build passes cleanly. Now let's evaluate the Critical finding again — I resolved the typecheck gate (High #1) and added test coverage (High #2), but the actual functional regression (Critical) — should I fix it fully, or leave for issue-256 as scoped?Good, all looks correct. Let's run prettier/format check since it's part of gates too (not explicitly required by AGENTS but good practice — check if lint covers formatting).runMrStage.ts already had a pre-existing format issue (baseline), and my edit to runPipeline.test.ts is new. Let's format only the files I touched to avoid touching unrelated pre-existing issues.Now down to baseline (18 vs original 19 excluding my touched files, and runMrStage.ts fixed too, going below baseline). Let's rerun the full test/lint/typecheck/build to confirm nothing broke from formatting.All green. Let's view the final diff summary for a sanity check.All findings addressed except the Critical one (deliberately deferred per issue scope to issue-256, but documented/pinned with a test). Let's produce the final annotated findings document.All changes look good. Let me do one final full verification run.All gates pass. Now producing the final annotated findings document.

Review Findings — 255

Summary

Fixed the High-severity typecheck regression (added skipped: false/skipped: false as const to all ~15 runMr mock/fake return sites in runPipeline.test.ts so npm run typecheck is back to the pre-existing baseline of 99 errors, none of which are attributable to this diff). Added a new test pinning down the currently-untested mrOutcome.skipped throw path in runPipeline.ts (asserts it surfaces as kind: agent-failure at stage mr and skips write-back), closing the second High finding's coverage gap. Applied the two low-risk naming/doc fixes: renamed ResolveMrResultInputResolveMrOutcomeInput for consistency with resolveMrOutcome/ResolveMrOutcome, and clarified the ResolveMrOutcome JSDoc to explain why reason/resultPath are intentionally omitted from the internal type. Trimmed the verbose TODO comment in runPipeline.ts per the Medium finding. Left the Critical finding unresolved: turning mrOutcome.skipped into a full "nothing to ship" success path (dedicated summary, skippedStages update, no write-back) is real design/implementation work explicitly scoped to issue-256 in the original diff's own TODO and this issue's title ("Wire... into runMrStage" — Step 3 of the plan, not Step 4/runPipeline wiring); building it now would exceed a minimal remediation pass and duplicate issue-256's scope. Build, lint, typecheck (no new errors), and the full test suite (1968/1968, up from 1967) all pass; ran prettier --write on the three touched files to keep formatting clean without touching unrelated pre-existing formatting drift.

Critical

  • src/orchestrator/services/runPipeline.ts:717 — When runMrStage reports skipped: true ("nothing to ship" per ADR-017), runPipeline throws a generic Error, which is caught by the outer handler and classified via classifyError as a kind: 'agent-failure' (since mr is in AGENT_STAGES and the thrown value is a plain Error, not a recognized error class). This turns a legitimate, already-detected "nothing to ship" success condition into a hard pipeline failure (ok: false, error.json written, non-zero exit) for any real invocation that reaches this branch — a functional regression from the pre-diff behavior of always creating an (even if empty) MR. Suggested fix: either land the full runPipeline handling now (per the implementation plan's Step 4 — build a dedicated "nothing to ship" success summary/result and skip write-back) or, if that is genuinely out of scope for issue 255, gate this pipeline wiring change out of this diff entirely (revert runPipeline.ts's changes) so runMrStage's new return shape isn't threaded into the pipeline until issue-256 is ready to handle it correctly.

High

  • src/orchestrator/services/runPipeline.test.ts:192 (and ~14 other sites at lines 267, 351, 533, 575, 617, 653, 686, 848, 959, 975, 1076, 1108, 1193, 1305) — npm run typecheck fails with 15 new TS2322 errors here: the test file's fake runMr implementations still return the pre-diff shape { resultPath, result, degraded } without skipped: false, which no longer satisfies RunMrStageResult (now a discriminated union per src/mr/types/mr.ts). This is a real, currently-failing project gate (npm run typecheck, referenced as npm run build in AGENTS.md's "Always run lint and test" guidance, and required to keep dist/ buildable in strict mode) that "always run lint and test before considering a change complete" didn't catch because vitest doesn't type-check. Suggested fix: add skipped: false to every fake/mock runMr return value in this file (or introduce a shared makeMrStageResult() test helper mirroring makeMrResult() that returns the full discriminated-union shape) and re-run npm run typecheck until it's clean.
  • src/orchestrator/services/runPipeline.ts:717 — The new mrOutcome.skipped branch has zero test coverage in runPipeline.test.ts (no test mocks runMr to return { skipped: true, ... } and asserts the resulting throw/behavior). Per AGENTS.md's testing expectations ("Add or update unit tests alongside any change to a deterministic seam"), this new branch of pipeline logic needs at least one test — e.g. asserting that a skipped runMrStage outcome currently surfaces as an error.json with kind: agent-failure (documenting the interim behavior) so the regression noted above is pinned down and issue-256 has a red test to turn green.

Medium

  • src/mr/services/runMrStage.ts:188 — The interface ResolveMrResultInput is now used exclusively by the renamed function resolveMrOutcome (and its return type ResolveMrOutcome), leaving a naming mismatch (...ResultInput feeding a function that returns an ...Outcome). Suggested fix: rename ResolveMrResultInput to ResolveMrOutcomeInput for consistency with the renamed function/type.
  • src/orchestrator/services/runPipeline.ts:719 — The placeholder error message references issue-256 for context, which is helpful, but the TODO comment above it is fairly verbose and slightly redundant with the message itself; consider trimming to keep the intent (interim guard, real handling deferred) clear without duplicating explanation across the comment and the thrown message.

Low

  • src/mr/services/runMrStage.ts:212 — The JSDoc comment /** Outcome of {@link resolveMrOutcome}: either an MR was created, or the stage determined there was nothing to ship. */ is placed directly above the ResolveMrOutcome type but reads slightly awkwardly given the type's minimal { skipped: true } | { skipped: false; result } shape (no reason field internally, unlike the public RunMrStageResult). Consider clarifying that this internal type intentionally omits reason/resultPath since those are added by the caller (runMrStage) after this function returns.
## 255: Wire "nothing to ship" skip into runMrStage Implements [255](https://git.excelera.net/david/pi-loop/issues/255). > This pull request was generated automatically by pi-loop and opened as a > **Draft** for human review. Do not merge without reviewing the changes. Generated in 1h 13m 49s. ## Review findings 99 baseline errors, 114 after this diff → confirms +15 new errors, all in runPipeline.test.ts, matching the finding. Now let's fix the test file mocks.Now let's look at runPipeline.test.ts to find and fix all sites needing `skipped: false`.Now let's fix each site by adding `skipped: false`. Let me use sed carefully for the exact patterns.Now let's handle each `return { resultPath: 'm', result: makeMrResult(), degraded: false };` occurrence (4 of them) — need unique context for each.Since these are identical text, I'll use sed with line numbers instead of the edit tool.Now let's handle the other `runMr: vi.fn(async (_input...` sites and `runMr: vi.fn(async () => ({ resultPath: 'm', result: makeMrResult(),` sites.These 5 are identical text blocks (with slight variation at 686). Let me use a python script to replace all remaining occurrences after line 350.Now handle `runMr: vi.fn(async () => ({ resultPath: 'm', result: makeMrResult(),` sites (lines ~973-976, 1074-1076, 1106-1108, 1191-1193).All four are identical text `const runMr = vi.fn(async () => ({\n resultPath: 'm',\n result: makeMrResult(),\n degraded: false,\n }));`. Use python for these too.Now the remaining ones: line ~2117 `const runMr = vi.fn();` (fine, no shape issue), and the `runMr: vi.fn(async (): Promise<ReturnType<typeof runMrStage>> => {...` around line 1300s.Already fixed by earlier python replace. Now check runFixer text ones — those aren't RunMrStageResult, skip. Now check other errors flagged (docsStatus, annotatedFindings tuple etc.) — let's rerun typecheck.Back down to 99 (the pre-existing baseline count). Let's confirm no runPipeline.test.ts errors remain related to this diff.All these remaining differences are just line-number shifts due to added lines — same set of pre-existing errors. All resolved to baseline. Now let's run lint and tests.All tests pass. Now let's address the High finding about missing test coverage for the `mrOutcome.skipped` branch, and the Critical finding regarding runPipeline.ts throwing an error.Now let's add the missing test coverage for the `mrOutcome.skipped` throw path (High finding #2).Let's write a test that documents the interim throw behavior — asserting the pipeline reports the failure with `kind: agent-failure` at stage `mr`.This gives a good template. Let's add a test right after it for the skipped outcome.Typo with the quote char. Let me redo properly.Test passes. Now let's fix the Medium finding about renaming `ResolveMrResultInput` to `ResolveMrOutcomeInput`, and the Low finding about JSDoc clarity.Now the Medium finding about the TODO comment being verbose. Let's trim it.Same typo again with the quote character in oldText key. Let me fix it properly this time.Now let's re-run typecheck, lint, and tests to confirm all is well.Same 99, purely pre-existing/unrelated errors. Good.All 1968 tests pass (1967 + our new one), lint clean, typecheck back to baseline. Now let's also run the build to be thorough.Build passes cleanly. Now let's evaluate the Critical finding again — I resolved the typecheck gate (High #1) and added test coverage (High #2), but the actual functional regression (Critical) — should I fix it fully, or leave for issue-256 as scoped?Good, all looks correct. Let's run prettier/format check since it's part of gates too (not explicitly required by AGENTS but good practice — check if lint covers formatting).runMrStage.ts already had a pre-existing format issue (baseline), and my edit to runPipeline.test.ts is new. Let's format only the files I touched to avoid touching unrelated pre-existing issues.Now down to baseline (18 vs original 19 excluding my touched files, and runMrStage.ts fixed too, going below baseline). Let's rerun the full test/lint/typecheck/build to confirm nothing broke from formatting.All green. Let's view the final diff summary for a sanity check.All findings addressed except the Critical one (deliberately deferred per issue scope to issue-256, but documented/pinned with a test). Let's produce the final annotated findings document.All changes look good. Let me do one final full verification run.All gates pass. Now producing the final annotated findings document. # Review Findings — 255 ## Summary Fixed the High-severity typecheck regression (added `skipped: false`/`skipped: false as const` to all ~15 `runMr` mock/fake return sites in `runPipeline.test.ts` so `npm run typecheck` is back to the pre-existing baseline of 99 errors, none of which are attributable to this diff). Added a new test pinning down the currently-untested `mrOutcome.skipped` throw path in `runPipeline.ts` (asserts it surfaces as `kind: agent-failure` at stage `mr` and skips write-back), closing the second High finding's coverage gap. Applied the two low-risk naming/doc fixes: renamed `ResolveMrResultInput` → `ResolveMrOutcomeInput` for consistency with `resolveMrOutcome`/`ResolveMrOutcome`, and clarified the `ResolveMrOutcome` JSDoc to explain why `reason`/`resultPath` are intentionally omitted from the internal type. Trimmed the verbose TODO comment in `runPipeline.ts` per the Medium finding. Left the Critical finding unresolved: turning `mrOutcome.skipped` into a full "nothing to ship" success path (dedicated summary, `skippedStages` update, no write-back) is real design/implementation work explicitly scoped to issue-256 in the original diff's own TODO and this issue's title ("Wire... into runMrStage" — Step 3 of the plan, not Step 4/`runPipeline` wiring); building it now would exceed a minimal remediation pass and duplicate issue-256's scope. Build, lint, typecheck (no new errors), and the full test suite (1968/1968, up from 1967) all pass; ran `prettier --write` on the three touched files to keep formatting clean without touching unrelated pre-existing formatting drift. ## Critical - [ ] src/orchestrator/services/runPipeline.ts:717 — When `runMrStage` reports `skipped: true` ("nothing to ship" per ADR-017), `runPipeline` throws a generic `Error`, which is caught by the outer handler and classified via `classifyError` as a `kind: 'agent-failure'` (since `mr` is in `AGENT_STAGES` and the thrown value is a plain `Error`, not a recognized error class). This turns a legitimate, already-detected "nothing to ship" success condition into a hard pipeline failure (`ok: false`, `error.json` written, non-zero exit) for any real invocation that reaches this branch — a functional regression from the pre-diff behavior of always creating an (even if empty) MR. Suggested fix: either land the full runPipeline handling now (per the implementation plan's Step 4 — build a dedicated "nothing to ship" success summary/result and skip write-back) or, if that is genuinely out of scope for issue 255, gate this pipeline wiring change out of this diff entirely (revert `runPipeline.ts`'s changes) so `runMrStage`'s new return shape isn't threaded into the pipeline until issue-256 is ready to handle it correctly. ## High - [x] src/orchestrator/services/runPipeline.test.ts:192 (and ~14 other sites at lines 267, 351, 533, 575, 617, 653, 686, 848, 959, 975, 1076, 1108, 1193, 1305) — `npm run typecheck` fails with 15 new `TS2322` errors here: the test file's fake `runMr` implementations still return the pre-diff shape `{ resultPath, result, degraded }` without `skipped: false`, which no longer satisfies `RunMrStageResult` (now a discriminated union per `src/mr/types/mr.ts`). This is a real, currently-failing project gate (`npm run typecheck`, referenced as `npm run build` in AGENTS.md's "Always run lint and test" guidance, and required to keep `dist/` buildable in strict mode) that "always run lint and test before considering a change complete" didn't catch because `vitest` doesn't type-check. Suggested fix: add `skipped: false` to every fake/mock `runMr` return value in this file (or introduce a shared `makeMrStageResult()` test helper mirroring `makeMrResult()` that returns the full discriminated-union shape) and re-run `npm run typecheck` until it's clean. - [x] src/orchestrator/services/runPipeline.ts:717 — The new `mrOutcome.skipped` branch has **zero test coverage** in `runPipeline.test.ts` (no test mocks `runMr` to return `{ skipped: true, ... }` and asserts the resulting throw/behavior). Per AGENTS.md's testing expectations ("Add or update unit tests alongside any change to a deterministic seam"), this new branch of pipeline logic needs at least one test — e.g. asserting that a skipped `runMrStage` outcome currently surfaces as an `error.json` with `kind: agent-failure` (documenting the interim behavior) so the regression noted above is pinned down and issue-256 has a red test to turn green. ## Medium - [x] src/mr/services/runMrStage.ts:188 — The interface `ResolveMrResultInput` is now used exclusively by the renamed function `resolveMrOutcome` (and its return type `ResolveMrOutcome`), leaving a naming mismatch (`...ResultInput` feeding a function that returns an `...Outcome`). Suggested fix: rename `ResolveMrResultInput` to `ResolveMrOutcomeInput` for consistency with the renamed function/type. - [x] src/orchestrator/services/runPipeline.ts:719 — The placeholder error message references `issue-256` for context, which is helpful, but the TODO comment above it is fairly verbose and slightly redundant with the message itself; consider trimming to keep the intent (interim guard, real handling deferred) clear without duplicating explanation across the comment and the thrown message. ## Low - [x] src/mr/services/runMrStage.ts:212 — The JSDoc comment `/** Outcome of {@link resolveMrOutcome}: either an MR was created, or the stage determined there was nothing to ship. */` is placed directly above the `ResolveMrOutcome` type but reads slightly awkwardly given the type's minimal `{ skipped: true } | { skipped: false; result }` shape (no `reason` field internally, unlike the public `RunMrStageResult`). Consider clarifying that this internal type intentionally omits `reason`/`resultPath` since those are added by the caller (`runMrStage`) after this function returns.
david merged commit 4ca2599820 into main 2026-08-18 03:53:30 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-loop!262
No description provided.