@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 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">&nbsp; 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.** No non-trivial change lands without a spec change the
97
- intent is written first, the code is made to match it, and the spec is promoted
98
- to the project's canonical record. It's a loop of five steps:
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
- explore → draft → build → sync → archive
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** | Create the change under `lawbook/changes/<name>/`: a `proposal.md` (why · what · non-goals), **delta specs** in `specs/<capability>/spec.md`, an optional `design.md`, and a `tasks.md` checklist. |
108
- | **build** | Implement the tasks in order, keeping code and spec in agreement. |
109
- | **sync** | Promote the change's delta specs into the canonical `lawbook/specs/` — the always-true description of how the system behaves. |
110
- | **archive** | Validate, promote, and move the change to `lawbook/changes/archive/` — **in the same PR**, never a post-merge chore. |
111
-
112
- **Delta specs are normative and testable.** Requirements use `SHALL`/`MUST`
113
- under `### Requirement:` headers, each with one or more `#### Scenario:` blocks
114
- whose acceptance criteria hold without production integrations. `lawbook_validate`
115
- checks that the code matches what the spec promises before you sync or archive.
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. **Use Compass before file scanning** — the `compass_explore` /
20
- `compass_search` / `compass_recall` tools (run `compass_index` first if the
21
- graph is missing). Cheat sheet: [`docs/compass.md`](docs/compass.md).
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 before grep
31
-
32
- This repo is indexed by Compass, speclaw's local code graph (`.speclaw/`). Use
33
- the `compass_explore`, `compass_search`, and `compass_recall` tools **BEFORE**
34
- grep/find or reading files at random; run `compass_index` first if the graph
35
- is missing. See [`docs/compass.md`](docs/compass.md). Fall back to Grep/Read
36
- only when the graph doesn't cover what you need.
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 use it **before** manual grep/read loops when
6
- exploring or editing code this is Rule 1 of the agent contract (`AGENTS.md`).
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
- until then, fall back to Grep/Read.
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`, optional `design.md`, and `tasks.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** — promote the change's delta specs into the canonical
17
- `lawbook/specs/` (`lawbook_sync`).
18
- 5. **archive** finalize: sync + move the change to `lawbook/changes/archive/`
19
- (`lawbook_archive`), **within the same PR** never a post-merge chore.
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, docs updated, archive within
26
- the PR. The agent performs the manual testing itself — never delegates it.
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
- run `lawbook_validate`, then `lawbook_archive` with today's date (YYYY-MM-DD). It
9
- syncs the specs and moves the change to `lawbook/changes/archive/`. Never move
10
- the folder by hand.
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:`), optional `design.md`, and
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: validate the change (`lawbook_validate`), then run
8
- `lawbook_sync` to promote each delta spec into `lawbook/specs/`. Report what was
9
- promoted; leave the change active.
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
- 2. Run `lawbook_validate` for the change; resolve any issues first.
17
- 3. Run the `lawbook_archive` tool with the change name and today's date
18
- (`YYYY-MM-DD`). It syncs the delta specs into `lawbook/specs/` and then moves
19
- `lawbook/changes/<name>/` to `lawbook/changes/archive/<date>-<name>/`.
20
- 4. Report the archive path and the promoted specs. Never move the folder by
21
- hand — always use `lawbook_archive`, which performs the sync and validation a
22
- manual move would skip.
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 — Hand off
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** (optional) for non-trivial changes: approach, alternatives,
50
- trade-offs.
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; docs updated; archive within the
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
- 2. Run `lawbook_validate` for the change; do not sync a change whose specs are
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
- 3. Run the `lawbook_sync` tool for the change. It copies each
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
- 4. Report the promoted files to the user. The change stays active — `archive`
21
- it when it's fully done.
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
- optional \`design.md\`, and \`specs/<capability>/spec.md\` delta specs.
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, or the archive target already exists.
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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esneiderbravo/speclaw",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },