Commit Graph

9 Commits

Author SHA1 Message Date
bae4b66fa4 refactor(work): drop date prefixes + move _state.json into _system/
Convention shift: epic folders + PRD filenames + frontmatter id
fields are now bare slugs. The created: timestamp (Phase 2) carries
the date; folder names don't repeat it. A future <task-id>-<slug>
shape (e.g. ClickUp) lands cleanly when that integration ships.

Renames (git mv preserves history):
- docs/work/2026-05-13-binder-wrap-helper/
    -> docs/work/binder-wrap-helper/
- docs/work/2026-05-14-library-evaluation-policy/
    -> docs/work/library-evaluation-policy/
- docs/work/2026-05-14-ci-security-and-supply-chain/
    -> docs/work/ci-security-and-supply-chain/
- docs/work/prds/2026-05-13-binder-wrap-helper.prd.md
    -> docs/work/prds/binder-wrap-helper.prd.md
- docs/work/prds/2026-05-13-coverage-architecture.prd.md
    -> docs/work/prds/coverage-architecture.prd.md
- docs/work/prds/2026-05-14-library-evaluation-policy.prd.md
    -> docs/work/prds/library-evaluation-policy.prd.md
- docs/work/prds/2026-05-14-ci-security-and-supply-chain.prd.md
    -> docs/work/prds/ci-security-and-supply-chain.prd.md

Frontmatter updates inside the renamed files: epic id, epic prd,
story epic, PRD id, PRD builds-on all drop date prefixes.

System folder + state file move:
- New docs/work/_system/ holds framework-managed state.
- docs/work/_state.json -> docs/work/_system/_state.json.
- state-builder.mjs adds _system to SKIP_FOLDERS.
- cli.mjs + state-sync-guard.mjs + .husky/pre-commit point at the
  new path.

template-reset-v1 epic deleted entirely (one-off cleanup epic from
the pre-date-convention era; status was already done).

Generator-template updates (so new artifacts ship in the right
shape):
- .sandcastle/decomposer.prompt.md emits bare-slug folder names +
  ISO created: timestamp.
- .claude/skills/to-prd/SKILL.md template uses bare-slug filename +
  bare-slug id field + ISO created: timestamp.

Doc reference updates: glossary, runbook, agent-first-workflow-
and-conformance, reviewer prompt, ADR-020, ADR-022, ADR-023 all
point at the new paths/slugs.
2026-05-14 21:16:51 +02:00
edbc6a8fad feat(work): dispatch loops + auto-ticks state on approve
Previously the orchestrator ran exactly one implementer + reviewer pair,
printed "(Automatic state mutation by the orchestrator is v2.)", and
exited — the human had to tick the bullet, flip story status, rebuild
state, and re-invoke for every slice. V2 closes the loop:

