@esneiderbravo/speclaw 1.0.1 → 2.0.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.
Files changed (48) hide show
  1. package/README.md +76 -59
  2. package/dist/cli/commands/cortex.js +49 -0
  3. package/dist/cli/commands/init.js +23 -16
  4. package/dist/cli/commands/lawbook.js +34 -1
  5. package/dist/cli/commands/update.js +66 -58
  6. package/dist/cli/index.js +12 -3
  7. package/dist/cli/lib/update-check.js +8 -5
  8. package/dist/modules/cortex/brief.js +75 -0
  9. package/dist/modules/cortex/harness.js +270 -0
  10. package/dist/modules/cortex/register.js +49 -0
  11. package/dist/modules/foundation/assets/AGENTS.template.md +13 -6
  12. package/dist/modules/foundation/assets/CLAUDE.template.md +15 -11
  13. package/dist/modules/foundation/assets/docs/standards/lawbook.template.md +24 -37
  14. package/dist/modules/foundation/assets/workflows/speclaw.yml +1 -1
  15. package/dist/modules/foundation/context-budget.js +2 -0
  16. package/dist/modules/foundation/setup-tool.js +3 -2
  17. package/dist/modules/lawbook/assets/agents/archiver.md +19 -0
  18. package/dist/modules/lawbook/assets/agents/explorer.md +24 -0
  19. package/dist/modules/lawbook/assets/agents/implementer.md +20 -0
  20. package/dist/modules/lawbook/assets/agents/planner.md +27 -0
  21. package/dist/modules/lawbook/assets/agents/reviewer.md +25 -0
  22. package/dist/modules/lawbook/assets/agents/tester.md +21 -0
  23. package/dist/modules/lawbook/assets/commands/cortex.md +11 -0
  24. package/dist/modules/lawbook/assets/rules/spec-tasks-mandatory-steps.md +11 -10
  25. package/dist/modules/lawbook/assets/skills/build/steps/03-implement.md +1 -1
  26. package/dist/modules/lawbook/assets/skills/build/steps/04-hand-off.md +17 -0
  27. package/dist/modules/lawbook/assets/skills/cortex/SKILL.md +13 -0
  28. package/dist/modules/lawbook/assets/skills/cortex/steps/01-load-or-start.md +25 -0
  29. package/dist/modules/lawbook/assets/skills/cortex/steps/02-dispatch-loop.md +19 -0
  30. package/dist/modules/lawbook/assets/skills/cortex/steps/03-complete.md +10 -0
  31. package/dist/modules/lawbook/assets/skills/test/SKILL.md +11 -0
  32. package/dist/modules/lawbook/assets/skills/{build/steps/04-quality-gates.md → test/steps/01-quality-gates.md} +1 -1
  33. package/dist/modules/lawbook/assets/skills/{build/steps/05-manual-verification.md → test/steps/02-manual-verification.md} +1 -1
  34. package/dist/modules/lawbook/assets/skills/{build/steps/06-discipline-reports.md → test/steps/03-discipline-reports.md} +5 -2
  35. package/dist/modules/lawbook/change-tool.js +22 -0
  36. package/dist/modules/lawbook/engine.js +4 -0
  37. package/dist/modules/lawbook/levels.js +9 -3
  38. package/dist/modules/lawbook/register.js +7 -0
  39. package/dist/modules/tools/assets/packs.json +1 -6
  40. package/dist/server.js +4 -1
  41. package/dist/shared/agents.js +3 -3
  42. package/dist/shared/exposure.js +2 -2
  43. package/dist/shared/tool-catalog.js +3 -2
  44. package/package.json +2 -2
  45. package/dist/modules/lawbook/assets/skills/build/steps/07-hand-off.md +0 -9
  46. package/dist/modules/tools/assets/packs/agents/backend-developer.md +0 -61
  47. package/dist/modules/tools/assets/packs/agents/frontend-developer.md +0 -62
  48. package/dist/modules/tools/assets/packs/agents/product-strategy-analyst.md +0 -56
package/dist/cli/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { parseFlags } from "./lib/args.js";
3
3
  import { ui, header } from "./lib/ui.js";
4
4
  import { maybeNotifyUpdate } from "./lib/update-check.js";
