From d474d64cc85261e311f480c950f749ebe961eef4 Mon Sep 17 00:00:00 2001 From: warren Date: Mon, 8 Jun 2026 10:09:19 +0000 Subject: [PATCH] chore(warren): seeds state --- .seeds/issues.jsonl | 6 ++++++ .seeds/plans.jsonl | 1 + 2 files changed, 7 insertions(+) diff --git a/.seeds/issues.jsonl b/.seeds/issues.jsonl index f578314..23a4440 100644 --- a/.seeds/issues.jsonl +++ b/.seeds/issues.jsonl @@ -328,3 +328,9 @@ {"id":"mulch-f7bf","title":"T5: adaptive parallel shard runner (core-count agnostic)","status":"open","type":"task","priority":1,"createdAt":"2026-05-28T19:15:54.049Z","updatedAt":"2026-05-28T19:16:03.298Z","description":"GOAL: `bun test` runs all files sequentially in ONE process, so wall-clock = sum of file times. Add a runner that splits test files into N balanced shards and runs them as concurrent `bun test ` processes. Shard count MUST be derived from the runtime environment — never hardcoded to any developer machine spec.\n\nWorking dir: cd /Users/jayminwest/Projects/os-eco/mulch\nINDEPENDENT build (blocks T6).\n\nFILES TO ADD:\n- ADD scripts/test-parallel.ts (run via Bun):\n - Determine shard count: `MULCH_TEST_SHARDS` env > `--shards N` arg > Math.max(1, os.availableParallelism?.() ?? os.cpus().length). Cap to number of test files.\n - Discover test files (glob test/**/*.test.ts).\n - Weight files by historical timing parsed from junit.xml when present (reuse parseJUnit from scripts/report-test-timing.ts), else by file size; greedily bin-pack into shards for balance.\n - Spawn one `bun test ` per shard concurrently (Bun.spawn), stream/prefix output, await all, exit non-zero if ANY shard fails.\n - SEQUENTIAL FALLBACK: if resolved shards <= 1, exec a single plain `bun test` (identical behavior to today).\n- ADD package.json script: \"test:parallel\": \"bun run scripts/test-parallel.ts\".\n- ADD scripts/test-parallel.test.ts: unit-test the pure pieces (shard-count resolution precedence, bin-packing balance, sequential fallback) — pure functions, no real spawning.\n\nDESIGN CONSTRAINTS:\n- Core-count agnostic + cloud-safe: works on a 1-vCPU box (falls back to sequential), a 2-4 vCPU cloud dev box, and large CI runners with the SAME code. No machine-specific constants.\n- Tests already isolate via mkdtemp unique prefixes, so cross-shard temp-dir collisions are not a concern.\n- Deterministic file->shard assignment given the same inputs (stable sort) for reproducibility.\n\nVERIFICATION (must exit 0):\n bun run lint && bun run typecheck\n bun test scripts/test-parallel.test.ts\n MULCH_TEST_SHARDS=1 bun run test:parallel # sequential fallback, full suite green\n MULCH_TEST_SHARDS=4 bun run test:parallel # parallel, same pass/fail outcome + lower wall-clock\n\nACCEPTANCE:\n- Same overall pass/fail result as `bun test` at any shard count.\n- Wall-clock scales down with shard count; sequential fallback exactly mirrors plain `bun test`.\n\nPITFALLS:\n- Aggregate exit code: fail if ANY shard fails (do not mask with tail/pipe).\n- os.availableParallelism may be undefined on older runtimes — guard with cpus().length.\n- Coverage merge across shards is handled in T6, but design spawn args so per-shard `--coverage --coverage-reporter=lcov` with distinct outfiles is possible.","blocks":["mulch-d61d","mulch-bee4"]} {"id":"mulch-d61d","title":"T6: wire CI to sharded runner + coverage merge","status":"open","type":"task","priority":2,"createdAt":"2026-05-28T19:15:54.152Z","updatedAt":"2026-05-28T19:16:03.352Z","description":"GOAL: Use the T5 shard runner in CI with an EXPLICIT, deterministic shard matrix (not derived from runner core count, so behavior is reproducible across runner sizes), merge per-shard coverage, and keep the timing report.\n\nWorking dir: cd /Users/jayminwest/Projects/os-eco/mulch\nDEPENDS ON: T5 (runner must exist).\n\nFILES TO EDIT:\n- .github/workflows/ci.yml:\n - Replace the single test:ci step with a small matrix (e.g. shard: [1,2,3,4], total 4) OR a single job invoking `MULCH_TEST_SHARDS=4 bun run test:parallel` — choose whichever keeps coverage merge simplest; document the choice in the job.\n - Each shard runs `bun test --coverage --coverage-reporter=lcov --coverage-reporter=...` writing a shard-unique lcov path, plus --reporter=junit to a shard-unique junit file.\n - Add a merge step: combine shard lcov files into coverage/lcov.info (concatenate/merge) and shard junit files into junit.xml so existing check:coverage + report:timing + report:quality keep working unchanged.\n - Keep uploading coverage/lcov.info and junit.xml artifacts (VAL-CROSS-003) and appending report:timing to GITHUB_STEP_SUMMARY.\n\nVERIFICATION (must exit 0):\n bun run lint && bun run typecheck\n # Locally emulate: run the runner with coverage, merge, then:\n bun run check:coverage\n bun run report:timing\n ls -la coverage/lcov.info junit.xml\n\nACCEPTANCE:\n- CI wall-clock for tests drops vs the previous single sequential run.\n- Merged coverage feeds check:coverage with no budget regression; merged junit feeds report:timing.\n- Matrix is explicit/deterministic — independent of the physical runner's core count.\n\nPITFALLS:\n- lcov merge: same source files appear in multiple shards' reports only if executed there; merge must union records, not double-count lines (use a real lcov merge approach, not naive concat if it corrupts totals — verify check:coverage numbers are sane).\n- junit merge must keep total tests = sum across shards for report:timing accuracy.\n- Do not regress check:all ordering or artifact-upload steps.","blockedBy":["mulch-f7bf"],"blocks":["mulch-bee4"]} {"id":"mulch-35c9","title":"pino-pretty transport fails for consumers (move to dep or fall back to JSON)","status":"closed","type":"bug","priority":2,"createdAt":"2026-06-02T19:20:56.332Z","updatedAt":"2026-06-02T19:23:22.152Z","description":"Symptom: running ml on a machine where mulch was installed without devDependencies (e.g. npm i -g @os-eco/mulch-cli, bun add, production install) throws: 'error: unable to determine transport target for \"pino-pretty\"'.\n\nRoot cause: pino-pretty is a devDependency, but src/log.ts createLogger() selects the pino-pretty transport whenever process.stderr.isTTY === true && MULCH_LOG_JSON !== \"1\". Published consumers running in an interactive terminal hit the pretty path, pino tries to load the pino-pretty transport target, the package is absent, and it throws. The file header claims published consumers 'get JSON logs and never need the pretty transport', but that only holds for non-TTY environments today.\n\nCurrent workaround (forces JSON path): MULCH_LOG_JSON=1 ml \n\nProper fix (preferred): make the pretty transport optional in src/log.ts. Guard the pino-pretty transport behind a resolvability check (e.g. require.resolve('pino-pretty') in try/catch, or try/catch around logger construction) and fall back to JSON-to-stderr (pino.destination(2)) when pino-pretty is not installed. This keeps pretty output for dev while honoring the documented JSON-for-consumers promise.\n\nAlternative: move pino-pretty from devDependencies to dependencies so it always ships — simpler but bloats the published install with a dev-only transport.\n\nAcceptance:\n- ml runs in an interactive TTY without pino-pretty installed and does NOT throw; logs fall back to JSON on stderr.\n- Dev environments (pino-pretty present, TTY) still get pretty output.\n- Add a test in src/log.test.ts (or test/) covering the missing-pino-pretty fallback path.","labels":["area:cli"],"closedAt":"2026-06-02T19:23:22.152Z"} +{"id":"mulch-273e","title":"nightwatch patrol: 2026-06-08","status":"open","type":"task","priority":3,"createdAt":"2026-06-08T10:08:08.666Z","updatedAt":"2026-06-08T10:09:09.860Z","labels":["patrol","nightwatch"],"plan_id":"pl-1557","blockedBy":["mulch-ac84","mulch-6bb5","mulch-2d07","mulch-05e0","mulch-350f"]} +{"id":"mulch-ac84","title":"Dedup parseStrict* helpers: import from src/utils/numeric-flags.ts in ready/prime/compact/rank","status":"open","type":"task","priority":3,"plan_step_index":0,"description":"\nStep 1 of plan pl-1557.\n\nParent seed: mulch-273e — nightwatch patrol: 2026-06-08\nPlan template: refactor\nPlan approach: Land each cleanup as its own small PR so the ratchet gates verify it in isolation. Start with the independent fixes (numeric-flag dedup, unused-barrel removal, unused-export pruning) since they touch disjoint files. Then refactor the…\n\nRun `sd plan show pl-1557` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-08T10:09:09.860Z","updatedAt":"2026-06-08T10:09:09.860Z","labels":["nightwatch","refactor","cleanup"],"plan_id":"pl-1557","blocks":["mulch-350f","mulch-273e"]} +{"id":"mulch-6bb5","title":"Delete unused barrel src/utils/index.ts","status":"open","type":"task","priority":3,"plan_step_index":1,"description":"\nStep 2 of plan pl-1557.\n\nParent seed: mulch-273e — nightwatch patrol: 2026-06-08\nPlan template: refactor\nPlan approach: Land each cleanup as its own small PR so the ratchet gates verify it in isolation. Start with the independent fixes (numeric-flag dedup, unused-barrel removal, unused-export pruning) since they touch disjoint files. Then refactor the…\n\nRun `sd plan show pl-1557` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-08T10:09:09.860Z","updatedAt":"2026-06-08T10:09:09.860Z","labels":["nightwatch","cleanup","dead-code"],"plan_id":"pl-1557","blocks":["mulch-350f","mulch-273e"]} +{"id":"mulch-2d07","title":"Prune unused exports + exported types flagged by knip","status":"open","type":"task","priority":3,"plan_step_index":2,"description":"\nStep 3 of plan pl-1557.\n\nParent seed: mulch-273e — nightwatch patrol: 2026-06-08\nPlan template: refactor\nPlan approach: Land each cleanup as its own small PR so the ratchet gates verify it in isolation. Start with the independent fixes (numeric-flag dedup, unused-barrel removal, unused-export pruning) since they touch disjoint files. Then refactor the…\n\nRun `sd plan show pl-1557` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-08T10:09:09.860Z","updatedAt":"2026-06-08T10:09:09.860Z","labels":["nightwatch","cleanup","dead-code"],"plan_id":"pl-1557","blocks":["mulch-350f","mulch-273e"]} +{"id":"mulch-05e0","title":"Split test/commands/record.test.ts so check:size ratchet goes green","status":"open","type":"task","priority":2,"plan_step_index":3,"description":"\nStep 4 of plan pl-1557.\n\nParent seed: mulch-273e — nightwatch patrol: 2026-06-08\nPlan template: refactor\nPlan approach: Land each cleanup as its own small PR so the ratchet gates verify it in isolation. Start with the independent fixes (numeric-flag dedup, unused-barrel removal, unused-export pruning) since they touch disjoint files. Then refactor the…\n\nRun `sd plan show pl-1557` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-08T10:09:09.860Z","updatedAt":"2026-06-08T10:09:09.860Z","labels":["nightwatch","test","refactor"],"plan_id":"pl-1557","blocks":["mulch-350f","mulch-273e"]} +{"id":"mulch-350f","title":"Release: run /release per .claude/commands/release.md","status":"open","type":"task","priority":3,"plan_step_index":4,"description":"\nStep 5 of plan pl-1557.\n\nParent seed: mulch-273e — nightwatch patrol: 2026-06-08\nPlan template: refactor\nPlan approach: Land each cleanup as its own small PR so the ratchet gates verify it in isolation. Start with the independent fixes (numeric-flag dedup, unused-barrel removal, unused-export pruning) since they touch disjoint files. Then refactor the…\n\nRun `sd plan show pl-1557` for the full plan (context, alternatives, sibling steps, acceptance criteria).\n","createdAt":"2026-06-08T10:09:09.860Z","updatedAt":"2026-06-08T10:09:09.860Z","labels":["nightwatch","release"],"plan_id":"pl-1557","blockedBy":["mulch-ac84","mulch-6bb5","mulch-2d07","mulch-05e0"],"blocks":["mulch-273e"]} diff --git a/.seeds/plans.jsonl b/.seeds/plans.jsonl index 257c676..0a981a9 100644 --- a/.seeds/plans.jsonl +++ b/.seeds/plans.jsonl @@ -9,3 +9,4 @@ {"id":"pl-7a81","seed":"mulch-e7f6","template":"refactor","status":"approved","revision":1,"sections":{"context":"Nightwatch patrol on 2026-05-28 found four small quality issues: (1) outcome.duration is parsed with bare Number.parseFloat in three CLI commands, so junk input silently writes NaN to .mulch/expertise/*.jsonl — violating the project's strict-numeric-flag-parsing convention (mx-5b9578, already followed by rank.ts); (2) fatal-error console.error sites have drifted — some use chalk.red('Error: ...'), but the common 'No .mulch/ directory found.' message is plain text in delete/ready/archive/query/prime/edit/restore/search, plus a few generic catch-block 'Error: ${err.message}' lines that are also plain — readability across call sites suffers; (3) the Claude setup recipe parses settings.json with bare JSON.parse and no path context, so a corrupted file throws an unhelpful 'Unexpected token …' error inconsistent with the file:line context convention used by readExpertiseFile (mx-7c199c); (4) the deprecated 'mulch update' command prints a non-JSON warning to stderr even when --json is set, breaking the JSON-output contract every other command honors. Quality gates (bun test, bun run lint, bun run typecheck) are green at HEAD — these are not regressions, just drift that has accumulated.","behavior_invariant":"All existing tests continue to pass. Public CLI surface, command names, flags, and JSON schema shapes are unchanged. Successful command paths produce byte-identical stdout/stderr to before for every input that was valid before. The only observable changes are: (a) invalid --outcome-duration / --duration inputs now exit non-zero with a clear error instead of silently writing NaN; (b) error lines that were plain text become chalk.red-wrapped to match peer commands (no message wording changes); (c) corrupted Claude settings.json yields an error mentioning the file path instead of raw JSON parser output; (d) 'mulch update --json' emits the standard JSON error envelope. No new commands, no new flags, no dependency changes, no public API signature changes.","approach":"Targeted, isolated fixes. Each step touches a small set of files and lands as a single PR. Independent steps (parsing, error-coloring, JSON.parse hardening, update.ts JSON output) can ship in any order; the release step is last and blocked by all four.","steps":[{"title":"Validate --outcome-duration / --duration with strict regex parser","description":"Files: src/commands/record.ts (line ~806), src/commands/edit.ts (line ~128), src/commands/outcome.ts (line ~124). All three currently call `Number.parseFloat(options.outcomeDuration as string)` (or options.duration) with no validation, so `--outcome-duration foo` writes `\"duration\": null` or NaN into the JSONL Outcome object. This violates the strict-numeric-flag-parsing convention (mulch record mx-5b9578) which rank.ts already follows. Fix: introduce a shared helper (e.g. parseStrictNonNegativeNumber in src/utils/format-helpers.ts or a new src/utils/numeric-flags.ts) mirroring the regex+Number() pair in src/commands/rank.ts:31-37, and call it from all three sites. On parse failure, print `Error: --outcome-duration must be a non-negative number (got \"\")` (chalk.red in non-JSON, outputJsonError in JSON mode), set process.exitCode = 1, and return without writing the record. Add unit tests under test/commands/record.test.ts (and edit/outcome equivalents) that pass a bogus value and assert exit code 1 + no JSONL mutation. Do NOT change the flag name or add new flags.","labels":["nightwatch"],"blocks":[]},{"title":"Normalize fatal-error coloring across commands","description":"Plain-text `console.error(\"Error: ...\")` and `console.error(\\`Error: ${...}\\`)` calls have drifted from the chalk.red(`Error: ...`) idiom used by most commands. Specifically: src/commands/delete.ts:303,309; src/commands/ready.ts:177,183; src/commands/archive.ts:201,207; src/commands/query.ts:91,203,209; src/commands/prime.ts:150,162,183,255,283,514,675,681; src/commands/onboard.ts:369; src/commands/edit.ts:202,208; src/commands/restore.ts:154,160; src/commands/search.ts:316,322; src/commands/learn.ts:174; src/commands/move.ts:314,317; src/commands/rank.ts:166,173. Wrap each with chalk.red so non-JSON output is visually consistent with peer commands (delete.ts:60, archive.ts uses it earlier, etc.). Do not change message wording or add new error paths. Verify by running `bun test` and a quick `grep -rn 'console.error(\"Error:\\|console.error(\\`Error:' src/` — should return no matches.","labels":["nightwatch"],"blocks":[]},{"title":"Add file-path context to Claude settings.json parse errors in setup.ts","description":"src/commands/setup.ts at lines 199, 234, and 266 call `JSON.parse(raw) as ClaudeSettings` on the contents of `~/.claude/settings.json` (resolved via claudeSettingsPath(cwd)) with no try/catch, so a corrupted/edited settings file throws a bare `SyntaxError: Unexpected token ...` with no path or hint. The project's convention is to wrap JSONL parse errors with file:line context (mulch record mx-7c199c, see src/utils/expertise.ts:67-76). Fix: extract a small helper inside setup.ts (e.g. `parseClaudeSettings(path: string, raw: string): ClaudeSettings`) that wraps JSON.parse in try/catch and rethrows `Error(`Malformed Claude settings at ${path}: ${err.message}. Edit or remove the file to recover.`)`. Use it from install(), check(), and remove(). No behavior change on the happy path. Add a test in test/commands/setup.test.ts that writes a corrupt settings.json, invokes the claude recipe's check(), and asserts the error message mentions the file path.","labels":["nightwatch"],"blocks":[]},{"title":"Honor --json in the deprecated 'mulch update' command","description":"src/commands/update.ts unconditionally calls `printWarning(...)` and exits with code 1, even when the global --json flag is set. Every other command branches on `program.opts().json === true` and emits a structured envelope via outputJson/outputJsonError. Fix: in registerUpdateCommand's action, capture `const jsonMode = program.opts().json === true;` and call `outputJsonError(\"update\", \"'mulch update' is deprecated. Use 'mulch upgrade' instead.\")` when jsonMode, otherwise the existing printWarning. Keep `process.exitCode = 1` either way. Add a one-line test verifying the JSON envelope shape under --json. No new flags, no rename, the command stays hidden.","labels":["nightwatch"],"blocks":[]},{"title":"Release: run /release per .claude/commands/release.md","description":"Final patrol step. After steps 1-4 land, run /release as documented in .claude/commands/release.md to cut and publish the patch release containing these fixes. Bumps version in package.json and src/cli.ts in lockstep (mulch record mx-5ab2bc), updates CHANGELOG.md (mx-900a53), and runs the usual release ritual.","labels":["nightwatch"],"blocks":[]}],"acceptance":["bun test passes (all 1460+ existing tests plus the new tests added in steps 1, 3, and 4).","bun run lint and bun run typecheck both exit 0.","`grep -rn 'console.error(\"Error:\\|console.error(\\`Error:' src/` returns no matches after step 2.","Running `ml record cli --type pattern --name x --description y --outcome-status success --outcome-duration foo` exits non-zero with a 'must be a non-negative number' error and does NOT append a record to .mulch/expertise/cli.jsonl.","Writing invalid JSON to ~/.claude/settings.json and running `ml setup claude --check` produces an error message that contains the settings.json path, not a bare 'Unexpected token'.","`ml update --json` writes a JSON object with {\"success\": false, \"command\": \"update\", \"error\": ...} to stderr and exits non-zero.","No new CLI commands, flags, or dependencies are introduced. Public API and JSON schema shapes are unchanged."]},"children":["mulch-5b9c","mulch-2a17","mulch-1b36","mulch-a8cd","mulch-201a"],"createdAt":"2026-05-28T10:06:37.862Z","updatedAt":"2026-05-28T10:06:37.862Z","name":"nightwatch patrol fixes 2026-05-28"} {"id":"pl-4fb9","seed":"mulch-3204","template":"feature","status":"done","revision":1,"sections":{"context":"Mulch is the structured-expertise CLI tool in the os-eco ecosystem. The os-eco L5 uplift mission targets Level 5 (≥80% pass rate, matching warren's ceiling per docs/l5-uplift/library/warren-ceiling.md) for every sub-repo by porting templates/l5-toolkit/ (the warren-derived toolkit, finalized 2026-05-27) and authoring repo-specific artifacts. Mulch has NOT started uplift yet — this plan ports the toolkit from scratch and lifts mulch to ~50/63 ≈ ~80% → Level 5 ✓ per the per-repo expected math in docs/l5-uplift/library/warren-ceiling.md. The 6 children of this plan are self-contained: an agent reading `sd show ` has the full target dir, files-to-add/edit, source-of-truth refs, verification commands, rubric criteria closed, commit guidance, and mulch-specific pitfalls without needing the parent mission context.","approach":"Port the os-eco L5 toolkit (templates/l5-toolkit/) into mulch/ in six focused commits, in dependency order. Each child seed targets one feature from the mission's seed-plan-source/mulch.json. Mulch hasn't started, so the first child (mulch-repo-scaffolding) lays down all the baseline configs + devcontainer + env + gitignore + biome extension; subsequent children layer ratchet scripts + budgets, coverage + reporters, AGENTS.md + validator + RUNBOOK + skill, pino logger + governance hooks (pre-commit + dependabot cooldown + labels.yml), and finally the check:all aggregator + CI workflow extension. Each child commits one feature to mulch main; do NOT push (user pushes manually). After the final child seals, the user runs /readiness-report from mulch/ to confirm Level 5 ≥80%.","steps":[{"title":"mulch-repo-scaffolding: port baseline configs + devcontainer + env + gitignore + biome/knip/jscpd/bunfig"},{"title":"mulch-ratchet-scripts: port check-file-sizes + check-debt-markers + budgets"},{"title":"mulch-test-coverage-and-reporters: wire coverage + test:ci + report-test-timing + report-quality-metrics"},{"title":"mulch-agents-md-validator-runbook-skills: author AGENTS.md + validator + RUNBOOK + mulch-expertise skill"},{"title":"mulch-pino-logger-and-governance: add pino + redact + pre-commit hook + dependabot cooldown + labels.yml"},{"title":"mulch-check-all-and-ci-finalize: wire check:all aggregator + extend ci.yml + final verification"}],"acceptance":["VAL-MULCH-002 (naming_consistency), VAL-MULCH-003 (cyclomatic_complexity), VAL-MULCH-005 (duplicate_code_detection), VAL-MULCH-007 (unused_dependencies_detection), VAL-MULCH-012 (devcontainer), VAL-MULCH-017 (env_template), VAL-MULCH-018 (gitignore_comprehensive) closed by step 1","VAL-MULCH-004 (large_file_detection), VAL-MULCH-006 (tech_debt_tracking) closed by step 2","VAL-MULCH-008 (test_performance_tracking), VAL-MULCH-009 (test_coverage_thresholds), VAL-MULCH-015 (code_quality_metrics) closed by step 3","VAL-MULCH-010 (agents_md), VAL-MULCH-011 (agents_md_validation), VAL-MULCH-016 (runbooks_documented), VAL-MULCH-020 (skills) closed by step 4","VAL-MULCH-001 (pre_commit_hooks), VAL-MULCH-013 (structured_logging), VAL-MULCH-014 (log_scrubbing), VAL-MULCH-019 (min_release_age), VAL-MULCH-021 (issue_labeling_system) closed by step 5","VAL-MULCH-FINAL: post-mission /readiness-report from mulch/ shows Level 5 / 5 with pass rate ≥80%; each VAL-MULCH-001..021 criterion at 1/1","From cd /Users/jayminwest/Projects/os-eco/mulch: bun install && bun run lint && bun run typecheck && bun test && bun run check:all all exit 0 after step 6"]},"children":["mulch-409a","mulch-3fda","mulch-8cd7","mulch-d919","mulch-3fd5","mulch-6048"],"createdAt":"2026-05-28T03:22:52.773Z","updatedAt":"2026-05-28T18:57:32.181Z","name":"L5 uplift: mulch"} {"id":"pl-099d","seed":"mulch-5acb","template":"feature","status":"done","revision":1,"sections":{"context":"Mulch is one of five sub-repos targeted by the os-eco L5 readiness uplift mission. Prior /readiness-report audits placed mulch below Level 5 because it lacks the ratchet scripts, governance artifacts, validators, structured logger, and CI wiring that warren (the exemplar at Level 5) ships. The orchestrator has already finalised templates/l5-toolkit/ at the os-eco root; this work ports that toolkit into mulch so the next /readiness-report audit can certify Level 5 (≥80% pass rate) and close every VAL-MULCH-* assertion in docs/l5-uplift/validation-contract.md. Mulch's test directory layout (test/ rather than src/) and existing debug/output channels make this slightly different from the canopy and plot ports already shipped — those deltas are documented in docs/l5-uplift/library/porting-playbook.md.","approach":"Port the L5 toolkit (templates/l5-toolkit/) into mulch in six dependent stages, matching warren's ceiling and closing the VAL-MULCH-* assertions enumerated in docs/l5-uplift/validation-contract.md. Each stage lands in one commit on mulch's working branch (no push) and is gated by bun install / lint / typecheck / test exit 0. Special handling for mulch's test/ directory layout (no bunfig change needed for discovery) and the requirement to route the existing debug/output channels through pino in stage 5 instead of adding a parallel logger. Final stage wires check:all + extends ci.yml and asks the user to run /readiness-report.","steps":[{"title":"mulch-repo-scaffolding"},{"title":"mulch-ratchet-scripts"},{"title":"mulch-test-coverage-and-reporters"},{"title":"mulch-agents-md-validator-runbook-skills"},{"title":"mulch-pino-logger-and-governance"},{"title":"mulch-check-all-and-ci-finalize"}],"acceptance":["All six child seeds exist as dependent open seeds linked to parent mulch-5acb.","Each child seed has a self-contained description (>3000 chars) covering goal, working dir, references to templates/l5-toolkit/ + docs/l5-uplift/library/, files to add/edit, exact verification commands, acceptance rubric criteria, commit guidance, and pitfalls.","After all six children complete, bun install && bun run lint && bun run typecheck && bun test exit 0 from /Users/jayminwest/Projects/os-eco/mulch.","bun run check:all exits 0 after the final child lands.","The agent-readiness auditor (/readiness-report) confirms mulch reaches Level 5 (≥80% pass rate) closing each VAL-MULCH-001..021 plus VAL-MULCH-FINAL."]},"children":["mulch-3744","mulch-5c6f","mulch-f8d1","mulch-cbf4","mulch-9258","mulch-a11b"],"createdAt":"2026-05-28T13:14:50.739Z","updatedAt":"2026-05-28T17:38:48.524Z","name":"L5 uplift: mulch"} +{"id":"pl-1557","seed":"mulch-273e","template":"refactor","status":"approved","revision":1,"sections":{"context":"Routine nightwatch patrol scan turned up a handful of internal-cleanup issues that don't change public behavior but tighten the repo: (1) `scripts/check-file-sizes.ts` ratchet is currently RED — `test/commands/record.test.ts` is 2939 lines against a frozen budget of 2750, so `bun run check:size` fails. (2) Four command modules (`src/commands/ready.ts`, `src/commands/prime.ts`, `src/commands/compact.ts`, `src/commands/rank.ts`) each define their own local `parseStrictPositiveInt` (and `rank.ts` also `parseStrictNonNegativeNumber`) even though `src/utils/numeric-flags.ts` already exports the same helpers with the same convention comment — other commands (`record.ts`, `outcome.ts`, `edit.ts`) already import from the shared util, so this is an unambiguous inconsistency. (3) `bunx knip` (which `bun run check:deps` calls) reports `src/utils/index.ts` as a fully unused barrel file plus ~21 unused exported functions and ~21 unused exported types; these widen the surface area for no benefit and the barrel is dead code. None of these change a command's CLI surface, output format, on-disk JSONL shape, dependencies, or architecture — they are pure internal hygiene that the existing ratchet gates already flag.","behavior_invariant":"Every `ml`/`mulch` subcommand keeps its current CLI surface: same flags, same human and `--json` output shape, same exit codes, same on-disk `.mulch/expertise/*.jsonl` and `.mulch/mulch.config.yaml` semantics. No public API in `src/api.ts` or `src/index.ts` changes signature. The full test suite (`bun test`) passes with the same number of assertions modulo any tests that are physically moved between files when `test/commands/record.test.ts` is split. `bun run lint`, `bun run typecheck`, `bun run check:size`, `bun run check:debt`, `bun run check:agents`, and `bun run check:coverage` all exit 0 after the work; coverage floors in `scripts/coverage-budgets.json` are not lowered. Numeric-flag parsing semantics (regex gate + `Number()`, reject NaN / trailing garbage / fractional positive ints) are byte-for-byte preserved when call sites switch to the shared util.","approach":"Land each cleanup as its own small PR so the ratchet gates verify it in isolation. Start with the independent fixes (numeric-flag dedup, unused-barrel removal, unused-export pruning) since they touch disjoint files. Then refactor the oversized record test file into topic-grouped siblings so the size ratchet goes green. Final step is the standard release.","steps":[{"title":"Dedup parseStrict* helpers: import from src/utils/numeric-flags.ts in ready/prime/compact/rank","type":"task","priority":3,"labels":["nightwatch","refactor","cleanup"],"blocks":[5]},{"title":"Delete unused barrel src/utils/index.ts","type":"task","priority":3,"labels":["nightwatch","cleanup","dead-code"],"blocks":[5]},{"title":"Prune unused exports + exported types flagged by knip","type":"task","priority":3,"labels":["nightwatch","cleanup","dead-code"],"blocks":[5]},{"title":"Split test/commands/record.test.ts so check:size ratchet goes green","type":"task","priority":2,"labels":["nightwatch","test","refactor"],"blocks":[5]},{"title":"Release: run /release per .claude/commands/release.md","type":"task","priority":3,"labels":["nightwatch","release"],"blocks":[]}],"acceptance":["`bun run lint` exits 0","`bun run typecheck` exits 0","`bun test` exits 0 with no fewer passing tests than before (allowing for test-file renames)","`bun run check:size` exits 0 (currently RED on test/commands/record.test.ts)","`bun run check:debt` exits 0","`bun run check:agents` exits 0","`bun run check:coverage` exits 0 without lowering any floor in scripts/coverage-budgets.json","`bunx knip` reports zero unused files and at minimum the previously-flagged unused exports are gone (no new ones introduced)","`grep -rn 'function parseStrictPositiveInt' src/commands/` returns no matches — only the shared util defines it","`ls src/utils/index.ts` returns 'No such file or directory'","`ml --help`, `ml config show`, `ml record --help`, and a representative `ml prime --json` invocation produce byte-identical output to a pre-refactor capture (modulo any timestamp fields)"]},"children":["mulch-ac84","mulch-6bb5","mulch-2d07","mulch-05e0","mulch-350f"],"createdAt":"2026-06-08T10:09:09.860Z","updatedAt":"2026-06-08T10:09:09.860Z","name":"nightwatch patrol 2026-06-08"}