From c775248709726f9c3874d286a5ad7c11afc76ac1 Mon Sep 17 00:00:00 2001 From: Warren <28354219+jayminwest@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:28:10 +0000 Subject: [PATCH 1/3] mulch: dedupe local parseStrict* copies, import from numeric-flags sd mulch-fed1: src/commands/{ready,prime,rank,compact}.ts each had a copy of parseStrictPositiveInt (and rank.ts also of parseStrictNonNegativeNumber). The canonical implementation already lives in src/utils/numeric-flags.ts (covered by test/utils/numeric-flags.test.ts). Replace each local copy with an import. --- .mulch/expertise/refactoring.jsonl | 1 + .mulch/mulch.config.yaml | 1 + src/commands/compact.ts | 12 +----------- src/commands/prime.ts | 12 +----------- src/commands/rank.ts | 16 +--------------- src/commands/ready.ts | 12 +----------- 6 files changed, 6 insertions(+), 48 deletions(-) create mode 100644 .mulch/expertise/refactoring.jsonl diff --git a/.mulch/expertise/refactoring.jsonl b/.mulch/expertise/refactoring.jsonl new file mode 100644 index 0000000..a52c308 --- /dev/null +++ b/.mulch/expertise/refactoring.jsonl @@ -0,0 +1 @@ +{"type":"pattern","classification":"tactical","recorded_at":"2026-06-15T10:27:55.007Z","evidence":{"commit":"c6a07bbae64a7a8c94576195019f9d09e86d5ca8"},"dir_anchors":["src/commands"],"name":"dedupe-numeric-flag-parsers","description":"Local parseStrictPositiveInt/parseStrictNonNegativeNumber copies in src/commands/{ready,prime,rank,compact}.ts replaced by imports from src/utils/numeric-flags.ts (sd mulch-fed1).","files":[".mulch/mulch.config.yaml","src/commands/compact.ts","src/commands/prime.ts","src/commands/rank.ts","src/commands/ready.ts"],"id":"mx-ccfc7c"} diff --git a/.mulch/mulch.config.yaml b/.mulch/mulch.config.yaml index 8b465c9..efe8b62 100644 --- a/.mulch/mulch.config.yaml +++ b/.mulch/mulch.config.yaml @@ -58,6 +58,7 @@ domains: - convention - failure quality-gates: {} + refactoring: {} governance: max_entries: 100 warn_entries: 150 diff --git a/src/commands/compact.ts b/src/commands/compact.ts index d6f9cfd..12de97f 100644 --- a/src/commands/compact.ts +++ b/src/commands/compact.ts @@ -15,6 +15,7 @@ import { getRecordSummary } from "../utils/format.ts"; import { runHooks } from "../utils/hooks.ts"; import { outputJson, outputJsonError } from "../utils/json-output.ts"; import { withFileLock } from "../utils/lock.ts"; +import { parseStrictPositiveInt } from "../utils/numeric-flags.ts"; import { accent, brand, isQuiet } from "../utils/palette.ts"; // Payload sent to `pre-compact` hooks. A hook may print `{ replacement: = 1 ? n : null; -} - interface CompactCandidate { domain: string; type: RecordType; diff --git a/src/commands/prime.ts b/src/commands/prime.ts index 258ffaf..fa2b7f7 100644 --- a/src/commands/prime.ts +++ b/src/commands/prime.ts @@ -42,6 +42,7 @@ import { } from "../utils/git.ts"; import { runHooks } from "../utils/hooks.ts"; import { outputJsonError } from "../utils/json-output.ts"; +import { parseStrictPositiveInt } from "../utils/numeric-flags.ts"; import { brand, isQuiet } from "../utils/palette.ts"; import { buildSurfaceAnnotations, @@ -101,17 +102,6 @@ export function estimateRecordText(record: ExpertiseRecord): string { return def.formatCompactLine(record); } -// Strict numeric flag parsing — see mx-5b9578 / src/commands/rank.ts. -// `Number.parseInt("10abc", 10)` silently returns 10; use regex + Number() so -// typos like `--budget 5000abc` or `--budget 3.7` are rejected. -const POSITIVE_INT_RE = /^\d+$/; - -function parseStrictPositiveInt(raw: string): number | null { - if (!POSITIVE_INT_RE.test(raw)) return null; - const n = Number(raw); - return Number.isFinite(n) && n >= 1 ? n : null; -} - export function registerPrimeCommand(program: Command): void { program .command("prime") diff --git a/src/commands/rank.ts b/src/commands/rank.ts index 6009df2..4750619 100644 --- a/src/commands/rank.ts +++ b/src/commands/rank.ts @@ -6,6 +6,7 @@ import { getExpertisePath, readConfig } from "../utils/config.ts"; import { filterByType, readExpertiseFile } from "../utils/expertise.ts"; import { getRecordSummary } from "../utils/format.ts"; import { outputJson, outputJsonError } from "../utils/json-output.ts"; +import { parseStrictNonNegativeNumber, parseStrictPositiveInt } from "../utils/numeric-flags.ts"; import { accent } from "../utils/palette.ts"; import { computeConfirmationScore, type ScoredRecord } from "../utils/scoring.ts"; @@ -19,21 +20,6 @@ function formatScore(score: number): string { return Number.isInteger(score) ? String(score) : score.toFixed(1); } -const POSITIVE_INT_RE = /^\d+$/; -const NON_NEGATIVE_NUMBER_RE = /^\d+(\.\d+)?$/; - -function parseStrictPositiveInt(raw: string): number | null { - if (!POSITIVE_INT_RE.test(raw)) return null; - const n = Number(raw); - return Number.isFinite(n) && n >= 1 ? n : null; -} - -function parseStrictNonNegativeNumber(raw: string): number | null { - if (!NON_NEGATIVE_NUMBER_RE.test(raw)) return null; - const n = Number(raw); - return Number.isFinite(n) && n >= 0 ? n : null; -} - export function registerRankCommand(program: Command): void { program .command("rank") diff --git a/src/commands/ready.ts b/src/commands/ready.ts index 02a5cf3..50e2455 100644 --- a/src/commands/ready.ts +++ b/src/commands/ready.ts @@ -5,6 +5,7 @@ import { getExpertisePath, readConfig } from "../utils/config.ts"; import { readExpertiseFile } from "../utils/expertise.ts"; import { formatTimeAgo, getRecordSummary } from "../utils/format.ts"; import { outputJson, outputJsonError } from "../utils/json-output.ts"; +import { parseStrictPositiveInt } from "../utils/numeric-flags.ts"; import { accent } from "../utils/palette.ts"; interface AnnotatedRecord { @@ -12,17 +13,6 @@ interface AnnotatedRecord { record: ExpertiseRecord; } -// Strict numeric flag parsing — see mx-5b9578 / src/commands/rank.ts. -// `Number.parseInt("10abc", 10)` silently returns 10; use regex + Number() so -// typos like `--limit 10abc` or `--limit 3.7` are rejected. -const POSITIVE_INT_RE = /^\d+$/; - -function parseStrictPositiveInt(raw: string): number | null { - if (!POSITIVE_INT_RE.test(raw)) return null; - const n = Number(raw); - return Number.isFinite(n) && n >= 1 ? n : null; -} - function parseDuration(input: string): number { const match = input.match(/^(\d+)(h|d|w)$/); if (!match) { From 7efe7d1ed68936d6769d0f72afc7346bafb0ff00 Mon Sep 17 00:00:00 2001 From: Warren <28354219+jayminwest@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:28:23 +0000 Subject: [PATCH 2/3] seeds: close mulch-fed1 --- .seeds/issues.jsonl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.seeds/issues.jsonl b/.seeds/issues.jsonl index f201fbb..2cf8cf3 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:18:09.429Z","labels":["patrol","nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-fed1","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:28:21.296Z","labels":["patrol","nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-4bd0","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":"open","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:08:46.513Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"]} +{"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:21.296Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"],"closedAt":"2026-06-15T10:28:21.296Z"} {"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-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:18:09.429Z","labels":["nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-fed1","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:28:21.296Z","labels":["nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-4bd0","mulch-9465","mulch-a424","mulch-4dc4"],"blocks":["mulch-e3e9"]} From db5827efd6d2dc8107a345eff93ee6e44be22f0f Mon Sep 17 00:00:00 2001 From: warren Date: Mon, 15 Jun 2026 10:28:34 +0000 Subject: [PATCH 3/3] 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 2cf8cf3..7117d92 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:21.296Z","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:28:34.748Z","labels":["patrol","nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-4bd0","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:21.296Z","labels":["nightwatch"],"plan_id":"pl-b637","blocks":["mulch-0768","mulch-e3e9"],"closedAt":"2026-06-15T10:28:21.296Z"} +{"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-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:21.296Z","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:28:34.748Z","labels":["nightwatch"],"plan_id":"pl-b637","blockedBy":["mulch-4bd0","mulch-9465","mulch-a424","mulch-4dc4"],"blocks":["mulch-e3e9"]}