5
- const HELP = `speclaw — spec-driven, agent-ready projects (foundation + Compass + Lawbook)
5
+ const HELP = `speclaw — spec-driven, agent-ready projects (Foundation + Compass + Lawbook + Cortex)
6
6
 
7
7
  Usage: speclaw <command> [options]
8
8
 
@@ -12,8 +12,8 @@ Install globally so the command is always available:
12
12
  Setup
13
13
  init Interactive setup: pick agents, scaffold, index, get the prompt
14
14
  (--minimal omits setup/lifecycle MCP tools)
15
- update Upgrade speclaw and apply only what's new (no re-init)
16
- (--minimal persists minimal exposure; omit to keep prior)
15
+ update Apply project migrations (advisory if a newer binary exists)
16
+ (--check reports version only; --minimal persists minimal exposure)
17
17
  agent list Show which agents are configured
18
18
  agent add <id> Configure another agent later (symlinks + MCP)
19
19
 
@@ -31,6 +31,9 @@ Compass (code intelligence — the same surface agents use via MCP)
31
31
  trace <from> <to> A call path between two nodes
32
32
  visualize [node] Interactive HTML graph → .speclaw/graph.html
33
33
 
34
+ Cortex (One brain. Many agents. — multi-agent loop)
35
+ cortex <op> status|start|advance|rework|brief — drive harness.json (--change)
36
+
34
37
  Lawbook (spec-driven workflow)
35
38
  quick <name> Scaffold a level-0 change (record.md + reports)
36
39
  lawbook init Create the lawbook/ workspace
@@ -41,6 +44,7 @@ Lawbook (spec-driven workflow)
41
44
  lawbook validate <c> Validate a change's artifacts
42
45
  lawbook sync <c> Promote delta specs to canonical
43
46
  lawbook archive <c> Finalize and archive a change
