task-pipeline-skill 1.11.0 → 1.13.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 CHANGED
@@ -1,5 +1,98 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.13.0 — 2026-08-05
4
+
5
+ ### Added — the read-back: four rules that existed and were never handed over
6
+
7
+ Stages 3 and 4 produced documents stating things nobody verified: that a named
8
+ check exists, that the spec agrees with decisions already made, that the
9
+ self-review happened at all, and that the change still costs what it was worth.
10
+
11
+ Five defects, and **four of them are one shape.** The rule already lived in this
12
+ bundle, in a stage that never handed it to the stage which had to obey it. The
13
+ evidence-for-checks rule sits at stages 6 and 10; the rejected-alternatives rule
14
+ sits at stage 2; `learned.md` rule 14 has sat at stage 9 since v1.4.0. Stage 3
15
+ names checks, contradicts decisions and writes DoDs — and read none of them back.
16
+
17
+ So the fix is one mechanism applied four times. `spec.md`'s self-review now asks
18
+ whether every check it names is real, reads back the brief's `Decisions locked`
19
+ table **and** the alternatives stage 2 rejected, and prints the cost.
20
+ `planning.md` asks whether every command, path and file a DoD names resolves.
21
+ `learned.md`'s stage map binds rule 14 at 3 and 4, not only at 9.
22
+
23
+ **The fifth is a genuine absence, and it prints rather than decides.** Nothing
24
+ anywhere asked whether a change had outgrown its worth. The new checkpoint counts
25
+ surfaces, guards and REQ rows now versus at stage 2 and prints all three — the
26
+ stage-3 gate is the operator's, and an agent that narrows the task on its own
27
+ judgement breaks *never narrow the task silently*.
28
+
29
+ **Both self-reviews now leave a committed trace.** A `## Self-review` section,
30
+ identical in shape across the two files, every line a **computed number rather
31
+ than a tick** — because a number nobody computed is visible as such and a
32
+ checkbox never is. `planning.md` already demanded the REQ set difference be
33
+ *printed*; this extends that principle to the rest of the checklist.
34
+
35
+ Three guards prove the files carry the items, with four probes. What they cannot
36
+ prove is that a run in someone else's repository performed a self-review — and
37
+ that boundary is stated in the spec, in the guard's own comment and here, because
38
+ a guard claiming otherwise would be the exact defect this release fixes.
39
+
40
+ Negative self-tests: 76 → 80.
41
+
42
+ ## v1.12.0 — 2026-08-05
43
+
44
+ ### Added — a gate for the defects an agent leaves behind
45
+
46
+ Every other check in this repository asks whether the work is *right*. None of
47
+ them asked whether the text is *intact* — whether a merge was left half-resolved,
48
+ a stub outlived its task, a generation stopped mid-fence, a file was "shortened"
49
+ while being rewritten, a retried edit duplicated a block instead of replacing it,
50
+ or a section was opened and abandoned. That class has an author, and the author is
51
+ an agent.
52
+
53
+ `templates/hygiene.sh` is the answer: a seeded, executable gate, sibling of
54
+ `docgate.sh`, carrying the same contracts — a `SCOPE:` header that names its own
55
+ false-positive surfaces, a VERDICT block that must be last, portability to macOS
56
+ bash 3.2, progressive arming, and a final line of computed numbers. It runs in two
57
+ modes: the run's diff at **zero tolerance**, and the whole tree behind per-check
58
+ **ratchet floors**, so an existing repository can adopt it without starting red
59
+ while nothing new is forgiven.
60
+
61
+ **It never edits.** None of the six defects is safely machine-fixable — deleting a
62
+ "duplicated block" sometimes deletes a legitimate repetition, and deleting a `TODO`
63
+ erases a reminder instead of discharging it. `references/build.md` makes fixing the
64
+ agent's obligation, at the point it is cheapest: after each task reports `DONE`,
65
+ before the reviewer sees it. Found one task later, a defect costs a re-dispatch;
66
+ found eight tasks later it is fixed by an agent that no longer remembers the code.
67
+
68
+ **Two of the six definitions were wrong until they were measured**, which is the
69
+ point of the rule that says measure a detector before shipping it. Matching the
70
+ word `TODO` anywhere found 33 hits here, 28 of them ordinary English — this is a
71
+ repository whose own doctrine is *"a ratchet, never a TODO"*. Anchored as a
72
+ line-leading marker: 0. Flagging a heading followed by any heading found 62
73
+ legitimate nestings; restricted to same-or-higher level it found 4, and the
74
+ detector still had a bug — fenced lines were skipped entirely, so a section whose
75
+ body is one code block looked empty. Counting fenced content as body: 2, both real.
76
+ Check 4 then repeated check 2's mistake in its sibling and was caught the same way.
77
+
78
+ Those last 2 were fixed rather than absorbed into a floor, so all six ship at 0.
79
+ One of them was a shape mistake in the retro template every project copies: a
80
+ one-line retirement record written as a heading, when a heading promises a section.
81
+
82
+ ### Fixed — a guard that had been decorative since it shipped
83
+
84
+ Writing the first probe for the VERDICT-last contract revealed that the guard split
85
+ each gate script on the *word* `VERDICT`, which also appears in the header sentence
86
+ forbidding anything after it. Its "tail after the verdict" was therefore most of the
87
+ script, exit lines included, and it passed on anything. It now splits on the
88
+ `# ---------- VERDICT` marker and requires that marker to exist. A green from a
89
+ check nobody has watched fail is not evidence; this one was green for nine releases.
90
+
91
+ The template contract guards now iterate a `GATE_SCRIPTS` list instead of naming one
92
+ file, so a third gate costs one line rather than a copied block that drifts.
93
+
94
+ Negative self-tests: 68 → 75.
95
+
3
96
  ## v1.11.0 — 2026-08-04
4
97
 
5
98
  ### Added — run continuity: the pacing rules the operator was repeating by hand
