@dougharness/cli 1.0.2 → 1.1.1
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.
- package/dist/bin.js +18 -3
- package/dist/bin.js.map +1 -1
- package/dist/board-serve.js +14 -7
- package/dist/board-serve.js.map +1 -1
- package/dist/board.d.ts +1 -1
- package/dist/board.js +42 -9
- package/dist/board.js.map +1 -1
- package/dist/detect/repo.js +14 -1
- package/dist/detect/repo.js.map +1 -1
- package/dist/detect/types.d.ts +2 -0
- package/dist/doctor.d.ts +46 -0
- package/dist/doctor.js +491 -0
- package/dist/doctor.js.map +1 -0
- package/dist/generate/agents.d.ts +7 -0
- package/dist/generate/agents.js +62 -12
- package/dist/generate/agents.js.map +1 -1
- package/dist/generate/claude-md.js +4 -0
- package/dist/generate/claude-md.js.map +1 -1
- package/dist/generate/config.d.ts +8 -0
- package/dist/generate/config.js +4 -1
- package/dist/generate/config.js.map +1 -1
- package/dist/generate/proposal.js +15 -6
- package/dist/generate/proposal.js.map +1 -1
- package/dist/generate/settings.d.ts +2 -0
- package/dist/generate/settings.js +17 -2
- package/dist/generate/settings.js.map +1 -1
- package/dist/init.d.ts +14 -0
- package/dist/init.js +94 -8
- package/dist/init.js.map +1 -1
- package/dist/memory-setup.d.ts +28 -0
- package/dist/memory-setup.js +326 -0
- package/dist/memory-setup.js.map +1 -0
- package/node_modules/@dougharness/flow/.claude-plugin/plugin.json +16 -4
- package/node_modules/@dougharness/flow/agents/lead.md +1 -1
- package/node_modules/@dougharness/flow/agents/planner.md +3 -3
- package/node_modules/@dougharness/flow/agents/researcher.md +2 -2
- package/node_modules/@dougharness/flow/hooks/hooks.json +16 -0
- package/node_modules/@dougharness/flow/lib/board.mjs +171 -13
- package/node_modules/@dougharness/flow/lib/code-index.mjs +18 -3
- package/node_modules/@dougharness/flow/lib/context.mjs +57 -0
- package/node_modules/@dougharness/flow/lib/cost.mjs +155 -7
- package/node_modules/@dougharness/flow/lib/embeddings.mjs +188 -24
- package/node_modules/@dougharness/flow/lib/land.mjs +74 -10
- package/node_modules/@dougharness/flow/lib/learn.mjs +4 -4
- package/node_modules/@dougharness/flow/lib/memory.mjs +368 -50
- package/node_modules/@dougharness/flow/lib/plan.mjs +11 -1
- package/node_modules/@dougharness/flow/package.json +2 -1
- package/node_modules/@dougharness/flow/scripts/board.mjs +86 -25
- package/node_modules/@dougharness/flow/scripts/context.mjs +50 -0
- package/node_modules/@dougharness/flow/scripts/cost.mjs +15 -3
- package/node_modules/@dougharness/flow/scripts/memory.mjs +197 -37
- package/node_modules/@dougharness/flow/scripts/plan.mjs +54 -64
- package/node_modules/@dougharness/flow/scripts/session-recall.mjs +132 -0
- package/node_modules/@dougharness/flow/skills/doug-hand/SKILL.md +105 -0
- package/node_modules/@dougharness/flow/skills/doug-learn/SKILL.md +1 -1
- package/node_modules/@dougharness/flow/skills/doug-next/SKILL.md +50 -14
- package/node_modules/@dougharness/flow/skills/doug-plan/SKILL.md +1 -1
- package/node_modules/@dougharness/flow/skills/doug-swarm/SKILL.md +7 -7
- package/node_modules/@dougharness/flow/skills/research/SKILL.md +4 -2
- package/node_modules/@dougharness/flow/skills/run-report/SKILL.md +14 -13
- package/node_modules/@dougharness/flow/skills/swarm-launch/SKILL.md +1 -1
- package/node_modules/@dougharness/flow/workflows/doug-implement.js +145 -86
- package/node_modules/@dougharness/gates/.claude-plugin/plugin.json +1 -1
- package/node_modules/@dougharness/gates/hooks/hooks.json +11 -0
- package/node_modules/@dougharness/gates/lib/baseline.mjs +166 -1
- package/node_modules/@dougharness/gates/lib/bash-rules.mjs +21 -14
- package/node_modules/@dougharness/gates/lib/block-ledger.mjs +58 -1
- package/node_modules/@dougharness/gates/lib/checkpoint.mjs +1 -1
- package/node_modules/@dougharness/gates/lib/config.mjs +20 -0
- package/node_modules/@dougharness/gates/lib/docs-gate.mjs +95 -0
- package/node_modules/@dougharness/gates/lib/evidence.mjs +2 -0
- package/node_modules/@dougharness/gates/lib/gate-lock.mjs +94 -0
- package/node_modules/@dougharness/gates/lib/run.mjs +4 -0
- package/node_modules/@dougharness/gates/lib/test-totals.mjs +40 -7
- package/node_modules/@dougharness/gates/lib/trace.mjs +24 -7
- package/node_modules/@dougharness/gates/lib/verified-tree.mjs +225 -0
- package/node_modules/@dougharness/gates/package.json +1 -1
- package/node_modules/@dougharness/gates/scripts/research-cap.mjs +133 -5
- package/node_modules/@dougharness/gates/scripts/stop-gate.mjs +558 -197
- package/node_modules/@dougharness/gates/scripts/trace.mjs +5 -1
- package/node_modules/@dougharness/gates/scripts/turn-start.mjs +17 -0
- package/node_modules/@dougharness/gates/scripts/verify.mjs +201 -0
- package/package.json +4 -4
- package/templates/board-app.js +4 -3
- package/node_modules/@dougharness/flow/lib/rehearse.mjs +0 -825
- package/node_modules/@dougharness/flow/scripts/rehearse.mjs +0 -129
- package/node_modules/@dougharness/flow/skills/core-next/SKILL.md +0 -92
- package/node_modules/@dougharness/flow/skills/harness-fix/SKILL.md +0 -85
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// On-demand live rehearsal of the flow, swarm, and hand tracks on a small real card (the ts-basic fixture's
|
|
3
|
-
// fix-hours), spawning the real `claude` binary. It is a script, not a test, and it never runs in the gate:
|
|
4
|
-
// evals/ already spends real money to measure arms and must not gain correctness assertions, and seam-contracts
|
|
5
|
-
// settles the deterministic side without a new top-level suite, so this lives here, next to cost.mjs and
|
|
6
|
-
// board.mjs, as a spend-and-record script of the flow plugin that reads the same skills it exercises.
|
|
7
|
-
//
|
|
8
|
-
// rehearse.mjs [flow|swarm|hand|all] --card <id> --spend [dir]
|
|
9
|
-
// no arguments print the estimate table and exit 2; nothing is spawned
|
|
10
|
-
// a scenario, no --spend print that scenario's per-stage estimate and exit 2; nothing is spawned
|
|
11
|
-
// --spend, no --card exit 2 ("--card <id> names the card this rehearsal records under"); nothing is spawned
|
|
12
|
-
// --spend and --card run it: [dir] is the real repository (default cwd) the outcome records under
|
|
13
|
-
// <the id> on its board; exit 0 when every run scenario is "passed", 1 otherwise
|
|
14
|
-
//
|
|
15
|
-
// Recording goes through the existing `board.mjs record` CLI only, never lib/board.mjs directly (that would
|
|
16
|
-
// bypass its research-note promotion and batch handling): the run form for flow/swarm (a synthetic, honest
|
|
17
|
-
// report when the workflow never wrote one, never invented task rows) and the hand form for hand, both carrying
|
|
18
|
-
// a rehearsal --note and --rehearsal <scenario> (the entry's heading and commit sha read as the fixture's, not
|
|
19
|
-
// this repository's landing); never memory.mjs (a fixture run has no run id the cost script can find, and the
|
|
20
|
-
// outcome log is for this repository's own runs). Every adversary block a real report carries is printed for the
|
|
21
|
-
// user to classify by hand.
|
|
22
|
-
//
|
|
23
|
-
// A passed scenario's fixture is removed after its stream logs and last report are archived into this
|
|
24
|
-
// repository's own .doug/.state/rehearsal/<scenario>-<timestamp>/ (a failed scenario keeps the whole fixture
|
|
25
|
-
// instead, as before). Every session this script spawns disallows the Artifact tool, since a gate or build stage
|
|
26
|
-
// that reaches the doug-board skill must never actually publish from a rehearsal.
|
|
27
|
-
//
|
|
28
|
-
// The `claude` binary name is overridable with $DOUG_REHEARSE_CLAUDE; a real user never sets it.
|
|
29
|
-
|
|
30
|
-
import { readFileSync, rmSync } from "node:fs";
|
|
31
|
-
import { resolve } from "node:path";
|
|
32
|
-
import { loadBoard, findCard, adversaryBlocks } from "../lib/board.mjs";
|
|
33
|
-
import { parseArgs, estimateLines, SCENARIOS, scenarioTotal, runScenario, recordRunOutcome, recordHandOutcome, rehearsalNote, extractGateLine, archiveEvidence } from "../lib/rehearse.mjs";
|
|
34
|
-
|
|
35
|
-
function fail(msg, code = 2) {
|
|
36
|
-
process.stderr.write(msg + "\n");
|
|
37
|
-
process.exit(code);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function printEstimates(scenario) {
|
|
41
|
-
process.stdout.write("Estimated cost (USD), from docs/live-runs.md measurements:\n");
|
|
42
|
-
for (const l of estimateLines(scenario)) process.stdout.write(l + "\n");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const opts = parseArgs(process.argv.slice(2));
|
|
46
|
-
|
|
47
|
-
if (!opts.scenario) {
|
|
48
|
-
printEstimates(null);
|
|
49
|
-
process.stdout.write("\nusage: rehearse.mjs [flow|swarm|hand|all] --card <id> --spend [dir]\n");
|
|
50
|
-
process.exit(2);
|
|
51
|
-
}
|
|
52
|
-
if (opts.scenario !== "all" && !SCENARIOS.includes(opts.scenario)) fail(`unknown scenario "${opts.scenario}"; scenarios are ${SCENARIOS.join(", ")}, all`, 2);
|
|
53
|
-
|
|
54
|
-
if (!opts.spend) {
|
|
55
|
-
printEstimates(opts.scenario === "all" ? null : opts.scenario);
|
|
56
|
-
process.stdout.write("\nAdd --spend to run it.\n");
|
|
57
|
-
process.exit(2);
|
|
58
|
-
}
|
|
59
|
-
if (!opts.card) fail("--card <id> names the card this rehearsal records under", 2);
|
|
60
|
-
|
|
61
|
-
const dir = resolve(opts.dir || process.cwd());
|
|
62
|
-
let board;
|
|
63
|
-
try {
|
|
64
|
-
board = loadBoard(dir);
|
|
65
|
-
} catch (err) {
|
|
66
|
-
fail(`cannot read the board at ${dir}: ${err.message}`, 2);
|
|
67
|
-
}
|
|
68
|
-
if (!findCard(board, opts.card)) fail(`no card "${opts.card}" on the board at ${dir}`, 2);
|
|
69
|
-
|
|
70
|
-
const scenarios = opts.scenario === "all" ? SCENARIOS : [opts.scenario];
|
|
71
|
-
process.stdout.write(`Estimated cost: $${scenarios.reduce((s, n) => s + scenarioTotal(n), 0).toFixed(2)}. Running ${scenarios.join(", ")} against ${opts.card} at ${dir}.\n`);
|
|
72
|
-
|
|
73
|
-
let allPassed = true;
|
|
74
|
-
for (const scenario of scenarios) {
|
|
75
|
-
const result = runScenario(scenario);
|
|
76
|
-
const passed = result.outcome === "passed";
|
|
77
|
-
allPassed = allPassed && passed;
|
|
78
|
-
|
|
79
|
-
const outcomeForNote = result.reportSynthesized ? `no report: the session ended at stage ${result.failedStage}: ${result.failedMessage}` : result.outcome;
|
|
80
|
-
const note = rehearsalNote({ scenario, outcome: outcomeForNote, stages: result.stages, cost: result.totalCost, agentNames: result.agentNames });
|
|
81
|
-
|
|
82
|
-
process.stdout.write(`\n${scenario}: ${result.outcome}\n`);
|
|
83
|
-
process.stdout.write(` wall clock: ${result.wallClock}\n`);
|
|
84
|
-
process.stdout.write(` measured cost: $${result.totalCost.toFixed(2)}\n`);
|
|
85
|
-
for (const s of result.stages) process.stdout.write(` stage ${s.name}: wall ${s.wallClock}${s.cost !== null ? `, cost $${s.cost.toFixed(2)}` : ""}${s.streamPath ? `, stream ${s.streamPath}` : ""}\n`);
|
|
86
|
-
|
|
87
|
-
// A passed fixture's evidence (its stream logs and last report) is archived into this repository before the
|
|
88
|
-
// fixture is removed. Archiving lives in the same defended path as recording, right below: a throw here (an
|
|
89
|
-
// unwritable .doug/.state, ENOSPC, ...) must not abort the loop before the outcome is recorded, and the evidence
|
|
90
|
-
// must survive somewhere, so an archive failure is reported on its own line and keeps the fixture instead of
|
|
91
|
-
// removing it, exactly like a failed scenario already does.
|
|
92
|
-
let evidencePath = null;
|
|
93
|
-
let keepFixture = !passed;
|
|
94
|
-
if (passed) {
|
|
95
|
-
try {
|
|
96
|
-
evidencePath = archiveEvidence({ fixtureDir: result.fixtureDir, dir, scenario });
|
|
97
|
-
process.stdout.write(` evidence archived: ${evidencePath}\n`);
|
|
98
|
-
} catch (err) {
|
|
99
|
-
keepFixture = true;
|
|
100
|
-
process.stdout.write(` evidence not archived: ${err.message}\n`);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
process.stdout.write(` fixture: ${result.fixtureDir}${keepFixture ? " (kept for inspection)" : " (removed)"}\n`);
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
let recorded;
|
|
107
|
-
if (scenario === "hand") {
|
|
108
|
-
recorded = recordHandOutcome(dir, opts.card, { commit: result.commit, wallClock: result.wallClock, gate: extractGateLine(result.fixtureDir) || "not observed", note, rehearsal: scenario });
|
|
109
|
-
} else {
|
|
110
|
-
recorded = recordRunOutcome(dir, opts.card, { reportFile: result.reportFile, wallClock: result.wallClock, cost: result.totalCost, mergeCommit: result.commit, note, rehearsal: scenario });
|
|
111
|
-
if (!result.reportSynthesized) {
|
|
112
|
-
const report = JSON.parse(readFileSync(result.reportFile, "utf8"));
|
|
113
|
-
const blocks = adversaryBlocks(report);
|
|
114
|
-
if (blocks.length) {
|
|
115
|
-
process.stdout.write(" adversary blocks (classify by hand with board.mjs record --adversary):\n");
|
|
116
|
-
for (const b of blocks) process.stdout.write(` ${b.task}/${b.id}${b.pass !== null ? ` (pass ${b.pass})` : ""}: ${b.description}\n`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
process.stdout.write(` recorded: ${recorded}`);
|
|
121
|
-
} catch (err) {
|
|
122
|
-
process.stdout.write(` recording failed: ${err.message}\n`);
|
|
123
|
-
allPassed = false;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (!keepFixture) rmSync(result.fixtureDir, { recursive: true, force: true });
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
process.exit(allPassed ? 0 : 1);
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: core-next
|
|
3
|
-
description: Take the next hand-track board card (harness work, decision 0005) start to finish, or several together as one swarmed batch (`/core-next <id> <id>...` or `--batch <n>`): show its goal, ask before starting, build it by hand or, for a size M or L card, through the doug-implement workflow as a swarm, with tests and the full gate, commit without trailers, record the landing in docs/live-runs.md and on the board, and ask before the next card. Either way the session model leads only; implement and review work run on the Models table rows, a swarm's workers on the worker row. Only the user invokes this.
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
allowed-tools: AskUserQuestion, Agent, Workflow, Read, Edit, Write, Grep, Glob, Bash
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# core-next
|
|
9
|
-
|
|
10
|
-
One hand-track card, start to finish, with two human gates: start and continue. A hand-track card is harness work (plugins/doug-flow, plugins/doug-gates, packages/doug-codex, evals) built by hand directly in the checkout with tests and the gate, or, for a size M or L card the user chooses to swarm, through the doug-implement workflow under an approved plan (section 2b; decision 0005, `docs/decisions/0005-two-tracks.md`, as amended 2026-09-07). Nothing starts before the user says so, and the loop never continues to another card without being asked.
|
|
11
|
-
|
|
12
|
-
`/core-next` takes the first Ready hand-track card whose dependencies are Done. `/core-next <card-id>` takes that card.
|
|
13
|
-
|
|
14
|
-
**Several cards in one swarmed batch.** `/core-next <id> <id>...` takes those hand-track cards together, and `/core-next --batch <n>` the first n runnable Ready hand-track cards (`board.mjs next --batch <n> --track hand`, board order). A batch runs only as a swarm: one planner per card in parallel writes `.doug/.state/drafts/<id>.json`, `plan.mjs merge` joins the drafts into one plan whose tasks carry their card, then one approval, one run, and one land, with each card keeping its own Done move and its own run entry. By-hand stays one card at a time: for the by-hand path a batch would only be step 6's loop with fewer gates, and the single gate run it would save is not worth collapsing n approvals for. Where the text below says "the card", read "each card of the batch" and follow the batch paragraphs marked as such.
|
|
15
|
-
|
|
16
|
-
## 1. Pick the card
|
|
17
|
-
|
|
18
|
-
Start from a clean working tree on the base branch (`git status --porcelain` empty apart from `.doug/` and `.claude/worktrees/`).
|
|
19
|
-
|
|
20
|
-
- With an id: `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" card <id>`. If the card does not carry `"track": "hand"`, stop and say it is a flow-track card for `/doug-next <id>`.
|
|
21
|
-
- Without: `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" next --track hand`. Show the `waiting on` skipping lines it prints (those hand-track cards wait on a dependency); the `flow track` lines need no comment.
|
|
22
|
-
- Batch: with several ids, `board.mjs card <id>` for each. With `--batch <n>`, `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" next --batch <n> --track hand` prints the first n runnable Ready hand-track cards as a JSON array in board order (fewer than n when fewer are runnable; a batch left with one card, whether by a set-aside or by a drop, is a plain single-card run from here on). Show the `waiting on` skipping lines. Keep the ids in board order from here on.
|
|
23
|
-
|
|
24
|
-
Batch eligibility is checked here, on the card records alone, before the start gate and before any planner is spawned: a batch runs only as a swarm, so a card that step 2b would send by hand without asking (size S, or no size at all; or a goal naming `plugins/doug-flow/workflows/doug-implement.js`, anything under `plugins/doug-gates/scripts/` or `.doug/hooks/scripts/`, or anything under `plugins/doug-flow/agents/`) cannot be in a batch. Such a card is set aside, not the whole batch: it stays Ready, untouched, and the start-gate question below names it and the reason and says `/core-next <id>` takes it alone by hand. If eligibility sets every card of the batch aside, say which and why, and stop: nothing moves.
|
|
25
|
-
|
|
26
|
-
Put the card's title and goal in the chat verbatim, in a fenced code block: the goal is the acceptance list for the work. Note the wall clock. Batch: put each remaining card's title and goal in the chat the same way.
|
|
27
|
-
|
|
28
|
-
If the goal depends on a fact outside the repository (a Claude Code hook contract, a CLI's output format, a third-party API), say so here: the research step runs after the start gate, before any test is written.
|
|
29
|
-
|
|
30
|
-
## 2. Human gate: start
|
|
31
|
-
|
|
32
|
-
Ask with `AskUserQuestion`: start this card, skip to the next hand-track card, or stop. Starting is never automatic.
|
|
33
|
-
|
|
34
|
-
Batch: one `AskUserQuestion` names every remaining card of the batch (id, title, size) — and any card step 1 set aside, with its reason — with options Start the batch / Stop. One start gate for the whole batch: the batch's point is fewer gates, and naming every card in the one question keeps the drop-before-run ability the batch needs. The automatic "Other" answer is how the user drops cards: they type the ids to leave out, and the gate is asked again with the rest. A batch reduced to one card from there is a plain single-card run, through 2b's usual question.
|
|
35
|
-
|
|
36
|
-
- **Start**: give the card a `class` if it has none (`doug board edit <id> --class <tests-only|prose|gate-script|code|docs|eval|decision>`; `condition open` below refuses a card without one), then `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" move <id> flow`. Leave the move uncommitted; the served page (`doug board serve`) shows it live. Then stamp the landing's condition before the brief: `node "${CLAUDE_PLUGIN_ROOT}/scripts/memory.mjs" condition open <id>` writes `.doug/.state/reports/<id>/condition.json`, which `memory.mjs record --hand` (run by the `run-report` skill at landing) reads to fill the outcome row's condition columns. Batch: one move per remaining card, and one `condition open` per card.
|
|
37
|
-
- **Skip**: go back to step 1 and take the next hand-track card after this one.
|
|
38
|
-
- **Stop**: end.
|
|
39
|
-
|
|
40
|
-
## 2b. Shape: by hand, or a swarm
|
|
41
|
-
|
|
42
|
-
Size S, or a card with no size at all: by hand, no question.
|
|
43
|
-
|
|
44
|
-
Size M or L: by hand without asking when the goal names a file the workflow cannot touch under an approved plan: `plugins/doug-flow/workflows/doug-implement.js`, anything under `plugins/doug-gates/scripts/` or `.doug/hooks/scripts/`, anything under `plugins/doug-flow/agents/`. The workflow cannot rewrite itself, its gate hooks, or its agents while it runs them, so those stay by hand.
|
|
45
|
-
|
|
46
|
-
Batch: a batch runs only as a swarm, so for two or more cards this step does not ask swarm-or-by-hand; it says so and goes straight to spawning one planner per card in the same message so they run in parallel, on the `plan` row — the same `plan.mjs models` row and prompt shape below, each told to write its draft to `.doug/.state/drafts/<id>.json` (not `.doug/plan.json`) and validate it with `plan.mjs validate --file .doug/.state/drafts/<id>.json`, each also told this plan runs as a swarm: set `"swarm": true` and size every task M or L unless it is truly one bounded change. When every planner has returned, `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" merge <id> <id>...` joins the drafts into `.doug/plan.json` (its output in the chat verbatim), then `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" set swarm on`. Never `plan.mjs set card` for a batch: merge's `cards` replaces it. Then `plan.mjs show` (it prints `Cards:` and each task's card) and the approval gate below; Stop on a batch moves every card back to Ready with `board.mjs move <id> ready` (mirroring `/doug-next` section 3's Stop).
|
|
47
|
-
|
|
48
|
-
Otherwise, for one card, ask with `AskUserQuestion`: run it as a swarm, or by hand. Swarm: spawn the planner the way `doug-plan` does, on the `plan` row: read it with the `plan.mjs models` command, `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" models` (`roles.plan.model`, `roles.plan.effort`), and pass that row's model (omit `model` when the row says `inherit`), but tell it this plan runs as a swarm the way `/doug-swarm` does: `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." })`. Then `node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" set card <id>` and `plan.mjs set swarm on` (`node "${CLAUDE_PLUGIN_ROOT}/scripts/plan.mjs" set swarm on`) guarantee the card and the opt-in even if the planner forgot, put `plan.mjs show` in the chat, and ask the approval gate with `AskUserQuestion`: approve as written, edit first, or stop. Approval is never automatic.
|
|
49
|
-
|
|
50
|
-
On approve: `plan.mjs approve`, `plan.mjs anchor`, invoke the `swarm-launch` skill. Note the wall clock when it starts and ends. When it returns, save the `result` object of the Workflow tool's output to `.doug/.state/last-report.json` with the Write tool and note the run id the Workflow tool printed (`wf_...`), the way `/doug-next` step 4 does: `plan.mjs replan`, `plan.mjs done`, `board.mjs record`, and `cost.mjs <run-id>` all read those two. If the report carries `paused`, follow `/doug-next` step 4's paused paragraph: name the tasks in `next` and ask with `AskUserQuestion` whether to open the gate; yes reopens with `plan.mjs gate open <level>` and a relaunch with `resumeFromRunId`, no leaves the card In flow and stops — `plan.mjs done` refuses while the last report is paused. A paused or failed batch changes nothing in this mechanism: the paused paragraph and `plan.mjs replan` both work from the plan file, which keeps `cards` through replan, and a second pass is detected the way `/doug-next` section 2 is, by the plan's `cards` matching this batch. Otherwise read the report as `swarm-launch` says, then `plan.mjs done` and `plan.mjs land`; a report that is not ok goes back through `plan.mjs replan` and the approval gate above, or by hand if the user says so — for a batch, "by hand" dissolves it: the first card continues by hand at step 3, and every other card of the batch goes back to Ready with `board.mjs move <id> ready`.
|
|
51
|
-
|
|
52
|
-
Then continue at step 4.
|
|
53
|
-
|
|
54
|
-
## 3. Build it by hand
|
|
55
|
-
|
|
56
|
-
A dissolved batch (2b's approval gate went by hand after a failed run) builds only its first card here; the other cards of the batch already went back to Ready.
|
|
57
|
-
|
|
58
|
-
The session model is the lead and only the lead, on both tracks; it never implements, verifies, or reviews the change itself. The order is: research (when called for), harness-fix, the brief, the tester, the coder, the reviewer, then the lead's own gate and commit.
|
|
59
|
-
|
|
60
|
-
First the research step when step 1 called for one: invoke the `research` skill. It names the agent (claude-code-guide for Claude Code questions, the plugin's read-only `researcher` agent otherwise), runs several researchers in parallel when there are several distinct questions, and writes one cited note to `.doug/.state/research/<id>.md`; read it before writing the brief, and treat a line marked unverified as a fact the code must not depend on.
|
|
61
|
-
|
|
62
|
-
Invoke the `harness-fix` skill to learn the test file that covers each module and the rules for harness code (the workflow file's constraints, gate scripts copied into `.doug/hooks/scripts`, the CLI's type declarations), and put its rules into the brief: read the goal as the acceptance list, name the test file harness-fix's table gives — the tester writes or extends that file first, from the goal; the coder runs only that file while iterating and does not write tests — and the mutation list rule 7 now demands (one mutation per case the card's goal names, each with the test that must fail). The coder and the reviewer apply each mutation in a scratch copy or git worktree under `.doug/.state`, run the one test file there, and remove the copy. On facts: a fact the brief states about the workflow, a hook contract, or a model row is copied verbatim from CLAUDE.md, README, docs, or the code with its path, never paraphrased; a claim the lead cannot cite goes into the brief as unverified.
|
|
63
|
-
|
|
64
|
-
Next: before the coder, spawn the project's `tester` agent (`.claude/agents/tester.md`, on the `implement` row) with the goal as the acceptance list and the test file `harness-fix` names, so the failing tests are written from the card, not from the code — the Agent tool naming the project's `tester` agent, `model` set to the `implement` row's model, the same way the coder runs on that row. Tell the tester to end its report with the `tests_red_by_design` claim naming the test files it left red, so the SubagentStop gate lets it stop; the lead's own Stop stays red until the coder's change lands. The claim may sit in the tester's final plain-text message or in its hand-back call; the gate reads the plain text first, then the last hand-back.
|
|
65
|
-
|
|
66
|
-
A tests-only card (its goal says tests only, no production change) runs as tester then reviewer, with no coder: the tester writes the test from the goal, runs the brief's mutation list itself in a scratch copy or git worktree under `.doug/.state`, and reports each result; the reviewer reruns the list and adds its own.
|
|
67
|
-
|
|
68
|
-
Then spawn the implementation with the Agent tool on the `implement` row of the CLAUDE.md Models table (the project's `coder` agent with `model` set to that row's model, which executes the brief); the coder then makes them pass and may not edit a test except with a stated reason in its report; the reviewer checks that rule. Then a review on the `review` row (the project's `reviewer` agent); a swarm's workers run on the `worker` row. Verification on the hand track is the gate itself (`pnpm typecheck` and `pnpm test:unit`), which the reviewer runs and the lead re-runs before the commit, says what ran, and records. A row that says `inherit` means the session model, and that is the user's choice in the table, not the lead's.
|
|
69
|
-
|
|
70
|
-
Before the commit the lead itself runs `pnpm typecheck` and `pnpm test:unit`, commits without attribution trailers, and leaves `.doug/plan.json` and `.doug/anchor.md` out unless the card is about them. If a hook blocks a step, report it; do not work around it.
|
|
71
|
-
|
|
72
|
-
Stay within the card's goal. A defect found on the way that the goal does not cover becomes a new hand-track card, not part of this commit.
|
|
73
|
-
|
|
74
|
-
## 4. Record and land
|
|
75
|
-
|
|
76
|
-
With the commit sha (the merge sha for a swarmed card) and the wall clock since step 1 (or since 2b's launch, for a swarmed card):
|
|
77
|
-
|
|
78
|
-
1. Invoke the `run-report` skill. By hand: its hand-track-landing form, `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> --hand --commit <sha> --wall "<m> min" --gate "<gate line>"` (the gate line is run-report's own rule: one `<command> <exit>` clause per entry of `.doug/config.json` `stopGate.commands`, joined by `; `; Doug's own is `typecheck 0; test:unit 0 (<n> passed)`), and `--note "<one line>"` when something about the build is worth keeping (what was harder than the goal said, what was left out and why). Swarmed: its workflow-run form, `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> <report.json>` (the file 2b saved, `.doug/.state/last-report.json`) `--commit <merge sha> --wall "<m> min" --cost <usd> --codex-cost <usd>` (add --codex-cost <usd> when cost.mjs printed a Codex line, next to the run total) with one `--adversary "<id>=<real|marginal|false>: <reason>"` per adversary block the report carries (run-report requires every block classified), then `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" summary <report.json> --wall "<m> min" --commit <merge sha>` and put its three lines in the chat verbatim. Batch: the `run-report` skill's batch form — `cost.mjs <run-id> --by-card` for each card's cost with the shared integration agents counted once under the first, then one `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" record <id> <report.json> --commit <merge sha> --wall "<m> min" --cost <that card's usd> --shared-cost <shared usd> --adversary ...` per card in the plan's `cards` order, then `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" summary <report.json> --card <id> --wall "<m> min" --commit <merge sha>` per card with each card's three lines in the chat verbatim.
|
|
79
|
-
2. `node "${CLAUDE_PLUGIN_ROOT}/scripts/board.mjs" move <id> done --source "commit <sha>"`. Batch: once per card of the batch, same merge sha.
|
|
80
|
-
3. `git add <record> docs/live-runs.md && git commit -m "Board: <id> done as <sha>"` (swarmed: add `.doug/plan.json` too); when step 1's `run-report` invocation printed that it promoted a research note, add `docs/research/<id>.md` too. No trailers. Batch: one commit `"Board: <id>, <id> done as <sha>"` including `.doug/plan.json`.
|
|
81
|
-
|
|
82
|
-
## 5. Follow-up cards
|
|
83
|
-
|
|
84
|
-
Before the continue gate, gather the follow-up candidates this landing produced: the coder's and reviewer's "left out", "not closed", and "found on the way" items, the landing note, and any defect the reviewer marked out of scope. Suggestions come only from what the run actually reported, never invented; each carries the one-line reason and cites the pass that found it. A reviewer's out-of-scope finding is offered only when it is realistic drift (a form a model or a person would plausibly write) or when one card would close the whole class; otherwise it goes in the landing note and the lead recommends none. The suggestion's goal text carries both: the goal names the pass that found it (the reviewer's finding, the coder's report, the adversary block id) and the landed commit.
|
|
85
|
-
|
|
86
|
-
A landing with no candidates skips the question. Otherwise ask once with `AskUserQuestion`, `multiSelect: true`, one option per suggestion with a proposed id, title, column, size, class, track, and deps on the landed card, plus a "none" option.
|
|
87
|
-
|
|
88
|
-
Each accepted suggestion is added with `doug board add` (never a script that edits the record): `doug board add <id> --title "<title>" --goal "<goal>" --component <c> --size <S|M|L> --class <tests-only|prose|gate-script|code|docs|eval|decision> --track <hand|flow> --deps <landed-id> --column <ready|decide>` (or `node packages/doug-cli/dist/bin.js board add ...` when `doug` is not on PATH; build the CLI first if `dist/` is missing); an S hand card goes to the top of Ready (`doug board reorder <id> --top`) when it is mechanical and to Decide when it needs a decision. Then one commit `"Board: <id> follow-ups added: <new-id>, <new-id>"`, whatever the count; the served page (`doug board serve`) shows the new cards without a republish. Batch: each suggestion's deps name the card of the batch it came from; still one question and one commit for the whole batch.
|
|
89
|
-
|
|
90
|
-
## 6. Human gate: continue
|
|
91
|
-
|
|
92
|
-
Ask with `AskUserQuestion` whether to take the next hand-track card. Yes: go to step 1 with no id. No: end.
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: harness-fix
|
|
3
|
-
description: The by-hand procedure for changing Doug's own harness code (plugins/doug-flow, plugins/doug-gates, packages/doug-codex, packages/doug-cli, evals) without running it through the flow (decision 0005). Which test file covers which module, a single test file while iterating, the full gate before a commit, gate scripts copied into .doug/hooks/scripts, no attribution trailers, and a docs/live-runs.md line when a measurement changes. Use it for any edit under those directories, and from /core-next.
|
|
4
|
-
allowed-tools: Read, Edit, Write, Grep, Glob, Bash
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# harness-fix
|
|
8
|
-
|
|
9
|
-
Harness code is built directly in the checkout: tests first, the full gate, a plain commit. This is the procedure; `/core-next` follows it for every hand-track card, and it applies just as well to a one-line fix made outside a card.
|
|
10
|
-
|
|
11
|
-
## 0. Who does the work
|
|
12
|
-
|
|
13
|
-
The lead briefs and gates; it never implements, verifies, or reviews the change itself. Before the coder, the `tester` agent (`.claude/agents/tester.md`), spawned with the Agent tool on the `implement` row of the CLAUDE.md Models table, writes or extends the test file first, from the goal; the coder implements against it. A tester that leaves tests red by design ends with its tests_red_by_design claim, in its final plain-text message or its hand-back call; the SubagentStop gate reads both. The `coder` agent, on the same row, may not edit a test except with a stated reason in its report. The `reviewer` agent, on the `review` row, reviews, and checks that rule. Verification on the hand track is the gate itself (`pnpm typecheck` and `pnpm test:unit`), which the reviewer runs and the lead re-runs before the commit. This holds on both tracks, swarm or not; a hand-track lead is the session model. For a card whose acceptance is that a mechanism exists, the reviewer is expected to mutate the mechanism and rerun the test rather than only read the diff (rule 7), rerunning the brief's mutation list and adding its own; the flow track's `plugins/doug-flow/agents/reviewer.md` does not carry this instruction — it is read-only by tool policy, and its verifier and adversary stages run the code afterward instead. On a tests-only card (the goal says tests only, no production change) there is no coder: the tester runs the brief's mutation list itself in a scratch copy or git worktree under `.doug/.state`, never editing a production file in the live checkout, and reports each result.
|
|
14
|
-
|
|
15
|
-
## 1. Find the test that covers the module
|
|
16
|
-
|
|
17
|
-
Under a card, the tester writes or extends the test before the change, in the file that already covers the module, and the coder runs only that file while iterating: `pnpm exec vitest run <path/to/file.test>`. For a one-line fix outside a card there is no tester: its author writes the test first, in the same file, and runs only that file.
|
|
18
|
-
|
|
19
|
-
| Module | Test file |
|
|
20
|
-
|---|---|
|
|
21
|
-
| `plugins/doug-flow/workflows/doug-implement.js` (the loop, the ledger, the prompts), `agents/*.md`, `skills/*/SKILL.md`, the plugin manifest | `plugins/doug-flow/tests/template.test.mjs` |
|
|
22
|
-
| `plugins/doug-flow/lib/board.mjs`, `scripts/board.mjs` | `plugins/doug-flow/tests/board.test.mjs` |
|
|
23
|
-
| `plugins/doug-flow/lib/plan.mjs` validation, levels, rendering, `mergePlans` | `plugins/doug-flow/tests/plan.test.mjs` |
|
|
24
|
-
| `scripts/plan.mjs set`, `plan.mjs merge` (several cards in one plan) and `--file`, config defaults for install and adversary | `plugins/doug-flow/tests/plan-set.test.mjs` |
|
|
25
|
-
| `lib/plan.mjs` specHash, reusePlan, `plan.mjs replan` | `plugins/doug-flow/tests/replan.test.mjs` |
|
|
26
|
-
| `plugins/doug-flow/lib/land.mjs`, `plan.mjs land`, worktree pruning | `plugins/doug-flow/tests/land.test.mjs` |
|
|
27
|
-
| `plugins/doug-flow/lib/worktrees.mjs` (worktree removal, used by `land.mjs`; branch retirement, used by `scripts/plan.mjs`) | `plugins/doug-flow/tests/land.test.mjs` (removal), `plugins/doug-flow/tests/replan.test.mjs` (retirement) |
|
|
28
|
-
| `plugins/doug-flow/lib/models.mjs` (the CLAUDE.md Models table) | `plugins/doug-flow/tests/models.test.mjs` |
|
|
29
|
-
| `plugins/doug-flow/lib/cost.mjs`, `scripts/cost.mjs` (a run's cost from local transcripts; the price table) | `plugins/doug-flow/tests/cost.test.mjs` |
|
|
30
|
-
| `plugins/doug-flow/lib/embeddings.mjs` (the embedding provider layer: openai-compatible and voyage adapters, probeProvider, doctorReport) | `plugins/doug-flow/tests/embeddings.test.mjs` |
|
|
31
|
-
| `plugins/doug-flow/lib/code-index.mjs` (the opt-in semantic code index: file listing, chunking, incremental build, status, search) | `plugins/doug-flow/tests/code-index.test.mjs` |
|
|
32
|
-
| `plugins/doug-flow/lib/learn.mjs`, `scripts/learn.mjs` (deterministic proposals from outcomes/lessons signals and the run trace; card learn-signals) | `plugins/doug-flow/tests/learn.test.mjs` |
|
|
33
|
-
| `plugins/doug-flow/lib/decisions.mjs` (proposal diffs for docs/decisions/ ADRs and .claude/rules/ conventions; card memory-decisions), `scripts/memory.mjs`'s `decision`/`rule` cases | `plugins/doug-flow/tests/decisions.test.mjs` |
|
|
34
|
-
| `plugins/doug-flow/lib/memory.mjs`, `scripts/memory.mjs` (the outcome log and lesson store: recall, the outcome record, reflect, and the memory.mjs CLI's index dispatch; card memory-lib) | `plugins/doug-flow/tests/memory.test.mjs`, `plugins/doug-flow/tests/memory-cli.test.mjs` |
|
|
35
|
-
| `plugins/doug-flow/lib/memory-import.mjs` (the `memory.mjs import` command; card memory-import) | `plugins/doug-flow/tests/memory-import.test.mjs` |
|
|
36
|
-
| `plugins/doug-gates/scripts/*.mjs` end to end (protect-paths, guard-bash, format-on-edit, edit-loop, stop-gate, reanchor, checkpoint) | `plugins/doug-gates/tests/hooks.test.mjs` |
|
|
37
|
-
| `plugins/doug-gates/lib/bash-rules.mjs` | `plugins/doug-gates/tests/bash-rules.test.mjs` |
|
|
38
|
-
| `plugins/doug-gates/lib/scope.mjs` (plan scope) | `plugins/doug-gates/tests/scope.test.mjs` |
|
|
39
|
-
| `plugins/doug-gates/lib/secret-rules.mjs`, `scripts/secret-scan.mjs` | `plugins/doug-gates/tests/secret-scan.test.mjs` |
|
|
40
|
-
| `plugins/doug-gates/lib/checkpoint.mjs` | `plugins/doug-gates/tests/checkpoint.test.mjs` |
|
|
41
|
-
| `plugins/doug-gates/lib/config.mjs`, state and paths | `plugins/doug-gates/tests/config-state.test.mjs` |
|
|
42
|
-
| `plugins/doug-gates/lib/proposals.mjs` (the applied-proposal ledger reader; card memory-decisions) | `plugins/doug-gates/tests/hooks.test.mjs` (protect-paths/stop-gate proposal-path blocks) |
|
|
43
|
-
| `plugins/doug-gates/lib/glob.mjs` | `plugins/doug-gates/tests/glob.test.mjs` |
|
|
44
|
-
| `plugins/doug-gates/lib/anchor.mjs` (the PreCompact snapshot in the anchor) | `plugins/doug-gates/tests/hooks.test.mjs` (reanchor on PreCompact) |
|
|
45
|
-
| `plugins/doug-gates/lib/evidence.mjs` (verification evidence: which recorded commands count) | `plugins/doug-gates/tests/evidence.test.mjs`; the stop-gate and guard-bash sides in `hooks.test.mjs` |
|
|
46
|
-
| `plugins/doug-gates/lib/trace.mjs` (the run trace: line shape, transcript usage, replay, attribution) | `plugins/doug-gates/tests/trace.test.mjs`; the `trace` script end to end in `hooks.test.mjs` |
|
|
47
|
-
| `plugins/doug-gates/scripts/statusline.mjs` | `plugins/doug-gates/tests/statusline.test.mjs` |
|
|
48
|
-
| the checked-in pre-commit hook | `plugins/doug-gates/tests/git-hooks.test.mjs` |
|
|
49
|
-
| `packages/doug-cli/src/board.ts` (`doug board`) | `packages/doug-cli/tests/board.test.ts` |
|
|
50
|
-
| `packages/doug-cli/src/board-page.ts`, `board-serve.ts`, `templates/board-app.js` | `packages/doug-cli/tests/board-page.test.ts` (serve tests skip with a reason where a local listener is denied: `board-page-skip.test.ts`) |
|
|
51
|
-
| `packages/doug-cli/src/detect/*` | `packages/doug-cli/tests/detect.test.ts` |
|
|
52
|
-
| `packages/doug-cli/src/generate/*` (config, CLAUDE.md, settings, proposal, apply) | `packages/doug-cli/tests/proposal.test.ts` |
|
|
53
|
-
| `packages/doug-cli/src/diff.ts` | `packages/doug-cli/tests/diff.test.ts` |
|
|
54
|
-
| `packages/doug-cli/src/ablate.ts` (`doug ablate`: eval results per condition, the runner pass-through) | `packages/doug-cli/tests/ablate.test.ts` |
|
|
55
|
-
| `packages/doug-cli/src/trace.ts` (`doug trace`: replay and per-agent attribution of a run trace) | `packages/doug-cli/tests/trace.test.ts` |
|
|
56
|
-
| `packages/doug-codex/src/prompt.ts` | `packages/doug-codex/tests/prompt.test.ts` |
|
|
57
|
-
| `packages/doug-codex/src/parse.ts` (Codex JSONL, verdict, exit codes) | `packages/doug-codex/tests/parse.test.ts` |
|
|
58
|
-
| `packages/doug-codex/src/run.ts` (spawning codex, timeout, worktree check) | `packages/doug-codex/tests/run.test.ts` |
|
|
59
|
-
| `packages/doug-codex/src/contract.ts` and `docs/worker-contract.md` | `packages/doug-codex/tests/contract.test.ts` |
|
|
60
|
-
| `packages/doug-codex/src/bin.ts` (`codex-review`) | `packages/doug-codex/tests/cli.test.ts` |
|
|
61
|
-
| `evals/run.mjs` (the scorer: tool calls from stream-json, leaks, must-run commands) and `evals/tasks/*.json` | `evals/tests/run.test.mjs` |
|
|
62
|
-
| `plugins/doug-flow/scripts/rehearse.mjs`, `lib/rehearse.mjs` (the on-demand live rehearsal runner: estimates, fixture, stream assertions, recording) | `plugins/doug-flow/tests/rehearse.test.mjs` |
|
|
63
|
-
| the `reviewIssues` report key (doug-implement.js) round-tripping into `lib/memory.mjs`'s `review_issue_count`/`review_issues`, through `board.mjs record`, `board.mjs summary`, and `plan.mjs replan` (card seam-contracts) | `plugins/doug-flow/tests/seams.test.mjs` |
|
|
64
|
-
| every `board.mjs`/`plan.mjs`/`memory.mjs` subcommand a `skills/*/SKILL.md`, `CLAUDE.md`, or `docs/worker-contract.md` names being a real dispatch case (card seam-contracts) | `plugins/doug-flow/tests/seams.test.mjs` |
|
|
65
|
-
| `claude plugin validate` actually catching a broken plugin manifest (card seam-contracts) | `plugins/doug-flow/tests/seams.test.mjs` |
|
|
66
|
-
| `doug init`'s PreToolUse wiring firing for real (protect-paths.mjs deny/allow, not just a string comparison of the wiring) (card seam-contracts) | `packages/doug-cli/tests/init-hooks.test.ts` |
|
|
67
|
-
|
|
68
|
-
`packages/doug-codex/tests/codex-live.test.ts` spawns the real Codex and is excluded from the gate; leave it out of iteration.
|
|
69
|
-
|
|
70
|
-
## 2. Rules while changing harness code
|
|
71
|
-
|
|
72
|
-
1. The workflow file `doug-implement.js` may not import, call `Date.now` or `Math.random`, or name a model; its template tests assert that, and it must parse when wrapped the way the Workflow runtime wraps it. A top-level `return` in it is expected, so `node --check` fails on it by design.
|
|
73
|
-
2. The Workflow tool runs the plugin copy loaded at session start. An edited workflow is launched with `scriptPath` pointing at the file, or the session is restarted.
|
|
74
|
-
3. After editing a gate script under `plugins/doug-gates/scripts/`, copy it to `.doug/hooks/scripts/` too: this repository has Doug installed on itself and its hooks run from there.
|
|
75
|
-
4. A change to `plugins/doug-flow/lib/board.mjs` or `lib/plan.mjs` reaches `packages/doug-cli` through the `@dougharness/flow` workspace dependency; the CLI's `src/flow-board.d.ts` declares the shapes, so a new field or option is added there as well or `pnpm typecheck` fails.
|
|
76
|
-
5. Keep the change to what the card or fix asks. A defect found on the way becomes its own hand-track card.
|
|
77
|
-
6. If a hook blocks a step, report it; do not work around it.
|
|
78
|
-
7. When the card's acceptance is that a mechanism exists, prove it before reporting done: mutate or remove the mechanism, run the new test file, confirm it fails, then revert the mutation and report which assertion failed. That is the line: a card whose acceptance is that a mechanism exists needs it; a card that only changes prose, a message, or a docs line does not. To prove it, the brief lists one mutation per case the card's goal names, each with the test that must fail; the coder runs every listed mutation and reports each result; the reviewer reruns the list and adds its own. Apply each mutation in a scratch copy or git worktree under `.doug/.state`, run the one test file there, and remove the copy.
|
|
79
|
-
8. A card that changes a workflow's or a skill's user-visible behaviour runs the live scenario that covers it before landing: `node plugins/doug-flow/scripts/rehearse.mjs <flow|swarm|hand> --card <id> --spend`, and its landing note quotes the outcome. The swarm scenario is the one that proves plugin loading and agent-name resolution in the real runtime. `docs/rehearsals.md` says what each scenario proves, how to read a result, and how to add one.
|
|
80
|
-
|
|
81
|
-
## 3. The gate, the commit, the record
|
|
82
|
-
|
|
83
|
-
1. Before the commit run the full gate: `pnpm typecheck` and `pnpm test:unit`. Never `pnpm test`, which spawns Codex. Say what ran and the counts.
|
|
84
|
-
2. Commit without attribution trailers, one commit per coherent change, with a message that says what changed and why. Leave `.doug/plan.json` and `.doug/anchor.md` out unless the change is about them.
|
|
85
|
-
3. When the change alters a measurement (a run's cost, wall clock, pass count, or what a stage does), add a line to `docs/live-runs.md`; a change to the board record shows on the served page (`doug board serve`).
|