47
+ lawbook harness <op> Deprecated alias for \`speclaw cortex\` (compat)
44
48
 
45
49
  Other
46
50
  doctor Verify the installation (--json, --offline, --strict)
@@ -84,6 +88,7 @@ const HEADER_COMMANDS = new Set([
84
88
  "index",
85
89
  "watch",
86
90
  "lawbook",
91
+ "cortex",
87
92
  "quick",
88
93
  ]);
89
94
  /**
@@ -116,6 +121,8 @@ function maybeHeader(cmd, flags) {
116
121
  return;
117
122
  if (cmd === "lawbook" && flags.json && flags._[0] === "draft")
118
123
  return;
124
+ if (cmd === "cortex" && flags.json)
125
+ return;
119
126
  header();
120
127
  }
121
128
  /** Run the handler for a single command. Returns when the command completes. */
@@ -160,6 +167,8 @@ async function dispatch(cmd, flags) {
160
167
  return (await import("./commands/visualize.js")).runVisualize(flags);
161
168
  case "quick":
162
169
  return (await import("./commands/quick.js")).runQuick(flags);
170
+ case "cortex":
171
+ return (await import("./commands/cortex.js")).runCortex(flags);
163
172
  case "lawbook":
164
173
  return (await import("./commands/lawbook.js")).runSpec(flags);
165
174
  case "doctor":
@@ -102,15 +102,16 @@ export function npmPackageUrl(name) {
102
102
  /**
103
103
  * Build the two-line "update available" notice. The latest version is rendered
104
104
  * as a clickable link to the package's npm page, so a capable terminal lets the
105
- * user open the release with a single click while `speclaw update` remains the
106
- * command that performs the upgrade.
105
+ * user open the release with a single click. Project migrations stay
106
+ * `speclaw update`; the binary is upgraded separately (prefer npx).
107
107
  *
108
108
  * @param current - The installed version.
109
109
  * @param latest - The newest published version.
110
110
  * @returns The formatted, styled notice (no leading/trailing blank lines).
111
111
  */
112
112
  export function upgradeNotice(current, latest) {
113
- const latestLink = c.cyan(link(latest, npmPackageUrl(pkgName())));
113
+ const name = pkgName();
114
+ const latestLink = c.cyan(link(latest, npmPackageUrl(name)));
114
115
  return (" " +
115
116
  c.amber("⬆ speclaw ") +
116
117
  c.muted(current + " → ") +
@@ -118,9 +119,11 @@ export function upgradeNotice(current, latest) {
118
119
  c.muted(" available") +
119
120
  "\n" +
120
121
  " " +
121
- c.muted("run ") +
122
+ c.muted("prefer ") +
123
+ c.cyan(`npx ${name}@latest update`) +
124
+ c.muted(" — or ") +
122
125
  c.cyan("speclaw update") +
123
- c.muted(" — upgrades and applies only what's new"));
126
+ c.muted(" to migrate this project"));
124
127
  }
125
128
  /**
126
129
  * Print the "update available" notice to stderr when a newer version exists.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Stage → role brief for Cortex coordinators.
3
+ *
4
+ * // Covers: req~cortex-module~1
5
+ */
6
+ /**
7
+ * Map a harness stage to the role the coordinator should dispatch.
8
+ *
9
+ * @param stage - Current harness stage, or null when not started.
10
+ */
11
+ export function briefForStage(stage) {
12
+ switch (stage) {
13
+ case "exploring":
14
+ return {
15
+ role: "explorer",
16
+ agentPath: "ai-specs/agents/explorer.md",
17
+ skillHints: ["explore"],
18
+ nextOps: ["advance"],
19
+ };
20
+ case "planning":
21
+ return {
22
+ role: "planner",
23
+ agentPath: "ai-specs/agents/planner.md",
24
+ skillHints: ["draft"],
25
+ nextOps: ["advance", "pauseForQuestions"],
26
+ };
27
+ case "questions":
28
+ return {
29
+ role: "planner",
30
+ agentPath: "ai-specs/agents/planner.md",
31
+ skillHints: ["draft"],
32
+ nextOps: ["advance"],
33
+ };
34
+ case "implementing":
35
+ return {
36
+ role: "implementer",
37
+ agentPath: "ai-specs/agents/implementer.md",
38
+ skillHints: ["build"],
39
+ nextOps: ["advance"],
40
+ };
41
+ case "reviewing":
42
+ return {
43
+ role: "reviewer",
44
+ agentPath: "ai-specs/agents/reviewer.md",
45
+ skillHints: [],
46
+ nextOps: ["advance", "rework"],
47
+ };
48
+ case "testing":
49
+ return {
50
+ role: "tester",
51
+ agentPath: "ai-specs/agents/tester.md",
52
+ skillHints: ["test"],
53
+ nextOps: ["advance", "rework"],
54
+ };
55
+ case "archiving":
56
+ return {
57
+ role: "archiver",
58
+ agentPath: "ai-specs/agents/archiver.md",
59
+ skillHints: ["sync", "archive"],
60
+ nextOps: ["advance"],
61
+ };
62
+ case "done":
63
+ case null:
64
+ return {
65
+ role: null,
66
+ agentPath: null,
67
+ skillHints: [],
68
+ nextOps: [],
69
+ };
70
+ default: {
71
+ const _exhaustive = stage;
72
+ throw new Error(`unknown stage ${String(_exhaustive)}`);
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,270 @@
1
+ /**
2
+ * Deterministic multi-agent harness state for a lawbook change.
3
+ *
4
+ * Speclaw does not spawn LLMs. This module only persists stage transitions;
5
+ * the host agent (Cursor/Claude/…) follows the Cortex skill and calls these
6
+ * ops via `speclaw cortex` or the `cortex` MCP tool.
7
+ *
8
+ * Cortex must not import from the lawbook module (no cycles). Ceremony level
9
+ * is read locally from `change.json` `confirmedLevel` (default 3).
10
+ */
11
+ import fs from "node:fs";
12
+ import path from "node:path";
13
+ export const HARNESS_STAGES = [
14
+ "exploring",
15
+ "planning",
16
+ "questions",
17
+ "implementing",
18
+ "reviewing",
19
+ "testing",
20
+ "archiving",
21
+ "done",
22
+ ];
23
+ export const harnessOps = ["status", "start", "advance", "rework"];
24
+ const DEFAULT_MAX_REWORK = 3;
25
+ function specRoot(projectPath) {
26
+ return path.join(projectPath, "lawbook");
27
+ }
28
+ function changeDir(projectPath, change) {
29
+ return path.join(specRoot(projectPath), "changes", change);
30
+ }
31
+ function harnessPath(projectPath, change) {
32
+ return path.join(changeDir(projectPath, change), "harness.json");
33
+ }
34
+ function nowIso() {
35
+ return new Date().toISOString();
36
+ }
37
+ function requireChangeDir(projectPath, change) {
38
+ const dir = changeDir(projectPath, change);
39
+ if (!fs.existsSync(dir)) {
40
+ throw new Error(`change "${change}" not found under lawbook/changes/`);
41
+ }
42
+ return dir;
43
+ }
44
+ /**
45
+ * Read confirmed ceremony level from change.json without importing lawbook.
46
+ * Missing file or field → 3.
47
+ */
48
+ function readConfirmedLevel(projectPath, change) {
49
+ const p = path.join(changeDir(projectPath, change), "change.json");
50
+ if (!fs.existsSync(p))
51
+ return 3;
52
+ try {
53
+ const raw = JSON.parse(fs.readFileSync(p, "utf8"));
54
+ const n = raw.confirmedLevel;
55
+ if (n === 0 || n === 1 || n === 2 || n === 3)
56
+ return n;
57
+ }
58
+ catch {
59
+ /* default */
60
+ }
61
+ return 3;
62
+ }
63
+ /** Read harness.json or return null when absent. */
64
+ export function readHarness(projectPath, change) {
65
+ requireChangeDir(projectPath, change);
66
+ const p = harnessPath(projectPath, change);
67
+ if (!fs.existsSync(p))
68
+ return null;
69
+ return JSON.parse(fs.readFileSync(p, "utf8"));
70
+ }
71
+ function writeHarness(projectPath, change, state) {
72
+ const p = harnessPath(projectPath, change);
73
+ fs.writeFileSync(p, JSON.stringify(state, null, 2) + "\n");
74
+ }
75
+ function nextAfterExplore(level) {
76
+ return level === 0 ? "implementing" : "planning";
77
+ }
78
+ function nextAfterImplement(level) {
79
+ return level === 0 ? "testing" : "reviewing";
80
+ }
81
+ /**
82
+ * Legal one-step advances (not rework). Rework is handled separately.
83
+ */
84
+ function allowedAdvance(from, level, verdict) {
85
+ switch (from) {
86
+ case "exploring":
87
+ return nextAfterExplore(level);
88
+ case "planning":
89
+ return "implementing";
90
+ case "questions":
91
+ return "planning";
92
+ case "implementing":
93
+ return nextAfterImplement(level);
94
+ case "reviewing":
95
+ if (verdict === "FAIL") {
96
+ throw new Error(`harness advance from reviewing with FAIL requires op "rework" (not advance)`);
97
+ }
98
+ if (verdict !== "PASS") {
99
+ throw new Error(`harness advance from reviewing requires verdict PASS or FAIL`);
100
+ }
101
+ return "testing";
102
+ case "testing":
103
+ if (verdict === "FAIL") {
104
+ throw new Error(`harness advance from testing with FAIL requires op "rework"`);
105
+ }
106
+ if (verdict !== "PASS") {
107
+ throw new Error(`harness advance from testing requires verdict PASS or FAIL`);
108
+ }
109
+ return "archiving";
110
+ case "archiving":
111
+ return "done";
112
+ case "done":
113
+ throw new Error(`harness already done — no further advance`);
114
+ default: {
115
+ const _exhaustive = from;
116
+ throw new Error(`unknown stage ${String(_exhaustive)}`);
117
+ }
118
+ }
119
+ }
120
+ /**
121
+ * Dispatch a harness op for a change.
122
+ *
123
+ * // Covers: req~harness-state~1
124
+ */
125
+ export function handleHarness(args) {
126
+ const { projectPath, change, harnessOp } = args;
127
+ requireChangeDir(projectPath, change);
128
+ if (harnessOp === "status") {
129
+ return { state: readHarness(projectPath, change) };
130
+ }
131
+ if (harnessOp === "start") {
132
+ const existing = readHarness(projectPath, change);
133
+ if (existing && existing.stage !== "done") {
134
+ throw new Error(`harness already started for "${change}" (stage=${existing.stage}); use status/advance/rework`);
135
+ }
136
+ const level = readConfirmedLevel(projectPath, change);
137
+ const state = {
138
+ version: 1,
139
+ change,
140
+ stage: "exploring",
141
+ level,
142
+ iteration: 0,
143
+ maxRework: DEFAULT_MAX_REWORK,
144
+ verdicts: { review: null, test: null },
145
+ openQuestions: [],
146
+ history: [
147
+ {
148
+ at: nowIso(),
149
+ from: "exploring",
150
+ to: "exploring",
151
+ op: "start",
152
+ note: args.note,
153
+ },
154
+ ],
155
+ };
156
+ writeHarness(projectPath, change, state);
157
+ return state;
158
+ }
159
+ const current = readHarness(projectPath, change);
160
+ if (!current) {
161
+ throw new Error(`no harness.json for "${change}" — run cortex start first`);
162
+ }
163
+ if (harnessOp === "advance") {
164
+ // Planner may pause for questions instead of advancing to implement.
165
+ if (current.stage === "planning" && args.pauseForQuestions) {
166
+ const questions = args.openQuestions ?? [];
167
+ if (questions.length === 0) {
168
+ throw new Error(`pauseForQuestions requires at least one openQuestions entry`);
169
+ }
170
+ const next = {
171
+ ...current,
172
+ stage: "questions",
173
+ openQuestions: questions,
174
+ history: [
175
+ ...current.history,
176
+ {
177
+ at: nowIso(),
178
+ from: "planning",
179
+ to: "questions",
180
+ op: "advance",
181
+ note: args.note,
182
+ },
183
+ ],
184
+ };
185
+ writeHarness(projectPath, change, next);
186
+ return next;
187
+ }
188
+ const to = allowedAdvance(current.stage, current.level, args.verdict ?? undefined);
189
+ const verdicts = { ...current.verdicts };
190
+ if (current.stage === "reviewing" && args.verdict)
191
+ verdicts.review = args.verdict;
192
+ if (current.stage === "testing" && args.verdict)
193
+ verdicts.test = args.verdict;
194
+ const next = {
195
+ ...current,
196
+ stage: to,
197
+ verdicts,
198
+ openQuestions: current.stage === "questions" ? [] : current.openQuestions,
199
+ history: [
200
+ ...current.history,
201
+ {
202
+ at: nowIso(),
203
+ from: current.stage,
204
+ to,
205
+ op: "advance",
206
+ note: args.note,
207
+ },
208
+ ],
209
+ };
210
+ writeHarness(projectPath, change, next);
211
+ return next;
212
+ }
213
+ if (harnessOp === "rework") {
214
+ if (current.stage !== "reviewing" && current.stage !== "testing") {
215
+ throw new Error(`rework only allowed from reviewing or testing (now ${current.stage})`);
216
+ }
217
+ if (args.verdict !== "FAIL") {
218
+ throw new Error(`rework requires verdict FAIL`);
219
+ }
220
+ if (current.iteration >= current.maxRework) {
221
+ throw new Error(`max rework (${current.maxRework}) reached — coordinator must ask the human before continuing`);
222
+ }
223
+ const verdicts = { ...current.verdicts };
224
+ if (current.stage === "reviewing")
225
+ verdicts.review = "FAIL";
226
+ if (current.stage === "testing")
227
+ verdicts.test = "FAIL";
228
+ const next = {
229
+ ...current,
230
+ stage: "implementing",
231
+ iteration: current.iteration + 1,
232
+ verdicts,
233
+ history: [
234
+ ...current.history,
235
+ {
236
+ at: nowIso(),
237
+ from: current.stage,
238
+ to: "implementing",
239
+ op: "rework",
240
+ note: args.note,
241
+ },
242
+ ],
243
+ };
244
+ writeHarness(projectPath, change, next);
245
+ return next;
246
+ }
247
+ throw new Error(`unknown harnessOp '${String(harnessOp)}'`);
248
+ }
249
+ /**
250
+ * Archive blockers derived from harness verdicts.
251
+ *
252
+ * // Covers: req~harness-archive-gate~1
253
+ */
254
+ export function harnessArchiveBlockers(projectPath, change) {
255
+ const state = readHarness(projectPath, change);
256
+ if (!state) {
257
+ return [`missing harness.json — run \`speclaw cortex start --change ${change}\``];
258
+ }
259
+ const blockers = [];
260
+ if (state.verdicts.test !== "PASS") {
261
+ blockers.push(`harness test verdict is not PASS (got ${String(state.verdicts.test)})`);
262
+ }
263
+ if (state.level >= 1 && state.verdicts.review !== "PASS") {
264
+ blockers.push(`harness review verdict is not PASS (got ${String(state.verdicts.review)})`);
265
+ }
266
+ if (state.stage !== "archiving" && state.stage !== "done") {
267
+ blockers.push(`harness stage is ${state.stage} — advance to archiving before archive`);
268
+ }
269
+ return blockers;
270
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Cortex MCP registration — One brain. Many agents.
3
+ *
4
+ * // Covers: req~cortex-module~1
5
+ */
6
+ import { z } from "zod";
7
+ import { defineTool, text } from "../../shared/mcp.js";
8
+ import { shouldExpose } from "../../shared/exposure.js";
9
+ import { handleHarness, harnessOps, readHarness, } from "./harness.js";
10
+ import { briefForStage } from "./brief.js";
11
+ export const cortexActions = [...harnessOps, "brief"];
12
+ /**
13
+ * Register the canonical `cortex` MCP tool.
14
+ *
15
+ * @param server - MCP server instance.
16
+ * @param opts - Exposure profile.
17
+ */
18
+ export function registerCortex(server, opts = {}) {
19
+ const minimal = Boolean(opts.minimal);
20
+ const add = (name, description, inputSchema, handler) => {
21
+ if (!shouldExpose(name, minimal))
22
+ return;
23
+ defineTool(server, { name, description, inputSchema, handler });
24
+ };
25
+ add("cortex", "Cortex multi-agent loop: status, start, advance, rework, brief for a change.", {
26
+ projectPath: z.string(),
27
+ change: z.string(),
28
+ action: z.enum(cortexActions),
29
+ note: z.string().optional(),
30
+ verdict: z.enum(["PASS", "FAIL"]).optional(),
31
+ openQuestions: z.array(z.string()).optional(),
32
+ pauseForQuestions: z.boolean().optional(),
33
+ }, async ({ projectPath, change, action, note, verdict, openQuestions, pauseForQuestions }) => {
34
+ if (action === "brief") {
35
+ const state = readHarness(projectPath, change);
36
+ const brief = briefForStage(state?.stage ?? null);
37
+ return text(JSON.stringify({ state, ...brief }, null, 2));
38
+ }
39
+ return text(handleHarness({
40
+ projectPath,
41
+ change,
42
+ harnessOp: action,
43
+ note,
44
+ verdict: verdict ?? null,
45
+ openQuestions,
46
+ pauseForQuestions,
47
+ }));
48
+ });
49
+ }
@@ -22,23 +22,30 @@ Claude-specific notes: [`CLAUDE.md`](CLAUDE.md). The law: [`LAWS.md`](LAWS.md).
22
22
  after Compass returns nothing useful, the graph is missing (`compass_index`
23
23
  first), or the target isn't indexed code (stylesheets, config, logs). Cheat
