From 7184008b3205ea6b177151d9c8ca8b35c84d2f88 Mon Sep 17 00:00:00 2001 From: Lior Date: Thu, 28 May 2026 04:55:10 -0400 Subject: [PATCH] fix(B-0891): fix-fwd 4 tsc errors in zflash test-harness PoC (exactOptionalPropertyTypes + noUncheckedIndexedAccess) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #5724 (zflash test-harness PoC) race-merged through with non-required lint(tsc tools) failure per blocked-green-ci-investigate-threads.md auto-merge-race-with-follow-up-commit anti-pattern. Fix-fwd lands the tsc-clean substrate. 4 errors fixed: 1. run.ts:88 / 95 — array-index returns string | undefined under noUncheckedIndexedAccess; reify positional[0] into typed local + check undefined explicitly before returning ParsedArgs 2. run.ts:174 — message: undefined fails exactOptionalPropertyTypes; refactored to conditional return that omits message field when not set 3. scenarios.test.ts:68 — { ...SCENARIOS[0] } widens because SCENARIOS[0] is Scenario | undefined; reify first into typed local + assert before spread Gate verification: - bunx tsc --noEmit (tools/zflash) → 0 errors - bun test tools/zflash/test-harness/ → 12 pass / 0 fail / 26 expect Substrate-honest framing: race-merged content stays on main as substrate; fix-fwd lands the correction per substrate-honest discipline per auto-merge-race documentation in blocked-green-ci-investigate-threads.md. Composes with: PR #5724 (B-0891 PoC); blocked-green-ci-investigate-threads rule (Auto-merge-race-with-follow-up-commit anti-pattern); zeta-expected- branch.md race-window-caveat (isolated worktree workflow). Co-Authored-By: Claude Opus 4.7 --- tools/zflash/test-harness/run.ts | 25 ++++++++++++--------- tools/zflash/test-harness/scenarios.test.ts | 4 +++- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tools/zflash/test-harness/run.ts b/tools/zflash/test-harness/run.ts index e090a63526..4299f1f1ec 100644 --- a/tools/zflash/test-harness/run.ts +++ b/tools/zflash/test-harness/run.ts @@ -82,17 +82,19 @@ function parseArgs(argv: ReadonlyArray): ParsedArgs | { error: string } } const id = args[scenarioIdx + 1] as ScenarioId; const positional = args.filter((a, i) => i !== scenarioIdx && i !== scenarioIdx + 1 && !a.startsWith("--")); - if (positional.length === 0) { + const isoPath = positional[0]; + if (positional.length === 0 || isoPath === undefined) { return { error: "--scenario requires an ISO path positional argument" }; } - return { mode: "scenario", scenarioId: id, isoPath: positional[0] }; + return { mode: "scenario", scenarioId: id, isoPath }; } if (args.includes("--all")) { const positional = args.filter((a) => !a.startsWith("--")); - if (positional.length === 0) { + const isoPath = positional[0]; + if (positional.length === 0 || isoPath === undefined) { return { error: "--all requires an ISO path positional argument" }; } - return { mode: "all", isoPath: positional[0] }; + return { mode: "all", isoPath }; } return { error: `unrecognized arguments: ${args.join(" ")}` }; } @@ -171,12 +173,15 @@ function runComposingScenario(scenario: Scenario, isoPath: string): ScenarioResu stdio: "inherit", }); const durationMs = Date.now() - start; - return { - id: scenario.id, - status: result.status === 0 ? "passed" : "failed", - durationMs, - message: result.status === 0 ? undefined : `delegated harness exited ${result.status}`, - }; + const passed = result.status === 0; + return passed + ? { id: scenario.id, status: "passed", durationMs } + : { + id: scenario.id, + status: "failed", + durationMs, + message: `delegated harness exited ${result.status}`, + }; } function reportScaffolded(scenario: Scenario): ScenarioResult { diff --git a/tools/zflash/test-harness/scenarios.test.ts b/tools/zflash/test-harness/scenarios.test.ts index ce4fb89d09..3396b3a643 100644 --- a/tools/zflash/test-harness/scenarios.test.ts +++ b/tools/zflash/test-harness/scenarios.test.ts @@ -65,7 +65,9 @@ describe("B-0891 scenarios.ts invariants", () => { }); it("validateScenarios catches duplicate id", () => { - const dup = [...SCENARIOS, { ...SCENARIOS[0] }]; + const first = SCENARIOS[0]; + if (!first) throw new Error("SCENARIOS unexpectedly empty"); + const dup = [...SCENARIOS, { ...first }]; expect(() => validateScenarios(dup)).toThrow(); });