@dougharness/cli 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/bin.js +10 -3
  2. package/dist/bin.js.map +1 -1
  3. package/dist/board-serve.js +45 -10
  4. package/dist/board-serve.js.map +1 -1
  5. package/dist/board.d.ts +1 -1
  6. package/dist/board.js +33 -8
  7. package/dist/board.js.map +1 -1
  8. package/dist/init.js +1 -1
  9. package/dist/init.js.map +1 -1
  10. package/dist/memory-setup.d.ts +7 -0
  11. package/dist/memory-setup.js +75 -48
  12. package/dist/memory-setup.js.map +1 -1
  13. package/node_modules/@dougharness/flow/.claude-plugin/plugin.json +6 -2
  14. package/node_modules/@dougharness/flow/agents/coder.md +23 -0
  15. package/node_modules/@dougharness/flow/agents/lead.md +1 -1
  16. package/node_modules/@dougharness/flow/agents/planner.md +2 -2
  17. package/node_modules/@dougharness/flow/agents/spec-writer.md +23 -0
  18. package/node_modules/@dougharness/flow/agents/tester.md +21 -0
  19. package/node_modules/@dougharness/flow/hooks/hooks.json +4 -1
  20. package/node_modules/@dougharness/flow/hooks/register.tsx +75 -0
  21. package/node_modules/@dougharness/flow/lib/autopilot-core.mjs +117 -0
  22. package/node_modules/@dougharness/flow/lib/autopilot-view.mjs +17 -0
  23. package/node_modules/@dougharness/flow/lib/board.mjs +107 -10
  24. package/node_modules/@dougharness/flow/lib/card-land.mjs +115 -0
  25. package/node_modules/@dougharness/flow/lib/card-loop.mjs +746 -0
  26. package/node_modules/@dougharness/flow/lib/coordinator.mjs +368 -0
  27. package/node_modules/@dougharness/flow/lib/cost.mjs +44 -11
  28. package/node_modules/@dougharness/flow/lib/embeddings.mjs +147 -4
  29. package/node_modules/@dougharness/flow/lib/land.mjs +33 -9
  30. package/node_modules/@dougharness/flow/lib/memory.mjs +96 -33
  31. package/node_modules/@dougharness/flow/lib/plan.mjs +34 -5
  32. package/node_modules/@dougharness/flow/lib/worktrees.mjs +3 -3
  33. package/node_modules/@dougharness/flow/package.json +1 -1
  34. package/node_modules/@dougharness/flow/scripts/autopilot.mjs +407 -0
  35. package/node_modules/@dougharness/flow/scripts/board.mjs +128 -32
  36. package/node_modules/@dougharness/flow/scripts/context.mjs +4 -2
  37. package/node_modules/@dougharness/flow/scripts/cost.mjs +16 -3
  38. package/node_modules/@dougharness/flow/scripts/memory.mjs +29 -14
  39. package/node_modules/@dougharness/flow/scripts/plan.mjs +122 -30
  40. package/node_modules/@dougharness/flow/skills/doug-hand/SKILL.md +10 -4
  41. package/node_modules/@dougharness/flow/skills/doug-next/SKILL.md +34 -12
  42. package/node_modules/@dougharness/flow/skills/doug-plan/SKILL.md +1 -1
  43. package/node_modules/@dougharness/flow/skills/doug-start/SKILL.md +80 -0
  44. package/node_modules/@dougharness/flow/skills/doug-swarm/SKILL.md +2 -2
  45. package/node_modules/@dougharness/flow/skills/run-report/SKILL.md +12 -10
  46. package/node_modules/@dougharness/flow/workflows/card-queen.js +807 -0
  47. package/node_modules/@dougharness/flow/workflows/doug-implement.js +33 -21
  48. package/node_modules/@dougharness/gates/.claude-plugin/plugin.json +1 -1
  49. package/node_modules/@dougharness/gates/lib/anchor.mjs +29 -4
  50. package/node_modules/@dougharness/gates/lib/bash-rules.mjs +217 -20
  51. package/node_modules/@dougharness/gates/lib/gate-lock.mjs +126 -0
  52. package/node_modules/@dougharness/gates/lib/proposals.mjs +41 -5
  53. package/node_modules/@dougharness/gates/lib/scope.mjs +43 -4
  54. package/node_modules/@dougharness/gates/package.json +1 -1
  55. package/node_modules/@dougharness/gates/scripts/reanchor.mjs +2 -1
  56. package/node_modules/@dougharness/gates/scripts/stop-gate.mjs +148 -97
  57. package/node_modules/@dougharness/gates/scripts/verify.mjs +64 -5
  58. package/package.json +4 -4
  59. package/templates/board-app.js +48 -1
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: doug-start
3
+ description: Start a new project or a large feature one phase at a time, each phase a committed file behind a human gate. With no argument in a repo with no docs/product/idea.md it walks Idea, Architecture, Walking skeleton, and Scope; with a card id or a goal it walks Specification, optional Pseudocode, Architecture, and Refinement into an approvable plan.
4
+ when_to_use: The user runs /doug-start, wants to begin a project from an idea, or wants a large card specified and designed before it is planned.
5
+ allowed-tools: Agent, AskUserQuestion, Read, Write, Edit, Bash(doug board *), Bash(node *bin.js board *), Bash(node *bin.js init *), Bash(doug init *), Bash(node *plan.mjs *), Bash(node *memory.mjs *), Bash(node *learn.mjs *), Bash(node *board.mjs *), Bash(git *)
6
+ ---
7
+
8
+ # doug-start
9
+
10
+ One skill, two modes, no MCP server and no daemon. Each phase writes one committed file and stops at a human gate (AskUserQuestion). A rerun of a phase shows the diff and asks; it never overwrites silently.
11
+
12
+ Mode and resume are derived from disk, with no state file: a card id or goal argument picks feature mode; no argument and no `docs/product/idea.md` picks project mode; a run resumes at the first missing phase file.
13
+
14
+ An argument that is not a card id on the board, in a repo with no `docs/product/idea.md`, is ambiguous (an idea or a goal): ask at an AskUserQuestion gate (project / feature) unless the conversation already says `project mode` or `feature mode`. Once the mode is project, text the user gave is the idea, not a feature goal. A card id on the board, or any goal once `docs/product/idea.md` exists, picks feature mode.
15
+
16
+ The Walking skeleton writes no phase file; it is done when the `walking-skeleton` card is in Done on the board and `stopGate.commands` in `.doug/config.json` is non-empty, and a run resumes at it until both hold.
17
+
18
+ ## Phase rules (both modes)
19
+
20
+ - Only a committed phase file counts as done: a phase file that exists but is uncommitted means that phase is still waiting at its gate, so the next run re-asks that gate, showing the file, and does not move on.
21
+ - A phase file is written by the lead (this session). The spec-writer and researcher agents return text and write nothing.
22
+ - Before writing a phase, check whether its file exists. If it does, write the new text to `.doug/.state/scratch/<name>.new`, show `git diff --no-index` of the two, and ask (AskUserQuestion: replace / keep / revise). Never overwrite a phase file without that answer.
23
+ - The gate after each phase asks AskUserQuestion with the options approve / revise / stop. Approve: commit the phase file (a plain message, no trailers) and go to the next phase. Revise: take the user's note, redraft, show the diff, ask again. Stop: leave the file uncommitted and end; a later `/doug-start` resumes at the first missing phase file.
24
+ - At every gate the lead calls AskUserQuestion, even when the session looks non-interactive; a denied or unanswered call ends the phase there, and the lead does nothing past it (it names the phase that is waiting).
25
+
26
+ ## Project mode
27
+
28
+ Start here: if `.doug/config.json` is missing, run `doug init --yes` first. It writes the project's `architect`, `tester`, `coder`, and `reviewer` agents into `.claude/agents/`, which the phases below spawn.
29
+
30
+ ### 1. Idea
31
+
32
+ Writes `docs/product/idea.md` (problem, users, success, non-goals). Spawn the spec-writer with the user's idea and ask it for those four sections:
33
+ `Agent({ subagent_type: "doug-flow:spec-writer", prompt: "<the user's idea verbatim>. Draft docs/product/idea.md with sections: problem, users, success, non-goals. Return the file text." })`
34
+ Write the file, then ask at the AskUserQuestion gate (approve / revise / stop).
35
+
36
+ ### 2. Architecture
37
+
38
+ Writes `docs/product/architecture.md` plus the ADR `docs/decisions/0001-stack.md` (through the proposal path, below), from the project's `architect` agent, never drafted by the lead: the stack, the modules, the commands that build and test it, and the one decision the stack rests on. Spawn a researcher for any stack fact that lives outside the repository (a tool version, a package's constraint):
39
+ `Agent({ subagent_type: "doug-flow:researcher", prompt: "<one stack question>. Cite each fact with its source." })`
40
+ Then spawn the architect on the `plan` row's model (`node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" models`; omit `model` when it says `inherit`):
41
+ `Agent({ subagent_type: "architect", model: "<the plan row's model>", prompt: "<the approved idea and the research>. Return the text of docs/product/architecture.md and, separately, the body of the stack ADR without its title line." })`
42
+ Write `docs/product/architecture.md` from its returned text. `docs/decisions/` is proposal-only, so the lead never Writes it directly. Save the ADR body under `.doug/.state/scratch/` and run `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" decision propose --title "Stack" --file <body>`, which renders the diff for `docs/decisions/0001-stack.md` under `.doug/.state/learn/` and writes nothing else. Then ask at the AskUserQuestion gate (approve / revise / stop). On approve, run `node "${CLAUDE_PLUGIN_ROOT}/scripts/learn.mjs" apply <proposal-file>`, which writes the ADR, and commit. Unapproved, the proposal stays pending and the ADR does not exist.
43
+
44
+ ### 3. Walking skeleton
45
+
46
+ Writes the first card with `doug board add` (id `walking-skeleton`, size S, class code) and builds it: the thinnest program that runs and has one passing test, built tester, then coder, then reviewer (the project's `tester` and `coder` agents on the implement row, the tester first writing the test; the project's `reviewer` agent on the review row). Mandatory: no feature card starts before it, because until then the gates check nothing. When it is built and its tests pass, leave it uncommitted (approval at the gate commits it); then `doug init` re-runs so `stopGate.commands` are detected; confirm `.doug/config.json` lists at least one command. Leave the work uncommitted and the card out of Done until the gate. Ask at the AskUserQuestion gate (approve / revise / stop) with the commands shown. On approve, run `doug board move walking-skeleton done` and commit.
47
+
48
+ ### 4. Scope
49
+
50
+ Writes `docs/product/scope.md` (v1 in one paragraph, then one line per card) and the board cards. Spawn the spec-writer with the approved idea and architecture; it returns the text of `scope.md` and the card list as one line per card (id, title, goal, size, class, column):
51
+ `Agent({ subagent_type: "doug-flow:spec-writer", prompt: "<idea and architecture paths>. Draft docs/product/scope.md and its card list: id, title, goal, size, class, column. Return the file text." })`
52
+ The lead never drafts the scope; it writes the returned `scope.md` and runs `doug board add` only for each card the user approved. A card counts as approved at the gate, or in advance in the conversation. Cards are written with `doug board add`, never by a script editing `.doug/board.json`. Put at most 7 to Ready for v1 and the rest to Backlog, each with a size; a large card then goes through feature mode (`/doug-start <card-id>`). Show the board (`doug board list`) and ask at the AskUserQuestion gate (approve / revise / stop).
53
+
54
+ ## Feature mode
55
+
56
+ Files live under `docs/specs/<card-id>/`. A goal with no card id gets a short kebab-case id from the lead, confirmed in the first gate. Read the card first (`doug board list --json`) when one exists.
57
+
58
+ ### 1. Specification
59
+
60
+ Writes `spec.md`: what must be true when the work is done, as outcomes a command can check, plus non-goals and open questions.
61
+ `Agent({ subagent_type: "doug-flow:spec-writer", prompt: "<card goal or the user's goal verbatim>. Draft docs/specs/<card-id>/spec.md: outcomes, non-goals, open questions. Return the file text." })`
62
+ Write the file, then ask at the AskUserQuestion gate (approve / revise / stop).
63
+
64
+ ### 2. Pseudocode (optional)
65
+
66
+ Writes `pseudocode.md`, only when the user asks for it at this gate (AskUserQuestion: write it / skip). Skip leaves no file and goes to Architecture; a missing `pseudocode.md` counts as skipped once `architecture.md` exists, or when the user said they skipped it; resuming with only `spec.md` asks write / skip once. When written, spawn the spec-writer again for the algorithm in plain steps, and gate it as above.
67
+
68
+ ### 3. Architecture
69
+
70
+ Writes `architecture.md` in the shape of the architect agent: modules, files, interfaces, the test for each. Every file it names will be owned by a plan task, so name each exactly. Spawn the project's `architect` agent on the `plan` row's model with `spec.md` (and `pseudocode.md` if present) and the code it touches: `Agent({ subagent_type: "architect", model: "<the plan row's model>", prompt: "<the spec path>. Return the text of docs/specs/<card-id>/architecture.md." })`. The lead writes its returned text to the file and never drafts it. Then ask at the AskUserQuestion gate (approve / revise / stop).
71
+
72
+ ### 4. Refinement
73
+
74
+ The planner writes an approvable plan whose tasks own the files `architecture.md` names. Read the `plan` row with `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" models` and pass its model (omit `model` when it says `inherit`):
75
+ `Agent({ subagent_type: "doug-flow:planner", model: "<the plan row's model>", prompt: "<card goal>. Spec: docs/specs/<card-id>/spec.md. Architecture: docs/specs/<card-id>/architecture.md. Scripts: ${CLAUDE_PLUGIN_ROOT}/scripts. Write .doug/plan.json as a draft and return the rendered plan." })`
76
+ Set the card when the argument was a board card id (`node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" set card <card-id>`), render it (`node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" show`) and ask at the AskUserQuestion gate (approve / revise / stop). The plan is approved with `/doug-approve`, by the user; this skill never approves it.
77
+
78
+ ### 5. Completion
79
+
80
+ `/doug-implement` and land, unchanged. Tell the user to run `/doug-approve`, then `/doug-implement`.
@@ -22,7 +22,7 @@ Put the card's title and goal (or the goal text) in the chat verbatim, in a fenc
22
22
 