24
24
  sheet: [`docs/compass.md`](docs/compass.md).
25
- 3. **Follow the lawbook workflow** for every non-trivial change; archive
26
- within the same PR. Rules:
25
+ 3. **Follow Cortex** (*One brain. Many agents.*) for every non-trivial change;
26
+ archive within the same PR. The primary agent is the **coordinator**
27
+ (`cortex` skill / `/lawbook/cortex`) — it MUST NOT implement
28
+ product code itself. It dispatches explorer → planner → implementer →
29
+ reviewer → tester → archiver (or adopts each role while obeying that
30
+ role's permission profile). Drive state with the `cortex` MCP tool or
31
+ `speclaw cortex`. Rules:
27
32
  [`docs/standards/lawbook.md`](docs/standards/lawbook.md).
28
33
  4. **Run the quality gates yourself** before declaring anything done — see
29
- [`docs/standards/testing-standards.md`](docs/standards/testing-standards.md):
34
+ [`docs/standards/testing-standards.md`](docs/standards/testing-standards.md)
35
+ (owned by the **tester** role under Cortex):
30
36
  - Tests: `{{test_commands}}`
31
37
  - Lint / type-check: `{{lint_commands}}`
32
38
  5. **Respect the conventions** — branches `{{branch_pattern}}`, commits
33
39
  {{commit_style}}, code that reads like its neighbors. See
34
40
  [`docs/standards/base-standards.md`](docs/standards/base-standards.md) and
35
41
  [`docs/standards/conventions.md`](docs/standards/conventions.md).
36
- 6. **Use the skills.** `ai-specs/` is the canonical home for skills, commands,
37
- and subagents, mirrored to each IDE directory via symlinks.
42
+ 6. **Use the skills and role agents.** `ai-specs/` is the canonical home for
43
+ skills, commands, rules, and agents, mirrored to each IDE directory via
44
+ symlinks.
38
45
  7. **Ask before irreversible or outward-facing actions** — destructive commands;
39
46
  writing to a real data store (DB rows or files with real user data, including
40
47
  for tests — verify against an isolated/throwaway store); publishing
41
- reviews/tickets/comments.
48
+ reviews/tickets/comments. Planner questions always go to the human.
42
49
 
43
50
  ## The standards (the law, in detail)
44
51
 
@@ -48,21 +48,24 @@ target is not indexed code — stylesheets, JSON/config, markdown, logs,
48
48
  generated files, lockfiles. Never skip Compass because grep "feels faster."
49
49
  See [`docs/compass.md`](docs/compass.md).
50
50
 
51
- ## Rule 2 — Spec-driven, always
51
+ ## Rule 2 — Spec-driven Cortex loop, always
52
52
 
53
- No non-trivial change lands without a spec change (propose → implement →
54
- verify → archive). The rules are in
53
+ No non-trivial change lands without a lawbook change (propose → implement →
54
+ verify → archive) run through **Cortex** (*One brain. Many agents.*). You are the
55
+ **coordinator** unless you were spawned as a role agent: use the `cortex`
56
+ skill / `/lawbook/cortex` and the `cortex` MCP tool; do **not** implement product code yourself.
57
+ Role agents live in `ai-specs/agents/`. The rules are in
55
58
  [`docs/standards/lawbook.md`](docs/standards/lawbook.md);
56
- the workflow skills live in `ai-specs/skills/` and the `/lawbook` commands wrap
57
- them. A change is not done until it is archived — archiving belongs in the PR.
59
+ workflow skills live in `ai-specs/skills/` and `/lawbook` commands wrap them.
60
+ A change is not done until it is archived — archiving belongs in the PR.
58
61
 
59
62
  ## Rule 3 — Quality gates are non-negotiable
60
63
 
61
64
  - Tests: `{{test_commands}}`
62
65
  - Lint / type-check: `{{lint_commands}}`
63
66
 
64
- Run them yourself and report real output. Never claim success you did not
65
- observe. Full rules:
67
+ The **tester** role runs them and reports real output. Never claim success you
68
+ did not observe. Full rules:
66
69
  [`docs/standards/testing-standards.md`](docs/standards/testing-standards.md).
67
70
 
68
71
  ## Rule 4 — Conventions
@@ -72,11 +75,11 @@ its neighbors, comments that carry constraints (never ticket IDs). Full rules:
72
75
  [`docs/standards/base-standards.md`](docs/standards/base-standards.md) and
73
76
  [`docs/standards/conventions.md`](docs/standards/conventions.md).
74
77
 
75
- ## Rule 5 — Skills are law-adjacent
78
+ ## Rule 5 — Skills and role agents are law-adjacent
76
79
 
77
- Skills, commands, and subagents live in `ai-specs/` (symlinked into
78
- `.claude/`, `.cursor/`, `.codex/`, `.agents/`). When a skill matches the
79
- task, use it — do not improvise a parallel process.
80
+ Skills, commands, rules, and agents live in `ai-specs/` (symlinked into
81
+ `.claude/`, `.cursor/`, `.codex/`, `.agents/`). When a skill or role matches
82
+ the task, use it — do not improvise a parallel process.
80
83
 
81
84
  ## Rule 6 — Stop conditions
82
85
 
@@ -85,3 +88,4 @@ schema drops), writing to a real data store (DB rows or files holding real user
85
88
  data — including to set up or tear down test data; verification runs against an
86
89
  isolated/throwaway store instead), publishing anything outward-facing (PR
87
90
  reviews, tickets, comments), or any action that contradicts a standard.
