@gobing-ai/spur 0.3.20 → 0.3.21
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/config/rules/typescript/prefer-accessible-role-for-button-queries.yaml +31 -0
- package/config/templates/docs/00_ADR.md +25 -15
- package/config/templates/docs/01_PRD.md +12 -7
- package/config/templates/docs/02_ROADMAP.md +16 -8
- package/config/templates/docs/03_ARCHITECTURE.md +12 -5
- package/config/templates/docs/04_DESIGN.md +26 -8
- package/config/templates/docs/05_FEATURES.md +18 -7
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +427 -33
- package/package.json +1 -1
- package/spur.js +13 -4
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
$schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
|
|
2
|
+
# Forbid getByLabelText queries on button accessible names in web component tests.
|
|
3
|
+
#
|
|
4
|
+
# WHY: HTML `<button>` elements are non-labellable per WHATWG HTML spec. In DOM Testing
|
|
5
|
+
# Library, `getByLabelText` queries elements associated with `<label>` tags. When used to
|
|
6
|
+
# query a `<button>` with an `aria-label` attribute, it works when happy-dom DOM state is
|
|
7
|
+
# fresh, but throws `TestingLibraryElementError: element associated with this label is non-labellable`
|
|
8
|
+
# when running in full multi-file test suites on CI (e.g. Linux runners).
|
|
9
|
+
# Real incident: CI-only failure in apps/web/tests/modules/task-kanban/components.test.tsx on 2026-07-24.
|
|
10
|
+
#
|
|
11
|
+
# FIX: Use `getByRole('button', { name: '...' })` or `getByRole('button', { name: /.../ })`,
|
|
12
|
+
# which queries the W3C accessible name provided by `aria-label`.
|
|
13
|
+
include:
|
|
14
|
+
- "apps/web/tests/**/*.test.ts"
|
|
15
|
+
- "apps/web/tests/**/*.test.tsx"
|
|
16
|
+
exclude:
|
|
17
|
+
- "**/node_modules/**"
|
|
18
|
+
- "**/dist/**"
|
|
19
|
+
|
|
20
|
+
rules:
|
|
21
|
+
- id: prefer-accessible-role-for-button-queries
|
|
22
|
+
description: >
|
|
23
|
+
Do not query button accessible names with `getByLabelText` in web React tests.
|
|
24
|
+
`<button>` elements are non-labellable per WHATWG spec, causing timing-dependent
|
|
25
|
+
CI failures when tests run sequentially in a single process.
|
|
26
|
+
Use `getByRole('button', { name: ... })` instead.
|
|
27
|
+
severity: warning
|
|
28
|
+
evaluator:
|
|
29
|
+
type: rg
|
|
30
|
+
config:
|
|
31
|
+
pattern: "getByLabelText\\(['\"]Sort "
|
|
@@ -4,29 +4,39 @@ doc: 00_ADR
|
|
|
4
4
|
owns: WHY — which cross-cutting decision was made, and the one-line reason
|
|
5
5
|
authority: authoritative
|
|
6
6
|
version: 1.0.0
|
|
7
|
-
|
|
8
|
-
updated_at:
|
|
7
|
+
owner: _(project owner)_
|
|
8
|
+
updated_at: {{init-date}}
|
|
9
|
+
read_before: any structural change
|
|
10
|
+
edit_rules: 99 §6.1
|
|
11
|
+
sync: [T1, T2]
|
|
9
12
|
---
|
|
10
13
|
|
|
11
14
|
# Architecture Decision Records
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
> Each entry is append-only; supersession is by a new dated entry, never by editing an old one.
|
|
15
|
-
> Only real cross-cutting decisions belong here — not implementation notes, not feature status, not
|
|
16
|
-
> how-to guidance. Entries that grow past decision + reason are carrying mechanism that belongs in
|
|
17
|
-
> `03`/`04`; link it instead of inlining it.
|
|
16
|
+
## ADR-001 — Adopt this doc structure
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
**Status:** Accepted · **Date:** {{init-date}}
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
**Decision.** Adopt the Spur doc structure (`00`–`05` + `99` constitution).
|
|
21
|
+
|
|
22
|
+
**Why.** Separates WHY (`00`) from WHAT (`01`) from HOW (`03`/`04`) — one fact, one home.
|
|
23
|
+
|
|
24
|
+
**Detail:** `docs/99_PROJECT_CONSTITUTION.md` §4.1.
|
|
26
25
|
|
|
27
26
|
<!--
|
|
28
|
-
Add new ADRs here.
|
|
27
|
+
Add new ADRs here. Entry shape (per 99 §6.1):
|
|
28
|
+
|
|
29
|
+
## ADR-NNN: <Decision title, outcome-shaped>
|
|
30
|
+
|
|
31
|
+
**Status:** Accepted | Accepted (design) | Superseded by ADR-MMM | Skipped · **Date:** YYYY-MM-DD
|
|
32
|
+
|
|
33
|
+
**Decision.** <What was decided — the smallest complete statement of the choice.>
|
|
34
|
+
|
|
35
|
+
**Why.** <One line. The single strongest reason.>
|
|
36
|
+
|
|
37
|
+
**Detail:** <pointer into 03/04/plans — depth never lives here.>
|
|
38
|
+
|
|
29
39
|
A decision that reverses a prior ADR adds a new entry that says "supersedes ADR-NNN".
|
|
30
40
|
An Amendment records the decision delta + one-line reason — not the mechanism. Implementation
|
|
31
41
|
paths, detailed semantics, and multi-paragraph rationale belong in 03/04, not in the amendment.
|
|
32
|
-
-->
|
|
42
|
+
-->
|
|
@@ -4,14 +4,15 @@ doc: 01_PRD
|
|
|
4
4
|
owns: WHAT — product vision, users, scope (in / out / deferred)
|
|
5
5
|
authority: authoritative-on-scope
|
|
6
6
|
version: 1.0.0
|
|
7
|
-
|
|
8
|
-
updated_at:
|
|
7
|
+
owner: _(project owner)_
|
|
8
|
+
updated_at: {{init-date}}
|
|
9
|
+
read_before: adding a command or feature
|
|
10
|
+
edit_rules: 99 §6.2
|
|
11
|
+
sync: [T1, T4, T6]
|
|
9
12
|
---
|
|
10
13
|
|
|
11
14
|
# Product Requirements Document
|
|
12
15
|
|
|
13
|
-
> Authoritative on **scope**. In-scope vs out-of-scope vs deferred lives here and nowhere else.
|
|
14
|
-
|
|
15
16
|
## 1. Vision
|
|
16
17
|
|
|
17
18
|
_(One sentence: what this product is and who it is for.)_
|
|
@@ -28,10 +29,14 @@ _(One sentence: what this product is and who it is for.)_
|
|
|
28
29
|
|
|
29
30
|
- _(capability the product delivers)_
|
|
30
31
|
|
|
31
|
-
###
|
|
32
|
+
### Supporting
|
|
32
33
|
|
|
33
|
-
- _(
|
|
34
|
+
- _(capability that enables an in-scope item but is not user-facing)_
|
|
34
35
|
|
|
35
36
|
### Deferred
|
|
36
37
|
|
|
37
|
-
- _(capability intentionally postponed — not a commitment)_
|
|
38
|
+
- _(capability intentionally postponed — not a commitment; carry the condition that would reactivate it)_
|
|
39
|
+
|
|
40
|
+
### Out of scope
|
|
41
|
+
|
|
42
|
+
- _(explicitly excluded capability — prevents scope creep)_
|
|
@@ -4,19 +4,27 @@ doc: 02_ROADMAP
|
|
|
4
4
|
owns: WHEN — phases, current vs deferred, sequencing
|
|
5
5
|
authority: derived
|
|
6
6
|
version: 1.0.0
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
derived_from: [00_ADR, 01_PRD]
|
|
8
|
+
owner: _(project owner)_
|
|
9
|
+
updated_at: {{init-date}}
|
|
10
|
+
read_before: placing work in a phase
|
|
11
|
+
edit_rules: 99 §6.3
|
|
12
|
+
sync: [T5]
|
|
9
13
|
---
|
|
10
14
|
|
|
11
15
|
# Roadmap
|
|
12
16
|
|
|
13
|
-
> Derived from `01_PRD` (scope) and `00_ADR` (decisions). Edit when phase status changes.
|
|
14
|
-
|
|
15
17
|
## Phases
|
|
16
18
|
|
|
17
|
-
| Phase | Status | Items |
|
|
18
|
-
|
|
19
|
-
| Phase 0 — Foundation | ✅ done | _(list the real, shipped deliverable names)_ |
|
|
20
|
-
| Phase 1 — _(name)_ | ⏳ planned | _(items)_ |
|
|
19
|
+
| Phase | Status | Items | Exit criterion |
|
|
20
|
+
|-------|--------|-------|----------------|
|
|
21
|
+
| Phase 0 — Foundation | ✅ done | _(list the real, shipped deliverable names)_ | _(what must be true to exit)_ |
|
|
22
|
+
| Phase 1 — _(name)_ | ⏳ planned | _(items)_ | _(what must be true to exit)_ |
|
|
21
23
|
|
|
22
24
|
**Status legend:** ✅ done · 🔶 partial · ⏳ planned · 💤 deferred
|
|
25
|
+
|
|
26
|
+
<!--
|
|
27
|
+
Markers: [x] done · [~] partial · [ ] pending. [x]/[~] carry a one-line evidence note.
|
|
28
|
+
When a deliverable lands under a different name than planned, rewrite the bullet to the real name.
|
|
29
|
+
Insert sub-phases (1.5) rather than renumbering existing ones.
|
|
30
|
+
-->
|
|
@@ -4,14 +4,16 @@ doc: 03_ARCHITECTURE
|
|
|
4
4
|
owns: HOW — module boundaries, data flow, runtime model, invariants
|
|
5
5
|
authority: derived
|
|
6
6
|
version: 1.0.0
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
derived_from: [00_ADR, 01_PRD]
|
|
8
|
+
owner: _(project owner)_
|
|
9
|
+
updated_at: {{init-date}}
|
|
10
|
+
read_before: cross-module, seam, or schema work
|
|
11
|
+
edit_rules: 99 §6.4
|
|
12
|
+
sync: [T1]
|
|
9
13
|
---
|
|
10
14
|
|
|
11
15
|
# Architecture
|
|
12
16
|
|
|
13
|
-
> Derived (ADR wins). Read before cross-module/seam/schema work; edit when boundaries or mechanisms change.
|
|
14
|
-
|
|
15
17
|
## 1. Module map
|
|
16
18
|
|
|
17
19
|
```
|
|
@@ -24,4 +26,9 @@ _(Describe the primary data path: input → processing → output. Name the seam
|
|
|
24
26
|
|
|
25
27
|
## 3. Invariants
|
|
26
28
|
|
|
27
|
-
- _(state or property that always holds —
|
|
29
|
+
- _(state or property that always holds — phrased so a constraint rule or reviewer can check it mechanically)_
|
|
30
|
+
|
|
31
|
+
<!--
|
|
32
|
+
Describes the CURRENT architecture only. Future/accepted designs go in sections explicitly
|
|
33
|
+
titled "(accepted design — ADR-NNN; not yet built)". On conflict with 00, the ADR wins; fix here.
|
|
34
|
+
-->
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: Design
|
|
3
3
|
doc: 04_DESIGN
|
|
4
|
-
owns: SURFACE — concrete shapes: every CLI command, flag, config key, env var, table, DTO
|
|
4
|
+
owns: SURFACE — concrete shapes: every CLI command, flag, config key, env var, table, DTO; index over docs/design/
|
|
5
5
|
authority: derived
|
|
6
6
|
version: 1.0.0
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
derived_from: [00_ADR, 01_PRD]
|
|
8
|
+
owner: _(project owner)_
|
|
9
|
+
updated_at: {{init-date}}
|
|
10
|
+
read_before: changing a command, flag, env var, or schema
|
|
11
|
+
edit_rules: 99 §6.5
|
|
12
|
+
sync: [T3, T9]
|
|
9
13
|
---
|
|
10
14
|
|
|
11
15
|
# Design
|
|
12
16
|
|
|
13
|
-
>
|
|
14
|
-
>
|
|
17
|
+
> **Index page** over `docs/design/` satellites (99 §4.5). Each surface area gets a
|
|
18
|
+
> `docs/design/<slug>.md` satellite; this index carries the surface map + pointers.
|
|
19
|
+
> Edit order: satellite first, then index row — same change (T9).
|
|
20
|
+
|
|
21
|
+
## UI/UX boundary
|
|
22
|
+
|
|
23
|
+
Repository-root `DESIGN.md` owns all UI/UX design, including visual language, design tokens,
|
|
24
|
+
components, layout, interaction, accessibility, and responsive behavior. Read and update it for UI
|
|
25
|
+
work; keep this document focused on non-UI surface design. If `DESIGN.md` is absent, follow the
|
|
26
|
+
project's established UI conventions rather than adding UI guidance here.
|
|
15
27
|
|
|
16
28
|
## 1. CLI commands
|
|
17
29
|
|
|
@@ -19,12 +31,18 @@ updated_at: 1970-01-01T00:00:00.000Z
|
|
|
19
31
|
_(command) <positional> [--flag <value>] [--json]
|
|
20
32
|
```
|
|
21
33
|
|
|
22
|
-
| Command | Description |
|
|
23
|
-
|
|
24
|
-
| _(command)_ | _(one-line description)_ |
|
|
34
|
+
| Command | Description | Design doc |
|
|
35
|
+
|---------|-------------|------------|
|
|
36
|
+
| _(command)_ | _(one-line description)_ | `docs/design/_(slug)_.md` |
|
|
25
37
|
|
|
26
38
|
## 2. Configuration keys
|
|
27
39
|
|
|
28
40
|
| Key | Type | Default | Description |
|
|
29
41
|
|-----|------|---------|-------------|
|
|
30
42
|
| _(key)_ | _(type)_ | _(default)_ | _(description)_ |
|
|
43
|
+
|
|
44
|
+
<!--
|
|
45
|
+
Shapes only — rationale lives in 00/03. Behavioral notes are shapes ("resolving zero rules exits 1"
|
|
46
|
+
— keep); justifications are not ("...because a silent gate is the worst failure mode" — cut).
|
|
47
|
+
Transcribe command signatures from the code registrations, never from memory.
|
|
48
|
+
-->
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: Features
|
|
3
3
|
doc: 05_FEATURES
|
|
4
|
-
owns: STATUS — feature decomposition + state
|
|
4
|
+
owns: STATUS — feature decomposition + state; index over docs/features/
|
|
5
5
|
authority: derived
|
|
6
6
|
version: 1.0.0
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
derived_from: [00_ADR, 01_PRD]
|
|
8
|
+
owner: _(project owner)_
|
|
9
|
+
updated_at: {{init-date}}
|
|
10
|
+
read_before: finding a feature's state
|
|
11
|
+
edit_rules: 99 §6.6
|
|
12
|
+
sync: [T4, T9]
|
|
9
13
|
---
|
|
10
14
|
|
|
11
15
|
# Features
|
|
12
16
|
|
|
13
|
-
>
|
|
17
|
+
> **Index page** over `docs/features/` satellites (99 §4.5). Both the satellites and this index's
|
|
18
|
+
> generated region are **tool-owned** (e.g. `spur feature`/`ftree`); edit through the tool, never
|
|
19
|
+
> with raw file writes. Edit order: satellite first, then refresh index (T9).
|
|
14
20
|
|
|
15
21
|
## Status legend
|
|
16
22
|
|
|
@@ -18,6 +24,11 @@ updated_at: 1970-01-01T00:00:00.000Z
|
|
|
18
24
|
|
|
19
25
|
## Feature tree
|
|
20
26
|
|
|
21
|
-
| ID | Feature | Status | Parent | Notes |
|
|
22
|
-
|
|
23
|
-
| F1 | _(root feature)_ | ⏳ planned | — | _(one-line scope)_ |
|
|
27
|
+
| ID | Feature | Status | Parent | Notes | Satellite |
|
|
28
|
+
|----|---------|--------|--------|-------|-----------|
|
|
29
|
+
| F1 | _(root feature)_ | ⏳ planned | — | _(one-line scope)_ | `docs/features/F1__(slug)_.md` |
|
|
30
|
+
|
|
31
|
+
<!--
|
|
32
|
+
Never trust a row you have not verified — check status against code before citing or building on it.
|
|
33
|
+
One item per satellite; <feature-id> is the stable grep anchor. Renaming is a tool operation.
|
|
34
|
+
-->
|
|
@@ -3,63 +3,457 @@ name: Project Constitution
|
|
|
3
3
|
doc: 99_PROJECT_CONSTITUTION
|
|
4
4
|
owns: PROCESS — how the key files are maintained
|
|
5
5
|
authority: authoritative-on-process
|
|
6
|
-
version: 1.
|
|
7
|
-
created_at:
|
|
8
|
-
updated_at:
|
|
6
|
+
version: 1.3.0
|
|
7
|
+
created_at: {{init-date}}
|
|
8
|
+
updated_at: {{init-date}}
|
|
9
|
+
edit_rules: 99 §6.8
|
|
10
|
+
sync: [T7]
|
|
11
|
+
read_before: editing any numbered doc above
|
|
9
12
|
---
|
|
10
13
|
|
|
11
14
|
# Project Constitution — How to Organize the Project
|
|
12
15
|
|
|
13
|
-
> **This is a template.** Spur's own constitution at
|
|
14
|
-
> [docs/99_PROJECT_CONSTITUTION.md](https://github.com/gobing-ai/spur/blob/main/docs/99_PROJECT_CONSTITUTION.md)
|
|
15
|
-
> is the canonical version. Copy the full content there into this file for a real project, then
|
|
16
|
-
> localize the Lessons section (§8) and the tool-binding column (§3).
|
|
17
|
-
|
|
18
16
|
## 1. What this is & what this is not
|
|
19
17
|
|
|
20
18
|
This is the **constitution** for the project's key files: an accumulated, machine-maintained set
|
|
21
19
|
of rules and lessons for running the same file structure across different projects and
|
|
22
|
-
cooperating with multiple coding agents (Claude Code, Codex, Gemini CLI, pi, Antigravity,
|
|
23
|
-
OpenCode, OpenClaw, ...).
|
|
20
|
+
cooperating with multiple coding agents (Claude Code, Codex, Gemini CLI, pi, omp, Antigravity,
|
|
21
|
+
OpenCode, OpenClaw, Hermes, Grok, ...).
|
|
24
22
|
|
|
25
23
|
- One copy lives in every project at `docs/99_PROJECT_CONSTITUTION.md`.
|
|
26
24
|
- It is **byte-identical across projects** except the Lessons sections (§8) and the tool-binding
|
|
27
25
|
column (§3). When it improves in one project, propagate to the others — forks are drift.
|
|
28
26
|
- It contains **zero project-specific facts** — no project command names, package names, feature
|
|
29
|
-
states, or decisions. Project facts live in the numbered docs this file governs.
|
|
27
|
+
states, or decisions. Project facts live in the numbered docs this file governs. If you find a
|
|
28
|
+
project fact here, that itself is drift: move it to its owning doc.
|
|
29
|
+
|
|
30
|
+
This is **not** a project review summary, a technical review list, or a product-design
|
|
31
|
+
reflection.
|
|
32
|
+
|
|
33
|
+
Audience: humans and coding agents equally. Every rule below is written to be checkable — an
|
|
34
|
+
agent should be able to verify compliance mechanically, not interpret intent.
|
|
30
35
|
|
|
31
36
|
## 2. Authority model
|
|
32
37
|
|
|
38
|
+
Two axes that cannot collide:
|
|
39
|
+
|
|
33
40
|
| Axis | Question | Winner |
|
|
34
41
|
|------|----------|--------|
|
|
35
42
|
| **Content** | What is true about the project? | Lower number wins: `00_ADR` is binding on *decisions*; `01_PRD` is authoritative on *scope*; `02`–`05` are derived |
|
|
36
43
|
| **Process** | How are the key files maintained? | **This file** |
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
They cannot conflict because this file holds no project content (§1 rule 3).
|
|
46
|
+
|
|
47
|
+
**Why this file is numbered 99, not 00:** "lower number wins" is a *content* rule, and this file
|
|
48
|
+
plays on the other axis. The out-of-band number is the visible signal that the constitution sits
|
|
49
|
+
outside the content chain — renumbering it into the chain (e.g. as `00`) would re-entangle the
|
|
50
|
+
two axes and force a renumber of every content doc, invalidating the dense web of cross-pointers
|
|
51
|
+
(`03 §12`-style references baked into append-only ADR text) for a purely aesthetic gain. Do not
|
|
52
|
+
renumber.
|
|
53
|
+
|
|
54
|
+
**Content conflict rule:** when two docs disagree, fix the **authoritative** doc first (with a
|
|
55
|
+
dated amendment if it is append-only), then the derived doc, then `AGENTS.md` — and flag the
|
|
56
|
+
drift in the commit message or task. Never average two conflicting statements into a third.
|
|
57
|
+
|
|
58
|
+
## 3. Shared tools
|
|
59
|
+
|
|
60
|
+
Tools are bound by **role**; roles are permanent, bindings evolve. This table is the only
|
|
61
|
+
project-variable section besides Lessons — update the binding when the toolchain migrates.
|
|
62
|
+
|
|
63
|
+
| Role | Current binding | Notes |
|
|
64
|
+
|------|-----------------|-------|
|
|
65
|
+
| Spec lifecycle — tasks | _(project tool — e.g. `spur task` or a task CLI)_ | Task files are tool-owned; edit through the tool, never the Write tool |
|
|
66
|
+
| Spec lifecycle — features | _(project tool — e.g. `spur feature` or a feature CLI)_ | Same tool-owned rule |
|
|
67
|
+
| Delivery harness | _(project harness — e.g. `spur`)_ | Quality gates are self-hosted through it where possible |
|
|
68
|
+
| Agent-facing wrappers | per-project plugin dir (e.g. `plugins/sp/`) | **Fat Skills, thin others:** skills are the SSOT for agent-facing behavior and may be arbitrarily rich; slash commands and subagents are thin wrappers of skills (every agent supports skills; command/subagent support varies) |
|
|
69
|
+
|
|
70
|
+
## 4. Common file layout
|
|
71
|
+
|
|
72
|
+
### 4.1 The doc map (canonical template)
|
|
73
|
+
|
|
74
|
+
Each project's `AGENTS.md` embeds an instantiated copy of this table (§4.4). A fact lives in
|
|
75
|
+
**one** doc; other docs link to it, never restate it.
|
|
76
|
+
|
|
77
|
+
| Doc | Owns the question | Authority | Read / edit when |
|
|
78
|
+
|-----|-------------------|-----------|------------------|
|
|
79
|
+
| `docs/00_ADR.md` | **WHY** — which cross-cutting decision was made, and the one-line reason | **Authoritative** (wins all content) | Read before any structural change; add a dated entry before diverging from a decision |
|
|
80
|
+
| `docs/01_PRD.md` | **WHAT** — product vision, users, scope (in / out / deferred) | **Authoritative on scope** | Read before adding a command/feature; edit when scope changes |
|
|
81
|
+
| `docs/02_ROADMAP.md` | **WHEN** — phases, current vs deferred, sequencing | Derived | Read to place work in a phase; edit when phase status changes |
|
|
82
|
+
| `docs/03_ARCHITECTURE.md` | **HOW** — module boundaries, data flow, runtime model, invariants, rationale-in-depth | Derived (ADR wins) | Read before cross-module/seam/schema work; edit when boundaries or mechanisms change |
|
|
83
|
+
| `docs/04_DESIGN.md` | **SURFACE** — concrete shapes: every CLI command, flag, config key, env var, table, DTO; **index over `docs/design/<slug>.md`** (§4.5) | Derived | Read/edit when changing a command, flag, env var, or schema — same commit |
|
|
84
|
+
| `docs/05_FEATURES.md` | **STATUS** — feature decomposition + state (✅ done / 🔶 partial / ⏳ planned / 💤 deferred); **index over `docs/features/<id>_<slug>.md`** (§4.5) | Derived | Read to find a feature's state; edit when a feature's status changes |
|
|
85
|
+
| `docs/99_PROJECT_CONSTITUTION.md` | **PROCESS** — how the files above are maintained | **Authoritative on process** | Read before editing any doc above; edit per §6.8 |
|
|
86
|
+
| `AGENTS.md` (repo root) | **ENTRY** — how agents work in this repo: stack, commands, gates, conventions + the instantiated doc map | Derived (from 99 + 00/01/04) | Read first every session; regenerate factual blocks from code (§6.7) |
|
|
87
|
+
|
|
88
|
+
**Routing — put each fact in its owning doc, link from the rest:**
|
|
89
|
+
|
|
90
|
+
- Decision + one-line reason → `00`. Rationale/mechanism in depth → `03`.
|
|
91
|
+
- Scope (in/out/deferred) → `01`. Mechanism / data flow / invariants → `03`.
|
|
92
|
+
- Command/flag/config/schema/DTO shapes → `04`. Phase timing → `02`. Feature status → `05`.
|
|
93
|
+
- If you are writing *how it's built* or *why* inside `00`/`01`/`02`, it belongs in `03`/`04`.
|
|
94
|
+
|
|
95
|
+
### 4.2 Working layers (outside the authority chain)
|
|
96
|
+
|
|
97
|
+
| Location | Purpose | Rules |
|
|
98
|
+
|----------|---------|-------|
|
|
99
|
+
| `docs/plans/YYYY-MM-DD-<topic>.md` | Dated working documents: research, triage, design discussions, decision records-in-progress | They **record**, they do not **govern**. Once concluded, immutable except dated correction sections. Decisions they reach must be promoted into `00`–`05` to take effect |
|
|
100
|
+
| `docs/tasks/` | Task files | Tool-owned (§3). Never edited with raw file writes |
|
|
101
|
+
| other `docs/` folders | Optional scratch (analysis, refactor notes, ...) | Nothing in the authority chain may depend on them |
|
|
102
|
+
|
|
103
|
+
`docs/design/` and `docs/features/` are **not** scratch — they are the satellite layers of `04` and
|
|
104
|
+
`05` and are governed by §4.5.
|
|
105
|
+
|
|
106
|
+
### 4.3 Standard frontmatter (the doc's machine-readable contract)
|
|
107
|
+
|
|
108
|
+
Every numbered doc (`00`–`05`, and `99` itself) opens with YAML frontmatter carrying its doc-map
|
|
109
|
+
row plus bookkeeping — so an agent learns the doc's contract from the file head without loading
|
|
110
|
+
the doc map, and tooling can validate it:
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
---
|
|
114
|
+
doc: 03_ARCHITECTURE
|
|
115
|
+
owns: HOW — module boundaries, data flow, runtime model, invariants
|
|
116
|
+
authority: derived # authoritative | authoritative-on-scope | authoritative-on-process | derived
|
|
117
|
+
version: 1.1.0
|
|
118
|
+
derived_from: [00_ADR, 01_PRD] # omit for 00
|
|
119
|
+
owner: <name>
|
|
120
|
+
updated_at: YYYY-MM-DD
|
|
121
|
+
read_before: cross-module, seam, or schema work
|
|
122
|
+
edit_rules: 99 §6.4
|
|
123
|
+
sync: [T1] # §5 trigger IDs that obligate touching this doc
|
|
124
|
+
---
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Rules:
|
|
128
|
+
|
|
129
|
+
1. The frontmatter **is** the instantiated copy of this file's §4.1 row — `owns`/`authority`
|
|
130
|
+
must match it verbatim in meaning; the §7 audit checks this. On mismatch, §4.1 wins.
|
|
131
|
+
2. `edit_rules` points to the owning §6 subsection — rules are never restated in frontmatter
|
|
132
|
+
(pointers over prose, §6.0).
|
|
133
|
+
3. Bump `version` (minor) on any substantive edit; always refresh `updated_at` in the same edit.
|
|
134
|
+
A doc whose `updated_at` predates a change it should reflect is drift — repair per §7.
|
|
135
|
+
4. Frontmatter replaces the legacy bold header block (`**Version:** …` lines); a doc carrying
|
|
136
|
+
both is drift.
|
|
137
|
+
5. Doc **bodies do not restate** their own authority or the conflict rule ("when this conflicts
|
|
138
|
+
with the ADR, the ADR wins") — frontmatter `authority` and §2 own that. Preamble
|
|
139
|
+
restatements are drift.
|
|
140
|
+
|
|
141
|
+
### 4.4 AGENTS.md synchronization
|
|
142
|
+
|
|
143
|
+
- `AGENTS.md` is the **per-project instantiation**: the §4.1 table (instantiated), plus
|
|
144
|
+
project-specific stack, commands, verification gates, and conventions.
|
|
145
|
+
- This file is the canonical template; when §4.1 or §5 changes here, re-sync `AGENTS.md` in the
|
|
146
|
+
same change.
|
|
147
|
+
- `AGENTS.md` may **add** project facts; it may never **contradict** the numbered docs. On
|
|
148
|
+
contradiction, the numbered doc wins — fix `AGENTS.md`.
|
|
149
|
+
|
|
150
|
+
### 4.5 Index + satellite docs (`04`/`05` and their folders)
|
|
151
|
+
|
|
152
|
+
Two derived docs are **index pages** over a folder of per-item **satellite** files. The index holds
|
|
153
|
+
the headline rows + pointers; each satellite holds one item's detail. This keeps the index readable
|
|
154
|
+
(loaded every session) while detail scales without bloating it.
|
|
155
|
+
|
|
156
|
+
| Index doc | Satellite folder | Satellite file name | Satellite ownership |
|
|
157
|
+
|-----------|------------------|---------------------|---------------------|
|
|
158
|
+
| `docs/04_DESIGN.md` | `docs/design/` | `docs/design/<slug>.md` | Hand-maintained derived doc (§6.5) |
|
|
159
|
+
| `docs/05_FEATURES.md` | `docs/features/` | `docs/features/<feature-id>_<slug>.md` | **Tool-owned** (§3 — `spur feature`/`ftree`); satellites *and* the index region are written by the tool, never by raw file writes |
|
|
160
|
+
|
|
161
|
+
Rules (both axes):
|
|
162
|
+
|
|
163
|
+
1. **The index is the single entry point.** A reader starts at `04`/`05`; every satellite is
|
|
164
|
+
reachable from exactly one index row. A satellite with no index row, or an index row with no
|
|
165
|
+
satellite, is drift (§7 audit).
|
|
166
|
+
2. **One item per satellite.** `<slug>` (design) / `<feature-id>_<slug>` (features) is the grep
|
|
167
|
+
anchor (§6.0 rule 6) — stable once chosen; renaming is a rename of the file *and* its index row in
|
|
168
|
+
the same change.
|
|
169
|
+
3. **Detail lives only in the satellite; the index carries pointer + status only.** The index never
|
|
170
|
+
restates a satellite's body (§6.0 rule 2). For `05`, a row is `<id> <status> <name> → pointer`;
|
|
171
|
+
for `04`, an index row names the surface area and points at its `docs/design/<slug>.md`.
|
|
172
|
+
4. **The index is regenerable for `05`** (tool-written) and **hand-curated for `04`** — but in both
|
|
173
|
+
cases the satellite is the source of truth and the index is derived from it. Never edit `05`'s
|
|
174
|
+
generated index region by hand; never let a `04` index row diverge from its satellite.
|
|
175
|
+
5. **Edit order is fixed (§5 T9): detail first, then index.** Write/update the satellite, then update
|
|
176
|
+
the index row — in the **same change**. Updating the index before the detail exists creates a
|
|
177
|
+
pointer to nothing; the reverse leaves the detail unindexed. For tool-owned features, "update the
|
|
178
|
+
index" is running the tool's refresh (e.g. `spur feature refresh`), not a manual edit.
|
|
179
|
+
|
|
180
|
+
## 5. Sync triggers — same-commit obligations
|
|
181
|
+
|
|
182
|
+
The root cause of stale key files is *unsynchronized success*: code ships, docs don't hear about
|
|
183
|
+
it. Each trigger below has a stable ID (referenced by doc frontmatter `sync:` lists, §4.3) and
|
|
184
|
+
names the docs that must be touched **in the same commit / same change**:
|
|
185
|
+
|
|
186
|
+
| ID | When this happens | Touch (same change) |
|
|
187
|
+
|----|-------------------|---------------------|
|
|
188
|
+
| T1 | New cross-cutting decision, or reversal of one | `00` **first** (dated entry), then `03` mechanism, `01` if scope shifts |
|
|
189
|
+
| T2 | A code change would contradict an existing ADR | **Stop.** Add the superseding/amending ADR entry first — never silently diverge |
|
|
190
|
+
| T3 | Command, flag, config key, env var, schema, or DTO added/changed | `04` + the `AGENTS.md` surface block |
|
|
191
|
+
| T4 | A feature ships or changes state | its `05` row; a new `01` scope row if it is new surface |
|
|
192
|
+
| T5 | A phase completes, reorders, or gains items | `02` (update the bullet to the *real, shipped name* of the deliverable) |
|
|
193
|
+
| T6 | Scope added / cut / deferred | `01`; placement in `02` |
|
|
194
|
+
| T7 | The doc map or process changes | this file → re-sync `AGENTS.md` (§4.4) → propagate to sibling projects |
|
|
195
|
+
| T8 | A multi-wave batch is planned | schedule "doc sync" as an **explicit work item** — same-commit discipline does not survive on memory alone |
|
|
196
|
+
| T9 | A design or feature item is added/changed | the satellite **first** (`docs/design/<slug>.md` or `docs/features/<id>_<slug>.md`), **then** its index row in `04`/`05` — same change (§4.5 rule 5) |
|
|
197
|
+
|
|
198
|
+
## 6. Edit principles per file
|
|
199
|
+
|
|
200
|
+
### 6.0 Writing rules (all key files)
|
|
201
|
+
|
|
202
|
+
Token economy is a design goal: these files are read by LLM agents at session start, every
|
|
203
|
+
session, across every project — a redundant sentence is paid for thousands of times. Precise
|
|
204
|
+
**and** concise; precision wins when they conflict.
|
|
205
|
+
|
|
206
|
+
1. Declarative, information-dense sentences. No filler, no marketing adjectives, no hedging, no
|
|
207
|
+
narrative buildup.
|
|
208
|
+
2. A fact lives once — link or point (`see 03 §12`) instead of restating, both in-file and
|
|
209
|
+
cross-file. Restatement is the largest token sink in a doc system, bigger than any tone rule.
|
|
210
|
+
3. Tables for enumerable facts; prose only where reasoning is needed.
|
|
211
|
+
4. Front-load: rule first, elaboration after — readers (human or agent) may only take the head.
|
|
212
|
+
5. Define a term once, then reuse it verbatim. Synonyms read as new concepts to a machine.
|
|
213
|
+
6. Headings and IDs (`ADR-NNN`, `T1`–`T8`, `§6.x`, feature rows) are grep targets and
|
|
214
|
+
cross-reference anchors — never rename casually.
|
|
215
|
+
7. **Concise never beats correct.** If brevity creates ambiguity, add the missing words: tokens
|
|
216
|
+
saved in reading are lost many times over in a misexecuted run.
|
|
217
|
+
|
|
218
|
+
These rules are stated once, here. Per-file sections below and doc frontmatter inherit them via
|
|
219
|
+
pointer — restating them per file would violate rule 2.
|
|
220
|
+
|
|
221
|
+
### 6.1 `docs/00_ADR.md`
|
|
222
|
+
|
|
223
|
+
Entry template:
|
|
224
|
+
|
|
225
|
+
```markdown
|
|
226
|
+
## ADR-NNN: <Decision title, outcome-shaped>
|
|
227
|
+
|
|
228
|
+
**Status:** Accepted | Accepted (design) | Superseded by ADR-MMM | Skipped · **Date:** YYYY-MM-DD
|
|
229
|
+
|
|
230
|
+
**Decision.** <What was decided — the smallest complete statement of the choice.>
|
|
231
|
+
|
|
232
|
+
**Why.** <One line. The single strongest reason.>
|
|
233
|
+
|
|
234
|
+
**Detail:** <pointer into 03/04/plans — depth never lives here.>
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
1. **One decision per entry.** If a draft contains a principle *and* a deferred design *and* a
|
|
238
|
+
mechanism choice *and* implementation tips — split it: decision(s) here, mechanism in `03`,
|
|
239
|
+
shapes in `04`, tips nowhere (they are implementation guidance, not decisions).
|
|
240
|
+
2. **ADR = decision + one-line reason.** No Zod patterns, no lock details, no code idioms.
|
|
241
|
+
3. **Append-only.** Never renumber, never delete, never rewrite history. Corrections are dated
|
|
242
|
+
`**Amendment (YYYY-MM-DD)**` blocks inside the entry; reversals are **new entries** that name
|
|
243
|
+
what they supersede, while the old entry's Status becomes `Superseded by ADR-MMM`.
|
|
244
|
+
4. **Numbering:** next free integer, one sequence per repo. A burned/skipped number gets a stub
|
|
245
|
+
entry (`Status: Skipped`) so the gap is audit-clean and never reused.
|
|
246
|
+
5. **`Accepted (design)`** means decided but not built — readers must be able to tell decided
|
|
247
|
+
from shipped.
|
|
248
|
+
6. **Before any code that contradicts an ADR:** the superseding entry lands first (§5 row 2).
|
|
249
|
+
7. **Retrofit rule:** the entry template binds **new entries and amendments only**. Historical
|
|
250
|
+
entries are never restructured to match it — append-only beats stylistic consistency. The
|
|
251
|
+
non-entry preamble is normal editable text.
|
|
252
|
+
8. **Amendments record the decision delta.** An `**Amendment**` block records *what changed about the
|
|
253
|
+
decision* — the new choice and its one-line reason — plus a `Detail:` pointer for mechanism.
|
|
254
|
+
Implementation file paths, detailed semantics, and multi-paragraph rationale belong in `03`/`04`,
|
|
255
|
+
not in the amendment body. If an amendment would carry more than a few lines of non-decision text,
|
|
256
|
+
the mechanism has leaked in; link it instead of inlining it.
|
|
257
|
+
|
|
258
|
+
### 6.2 `docs/01_PRD.md`
|
|
259
|
+
|
|
260
|
+
1. Owns vision, users, principles, scope. **No mechanism** (→ `03`), **no timing** (→ `02`),
|
|
261
|
+
**no shapes** (→ `04`).
|
|
262
|
+
2. **Every shipped surface has a scope row.** When a command/capability ships, its row enters
|
|
263
|
+
the in-scope table in the same change — shipped-but-unlisted is the most common drift.
|
|
264
|
+
3. Scope states are explicit: *in (committed)* / *supporting* / *deferred (needs design
|
|
265
|
+
reconfirmation)* / *out of scope*. A deferred item carries the condition that would
|
|
266
|
+
reactivate it.
|
|
267
|
+
4. Surface beyond the committed set is **not ported/built speculatively** — re-confirm the need
|
|
268
|
+
first and record the evidence pointer (a dated plans doc, usage data) in the entry that
|
|
269
|
+
admits it.
|
|
270
|
+
5. **Scope tables carry membership only** — no delivery-status columns (`05` owns status; a
|
|
271
|
+
status column in `01` is a guaranteed drift magnet). Likewise, quantitative gate values
|
|
272
|
+
(coverage thresholds, etc.) live with their enforcement config — point to the gate, never
|
|
273
|
+
restate the numbers.
|
|
274
|
+
|
|
275
|
+
### 6.3 `docs/02_ROADMAP.md`
|
|
276
|
+
|
|
277
|
+
1. Derived: it may **sequence** facts from `00`/`01`/`05` but never introduce new ones.
|
|
278
|
+
2. Every phase has a goal sentence, checkbox items, and an explicit **Exit:** criterion.
|
|
279
|
+
3. Markers: `[x]` done · `[~]` partial · `[ ]` pending. `[x]`/`[~]` carry a one-line evidence
|
|
280
|
+
note (what shipped, where).
|
|
281
|
+
4. When a deliverable lands under a different name than planned, rewrite the bullet to the real
|
|
282
|
+
name — a roadmap that tracks dead names reads as undelivered work.
|
|
283
|
+
5. Phases gate on the previous one. Insert sub-phases (`1.5`) rather than renumbering existing
|
|
284
|
+
ones.
|
|
285
|
+
|
|
286
|
+
### 6.4 `docs/03_ARCHITECTURE.md`
|
|
287
|
+
|
|
288
|
+
1. Describes the **current** architecture. Future/accepted designs are allowed only in sections
|
|
289
|
+
explicitly titled `(accepted design — ADR-NNN; not yet built)`.
|
|
290
|
+
2. Owns module boundaries, data flow, runtime model, invariants, and rationale-in-depth. Not
|
|
291
|
+
schemas/signatures (code and `04`), not decisions (`00`).
|
|
292
|
+
3. Write invariants as **enforceable statements** — phrased so a constraint rule or a reviewer
|
|
293
|
+
can check them mechanically.
|
|
294
|
+
4. When a migration replaces a mechanism (parser, dispatcher, bootstrap), update the module
|
|
295
|
+
descriptions in the same change — stale module lists survive multiple releases unnoticed.
|
|
296
|
+
5. On conflict with `00`: the ADR wins; fix here and flag.
|
|
297
|
+
|
|
298
|
+
### 6.5 `docs/04_DESIGN.md` + `docs/design/<slug>.md`
|
|
299
|
+
|
|
300
|
+
`04` is the **index page** over the `docs/design/` satellites (§4.5). The index carries the surface
|
|
301
|
+
map + pointers; each `docs/design/<slug>.md` holds one surface area's detailed design.
|
|
302
|
+
|
|
303
|
+
1. **Same-commit rule:** any change to a command, flag, config key, env var, table, or DTO
|
|
304
|
+
updates `04` (and its satellite) in that commit (§5 T3/T9). In batch planning, doc sync is an
|
|
305
|
+
explicit scheduled item.
|
|
306
|
+
2. **Detail-first edit order (§4.5 rule 5 / T9):** write or update the `docs/design/<slug>.md`
|
|
307
|
+
satellite first, then update its `04` index row — never the reverse. A new surface area gets a
|
|
308
|
+
new satellite + a new index row in the same change.
|
|
309
|
+
3. Prefer **generated** artifacts over hand-maintained ones (e.g. OpenAPI from the contract);
|
|
310
|
+
never hand-write what can be derived — and never let a derivable artifact be edited by hand.
|
|
311
|
+
4. Shapes only. Rationale lives in `00`/`03`. **Behavioral notes are shapes** ("resolving zero
|
|
312
|
+
rules exits 1" — keep); justifications are not ("...because a silent gate is the worst
|
|
313
|
+
failure mode" — cut, or point to `00`/`03`). This applies to satellites too — they hold
|
|
314
|
+
*detailed shapes*, not rationale.
|
|
315
|
+
5. Command signatures are **transcribed from the code registrations**, never from memory or from
|
|
316
|
+
an older doc revision — a signature is a factual block in the §6.7 sense.
|
|
317
|
+
6. The index never restates a satellite's body (§6.0 rule 2): an `04` row names the surface area,
|
|
318
|
+
its status, and points at `docs/design/<slug>.md`. `<slug>` is a stable grep anchor (§6.0 rule 6).
|
|
319
|
+
|
|
320
|
+
### 6.6 `docs/05_FEATURES.md` + `docs/features/<feature-id>_<slug>.md`
|
|
321
|
+
|
|
322
|
+
`05` is the **index page** over the `docs/features/` satellites (§4.5). Both the satellites and `05`'s
|
|
323
|
+
generated index region are **tool-owned** (§3 — `spur feature`/`ftree`): edit through the tool, never
|
|
324
|
+
with raw file writes.
|
|
325
|
+
|
|
326
|
+
1. One index row per deliverable, each with a concrete **acceptance** check, status from the legend
|
|
327
|
+
(✅ done · 🔶 partial · ⏳ planned · 💤 deferred), and a pointer to its
|
|
328
|
+
`docs/features/<feature-id>_<slug>.md` satellite.
|
|
329
|
+
2. The satellite + its index row change in the **same change** that ships or re-scopes the feature
|
|
330
|
+
(§5 T4/T9).
|
|
331
|
+
3. **Detail-first edit order (§4.5 rule 5 / T9):** update the feature satellite first (via the tool),
|
|
332
|
+
then refresh the index (e.g. `spur feature refresh`) — never hand-edit the generated index region,
|
|
333
|
+
and never update the index ahead of the detail.
|
|
334
|
+
4. **Never trust a row you have not verified.** Before citing or building on a status, check it
|
|
335
|
+
against code — status rows rot silently in both directions (done-but-⏳ and ⏳-but-claimed).
|
|
336
|
+
5. `05` keeps headline rows + pointers; the full decomposition lives in the satellite files.
|
|
337
|
+
`<feature-id>` is the stable grep anchor (§6.0 rule 6); renaming is a tool operation, not a raw
|
|
338
|
+
edit.
|
|
339
|
+
|
|
340
|
+
### 6.7 `AGENTS.md`
|
|
341
|
+
|
|
342
|
+
1. Factual blocks that mirror code — the command surface, the workspace layout, tool versions —
|
|
343
|
+
are **regenerated from code**, never edited from memory. Verify with the actual registrations
|
|
344
|
+
(e.g. list the CLI's registered nouns/verbs) before writing the block.
|
|
345
|
+
2. Keep it lean: link to the owning doc instead of restating its facts. `AGENTS.md` repeats only
|
|
346
|
+
what an agent needs in the first 30 seconds of a session.
|
|
347
|
+
3. Surfaces that are decided-but-unbuilt are flagged as planned with their ADR pointer, and
|
|
348
|
+
marked "do not invoke as if they exist".
|
|
349
|
+
4. Re-synced whenever this file changes the map or process (§4.4).
|
|
350
|
+
|
|
351
|
+
### 6.8 This file (`99`)
|
|
352
|
+
|
|
353
|
+
1. **No project facts** — ever (§1). Tool bindings (§3) and Lessons (§8) are the only
|
|
354
|
+
project-variable content.
|
|
355
|
+
2. Structure and principles change only on operator request; Lessons sections are
|
|
356
|
+
machine-appendable per the §8 protocol without asking.
|
|
357
|
+
3. When this file improves in one project, **propagate the improvement to sibling projects** —
|
|
358
|
+
it is one constitution with N copies, not N constitutions.
|
|
359
|
+
|
|
360
|
+
## 7. Drift control
|
|
361
|
+
|
|
362
|
+
**Drift** = reality (code, shipped behavior) disagreeing with what a key file says, or two key
|
|
363
|
+
files disagreeing with each other.
|
|
364
|
+
|
|
365
|
+
**Repair protocol** (always this order):
|
|
366
|
+
|
|
367
|
+
1. Fix the **authoritative** doc — for append-only files, by dated amendment, never rewriting.
|
|
368
|
+
2. Then the derived docs that restate or sequence it.
|
|
369
|
+
3. Then `AGENTS.md`.
|
|
370
|
+
4. Flag what drifted and why in the commit message / task — a silent fix hides the systemic
|
|
371
|
+
cause.
|
|
372
|
+
|
|
373
|
+
**Audit cadence:** at every phase exit, and before designing any large batch, run the drift
|
|
374
|
+
audit:
|
|
375
|
+
|
|
376
|
+
- [ ] List the real CLI/tool surface from code; diff against `AGENTS.md`'s surface block and
|
|
377
|
+
`00`'s committed-surface entries.
|
|
378
|
+
- [ ] For every `05` row marked ✅/🔶, spot-check the acceptance against code; for every ⏳, check
|
|
379
|
+
it didn't quietly ship.
|
|
380
|
+
- [ ] For every shipped surface, confirm a `01` scope row exists.
|
|
381
|
+
- [ ] Check `02`'s current phase bullets name things that actually exist (no dead names).
|
|
382
|
+
- [ ] Check `03`'s module descriptions against the real file tree of each app/package.
|
|
383
|
+
- [ ] Confirm `04` covers every command/flag/config/schema that exists.
|
|
384
|
+
- [ ] For `04`/`05` (§4.5): every index row points to an existing satellite, and every satellite
|
|
385
|
+
(`docs/design/<slug>.md`, `docs/features/<id>_<slug>.md`) has exactly one index row — no orphan
|
|
386
|
+
satellites, no dangling pointers.
|
|
387
|
+
- [ ] Confirm `AGENTS.md`'s doc map matches §4.1 of this file.
|
|
388
|
+
- [ ] Confirm each doc's frontmatter matches its §4.1 row and its `updated_at` is plausible
|
|
389
|
+
against recent commits (§4.3).
|
|
390
|
+
|
|
391
|
+
Findings are repaired via the protocol above, and anything systemic becomes a Lesson (§8) — or,
|
|
392
|
+
if it recurs, a new rule in §6.
|
|
393
|
+
|
|
394
|
+
## 8. Lessons learned per file
|
|
395
|
+
|
|
396
|
+
**Append protocol (machine-maintained):**
|
|
397
|
+
|
|
398
|
+
- Format: `- [YYYY-MM-DD] <project>: <lesson — what went wrong / what to do instead>`
|
|
399
|
+
- Threshold is **low** — when in doubt, append. Check for an existing equivalent first; bump its
|
|
400
|
+
date instead of duplicating.
|
|
401
|
+
- **Promotion rule:** a lesson that recurs or hardens into practice is promoted into a §6 rule
|
|
402
|
+
(or a §5 trigger) and removed from this section. Lessons are the inbox; §5/§6 are the law.
|
|
403
|
+
Promotion is the only sanctioned deletion.
|
|
404
|
+
- Lessons carry project provenance because this file is copied across projects — a lesson from
|
|
405
|
+
one project is a warning, not yet a law, for the others.
|
|
406
|
+
|
|
407
|
+
### Lessons for `docs/00_ADR.md`
|
|
408
|
+
|
|
409
|
+
_(empty — add lessons as the project evolves)_
|
|
410
|
+
|
|
411
|
+
### Lessons for `docs/01_PRD.md`
|
|
412
|
+
|
|
413
|
+
_(empty — add lessons as the project evolves)_
|
|
414
|
+
|
|
415
|
+
### Lessons for `docs/02_ROADMAP.md`
|
|
416
|
+
|
|
417
|
+
_(empty — add lessons as the project evolves)_
|
|
418
|
+
|
|
419
|
+
### Lessons for `docs/03_ARCHITECTURE.md`
|
|
420
|
+
|
|
421
|
+
_(empty — add lessons as the project evolves)_
|
|
422
|
+
|
|
423
|
+
### Lessons for `docs/04_DESIGN.md`
|
|
424
|
+
|
|
425
|
+
_(empty — add lessons as the project evolves)_
|
|
426
|
+
|
|
427
|
+
### Lessons for `docs/05_FEATURES.md`
|
|
428
|
+
|
|
429
|
+
_(empty — add lessons as the project evolves)_
|
|
430
|
+
|
|
431
|
+
### Lessons for `AGENTS.md`
|
|
39
432
|
|
|
40
|
-
|
|
41
|
-
|-----|-------------------|-----------|
|
|
42
|
-
| `00_ADR.md` | **WHY** — decisions + one-line reason | Authoritative (wins all) |
|
|
43
|
-
| `01_PRD.md` | **WHAT** — product vision, scope | Authoritative on scope |
|
|
44
|
-
| `02_ROADMAP.md` | **WHEN** — phases, sequencing | Derived |
|
|
45
|
-
| `03_ARCHITECTURE.md` | **HOW** — module boundaries, data flow | Derived |
|
|
46
|
-
| `04_DESIGN.md` | **SURFACE** — commands, flags, schemas | Derived |
|
|
47
|
-
| `05_FEATURES.md` | **STATUS** — feature decomposition + state | Derived |
|
|
48
|
-
| `99_PROJECT_CONSTITUTION.md` | **PROCESS** — how files are maintained | Authoritative on process |
|
|
433
|
+
_(empty — add lessons as the project evolves)_
|
|
49
434
|
|
|
50
|
-
|
|
435
|
+
### Lessons for this file (`99`)
|
|
51
436
|
|
|
52
|
-
|
|
437
|
+
_(empty — add lessons as the project evolves)_
|
|
53
438
|
|
|
54
|
-
|
|
55
|
-
|----|---------|--------|
|
|
56
|
-
| T1 | New cross-cutting decision (entry = decision + one-line reason; amendment = decision delta only — mechanism goes in `03`/`04`, not the amendment) | `00` first, then `03` mechanism |
|
|
57
|
-
| T3 | Command/flag/config/schema/DTO added/changed | `04` + `AGENTS.md` |
|
|
58
|
-
| T4 | Feature ships or changes state | `05` row |
|
|
59
|
-
| T6 | Scope added / cut / deferred | `01` |
|
|
439
|
+
## 9. Bootstrapping a new project
|
|
60
440
|
|
|
61
|
-
|
|
441
|
+
Checklist to instantiate this structure in a fresh repo:
|
|
62
442
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
443
|
+
1. Copy this file verbatim to `docs/99_PROJECT_CONSTITUTION.md`; empty the §8 lessons of
|
|
444
|
+
other projects' entries or keep them as inherited warnings (recommended: keep).
|
|
445
|
+
2. Update §3 bindings if the new project's toolchain differs.
|
|
446
|
+
3. Create `docs/00_ADR.md` with the §4.3 frontmatter and `ADR-001` recording the founding
|
|
447
|
+
decision (stack, structure, the why).
|
|
448
|
+
4. Create `docs/01_PRD.md`: vision paragraph, users, principles table, scope tables (in /
|
|
449
|
+
supporting / deferred / out).
|
|
450
|
+
5. Create `docs/02_ROADMAP.md` with Phase 0 and its exit criterion.
|
|
451
|
+
6. Create `docs/03_ARCHITECTURE.md`: topology, dependency boundary, runtime model — current
|
|
452
|
+
state only.
|
|
453
|
+
7. Create `docs/04_DESIGN.md` (may start near-empty) and `docs/05_FEATURES.md` (legend + first
|
|
454
|
+
rows).
|
|
455
|
+
8. Create root `AGENTS.md`: instantiated §4.1 doc map, stack/layout, commands, verification
|
|
456
|
+
gate, conventions. Symlink `CLAUDE.md` (and equivalents) to it.
|
|
457
|
+
9. Wire the §3 tools (spec lifecycle, harness) per their own docs.
|
|
458
|
+
10. First-session rule for any agent: read `AGENTS.md` → this file → `00`/`01` before touching
|
|
459
|
+
anything.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.21",
|
|
4
4
|
"description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spur",
|
package/spur.js
CHANGED
|
@@ -74710,7 +74710,7 @@ import { join as join15, resolve as resolve5 } from "path";
|
|
|
74710
74710
|
var CLI_CONFIG = {
|
|
74711
74711
|
binaryName: "spur",
|
|
74712
74712
|
binaryLabel: "spur",
|
|
74713
|
-
binaryVersion: "0.3.
|
|
74713
|
+
binaryVersion: "0.3.21",
|
|
74714
74714
|
configDir: ".spur",
|
|
74715
74715
|
configFile: ".spur/config.yaml",
|
|
74716
74716
|
databaseFile: ".spur/spur.db"
|
|
@@ -74770,6 +74770,10 @@ function substituteAgentsMdTemplate(content, projectName) {
|
|
|
74770
74770
|
const description = "local Spur project";
|
|
74771
74771
|
return content.replaceAll("{project-name}", projectName).replaceAll("{project-description}", description);
|
|
74772
74772
|
}
|
|
74773
|
+
function substituteDocTemplateTokens(content, date6 = new Date) {
|
|
74774
|
+
const dateStr = date6.toISOString().slice(0, 10);
|
|
74775
|
+
return content.replaceAll("{{init-date}}", dateStr);
|
|
74776
|
+
}
|
|
74773
74777
|
var INDEXED_CONTEXT_BLOCK = `
|
|
74774
74778
|
---
|
|
74775
74779
|
|
|
@@ -74908,6 +74912,8 @@ ${contextEntry}
|
|
|
74908
74912
|
const configRoot = bundledConfigRoot();
|
|
74909
74913
|
if (configRoot !== null) {
|
|
74910
74914
|
for (const relPath of listBundledProjectSeedFiles()) {
|
|
74915
|
+
if (relPath.startsWith("templates/docs/"))
|
|
74916
|
+
continue;
|
|
74911
74917
|
const sourcePath = join15(configRoot, relPath);
|
|
74912
74918
|
if (!await context4.fs.exists(sourcePath))
|
|
74913
74919
|
continue;
|
|
@@ -74927,6 +74933,9 @@ ${contextEntry}
|
|
|
74927
74933
|
if (entry.target === "AGENTS.md") {
|
|
74928
74934
|
body = substituteAgentsMdTemplate(body, projectName);
|
|
74929
74935
|
}
|
|
74936
|
+
if (entry.source.startsWith("templates/docs/")) {
|
|
74937
|
+
body = substituteDocTemplateTokens(body);
|
|
74938
|
+
}
|
|
74930
74939
|
await writeIfNew(context4, targetPath, body, entryForce, result);
|
|
74931
74940
|
}
|
|
74932
74941
|
}
|
|
@@ -84577,7 +84586,7 @@ async function runStatusCore(path9, options, context4) {
|
|
|
84577
84586
|
]);
|
|
84578
84587
|
const target = path9 === undefined ? undefined : await readTargetStatus(context4, path9);
|
|
84579
84588
|
const status = {
|
|
84580
|
-
ok:
|
|
84589
|
+
ok: spurConfigExists,
|
|
84581
84590
|
packageJson: packageJsonExists,
|
|
84582
84591
|
spurConfig: spurConfigExists,
|
|
84583
84592
|
git,
|
|
@@ -84588,8 +84597,8 @@ async function runStatusCore(path9, options, context4) {
|
|
|
84588
84597
|
context4.output.write(toJson2(status));
|
|
84589
84598
|
} else {
|
|
84590
84599
|
context4.output.write([
|
|
84591
|
-
`Project: ${
|
|
84592
|
-
|
|
84600
|
+
`Project: ${spurConfigExists ? "ok" : "missing .spur/config.yaml"}`,
|
|
84601
|
+
`Package: ${packageJsonExists ? "ok" : "none"}`,
|
|
84593
84602
|
`Agents: ${agentSpecs.length === 0 ? "none" : agentSpecs.join(", ")}`,
|
|
84594
84603
|
`Git: ${git.root === null ? "none" : `${git.branch ?? "detached"}${git.dirty ? " dirty" : " clean"}`}`,
|
|
84595
84604
|
...target === undefined ? [] : [`Path: ${target.path} ${target.size} bytes`]
|