23
23
  ## 2. Plan on the plan row
24
24
 
25
- The planner runs on the `plan` row, the same row `doug-plan` uses, not the `lead` row (the `lead` row is the swarm's own lead agent inside the workflow, a different job). Read it with the `plan.mjs models` command, `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" models` (`roles.plan.model`, `roles.plan.effort`). Spawn the planner on it: `Agent({ subagent_type: "doug-flow:planner", model: "<the plan row's model>", prompt: "Board card <id>, title \"<title>\": <goal>. This plan runs as a swarm: set \"swarm\": true in the plan file, and size every task M or L unless it is truly one bounded change (a size-S task keeps a single implementer). Write .doug/plan.json and return the rendered plan." })` (for a goal, the prompt is the goal text with the same swarm sentence); omit `model` when the row says `inherit`. The planner's rules apply unchanged: disjoint file ownership per task, a verify command per task, acceptance criteria with commands, no tier set on its own.
25
+ The planner runs on the `plan` row, the same row `doug-plan` uses, not the `lead` row (the `lead` row is the swarm's own lead agent inside the workflow, a different job). Read it with the `plan.mjs models` command, `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" models` (`roles.plan.model`, `roles.plan.effort`). Spawn the planner on it: `Agent({ subagent_type: "doug-flow:planner", model: "<the plan row's model>", prompt: "Board card <id>, title \"<title>\": <goal>. This plan runs as a swarm: set \"swarm\": true in the plan file, and size every task M or L unless it is truly one bounded change (a size-S task keeps a single implementer). Scripts: ${CLAUDE_PLUGIN_ROOT}/scripts. Write .doug/plan.json and return the rendered plan." })` (for a goal, the prompt is the goal text with the same swarm sentence); omit `model` when the row says `inherit`. The planner's rules apply unchanged: disjoint file ownership per task, a verify command per task, acceptance criteria with commands, no tier set on its own.
26
26
 
