From 78838b0c42e216a828212987c3d312adeaa7d29b Mon Sep 17 00:00:00 2001 From: nightwatch-agent Date: Mon, 15 Jun 2026 10:37:55 +0000 Subject: [PATCH 1/2] mulch: replace (err as Error).message with instanceof Error guard Replaces unsafe '(err as Error).message' with the type-safe 'err instanceof Error ? err.message : String(err)' pattern across src/commands/ and src/utils/. Non-Error throws (strings, objects) now stringify cleanly instead of yielding 'undefined' messages. Closes sd mulch-4bd0. --- .mulch/expertise/patterns.jsonl | 1 + .mulch/mulch.config.yaml | 1 + src/commands/archive.ts | 6 ++++-- src/commands/compact.ts | 2 +- src/commands/config.ts | 10 ++++++---- src/commands/delete-domain.ts | 4 ++-- src/commands/delete.ts | 6 ++++-- src/commands/diff.ts | 4 ++-- src/commands/doctor.ts | 2 +- src/commands/edit.ts | 4 ++-- src/commands/learn.ts | 4 ++-- src/commands/move.ts | 7 +++++-- src/commands/onboard.ts | 4 ++-- src/commands/prime.ts | 4 ++-- src/commands/query.ts | 4 ++-- src/commands/rank.ts | 2 +- src/commands/ready.ts | 10 ++++++---- src/commands/restore.ts | 4 ++-- src/commands/search.ts | 6 ++++-- src/commands/sync.ts | 15 +++++++++++---- src/utils/config.ts | 2 +- 21 files changed, 62 insertions(+), 40 deletions(-) create mode 100644 .mulch/expertise/patterns.jsonl diff --git a/.mulch/expertise/patterns.jsonl b/.mulch/expertise/patterns.jsonl new file mode 100644 index 0000000..1daf51c --- /dev/null +++ b/.mulch/expertise/patterns.jsonl @@ -0,0 +1 @@ +{"type":"pattern","classification":"tactical","recorded_at":"2026-06-15T10:37:47.637Z","evidence":{"commit":"e68a2ae9df420c2de7d30026727396d6c265306f"},"dir_anchors":["src/commands"],"name":"safe-error-message","description":"Use 'err instanceof Error ? err.message : String(err)' instead of '(err as Error).message' in catch blocks to safely handle non-Error throws","files":[".mulch/mulch.config.yaml","src/commands/archive.ts","src/commands/compact.ts","src/commands/config.ts","src/commands/delete-domain.ts","src/commands/delete.ts","src/commands/diff.ts","src/commands/doctor.ts","src/commands/edit.ts","src/commands/learn.ts","src/commands/move.ts","src/commands/onboard.ts","src/commands/prime.ts","src/commands/query.ts","src/commands/rank.ts","src/commands/ready.ts","src/commands/restore.ts","src/commands/search.ts","src/commands/sync.ts","src/utils/config.ts"],"id":"mx-357d18"} diff --git a/.mulch/mulch.config.yaml b/.mulch/mulch.config.yaml index efe8b62..40d1e8c 100644 --- a/.mulch/mulch.config.yaml +++ b/.mulch/mulch.config.yaml @@ -59,6 +59,7 @@ domains: - failure quality-gates: {} refactoring: {} + patterns: {} governance: max_entries: 100 warn_entries: 150 diff --git a/src/commands/archive.ts b/src/commands/archive.ts index b062ee9..5016bcd 100644 --- a/src/commands/archive.ts +++ b/src/commands/archive.ts @@ -204,9 +204,11 @@ export function registerArchiveCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("archive", (err as Error).message); + outputJsonError("archive", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error( + chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`), + ); } } process.exitCode = 1; diff --git a/src/commands/compact.ts b/src/commands/compact.ts index 12de97f..1af4e56 100644 --- a/src/commands/compact.ts +++ b/src/commands/compact.ts @@ -773,7 +773,7 @@ async function handleApply( try { indicesToRemove = resolveRecordIds(records, identifiers); } catch (err) { - const msg = (err as Error).message; + const msg = err instanceof Error ? err.message : String(err); if (jsonMode) { outputJsonError("compact", msg); } else { diff --git a/src/commands/config.ts b/src/commands/config.ts index 89ee8ae..bf1b84f 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -32,7 +32,7 @@ export function registerConfigCommand(program: Command): void { try { cfg = await readConfig(); } catch (err) { - process.stderr.write(`${(err as Error).message}\n`); + process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`); process.exitCode = 1; return; } @@ -72,7 +72,7 @@ export function registerConfigCommand(program: Command): void { try { await runConfigSet(path, value); } catch (err) { - process.stderr.write(`${(err as Error).message}\n`); + process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`); process.exitCode = 1; } }); @@ -90,7 +90,7 @@ export function registerConfigCommand(program: Command): void { try { await runConfigUnset(path); } catch (err) { - process.stderr.write(`${(err as Error).message}\n`); + process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`); process.exitCode = 1; } }); @@ -119,7 +119,9 @@ async function runConfigSet(rawPath: string, rawValue: string): Promise { try { parsedValue = yaml.load(rawValue); } catch (err) { - throw new Error(`Invalid YAML for : ${(err as Error).message}`); + throw new Error( + `Invalid YAML for : ${err instanceof Error ? err.message : String(err)}`, + ); } await withFileLock(configPath, async () => { diff --git a/src/commands/delete-domain.ts b/src/commands/delete-domain.ts index 92f9ff3..10652d2 100644 --- a/src/commands/delete-domain.ts +++ b/src/commands/delete-domain.ts @@ -110,9 +110,9 @@ export function registerDeleteDomainCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("delete-domain", (err as Error).message); + outputJsonError("delete-domain", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } } process.exitCode = 1; diff --git a/src/commands/delete.ts b/src/commands/delete.ts index a263585..d78a666 100644 --- a/src/commands/delete.ts +++ b/src/commands/delete.ts @@ -306,9 +306,11 @@ export function registerDeleteCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("delete", (err as Error).message); + outputJsonError("delete", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error( + chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`), + ); } } process.exitCode = 1; diff --git a/src/commands/diff.ts b/src/commands/diff.ts index 5b7966d..dfc9fee 100644 --- a/src/commands/diff.ts +++ b/src/commands/diff.ts @@ -190,9 +190,9 @@ export function registerDiffCommand(program: Command): void { } } catch (err) { if (jsonMode) { - outputJsonError("diff", (err as Error).message); + outputJsonError("diff", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } process.exitCode = 1; } diff --git a/src/commands/doctor.ts b/src/commands/doctor.ts index 94e7375..6c94c44 100644 --- a/src/commands/doctor.ts +++ b/src/commands/doctor.ts @@ -64,7 +64,7 @@ async function checkConfig(cwd?: string): Promise { return { name: "config", status: "fail", - message: `Config error: ${(err as Error).message}`, + message: `Config error: ${err instanceof Error ? err.message : String(err)}`, fixable: false, details: [], }; diff --git a/src/commands/edit.ts b/src/commands/edit.ts index 822ffce..e3582f8 100644 --- a/src/commands/edit.ts +++ b/src/commands/edit.ts @@ -215,9 +215,9 @@ export function registerEditCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("edit", (err as Error).message); + outputJsonError("edit", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } } process.exitCode = 1; diff --git a/src/commands/learn.ts b/src/commands/learn.ts index 54b8204..eaf1fc8 100644 --- a/src/commands/learn.ts +++ b/src/commands/learn.ts @@ -169,9 +169,9 @@ export function registerLearnCommand(program: Command): void { console.log(); } catch (err) { if (jsonMode) { - outputJsonError("learn", (err as Error).message); + outputJsonError("learn", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } process.exitCode = 1; } diff --git a/src/commands/move.ts b/src/commands/move.ts index ae6f4bc..bcf96f7 100644 --- a/src/commands/move.ts +++ b/src/commands/move.ts @@ -313,8 +313,11 @@ export function registerMoveCommand(program: Command): void { if (jsonMode) outputJsonError("move", msg); else console.error(chalk.red(`Error: ${msg}`)); } else { - if (jsonMode) outputJsonError("move", (err as Error).message); - else console.error(chalk.red(`Error: ${(err as Error).message}`)); + if (jsonMode) outputJsonError("move", err instanceof Error ? err.message : String(err)); + else + console.error( + chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`), + ); } process.exitCode = 1; } diff --git a/src/commands/onboard.ts b/src/commands/onboard.ts index 9d4be73..82ad6f9 100644 --- a/src/commands/onboard.ts +++ b/src/commands/onboard.ts @@ -364,9 +364,9 @@ export function registerOnboardCommand(program: Command): void { await runOnboard({ ...options, jsonMode }); } catch (err) { if (jsonMode) { - outputJsonError("onboard", (err as Error).message); + outputJsonError("onboard", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } process.exitCode = 1; } diff --git a/src/commands/prime.ts b/src/commands/prime.ts index fa2b7f7..e74f09d 100644 --- a/src/commands/prime.ts +++ b/src/commands/prime.ts @@ -667,9 +667,9 @@ export function registerPrimeCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("prime", (err as Error).message); + outputJsonError("prime", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } } process.exitCode = 1; diff --git a/src/commands/query.ts b/src/commands/query.ts index 5387d0f..ea0d2dc 100644 --- a/src/commands/query.ts +++ b/src/commands/query.ts @@ -207,9 +207,9 @@ export function registerQueryCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("query", (err as Error).message); + outputJsonError("query", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } } process.exitCode = 1; diff --git a/src/commands/rank.ts b/src/commands/rank.ts index 4750619..efd4a0b 100644 --- a/src/commands/rank.ts +++ b/src/commands/rank.ts @@ -152,7 +152,7 @@ export function registerRankCommand(program: Command): void { console.error(chalk.red(`Error: ${msg}`)); } } else { - const msg = (err as Error).message; + const msg = err instanceof Error ? err.message : String(err); if (jsonMode) { outputJsonError("rank", msg); } else { diff --git a/src/commands/ready.ts b/src/commands/ready.ts index 50e2455..d5d152e 100644 --- a/src/commands/ready.ts +++ b/src/commands/ready.ts @@ -86,9 +86,11 @@ export function registerReadyCommand(program: Command): void { sinceMs = parseDuration(options.since); } catch (err) { if (jsonMode) { - outputJsonError("ready", (err as Error).message); + outputJsonError("ready", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error( + chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`), + ); } process.exitCode = 1; return; @@ -168,9 +170,9 @@ export function registerReadyCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("ready", (err as Error).message); + outputJsonError("ready", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } } process.exitCode = 1; diff --git a/src/commands/restore.ts b/src/commands/restore.ts index a10163f..ec36f66 100644 --- a/src/commands/restore.ts +++ b/src/commands/restore.ts @@ -155,9 +155,9 @@ export function registerRestoreCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("restore", (err as Error).message); + outputJsonError("restore", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error(chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`)); } } process.exitCode = 1; diff --git a/src/commands/search.ts b/src/commands/search.ts index 1f383e7..047247c 100644 --- a/src/commands/search.ts +++ b/src/commands/search.ts @@ -320,9 +320,11 @@ export function registerSearchCommand(program: Command): void { } } else { if (jsonMode) { - outputJsonError("search", (err as Error).message); + outputJsonError("search", err instanceof Error ? err.message : String(err)); } else { - console.error(chalk.red(`Error: ${(err as Error).message}`)); + console.error( + chalk.red(`Error: ${err instanceof Error ? err.message : String(err)}`), + ); } } process.exitCode = 1; diff --git a/src/commands/sync.ts b/src/commands/sync.ts index 3e21d89..d4aa7f9 100644 --- a/src/commands/sync.ts +++ b/src/commands/sync.ts @@ -223,9 +223,14 @@ export function registerSyncCommand(program: Command): void { } } catch (err) { if (jsonMode) { - outputJsonError("sync", `Validation error: ${(err as Error).message}`); + outputJsonError( + "sync", + `Validation error: ${err instanceof Error ? err.message : String(err)}`, + ); } else { - console.error(chalk.red(`Validation error: ${(err as Error).message}`)); + console.error( + chalk.red(`Validation error: ${err instanceof Error ? err.message : String(err)}`), + ); } process.exitCode = 1; return; @@ -288,9 +293,11 @@ export function registerSyncCommand(program: Command): void { } } catch (err) { if (jsonMode) { - outputJsonError("sync", `Git error: ${(err as Error).message}`); + outputJsonError("sync", `Git error: ${err instanceof Error ? err.message : String(err)}`); } else { - console.error(chalk.red(`Git error: ${(err as Error).message}`)); + console.error( + chalk.red(`Git error: ${err instanceof Error ? err.message : String(err)}`), + ); } process.exitCode = 1; } diff --git a/src/utils/config.ts b/src/utils/config.ts index abc95bb..806b870 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -265,7 +265,7 @@ export async function readConfig(cwd: string = process.cwd()): Promise Date: Mon, 15 Jun 2026 10:38:05 +0000 Subject: [PATCH 2/2] chore(warren): seeds state --- .seeds/issues.jsonl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.seeds/issues.jsonl b/.seeds/issues.jsonl index 7117d92..e22b3b9 100644 --- a/.seeds/issues.jsonl +++ b/.seeds/issues.jsonl @@ -332,11 +332,11 @@ {"id":"mulch-ab79","title":"Add the canonical scripts/check-all.ts quiet runner copied byte-identical from templates/l5-toolkit/scripts/check-all.ts, with mulch's exported GATES manifest in the standard's order: lint, typecheck, check:agents, check:dups, check:deps, check:size, check:debt, check:coverage, check:ci-parity (last) -- note lint and typecheck must be ADDED to the manifest (they exist as scripts but are absent from the current && chain). Replace package.json check:all with `bun scripts/check-all.ts`, add `verify`: `bun run check:all`, and add scripts/check-all.test.ts. Confirm the quiet-output contract.","status":"closed","type":"task","priority":2,"plan_step_index":0,"description":"\nStep 1 of plan pl-237d.\n\nParent seed: mulch-f16f — Adopt canonical check:all standard\nPlan template: feature\nPlan approach: Swap the && chain for the canonical quiet runner with a GATES manifest in the standard's order (folding in the already-present lint and typecheck scripts), then add the generalized ci-parity gate and the verify alias. mulch has no UI…\n\nRun `sd plan show pl-237d` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-12T04:53:28.928Z","updatedAt":"2026-06-12T05:56:59.979Z","plan_id":"pl-237d","blocks":["mulch-c658","mulch-f16f"],"closedAt":"2026-06-12T05:56:59.979Z"} {"id":"mulch-c658","title":"Add scripts/check-ci-parity.ts (with test) copied from templates/l5-toolkit/scripts, importing the GATES array from check-all.ts; add the check:ci-parity script as the final gate in the manifest. Reconcile any residual non-canonical gate name. Verify check:ci-parity passes against .github/workflows/ci.yml.","status":"closed","type":"task","priority":2,"plan_step_index":1,"description":"\nStep 2 of plan pl-237d.\n\nParent seed: mulch-f16f — Adopt canonical check:all standard\nPlan template: feature\nPlan approach: Swap the && chain for the canonical quiet runner with a GATES manifest in the standard's order (folding in the already-present lint and typecheck scripts), then add the generalized ci-parity gate and the verify alias. mulch has no UI…\n\nRun `sd plan show pl-237d` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-12T04:53:28.928Z","updatedAt":"2026-06-12T05:57:00.030Z","plan_id":"pl-237d","blocks":["mulch-c6f8","mulch-f16f"],"closedAt":"2026-06-12T05:57:00.030Z"} {"id":"mulch-c6f8","title":"Wire .github/workflows/ci.yml to invoke the canonical gates (or `bun run check:all`) so the local manifest matches CI. Run `bun run check:all` and `bun run verify` green end-to-end with the quiet-output contract and a passing check:ci-parity.","status":"closed","type":"task","priority":2,"plan_step_index":2,"description":"\nStep 3 of plan pl-237d.\n\nParent seed: mulch-f16f — Adopt canonical check:all standard\nPlan template: feature\nPlan approach: Swap the && chain for the canonical quiet runner with a GATES manifest in the standard's order (folding in the already-present lint and typecheck scripts), then add the generalized ci-parity gate and the verify alias. mulch has no UI…\n\nRun `sd plan show pl-237d` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-12T04:53:28.928Z","updatedAt":"2026-06-12T05:57:00.083Z","plan_id":"pl-237d","blocks":["mulch-f16f"],"closedAt":"2026-06-12T05:57:00.083Z"} -{"id":"mulch-e3e9","title":"nightwatch patrol: 2026-06-15","status":"open","type":"task","priority":3,"createdAt":"2026-06-15T10:08:07.957Z","updatedAt":"2026-06-15T10:28:34.748Z","labels":["patrol","nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-4bd0","mulch-9465","mulch-a424","mulch-4dc4","mulch-0768"]} +{"id":"mulch-e3e9","title":"nightwatch patrol: 2026-06-15","status":"open","type":"task","priority":3,"createdAt":"2026-06-15T10:08:07.957Z","updatedAt":"2026-06-15T10:38:05.028Z","labels":["patrol","nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-9465","mulch-a424","mulch-4dc4","mulch-0768"]} {"id":"mulch-4ca6","title":"Canonicalize JSON `up_to_date` vs `upToDate` across upgrade/onboard output","status":"closed","type":"task","priority":3,"plan_step_index":0,"description":"\nStep 1 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:18:09.429Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"],"closedAt":"2026-06-15T10:18:09.429Z","closeReason":"Landed in 49f3aef: ml upgrade --json now emits up_to_date (snake_case) to match ml onboard --json. Test + CHANGELOG updated. bun run verify 9/9 green."} {"id":"mulch-fed1","title":"Dedupe local parseStrictPositiveInt/parseStrictNonNegativeNumber copies; import from src/utils/numeric-flags.ts","status":"closed","type":"task","priority":3,"plan_step_index":1,"description":"\nStep 2 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:28:34.748Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"],"closedAt":"2026-06-15T10:28:34.748Z"} -{"id":"mulch-4bd0","title":"Replace `(err as Error).message` with `err instanceof Error ? err.message : String(err)` across src/commands and src/utils","status":"open","type":"task","priority":3,"plan_step_index":2,"description":"\nStep 3 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:08:46.513Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"]} +{"id":"mulch-4bd0","title":"Replace `(err as Error).message` with `err instanceof Error ? err.message : String(err)` across src/commands and src/utils","status":"closed","type":"task","priority":3,"plan_step_index":2,"description":"\nStep 3 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:38:05.028Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"],"extensions":{"role":"pi","lastRunId":"run_ch27daacnb7q","lastRunAt":"2026-06-15T10:32:18.766Z"},"closedAt":"2026-06-15T10:38:05.028Z"} {"id":"mulch-9465","title":"Fix path-segment boundary in src/utils/git.ts fileMatchesAny + add false-positive coverage","status":"open","type":"task","priority":2,"plan_step_index":3,"description":"\nStep 4 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:08:46.513Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"]} {"id":"mulch-a424","title":"Unify JSON vs human error wording in src/commands/ready.ts unknown-domain branch","status":"open","type":"task","priority":3,"plan_step_index":4,"description":"\nStep 5 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:08:46.513Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"]} {"id":"mulch-4dc4","title":"Backfill direct unit tests for src/utils/domain-rules.ts and src/utils/format-helpers.ts","status":"open","type":"task","priority":3,"plan_step_index":5,"description":"\nStep 6 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:08:46.513Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"]} -{"id":"mulch-0768","title":"Release: run /release per .claude/commands/release.md","status":"open","type":"task","priority":3,"plan_step_index":6,"description":"\nStep 7 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:28:34.748Z","labels":["nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-4bd0","mulch-9465","mulch-a424","mulch-4dc4"],"blocks":["mulch-e3e9"]} +{"id":"mulch-0768","title":"Release: run /release per .claude/commands/release.md","status":"open","type":"task","priority":3,"plan_step_index":6,"description":"\nStep 7 of plan pl-b637.\n\nParent seed: mulch-e3e9 — nightwatch patrol: 2026-06-15\nPlan template: refactor\nPlan approach: One step per finding so each lands as an isolated PR. Independent steps (1, 2, 3, 4, 5, 6) run in parallel; the release step (7) batches them. Step 1 (JSON key canonicalization) and step 4 (suffix-boundary fix) are the only…\n\nRun `sd plan show pl-b637` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-15T10:08:46.513Z","updatedAt":"2026-06-15T10:38:05.028Z","labels":["nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-9465","mulch-a424","mulch-4dc4"],"blocks":["mulch-e3e9"]}