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.
- package/CHANGELOG.md +137 -0
- package/README.md +68 -8
- package/cursor/rules/task-pipeline.mdc +82 -0
- package/package.json +5 -2
- package/plugins/task-pipeline/.claude-plugin/plugin.json +4 -2
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +41 -10
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +6 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +10 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/audit.md +5 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +15 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/conventions.md +19 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/documentation.md +285 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +236 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/hooks.md +164 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +26 -5
- package/plugins/task-pipeline/skills/task-pipeline/references/learned.md +17 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +55 -14
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +71 -9
- package/plugins/task-pipeline/skills/task-pipeline/templates/README.md +14 -1
- package/plugins/task-pipeline/skills/task-pipeline/templates/adr.md +28 -3
- package/plugins/task-pipeline/skills/task-pipeline/templates/brief.md +26 -8
- package/plugins/task-pipeline/skills/task-pipeline/templates/decisions.md +50 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/docgate.sh +391 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/docmap.md +86 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/hooks.example.json +21 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/open-questions.md +21 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/retro-archive.md +41 -0
- 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 #
|
|
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
|
|
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
|
|
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. |
|