@esneiderbravo/speclaw 0.3.11 → 0.3.13

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 (29) hide show
  1. package/dist/cli/commands/lawbook.js +84 -6
  2. package/dist/cli/commands/quick.js +35 -0
  3. package/dist/cli/commands/update.js +18 -0
  4. package/dist/cli/index.js +17 -1
  5. package/dist/modules/foundation/doctor.js +94 -0
  6. package/dist/modules/lawbook/assets/commands/archive.md +5 -6
  7. package/dist/modules/lawbook/assets/commands/draft.md +6 -7
  8. package/dist/modules/lawbook/assets/commands/investigate.md +7 -0
  9. package/dist/modules/lawbook/assets/commands/quick.md +14 -0
  10. package/dist/modules/lawbook/assets/rules/spec-reports-disciplines.md +8 -0
  11. package/dist/modules/lawbook/assets/skills/archive/steps/03-validate-and-sync.md +4 -3
  12. package/dist/modules/lawbook/assets/skills/draft/SKILL.md +1 -1
  13. package/dist/modules/lawbook/assets/skills/draft/steps/02-understand.md +3 -0
  14. package/dist/modules/lawbook/assets/skills/draft/steps/04-write-artifacts.md +29 -25
  15. package/dist/modules/lawbook/assets/skills/investigate/SKILL.md +10 -0
  16. package/dist/modules/lawbook/assets/skills/investigate/steps/01-investigate.md +7 -0
  17. package/dist/modules/lawbook/assets/skills/investigate/steps/02-hand-off.md +6 -0
  18. package/dist/modules/lawbook/assets/skills/quick/SKILL.md +11 -0
  19. package/dist/modules/lawbook/assets/skills/quick/steps/01-scaffold.md +6 -0
  20. package/dist/modules/lawbook/assets/skills/quick/steps/02-implement.md +7 -0
  21. package/dist/modules/lawbook/bugfix.js +195 -0
  22. package/dist/modules/lawbook/engine.js +178 -55
  23. package/dist/modules/lawbook/investigate.js +358 -0
  24. package/dist/modules/lawbook/levels.js +468 -0
  25. package/dist/modules/lawbook/quick.js +86 -0
  26. package/dist/modules/lawbook/register.js +18 -0
  27. package/dist/modules/lawbook/stack-parse.js +135 -0
  28. package/dist/shared/exposure.js +2 -0
  29. package/package.json +1 -1
@@ -1,11 +1,15 @@
1
1
  import { specInit, specValidate, specSync, specArchive, specList, } from "../../modules/lawbook/engine.js";
2
+ import { handleLevel } from "../../modules/lawbook/quick.js";
3
+ import { scaffoldBugfix } from "../../modules/lawbook/bugfix.js";
4
+ import { investigate, formatInvestigateResult } from "../../modules/lawbook/investigate.js";
5
+ import { list } from "../lib/args.js";
2
6
  import { ui } from "../lib/ui.js";
3
7
  function today() {
4
8
  // The MCP path passes the date in; the CLL runs on a real machine, so read it here.
5
9
  return new Date().toISOString().slice(0, 10);
6
10
  }
