task-pipeline-skill 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/CHANGELOG.md +137 -0
  2. package/README.md +68 -8
  3. package/cursor/rules/task-pipeline.mdc +82 -0
  4. package/package.json +5 -2
  5. package/plugins/task-pipeline/.claude-plugin/plugin.json +4 -2
  6. package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +41 -10
  7. package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +6 -3
  8. package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +10 -2
  9. package/plugins/task-pipeline/skills/task-pipeline/references/audit.md +5 -1
  10. package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +15 -0
  11. package/plugins/task-pipeline/skills/task-pipeline/references/conventions.md +19 -0
  12. package/plugins/task-pipeline/skills/task-pipeline/references/documentation.md +285 -0
  13. package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +236 -0
  14. package/plugins/task-pipeline/skills/task-pipeline/references/hooks.md +164 -0
  15. package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +26 -5
  16. package/plugins/task-pipeline/skills/task-pipeline/references/learned.md +17 -2
  17. package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +55 -14
  18. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +71 -9
  19. package/plugins/task-pipeline/skills/task-pipeline/templates/README.md +14 -1
  20. package/plugins/task-pipeline/skills/task-pipeline/templates/adr.md +28 -3
  21. package/plugins/task-pipeline/skills/task-pipeline/templates/brief.md +26 -8
  22. package/plugins/task-pipeline/skills/task-pipeline/templates/decisions.md +50 -0
  23. package/plugins/task-pipeline/skills/task-pipeline/templates/docgate.sh +391 -0
  24. package/plugins/task-pipeline/skills/task-pipeline/templates/docmap.md +86 -0
  25. package/plugins/task-pipeline/skills/task-pipeline/templates/hooks.example.json +21 -0
  26. package/plugins/task-pipeline/skills/task-pipeline/templates/open-questions.md +21 -0
  27. package/plugins/task-pipeline/skills/task-pipeline/templates/retro-archive.md +41 -0
  28. package/plugins/task-pipeline/skills/task-pipeline/templates/retro.md +27 -13
@@ -9,11 +9,16 @@ shape.
9
9
 
