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 +93 -0
- package/CONTRIBUTING.md +14 -0
- package/README.md +2 -1
- package/SKILL-CARD.md +3 -3
- package/cursor/rules/task-pipeline.mdc +16 -1
- package/evals/RESULTS.md +4 -1
- package/package.json +1 -1
- package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/build.md +25 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/learned.md +2 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/planning.md +28 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +2 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/spec.md +36 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +12 -5
- package/plugins/task-pipeline/skills/task-pipeline/templates/README.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/hygiene.sh +236 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/retro.md +7 -1
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.
|
|
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` —
|
|
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.**
|
|
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.
|
|
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
|
|
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.
|
|
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.
|
|
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` |
|
|
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):**
|
|
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):**
|
|
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):**
|
|
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
|
|
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
|
-
###
|
|
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
|
|