7
11
  /**
8
- * Run a spec-workflow subcommand: init, list, validate, sync, or archive.
12
+ * Run a spec-workflow subcommand: init, list, validate, sync, archive, or level.
9
13
  *
10
14
  * @param flags - Parsed flags; `_[0]` is the subcommand and `_[1]` the change name where required.
11
15
  * @throws Exits the process with code 1 on unknown subcommands, missing arguments, or engine errors.
@@ -27,13 +31,48 @@ export async function runSpec(flags) {
27
31
  if (!r.initialized)
28
32
  return ui.warn("No lawbook/ — run `speclaw lawbook init`.");
29
33
  ui.heading("Lawbook workspace");
30
- ui.info(`active changes: ${r.activeChanges.join(", ") || "none"}`);
34
+ if (r.activeChanges.length === 0)
35
+ ui.info("active changes: none");
36
+ else {
37
+ ui.info("active changes:");
38
+ for (const name of r.activeChanges) {
39
+ const lvl = r.activeLevels[name] ?? 3;
40
+ ui.info(` ${name} (level ${lvl})`);
41
+ }
42
+ }
31
43
  ui.info(`archived: ${r.archivedChanges.join(", ") || "none"}`);
32
44
  ui.info(`capabilities: ${r.capabilities.join(", ") || "none"}`);
33
45
  return;
34
46
  }
47
+ case "level": {
48
+ const modeRaw = change ?? "propose";
49
+ const mode = modeRaw;
50
+ if (!["propose", "set", "promote", "explain"].includes(mode)) {
51
+ ui.err("Usage: speclaw lawbook level <propose|set|promote|explain> [--change <c>] [--path …] [--level N] [--reason …] [--json]");
52
+ process.exit(1);
53
+ }
54
+ const levelFlag = flags.level;
55
+ const level = levelFlag === undefined || levelFlag === true
56
+ ? undefined
57
+ : Number(levelFlag);
58
+ const result = handleLevel({
59
+ projectPath: cwd,
60
+ mode,
61
+ change: typeof flags.change === "string" ? flags.change : flags._[2],
62
+ paths: list(flags.path),
63
+ symbols: list(flags.symbol),
64
+ level,
65
+ reason: typeof flags.reason === "string" ? flags.reason : undefined,
66
+ });
67
+ if (flags.json) {
68
+ console.log(JSON.stringify(result, null, 2));
69
+ return;
70
+ }
71
+ console.log(JSON.stringify(result, null, 2));
72
+ return;
73
+ }
35
74
  case "validate": {
36
- const r = specValidate(cwd, req(change, "spec validate <change>"));
75
+ const r = specValidate(cwd, req(change, "lawbook validate <change>"));
37
76
  if (r.valid)
38
77
  ui.ok(`${r.change} is valid (${r.deltaSpecs.length} delta spec(s))`);
39
78
  else {
@@ -47,13 +86,52 @@ export async function runSpec(flags) {
47
86
  return;
48
87
  }
49
88
  case "sync": {
50
- const r = specSync(cwd, req(change, "spec sync <change>"));
89
+ const r = specSync(cwd, req(change, "lawbook sync <change>"));
51
90
  ui.ok(`promoted ${r.promoted.length} spec(s)`);
52
91
  r.promoted.forEach((p) => ui.info(`${r.created.includes(p) ? "created" : "updated"}: ${p}`));
53
92
  return;
54
93
  }
94
+ case "investigate": {
95
+ const stackTrace = typeof flags["stack-trace"] === "string"
96
+ ? flags["stack-trace"]
97
+ : typeof flags.stackTrace === "string"
98
+ ? flags.stackTrace
99
+ : undefined;
100
+ const symptom = typeof flags.symptom === "string" ? flags.symptom : undefined;
101
+ const result = await investigate({
102
+ projectPath: cwd,
103
+ stackTrace,
104
+ symptom,
105
+ hintPaths: list(flags.path),
106
+ maxSuspects: flags.max !== undefined && flags.max !== true ? Number(flags.max) : undefined,
107
+ });
108
+ if (flags.json) {
109
+ console.log(formatInvestigateResult(result));
110
+ return;
111
+ }
112
+ console.log(formatInvestigateResult(result));
113
+ return;
114
+ }
115
+ case "draft": {
116
+ if (!flags.bug) {
117
+ ui.err("Usage: speclaw lawbook draft --bug <name> [--level N] [--json]");
118
+ process.exit(1);
119
+ }
120
+ const name = typeof flags.bug === "string" ? flags.bug : req(change, "lawbook draft --bug <name>");
121
+ const levelFlag = flags.level;
122
+ const level = levelFlag === undefined || levelFlag === true
123
+ ? undefined
124
+ : Number(levelFlag);
125
+ const result = scaffoldBugfix(cwd, name, { level });
126
+ if (flags.json) {
127
+ console.log(JSON.stringify(result, null, 2));
128
+ return;
129
+ }
130
+ ui.ok(`bug change "${name}" scaffolded at ${result.dir}`);
131
+ return;
132
+ }
55
133
  case "archive": {
56
- const r = specArchive(cwd, req(change, "spec archive <change>"), today());
134
+ const r = specArchive(cwd, req(change, "lawbook archive <change>"), today());
57
135
  ui.ok(`archived to ${r.archivedTo} (${r.promoted.length} spec(s) promoted)`);
58
136
  r.promoted.forEach((p) => ui.info(`${r.created.includes(p) ? "created" : "updated"}: ${p}`));
59
137
  for (const s of r.seals) {
@@ -66,7 +144,7 @@ export async function runSpec(flags) {
66
144
  return;
67
145
  }
68
146
  default:
69
- ui.err("Usage: speclaw lawbook <init|list|validate|sync|archive> [change]");
147
+ ui.err("Usage: speclaw lawbook <init|list|validate|sync|archive|level|draft|investigate> [change]");
70
148
  process.exit(1);
71
149
  }
72
150
  }
@@ -0,0 +1,35 @@
1
+ import { list } from "../lib/args.js";
2
+ import { ui } from "../lib/ui.js";
3
+ import { scaffoldQuick } from "../../modules/lawbook/quick.js";
4
+ /**
5
+ * Scaffold a level-0 change (`speclaw quick <name>`).
6
+ *
7
+ * @param flags - `_[0]` is the change name; optional `--path` / `--symbol` / `--json`.
8
+ */
9
+ export async function runQuick(flags) {
10
+ const cwd = process.cwd();
11
+ const name = flags._[0];
12
+ if (!name || typeof name !== "string") {
13
+ ui.err("Usage: speclaw quick <name> [--path <file>] [--symbol <sym>] [--json]");
14
+ process.exit(1);
15
+ }
16
+ try {
17
+ const result = scaffoldQuick(cwd, name, {
18
+ paths: list(flags.path),
19
+ symbols: list(flags.symbol),
20
+ });
21
+ if (flags.json) {
22
+ console.log(JSON.stringify(result, null, 2));
23
+ return;
24
+ }
25
+ ui.ok(`level-0 change ${ui.code(result.change)} at ${result.dir}`);
26
+ ui.info(result.proposal.rationale);
27
+ if (result.proposal.level !== null && result.proposal.level > 0) {
28
+ ui.warn(`measured proposal was level ${result.proposal.level} — promote if the fix grows`);
29
+ }
30
+ }
31
+ catch (err) {
32
+ ui.err(err.message);
33
+ process.exit(1);
34
+ }
35
+ }
@@ -125,6 +125,24 @@ const MIGRATIONS = [
125
125
  "(`node_metrics`) — reindex with `speclaw index`. Default history window is 90 days.\n" +
126
126
  "- Preserve all project-specific wording; only apply these speclaw-authored changes.",
127
127
  },