27
27
  Then guarantee the opt-in and the card (`plan.mjs set swarm on`): `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" set swarm on`, and `plan.mjs set card <id>` for a card. `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" show` and put its complete output in the chat verbatim, in a fenced code block, before anything else; it prints `Swarm: on`. Name any task the planner sized S (it will not swarm) and any assumption it stated.
28
28
 
@@ -36,7 +36,7 @@ Invoke the `swarm-launch` skill: it checks the plan is approved with swarm on an
36
36
 
37
37
  ## 5. Land and record
38
38
 
39
- `/doug-next` step 5 verbatim: `plan.mjs done`, `plan.mjs land`, the card's move to Done with the merge commit, the `run-report` skill (its cost table lists every worker by label), and one commit for the record and the log. A goal without a card lands the same way and skips the board steps; its run entry is not written, since the log is per card.
39
+ `/doug-next` step 5 verbatim: `plan.mjs done`, `plan.mjs land`, the card's move to Done with the merge commit, the `run-report` skill (its cost table lists every worker by label), and one commit for the record and the log. The landing's first chat line is the one-sentence summary `run-report` prints. A goal without a card lands the same way and skips the board steps; its run entry is not written, since the log is per card. After the landing block, the lead prints the card's recap, a few lines in plain words: what the task was, what the outcome was, any other important facts (a follow-up worth filing, an edge case taken conservatively, a cost or wall-clock surprise), and, for a new feature, how to use it.
40
40
 
