@esneiderbravo/speclaw 0.1.9 → 0.1.11
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/README.md +27 -13
- package/dist/modules/foundation/assets/AGENTS.template.md +6 -3
- package/dist/modules/foundation/assets/CLAUDE.template.md +20 -7
- package/dist/modules/foundation/assets/LAWS.template.md +1 -1
- package/dist/modules/foundation/assets/docs/compass.template.md +10 -4
- package/dist/modules/foundation/assets/docs/standards/lawbook.template.md +31 -8
- package/dist/modules/lawbook/assets/commands/archive.md +5 -4
- package/dist/modules/lawbook/assets/commands/draft.md +1 -1
- package/dist/modules/lawbook/assets/commands/sync.md +4 -3
- package/dist/modules/lawbook/assets/rules/spec-tasks-mandatory-steps.md +8 -0
- package/dist/modules/lawbook/assets/skills/archive/SKILL.md +33 -8
- package/dist/modules/lawbook/assets/skills/build/SKILL.md +15 -2
- package/dist/modules/lawbook/assets/skills/draft/SKILL.md +8 -4
- package/dist/modules/lawbook/assets/skills/sync/SKILL.md +24 -4
- package/dist/modules/lawbook/engine.js +58 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,26 +93,40 @@ Compass is inspired by [CodeGraph](https://github.com/colbymchenry/codegraph) an
|
|
|
93
93
|
## <img src="https://raw.githubusercontent.com/esneiderbravo/speclaw/main/brand/diamond.png" height="20" alt="◆" align="absmiddle"> The spec-driven workflow (Lawbook)
|
|
94
94
|
|
|
95
95
|
Lawbook is speclaw's answer to the biggest risk with AI agents: **code that
|
|
96
|
-
drifts from intent.**
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
drifts from intent.** The intent is written first, the code is made to match it,
|
|
97
|
+
and the spec is promoted to the project's canonical record — so nothing
|
|
98
|
+
non-trivial lands without a spec change. It's a loop of five steps:
|
|
99
99
|
|
|
100
100
|
```
|
|
101
|
-
|
|
101
|
+
┌─────────┐ ┌───────┐ ┌───────┐ ┌──────┐ ┌─────────┐
|
|
102
|
+
│ explore │ ─▶ │ draft │ ─▶ │ build │ ─▶ │ sync │ ─▶ │ archive │
|
|
103
|
+
└─────────┘ └───────┘ └───────┘ └──────┘ └─────────┘
|
|
102
104
|
```
|
|
103
105
|
|
|
104
106
|
| Step | What happens |
|
|
105
107
|
| :-- | :-- |
|
|
106
108
|
| **explore** | Think an idea through *before* committing to it — should we do this, and how. Writes nothing. |
|
|
107
|
-
| **draft** |
|
|
108
|
-
| **build** | Implement the tasks in order, keeping code and spec in agreement
|
|
109
|
-
| **sync** |
|
|
110
|
-
| **archive** |
|
|
111
|
-
|
|
112
|
-
**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
| **draft** | Capture the intent as a change under `lawbook/changes/<name>/` — four artifacts plus a `reports/` folder, always (see below). |
|
|
110
|
+
| **build** | Implement the tasks in order, keeping code and spec in agreement, and record test results under `reports/`. |
|
|
111
|
+
| **sync** | Reconcile the delta specs against what was actually built, then promote them into the canonical `lawbook/specs/` — the always-true description of how the system behaves. |
|
|
112
|
+
| **archive** | Reconcile, then validate, promote, and move the change to `lawbook/changes/archive/` — **in the same PR**, never a post-merge chore. Gated: refused while any task is unchecked, `reports/` is empty, or the specs are unsynced. |
|
|
113
|
+
|
|
114
|
+
**Every `draft` writes four artifacts under `lawbook/changes/<name>/` — none optional:**
|
|
115
|
+
|
|
116
|
+
| Artifact | What it captures |
|
|
117
|
+
| :-- | :-- |
|
|
118
|
+
| `proposal.md` | The **why** — motivation, what changes, non-goals, and whether migrations are needed. |
|
|
119
|
+
| `specs/<capability>/spec.md` | The **delta specs** — one per affected capability, normative and testable. |
|
|
120
|
+
| `design.md` | The **how** — approach, alternatives weighed, and the trade-offs behind the decision. |
|
|
121
|
+
| `tasks.md` | The **plan** — ordered, checkable steps, including the mandatory ones from `config.yaml`. |
|
|
122
|
+
|
|
123
|
+
Plus a **`reports/`** folder — scaffolded at draft, filled at build with one report per discipline (`backend.md`, `frontend.md`, …) recording the real unit/integration/e2e results. Evidence of testing travels with the change, and `lawbook_archive` refuses to archive without it.
|
|
124
|
+
|
|
125
|
+
> [!NOTE]
|
|
126
|
+
> **Delta specs are normative and testable.** Requirements use `SHALL`/`MUST`
|
|
127
|
+
> under `### Requirement:` headers, each with one or more `#### Scenario:` blocks
|
|
128
|
+
> whose acceptance criteria hold without production integrations. `lawbook_validate`
|
|
129
|
+
> checks that the code matches what the spec promises before you sync or archive.
|
|
116
130
|
|
|
117
131
|
**Three ways to drive it — same engine, no external CLI:**
|
|
118
132
|
|
|
@@ -16,9 +16,12 @@ Claude-specific notes: [`CLAUDE.md`](CLAUDE.md). The law: [`LAWS.md`](LAWS.md).
|
|
|
16
16
|
standards below. Open the standard that governs your change before making
|
|
17
17
|
it. Conflicts resolve in favor of the standard; amendments go through a
|
|
18
18
|
spec change, never silent deviation.
|
|
19
|
-
2. **
|
|
20
|
-
`compass_search` / `compass_recall`
|
|
21
|
-
|
|
19
|
+
2. **Compass first, always** — for any code question call `compass_explore` /
|
|
20
|
+
`compass_search` / `compass_recall` **before** any grep/sed/cat/Read,
|
|
21
|
+
including files you already know by name. Fall back to manual file tools only
|
|
22
|
+
after Compass returns nothing useful, the graph is missing (`compass_index`
|
|
23
|
+
first), or the target isn't indexed code (stylesheets, config, logs). Cheat
|
|
24
|
+
sheet: [`docs/compass.md`](docs/compass.md).
|
|
22
25
|
3. **Follow the lawbook workflow** for every non-trivial change; archive
|
|
23
26
|
within the same PR. Rules:
|
|
24
27
|
[`docs/standards/lawbook.md`](docs/standards/lawbook.md).
|
|
@@ -27,13 +27,26 @@ When any instruction conflicts with a standard, **the standard wins** — and if
|
|
|
27
27
|
you believe it is wrong, propose an amendment via a spec change; never silently
|
|
28
28
|
ignore it.
|
|
29
29
|
|
|
30
|
-
## Rule 1 — Compass
|
|
31
|
-
|
|
32
|
-
This repo is indexed by Compass, speclaw's local code graph (`.speclaw/`).
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
## Rule 1 — Compass first, always
|
|
31
|
+
|
|
32
|
+
This repo is indexed by Compass, speclaw's local code graph (`.speclaw/`). For
|
|
33
|
+
**any** question about code — what a symbol is, what it uses, who calls it,
|
|
34
|
+
where it lives, how a value flows — call Compass **first**: `compass_search` /
|
|
35
|
+
`compass_recall` to locate, `compass_explore` to read a symbol with its callers
|
|
36
|
+
and callees, `compass_impact` / `compass_trace` for blast radius and call paths.
|
|
37
|
+
Run `compass_index` first if the graph is missing.
|
|
38
|
+
|
|
39
|
+
This includes files you already know the name of: to learn what `Foo` imports,
|
|
40
|
+
uses, or depends on, run `compass_explore Foo` — do **not** `cat`/`sed`/`grep`/
|
|
41
|
+
Read the file to work it out by hand. "I know which file it is" is not an
|
|
42
|
+
exemption.
|
|
43
|
+
|
|
44
|
+
Fall back to Grep / Read / `sed` / `cat` **only after** you can name which of
|
|
45
|
+
these holds: (1) a Compass call actually ran and returned nothing useful for the
|
|
46
|
+
query, (2) the graph is missing and `compass_index` can't be run, or (3) the
|
|
47
|
+
target is not indexed code — stylesheets, JSON/config, markdown, logs,
|
|
48
|
+
generated files, lockfiles. Never skip Compass because grep "feels faster."
|
|
49
|
+
See [`docs/compass.md`](docs/compass.md).
|
|
37
50
|
|
|
38
51
|
## Rule 2 — Spec-driven, always
|
|
39
52
|
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
| Documentation | [`docs/standards/documentation.md`](docs/standards/documentation.md) | Docstring/API-comment convention per language |
|
|
23
23
|
| Conventions | [`docs/standards/conventions.md`](docs/standards/conventions.md) | Branches, PRs, tracker, versioning |
|
|
24
24
|
| Lawbook | [`docs/standards/lawbook.md`](docs/standards/lawbook.md) | Spec-driven workflow, mandatory task steps, archiving |
|
|
25
|
-
| Compass | [`docs/compass.md`](docs/compass.md) | Using the code knowledge graph before grep |
|
|
25
|
+
| Compass | [`docs/compass.md`](docs/compass.md) | Using the code knowledge graph first — before any grep/read |
|
|
26
26
|
|
|
27
27
|
## Binding rules
|
|
28
28
|
|
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
**Compass** is speclaw's local code graph: a pre-indexed map of every symbol
|
|
4
4
|
(node) and relationship (edge) in this workspace, plus a local vector store for
|
|
5
|
-
semantic recall. Agents MUST
|
|
6
|
-
|
|
5
|
+
semantic recall. Agents **MUST** call Compass first for any code question —
|
|
6
|
+
before any `grep`/`sed`/`cat`/Read, and before opening a file whose name they
|
|
7
|
+
already know. Manual file tools are a fallback used **only after** a Compass
|
|
8
|
+
call returns nothing useful, the graph is missing, or the target isn't indexed
|
|
9
|
+
code (stylesheets, config, logs). This is Rule 1 of the agent contract
|
|
10
|
+
(`AGENTS.md`).
|
|
7
11
|
|
|
8
12
|
It runs entirely on your machine, needs no LLM and no external service, and
|
|
9
13
|
stores everything in `.speclaw/` (gitignored). It ships inside speclaw — there
|
|
@@ -33,8 +37,10 @@ a request, not whole files.
|
|
|
33
37
|
| `compass_trace` | Trace a call path between two nodes — how an entrypoint reaches a sink. |
|
|
34
38
|
| `compass_watch` | Keep the index fresh automatically (start/stop a debounced incremental re-index on file change). |
|
|
35
39
|
|
|
36
|
-
If the graph is missing (no `.speclaw/index.db`), run `compass_index` first
|
|
37
|
-
|
|
40
|
+
If the graph is missing (no `.speclaw/index.db`), run `compass_index` first —
|
|
41
|
+
a missing graph is not license to skip Compass. The only legitimate fallbacks
|
|
42
|
+
to Grep/Read: a Compass call returned nothing useful for your query, or the
|
|
43
|
+
target isn't indexed code (stylesheets, JSON/config, markdown, logs).
|
|
38
44
|
|
|
39
45
|
## Project-specific starting points
|
|
40
46
|
|
|
@@ -10,20 +10,32 @@ No non-trivial change lands without a spec change:
|
|
|
10
10
|
|
|
11
11
|
1. **explore** — think an idea through before committing (writes nothing).
|
|
12
12
|
2. **draft** — create `lawbook/changes/<name>/`: `proposal.md`, delta specs under
|
|
13
|
-
`specs/<capability>/spec.md`,
|
|
13
|
+
`specs/<capability>/spec.md`, `design.md`, `tasks.md`, and a `reports/`
|
|
14
|
+
folder.
|
|
14
15
|
3. **build** — implement the tasks in order, keeping code and spec in
|
|
15
|
-
agreement
|
|
16
|
-
4. **sync** —
|
|
17
|
-
`lawbook/specs/` (`lawbook_sync`).
|
|
18
|
-
|
|
19
|
-
|
|
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).
|
|
20
23
|
|
|
21
24
|
## Mandatory task steps
|
|
22
25
|
|
|
23
26
|
`tasks.md` MUST include the steps defined in `lawbook/config.yaml` and the
|
|
24
27
|
`spec-tasks-mandatory-steps` rule: feature branch first, tests reviewed and
|
|
25
|
-
run, manual verification executed by the agent,
|
|
26
|
-
the PR. The agent performs the manual testing
|
|
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.
|
|
31
|
+
|
|
32
|
+
## Reports
|
|
33
|
+
|
|
34
|
+
Every change carries a `reports/` folder. `build` writes one report per
|
|
35
|
+
discipline it touched (`backend.md`, `frontend.md`, …) recording what was tested
|
|
36
|
+
and the real results — unit, integration, and end-to-end as applicable — with
|
|
37
|
+
the commands run and their output. It is evidence of testing that travels with
|
|
38
|
+
the change; the archive is blocked until at least one discipline report exists.
|
|
27
39
|
|
|
28
40
|
## Delta specs
|
|
29
41
|
|
|
@@ -39,6 +51,17 @@ the PR. The agent performs the manual testing itself — never delegates it.
|
|
|
39
51
|
Always archive with the `archive` command / `lawbook_archive` tool, never a manual
|
|
40
52
|
`mv` — the tool performs the spec promotion and validation a manual move skips.
|
|
41
53
|
|
|
54
|
+
Before archiving, the agent runs a reconciliation review: it compares what was
|
|
55
|
+
built against the delta specs and, when the code has drifted past the original
|
|
56
|
+
contracts, shows short insights and reconciles the delta specs.
|
|
57
|
+
|
|
58
|
+
`lawbook_archive` is then **gated in the engine** — it refuses to archive (and
|
|
59
|
+
reports the reason) while any task is unchecked, while `reports/` holds no
|
|
60
|
+
discipline report, or while the delta specs are not yet synced into the
|
|
61
|
+
canonical specs. Because the gate covers both the tool and the CLI, a change
|
|
62
|
+
reaches the archive only when it is genuinely complete: reconcile, `sync`, then
|
|
63
|
+
archive.
|
|
64
|
+
|
|
42
65
|
## Amendments to the law
|
|
43
66
|
|
|
44
67
|
The standards in `docs/standards/` are amended like code: through a spec change
|
|
@@ -4,7 +4,8 @@ description: Finalize a completed change — sync specs into canonical, then arc
|
|
|
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,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
the
|
|
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,6 +7,6 @@ Draft a new change under `lawbook/changes/<name>/` for: $ARGUMENTS
|
|
|
7
7
|
Follow the `draft` skill: ensure `lawbook/` exists (`lawbook_init`), investigate the
|
|
8
8
|
code with `compass_explore`/`compass_recall`, read the governing
|
|
9
9
|
`docs/standards/`, then write `proposal.md`, `specs/<capability>/spec.md`
|
|
10
|
-
(normative `SHALL`/`MUST` + `#### Scenario:`),
|
|
10
|
+
(normative `SHALL`/`MUST` + `#### Scenario:`), `design.md`, and
|
|
11
11
|
`tasks.md` (with the mandatory steps from `lawbook/config.yaml`). Finish by
|
|
12
12
|
running `lawbook_validate` and fixing every issue.
|
|
@@ -4,6 +4,7 @@ description: Promote a change's delta specs into the canonical specs, without ar
|
|
|
4
4
|
|
|
5
5
|
Sync the change's specs into canonical: $ARGUMENTS
|
|
6
6
|
|
|
7
|
-
Follow the `sync` skill:
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
Follow the `sync` skill: reconcile the delta specs against what was actually
|
|
8
|
+
built (branch diff + code graph), validate the change (`lawbook_validate`), then
|
|
9
|
+
run `lawbook_sync` to promote each delta spec into `lawbook/specs/`. Report what
|
|
10
|
+
you reconciled and what was promoted; leave the change active.
|
|
@@ -21,6 +21,9 @@ steps, branch convention, and testing/documentation requirements.
|
|
|
21
21
|
(see `docs/standards/testing-standards.md`).
|
|
22
22
|
- Perform manual verification of the behavior — **the agent executes this
|
|
23
23
|
itself, never the user.**
|
|
24
|
+
- Produce the discipline reports under `reports/` (`backend.md`, `frontend.md`,
|
|
25
|
+
… as relevant) with the unit/integration/e2e results for what the feature
|
|
26
|
+
touched.
|
|
24
27
|
- Update the technical documentation the change touches.
|
|
25
28
|
- Archive the change within the same PR (the `archive` command / `lawbook_archive`
|
|
26
29
|
tool).
|
|
@@ -35,3 +38,8 @@ not complete until the agent has verified it.
|
|
|
35
38
|
|
|
36
39
|
A change is not done until it is archived with `lawbook_archive` (never a manual
|
|
37
40
|
`mv`). The archive lands in the same PR that implements the change.
|
|
41
|
+
|
|
42
|
+
`lawbook_archive` is gated: it refuses to archive while any task is unchecked,
|
|
43
|
+
while `reports/` has no discipline report, or while the delta specs are not yet
|
|
44
|
+
synced into the canonical specs. Resolve those first — the gate is enforced in
|
|
45
|
+
the engine, so a manual `mv` only hides an incomplete change.
|
|
@@ -9,14 +9,39 @@ Close out a completed change: its delta specs become canonical and the change
|
|
|
9
9
|
folder moves to `lawbook/changes/archive/`. This is part of the PR that
|
|
10
10
|
implements the change, not a post-merge chore.
|
|
11
11
|
|
|
12
|
+
`lawbook_archive` is **gated** — the engine refuses to archive (and reports the
|
|
13
|
+
reason) while any task is unchecked, while `reports/` holds no discipline report,
|
|
14
|
+
or while the delta specs are not yet synced into the canonical specs. So archive
|
|
15
|
+
is the last step of a completed change: reconcile, sync, then archive.
|
|
16
|
+
|
|
12
17
|
## Steps
|
|
13
18
|
|
|
14
19
|
1. Confirm the change is truly done: every task in `tasks.md` checked, quality
|
|
15
|
-
gates green, behavior verified
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
`
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
gates green, behavior verified, and the discipline reports written under
|
|
21
|
+
`reports/`.
|
|
22
|
+
|
|
23
|
+
2. **Reconciliation review (agent-executed).** Run the reconciliation from the
|
|
24
|
+
`sync` skill: reconstruct what was built (branch diff since draft +
|
|
25
|
+
`compass_explore` / `compass_impact`) and compare it to the change's delta
|
|
26
|
+
specs.
|
|
27
|
+
- **If the code drifted past the contracts:** show short insights — a tight
|
|
28
|
+
bullet list of what was built outside the delta specs and why it matters
|
|
29
|
+
(e.g. "DB path renamed to `data/app.db` + auto-migration — infra behavior
|
|
30
|
+
absent from the spec") — and reconcile the delta specs (write the drift
|
|
31
|
+
in). Drift left unreconciled cannot be archived: the specs-synced gate will
|
|
32
|
+
block it.
|
|
33
|
+
- **If nothing drifted:** say so and continue.
|
|
34
|
+
|
|
35
|
+
3. Run `lawbook_validate`, then `lawbook_sync` to promote the delta specs into
|
|
36
|
+
`lawbook/specs/`. This is required, not optional: `lawbook_archive` refuses
|
|
37
|
+
unless the canonical specs already match the delta specs.
|
|
38
|
+
|
|
39
|
+
4. Run the `lawbook_archive` tool with the change name and today's date
|
|
40
|
+
(`YYYY-MM-DD`). It re-checks the gate deterministically and, if it passes,
|
|
41
|
+
moves `lawbook/changes/<name>/` to `lawbook/changes/archive/<date>-<name>/`.
|
|
42
|
+
If it refuses, resolve the reported blockers (unchecked tasks, missing
|
|
43
|
+
reports, unsynced specs) and retry.
|
|
44
|
+
|
|
45
|
+
5. Report the archive path, what you reconciled (or that nothing drifted), and
|
|
46
|
+
the promoted specs. Never move the folder by hand — a manual `mv` skips the
|
|
47
|
+
gate and hides an incomplete change.
|
|
@@ -43,7 +43,20 @@ Run them yourself and report real output. A red gate blocks completion.
|
|
|
43
43
|
Exercise the behavior (endpoint/UI/CLI) yourself where feasible — do not
|
|
44
44
|
delegate manual testing to the user. Record what you verified.
|
|
45
45
|
|
|
46
|
-
## Step 5 —
|
|
46
|
+
## Step 5 — Write the discipline reports (mandatory)
|
|
47
|
+
|
|
48
|
+
Record the evidence of testing under `lawbook/changes/<name>/reports/`, one file
|
|
49
|
+
per discipline the change touched (`backend.md`, `frontend.md`, …). Each report
|
|
50
|
+
states what was tested and the real results — unit, integration, and end-to-end
|
|
51
|
+
as applicable — with the commands run and their output and a one-line verdict.
|
|
52
|
+
If a test kind does not yet apply (e.g. no unit runner), say so and record the
|
|
53
|
+
gates and manual verification that stood in. Omit disciplines the change did not
|
|
54
|
+
touch. The archive is blocked until at least one discipline report exists.
|
|
55
|
+
|
|
56
|
+
## Step 6 — Hand off
|
|
47
57
|
|
|
48
58
|
When every task is checked and gates are green, tell the user the change is
|
|
49
|
-
ready to `sync` and `archive`.
|
|
59
|
+
ready to `sync` and `archive`. Keep the delta specs current as you build, but
|
|
60
|
+
know that `sync` formally reconciles the delta specs against what was actually
|
|
61
|
+
built — so behavior that drifted past the original spec is caught there, not
|
|
62
|
+
left to chance.
|
|
@@ -46,12 +46,16 @@ Create under `lawbook/changes/<name>/`:
|
|
|
46
46
|
- When <action>
|
|
47
47
|
- Then <observable outcome>
|
|
48
48
|
```
|
|
49
|
-
- **design.md**
|
|
50
|
-
|
|
49
|
+
- **design.md** — always: approach, alternatives weighed, and the trade-offs
|
|
50
|
+
behind the decision. For a small change, keep it short — but write it.
|
|
51
51
|
- **tasks.md** — ordered, checkable steps. MUST include the mandatory steps
|
|
52
52
|
from `lawbook/config.yaml` (feature branch first; tests reviewed and run;
|
|
53
|
-
manual verification executed by the agent;
|
|
54
|
-
PR).
|
|
53
|
+
manual verification executed by the agent; discipline reports produced; docs
|
|
54
|
+
updated; archive within the PR).
|
|
55
|
+
- **reports/** — create the folder with a short `reports/README.md` naming the
|
|
56
|
+
discipline reports (`backend.md`, `frontend.md`, … as relevant) that `build`
|
|
57
|
+
will fill with real test results. Every change ships this folder; archive is
|
|
58
|
+
blocked until it holds at least one discipline report.
|
|
55
59
|
|
|
56
60
|
## Step 4 — Validate
|
|
57
61
|
|
|
@@ -9,13 +9,33 @@ Update the project's canonical specifications (`lawbook/specs/`) with a change's
|
|
|
9
9
|
delta specs, without archiving the change. Use this when the specs should
|
|
10
10
|
become the source of truth but the change isn't finished (e.g. multi-PR work).
|
|
11
11
|
|
|
12
|
+
`lawbook_sync` is a deterministic copy — it is blind to the code. So before
|
|
13
|
+
promoting, YOU reconcile the delta specs against what was actually built, so the
|
|
14
|
+
specs that become canonical describe reality, not just the original draft.
|
|
15
|
+
|
|
12
16
|
## Steps
|
|
13
17
|
|
|
14
18
|
1. Confirm which change to sync (run `lawbook_list` if unsure).
|
|
15
|
-
|
|
19
|
+
|
|
20
|
+
2. **Reconcile code → delta specs (agent-executed).** Before promoting, compare
|
|
21
|
+
what was built against the change's delta specs:
|
|
22
|
+
- Reconstruct what shipped: `git diff <branch-point>...HEAD` for the change's
|
|
23
|
+
branch, then `compass_explore` / `compass_impact` on the touched symbols to
|
|
24
|
+
understand behavior, not just changed lines.
|
|
25
|
+
- Diff intent vs reality: list behavior that is implemented but missing from,
|
|
26
|
+
or contradicted by, `lawbook/changes/<name>/specs/**`.
|
|
27
|
+
- Write the gaps into the delta specs — normative `SHALL`/`MUST` requirements
|
|
28
|
+
under `### Requirement:` and `#### Scenario:` acceptance criteria — so the
|
|
29
|
+
contract matches what was built. Capture only behavior that actually
|
|
30
|
+
exists; never invent scope that was not implemented.
|
|
31
|
+
- If nothing drifted, make no edits and say so.
|
|
32
|
+
|
|
33
|
+
3. Run `lawbook_validate` for the change; do not sync a change whose specs are
|
|
16
34
|
invalid.
|
|
17
|
-
|
|
35
|
+
|
|
36
|
+
4. Run the `lawbook_sync` tool for the change. It copies each
|
|
18
37
|
`lawbook/changes/<name>/specs/<capability>/spec.md` over the canonical
|
|
19
38
|
`lawbook/specs/<capability>/spec.md` and reports what it promoted.
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
|
|
40
|
+
5. Report to the user what you reconciled (or that nothing drifted) and the
|
|
41
|
+
promoted files. The change stays active — `archive` it when it's fully done.
|
|
@@ -26,6 +26,7 @@ mandatory_task_steps:
|
|
|
26
26
|
- "Review and update the affected tests."
|
|
27
27
|
- "Run the quality gates and verify they pass (see docs/standards/testing-standards.md)."
|
|
28
28
|
- "Perform manual verification of the behavior — the agent executes this itself, never the user."
|
|
29
|
+
- "Produce the discipline reports under reports/ (unit/integration/e2e results for what the feature touched)."
|
|
29
30
|
- "Update the technical documentation touched by the change."
|
|
30
31
|
- "Archive the change within the same PR (lawbook:archive)."
|
|
31
32
|
|
|
@@ -38,7 +39,7 @@ This directory is managed by speclaw's **lawbook** module.
|
|
|
38
39
|
|
|
39
40
|
- \`specs/\` — the canonical specifications (the current source of truth).
|
|
40
41
|
- \`changes/<name>/\` — an in-flight change: \`proposal.md\`, \`tasks.md\`,
|
|
41
|
-
|
|
42
|
+
\`design.md\`, and \`specs/<capability>/spec.md\` delta specs.
|
|
42
43
|
- \`changes/archive/\` — completed, archived changes.
|
|
43
44
|
- \`config.yaml\` — mandatory task steps and workflow rules.
|
|
44
45
|
|
|
@@ -185,6 +186,56 @@ export function specSync(projectPath, change) {
|
|
|
185
186
|
walk(changeSpecs);
|
|
186
187
|
return { change, promoted };
|
|
187
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Deterministic completeness checks that gate archiving a change. Returns the
|
|
191
|
+
* blocking reasons; an empty array means the change may be archived.
|
|
192
|
+
*
|
|
193
|
+
* A change is blocked when any task is still unchecked, when it has no discipline
|
|
194
|
+
* report under reports/, or when its delta specs are not synced — the canonical
|
|
195
|
+
* spec is missing for, or differs from, a delta (meaning sync was not run after
|
|
196
|
+
* the last spec edit). The reports/README.md scaffold does not count as a report.
|
|
197
|
+
*
|
|
198
|
+
* @param projectPath - Absolute path to the project root.
|
|
199
|
+
* @param change - Change name (folder under lawbook/changes/).
|
|
200
|
+
* @returns Human-readable blockers; empty when the change is ready to archive.
|
|
201
|
+
*/
|
|
202
|
+
export function specArchivePreconditions(projectPath, change) {
|
|
203
|
+
const root = specRoot(projectPath);
|
|
204
|
+
const changeDir = path.join(root, "changes", change);
|
|
205
|
+
if (!fs.existsSync(changeDir))
|
|
206
|
+
return [`change "${change}" not found under lawbook/changes/`];
|
|
207
|
+
const blockers = [];
|
|
208
|
+
// 1. Every task must be checked.
|
|
209
|
+
const tasksPath = path.join(changeDir, "tasks.md");
|
|
210
|
+
if (!fs.existsSync(tasksPath)) {
|
|
211
|
+
blockers.push("missing tasks.md");
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
const unchecked = (fs.readFileSync(tasksPath, "utf8").match(/^\s*[-*]\s+\[ \]/gm) ?? []).length;
|
|
215
|
+
if (unchecked > 0)
|
|
216
|
+
blockers.push(`${unchecked} unchecked task(s) in tasks.md`);
|
|
217
|
+
}
|
|
218
|
+
// 2. At least one discipline report must exist (README.md scaffold aside).
|
|
219
|
+
const reportsDir = path.join(changeDir, "reports");
|
|
220
|
+
const reports = fs.existsSync(reportsDir)
|
|
221
|
+
? fs.readdirSync(reportsDir).filter((n) => n.endsWith(".md") && n.toLowerCase() !== "readme.md")
|
|
222
|
+
: [];
|
|
223
|
+
if (reports.length === 0) {
|
|
224
|
+
blockers.push("no discipline report under reports/ (build must record what was tested)");
|
|
225
|
+
}
|
|
226
|
+
// 3. Delta specs must already be synced into the canonical specs.
|
|
227
|
+
for (const file of deltaSpecFiles(changeDir)) {
|
|
228
|
+
const rel = path.relative(path.join(changeDir, "specs"), file);
|
|
229
|
+
const canonical = path.join(root, "specs", rel);
|
|
230
|
+
if (!fs.existsSync(canonical)) {
|
|
231
|
+
blockers.push(`spec not synced: lawbook/specs/${rel} missing (run sync first)`);
|
|
232
|
+
}
|
|
233
|
+
else if (fs.readFileSync(file, "utf8") !== fs.readFileSync(canonical, "utf8")) {
|
|
234
|
+
blockers.push(`spec not synced: lawbook/specs/${rel} differs from the delta (run sync first)`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return blockers;
|
|
238
|
+
}
|
|
188
239
|
/**
|
|
189
240
|
* Finalize a change: promote its delta specs (via {@link specSync}), then move
|
|
190
241
|
* it to changes/archive/<date>-<name>/.
|
|
@@ -193,13 +244,18 @@ export function specSync(projectPath, change) {
|
|
|
193
244
|
* @param change - Change name (folder under lawbook/changes/).
|
|
194
245
|
* @param date - Archive date prefix, formatted YYYY-MM-DD.
|
|
195
246
|
* @returns The promoted specs and the archive destination path.
|
|
196
|
-
* @throws If the change does not exist,
|
|
247
|
+
* @throws If the change does not exist, the archive target already exists, or
|
|
248
|
+
* any archive precondition (see {@link specArchivePreconditions}) is unmet.
|
|
197
249
|
*/
|
|
198
250
|
export function specArchive(projectPath, change, date) {
|
|
199
251
|
const root = specRoot(projectPath);
|
|
200
252
|
const changeDir = path.join(root, "changes", change);
|
|
201
253
|
if (!fs.existsSync(changeDir))
|
|
202
254
|
throw new Error(`change "${change}" not found`);
|
|
255
|
+
const blockers = specArchivePreconditions(projectPath, change);
|
|
256
|
+
if (blockers.length > 0) {
|
|
257
|
+
throw new Error(`cannot archive "${change}" — resolve first:\n${blockers.map((b) => ` - ${b}`).join("\n")}`);
|
|
258
|
+
}
|
|
203
259
|
const { promoted } = specSync(projectPath, change);
|
|
204
260
|
const archiveDir = path.join(root, "changes", "archive", `${date}-${change}`);
|
|
205
261
|
fs.mkdirSync(path.dirname(archiveDir), { recursive: true });
|