package/CONTRIBUTING.md CHANGED
@@ -117,6 +117,20 @@ found**, and run a minimum of live checks — because every section can go `dorm
117
117
  and a gate blind to a shape passes exactly like one that reads it. Change a template
118
118
  → run `npm test`, not just your eyes.
119
119
 
120
+ **The same law now covers `templates/hygiene.sh`.** It is run over a clean scratch
121
+ project, must exit `0`, and must report all six of its check counts — exit `0` alone
122
+ proves nothing, because every check can go `dormant` and dormant is green. Both gate
123
+ scripts are validated by one iterated block, not two copies, and both must keep a
124
+ `# ---------- VERDICT` marker with nothing after it.
125
+ *(guard: `the VERDICT block must be last and must `)*
126
+
127
+ **Stages 3 and 4 must keep reading their rules back.** `spec.md`'s self-review asks
128
+ whether every check it names is real, reads back the brief's decisions and stage 2's
129
+ rejected options, and prints the cost; `planning.md` asks whether every command a DoD
130
+ names resolves; `learned.md` binds rule 14 at both stages. Both files carry a
131
+ committed `## Self-review` section of computed numbers rather than ticks.
132
+ *(guard: `the self-review no longer asks `)*
133
+
120
134
  **11. Every reference over 100 lines carries a `## Contents` list**, and the list is
121
135
  compared against that file's own `##` headings. The guidance asks for it because a
122
136
  long file gets previewed with a partial read; the comparison is because a hand-kept
package/README.md CHANGED
@@ -129,6 +129,7 @@ until it is installed.
129
129
  | any loop | [`loop-guard.md`](plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md) — churn detection, caps, the break protocol |
130
130
  | run-wide pacing | [`continuity.md`](plugins/task-pipeline/skills/task-pipeline/references/continuity.md) — the loop mode (`run.loop`, default off, never collapses a manual gate) and the context budget (fires on a harness signal, never on an estimate) |
131
131
  | 0 + 9 + any settled decision | [`documentation.md`](plugins/task-pipeline/skills/task-pipeline/references/documentation.md) — the inventory, registers and ids, SSOT, the Doc Loop, supersede semantics, the propagation matrix, intent vs as-built |
132
+ | 3 + 4 · every spec and plan | the self-review reads its rules back — is every named check real, does anything contradict a locked decision or a rejected option, and what does this cost now versus at design time. Answers land as a committed `## Self-review` of computed numbers |
132
133
  | 6–10 + any check you write | [`gates.md`](plugins/task-pipeline/skills/task-pipeline/references/gates.md) — the two axes, the promotion ladder, gate anatomy, the probe recipe, ratchet floors |
133
134
  | any agent-time enforcement | [`hooks.md`](plugins/task-pipeline/skills/task-pipeline/references/hooks.md) — the `PreToolUse` contract, the fail-open hazard, the Claude-Code-only limit |
134
135
 
@@ -776,7 +777,7 @@ recommendation, so you arm the whole run in one exchange. Detail:
776
777
  | [`references/knowledge-graph.md`](plugins/task-pipeline/skills/task-pipeline/references/knowledge-graph.md) | the code graph: install line, stage-0 reach queries, the stage-9 refresh, the graph↔docs divergence check |
777
778
  | [`references/retrospective.md`](plugins/task-pipeline/skills/task-pipeline/references/retrospective.md) | the project retro: the three grades of fix, the mandatory prune, the cap of ten |
778
779
  | [`references/model-tiering.md`](plugins/task-pipeline/skills/task-pipeline/references/model-tiering.md) | model policy, the `/model` reminder, overrides |
779
- | [`templates/`](plugins/task-pipeline/skills/task-pipeline/templates/README.md) | brief, carry-over ledger, `CONTEXT.md` and ADR skeletons, the doc map, both registers, the retro and its archive, the seeded `docgate.sh`, a worked hook |
780
+ | [`templates/`](plugins/task-pipeline/skills/task-pipeline/templates/README.md) | brief, carry-over ledger, `CONTEXT.md` and ADR skeletons, the doc map, both registers, the retro and its archive, the seeded `docgate.sh` and `hygiene.sh`, a worked hook |
780
781
  | [`references/adoption.md`](plugins/task-pipeline/skills/task-pipeline/references/adoption.md) | the first run in a project: greenfield seeding, and the brownfield walkthrough |
781
782
  | [`references/setup.md`](plugins/task-pipeline/skills/task-pipeline/references/setup.md) | the entry audit: seven passes over the docs a project already has, offered once, output as a fix plan |
782
783
  | [`references/portability.md`](plugins/task-pipeline/skills/task-pipeline/references/portability.md) | the manifest of workflow decisions and their homes in the bundle, and the boundary against a project's own answers |
package/SKILL-CARD.md CHANGED
@@ -12,7 +12,7 @@ harmless.
12
12
  |---|---|
13
13
  | **Purpose** | Runs a substantial task through ten gated delivery stages — intake grill, docs study, brainstorm, spec, plan, subagent build, tests, lint/deploy, post-deploy, docs+registers, acceptance — refusing to advance until each gate passes |