41
41
  ## 6. Follow-up cards
42
42
 
@@ -8,17 +8,19 @@ allowed-tools: Agent, Read, Bash
8
8
 
9
9
  One card's landing, recorded and summarized. The entry and the summary are rendered by `scripts/board.mjs`, never written by hand, so every run is recorded in the same shape.
10
10
 
11
+ Every `board.mjs record` and `board.mjs summary` takes a required `--summary "<one sentence>"`: one sentence, in plain words, of what changed for the user, which the command prints as the first line of its output; put that line first in the chat. The lead writes it; the command refuses a missing or multi-sentence value and does not write it to `docs/live-runs.md` or the board.
12
+
11
13
  ## A workflow run (flow track)
12
14
 
13
15
  With the card id, the report (`.doug/.state/last-report.json` unless another path is given), the run id the Workflow tool printed (`wf_...`), the wall clock, and the landing commit:
14
16
 
15
17
  1. `node "${CLAUDE_PLUGIN_ROOT}/scripts/cost.mjs" <run-id>`: the run's real cost, summed from the local Claude Code transcripts of every agent in the run and priced from the dated table in the script (no network). Its last lines are the run total and any agent it could not price. The number after `Run total:` is the cost; when the run cannot be found or every agent is unpriced, there is no cost. The workflow itself does not report cost; never estimate one. The Codex line after it, when present, is the Codex adversary's cost: pass its USD as --codex-cost <usd> to record and summary; it is never added to --cost.