128
+ {
129
+ version: "0.3.12",
130
+ describe: "Adaptive ceremony levels 0–3, speclaw quick, lawbook_level",
131
+ agentPrompt: "- Mention ceremony levels 0–3 (`change.json`), `speclaw quick` for level-0 scaffolds, and " +
132
+ "`lawbook_level` / `speclaw lawbook level` for propose/set/promote. Artifact volume follows " +
133
+ "the confirmed level; missing `change.json` still means full ceremony (level 3). Optional " +
134
+ "`ceremony:` block in `lawbook/config.yaml` (cuts default [3, 8, 15]). Update LAWS / " +
135
+ "docs/standards/lawbook.md wording if the project still says every change needs all four artifacts.\n" +
136
+ "- Preserve all project-specific wording; only apply these speclaw-authored changes.",
137
+ },
138
+ {
139
+ version: "0.3.13",
140
+ describe: "Bugfix specs — draft --bug, bugfix.md, lawbook_investigate",
141
+ agentPrompt: "- Mention bug changes: `speclaw lawbook draft --bug`, `bugfix.md` (repro + regression + prevention), " +
142
+ "and `lawbook_investigate` / the investigate skill for graph-backed RCA. Feature ceremony unchanged; " +
143
+ "`changeType: bug` in `change.json`. Security-withheld mode is not in this release.\n" +
144
+ "- Preserve all project-specific wording; only apply these speclaw-authored changes.",
145
+ },
128
146
  ];