14
14
  | **Owner** | ssheleg ([github.com/ssheleg/task-pipeline](https://github.com/ssheleg/task-pipeline)) |
15
- | **Version** | 1.11.0 |
15
+ | **Version** | 1.13.0 |
16
16
  | **Surface** | Claude Code (filesystem skill + plugin) and the vercel `skills` CLI. **Not** uploaded to the Skills API; custom Skills do not sync across surfaces |
17
17
  | **Dependencies** | None required. Optional: `context7` (MCP), `figma` (MCP), super-ux, agent-sync, graphify, obsidian-wiki. Every stage's doctrine ships in-repo; the one conditional requirement is super-ux for the stage-3 UX track on a user-facing task |
18
18
  | **Evaluation status** | Suite authored (15 evals, 5 categories). **Never executed** — see [`evals/RESULTS.md`](evals/RESULTS.md) |
@@ -38,7 +38,7 @@ apply.
38
38
  instruction surface, and every one is linked directly from `SKILL.md`.
39
39
  2. Read `templates/docgate.sh` before seeding it; it is the only shipped script a
40
40
  host project will run on its own repository.
41
- 3. Run `npm run test:all` — 68 guards, each with a negative self-test that plants a
41
+ 3. Run `npm run test:all` — 80 guards, each with a negative self-test that plants a
42
42
  defect and requires rejection.
43
43
  4. Run `python3 evals/run.py` for the behavioural protocol, and read
44
44
  `evals/RESULTS.md` for what has actually been observed.
@@ -54,7 +54,7 @@ apply.
54
54
  - **Versions are pinned by git tag** and mirrored into `sshlg-skills`'s catalogue.
55
55
  Rollback is `git checkout v<previous>` or pinning the previous plugin version;
56
56
  the previous version is never deleted.
57
- - **Behavioural evidence is missing, not merely thin.** 68 structural guards prove
57
+ - **Behavioural evidence is missing, not merely thin.** 80 structural guards prove
58
58
  the skill is well-formed. Until `evals/RESULTS.md` carries a dated run, nothing in
59
59
  this repository proves it *behaves* — triggers correctly, stays quiet on a
60
60
  question, or performs the steps it documents.
@@ -197,6 +197,13 @@ not authorize an outward, irreversible action — stage 7 stops and asks.
197
197
  exact values every later task inherits. Self-review it for placeholders,
198
198
  contradictions, ambiguity and scope before showing it. For UI tasks, run the UX
199
199
  chain FIRST (see super-ux below) and embed it.
200
+ **Before handing a spec or a plan over, read your rules back:** does every
201
+ check you named actually exist (or say `review`)? does anything here
202
+ contradict a decision already locked, including an option deliberately
203
+ rejected? how many surfaces, guards and requirements is this now versus
204
+ when the design was approved — print all three and let the operator
205
+ decide. Leave the answers as a committed `Self-review` section of
206
+ computed numbers, not ticks.
200
207
  4. **Plan** (auto) — every task names the REQ ids it `Implements:`, and the set of those ids must **equal** the brief's REQ set — a difference is scope lost, print it and fix it. Zero-context tasks: exact paths, complete code in every step,
201
208
  the exact command and its expected output, TDD steps, DoD each, dependency graph
202
209
  + parallel groups, non-overlapping file ownership. **No placeholders** — no TBD,
@@ -207,7 +214,15 @@ not authorize an outward, irreversible action — stage 7 stops and asks.
207
214
  commit), then a review with **all three** verdicts — spec compliance, **REQ
208
215
  satisfied** (judged against the requirement's own statement, not the task's
209
216
  instructions), and code quality. Findings loop back to the implementer (max 5
210
- rounds, then adjudicate in writing); minor findings are recorded, never blocking. Keep a progress ledger in
217
+ rounds, then adjudicate in writing); minor findings are recorded, never blocking.
218
+ **After each task reports done, before the review, scan what that task changed
219
+ for the defects an agent leaves behind** — a half-resolved merge, a stub that
220
+ outlived the task, a code fence left open, a file "shortened" while being
221
+ rewritten, a block duplicated by a retried edit, a section opened and abandoned.
222
+ Zero tolerance on a diff: that task wrote it, that task fixes it. A subagent's own
223
+ report is not evidence about the text it left behind, and a duplicated block eight
224
+ tasks later is repaired by an agent that no longer remembers the code.
225
+ Keep a progress ledger in
211
226
  a file so a lost context can resume instead of redoing finished work. Tasks in
212
227
  the same parallel group run concurrently **only** if each worker has its own
213
228
  worktree — otherwise sequential. Close the stage by integrating: sync with the
package/evals/RESULTS.md CHANGED
@@ -37,13 +37,16 @@ or it belongs in a check.
37
37
  | Dated runs recorded | **0** | 2026-08-03 |
38
38
 
39
39
  The bottom two numbers are the honest state of this skill's behavioural evidence.
40
- Everything else in this repository is proven by 68 structural guards that check the
40
+ Everything else in this repository is proven by 80 structural guards that check the
41
41
  *form*; these are the only checks that would speak to the *behaviour*, and they have
42
42
  not been run yet. Printed here so "63 of 63 green" is never read as "the skill is
43
43
  known to work".
44
44
 
45
45
  ## Runs
46
46
 
47
+ Newest first. Each entry states the model, the task, and how the evidence was
48
+ obtained — a self-observed run and a blind one are not the same claim.
49
+
47
50
  ## 2026-08-03 · opus · self-observed, not a blind run
48
51
 