16
18
  2. Classify every adversary block in the report. A block is a finding the adversary raised as a blocker (its ledger id, `F1`; for a report from before the ledger, `pass-<n>`); `board.mjs record` refuses an id the report does not have and lists the ones it has. This is judgment work and runs on the `review` row: read it with the `plan.mjs models` command, `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" models` (`roles.review.model`, `roles.review.effort`), and spawn `Agent({ subagent_type: "doug-flow:reviewer", model: "<review row model>", prompt: "There is no worktree and no diff for this job: read the report at <report path> (repository root, read-only) and classify every adversary block in it as real (a defect a user would hit), marginal (true to the spec, no user impact), or false (wrong), each with a one-line reason drawn from the fix passes and the checks in the report, not from the adversary's summary. Return exactly one line per block, nothing else: F1=<real|marginal|false>: <one-line reason> (pass-<n>=... for a pre-ledger report). Edit nothing." })`. The lead passes each returned line into `--adversary` verbatim and never reclassifies.
17
- 3. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> <report.json> --run <run-id> --wall "<m> min" --commit <sha> --cost <usd> [--codex-cost <usd>] --adversary "F1=<class>: <reason>"` with `--adversary` repeated once per block, leaving `--cost` out when step 1 gave no number and `--codex-cost` out when step 1 showed no Codex line. This appends the entry to `docs/live-runs.md`: the measures table with the precision line (n real / n marginal / n false), the per-level, per-task table, and one line per classified block. It also promotes `.doug/.state/research/<id>.md` to `docs/research/<id>.md` when that note exists, printing one line saying so; add that path to the landing commit alongside `docs/live-runs.md`.
19
+ 3. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> <report.json> --run <run-id> --wall "<m> min" --commit <sha> --cost <usd> --summary "<one sentence>" [--codex-cost <usd>] --adversary "F1=<class>: <reason>"` with `--adversary` repeated once per block, leaving `--cost` out when step 1 gave no number and `--codex-cost` out when step 1 showed no Codex line. This appends the entry to `docs/live-runs.md`: the measures table with the precision line (n real / n marginal / n false), the per-level, per-task table, and one line per classified block. It also promotes `.doug/.state/research/<id>.md` to `docs/research/<id>.md` when that note exists, printing one line saying so; add that path to the landing commit alongside `docs/live-runs.md`.
18
20
  4. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" import`: rolls any auto-memory file written since the last import into the lessons store — it is idempotent, a rewritten file supersedes its old row, and it never writes under the auto-memory directory. Its one-line output goes in the chat; an exit 1 naming Node is said in the chat and the landing goes on, the same as record below.
