@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.
- package/dist/cli/commands/lawbook.js +84 -6
- package/dist/cli/commands/quick.js +35 -0
- package/dist/cli/commands/update.js +18 -0
- package/dist/cli/index.js +17 -1
- package/dist/modules/foundation/doctor.js +94 -0
- package/dist/modules/lawbook/assets/commands/archive.md +5 -6
- package/dist/modules/lawbook/assets/commands/draft.md +6 -7
- package/dist/modules/lawbook/assets/commands/investigate.md +7 -0
- package/dist/modules/lawbook/assets/commands/quick.md +14 -0
- package/dist/modules/lawbook/assets/rules/spec-reports-disciplines.md +8 -0
- package/dist/modules/lawbook/assets/skills/archive/steps/03-validate-and-sync.md +4 -3
- package/dist/modules/lawbook/assets/skills/draft/SKILL.md +1 -1
- package/dist/modules/lawbook/assets/skills/draft/steps/02-understand.md +3 -0
- package/dist/modules/lawbook/assets/skills/draft/steps/04-write-artifacts.md +29 -25
- package/dist/modules/lawbook/assets/skills/investigate/SKILL.md +10 -0
- package/dist/modules/lawbook/assets/skills/investigate/steps/01-investigate.md +7 -0
- package/dist/modules/lawbook/assets/skills/investigate/steps/02-hand-off.md +6 -0
- package/dist/modules/lawbook/assets/skills/quick/SKILL.md +11 -0
- package/dist/modules/lawbook/assets/skills/quick/steps/01-scaffold.md +6 -0
- package/dist/modules/lawbook/assets/skills/quick/steps/02-implement.md +7 -0
- package/dist/modules/lawbook/bugfix.js +195 -0
- package/dist/modules/lawbook/engine.js +178 -55
- package/dist/modules/lawbook/investigate.js +358 -0
- package/dist/modules/lawbook/levels.js +468 -0
- package/dist/modules/lawbook/quick.js +86 -0
- package/dist/modules/lawbook/register.js +18 -0
- package/dist/modules/lawbook/stack-parse.js +135 -0
- package/dist/shared/exposure.js +2 -0
- 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
|
|
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
|
-
|
|
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, "
|
|
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, "
|
|
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, "
|
|
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), `
|
|
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
|
|
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
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
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
|
|
8
|
-
|
|
9
|
-
`
|
|
10
|
-
|
|
11
|
-
|
|
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,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
|
|
4
|
-
`
|
|
5
|
-
unless the canonical specs already match
|
|
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 —
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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,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,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.
|