91
+ Planner clarifying questions always go to the human via the coordinator.
@@ -6,38 +6,35 @@ mechanical steps are speclaw MCP tools).
6
6
 
7
7
  ## The loop
8
8
 
9
- No non-trivial change lands without a spec change:
10
-
11
- 1. **explore** — think an idea through before committing (writes nothing).
12
- 2. **draft** — create `lawbook/changes/<name>/`: `proposal.md`, delta specs under
13
- `specs/<capability>/spec.md`, `design.md`, `tasks.md`, and a `reports/`
14
- folder.
15
- 3. **build** — implement the tasks in order, keeping code and spec in
16
- agreement, and write the discipline reports under `reports/`.
17
- 4. **sync** — reconcile the delta specs against what was actually built, then
18
- promote them into the canonical `lawbook/specs/` (`lawbook_sync`). The tool
19
- is a deterministic copy; the agent does the code↔spec reconciliation first.
20
- 5. **archive** — finalize: reconcile, sync, then move the change to
21
- `lawbook/changes/archive/` (`lawbook_archive`), **within the same PR** —
22
- never a post-merge chore. The archive is gated (see below).
9
+ No non-trivial change lands without a lawbook change. The default execution
10
+ model is **Cortex** (*One brain. Many agents.*): the host primary agent is the
11
+ **coordinator** (`cortex` skill / MCP `cortex`) and dispatches explorer → planner →
12
+ implementer → reviewer → tester → archiver. State:
13
+ `lawbook/changes/<name>/harness.json` (`speclaw cortex`). Lawbook owns specs
14
+ and ceremony; Cortex owns the multi-agent loop.
15
+
16
+ 1. **explore** (explorer) — think an idea through before committing (writes nothing).
17
+ 2. **draft** / **quick** (planner) — create `lawbook/changes/<name>/` artifacts
18
+ for the confirmed ceremony level; questions go to the human via the
19
+ coordinator.
20
+ 3. **build** (implementer) — implement tasks; hand off before final gates.
21
+ 4. **review** (reviewer) — `reports/review.md` PASS/FAIL (skipped at level 0).
22
+ 5. **test** (tester) — quality gates, manual verification, discipline reports.
23
+ 6. **sync** / **archive** (archiver) — reconcile, sync when needed, archive
24
+ within the same PR. Gated on Cortex verdicts plus tasks/reports/sync.
23
25
 