19
21
  5. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" record <report.json> --run <run-id> --card <id> --commit <sha> --adversary "F1=<class>: <reason>"` with `--adversary` repeated once per block, the same values step 3 used, and `--commit` left out for a run that stopped. It appends one row per task of the report to the local outcome log `.doug/.state/memory/memory.db` (uncommitted; run id, card, spec hash, owned files from the plan, size, shape, models, verify and review results, the classified adversary blocks, fix passes, budget, tokens and USD measured from the same transcripts `cost.mjs` read, or null where nothing was measured). It needs Node >=22.16 (node:sqlite). When it exits 1 naming Node, say so in the chat and go on, since the record is a log, not a gate.
20
- 6. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" reflect <report.json> --run <run-id> --card <id> --commit <sha> --adversary "F1=<class>: <reason>"` with `--adversary` repeated once per block, the same values step 5 used. It applies deterministic helpful/harmful counters to any lesson a task's `memoryUsed` names, then runs a cheap-row `claude -p --json-schema` pass that proposes and appends at most three lessons from the report, filtered in code. Its one-line summary (with the measured cost) goes in the chat. An exit 1 naming the LLM pass is said in the chat and the landing goes on regardless — reflect is a log, not a gate, the same as record.
21
- 7. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" summary <report.json> --run <run-id> --wall "<m> min" --commit <sha> [--codex-cost <usd>]` (same `--cost` rule) and put its three lines in the chat verbatim, in a fenced code block: the outcome, the tasks, and what stopped or was not met.
22
+ 6. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" reflect <report.json> --run <run-id> --card <id> --commit <sha> --adversary "F1=<class>: <reason>"` with `--adversary` repeated once per block, the same values step 5 used. It applies deterministic helpful counters to any lesson a task's `memoryUsed` names. A real block no longer bumps harmful; pass `--harmful <id>` once per recalled lesson the lead judges actually misled the work (it must be in a task's `memoryUsed`, else exit 1). Give `--harmful` on the report's first reflect: a rerun is "Already reflected" and ignores it. Then it runs a cheap-row `claude -p --json-schema` pass that proposes and appends at most three lessons from the report, filtered in code. Its one-line summary (with the measured cost) goes in the chat. An exit 1 naming the LLM pass is said in the chat and the landing goes on regardless — reflect is a log, not a gate, the same as record.
23
+ 7. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" summary <report.json> --run <run-id> --wall "<m> min" --commit <sha> --summary "<one sentence>" [--codex-cost <usd>]` (same `--cost` rule) and put its sentence first in the chat, outside the fence (it is the same sentence step 3's record printed, so it goes in the chat once), then its three lines verbatim in a fenced code block: the outcome, the tasks, and what stopped or was not met. The workflow form's landing block is printed by `board.mjs record` (both forms) and by `board.mjs summary --card` as their last three lines (Changed, Outcome, Learned; at most 100 words, `--note` feeds Learned); plain `summary` (no `--card`) prints no block. Your final message carries it verbatim, and the Stop gate blocks until it does.
22
24
 
23
25
  A run that stopped is recorded the same way, without `--commit`; the summary's third line quotes each task's stop reason.
24
26
 
@@ -28,22 +30,22 @@ When `.doug/plan.json` carries `cards` (made by `plan.mjs merge`), the run is re
28
30
 
29
31
  1. `node "${CLAUDE_PLUGIN_ROOT}/scripts/cost.mjs" <run-id> --by-card`: after the usual tables, a per-card table with each card's tasks' agents summed for that card and one row for the shared integration agents (the integrate and level-adversary agents, task `level-<n>`). The card rows are each card's `--cost`; the shared row is `--shared-cost`, the same number for every card. Pass the Codex line's USD as --codex-cost to the first card's record only.