49
52
  **Scope of this evidence.** The author ran the skill on a real task
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "description": "Full-cycle delivery pipeline for coding agents: a mandatory built-in intake grill, then 10 gated stages (docs, brainstorm+decompose, spec, plan, build, tests, lint/deploy, post-deploy, docs/wiki, acceptance). Every stage's doctrine ships inside the skill — no companion plugin required. This package is the installer CLI.",
5
5
  "bin": {
6
6
  "task-pipeline": "bin/task-pipeline.js"
@@ -2,7 +2,7 @@
2
2
  "name": "task-pipeline",
3
3
  "displayName": "Task Pipeline",
4
4
  "description": "Runs a substantial task through a mandatory built-in intake grill, then 10 gated stages (docs, brainstorm+decompose, spec, plan, subagent build, tests, lint/deploy, post-deploy, docs/wiki, acceptance). Every stage's doctrine is built into the skill — no companion plugin required — with typed auto/manual gates, a frozen requirement spine that must close with evidence, a loop guard that breaks churn, one provider-agnostic model confirmed up front, and an optional super-ux UX track for user-facing work.",
5
- "version": "1.11.0",
5
+ "version": "1.13.0",
6
6
  "author": {
7
7
  "name": "ssheleg",
8
8
  "url": "https://x.com/sshlg93"
@@ -156,6 +156,7 @@ plugins/task-pipeline/
156
156
  conventions.md artifacts.md # host conventions, this layout
157
157
  companion-skills.md # optional companions + preflight
158
158
  templates/ # skeletons seeded into a host project
159
+ hygiene.sh # -> scripts/check-hygiene.sh (stages 5, 6, 9)
159
160
  README.md brief.md carryover.md context.md adr.md retro.md
160
161
  cursor/rules/task-pipeline.mdc # Cursor channel (self-contained rule)
161
162
  bin/task-pipeline.js # npx installer (package task-pipeline-skill)
@@ -247,11 +247,35 @@ physical: **two implementers writing one working tree corrupt each other's state
247
247
 
248
248
  | Status | Action |
249
249
  |---|---|
250
- | `DONE` | Build the review package, dispatch the task review ([`review.md`](review.md)). |
250
+ | `DONE` | **Run the hygiene gate in diff mode first** (below), then build the review package and dispatch the task review ([`review.md`](review.md)). |
251
251
  | `DONE_WITH_CONCERNS` | Read the concerns first. Correctness or scope → resolve before review. Observations ("this file is getting large") → **append to the carry-over ledger**, then proceed. A concern that stays only in the report dies with the workspace. |
252
252
  | `NEEDS_CONTEXT` | Supply exactly what's missing, re-dispatch. |
253
253
  | `BLOCKED` | Diagnose: missing context → re-dispatch with it; needs more reasoning → a more capable model; too large → split the task; the plan itself is wrong → escalate to the operator. |
254
254
 
255
+ ### The hygiene gate — after every task, before the review
256
+
257
+ A subagent's own report is not evidence about the text it left behind. Run the
258
+ seeded gate over **what this task changed**, before the reviewer sees it:
259
+
260
+ ```bash
261
+ HYGIENE_BASE=<the commit recorded before dispatching> bash scripts/check-hygiene.sh
262
+ ```
263
+
264
+ Diff mode has **no floor**: this task wrote it, this task fixes it. Six checks — a
265
+ half-resolved merge, a stub that outlived the task, a fence left open, a file
266
+ "shortened" while being rewritten, a **block duplicated by a retried edit**, and a
267
+ section opened and abandoned.
268
+
269
+ The fifth is why the gate runs here rather than only at stage 6. A batch of edits
270
+ where one applied twice, or never applied at all, is the incident behind standing
271
+ instruction R-002 — and it is invisible in a status report. Found one task later it
272
+ costs a re-dispatch; found eight tasks later it is fixed by an agent that no longer
273
+ remembers the code.
274
+
275
+ **The gate never edits. Fixing is yours.** A finding is repaired inside the same
276
+ task, or it becomes a carry-over row with a reason — never a silent pass. A gate
277
+ whose findings nobody acts on is a slower way of ignoring them.
278
+
255
279
  **Never** ignore an escalation, and never re-dispatch the same model with the same
256
280
  prompt after a BLOCKED. If the implementer says it's stuck, something must change.
257
281
  If the implementer asks a question — before or mid-task — answer it completely; do
@@ -154,6 +154,8 @@ answer would have exposed it in a minute.
154
154
  | 3 Spec · 4 Plan | 2 (both directions), 8 (compute, never restate) |
155
155
  | 5 Dev | 9 (generators seed green), 12 (tests create their own state), 13 (local infra) |
156
156
  | 6 Tests | 4, 5, 10, 11 — every new check probed both ways, measured, and asserted on its exit code |
157
+ | 3 Spec | 14 — every check the spec **names** must resolve at the moment it is named, or be marked `review` |
158
+ | 4 Plan | 14 — every command, path and file a DoD names must resolve |
157
159
  | 9 Docs | 8, 14 — every number computed, every target resolvable |
158
160
  | 10 Acceptance | 1, 3, 6, 7 — axis rotation recorded, closure verified against artefacts, classes swept, ratchets printed |
159
161
 
@@ -193,6 +193,34 @@ A checklist you run yourself, inline. No subagent:
193
193
  5. **Parallel safety:** no two tasks in the same group write the same file; every
194
194
  `depends:` points at a task that really produces what's consumed.
195
195
  6. **DoD present and verifiable** on every task.
196
+ 7. **Every command, path and file a DoD names resolves.** Walk each task's
197
+ *Definition of done* and its steps and check the targets exist — a DoD that says
198
+ `npm run lint:paths` when no such script exists is an instruction the implementer
199
+ cannot follow and a check the acceptance cannot run.
200
+ [`learned.md`](learned.md) rule 14 has said *every target resolvable* since
201
+ v1.4.0 and fired only at stage 9 — four stages after the target is written here.
202
+ 8. **Run the hygiene gate** over what this stage wrote and record its counts below.
203
+
204
+ **R-003 asked of this file, and answered:** `spec.md`'s items 8 and 9 — reading
205
+ decisions back, and printing the cost — deliberately do **not** appear here. This
206
+ stage settles nothing (see below), so it has no decisions to contradict and no scope
207
+ of its own to grow. Its sibling's item 7 does belong, reworded for DoDs.
208
+
209
+ ### The `## Self-review` section — committed, not asserted
210
+
211
+ Identical in shape to `spec.md`'s, so one habit covers both stages. Last section
212
+ before the gate; every line a **computed number, not a tick**.
213
+
214
+ ```markdown
215
+ ## Self-review
216
+
217
+ - REQ coverage: <n> in brief, <n> covered, difference <set or ∅>
218
+ - Named checks: <n> named, <n> resolve, <n> marked `review`
219
+ - Decisions: checked against <the brief's D-table> and <stage 2's rejected options> — <verdict>
220
+ - Cost: <surfaces>/<guards>/<REQ> now, <…> at stage 2 — <proportionate | grown, and why>
221
+ - Hygiene: <n> checks, <n> findings, <n> open
222
+ - Placeholders: <n> · Ambiguity: <n> found, <n> resolved inline
223
+ ```
196
224
 
197
225
  ## This stage settles nothing — and that is a rule, not an omission
198
226
 
@@ -57,6 +57,8 @@ a row pointing outside the bundle is the defect this file exists to catch.
57
57
  | Rules earned by failure | `references/learned.md` |
58
58
  | **The routing default and its boundary** | `templates/routing-rule.md` |
59
59
  | The seeded doc map, registers and gate | `templates/docmap.md`, `templates/decisions.md`, `templates/open-questions.md`, `templates/docgate.sh` |
60
+ | Which agent-introduced defects are found, and that the agent fixes them rather than the script | `templates/hygiene.sh`, `references/build.md` |
61
+ | What a stage-3/4 self-review must read back, and that its trace is computed numbers | `references/spec.md`, `references/planning.md`, `references/learned.md` |
60
62
  | What a stage reads, and which host files bind it | `references/artifacts.md` |
61
63
  | The design conversation, its hard gate, UI detection, user paths | `references/brainstorm.md` |
62
64
  | Cutting a platform into modules, brick criteria, build order | `references/decomposition.md` |
@@ -143,6 +143,42 @@ Read what you wrote with fresh eyes and fix inline. No subagent, no second pass:
143
143
  4. **Scope:** is this one implementable plan, or does it need decomposition?
144
144
  5. **Ambiguity:** can any requirement be read two ways? Pick one and say it.
145
145
  6. **Traceability (UI):** does every user-facing requirement name its scenario ID?
146
+ 7. **Every check this spec names resolves.** Walk the verification claims — the
147
+ table, and every sentence that says how something is proven. For each: does that
148
+ check exist today, or is this plan building it? A check that is neither is **not**
149
+ a verification: mark it `review` and say so, or build it. This repository's whole
150
+ doctrine is that a green from a check nobody watched fail is not evidence — and
151
+ this stage is where checks are first *named*, which is one step earlier than
152
+ anyone was looking.
153
+ 8. **Read the decisions back.** Open the brief's `## Decisions locked` table **and**
154
+ the register entries stage 2 recorded for the alternatives it *rejected*
155
+ (`references/brainstorm.md` → *The approved design is a set of decisions*). Does
156
+ any contract here contradict one? Resolve it **out loud** — amend the spec, or
157
+ reverse the decision and record the reversal. A spec that quietly contradicts a
158
+ settled decision re-opens a question the operator already answered.
159
+ 9. **Print the cost.** Count the surfaces this spec touches, the guards it adds and
160
+ the REQ rows, now versus at stage 2. **Print all three and decide nothing.**
161
+ Growth is information for the operator, whose gate this is; an agent that narrows
162
+ the task on its own judgement breaks *never narrow the task silently*.
163
+ 10. **Run the hygiene gate** over what this stage wrote and record its counts below.
164
+
165
+ ### The `## Self-review` section — committed, not asserted
166
+
167
+ The checklist above leaves a **committed trace**, last section before the gate.
168
+ Every line carries a **computed number, not a tick**: a number nobody computed is
169
+ visible as such, and a checkbox never is. `planning.md` uses the identical shape, so
170
+ one habit covers both stages.
171
+
172
+ ```markdown
173
+ ## Self-review
174
+
175
+ - REQ coverage: <n> in brief, <n> covered, difference <set or ∅>
176
+ - Named checks: <n> named, <n> resolve, <n> marked `review`
177
+ - Decisions: checked against <the brief's D-table> and <stage 2's rejected options> — <verdict>
178
+ - Cost: <surfaces>/<guards>/<REQ> now, <…> at stage 2 — <proportionate | grown, and why>
179
+ - Hygiene: <n> checks, <n> findings, <n> open
180
+ - Placeholders: <n> · Ambiguity: <n> found, <n> resolved inline
181
+ ```
146
182
 
147
183
  ## Locked contracts are decisions — the register, not only the spec
148
184
 
@@ -251,7 +251,10 @@ never that the work was skipped quietly.
251
251
  `SCR-` screens, the CJM stages the feature serves, and the UX
252
252
  patterns/principles from super-ux that apply (`best-practices.md`,
253
253
  `ux-design-principles.md`, `component-guidelines.md`).
254
- - **GATE (manual):** spec committed **and** user-reviewed; **every section carries
254
+ - **GATE (manual):** the **`## Self-review` section written and committed with
255
+ computed values** — every check the spec names resolving or marked `review`, the
256
+ brief's decisions and stage 2's rejected options read back with no unresolved
257
+ contradiction, and the cost delta printed; spec committed **and** user-reviewed; **every section carries
255
258
  `covers: REQ-…` and every REQ appears in at least one section**; for UI tasks
256
259
  additionally: the super-ux chain (foundation → flows → screens → scenarios) is
257
260
  designed, validated and approved; scenarios validated in `docs/ux/scenarios.md`;
@@ -270,7 +273,8 @@ never that the work was skipped quietly.
270
273
  paths, complete code in every step, TDD steps with expected output, DoD each,
271
274
  dependency graph + parallel groups, non-overlapping file ownership, and the
272
275
  Global Constraints block copied verbatim from the spec.
273
- - **GATE (auto):** **set equality — the REQ ids in the brief equal the union of
276
+ - **GATE (auto):** the **`## Self-review` section written with computed values**,
277
+ every command, path and file a DoD names resolving; **set equality — the REQ ids in the brief equal the union of
274
278
  `Implements:` across plan tasks.** A non-empty difference fails the gate and is
275
279
  reported as the explicit list of dropped requirements; this is the seam where
276
280
  scope leaks silently, so the check is mechanical, not a judgement call. Plus:
@@ -296,7 +300,9 @@ never that the work was skipped quietly.
296
300
  on the result, land it the project's way (merge, or a PR — outward, so it needs a
297
301
  go), remove the worktree. Stages 7–9 act on the integrated result, so a branch the
298
302
  operator chose to leave unmerged is recorded as such.
299
- - **GATE (auto):** all plan tasks DONE (three review verdicts per task: spec
303
+ - **GATE (auto):** **the hygiene gate green in diff mode after every task**
304
+ (`references/build.md`) — six checks over what that task changed, no floor, and a
305
+ finding fixed in-task or carried over with a reason; all plan tasks DONE (three review verdicts per task: spec
300
306
  compliance, **REQ satisfied**, code quality); every finding fixed or parked with a
301
307
  ruling; **every parked finding and implementer concern harvested into the
302
308
  carry-over ledger** — nothing stays only in the scratch workspace, which is
@@ -311,7 +317,8 @@ never that the work was skipped quietly.
311
317
  - **Invoke:** the host test runner (see `conventions.md` → *Lint + test*); the
312
318
  built-in [`tdd.md`](tdd.md) cycle for any uncovered gap — failing test first,
313
319
  same as stage 5.
314
- - **GATE (auto):** the **full** suite is green (not just the new tests); new/changed code
320
+ - **GATE (auto):** **the hygiene gate green over the whole tree**, its six counts
321
+ printed beside their floors; the **full** suite is green (not just the new tests); new/changed code
315
322
  is covered; no `skip`/`xfail` smuggling a red suite past the gate. Never advance
316
323
  to deploy on a red or partial run. **The carry-over count is printed beside this
317
324
  verdict** — a ratchet nobody prints is a TODO with a better name
@@ -381,7 +388,7 @@ never that the work was skipped quietly.
381
388
  - **Docs living in another repository** are outward: propose the edit, get an
382
389
  explicit go, then open a PR there. No go → the exact edit goes in the carry-over
383
390
  ledger.
384
- - **GATE (auto):** **the propagation matrix walked for every change type this run
391
+ - **GATE (auto):** **the hygiene gate green**, counts printed; **the propagation matrix walked for every change type this run
385
392
  produced**, with every settled thing recorded under an id and every answered
386
393
  question resolved; **the documentation gate green, its ratchet counts printed and
387
394
  any skip stated** — this is what replaced the unfalsifiable *"docs in sync with
@@ -17,6 +17,7 @@ from `super-ux`.
17
17
  | `decisions.md` | `docs/DECISIONS.md` — the decision register | 0 seeds it, the Doc Loop appends |
18
18
  | `open-questions.md` | `docs/OPEN_QUESTIONS.md` | 0 seeds it, the Doc Loop resolves rows |
19
19
  | `docgate.sh` | `scripts/check-docs.sh` | 0 seeds it · 9 runs it · 10 proves it |
20
+ | `hygiene.sh` | `scripts/check-hygiene.sh` | 0 seeds it · **5 runs it after every task** · 6 and 9 run it · 10 proves it |
20
21
  | `hooks.example.json` | the project's `.claude/settings.json` | 0 — offered, never installed silently |
21
22
  | `routing-rule.md` | the operator's `CLAUDE.md` — **offered by `setup`, never written silently** | 0 / `setup` |
22
23
  | `retro.md` | `docs/superpowers/retro.md` — **one per project, not per run** | 10 writes (prune → stamp → entry), 0 reads it in full |
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env bash
2
+ # hygiene.sh — the artifact hygiene gate for <project>.
3
+ #
4
+ # Seeded by task-pipeline (references/gates.md). IT IS YOURS NOW: extend it here,
5
+ # section by section. Each section is independent and removable.
6
+ #
7
+ # WHAT IT IS FOR: the defect class an AGENT produces and no other check looks for —
8
+ # a half-resolved merge, a stub that outlived its task, a generation cut off in the
9
+ # middle, a file "shortened" while being rewritten, a batch of edits where one
10
+ # applied twice, a section opened and abandoned.
11
+ #
12
+ # SCOPE: walks tracked files. In diff mode it walks only what this run changed; in
13
+ # tree mode it walks everything, behind per-check floors.
14
+ # It does NOT check: prose meaning, code correctness, style, formatting, spelling,
15
+ # another repository, or generated output. It is not a linter — your language has
16
+ # one, and this is not it.
17
+ # KNOWN FALSE-POSITIVE SURFACES, one per check, because a gate that hides them
18
+ # gets disabled the first time it is wrong:
19
+ # 1 conflict markers — a document explaining merge conflicts
20
+ # 2 placeholders — only a LINE-LEADING marker counts. The word TODO in the
21
+ # middle of a sentence is prose, not a stub. This
22
+ # distinction is the whole check: measured on the project
23
+ # that ships it, 28 of 33 hits were ordinary English.
24
+ # 3 unterminated fence — a fence deliberately shown unclosed as an example
25
+ # 4 truncation stubs — a document quoting one as an example of what not to do
26
+ # 5 duplicated block — a legitimately repeated stanza 3+ lines long
27
+ # 6 empty section — a heading used as a one-line record. If you have those,
28
+ # they want to be list items: a heading promises a section.
29
+ # Read this header before quoting a green from here as evidence.
30
+ #
31
+ # IT NEVER EDITS. It reports file:line and exits non-zero. None of the six is safely
32
+ # machine-fixable: deleting a "duplicated block" sometimes deletes a legitimate
33
+ # repetition, and deleting a TODO erases a reminder instead of discharging it.
34
+ # Fixing is the agent's job, and task-pipeline's references/build.md makes it one.
35
+ #
36
+ # EXIT CODE IS THE OUTPUT: non-zero on any failure. Nothing may run after the
37
+ # VERDICT block at the bottom — a gate that appended a check after its verdict
38
+ # printed FAIL and returned 0, and CI was green over it for an unknown period.
39
+ #
40
+ # PORTABLE to macOS bash 3.2: no grep -P, no sed -i, no readarray, no mapfile.
41
+ #
42
+ # PROGRESSIVE ARMING: a check whose input does not exist prints
43
+ # "dormant: … — no <input>" and does NOT fail. Dormant is visible so it is not
44
+ # forgotten, and green so a freshly seeded project does not start red.
45
+ #
46
+ # TWO MODES, AND THE DIFFERENCE IS THE POINT:
47
+ # diff mode (HYGIENE_BASE set) — ZERO tolerance. This run wrote it, this run fixes
48
+ # it. There is no floor, because a floor for work you just did is a licence.
49
+ # tree mode (default) — per-check RATCHET FLOORS. A count at or below its
50
+ # floor passes; above it fails. A floor may only fall. This is what lets an
51
+ # existing repository adopt the gate without starting red, while forgiving
52
+ # nothing new.
53
+
54
+ set -u
55
+
56
+ FAIL=0
57
+ HYGIENE_BASE=${HYGIENE_BASE:-}
58
+ HYGIENE_EXCLUDE=${HYGIENE_EXCLUDE:-graphify-out/}
59
+ HYGIENE_FLOOR_1=${HYGIENE_FLOOR_1:-0}
60
+ HYGIENE_FLOOR_2=${HYGIENE_FLOOR_2:-0}
61
+ HYGIENE_FLOOR_3=${HYGIENE_FLOOR_3:-0}
62
+ HYGIENE_FLOOR_4=${HYGIENE_FLOOR_4:-0}
63
+ HYGIENE_FLOOR_5=${HYGIENE_FLOOR_5:-0}
64
+ HYGIENE_FLOOR_6=${HYGIENE_FLOOR_6:-0}
65
+
66
+ TMP=${TMPDIR:-/tmp}/hygiene.$$
67
+ mkdir -p "$TMP" || exit 2
68
+ trap 'rm -rf "$TMP"' EXIT
69
+
70
+ err() { echo " FAIL: $*"; FAIL=1; }
71
+ ok() { echo " ok: $*"; }
72
+ dormant() { echo " dormant: $*"; }
73
+
74
+ # ---------- the file list ----------
75
+ # git is the source of truth for "authored": generated and ignored files are not
76
+ # this gate's business, and scanning them is how a gate becomes noise.
77
+ if git rev-parse --git-dir >/dev/null 2>&1; then
78
+ if [ -n "$HYGIENE_BASE" ]; then
79
+ MODE=diff
80
+ { git diff --name-only "$HYGIENE_BASE" 2>/dev/null
81
+ git diff --name-only 2>/dev/null
82
+ git diff --name-only --cached 2>/dev/null
83
+ } | sort -u > "$TMP/all"
84
+ else
85
+ MODE=tree
86
+ git ls-files > "$TMP/all"
87
+ fi
88
+ else
89
+ MODE=tree
90
+ find . -type f -not -path './.git/*' | sed 's|^\./||' > "$TMP/all"
91
+ fi
92
+
93
+ : > "$TMP/files"
94
+ while read -r f; do
95
+ [ -n "$f" ] || continue
96
+ [ -f "$f" ] || continue
97
+ case "$f" in
98
+ $HYGIENE_EXCLUDE*) continue ;;
99
+ esac
100
+ echo "$f" >> "$TMP/files"
101
+ done < "$TMP/all"
102
+
103
+ NFILES=$(wc -l < "$TMP/files" | tr -d ' ')
104
+ : > "$TMP/md"
105
+ while read -r f; do
106
+ case "$f" in *.md) echo "$f" >> "$TMP/md" ;; esac
107
+ done < "$TMP/files"
108
+ NMD=$(wc -l < "$TMP/md" | tr -d ' ')
109
+
110
+ echo "hygiene gate — mode $MODE · $NFILES file(s), $NMD markdown"
111
+
112
+ # The floor a check is judged against. Diff mode has none: zero tolerance.
113
+ floor_for() {
114
+ if [ "$MODE" = diff ]; then echo 0; return; fi
115
+ eval "echo \${HYGIENE_FLOOR_$1}"
116
+ }
117
+
118
+ judge() { # judge <n> <count> <label>
119
+ _f=$(floor_for "$1")
120
+ if [ "$2" -gt "$_f" ]; then
121
+ err "check $1 — $3: $2 finding(s), floor $_f"
122
+ sed 's/^/ /' "$TMP/hits$1"
123
+ else
124
+ ok "check $1 — $3: $2 (floor $_f)"
125
+ fi
126
+ }
127
+
128
+ if [ "$NFILES" -eq 0 ]; then
129
+ dormant "every check — no files in scope"
130
+ C1=0; C2=0; C3=0; C4=0; C5=0; C6=0
131
+ else
132
+
133
+ # ---------- 1. conflict markers ----------
134
+ : > "$TMP/hits1"
135
+ while read -r f; do
136
+ grep -n -E '^(<<<<<<< |=======$|>>>>>>> )' "$f" 2>/dev/null |
137
+ sed "s|^|$f:|" >> "$TMP/hits1"
138
+ done < "$TMP/files"
139
+ C1=$(wc -l < "$TMP/hits1" | tr -d ' ')
140
+ judge 1 "$C1" "conflict markers"
141
+
142
+ # ---------- 2. surviving placeholders ----------
143
+ # A LINE-LEADING marker, optionally behind a comment introducer. Not the word.
144
+ : > "$TMP/hits2"
145
+ while read -r f; do
146
+ grep -n -E '^[[:space:]]*([#/*<!-]+[[:space:]]*)*(TODO|TBD|FIXME|XXX)\b' "$f" 2>/dev/null |
147
+ sed "s|^|$f:|" >> "$TMP/hits2"
148
+ done < "$TMP/files"
149
+ C2=$(wc -l < "$TMP/hits2" | tr -d ' ')
150
+ judge 2 "$C2" "surviving placeholders"
151
+
152
+ # ---------- 3. unterminated code fence ----------
153
+ : > "$TMP/hits3"
154
+ if [ "$NMD" -eq 0 ]; then
155
+ dormant "check 3 — unterminated fence: no markdown in scope"
156
+ C3=0
157
+ else
158
+ while read -r f; do
159
+ # grep -c exits 1 on zero matches, so a `|| echo 0` would append a SECOND zero
160
+ # and make the test below see "0\n0". Swallow the status instead.
161
+ n=$(grep -c '^[[:space:]]*```' "$f" 2>/dev/null) || n=0
162
+ [ -z "$n" ] && n=0
163
+ if [ "$n" -gt 0 ] && [ "$(expr "$n" % 2)" -ne 0 ]; then
164
+ echo "$f:1: odd fence count ($n) — a code block is not closed" >> "$TMP/hits3"
165
+ fi
166
+ done < "$TMP/md"
167
+ C3=$(wc -l < "$TMP/hits3" | tr -d ' ')
168
+ judge 3 "$C3" "unterminated fence"
169
+ fi
170
+
171
+ # ---------- 4. truncation stubs ----------
172
+ # LINE-LEADING, exactly like check 2, and for exactly the same reason: the first
173
+ # version matched the phrase anywhere and fired three times on the very documents
174
+ # that DEFINE these patterns. A stub is a line standing in for content; a phrase in
175
+ # the middle of a sentence is prose about stubs.
176
+ : > "$TMP/hits4"
177
+ while read -r f; do
178
+ grep -n -E '^[[:space:]]*([#/*<!-]+[[:space:]]*)*(\.\.\. existing code|\[TRUNC|rest of (the )?file unchanged|unchanged\.\.\.)' "$f" 2>/dev/null |
179
+ sed "s|^|$f:|" >> "$TMP/hits4"
180
+ done < "$TMP/files"
181
+ C4=$(wc -l < "$TMP/hits4" | tr -d ' ')
182
+ judge 4 "$C4" "truncation stubs"
183
+
184
+ # ---------- 5. duplicated adjacent block ----------
185
+ # The mechanical form of "re-verify every edit in a batch": a retry that duplicated
186
+ # instead of replacing leaves two identical blocks back to back. Three lines is the
187
+ # floor because two-line repeats occur legitimately (table rows, list pairs).
188
+ : > "$TMP/hits5"
189
+ while read -r f; do
190
+ awk -v F="$f" '
191
+ NF { blk = blk "\n" $0; n++; next }
192
+ {
193
+ if (n >= 3) { if (blk == prev) print F ":" NR ": duplicated " n "-line block"; prev = blk }
194
+ else prev = ""
195
+ blk = ""; n = 0
196
+ }
197
+ END { if (n >= 3 && blk == prev) print F ":" NR ": duplicated " n "-line block at end of file" }
198
+ ' "$f" >> "$TMP/hits5" 2>/dev/null
199
+ done < "$TMP/files"
200
+ C5=$(wc -l < "$TMP/hits5" | tr -d ' ')
201
+ judge 5 "$C5" "duplicated adjacent block"
202
+
203
+ # ---------- 6. empty section ----------
204
+ # A heading of level N followed by a heading of level <= N with no body between.
205
+ # FENCED CONTENT COUNTS AS BODY — the first version of this check skipped fenced
206
+ # lines entirely and so reported every section whose body is one code block.
207
+ : > "$TMP/hits6"
208
+ if [ "$NMD" -eq 0 ]; then
209
+ dormant "check 6 — empty section: no markdown in scope"
210
+ C6=0
211
+ else
212
+ while read -r f; do
213
+ awk -v F="$f" '
214
+ /^[[:space:]]*(```|~~~)/ { fence = !fence; prev = 0; next }
215
+ fence { prev = 0; next }
216
+ /^#{1,6} / {
217
+ match($0, /^#+/); lvl = RLENGTH
218
+ if (prev && lvl <= prev) print F ":" NR ": empty section — h" prev " then h" lvl " with no body"
219
+ prev = lvl; next
220
+ }
221
+ NF { prev = 0 }
222
+ ' "$f" >> "$TMP/hits6" 2>/dev/null
223
+ done < "$TMP/md"
224
+ C6=$(wc -l < "$TMP/hits6" | tr -d ' ')
225
+ judge 6 "$C6" "empty section"
226
+ fi
227
+
228
+ fi
229
+
230
+ # ---------- VERDICT — nothing may run after this block ----------
231
+ if [ "$FAIL" -ne 0 ]; then
232
+ echo "FAIL: hygiene gate — mode $MODE · conflict ${C1} · placeholder ${C2} · fence ${C3} · truncation ${C4} · duplicate ${C5} · empty-section ${C6}"
233
+ exit 1
234
+ fi
235
+ echo "OK: hygiene gate — mode $MODE · $NFILES file(s) · conflict ${C1} · placeholder ${C2} · fence ${C3} · truncation ${C4} · duplicate ${C5} · empty-section ${C6}"
236
+ exit 0
@@ -48,7 +48,13 @@ stays short precisely so that reading it in full at stage 0 stays cheap.
48
48
  - **Upstream?** … (a lesson true in any repo belongs in the skill's own
49
49
  `references/learned.md` — open an issue and say so here)
50
50
 
51
- ### 2026-05-04 · retired R-000 — became a check (`npm run lint:paths`) · `<sha>`
51
+ ### Retirements
52
+
53
+ One line each, newest first. A retirement is a record, not a section — so it is a
54
+ list item. A heading promises a body, and a heading with nothing under it is the
55
+ shape the hygiene gate's check 6 exists to find.
56
+
57
+ - 2026-05-04 · retired R-000 — became a check (`npm run lint:paths`) · `<sha>`
52
58
 
53
59
  ## Run stamps
54
60