- Parses the JSON the implementer + reviewer prompts ask the agents to
  emit (`parseAgentJson` — tolerates both ```json fenced and bare
  trailing { ... } shapes). The reviewer's `decision` and the
  implementer's `status` are the orchestrator's discriminators.
- On approve: ticks the bullet in `_story.md` and writes it back. If
  the story now has zero unchecked bullets, flips its frontmatter
  `status: in-progress → done`; if all sibling stories are also done,
  flips the epic's frontmatter the same way. Commits the mutation on
  the host as a separate `chore(work): tick/finish ...` commit so the
  implementer's slice commit stays clean. `_state.json` regenerates
  via the existing pre-commit `rebuild-state` hook.
- On reject: re-dispatches the implementer with the reviewer's notes
  appended to TASK_FILE_CONTENT, bounded by SANDCASTLE_MAX_ATTEMPTS
  (default 3). On the (max+1)th reject the loop exits 1 with the last
  notes printed.
- After every approved slice, calls findNextTask again and dispatches
  the next ready bullet — including across story boundaries (the
  state-builder treats any non-done story with satisfied deps as
  ready, so flipping story 01 to done unblocks story 02 automatically).
- Flags: `--once` (legacy single-slice behavior) and `--max-tasks N`
  bound the loop. Default is unlimited — matches the
  continuous-execution preference.

Auth/sandbox setup is now pulled out of the per-iteration path so the
loop reuses one sandbox across slices.
2026-05-13 19:43:11 +02:00
52b4409d94 docs(work): refresh stale JSDoc headers in cli + prd-ship
Two stale-comment fixes surfaced after the dispatch handoff fix:

cli.mjs: the top-of-file JSDoc listed only 3 of 8 subcommands
(rebuild-state, status, next) and missed ready / blocked /
dispatch / decompose / prd-ship. Rewrote the header to describe
all 8 subcommands + their flags + the explicit-runCli routing
pattern that replaces the older side-effect-on-import approach
(established when the dispatch handoff broke and got fixed in
bb643b8).

prd-ship.mjs: the JSDoc claimed allowed transitions were
"<approved|in-review|draft> -> shipped", but the code refuses
draft (throws "still draft — flip to approved (human review)
before shipping"). Corrected the doc to "<approved|in-review>
-> shipped" + clarified that draft -> approved is the human step
deliberately kept outside the command's scope.

No behaviour change — comments only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 18:18:42 +02:00
bb643b8635 fix(work): dispatch CLI handoff broke after import-side-effect guard
cli.mjs's `dispatch` branch called `import("./dispatch.mjs")` and
relied on dispatch.mjs's top-level CLI block running as a side
effect of the import. The earlier guard added to dispatch.mjs (to
stop the CLI firing when sibling work scripts import
`resolveClaudeAuth`) also stopped this legit handoff — so
`pnpm work dispatch` silently exited with no output.

Fix: explicit CLI entry function, called by name. Same pattern
already in use for prd-ship + decompose.

dispatch.mjs:
  - Wraps the args parsing + print/execute branch in `export async
    function runCli(args)`
  - The invokedDirectly guard now wraps `runCli(process.argv.slice(2))`
    so direct-invocation (`node scripts/work/dispatch.mjs ...`) still
    works

cli.mjs:
  - Imports runCli as runDispatch
  - The `cmd === "dispatch"` branch calls runDispatch(args) directly
    with a .catch attached (instead of import("./dispatch.mjs"))

Verified: `pnpm work dispatch` now correctly prints the dispatch
plan for the first ready task (`binder-wrap-helper /
01-wire-use-case-helper`'s first bullet); decompose tests stay 9/9.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 18:17:47 +02:00
014578c9a8 feat(work): pnpm work decompose subcommand
Closes the gap surfaced by the user: `pnpm work` usage referenced
`decompose` (via docs + the to-prd skill) but the subcommand was
never built. Mirrors `pnpm work dispatch`'s shape.

scripts/work/decompose.mjs (new):
  - validatePrdForDecompose(prdPath) — refuses draft (must go
    through human review first), in-review (review incomplete),
    shipped (epic already exists); accepts only approved
  - printDecomposePlan(prdId, prdPath, frontmatter) — print-mode
    output showing the PRD's eligibility + sandcastle invocation
    plan + auth modes
  - executeDecompose(prdId, prdPath, prdText) — invokes sandcastle
    with .sandcastle/decomposer.prompt.md, passing PRD_FILE_CONTENT
    promptArg. The decomposer agent writes the epic + per-story
    files to disk on a sandcastle branch the human can review
  - runCli(args, { workRoot }) — entry point used by cli.mjs
  - Direct invocation also supported (mirrors dispatch.mjs's
    invokedDirectly guard, NEW pattern after this commit)

scripts/work/decompose.test.mjs (new, 9 tests, all green):
  - validatePrdForDecompose: accepts approved; rejects draft,
    in-review, shipped, unknown status, missing file
  - runCli: writes error + returns 1 on missing PRD; writes error
    + returns 1 on draft PRD; prints plan + returns 0 on approved

scripts/work/cli.mjs:
  - Adds `decompose` subcommand to usage + dispatch
  - Usage formatting realigned for the 3-line subcommand block

scripts/work/dispatch.mjs:
  - **Fix** the bug surfaced by the user: dispatch.mjs's CLI ran
    as a top-level side effect whenever any of its exports was
    imported. decompose.mjs imports resolveClaudeAuth from it, so
    importing decompose.mjs printed "No ready task to dispatch."
    Added an `import.meta.url === \`file://${process.argv[1]}\``
    guard so the CLI only runs when invoked directly. This unblocks
    cross-import without side effects.

Smoke-tested end-to-end:
  - `pnpm work decompose` (no id) prints usage + exits 2
  - `pnpm work decompose 2026-05-13-binder-wrap-helper` prints the
    decompose plan with status: approved (eligible)
  - 9/9 unit tests green
  - dispatch.mjs's existing direct-invocation path unchanged

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 17:46:57 +02:00
32d20872e3 feat(work): pnpm work prd-ship + auto-flip integration in sandcastle
Closes the PRD-lifecycle gap surfaced by the user: when sandcastle
finishes an epic's last task, the seed PRD should auto-flip from
approved -> shipped. Builds the mechanism, wires it into the work
CLI + state index + reviewer prompt + docs.

scripts/work/prd-ship.mjs (new):
  - parseFrontmatter / serializeFrontmatter — minimal YAML-ish parser
    sufficient for PRD frontmatter (scalar + list shapes)
  - flipPrdStatus — pure function: takes PRD text, returns new text
    with status=shipped + shipped=<date> + optional shipping-commits.
    Refuses to flip draft, idempotent fail-soft on already-shipped,
    rejects unexpected statuses
  - deriveShippingCommits — best-effort git log of the linked epic
    folder for the --auto-commits flag
  - findPrdPath — id -> path lookup under docs/work/prds/
  - runCli — wiring for `pnpm work prd-ship <id> [--commits|--auto-commits]`

scripts/work/prd-ship.test.mjs (new, 17 tests):
  - Frontmatter parser handles scalars + lists + missing frontmatter
  - flipPrdStatus covers all transitions + refusals + body/key preservation
  - findPrdPath + serializeFrontmatter coverage

scripts/work/state-builder.mjs:
  - Epic entries gain a `prd` field
  - New computeNeedsPrdShip surfaces epics done with PRD status not yet
    shipped: state.needs_prd_ship[] with action commands

scripts/work/cli.mjs:
  - New subcommand `pnpm work prd-ship <id>`

.sandcastle/reviewer.prompt.md:
  - "Epic close-out: PRD status flip" section instructing reviewer to
    check _state.json.needs_prd_ship and run the suggested action
  - JSON output extends with prd_shipped: "<id>" | null

docs/work/README.md:
  - "PRD lifecycle" section documenting the 4 statuses + auto-flip

Future PRDs follow the lifecycle automatically: decomposer refuses
draft, human flips to approved, sandcastle ships the epic, reviewer
runs prd-ship on the final task, PRD lands as shipped with its
commit trail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 16:51:48 +02:00
d1b00f1cf5 feat(scripts): pnpm work dispatch — wire CLI to dispatch.mjs 2026-05-13 08:19:19 +02:00
4cf979aaa5 feat(scripts): pnpm work ready + blocked subcommands, DAG-aware next 2026-05-13 08:05:19 +02:00
be8e89baed feat(scripts): pnpm work CLI — rebuild-state, status, next 2026-05-13 07:46:51 +02:00