30
32
  2. Classify the adversary blocks per card: `board.mjs record` lists only that card's tasks' blocks and refuses the others' ids.
31
- 3. For each card, in the plan's `cards` order: `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> <report.json> --run <run-id> --wall "<m> min" --commit <sha> --cost <that card's usd> --shared-cost <shared usd> [--codex-cost <usd>, first card only] --adversary ...`. Record reads the batch from the plan file: the entry names the other cards, shows only that card's tasks and the integration rows of their levels, and counts the shared cost once, under the first card.
33
+ 3. For each card, in the plan's `cards` order: `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> <report.json> --run <run-id> --wall "<m> min" --commit <sha> --summary "<one sentence>" --cost <that card's usd> --shared-cost <shared usd> [--codex-cost <usd>, first card only] --adversary ...`. Record reads the batch from the plan file: the entry names the other cards, shows only that card's tasks and the integration rows of their levels, and counts the shared cost once, under the first card.
32
34
  4. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" import` once for the whole report, the same as the workflow-run form's step 4: its one-line output goes in the chat, an exit 1 naming Node is said in the chat and the landing goes on, and it is idempotent, a rewritten file supersedes its old row, and it never writes under the auto-memory directory.
33
35
  5. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" record <report.json> --run <run-id> --commit <sha> --adversary ...` once for the whole report, with every card's block classes (each task carries its card in a batch report; no `--card`).
34
- 6. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" reflect <report.json> --run <run-id> --commit <sha> --adversary ...` once for the whole report, no `--card` (each appended lesson's card comes from its own task).
35
- 7. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" summary <report.json> --card <id> --run <run-id> --wall "<m> min" --commit <sha>` per card (same `--cost` rule), and put each card's three lines in the chat. Its "Acceptance not met" line reads only that card's own tasks by their `[<card>] ` tag (or the final integration level's acceptance, when the report carries it), so another card's failing command, or a command that depends on a level not yet run, never shows as this card's; the line names which source it used.
36
+ 6. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" reflect <report.json> --run <run-id> --commit <sha> --adversary ...` once for the whole report, no `--card` (each appended lesson's card comes from its own task). A real block no longer bumps harmful; pass `--harmful <id>` once per recalled lesson the lead judges actually misled the work (it must be in a task's `memoryUsed`, else exit 1). Give `--harmful` on the report's first reflect: a rerun is "Already reflected" and ignores it.
37
+ 7. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" summary <report.json> --card <id> --run <run-id> --wall "<m> min" --commit <sha> --summary "<one sentence>"` per card (same `--cost` rule), and put each card's three lines in the chat. Its "Acceptance not met" line reads only that card's own tasks by their `[<card>] ` tag (or the final integration level's acceptance, when the report carries it), so another card's failing command, or a command that depends on a level not yet run, never shows as this card's; the line names which source it used. In a batch, each card's landing block (one per card) goes in the final message verbatim.
36
38
 
37
39
  ## A hand-track landing
38
40
 
39
41
  With the card id, the landing commit, the wall clock, and the gate line — one `<command> <exit>` clause per entry of `.doug/config.json` `stopGate.commands`, in that order, joined by `; ` (a test command may add its pass count in parentheses; Doug's own is `typecheck 0; test:unit 0 (<n> passed)`; a pytest project's might be `pytest 0`). Step 4 alone also takes the coder rounds (how many until the reviewer passed) and the lead's classed reviewer blockers:
40
42
 
41
- 1. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> --hand --commit <sha> --wall "<m> min" --gate "<gate line>"`, with `--note "<one line>"` when something about the build is worth keeping. This appends the hand-track entry to `docs/live-runs.md`; its table is the summary. It also promotes `.doug/.state/research/<id>.md` to `docs/research/<id>.md` when that note exists, printing one line saying so; add that path to the landing commit alongside `docs/live-runs.md`.
43
+ 1. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> --hand --commit <sha> --wall "<m> min" --gate "<gate line>" --summary "<one sentence>" --cost <usd>`, with `--note "<one line>"` when something about the build is worth keeping. `<usd>` is the card's measured cost, never an estimate: `node "${CLAUDE_PLUGIN_ROOT}/scripts/cost.mjs" --session <session> --since <openedAt>` prices the lead session and its subagents from the card's `openedAt` (in `.doug/.state/reports/<id>/condition.json`) to now, where `<session>` is `CLAUDE_CODE_SESSION_ID` (not the card id); take the session total's dollar figure. When nothing was measured (no condition.json, no session id, or cost.mjs exits non-zero), leave `--cost` out: the entry then has no Cost row, never a `$0`, and you never estimate one. This appends the hand-track entry to `docs/live-runs.md`; its table is the summary. It also promotes `.doug/.state/research/<id>.md` to `docs/research/<id>.md` when that note exists, printing one line saying so; add that path to the landing commit alongside `docs/live-runs.md`.
42
44
  2. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" import`, the same as above: its one-line output goes in the chat, an exit 1 naming Node is said in the chat and the landing goes on, and it is idempotent, supersedes a rewritten file, and never writes under the auto-memory directory.