129
147
  /**
130
148
  * Update speclaw and bring the current project up to date without a full re-init:
package/dist/cli/index.js CHANGED
@@ -31,8 +31,12 @@ Compass (code intelligence — the same surface agents use via MCP)
31
31
  visualize [node] Interactive HTML graph → .speclaw/graph.html
32
32
 
33
33
  Lawbook (spec-driven workflow)
34
+ quick <name> Scaffold a level-0 change (record.md + reports)
34
35
  lawbook init Create the lawbook/ workspace
35
36
  lawbook list Active/archived changes and capabilities
37
+ lawbook level <mode> Propose/set/promote/explain ceremony level (--json)
38
+ lawbook draft --bug <c> Scaffold a bug change (bugfix.md + reports)
39
+ lawbook investigate Rank bug suspects from graph (--symptom / --stack-trace, --json)
36
40
  lawbook validate <c> Validate a change's artifacts
37
41
  lawbook sync <c> Promote delta specs to canonical
38
42
  lawbook archive <c> Finalize and archive a change
@@ -54,7 +58,8 @@ Other
54
58
  // interactive, human-facing commands whose stdout is prose. Deliberately
55
59
  // excluded: `version`/`--version`/`-v` (bare scriptable value), the Compass
56
60
  // query family (`explore`/`search`/`recall`/`impact`/`trace`/`affected-tests`/
57
- // `hotspots`/`coupling`, machine-consumed output), `mcp` (a long-running stdio
61
+ // `hotspots`/`coupling`, machine-consumed output), `quick` (often --json),
62
+ // `mcp` (a long-running stdio
58
63
  // server), and `init` (already opens with the fuller `banner()`).
59
64
  const HEADER_COMMANDS = new Set([
60
65
  undefined,
@@ -71,6 +76,7 @@ const HEADER_COMMANDS = new Set([
71
76
  "index",
72
77
  "watch",
73
78
  "lawbook",
79
+ "quick",
74
80
  ]);
75
81
  /**
76
82
  * Print the branded header once, ahead of a command's output, when it is a
@@ -94,6 +100,14 @@ function maybeHeader(cmd, flags) {
94
100
  return;
95
101
  if (cmd === "drift" && flags.json)
96
102
  return;
103
+ if (cmd === "quick" && flags.json)
104
+ return;
105
+ if (cmd === "lawbook" && flags.json && flags._[0] === "level")
106
+ return;
107
+ if (cmd === "lawbook" && flags.json && flags._[0] === "investigate")
108
+ return;
109
+ if (cmd === "lawbook" && flags.json && flags._[0] === "draft")
110
+ return;
97
111
  header();
98
112
  }
99
113
  /** Run the handler for a single command. Returns when the command completes. */