24
26
  ## Mandatory task steps
25
27
 
26
28
  `tasks.md` MUST include the steps defined in `lawbook/config.yaml` and the
27
- `spec-tasks-mandatory-steps` rule: feature branch first, tests reviewed and
28
- run, manual verification executed by the agent, discipline reports produced,
29
- docs updated, archive within the PR. The agent performs the manual testing
30
- itself — never delegates it.
29
+ `spec-tasks-mandatory-steps` rule. The **tester** role performs manual
30
+ verification — never the user; the coordinator must not archive without a
31
+ test PASS.
31
32
 
32
33
  ## Reports
33
34
 
34
- Every change carries a `reports/` folder. `build` writes one report per
35
- discipline it touched, named for that discipline — an open set (`backend.md`,
36
- `frontend.md`, `api.md`, `database.md`, `infra.md`, … — `api.md` required
37
- whenever the change touches an API surface) recording what was tested
38
- and the real results — unit, integration, and end-to-end as applicable — with
39
- the commands run and their output. It is evidence of testing that travels with
40
- the change; the archive is blocked until at least one discipline report exists.
35
+ Every change carries a `reports/` folder. The tester writes discipline reports;
36
+ the reviewer writes `review.md`. Archive is blocked until evidence and harness
37
+ verdicts are complete.
41
38
 
42
39
  ## Delta specs
43
40
 
@@ -51,18 +48,8 @@ the change; the archive is blocked until at least one discipline report exists.
51
48
  ## Archiving discipline
52
49
 
53
50
  Always archive with the `archive` command / `lawbook_archive` tool, never a manual
54
- `mv` — the tool performs the spec promotion and validation a manual move skips.
55
-
56
- Before archiving, the agent runs a reconciliation review: it compares what was
57
- built against the delta specs and, when the code has drifted past the original
58
- contracts, shows short insights and reconciles the delta specs.
59
-
60
- `lawbook_archive` is then **gated in the engine** — it refuses to archive (and
61
- reports the reason) while any task is unchecked, while `reports/` holds no
62
- discipline report, or while the delta specs are not yet synced into the
63
- canonical specs. Because the gate covers both the tool and the CLI, a change
64
- reaches the archive only when it is genuinely complete: reconcile, `sync`, then
65
- archive.
51
+ `mv`. The engine refuses archive while tasks are unchecked, reports are missing,
52
+ specs are out of sync when required, or harness review/test verdicts are not PASS.
66
53
 
67
54
  ## Amendments to the law
68
55