43
- 3. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" record <id> --hand --commit <sha> --wall "<m> min" --gate "<gate line>" [--note "<one line>"]`, the same values step 1 used. It records one row in the local outcome log `.doug/.state/memory/memory.db` (uncommitted), keyed `hand:<sha>` so re-recording the same commit updates that row and track `hand`; a card that lands twice, in two commits, gets two rows. Its workflow-only columns (verified, review counts, adversary verdict, blocks, fix passes, budget, tokens, USD, the report itself) are null because they do not exist on this track, never because they measured zero: no independent verifier ran, and the gate's own text is the `gate` column instead. It needs Node >=22.16 (node:sqlite). When it exits 1 naming Node, say so in the chat and go on, since the record is a log, not a gate.
44
- 4. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" reflect --hand <id> --commit <sha> --rounds <n> [--review "R1=<class>: <reason>"]... [--note "<one line>"]`. It reads the briefs, reviewer reports and lessons.json doug-hand saved, applies helpful/harmful counters to the card's memoryUsed, proposes at most three lessons on the cheap row, filtered in code. Its one-line summary (with the measured cost) goes in the chat. An exit 1 naming the LLM pass is said in the chat and the landing goes on regardless — reflect is a log, not a gate, the same as record.
45
+ 3. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" record <id> --hand --commit <sha> --wall "<m> min" --gate "<gate line>" [--cost <usd>] [--note "<one line>"]`, the same values step 1 used, leaving `--cost` out when step 1 did. It records one row in the local outcome log `.doug/.state/memory/memory.db` (uncommitted), keyed `hand:<sha>` so re-recording the same commit updates that row and track `hand`; a card that lands twice, in two commits, gets two rows. Its workflow-only columns (verified, review counts, adversary verdict, blocks, fix passes, budget, tokens, the report itself) are null because they do not exist on this track, never because they measured zero: no independent verifier ran, and the gate's own text is the `gate` column instead. The USD column is the measured `--cost`, null when none was given. It needs Node >=22.16 (node:sqlite). When it exits 1 naming Node, say so in the chat and go on, since the record is a log, not a gate.
46
+ 4. `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" reflect --hand <id> --commit <sha> --rounds <n> [--review "<key>=<class>: <reason>"]... (the key is "`R<n>` or `<card>/R<n>`") [--harmful <id>]... [--note "<one line>"]`. It reads the briefs, reviewer reports and lessons.json doug-hand saved, applies helpful counters to the card's memoryUsed. A real block no longer bumps harmful; pass `--harmful <id>` once per recalled lesson the lead judges actually misled the work (it must be in a task's `memoryUsed`, else exit 1). Give `--harmful` on the report's first reflect: a rerun is "Already reflected" and ignores it. It proposes at most three lessons on the cheap row, filtered in code. Its one-line summary (with the measured cost) goes in the chat. An exit 1 naming the LLM pass is said in the chat and the landing goes on regardless — reflect is a log, not a gate, the same as record.
45
47
 
46
- Say the commit, the wall clock, and the gate line in the chat.
48
+ Say the one-sentence summary record printed first, then the commit, the wall clock, and the gate line in the chat. In the hand form the landing block is printed by `board.mjs record` (both forms) and by `board.mjs summary --card` as their last three lines (Changed, Outcome, Learned; at most 100 words, `--note` feeds Learned); plain `summary` (no `--card`) prints no block. Your final message carries it verbatim, and the Stop gate blocks until it does.
47
49
 
48
50
  ## Then
49
51