@@ -135,6 +149,8 @@ async function dispatch(cmd, flags) {
135
149
  return (await import("./commands/query.js")).runQuery(cmd, flags);
136
150
  case "visualize":
137
151
  return (await import("./commands/visualize.js")).runVisualize(flags);
152
+ case "quick":
153
+ return (await import("./commands/quick.js")).runQuick(flags);
138
154
  case "lawbook":
139
155
  return (await import("./commands/lawbook.js")).runSpec(flags);
140
156
  case "doctor":
@@ -8,6 +8,7 @@ import { pkgName, pkgVersion } from "../../shared/version.js";
8
8
  import { indexExists, openDb } from "../compass/db.js";
9
9
  import { specList } from "../lawbook/engine.js";
10
10
  import { doctorDriftCheck } from "../lawbook/drift.js";
11
+ import { loadCeremonyConfig } from "../lawbook/levels.js";
11
12
  import { globError, hasBackend, hasBatchBackend, readLawManifest } from "./laws.js";
12
13
  import { redactValue } from "../../shared/redact.js";
13
14
  const STATUS_RANK = {
@@ -467,6 +468,98 @@ function specsOrphansCheck(projectPath) {
467
468
  remedy: `speclaw lawbook archive ${active[0]}`,
468
469
  };
469
470
  }
471
+ /** Ceremony config validity + archived level histogram. */
472
+ function ceremonyChecks(projectPath) {
473
+ const out = [];
474
+ const { invalidCuts } = loadCeremonyConfig(projectPath);
475
+ if (invalidCuts) {
476
+ out.push({
477
+ id: "cfg.ceremony.cuts",
478
+ title: "ceremony thresholds",
479
+ status: "warn",
480
+ detail: "invalid ceremony.cuts — using built-in defaults [3, 8, 15]",
481
+ remedy: "fix cuts in lawbook/config.yaml so they are strictly increasing, or remove the block",
482
+ });
483
+ }
484
+ else {
485
+ out.push({
486
+ id: "cfg.ceremony.cuts",
487
+ title: "ceremony thresholds",
488
+ status: "ok",
489
+ detail: "ceremony cuts valid (or using defaults)",
490
+ });
491
+ }
492
+ const archiveRoot = path.join(projectPath, "lawbook", "changes", "archive");
493
+ const counts = { "0": 0, "1": 0, "2": 0, "3": 0, missing: 0 };
494
+ if (fs.existsSync(archiveRoot)) {
495
+ for (const name of fs.readdirSync(archiveRoot)) {
496
+ const dir = path.join(archiveRoot, name);
497
+ if (!fs.statSync(dir).isDirectory())
498
+ continue;
499
+ // archived folder is YYYY-MM-DD-name; change.json lives inside
500
+ const rec = (() => {
501
+ try {
502
+ const p = path.join(dir, "change.json");
503
+ if (!fs.existsSync(p))
504
+ return null;
505
+ return JSON.parse(fs.readFileSync(p, "utf8"));
506
+ }
507
+ catch {
508
+ return null;
509
+ }
510
+ })();
511
+ if (!rec || rec.confirmedLevel === undefined) {
512
+ counts.missing += 1;
513
+ counts["3"] += 1;
514
+ }
515
+ else {
516
+ counts[String(rec.confirmedLevel)] = (counts[String(rec.confirmedLevel)] ?? 0) + 1;
517
+ }
518
+ }
519
+ }
520
+ const total = (counts["0"] ?? 0) + (counts["1"] ?? 0) + (counts["2"] ?? 0) + (counts["3"] ?? 0);
521
+ out.push({
522
+ id: "cfg.ceremony.levels",
523
+ title: "ceremony level distribution",
524
+ status: "ok",
525
+ value: JSON.stringify(counts),
526
+ detail: total === 0
527
+ ? "no archived changes"
528
+ : `archived levels: 0=${counts["0"]}, 1=${counts["1"]}, 2=${counts["2"]}, 3=${counts["3"]} (missing change.json=${counts.missing})`,
529
+ });
530
+ const typeCounts = { feature: 0, bug: 0, unknown: 0 };
531
+ if (fs.existsSync(archiveRoot)) {
532
+ for (const name of fs.readdirSync(archiveRoot)) {
533
+ const dir = path.join(archiveRoot, name);
534
+ if (!fs.statSync(dir).isDirectory())
535
+ continue;
536
+ try {
537
+ const p = path.join(dir, "change.json");
538
+ if (!fs.existsSync(p)) {
539
+ typeCounts.unknown += 1;
540
+ typeCounts.feature += 1;
541
+ continue;
542
+ }
543
+ const raw = JSON.parse(fs.readFileSync(p, "utf8"));
544
+ if (raw.changeType === "bug")
545
+ typeCounts.bug += 1;
546
+ else
547
+ typeCounts.feature += 1;
548
+ }
549
+ catch {
550
+ typeCounts.unknown += 1;
551
+ }
552
+ }
553
+ }
554
+ out.push({
555
+ id: "cfg.ceremony.changeTypes",
556
+ title: "change type distribution",
557
+ status: "ok",
558
+ value: JSON.stringify(typeCounts),
559
+ detail: `archived types: feature=${typeCounts.feature}, bug=${typeCounts.bug}`,
560
+ });
561
+ return out;
562
+ }
470
563
  function configurationChecks(projectPath, initialised) {
471
564
  if (!initialised) {
472
565
  const ids = [
@@ -559,6 +652,7 @@ export async function doctor(projectPath, opts = {}) {
559
652
  configuration.push(await budgetCheck(projectPath));
560
653
  configuration.push(freshnessCheck(projectPath));
561
654
  configuration.push(specsOrphansCheck(projectPath));
655
+ configuration.push(...ceremonyChecks(projectPath));
562
656
  {
563
657
  const d = doctorDriftCheck(projectPath);
564
658
  addCheck(configuration, {
@@ -1,11 +1,10 @@
1
1
  ---
2
- description: Finalize a completed change — sync specs into canonical, then archive it.
2
+ description: Finalize a completed change — sync specs when needed, then archive it.
3
3
  ---
4
4
 
5
5
  Archive the completed change: $ARGUMENTS
6
6
 
7
- Follow the `archive` skill: confirm every task is done and gates are green, run
8
- the reconciliation review (recommend a sync with short insights if the code
9
- drifted past the contracts), run `lawbook_validate`, then `lawbook_archive` with
10
- today's date (YYYY-MM-DD). It syncs the specs and moves the change to
11
- `lawbook/changes/archive/`. Never move the folder by hand.
7
+ Follow the `archive` skill: confirm every task (or level-0 checklist) is done
8
+ and gates are green, reconcile if the level has delta specs, run
9
+ `lawbook_validate`, then `lawbook_archive` with today's date (YYYY-MM-DD). Sync
10
+ runs only when the ceremony level requires specs. Never move the folder by hand.
@@ -1,12 +1,11 @@
1
1
  ---
2
- description: Draft a new spec-driven change (proposal, delta specs, tasks) before coding.
2
+ description: Draft a new spec-driven change at the confirmed ceremony level before coding.
3
3
  ---
4
4
 
5
5
  Draft a new change under `lawbook/changes/<name>/` for: $ARGUMENTS
6
6
 
7
- Follow the `draft` skill: ensure `lawbook/` exists (`lawbook_init`), investigate the
8
- code with `compass_explore`/`compass_recall`, read the governing
9
- `docs/standards/`, then write `proposal.md`, `specs/<capability>/spec.md`
10
- (normative `SHALL`/`MUST` + `#### Scenario:`), `design.md`, and
11
- `tasks.md` (with the mandatory steps from `lawbook/config.yaml`). Finish by
12
- running `lawbook_validate` and fixing every issue.
7
+ Follow the `draft` skill: ensure `lawbook/` exists (`lawbook_init`), investigate
8
+ with Compass, propose a ceremony level (`lawbook_level` mode `propose`) and
9
+ **confirm** it with the human (`set`), then scaffold only the artifacts that
10
+ level requires. For true one-liners use `speclaw quick` / the `quick` skill
11
+ instead. Finish by running `lawbook_validate` and fixing every issue.
@@ -0,0 +1,7 @@
1
+ ---
2
+ description: Investigate a bug — graph-backed suspect ranking before draft --bug.
3
+ ---
4
+
5
+ Investigate: $ARGUMENTS
6
+
7
+ Follow the `investigate` skill: refresh the index, call `lawbook_investigate`, explore the top suspect, then offer `draft --bug`.
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: Scaffold a level-0 lawbook change (record.md + reports) for a tiny fix.
3
+ ---
4
+
5
+ Scaffold a **ceremony level 0** change named `$ARGUMENTS` with `speclaw quick`
6
+ (or the equivalent `scaffoldQuick` path). Do **not** invent `proposal.md` /
7
+ `design.md` / delta specs for a true one-liner.
8
+
9
+ 1. Ensure `lawbook/` exists (`lawbook_init` if needed).
10
+ 2. Prefer passing `--path` / `--symbol` so the proposal rationale is measured.
11
+ 3. Confirm with the human if the measured proposal is higher than 0 — promote
12
+ (`lawbook_level` mode `promote`) instead of staying at quick.
13
+ 4. Implement, check the checklist in `record.md`, write a discipline report
14
+ under `reports/`, then archive (no sync required at level 0).
@@ -76,3 +76,11 @@ touched — and therefore which reports are owed, including `api.md` for any
76
76
  API-touching change — is the agent's responsibility to judge and satisfy before
77
77
  archiving; the engine gate counts files but cannot infer the set of concerns a
78
78
  change exercised.
79
+
80
+ ## 5. Bug changes must show the regression test failing first
81
+
82
+ When `changeType` is **bug**, the discipline report MUST include the output of
83
+ the regression test **failing before the fix** (or document why instrumentation
84
+ substitutes for a red-green cycle when reproduction is `unreproducible:`). A test
85
+ that only passes after the fix — with no evidence it ever failed — does not
86
+ satisfy the bug gate.
@@ -1,7 +1,8 @@
1
1
  # Validate and sync
2
2
 
3
- Run `lawbook_validate`, then `lawbook_sync` to promote the delta specs into
4
- `lawbook/specs/`. This is required, not optional: `lawbook_archive` refuses
5
- unless the canonical specs already match the delta specs.
3
+ Run `lawbook_validate`. If the confirmed ceremony level requires delta specs
4
+ (levels 1–3), run `lawbook_sync` to promote them into `lawbook/specs/`
5
+ `lawbook_archive` refuses unless the canonical specs already match. At **level
6
+ 0**, skip sync (there are no deltas).
6
7
 
7
8
  Next: read `steps/04-archive.md` and do only what it says.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: draft
3
- description: Draft a new spec-driven change — proposal, delta specs, and tasks — before writing any code. Use when the user wants to start, plan, or propose a new feature, fix, or refactor: "draft a change for X", "propose X", "let's plan X", "spec out X", "new change". Part of speclaw's lawbook module (draft → build → sync → archive).
3
+ description: Draft a new spec-driven change — propose a ceremony level, then write only the artifacts that level needs — before writing any code. Use when the user wants to start, plan, or propose a new feature, fix, or refactor: "draft a change for X", "propose X", "let's plan X", "spec out X", "new change". Part of speclaw's lawbook module (draft → build → sync → archive).
4
4
  ---
5
5
 
6
6
  # draft — Draft a new change
@@ -6,6 +6,9 @@
6
6
  - Clarify what the user wants (feature / fix / refactor) and confirm scope.
7
7
  - Use `compass_explore` and `compass_recall` (speclaw's code index) BEFORE
8
8
  grep/read to locate the real code the change touches and its blast radius.
9
+ - **Propose a ceremony level** with `lawbook_level` (mode `propose`) using the
10
+ paths/symbols you found; **confirm with the human** (mode `set`) before
11
+ writing artifacts. For an obvious one-liner, offer `speclaw quick` instead.
9
12
  - Read the governing standards in `docs/standards/` (architecture, backend,
10
13
  frontend, testing) so the change complies with the project's law.
11
14
 
@@ -1,15 +1,26 @@
1
1
  # Write the artifacts
2
2
 
3
- Create under `lawbook/changes/<name>/`:
4
-
5
- - **proposal.md** the why, the what, non-goals, and whether migrations are
6
- needed. Reference the team's tracker ticket if there is one.
7
- - **specs/<capability>/spec.md** the delta spec for each affected capability.
8
- `sync` promotes this by overwriting the whole canonical file, so the delta must
9
- carry the capability's **full** intended spec. When you are updating an existing
10
- capability, **start from the current `lawbook/specs/<capability>/spec.md`** and
11
- edit on top of it, so its existing requirements are carried forward — do not
12
- author it from scratch, or promotion will silently drop them. Use normative
3
+ Artifact volume follows the **confirmed ceremony level** in `change.json`
4
+ (propose + confirm with `lawbook_level` / the human **before** scaffolding).
5
+ Missing `change.json` means level 3.
6
+
7
+ Create under `lawbook/changes/<name>/` only what the level needs:
8
+
9
+ - **Level 0** prefer `speclaw quick` / the `quick` skill: `record.md`
10
+ (inline checklist) + `reports/` + `change.json`. No proposal/design/deltas.
11
+ - **Level 1** `record.md`, `tasks.md`, ≥1 delta under
12
+ `specs/<capability>/spec.md`, `reports/`, `change.json`.
13
+ - **Level 2** — `proposal.md`, `tasks.md`, delta specs, `reports/`;
14
+ `design.md` optional only with justification in `record.md`.
15
+ - **Level 3** — `proposal.md`, `design.md`, `tasks.md`, delta specs, `reports/`.
16
+ - **Bug (`draft --bug`)** — `bugfix.md` instead of proposal/design; see the investigate skill for RCA first.
17
+
18
+ For every level that needs delta specs:
19
+
20
+ - **specs/<capability>/spec.md** — the delta for each affected capability.
21
+ `sync` promotes by overwriting the whole canonical file, so the delta must
22
+ carry the capability's **full** intended spec. When updating an existing
23
+ capability, **start from** `lawbook/specs/<capability>/spec.md`. Use normative
13
24
  language and testable scenarios:
14
25
  ```markdown
15
26
  # <Capability>
@@ -22,20 +33,13 @@ Create under `lawbook/changes/<name>/`:
22
33
  - When <action>
23
34
  - Then <observable outcome>
24
35
  ```
25
- - **design.md** — always: approach, alternatives weighed, and the trade-offs
26
- behind the decision. For a small change, keep it short but write it.
27
- - **tasks.md** ordered, checkable steps. MUST include the mandatory steps
28
- from `lawbook/config.yaml` (feature branch first; tests reviewed and run;
29
- manual verification executed by the agent; discipline reports produced; docs
30
- updated; archive within the PR).
31
- - **reports/** create the folder with a short `reports/README.md` naming the
32
- discipline reports the change will need — one per discipline it touches, from an
33
- open set (`backend.md`, `frontend.md`, `api.md`, `database.md`, `infra.md`,
34
- `security.md`, … — and `api.md` is required when the change touches any API
35
- surface) that `build` will fill, following the required report structure
36
- (header · gates table · tests added · spec-scenario coverage · pre-existing
37
- failures · pending manual · verdict — see the `build` skill's discipline-reports
38
- step). Every change ships this folder; archive is blocked until it holds at
39
- least one discipline report.
36
+
37
+ - **tasks.md** (levels 1–3) ordered, checkable steps. MUST include the
38
+ mandatory steps from `lawbook/config.yaml` (feature branch first; tests;
39
+ manual verification by the agent; discipline reports; docs; archive in PR).
40
+ - **reports/** always scaffold with `reports/README.md` naming expected
41
+ disciplines (`backend.md`, `frontend.md`, `api.md`, … — `api.md` when an API
42
+ surface is touched). Archive is blocked until at least one discipline report
43
+ exists.
40
44
 
41
45
  Next: read `steps/05-validate.md` and do only what it says.
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: investigate
3
+ description: Forensic bug triage — rank suspects via lawbook_investigate before draft --bug. Use with a stack trace or symptom when starting RCA.
4
+ ---
5
+
6
+ # investigate — Bug RCA from the graph
7
+
8
+ Use when work is **"this is broken"**, not **"build X"**.
9
+
10
+ Read `steps/01-investigate.md` and do only what it says.
@@ -0,0 +1,7 @@
1
+ # Investigate the bug
2
+
3
+ - Refresh the index (`compass_index`).
4
+ - Call **`lawbook_investigate`** with `stackTrace` and/or `symptom`.
5
+ - **`compass_explore`** the top suspect — read the code yourself.
6
+
7
+ Next: read `steps/02-hand-off.md` and do only what it says.
@@ -0,0 +1,6 @@
1
+ # Hand off to draft
2
+
3
+ - **`compass_impact`** on the confirmed root cause for blast radius.
4
+ - **`speclaw lawbook draft --bug <name>`** — pre-seed only; fill repro, fix, regression test, prevention.
5
+
6
+ Treat the ranking as **evidence**, not a verdict. No further steps remain — investigate workflow complete.
@@ -0,0 +1,11 @@
1
+ ---
2
+ name: quick
3
+ description: Scaffold a level-0 lawbook change (record.md + reports) for a tiny fix. Use when the user wants a one-line fix, typo, or docs-only tweak without full ceremony — "quick change", "speclaw quick", "level 0", "skip proposal".
4
+ ---
5
+
6
+ # quick — Level-0 change
7
+
8
+ Create `lawbook/changes/<name>/` with `record.md`, `change.json` (confirmed
9
+ level 0), and `reports/`. No proposal, design, or delta specs.
10
+
11
+ Read `steps/01-scaffold.md` and do only what it says.
@@ -0,0 +1,6 @@
1
+ # Scaffold the level-0 change
2
+
3
+ Run `speclaw quick <name>` (or the library scaffold) so `record.md`,
4
+ `change.json`, and `reports/` exist. Pass `--path` / `--symbol` when known.
5
+
6
+ Next: read `steps/02-implement.md` and do only what it says.
@@ -0,0 +1,7 @@
1
+ # Implement and evidence
2
+
3
+ Make the fix, tick every `- [ ]` in `record.md`, and write at least one
4
+ discipline report under `reports/`. Archive with `lawbook_archive` (no sync at
5
+ level 0). Promote via `lawbook_level` if scope grew.
6
+
7
+ No further steps — workflow complete.