10
10
  ```
11
11
  CONTEXT.md # stage 0 — domain glossary, written inline as terms resolve
12
+ scripts/check-docs.sh # stage 0 seeds it, 9 runs it, 10 proves it — the docs gate
12
13
  docs/
14
+ DOCMAP.md # stage 0 — the inventory: registers, homes, matrix, gates
15
+ DECISIONS.md # the decision register (DEC-####), append-only …
16
+ OPEN_QUESTIONS.md # … and its questions (OQ-####) — never delete a resolved row
13
17
  adr/
14
- NNNN-<slug>.md # stage 0ADRs for hard-to-reverse decisions
18
+ NNNN-<slug>.md # the OTHER permitted decision home one project uses ONE
15
19
  superpowers/
16
20
  retro.md # stage 10's last act — ONE per project, not per run
21
+ retro/YYYY-QN.md # the archive: rotated entries + retirements, queried not read
17
22
  specs/
18
23
  YYYY-MM-DD-<topic>-brief.md # stage 0 — locked intake brief (grill output)
19
24
  YYYY-MM-DD-<topic>-carryover.md # stage 0 seeds it; EVERY stage appends; stage 10 reads it
@@ -63,7 +68,10 @@ record (see `build.md`).
63
68
  | 0→10 all | `specs/<topic>-carryover.md` — append-only ledger (seed from `templates/carryover.md`) | stage 10, in full |
64
69
  | 10 Acceptance | `specs/<topic>-acceptance.md` — every REQ with a status and evidence | the operator |
65
70
  | 10 Retro | `superpowers/retro.md` — standing instructions (max 10), the problem→cause→fix log, run stamps. Pruned **before** anything is added (`retrospective.md`) | **stage 0 of the next run**, in full |
66
- | 0 Grill (domain) | `CONTEXT.md`, `docs/adr/NNNN-<slug>.md` — created **lazily**, only when a term resolves or a decision qualifies | stages 2–4 + the repo |
71
+ | 0 Inventory | `docs/DOCMAP.md` + the registers + `scripts/check-docs.sh` — seeded **only when absent**, and the seeding is the register's first entry ([`documentation.md`](documentation.md)) | every later stage; **stage 9** walks the matrix, **stage 10** proves the gate |
72
+ | 0 Grill (domain) | `CONTEXT.md`, `docs/adr/NNNN-<slug>.md` — created **lazily**, only when a term resolves or a decision qualifies. Where `docs/adr/` **is** the register, entries carry the register's field set | stages 2–4 + the repo |
73
+ | any stage | a register entry per settled thing, via the **Doc Loop** — recorded, resolved, propagated, committed with its id | the next run's harvest |
74
+ | 10 Retro rotation | `docs/superpowers/retro/YYYY-QN.md` — entries older than five stamps, plus every retirement, each with its commit | queried by a later run's harvest |
67
75
  | 2 Decompose | `specs/<topic>-modules.md` — module map, build order, contracts, per-module status (platforms only) | stages 3–10, every module's run |
68
76
  | 3 Spec | `specs/<topic>-design.md` — module dossier for a decomposed platform (+ links `docs/ux/*` for UI) | stage 4 |
69
77
  | 4 Plan | `plans/<topic>.md` | stage 5 |
@@ -67,7 +67,7 @@ absence findable.
67
67
  | Rung | Layer | The artefact that must exist |
68
68
  |---|---|---|
69
69
  | **L0** | Requirement | a `REQ-###` row in the brief **with a named check** |
70
- | **L1** | Decision | the locked decision, ADR or `CONTEXT.md` term this REQ rests on |
70
+ | **L1** | Decision | the locked decision this REQ rests on — **an entry in the register** (`DEC-####` or an ADR), or a `CONTEXT.md` term ([`documentation.md`](documentation.md)) |
71
71
  | **L2** | Design | a spec section carrying `covers: REQ-…` |
72
72
  | **L3** | Contract | an exact signature or schema · **and its failure behavior** |
73
73
  | **L4** | Task | a plan task with `Implements:` and a DoD satisfiable **as written** |
@@ -195,6 +195,10 @@ defect class becomes permanent. If the class genuinely cannot be checked
195
195
  mechanically, say so in one line and *say why*; that sentence is itself a finding
196
196
  worth having.
197
197
 
198
+ **How to write, place, arm, probe and own that script is
199
+ [`gates.md`](gates.md).** "Put it in a script" with no place to put it is how the
200
+ third instance ends up in the ledger too.
201
+
198
202
  ### 2. Every pass changes the axis, not the effort
199
203
 
200
204
  "Look again, more carefully" is what converges. Passes must be **orthogonal by
@@ -13,6 +13,9 @@ better, plus one that is required only for user-facing work.
13
13
  | Stage | Doctrine |
14
14
  |---|---|
15
15
  | 0 Knowledge harvest (pre-grill) | `references/knowledge-sources.md` |
16
+ | 0 + 9 + any settled decision — the documentation system | `references/documentation.md` |
17
+ | 6–10 + any check you write — gates | `references/gates.md` |
18
+ | any agent-time enforcement — hooks | `references/hooks.md` |
16
19
  | 0 + 9 The code graph (the tool is optional; the doctrine ships) | `references/knowledge-graph.md` |
17
20
  | 0 Intake grill | `references/grill.md` |
18
21
  | 2 Brainstorm | `references/brainstorm.md` |
@@ -35,6 +38,7 @@ better, plus one that is required only for user-facing work.
35
38
  | **Figma** (MCP) | stage 3 UX track, when the project designs visually — super-ux mirrors each `SCR-` screen/state into a frame | Optional, **UI + Figma-on only**. Absent → super-ux degrades to text-only *by itself and never blocks*, so shipping a UI feature with no mockups becomes a silent scope call — which is why the stage-0 sweep decides it | connect the Figma MCP server (`/mcp`, or your claude.ai connectors) |
36
39
  | **[obsidian-wiki](https://github.com/ar9av/obsidian-wiki)** (`wiki-query`, `wiki-update`) | **stage 0 harvest** (query what's already known) **+ stage 9 sync** | **Recommended** — never a gate; absent → harvest runs on repo docs alone | `pip install obsidian-wiki` → `obsidian-wiki setup --vault /path/to/your/vault` |
37
40
  | **[graphify](https://github.com/Graphify-Labs/graphify)** (`/graphify`, `graphify query\|affected\|god-nodes`) | **stage 0 harvest** (reach: what calls this, what breaks if it moves) **+ stage 9 refresh + the graph↔docs divergence check** ([`knowledge-graph.md`](knowledge-graph.md)) | **Recommended** — never a gate; absent → the harvest greps instead, and the divergence axis is unavailable | `uv tool install graphifyy` → `graphify install` → `/graphify .` |
41
+ | **[agent-sync](https://github.com/ssheleg/agent-sync)** (`/agent-sync`) | **guarded registers** — a lease before writing one, `reserve` before minting an id, `reconcile`/`record` for intent vs as-built, and `finish` for the stage-10 multi-repository close-out ([`documentation.md`](documentation.md)) | **Recommended** — never a gate. Absent → the run is **`ungated`** and must say so out loud; the discipline still applies, only the arbitration is missing | `npx sshlg-skills install` |
38
42
  | ~~superpowers~~ | — | **Not a dependency.** Stages 2/4/5/6 run on the built-in doctrine above. See *Optional bridge* | — |
39
43
  | ~~grill-me / grilling~~ | — | **Not a dependency.** The stage-0 grill is built in (`references/grill.md`) | — |
40
44
 
@@ -76,6 +80,12 @@ Pipeline companions (stage doctrine is built in — nothing to install for it):
76
80
  pip install obsidian-wiki
77
81
  obsidian-wiki setup --vault /path/to/your/vault
78
82
  (running without it — the harvest uses repo docs only)
83
+ ✗ agent-sync — recommended: guarded registers, id reservation before
84
+ minting, intent-vs-as-built reconcile, and the stage-10
85
+ multi-repository close-out:
86
+ npx sshlg-skills install
87
+ (running without it — the doc track still applies, the
88
+ run is recorded `ungated`, and that is said out loud)
79
89
  ✗ graphify — recommended: stage 0 asks it what reaches what,
80
90
  stage 9 refreshes it beside the docs and the wiki:
81
91
  uv tool install graphifyy
@@ -114,6 +124,11 @@ Rules:
114
124
  the MCP and then *continues text-only on its own*, so without a recorded answer
115
125
  the run silently narrows from "designed" to "described". The sweep row is
116
126
  `3 Design surface` ([`grill.md`](grill.md) → *The autonomy sweep*).
127
+ - **agent-sync**: detect via a resolving `/agent-sync` or a `.claude/agent-sync.json`
128
+ in the project. Present → take a lease before writing a guarded register and
129
+ reserve ids before minting them. Absent → print the line **once**, continue, and
130
+ **record the run as `ungated`** — never describe the project as protected
131
+ ([`documentation.md`](documentation.md) → *Registers are shared state*).
117
132
  - **Never gate any stage on an install** except the stage-3 UX track on a UI task.
118
133
  - Optional tools missing → state the fallback, don't block.
119
134
  - Re-detect after the operator installs; don't assume.
@@ -60,6 +60,25 @@ found, surface it and **ask** rather than guessing.
60
60
  project names): updating it is **outward** — propose the change, get an explicit
61
61
  operator go, open a PR there. Never push to a repo the task didn't name.
62
62
 
63
+ ## Documentation regime (stage 0, then 9 and 10)
64
+
65
+ The host's `CLAUDE.md`/`AGENTS.md` wins over anything detected — read it first.
66
+ Then detect, in this order ([`documentation.md`](documentation.md)):
67
+
68
+ | Look for | Means |
69
+ |---|---|
70
+ | `docs/DOCMAP.md` | the inventory has been done; read it, extend it, do not re-seed |
71
+ | `docs/adr/` with at least one `NNNN-*.md` | **that is the register.** Record it in the doc map; never seed a second decision home beside it |
72
+ | `docs/DECISIONS.md` | the register shape; note its id scheme and its "Next free ID" line |
73
+ | a decisions/ADR section inside `AGENTS.md` or `CONTRIBUTING.md` | the rules live there; the doc map holds a **pointer**, not a copy |
74
+ | a `check-docs`, `lint:docs`, `docs` target in `package.json`/`Makefile`/CI | the documentation gate already exists — use it, and read its scope header |
75
+ | none of the above | **seed the set**: `docmap.md`, `decisions.md`, `open-questions.md` and `docgate.sh` from [`../templates/`](../templates/README.md), and record the seeding as the register's first entry |
76
+
77
+ Two rules that are not negotiable when seeding: the gate must exit `0` on the seeds
78
+ themselves, and the registers must be useful at three entries. A project whose gate
79
+ is red on day one has learned that the gate is noise, and nothing later un-teaches
80
+ that.
81
+
63
82
  ## Issue tracker (stage 10)
64
83
 
65
84
  Acceptance parks what wasn't delivered: every `deferred` REQ and every unresolved
@@ -0,0 +1,285 @@
1
+ # Documentation — the system, not the by-product
2
+
3
+ **One job: make documentation a deliverable with an address, an obligation and a
4
+ gate.** Not "write docs at the end" — a *system*: every settled thing has an id,
5
+ every fact has one home, every kind of change names the documents it owes, and a
6
+ script can say no.
7
+
8
+ This file is the **what and why**. [`gates.md`](gates.md) is how the script that
9
+ enforces it gets written; [`stages.md`](stages.md) says where each piece binds.
10
+
11
+ **Governance is a by-product here, never a separate step.** The run already
12
+ produces decisions — the brief's *Decisions locked*, the spec's locked contracts,
13
+ the ADRs the grill writes. Recording one is **transcription plus a stable id**, not
14
+ new thinking. Anything below that feels like ceremony is a sign the register is
15
+ being written twice; write it once, here.
16
+
17
+ ---
18
+
19
+ ## The inventory — four questions, answered before the first line of work
20
+
21
+ Stage 0 answers these before the interview, and writes the answers to
22
+ `docs/DOCMAP.md` (seeded from [`../templates/docmap.md`](../templates/docmap.md),
23
+ **only when absent** — never overwritten):
24
+
25
+ 1. **Where do settled things live?** The decision home, and its id scheme.
26
+ 2. **What is each fact's single home?** One place per fact; everything else links.
27
+ 3. **What does a change of type X oblige?** The propagation matrix.
28
+ 4. **What proves it?** The gate command, and what it does *not* cover.
29
+
30
+ A project with no answers gets them seeded. The seeding is itself recorded as the
31
+ first entry in the register, which is the cheapest possible demonstration that the
32
+ register works.
33
+
34
+ **A project that answers "we don't document" has still answered.** Write that down,
35
+ with the date, and the next agent stops re-deciding it every run.
36
+
37
+ ---
38
+
39
+ ## Registers and ids
40
+
41
+ **One decision home per project. The doc map names it. Never create a second.**
42
+
43
+ Two shapes satisfy the contract; the difference is physical, not semantic:
44
+
45
+ | Shape | Home | Id | Use when |
46
+ |---|---|---|---|
47
+ | **Register** | `docs/DECISIONS.md`, append-only | `DEC-####` | default; many small decisions, read as a list |
48
+ | **ADR set** | `docs/adr/NNNN-<slug>.md` | `ADR-NNNN` | the project already has `docs/adr/`; decisions are long and each wants a page |
49
+
50
+ **Detect, don't assume:** `docs/adr/` holds at least one `NNNN-*.md` → that is the
51
+ register, record it in the doc map and use it. Otherwise seed
52
+ [`../templates/decisions.md`](../templates/decisions.md). An existing ADR set is
53
+ **never migrated** as a side effect of some other task — migrating is its own
54
+ decision, with its own entry.
55
+
56
+ Both shapes owe the same six things: a **stable id**, an **append-only** history, a
57
+ **status line** with the supersede semantics below, a **`Consequences / affects:`**
58
+ line, a **`Source:`** line carrying the run and the **commit**, and the **edge
59
+ markers**.
60
+
61
+ Open questions get their own register (`docs/OPEN_QUESTIONS.md`, `OQ-####`) with a
62
+ closed status vocabulary: `Open` · `Resolved→DEC-####` · `Dropped (<why>)`.
63
+
64
+ **Two rules that look like formatting and are not:**
65
+
66
+ - **Reference facts by id, never by copying the text.** A copy is a second source
67
+ that nobody will update, and the reader cannot tell which one is current.
68
+ - **Ids are never renumbered and resolved questions are never deleted.** The
69
+ question is the history of the answer; without it the next reader re-litigates a
70
+ settled thing from scratch.
71
+
72
+ ---
73
+
74
+ ## Single source of truth
75
+
76
+ **Every fact has exactly one home. Everything else links to it.**
77
+
78
+ If the same fact is stated in two places, that is a **bug** — collapse it to one
79
+ home and link from the other. Not because duplication is untidy: because the two
80
+ copies will disagree, and at that moment both become unusable, since nobody can
81
+ tell which one moved.
82
+
83
+ - The *decision* lives in the register. Topic docs describe the *current design*
84
+ and cite the id.
85
+ - Each topic (architecture, data model, security, …) has one canonical document.
86
+ - Indexes and summaries **link**; they never restate a rule.
87
+
88
+ ### Across repositories
89
+
90
+ **The owning repository decides; a consumer repository describes.** Where a
91
+ consumer document disagrees with the owner, the consumer is wrong — stated rather
92
+ than adjudicated case by case, because case-by-case is how a consumer repo starts
93
+ legislating.
94
+
95
+ The boundary is worth writing out, because an unqualified "the owner always wins"
96
+ is false in one direction: **build state and task status belong to the repository
97
+ doing the work**. A submodule is cloned alone, and a status update must not require
98
+ two repositories.
99
+
100
+ Changing a consumer repo's documentation: compare against the owner's canonical
101
+ doc → look for a contradiction with it *and* with any accepted decision → if the
102
+ consumer document is wrong, fix it in place; if the **owner** is wrong or stale, it
103
+ is corrected there, by a decision entry, through a pull request — and the consumer
104
+ document stays divergent and marked until that lands. Quietly aligning a consumer
105
+ doc to its own view is the failure this paragraph exists to prevent.
106
+
107
+ ---
108
+
109
+ ## The Doc Loop
110
+
111
+ **Fires whenever something is settled — at any stage.** Scope, a contract, a name,
112
+ a policy, a status vocabulary, a price, a retention window. Not only at stage 9.
113
+
114
+ 1. **Orient and reconcile.** Read the register and the topic doc. Run the
115
+ intent/as-built reconcile (below). Do not contradict an accepted decision
116
+ without superseding it. *Skipping this is how a run spends a day building
117
+ against a system that does not exist.*
118
+ 2. **Reserve the id, then record.** Reading "Next free ID" is **not** reserving it
119
+ (see *Registers are shared state*). Then write the entry: date, status, context,
120
+ decision, consequences, source with the commit.
121
+ 3. **Resolve.** Flip every answered question to `Resolved→<id>`. Never delete it.
122
+ 4. **Propagate.** Walk the matrix row for this change type and update every
123
+ document it names, **in the same change**. Keep SSOT: the detail in one home,
124
+ links from the rest.
125
+ 5. **Adjust scope.** Roadmap, MVP, module map — if scope moved.
126
+ 6. **Record as-built.** What was actually built, with the ids and the files. Then
127
+ reconcile again.
128
+ 7. **Commit.** One focused commit, conventional message, **the ids in the subject**.
129
+
130
+ **Finishing the chat answer is not finishing the task.** Closing the loop is. A
131
+ decision that lives only in a spec dies with that spec; one that lives only in the
132
+ conversation was never made.
133
+
134
+ ---
135
+
136
+ ## Changing your mind
137
+
138
+ The register is **append-only**. To reverse or replace:
139
+
140
+ - add a **new** entry stating the new decision and naming what it replaces;
141
+ - edit **only the status line** of the old entry;
142
+ - leave the old body intact as history.
143
+
144
+ **A partial supersede annotates both sides.** Most reversals are partial — a later
145
+ decision replaces one clause and leaves the rest standing. If the old entry keeps a
146
+ bare `Accepted`, a reader who opens only that entry gets an answer that is no longer
147
+ true. So the new entry names which clause it replaces, and the old entry's status
148
+ line gains `· **Partially superseded by <id>** — <one line>`.
149
+
150
+ **Three markers, and they mean different things:**
151
+
152
+ | Marker | Meaning | Target's status line annotated? |
153
+ |---|---|---|
154
+ | `Refines:` | **additive only** — every clause of the target still holds | no |
155
+ | `Contradicts:` | a **named clause** of the target no longer holds | **yes** |
156
+ | `Supersedes:` | the whole target is retired | **yes** |
157
+
158
+ One word for "adds to" and "replaces a clause of" is unenforceable. Measured on the
159
+ project this practice comes from: across **275** refine/supersede edges, **204**
160
+ pointed at a target with no annotation — which is not 204 violations, because most
161
+ were additive. *That ambiguity is the defect*: neither a reader nor a script could
162
+ tell which of the 204 should have been annotated, so the rule could not be gated at
163
+ all.
164
+
165
+ **Existing edges are not retro-classified.** Each needs a judgement about what its
166
+ author meant, and a bulk pass would guess. An edge is reclassified when someone
167
+ touches it for another reason.
168
+
169
+ ---
170
+
171
+ ## The propagation matrix
172
+
173
+ The harvest ledger ([`knowledge-sources.md`](knowledge-sources.md)) names the
174
+ documents you **read**. The matrix names the documents you **owe**. They are not
175
+ the same list, and the gap between them is where documentation rots: the document
176
+ nobody read is exactly the document nobody updated.
177
+
178
+ **Build it in five steps** (stage 0; extend it whenever a new doc class appears):
179
+
180
+ 1. List the project's doc classes — one line each: what is this the home of?
181
+ 2. For each **change type** the project can undergo, name every document that must
182
+ move.
183
+ 3. For each row, name the **check** that would notice if it did not — or write
184
+ `review` *with a one-line reason why no check can decide it*.
185
+ 4. Write the rows into `docs/DOCMAP.md` → *Propagation matrix*.
186
+ 5. Arm the mechanical half in the gate: **a document named in an entry's
187
+ `Consequences / affects:` line must cite that entry.**
188
+
189
+ A row with an empty third column is a **finding**, not a blank. Either a check
190
+ exists, or somebody has said out loud that none can.
191
+
192
+ **The backlog is ratcheted, and that is the design.** Turning this check on in an
193
+ existing repository finds a lot: **162** missing propagations across **73**
194
+ decisions, on the project this comes from — not the four the audit had reported.
195
+ Failing on all of them makes the gate something people switch off, and bulk-fixing
196
+ them blind adds 162 citations nobody verified. So: entries from a **floor** id
197
+ onward fail; everything older is a counted backlog that may only shrink, printed on
198
+ every run. Raising the floor is a decision, and it belongs in the register.
199
+
200
+ ---
201
+
202
+ ## Navigation
203
+
204
+ **One definition per entity, and a mention links to the definition.**
205
+
206
+ - Every definition carries an explicit anchor.
207
+ - A mention links to the **anchor**, not the file. A file link makes the reader
208
+ search; a deep link that rots is caught by the gate.
209
+ - **Indexes never restate a rule.** They fall behind and then they lie with
210
+ authority — a navigation aid that omits an entry is worse than none, because a
211
+ reader concludes the entry does not exist.
212
+
213
+ A deep link is worth more than a file link *and* fails harder, so it only pays with
214
+ a check behind it. Land the anchor check with the first rewritten link, not after
215
+ the sweep.
216
+
217
+ ---
218
+
219
+ ## Intent and as-built
220
+
221
+ Two records, deliberately not merged:
222
+
223
+ | Record | Says | Written by |
224
+ |---|---|---|
225
+ | **Intent** — the registers, the spec, the plan | how it *should* be | the Doc Loop |
226
+ | **As-built** — the run record | how it *turned out* | step 6, at the end |
227
+
228
+ **Reconcile both before starting and after finishing.** Every divergence has one of
229
+ three resolutions: the document is stale, the record is wrong, or they genuinely
230
+ disagree and that is a decision to make. There is no fourth, and "I'll keep it in
231
+ mind" is not one of them.
232
+
233
+ Where a coordination tool is installed it does this for you. Where it is not, the
234
+ as-built record is a section of the carry-over ledger and the reconcile is a read
235
+ of it — **the tool is optional, the discipline is not**.
236
+
237
+ ---
238
+
239
+ ## Registers are shared state
240
+
241
+ A register is the one file two agents will write in the same minute.
242
+
243
+ - **Reserve the id before minting it.** "Next free ID" is a *reading*; a second
244
+ agent reading it in the same minute gets the same answer, and now two entries
245
+ carry one number.
246
+ - **Take a lease before writing a guarded register**, where a lease mechanism
247
+ exists. [`hooks.md`](hooks.md) is how such a guard blocks the edit;
248
+ [`companion-skills.md`](companion-skills.md) names the optional companion that
249
+ implements one.
250
+ - **Ask what two instances with the same identity would do**, and make the tool
251
+ answer it. This is [`learned.md`](learned.md) rule 15, and it is in that table
252
+ because it cost an entire day of work performed under another session's identity.
253
+ - **When nothing can arbitrate, the run is `ungated` — say so out loud.** That is
254
+ a real state, not a formality. Describing a project as protected while nothing
255
+ enforces it is worse than having no protection, because everyone downstream
256
+ believes the guarantee.
257
+
258
+ ---
259
+
260
+ ## Where this binds in the pipeline
261
+
262
+ | Point | What happens | Gate |
263
+ |---|---|---|
264
+ | **Stage 0**, phase 1b | the inventory — the four questions, into `docs/DOCMAP.md`; the regime recorded | manual |
265
+ | **Stage 0**, phase 1c | intent vs as-built reconciled; every divergence resolved | manual |
266
+ | **Any stage** | something is settled → the Doc Loop, all seven steps | the stage's own |
267
+ | **Stage 9** | the propagation sweep, the registers, the gate green with its ratchets printed | auto |
268
+ | **Stage 10** | the gate itself proven — every check seen failing once against a planted defect | manual |
269
+
270
+ ---
271
+
272
+ ## Rationalizations
273
+
274
+ | Excuse | Reality |
275
+ |---|---|
276
+ | "It's a small project, a register is overkill" | A register with three entries costs three minutes and is the only reason the fourth decision can be found. What people mean by overhead is *ceremony*, and none is required here — the entries are transcribed from artefacts the run already produced. |
277
+ | "I'll write the decision up at the end" | At the end you remember the outcome and not the alternatives you rejected, which is the only part with any value later. |
278
+ | "The spec already says it" | A spec is per-run and the next one supersedes it. A decision outlives every artefact that mentions it. |
279
+ | "I updated the docs I touched" | The matrix names the documents you did **not** touch. That is the entire reason it exists. |
280
+ | "Two docs saying the same thing is harmless" | Until they disagree — and then both are unusable, because nobody can tell which one moved. |
281
+ | "I'll just fix the old decision's text" | Then the reason someone chose it is gone and the next person re-litigates it from scratch. Supersede; never edit the body. |
282
+ | "It's obviously additive, no need to mark it" | 204 of 275 edges were left unmarked on exactly that reasoning, and afterwards nobody could tell which of them should have been. The marker costs one word. |
283
+ | "Nobody else is working in this repo right now" | You cannot know that from inside your session, and being wrong means two entries with one id. |
284
+ | "The gate is green, so the docs are fine" | The gate proves what it checks. Read its scope header before quoting it as evidence. |
285
+ | "The doc is stale but that's not this task" | Then it is a row in the carry-over ledger with the exact edit, right now. A staleness noticed and unrecorded is the next run's false premise. |
@@ -0,0 +1,236 @@
1
+ # Gates — the two axes, and how to build one that cannot lie
2
+
3
+ **One job: turn a rule into something that can say no.** [`audit.md`](audit.md)
4
+ says a class seen twice *belongs in a script*; this file is where that script comes
5
+ from — how it is written, where it runs, how it is armed, how it is proven, and
6
+ what you must know **before** you quote its green as evidence.
7
+
8
+ **Boundary, so this file does not become a second source.** The *law* lives
9
+ elsewhere and is not restated here:
10
+
11
+ | The law | Lives in | This file adds |
12
+ |---|---|---|
13
+ | A check must be watched failing before it is trusted | [`audit.md`](audit.md) *Exit criterion*, [`learned.md`](learned.md) 4–5 | the executable recipe |
14
+ | Ratchet, never TODO | [`audit.md`](audit.md) §3, [`learned.md`](learned.md) 7 | floor variables, and where the count is printed |
15
+ | A gate's exit code is part of its output | [`learned.md`](learned.md) 11 | where the verdict block goes |
16
+ | A checker with false positives is worse than none | [`learned.md`](learned.md) 10 | how to measure before shipping |
17
+ | A generator seeds green | [`learned.md`](learned.md) 9 | progressive arming |
18
+ | A class that repeats twice becomes a gate | [`audit.md`](audit.md) §1 | the six-step recipe |
19
+
20
+ ---
21
+
22
+ ## Axis A — the stage gate type
23
+
24
+ From [`../pipeline.schema.json`](../pipeline.schema.json), one per stage:
25
+
26
+ | Type | Meaning | Failure to respect it |
27
+ |---|---|---|
28
+ | `auto` | the orchestrator verifies the `check` itself, pass/fail, and stops on fail | advancing on an unverified check |
29
+ | `manual` | wait for the operator's **explicit** go | treating an auto verification as the approval |
30
+
31
+ **An auto gate never substitutes for a required manual approval.** A green table is
32
+ not the operator confirming it is what they asked for, and no amount of checking
33
+ makes it one. Which stages are manual is the **operator's** decision, recorded in
34
+ their `pipeline.json`; the framework fixes no stage count and no gate assignment.
35
+
36
+ ## Axis B — the enforcement mechanism
37
+
38
+ Where a rule actually lives. A rule climbs this ladder; it does not start at the top.
39
+
40
+ | Rung | Mechanism | Costs | Promote when |
41
+ |---|---|---|---|
42
+ | 1 | **Doctrine line** in a reference file | reading attention | it was violated once |
43
+ | 2 | **Review question** at a named gate | a person's time, every run | no check can decide it — and say *why*, in one line |
44
+ | 3 | **Script check** in the project's gate | writing it once | the class has occurred **twice** |
45
+ | 4 | **CI step** | minutes per push | it must hold for people who never run it locally |
46
+ | 5 | **Hook** ([`hooks.md`](hooks.md)) | latency on every tool call | the failure is cheaper to prevent than to detect, and the target is an edit an agent is making now |
47
+
48
+ A rule may sit on several rungs. What it may never do is **pretend** to be on a
49
+ higher one: a doctrine line that reads as if it were enforced is the same failure as
50
+ a gate that prints `FAIL` and exits `0` — both report a world they are not looking
51
+ at.
52
+
53
+ **Rung 2 is where honesty is bought.** "No check can decide this" is a legitimate,
54
+ common answer. Written down with its reason, it is a finding somebody can later
55
+ disprove. Left unwritten, it is indistinguishable from an omission.
56
+
57
+ ---
58
+
59
+ ## Before you run a check
60
+
61
+ Four preconditions. Skipping any of them turns a run into a claim.
62
+
63
+ 1. **The base is green** — or its known-red baseline is *recorded*. A new guard
64
+ added to an already-red base passes for the wrong reason and proves nothing.
65
+ 2. **The check has been probed.** Green from a check nobody has watched fail is
66
+ worth nothing. If you did not plant the defect, you do not know what the green
67
+ means.
68
+ 3. **You have read its scope header** and know what it does **not** cover. A gate
69
+ is evidence for exactly the surface it walks; quoting it beyond that is how
70
+ "the gate is green" becomes a false statement made in good faith.
71
+ 4. **You have read the ratchet floors.** A pass with a floor that was quietly
72
+ raised is a pass over the thing the floor was hiding.
73
+
74
+ ---
75
+
76
+ ## Anatomy of a project gate
77
+
78
+ Ten properties. Each one is here because its absence has shipped.
79
+
80
+ | Property | Rule | The failure it prevents |
81
+ |---|---|---|
82
+ | **Exit code** | non-zero on **any** failure | a gate that appended a check *after* its verdict block printed `FAIL` and returned `0`; CI was green over it for an unknown period |
83
+ | **Verdict last** | nothing may run after the verdict block | the same failure, from the other end |
84
+ | **Scope header** | states what the gate does **not** cover | a green quoted as proof of a surface nobody walked |
85
+ | **Portability** | POSIX + bash 3.2: no `grep -P`, no `sed -i`, no `readarray` | BSD `sed -i` needs an argument GNU refuses, and `0,/re/` does not exist there — it silently edits nothing and the check reads as a guard that failed to fire |
86
+ | **Ratchet floors** | `<NAME>_FLOOR` variables at the top; counts printed beside `OK` | a backlog that grows back without anyone explaining why |
87
+ | **Skips are printed** | a check that could not run says so | a submodule not checked out silently removing coverage |
88
+ | **Progressive arming** | a section with no input artefact prints `dormant: … — no <artefact> yet` and does **not** fail | a freshly seeded project starting red, which teaches everyone on day one that the gate is noise |
89
+ | **Computed, never restated** | derive every count at check time | two documents quoting a total that went stale |
90
+ | **Both directions** | any two-layer mapping is checked each way | four fully-specified entities with no schema anywhere — found only by the direction that felt redundant |
91
+ | **Named location** | every error prints file **and** line | a finding nobody can act on |
92
+
93
+ Shape:
94
+
95
+ ```bash
96
+ #!/usr/bin/env bash
97
+ # check-docs.sh — the documentation gate for <project>.
98
+ # SCOPE: walks <what>. Does NOT check <what>.
99
+ # Portable to macOS bash 3.2: no grep -P, no sed -i, no readarray.
100
+ set -u
101
+ FAIL=0
102
+ PROP_FLOOR=${PROP_FLOOR:-1} # ratchet: raising it is a decision
103
+
104
+ # ---------- 1. <name> ----------
105
+ ... # ok: / ERR: / skip: / dormant:
106
+
107
+ # ---------- VERDICT — nothing runs after this block ----------
108
+ if [ "$FAIL" -ne 0 ]; then echo "FAIL: <gate>"; exit 1; fi
109
+ echo "OK: <gate> — backlog: $BACKLOG (floor $PROP_FLOOR) · registers: $DECS decisions · $OQS open"
110
+ exit 0
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Writing the check itself
116
+
117
+ - **Pick the unit and say what it costs.** A check that scopes to a table *row*
118
+ will let one marker in that row exempt everything else in it. That is a real
119
+ blind spot; measured and accepted beats unmeasured and denied, so write it in the
120
+ comment.
121
+ - **Prefer a deterministic rule to a heuristic.** A parity-based check for
122
+ unbalanced markup produced six false positives out of six on a real corpus and
123
+ was discarded. If the rule cannot be stated exactly, that is information.
124
+ - **Never infer from strings the environment also produces.** Matching `"claude"`
125
+ in a process command line matched the throwaway shell of every tool call.
126
+ - **Compute the count you print.** A number restated in prose is a number that will
127
+ be wrong; derive it from the source at check time so the two cannot disagree.
128
+
129
+ ---
130
+
131
+ ## Probing — plant, run, restore
132
+
133
+ The law is [`audit.md`](audit.md)'s exit criterion. The procedure is this, and it is
134
+ not optional for a check you intend to trust:
135
+
136
+ ```bash
137
+ cp -R . /tmp/probe && cd /tmp/probe
138
+ python3 - <<'PY' # plant IN PYTHON, never sed -i
139
+ p = "docs/DECISIONS.md"
140
+ s = open(p).read().replace("DEC-0001", "DEC-9999", 1)
141
+ open(p, "w").write(s)
142
+ PY
143
+ bash scripts/check-docs.sh; echo "planted -> exit=$?" # MUST be non-zero
144
+ cd - && bash scripts/check-docs.sh; echo "clean -> exit=$?" # MUST be 0
145
+ ```
146
+
147
+ **Assert on `$?`, not on a `FAIL` line in the output.** A line on stdout is a
148
+ decoration; the exit code is what CI reads.
149
+
150
+ **Doubt the probe before you doubt the check.** On the project this comes from,
151
+ **four of five** silent probes were the probe's fault: one added a *definition*
152
+ where the check looks for an unresolved *reference*; one edited a string whose
153
+ whitespace did not match; one hit the first prose mention instead of the table row;
154
+ one flipped a row whose cell was already empty, so nothing was planted. A silent
155
+ check is a claim about two things, and the probe is the one to doubt first — prove
156
+ your edit landed in the text the check actually parses.
157
+
158
+ **Record the probe.** One line per section, in the change that ships the check.
159
+ Otherwise the next reader has to redo it to know whether it was ever done.
160
+
161
+ ---
162
+
163
+ ## The false-positive budget
164
+
165
+ Run a new heuristic over the **real corpus** before shipping it and count the false
166
+ positives. Zero, or replace the heuristic with a deterministic rule.
167
+
168
+ The budget is not perfectionism. A gate that cries wolf is switched off by the
169
+ third person who hits it, and after that it protects nothing while still appearing
170
+ in the pipeline as a control. A noisy check is worse than no check, because it also
171
+ consumes the credibility of the checks beside it.
172
+
173
+ ---
174
+
175
+ ## Ratchets
176
+
177
+ A **ratchet** is a named, counted set that may only shrink, printed on every run.
178
+
179
+ - Its floor is a **variable at the top of the script**, so raising it is a visible
180
+ edit and a decision.
181
+ - Its count is printed **beside the verdict**, so `PASS` never reads as *verified*
182
+ — it reads as *"green, and here is exactly what was not looked at"*.
183
+ - A ratchet that grew needs a sentence in the run log saying why.
184
+
185
+ ```
186
+ GATE 9 docs: PASS — propagation backlog: 121 (was 162) · unmarked residue: 0
187
+ ```
188
+
189
+ A ratchet nobody prints is a TODO with a better name.
190
+
191
+ ---
192
+
193
+ ## Where a gate runs
194
+
195
+ | Place | Good at | Limit |
196
+ |---|---|---|
197
+ | **Local pre-commit** | fast feedback for the author | skippable, and skipped exactly when someone is in a hurry |
198
+ | **CI** | authoritative; holds for people who never run it locally | minutes late, and it checks out the repo in a shape the author's machine never has — rehearse that shape |
199
+ | **Hook** ([`hooks.md`](hooks.md)) | stops the edit *before* it happens | Claude Code only; a crashing hook **fails open** |
200
+ | **Stage gate** (this pipeline) | judgement, and the things only a person can answer | it is the run's own memory, not the repository's |
201
+
202
+ The four are not alternatives. The same rule can be a hook for the agent, a
203
+ pre-commit for the human and a CI step for the record — what it must never be is
204
+ *declared* in one place and *enforced* in none.
205
+
206
+ ---
207
+
208
+ ## Adding a check to an existing gate
209
+
210
+ 1. **Name the class** — the shape, not the instance. "This id is undefined" is an
211
+ instance; "an id referenced and never defined" is a class.
212
+ 2. **Find the unit** the check will parse: a line, a table row, a paragraph, a
213
+ file. Write down what that unit will miss.
214
+ 3. **Write the predicate deterministically**, with the file and line in the error.
215
+ 4. **Measure it** over the real corpus; zero false positives or rewrite it.
216
+ 5. **Plant, run, restore** — both directions observed, and recorded.
217
+ 6. **Wire its count into the verdict line**, with a floor if it cannot be zero yet.
218
+
219
+ Step 6 is the one that gets skipped, and it is the one that makes the check
220
+ survive: a number beside `OK` is read every run, and a check nobody sees the output
221
+ of is deleted in the next refactor by someone who assumed it was dead.
222
+
223
+ ---
224
+
225
+ ## Rationalizations
226
+
227
+ | Excuse | Reality |
228
+ |---|---|
229
+ | "The check is green, that's evidence" | Only if you have seen it red. An unproven check is a decoration that reports success. |
230
+ | "It printed FAIL, so it failed" | CI reads `$?`. A gate has shipped that printed `FAIL` and exited `0`, and nobody noticed for an unknown number of runs. |
231
+ | "I'll write the check later, the rule is documented" | Then it is on rung 1 and behaves like rung 3 in everyone's head. That gap is the whole failure. |
232
+ | "It's one occurrence, a note is enough" | It is. On the second, the note becomes a script — that is the rule, and the third occurrence is proof it was ignored. |
233
+ | "The heuristic mostly works" | Measure it. Six false positives out of six on a real corpus is what "mostly" felt like from inside. |
234
+ | "The gate would be red on day one, so I'll add it later" | Make the section dormant instead. Dormant is visible and green; "later" is neither. |
235
+ | "I raised the floor to get the build green" | Then say so in the log, in the same commit. A floor raised silently is a ratchet running backwards. |
236
+ | "A hook is overkill, CI catches it" | CI catches it after the edit, the commit and the push. If the point is to stop the edit, CI is the wrong rung — and if it is not, do not pay the latency. |