task-pipeline-skill 1.10.3 → 1.12.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 +114 -0
- package/CONTRIBUTING.md +35 -1
- package/README.md +2 -1
- package/SKILL-CARD.md +3 -3
- package/cursor/rules/task-pipeline.mdc +50 -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/SKILL.md +11 -1
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +4 -0
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +30 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/build.md +34 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +214 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/grill.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +3 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +13 -3
- package/plugins/task-pipeline/skills/task-pipeline/templates/README.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/adr.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/templates/brief.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/carryover.md +1 -1
- 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/plugins/task-pipeline/skills/task-pipeline/templates/routing-rule.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,119 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.12.0 — 2026-08-05
|
|
4
|
+
|
|
5
|
+
### Added — a gate for the defects an agent leaves behind
|
|
6
|
+
|
|
7
|
+
Every other check in this repository asks whether the work is *right*. None of
|
|
8
|
+
them asked whether the text is *intact* — whether a merge was left half-resolved,
|
|
9
|
+
a stub outlived its task, a generation stopped mid-fence, a file was "shortened"
|
|
10
|
+
while being rewritten, a retried edit duplicated a block instead of replacing it,
|
|
11
|
+
or a section was opened and abandoned. That class has an author, and the author is
|
|
12
|
+
an agent.
|
|
13
|
+
|
|
14
|
+
`templates/hygiene.sh` is the answer: a seeded, executable gate, sibling of
|
|
15
|
+
`docgate.sh`, carrying the same contracts — a `SCOPE:` header that names its own
|
|
16
|
+
false-positive surfaces, a VERDICT block that must be last, portability to macOS
|
|
17
|
+
bash 3.2, progressive arming, and a final line of computed numbers. It runs in two
|
|
18
|
+
modes: the run's diff at **zero tolerance**, and the whole tree behind per-check
|
|
19
|
+
**ratchet floors**, so an existing repository can adopt it without starting red
|
|
20
|
+
while nothing new is forgiven.
|
|
21
|
+
|
|
22
|
+
**It never edits.** None of the six defects is safely machine-fixable — deleting a
|
|
23
|
+
"duplicated block" sometimes deletes a legitimate repetition, and deleting a `TODO`
|
|
24
|
+
erases a reminder instead of discharging it. `references/build.md` makes fixing the
|
|
25
|
+
agent's obligation, at the point it is cheapest: after each task reports `DONE`,
|
|
26
|
+
before the reviewer sees it. Found one task later, a defect costs a re-dispatch;
|
|
27
|
+
found eight tasks later it is fixed by an agent that no longer remembers the code.
|
|
28
|
+
|
|
29
|
+
**Two of the six definitions were wrong until they were measured**, which is the
|
|
30
|
+
point of the rule that says measure a detector before shipping it. Matching the
|
|
31
|
+
word `TODO` anywhere found 33 hits here, 28 of them ordinary English — this is a
|
|
32
|
+
repository whose own doctrine is *"a ratchet, never a TODO"*. Anchored as a
|
|
33
|
+
line-leading marker: 0. Flagging a heading followed by any heading found 62
|
|
34
|
+
legitimate nestings; restricted to same-or-higher level it found 4, and the
|
|
35
|
+
detector still had a bug — fenced lines were skipped entirely, so a section whose
|
|
36
|
+
body is one code block looked empty. Counting fenced content as body: 2, both real.
|
|
37
|
+
Check 4 then repeated check 2's mistake in its sibling and was caught the same way.
|
|
38
|
+
|
|
39
|
+
Those last 2 were fixed rather than absorbed into a floor, so all six ship at 0.
|
|
40
|
+
One of them was a shape mistake in the retro template every project copies: a
|
|
41
|
+
one-line retirement record written as a heading, when a heading promises a section.
|
|
42
|
+
|
|
43
|
+
### Fixed — a guard that had been decorative since it shipped
|
|
44
|
+
|
|
45
|
+
Writing the first probe for the VERDICT-last contract revealed that the guard split
|
|
46
|
+
each gate script on the *word* `VERDICT`, which also appears in the header sentence
|
|
47
|
+
forbidding anything after it. Its "tail after the verdict" was therefore most of the
|
|
48
|
+
script, exit lines included, and it passed on anything. It now splits on the
|
|
49
|
+
`# ---------- VERDICT` marker and requires that marker to exist. A green from a
|
|
50
|
+
check nobody has watched fail is not evidence; this one was green for nine releases.
|
|
51
|
+
|
|
52
|
+
The template contract guards now iterate a `GATE_SCRIPTS` list instead of naming one
|
|
53
|
+
file, so a third gate costs one line rather than a copied block that drifts.
|
|
54
|
+
|
|
55
|
+
Negative self-tests: 68 → 75.
|
|
56
|
+
|
|
57
|
+
## v1.11.0 — 2026-08-04
|
|
58
|
+
|
|
59
|
+
### Added — run continuity: the pacing rules the operator was repeating by hand
|
|
60
|
+
|
|
61
|
+
Two rules that had to be said out loud at the start of every run. One of them was
|
|
62
|
+
already written down, which is the interesting half.
|
|
63
|
+
|
|
64
|
+
**The loop mode is now config, not a request.** `references/build.md` has carried
|
|
65
|
+
*"Continuous execution: don't check in between tasks"* since early on, and it did
|
|
66
|
+
not work — for two compounding reasons. It lived inside stage 5, so an agent
|
|
67
|
+
running any other stage never saw it; and on a harness where a turn ends, prose
|
|
68
|
+
cannot carry a run across the boundary at all. Only a scheduled re-invocation
|
|
69
|
+
can. So the decision moved to `pipeline.json` → `run.loop`, read at preflight in
|
|
70
|
+
the same block as the model: recorded means armed and never re-asked, absent means
|
|
71
|
+
**off**. `build.md`'s rule keeps its full force and gains one sentence naming its
|
|
72
|
+
scope, because the two govern different things and a reader was going to take one
|
|
73
|
+
for the other.
|
|
74
|
+
|
|
75
|
+
**The context rule fires on evidence or not at all.** The opposite failure was
|
|
76
|
+
live: runs announcing that context was nearly exhausted against a mostly-empty
|
|
77
|
+
window, because an estimate from transcript length was being presented as a
|
|
78
|
+
measurement. Nothing returns the remaining percentage, so the rule now admits
|
|
79
|
+
exactly two signals — the harness saying so, or the operator saying so — and
|
|
80
|
+
forbids the announcement without one. When it does fire: finish the item in
|
|
81
|
+
flight, start no new one, make the ledgers true, continue. Not stop.
|
|
82
|
+
|
|
83
|
+
There is deliberately **no config field** for the context half. The one number
|
|
84
|
+
anyone would put in it is the one that can never be honoured, and the schema's own
|
|
85
|
+
`description` says so — a blank space in a contract gets filled back in by the
|
|
86
|
+
next contributor as an oversight.
|
|
87
|
+
|
|
88
|
+
Both halves live in one new file, `references/continuity.md`, because they are one
|
|
89
|
+
mechanism: the loop is what makes the context rule necessary. Without a loop a run
|
|
90
|
+
stops at the turn boundary anyway; with one, it will start an item it cannot
|
|
91
|
+
finish.
|
|
92
|
+
|
|
93
|
+
### Fixed — three templates whose links were broken everywhere they are read
|
|
94
|
+
|
|
95
|
+
`templates/adr.md`, `carryover.md` and `routing-rule.md` carried relative links
|
|
96
|
+
that resolved from `templates/` — where the files are stored — and from nowhere
|
|
97
|
+
they are ever actually seeded. `carryover.md` had shipped one since v1.1.0 and the
|
|
98
|
+
link checker was green the whole time, because it resolves from the file's home.
|
|
99
|
+
No run had seeded the template verbatim, so nobody hit it until one did.
|
|
100
|
+
|
|
101
|
+
The rule is now the one the Cursor rule already follows: a document that travels
|
|
102
|
+
is self-contained, and names files in code spans rather than linking to them.
|
|
103
|
+
|
|
104
|
+
### Guards — 63 → 68
|
|
105
|
+
|
|
106
|
+
`run.loop.mode` must be set explicitly in the shipped example; five surfaces must
|
|
107
|
+
name `continuity.md`; the two load-bearing clauses in `continuity.md` must survive
|
|
108
|
+
an edit (matched after whitespace normalisation, since both wrap at 80 columns and
|
|
109
|
+
a line-oriented search would reject correct prose); and no seeded template may
|
|
110
|
+
carry a relative link. Four invariants added to `CONTRIBUTING.md`, each citing the
|
|
111
|
+
guard that enforces it.
|
|
112
|
+
|
|
113
|
+
`CLAUDE.md`'s claim of *sixteen* invariants — the list held twenty-four — was
|
|
114
|
+
deleted rather than corrected. A hand-written count drifts; that is what the rule
|
|
115
|
+
about computing numbers is for, and this file was breaking it.
|
|
116
|
+
|
|
3
117
|
## v1.10.3 — 2026-08-03
|
|
4
118
|
|
|
5
119
|
### Fixed — the cause behind five audits, rather than a sixth symptom
|
package/CONTRIBUTING.md
CHANGED
|
@@ -117,6 +117,13 @@ 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
|
+
|
|
120
127
|
**11. Every reference over 100 lines carries a `## Contents` list**, and the list is
|
|
121
128
|
compared against that file's own `##` headings. The guidance asks for it because a
|
|
122
129
|
long file gets previewed with a partial read; the comparison is because a hand-kept
|
|
@@ -169,7 +176,34 @@ with the bundle instead of being hand-installed into an operator's config.
|
|
|
169
176
|
reads those files, and a partial read shows whichever sections come first.
|
|
170
177
|
*(guard: `needs the same partial-read protection references get`)*
|
|
171
178
|
|
|
172
|
-
**24.
|
|
179
|
+
**24. Run-wide pacing is config, and the example demonstrates its default.** The
|
|
180
|
+
`run` block in `pipeline.schema.json` carries the loop mode; the shipped example
|
|
181
|
+
sets `run.loop.mode` explicitly rather than omitting it, because the example is
|
|
182
|
+
what gets copied and an absent field reads as an oversight instead of a decision.
|
|
183
|
+
*(guard: `no explicit run.loop.mode`)*
|
|
184
|
+
|
|
185
|
+
**25. The run-wide mode is named by every stage that could be misled by it.**
|
|
186
|
+
`SKILL.md`, `references/grill.md`, `references/build.md`, `references/stages.md`
|
|
187
|
+
and `templates/brief.md` each name `continuity.md`. The continuous-execution rule
|
|
188
|
+
sat inside `build.md` for nine releases and did not work, because an agent running
|
|
189
|
+
one stage never re-reads the orchestrator to discover a run-wide rule exists.
|
|
190
|
+
*(guard: `a run-wide rule no stage has heard of`)*
|
|
191
|
+
|
|
192
|
+
**26. `references/continuity.md` keeps its two load-bearing clauses.** One forbids
|
|
193
|
+
announcing that context is nearly spent without a harness signal; the other names
|
|
194
|
+
the harness limit on the loop primitive. Both are one edit away from softening into
|
|
195
|
+
nothing. The guard normalises whitespace first — the clauses wrap at 80 columns,
|
|
196
|
+
and a line-oriented search would reject correctly formatted prose.
|
|
197
|
+
*(guard: `missing the contractual clause`)*
|
|
198
|
+
|
|
199
|
+
**27. A seeded template carries no relative markdown links.** A template is copied
|
|
200
|
+
somewhere else by definition, so a link that resolves from `templates/` is broken
|
|
201
|
+
everywhere the file is actually read. `carryover.md` shipped one for nine minor
|
|
202
|
+
releases and the link checker stayed green throughout, because it resolves from the
|
|
203
|
+
file's home. Name the file in a code span instead — the same rule the Cursor rule
|
|
204
|
+
follows, for the same reason. *(guard: `resolves only from`)*
|
|
205
|
+
|
|
206
|
+
**28. Every invariant above names the guard that enforces it, and that guard exists.**
|
|
173
207
|
This list claims to be *what the validator enforces*; it was eight guards behind when
|
|
174
208
|
an audit measured it. A claim of enforcement is now checked like any other claim.
|
|
175
209
|
*(guard: `whose message does not appear in`)* — and a cited literal must lie inside
|
package/README.md
CHANGED
|
@@ -127,6 +127,7 @@ until it is installed.
|
|
|
127
127
|
| 10 Acceptance | [`acceptance.md`](plugins/task-pipeline/skills/task-pipeline/references/acceptance.md) — REQ coverage table, evidence rules, the closing question |
|
|
128
128
|
| 10 + any audit | [`audit.md`](plugins/task-pipeline/skills/task-pipeline/references/audit.md) — the L0→L7 ladder and its seams, axis rotation, ratchets, proven checks |
|
|
129
129
|
| any loop | [`loop-guard.md`](plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md) — churn detection, caps, the break protocol |
|
|
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) |
|
|
130
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 |
|
|
131
132
|
| 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 |
|
|
132
133
|
| 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 |
|
|
@@ -775,7 +776,7 @@ recommendation, so you arm the whole run in one exchange. Detail:
|
|
|
775
776
|
| [`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 |
|
|
776
777
|
| [`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 |
|
|
777
778
|
| [`references/model-tiering.md`](plugins/task-pipeline/skills/task-pipeline/references/model-tiering.md) | model policy, the `/model` reminder, overrides |
|
|
778
|
-
| [`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 |
|
|
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` and `hygiene.sh`, a worked hook |
|
|
779
780
|
| [`references/adoption.md`](plugins/task-pipeline/skills/task-pipeline/references/adoption.md) | the first run in a project: greenfield seeding, and the brownfield walkthrough |
|
|
780
781
|
| [`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 |
|
|
781
782
|
| [`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.12.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` — 76 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.** 76 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.
|
|
@@ -24,6 +24,47 @@ you recommend and what they're on, let them confirm or override (per-stage
|
|
|
24
24
|
overrides welcome), record the answer — then **stop asking at every stage**. If the
|
|
25
25
|
recommended tier isn't available, say which one you're using and continue.
|
|
26
26
|
|
|
27
|
+
## Pacing — the run mode, and the context budget
|
|
28
|
+
|
|
29
|
+
Two rules about how a run keeps going. Settle the first at stage 0, in the same
|
|
30
|
+
block as the model; the second is unconditional and needs no setting.
|
|
31
|
+
|
|
32
|
+
**The run mode.** On, the run advances one item per iteration and does not ask
|
|
33
|
+
between items — the plan's tasks, the boundary between stages whose gate is
|
|
34
|
+
automatic, the per-module loop, the acceptance tail. It stops for exactly four
|
|
35
|
+
things: a manual gate, a block it can't resolve, a genuine ambiguity, completion.
|
|
36
|
+
|
|
37
|
+
- **Off unless recorded.** Read the project's pipeline config → `run.loop`. A
|
|
38
|
+
recorded mode is the answer and is never re-asked; absent, the mode is **off**.
|
|
39
|
+
Silence arms nothing, the same floor deploy authorization uses.
|
|
40
|
+
- **It buys pacing, never authorization.** It never collapses a manual gate, and
|
|
41
|
+
never a deploy, publish, PR or shared-design-file write. A generic flag is not a
|
|
42
|
+
specific authorization.
|
|
43
|
+
- **Prose does not survive a turn boundary.** Where the harness offers a loop
|
|
44
|
+
primitive, arm it and say the interval, the job id and how to cancel; where it
|
|
45
|
+
does not, the mode degrades to discipline plus the build ledger — and you say
|
|
46
|
+
which of the two you are actually running rather than implying the stronger one.
|
|
47
|
+
- **Pick an interval longer than a typical item**, and one that divides its unit
|
|
48
|
+
cleanly. If the job auto-expires, say when: a loop that quietly stops is worse
|
|
49
|
+
than one never armed.
|
|
50
|
+
- **Parked at a manual gate, cancel the loop** and print the re-arm command beside
|
|
51
|
+
the gate. A loop firing into a gate every few minutes is a nag, and a nagged
|
|
52
|
+
operator stops reading.
|
|
53
|
+
|
|
54
|
+
**The context budget.** Near the end of the window: finish the item in flight,
|
|
55
|
+
**start no new one**, make the ledgers true, then continue. Compaction is a normal
|
|
56
|
+
event, not the end of the run — what loses work is crossing it mid-item.
|
|
57
|
+
|
|
58
|
+
- **It fires on evidence only:** a harness signal (a compaction warning, a
|
|
59
|
+
pre-compaction hook) or the user saying so. Nothing returns the remaining
|
|
60
|
+
percentage, so an estimate from transcript length is a guess — and announcing
|
|
61
|
+
that context is nearly spent without a signal is presenting a guess as a
|
|
62
|
+
measurement. A run that keeps volunteering it against a mostly-empty window
|
|
63
|
+
trains the user to disregard the one time it is true.
|
|
64
|
+
- **Flushing updates what already exists** — the build ledger, the carry-over
|
|
65
|
+
ledger, requirement statuses, the task list. Not a summary written for the
|
|
66
|
+
compactor: that is a fourth copy of the truth that nobody maintains.
|
|
67
|
+
|
|
27
68
|
## Stage 0 — intake grill (MANDATORY, do this FIRST)
|
|
28
69
|
|
|
29
70
|
Never skipped, and nothing to install — the grill is part of this rule. No "the
|
|
@@ -166,7 +207,15 @@ not authorize an outward, irreversible action — stage 7 stops and asks.
|
|
|
166
207
|
commit), then a review with **all three** verdicts — spec compliance, **REQ
|
|
167
208
|
satisfied** (judged against the requirement's own statement, not the task's
|
|
168
209
|
instructions), and code quality. Findings loop back to the implementer (max 5
|
|
169
|
-
rounds, then adjudicate in writing); minor findings are recorded, never blocking.
|
|
210
|
+
rounds, then adjudicate in writing); minor findings are recorded, never blocking.
|
|
211
|
+
**After each task reports done, before the review, scan what that task changed
|
|
212
|
+
for the defects an agent leaves behind** — a half-resolved merge, a stub that
|
|
213
|
+
outlived the task, a code fence left open, a file "shortened" while being
|
|
214
|
+
rewritten, a block duplicated by a retried edit, a section opened and abandoned.
|
|
215
|
+
Zero tolerance on a diff: that task wrote it, that task fixes it. A subagent's own
|
|
216
|
+
report is not evidence about the text it left behind, and a duplicated block eight
|
|
217
|
+
tasks later is repaired by an agent that no longer remembers the code.
|
|
218
|
+
Keep a progress ledger in
|
|
170
219
|
a file so a lost context can resume instead of redoing finished work. Tasks in
|
|
171
220
|
the same parallel group run concurrently **only** if each worker has its own
|
|
172
221
|
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 76 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.12.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.12.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ssheleg",
|
|
8
8
|
"url": "https://x.com/sshlg93"
|
|
@@ -65,6 +65,7 @@ gate stops until it is installed.
|
|
|
65
65
|
| **first run · the entry audit** (offered once) | [`references/setup.md`](references/setup.md) |
|
|
66
66
|
| **what travels with the bundle vs stays in a project** | [`references/portability.md`](references/portability.md) |
|
|
67
67
|
| any repeating loop | [`references/loop-guard.md`](references/loop-guard.md) |
|
|
68
|
+
| run-wide · how a run keeps going (the loop mode + the context budget) | [`references/continuity.md`](references/continuity.md) |
|
|
68
69
|
|
|
69
70
|
**Optional bridge.** If the operator already runs an equivalent skill set (e.g.
|
|
70
71
|
`superpowers:brainstorming` / `writing-plans` / `subagent-driven-development` /
|
|
@@ -181,7 +182,12 @@ Three things the grill does beyond clarifying the request:
|
|
|
181
182
|
tasks, context7, wiki-update, graphify) and emits ONE block covering them
|
|
182
183
|
**and the model decision** (`references/model-tiering.md`): recommend
|
|
183
184
|
the most capable model available, let the operator confirm or override, record
|
|
184
|
-
it. Ask once, here.
|
|
185
|
+
it. Ask once, here. **The same block carries the run mode**
|
|
186
|
+
(`references/continuity.md`): read `pipeline.json` → `run.loop`; where it is
|
|
187
|
+
recorded, arm it and print the job id and the cancel command — the config is
|
|
188
|
+
the authorization, so re-asking rebuilds the habit it exists to retire. Where
|
|
189
|
+
it is **absent, the mode is off**; recommend it in one line and move on.
|
|
190
|
+
Silence arms nothing, and the mode never collapses a `manual` gate.
|
|
185
191
|
2. **Run stage 0 — always, no exceptions.** It opens with the **knowledge harvest**
|
|
186
192
|
(`references/knowledge-sources.md`): query the project's own sources — repo docs,
|
|
187
193
|
ADRs, `docs/ux/`, past briefs, the wiki if installed, any doc repo the project
|
|
@@ -341,6 +347,10 @@ automation is on — `pipeline.schema.json` is the only contract.
|
|
|
341
347
|
inside the bundle, and the boundary against a project's own answers
|
|
342
348
|
- `references/adoption.md` — the first run in a project: greenfield seeding, and the
|
|
343
349
|
brownfield walkthrough whose third step baselines the ratchets at today
|
|
350
|
+
- `references/continuity.md` — run-wide pacing: the loop mode (`run.loop`, default
|
|
351
|
+
off, and it never collapses a manual gate), how it is armed on Claude Code and
|
|
352
|
+
what that costs, and the context budget — which fires on a harness signal, never
|
|
353
|
+
on an estimate
|
|
344
354
|
- `references/conventions.md` — how stages 6–10 read the host project's CLAUDE.md
|
|
345
355
|
- `references/companion-skills.md` — companion skills, install lines, preflight recommendation
|
|
346
356
|
- `references/artifacts.md` — the canonical document/artifact layout per stage
|
|
@@ -170,6 +170,10 @@
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
],
|
|
173
|
+
"_run_note": "Run-wide pacing. Shipped explicitly OFF so this example DEMONSTRATES the default rather than relying on its absence — see references/continuity.md. Set mode to 'interval' and a project stops being asked whether to run item-by-item; it still stops at every manual gate. There is no context-budget field here on purpose: the threshold is not measurable, so it is unconditional doctrine, not config.",
|
|
174
|
+
"run": {
|
|
175
|
+
"loop": { "mode": "off", "interval": "5m", "command": "/loop" }
|
|
176
|
+
},
|
|
173
177
|
"release": {
|
|
174
178
|
"enabled": true,
|
|
175
179
|
"trigger": "tag",
|
|
@@ -14,9 +14,38 @@
|
|
|
14
14
|
"description": "The pipeline stages, in order. Any number, any names — your project's real stages.",
|
|
15
15
|
"items": { "$ref": "#/definitions/stage" }
|
|
16
16
|
},
|
|
17
|
-
"release": { "$ref": "#/definitions/release" }
|
|
17
|
+
"release": { "$ref": "#/definitions/release" },
|
|
18
|
+
"run": { "$ref": "#/definitions/run" }
|
|
18
19
|
},
|
|
19
20
|
"definitions": {
|
|
21
|
+
"run": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"additionalProperties": true,
|
|
24
|
+
"description": "Optional run-wide pacing. Omit the object and every field defaults OFF: silence arms nothing and authorises nothing — the same floor deploy authorization uses. There is deliberately NO context-budget field: the remaining context is not measurable from inside a run, so a field accepting a percentage would validate a number that can never be honoured. That rule is unconditional doctrine in references/continuity.md instead.",
|
|
25
|
+
"properties": {
|
|
26
|
+
"loop": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"required": ["mode"],
|
|
29
|
+
"additionalProperties": true,
|
|
30
|
+
"description": "Whether the run advances item by item without a discretionary check-in. It NEVER collapses a manual gate or an outward action — a generic flag is not a specific authorization.",
|
|
31
|
+
"properties": {
|
|
32
|
+
"mode": {
|
|
33
|
+
"enum": ["off", "interval"],
|
|
34
|
+
"description": "off (the default when absent) = the run pauses between items as it always did. interval = the run is armed with the harness's own loop primitive and advances one item per fire, stopping only at a manual gate, an unresolvable block, a genuine ambiguity, or completion."
|
|
35
|
+
},
|
|
36
|
+
"interval": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"pattern": "^[0-9]+[smhd]$",
|
|
39
|
+
"description": "Required when mode is 'interval'. Must divide its unit cleanly (5m, 10m, 2h — not 7m or 90m); a value that does not is rounded to the nearest that does, and the rounding is stated out loud. Pick the shortest interval that is longer than a typical item."
|
|
40
|
+
},
|
|
41
|
+
"command": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"description": "How this harness arms it, e.g. '/loop'. Harness-specific and therefore project-recorded rather than assumed: on a harness with no loop primitive, omit it — the mode then degrades to prose discipline plus the build ledger, and the run says so instead of implying it is armed."
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
20
49
|
"release": {
|
|
21
50
|
"type": "object",
|
|
22
51
|
"additionalProperties": true,
|
|
@@ -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)
|
|
@@ -18,6 +18,15 @@ session's history, so it stays focused; your context stays free for coordination
|
|
|
18
18
|
plan to be executed — execute it. Stop only for BLOCKED you can't resolve, a
|
|
19
19
|
genuine ambiguity, or completion. "Should I continue?" between tasks is noise.
|
|
20
20
|
|
|
21
|
+
**Scope, so this is not mistaken for the run-wide mode:** the rule above governs
|
|
22
|
+
the *inside* of this stage and is unconditional — it holds whether or not a loop
|
|
23
|
+
is armed. What it cannot do is reach the other nine stages, or survive the
|
|
24
|
+
boundary between one agent turn and the next; on a harness where prose does not
|
|
25
|
+
carry across that boundary, only a scheduled re-invocation does.
|
|
26
|
+
[`continuity.md`](continuity.md) owns that half, is recorded in `pipeline.json` →
|
|
27
|
+
`run.loop`, and is **off unless recorded**. The two never disagree: one is a
|
|
28
|
+
stage's internal discipline, the other is the run's pacing.
|
|
29
|
+
|
|
21
30
|
**Narration:** at most one short line between tool calls. The ledger and the tool
|
|
22
31
|
results are the record.
|
|
23
32
|
|
|
@@ -238,11 +247,35 @@ physical: **two implementers writing one working tree corrupt each other's state
|
|
|
238
247
|
|
|
239
248
|
| Status | Action |
|
|
240
249
|
|---|---|
|
|
241
|
-
| `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)). |
|
|
242
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. |
|
|
243
252
|
| `NEEDS_CONTEXT` | Supply exactly what's missing, re-dispatch. |
|
|
244
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. |
|
|
245
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
|
+
|
|
246
279
|
**Never** ignore an escalation, and never re-dispatch the same model with the same
|
|
247
280
|
prompt after a BLOCKED. If the implementer says it's stuck, something must change.
|
|
248
281
|
If the implementer asks a question — before or mid-task — answer it completely; do
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Continuity — how a run keeps going
|
|
2
|
+
|
|
3
|
+
Two rules about pacing, in one file because they are two halves of one mechanism.
|
|
4
|
+
The loop is what lets a run walk a task list without asking permission between
|
|
5
|
+
items. The context budget is what stops that loop from starting an item it cannot
|
|
6
|
+
finish. **Neither is useful without the other**, and a run that has the first
|
|
7
|
+
without the second fails in a specific, expensive way: it begins task nine with
|
|
8
|
+
almost no window left, loses the middle of it, and re-derives what it already did.
|
|
9
|
+
|
|
10
|
+
## Contents
|
|
11
|
+
|
|
12
|
+
- The limit, before the capability
|
|
13
|
+
- Part 1 — the loop
|
|
14
|
+
- Arming it on Claude Code
|
|
15
|
+
- Other harnesses, and honest degradation
|
|
16
|
+
- What one iteration means
|
|
17
|
+
- Parked at a manual gate
|
|
18
|
+
- Part 2 — the context budget
|
|
19
|
+
- The evidence rule
|
|
20
|
+
- What happens at the signal
|
|
21
|
+
- The flush is not a new document
|
|
22
|
+
- Rationalizations
|
|
23
|
+
|
|
24
|
+
## The limit, before the capability
|
|
25
|
+
|
|
26
|
+
**The loop mode collapses discretionary check-ins. It collapses nothing else.**
|
|
27
|
+
|
|
28
|
+
- A `manual` gate still waits for an explicit operator go. Always.
|
|
29
|
+
- An outward or irreversible act — deploy, publish, opening a PR, creating or
|
|
30
|
+
editing a shared design file — still needs its own authorization. A recorded
|
|
31
|
+
loop mode is a **generic** flag, and a generic flag is not a specific
|
|
32
|
+
authorization; that floor is set in [`grill.md`](grill.md) and this file does
|
|
33
|
+
not lower it.
|
|
34
|
+
- **Default off.** Absent config and absent instruction, the mode is off. Silence
|
|
35
|
+
arms nothing, exactly as silence authorizes no deploy.
|
|
36
|
+
|
|
37
|
+
The mode's whole content is the removal of *"should I continue?"* between items
|
|
38
|
+
the operator already asked for. Anything beyond that is a different decision,
|
|
39
|
+
taken separately.
|
|
40
|
+
|
|
41
|
+
## Part 1 — the loop
|
|
42
|
+
|
|
43
|
+
**On, the run advances one item per iteration and does not ask between items.**
|
|
44
|
+
This spans the whole run, not one stage:
|
|
45
|
+
|
|
46
|
+
- the plan's tasks at stage 5;
|
|
47
|
+
- the boundary between stages whose gate is `auto`;
|
|
48
|
+
- the per-module program loop when the brief was a platform;
|
|
49
|
+
- the acceptance → retro tail, which is where runs most often stall one step from
|
|
50
|
+
finished.
|
|
51
|
+
|
|
52
|
+
It stops for exactly four things: a `manual` gate, a block it cannot resolve, a
|
|
53
|
+
genuine ambiguity the brief does not answer, and completion.
|
|
54
|
+
|
|
55
|
+
**This extends [`build.md`](build.md); it does not replace it.** That file's
|
|
56
|
+
*Continuous execution* rule governs the inside of a stage-5 execution — do not
|
|
57
|
+
check in between the tasks of a plan you were told to execute. It is unconditional
|
|
58
|
+
and stays that way. What it cannot do is reach the other nine stages, or survive
|
|
59
|
+
the boundary between one agent turn and the next. This file is that reach.
|
|
60
|
+
|
|
61
|
+
**Where it is recorded:** `pipeline.json` → `run.loop`. A project sets it once and
|
|
62
|
+
is never asked again — which is the entire point. A mode that has to be requested
|
|
63
|
+
every run is not a mode, it is a habit the operator maintains by hand.
|
|
64
|
+
|
|
65
|
+
## Arming it on Claude Code
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
/loop <interval> <the invocation that continues the run>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The interval **must divide its unit cleanly** — `5m`, `10m`, `2h` are fine; `7m`
|
|
72
|
+
gives uneven gaps and `90m` cannot be expressed at all. A value that does not
|
|
73
|
+
divide cleanly is rounded to the nearest one that does, **and the rounding is said
|
|
74
|
+
out loud** before it is used.
|
|
75
|
+
|
|
76
|
+
Pick **the shortest interval that is longer than a typical item.** Shorter buys
|
|
77
|
+
nothing: the scheduler cannot deliver faster than the item finishes.
|
|
78
|
+
|
|
79
|
+
Two properties of the armed job must be stated when arming it, because both are
|
|
80
|
+
silent failures otherwise:
|
|
81
|
+
|
|
82
|
+
- **The job is session-only.** It is not written to disk and it dies with the
|
|
83
|
+
session.
|
|
84
|
+
- **It auto-expires after seven days**, firing one last time. A loop that quietly
|
|
85
|
+
stops on day eight is worse than one that was never armed, because the operator
|
|
86
|
+
believes work is still moving.
|
|
87
|
+
|
|
88
|
+
Where `run.loop.mode` is `interval`, the **preflight arms it** and prints the job
|
|
89
|
+
id and the cancel command. Arming is not a new decision at that point — the config
|
|
90
|
+
is the recorded authorization, and re-asking would rebuild the habit the config
|
|
91
|
+
exists to retire.
|
|
92
|
+
|
|
93
|
+
## Other harnesses, and honest degradation
|
|
94
|
+
|
|
95
|
+
`/loop` is **Claude Code only**, in the same way the `PreToolUse` contract in
|
|
96
|
+
[`hooks.md`](hooks.md) is. This file names the limit rather than pretending the
|
|
97
|
+
mechanism is universal.
|
|
98
|
+
|
|
99
|
+
On a harness with no loop primitive, the mode degrades to what prose can do: the
|
|
100
|
+
discipline in [`build.md`](build.md) plus the build ledger, which is genuinely
|
|
101
|
+
useful and genuinely weaker. **Say which one you are running.** A run that reports
|
|
102
|
+
itself as looping while nothing is scheduled is claiming a capability it does not
|
|
103
|
+
have, and the operator finds out by discovering that nothing happened.
|
|
104
|
+
|
|
105
|
+
## What one iteration means
|
|
106
|
+
|
|
107
|
+
One item, taken to its gate.
|
|
108
|
+
|
|
109
|
+
**A fixed interval cannot interrupt an unfinished item.** The scheduler enqueues
|
|
110
|
+
only while the harness is idle — never mid-query. This is the reason the interval
|
|
111
|
+
form is safe, and it is worth stating precisely, because the plausible-sounding
|
|
112
|
+
alternative explanation is wrong.
|
|
113
|
+
|
|
114
|
+
**The build ledger is the second line, not the first.** It covers a different
|
|
115
|
+
case: a fire that lands after a context loss, where the controller no longer
|
|
116
|
+
remembers what it finished. There, `Task <N>: complete` in
|
|
117
|
+
`.task-pipeline/build/<plan>/progress.md` is the only DONE marker that counts, and
|
|
118
|
+
a task carrying one is never re-dispatched ([`build.md`](build.md)).
|
|
119
|
+
|
|
120
|
+
Do not write, and do not believe, that the ledger is what makes the interval safe.
|
|
121
|
+
Someone will eventually remove the ledger on the strength of that sentence, and
|
|
122
|
+
the protection they think they are keeping is in the scheduler.
|
|
123
|
+
|
|
124
|
+
## Parked at a manual gate
|
|
125
|
+
|
|
126
|
+
A fixed interval firing into a `manual` gate is a nag. Five minutes later it fires
|
|
127
|
+
again, and again, and the operator — who is reading the thing the gate asked them
|
|
128
|
+
to read — learns to ignore the loop. That is the same failure as a rule nobody
|
|
129
|
+
reads to the end: everyone believes it is covered.
|
|
130
|
+
|
|
131
|
+
**So the run cancels its own loop job when it parks**, and prints the re-arm
|
|
132
|
+
command beside the gate. The go and the re-arm are then one act. This is also the
|
|
133
|
+
honest accounting: while the run waits for a person, nothing is looping, and the
|
|
134
|
+
job list should agree with that.
|
|
135
|
+
|
|
136
|
+
**Verify the cancel by listing the jobs, never by the cancel's own reply.** A
|
|
137
|
+
teardown call will happily accept an id that was never scheduled and report
|
|
138
|
+
success, which leaves the real job running while the transcript says it stopped.
|
|
139
|
+
List afterwards and read the list. This is the same law the rest of this pipeline
|
|
140
|
+
applies to every gate — a green nobody watched is not evidence — and a loop is
|
|
141
|
+
exactly where breaking it goes unnoticed, because the symptom is a message
|
|
142
|
+
arriving on time.
|
|
143
|
+
|
|
144
|
+
## Part 2 — the context budget
|
|
145
|
+
|
|
146
|
+
Near the end of the context window, the run does **not** stop and it does not
|
|
147
|
+
start anything new:
|
|
148
|
+
|
|
149
|
+
1. **Finish the item in flight.** A half-finished item is the expensive state.
|
|
150
|
+
2. **Start no new item.** An item begun in this window loses its own middle.
|
|
151
|
+
3. **Make the ledgers true** (below).
|
|
152
|
+
4. **Continue.** Compaction is a normal event, not the end of the run.
|
|
153
|
+
|
|
154
|
+
The failure this prevents is not running out of room — that is survivable. It is
|
|
155
|
+
crossing the boundary *in the middle of something*, so the work either vanishes or
|
|
156
|
+
gets done twice.
|
|
157
|
+
|
|
158
|
+
## The evidence rule
|
|
159
|
+
|
|
160
|
+
**The rule fires only on evidence**, and there are exactly two admissible kinds:
|
|
161
|
+
|
|
162
|
+
- **A signal from the harness** — a warning that compaction is near, a `PreCompact`
|
|
163
|
+
hook firing, an explicit low-context notice.
|
|
164
|
+
- **The operator saying so.**
|
|
165
|
+
|
|
166
|
+
Absent both, the threshold has not been observed, and you **never announce that
|
|
167
|
+
the context is nearly spent without one of those signals**. No tool returns the
|
|
168
|
+
remaining percentage. An estimate from transcript length or how much has been read
|
|
169
|
+
is a guess, and presenting a guess as a measurement is precisely the failure
|
|
170
|
+
[`learned.md`](learned.md) names — compute, never restate.
|
|
171
|
+
|
|
172
|
+
This cuts both ways, and the second direction is the one that actually goes wrong:
|
|
173
|
+
a run that keeps volunteering *"context is nearly exhausted, start a new session"*
|
|
174
|
+
against a mostly-empty window trains the operator to disregard the one time it is
|
|
175
|
+
true.
|
|
176
|
+
|
|
177
|
+
## What happens at the signal
|
|
178
|
+
|
|
179
|
+
The four acts above, in order, and one prohibition: **do not begin an item you
|
|
180
|
+
cannot finish inside what is left.** When the next item is plainly too large,
|
|
181
|
+
say so and take the smaller thing that fits — closing a ledger, writing a decision
|
|
182
|
+
that is already settled, updating a REQ status.
|
|
183
|
+
|
|
184
|
+
If the project has recorded that it must not cross a compaction mid-stage, stop at
|
|
185
|
+
the stage boundary instead and hand off. That is a project answer, not a default:
|
|
186
|
+
the default is to continue.
|
|
187
|
+
|
|
188
|
+
## The flush is not a new document
|
|
189
|
+
|
|
190
|
+
Flushing means **making the artifacts that already exist true**:
|
|
191
|
+
|
|
192
|
+
- the build ledger — every completed task carrying its `complete` line;
|
|
193
|
+
- the carry-over ledger — everything deferred, dropped or half-done;
|
|
194
|
+
- the brief's REQ statuses;
|
|
195
|
+
- the task list.
|
|
196
|
+
|
|
197
|
+
It does **not** mean writing a summary for the compactor. A summary is a fourth
|
|
198
|
+
copy of the truth, it is written once, nobody updates it, and the next run reads
|
|
199
|
+
it as current. The artifacts above are read by later stages anyway; making them
|
|
200
|
+
right costs nothing extra and pays twice.
|
|
201
|
+
|
|
202
|
+
## Rationalizations
|
|
203
|
+
|
|
204
|
+
| Excuse | Reality |
|
|
205
|
+
|---|---|
|
|
206
|
+
| "The operator obviously wants it to keep going, I'll just loop" | Default off. Silence arms nothing — the same floor deploy authorization uses. Ask once at preflight, or read `run.loop`. |
|
|
207
|
+
| "The mode is on, so I can push the deploy through" | The mode collapses check-ins, never gates or outward acts. A generic flag is not a specific authorization. |
|
|
208
|
+
| "I'll tell it to keep going and it will" | On Claude Code, prose does not survive the turn boundary. Either a job is scheduled or the run stops; say which. |
|
|
209
|
+
| "A one-minute interval is closest to no pause" | The interval must be longer than a typical item, or every fire lands on work already in progress and buys nothing. |
|
|
210
|
+
| "The ledger protects me from mid-task fires" | The scheduler not firing mid-query protects you. The ledger covers context loss. Confusing them is how the real protection gets deleted. |
|
|
211
|
+
| "It's still looping while we wait for approval" | A loop firing into a manual gate is a nag, and a nagged operator stops reading. Cancel on parking, re-arm with the go. |
|
|
212
|
+
| "Context feels tight, I should warn them" | Feels is not evidence. Without a harness signal or the operator's word, the warning is a guess wearing the clothes of a measurement. |
|
|
213
|
+
| "I'll write a handoff summary before compaction" | Update the ledgers instead. A summary is a copy of the truth that nobody maintains. |
|
|
214
|
+
| "One more task will fit" | If it does not fit, its middle is what you lose — and half a task is the most expensive state there is. |
|
|
@@ -140,6 +140,7 @@ explicit "stop and ask me here":
|
|
|
140
140
|
| Stage | What to settle up front |
|
|
141
141
|
|---|---|
|
|
142
142
|
| run-wide | the model decision ([`model-tiering.md`](model-tiering.md)); what to decide autonomously vs escalate |
|
|
143
|
+
| run-wide Pacing | the **run mode** ([`continuity.md`](continuity.md)): does the run advance item-by-item with no check-in between items, and on what interval? Read `pipeline.json` → `run.loop` first — a recorded mode is the answer and is not re-asked. **Absent, it is off**: recommend it, take the answer, record it. It never collapses a `manual` gate or an outward act, so this row buys pacing, not authorization |
|
|
143
144
|
| 0 Harvest | doc sources beyond this repo — other repos, hosted doc systems, the knowledge wiki, **the code graph** ([`knowledge-graph.md`](knowledge-graph.md): built / installed-not-built / absent) — and whether stage 9 may write to them (another repo is outward: propose + PR, never a direct push) |
|
|
144
145
|
| 0 Setup audit | doc map absent or stale: run the entry audit over the existing documentation before building on it ([`setup.md`](setup.md))? Asked once; a refusal is recorded and never re-asked |
|
|
145
146
|
| run-wide Escalation | cost of being wrong: decide alone while it stays inside the repository and reversible; escalate price, legal posture, promise, money, reputation, irreversible outward acts. Project exceptions? |
|
|
@@ -51,10 +51,13 @@ a row pointing outside the bundle is the defect this file exists to catch.
|
|
|
51
51
|
| The entry audit and what it inspects | `references/setup.md` |
|
|
52
52
|
| The ladder, seams, axis rotation, ratchets | `references/audit.md` |
|
|
53
53
|
| Loop detection and its caps | `references/loop-guard.md` |
|
|
54
|
+
| **The run mode** — item-by-item pacing, default off, what it never collapses | `references/continuity.md` |
|
|
55
|
+
| **The context budget** — the evidence rule and what a flush actually updates | `references/continuity.md` |
|
|
54
56
|
| The retro: prune, cap, commits, archive | `references/retrospective.md` |
|
|
55
57
|
| Rules earned by failure | `references/learned.md` |
|
|
56
58
|
| **The routing default and its boundary** | `templates/routing-rule.md` |
|
|
57
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` |
|
|
58
61
|
| What a stage reads, and which host files bind it | `references/artifacts.md` |
|
|
59
62
|
| The design conversation, its hard gate, UI detection, user paths | `references/brainstorm.md` |
|
|
60
63
|
| Cutting a platform into modules, brick criteria, build order | `references/decomposition.md` |
|
|
@@ -131,6 +131,13 @@ never that the work was skipped quietly.
|
|
|
131
131
|
calls) and the **autonomy sweep** that pre-resolves every stage-1→10 blocker.
|
|
132
132
|
Deploy authorization has a hard floor there: a standing go counts only when it
|
|
133
133
|
names the target and the preconditions.
|
|
134
|
+
- **The run mode is settled here too** ([`continuity.md`](continuity.md)): does
|
|
135
|
+
the run advance item-by-item with no check-in between items, and on what
|
|
136
|
+
interval? Read `pipeline.json` → `run.loop` first — a recorded mode is the
|
|
137
|
+
answer and is not re-asked; **absent, it is off**. It buys pacing, never
|
|
138
|
+
authorization: no loop mode collapses a `manual` gate or an outward act. The
|
|
139
|
+
same file carries the other half of pacing — the context budget, which fires
|
|
140
|
+
only on a harness signal or the operator's word, never on an estimate.
|
|
134
141
|
- **UI early-detect:** one branch of the grill is always "does this touch a
|
|
135
142
|
user-facing surface (web/mobile/CLI/TUI)?". If yes → surface **super-ux**
|
|
136
143
|
now (use it if installed; otherwise give the install line — see SKILL.md
|
|
@@ -289,7 +296,9 @@ never that the work was skipped quietly.
|
|
|
289
296
|
on the result, land it the project's way (merge, or a PR — outward, so it needs a
|
|
290
297
|
go), remove the worktree. Stages 7–9 act on the integrated result, so a branch the
|
|
291
298
|
operator chose to leave unmerged is recorded as such.
|
|
292
|
-
- **GATE (auto):**
|
|
299
|
+
- **GATE (auto):** **the hygiene gate green in diff mode after every task**
|
|
300
|
+
(`references/build.md`) — six checks over what that task changed, no floor, and a
|
|
301
|
+
finding fixed in-task or carried over with a reason; all plan tasks DONE (three review verdicts per task: spec
|
|
293
302
|
compliance, **REQ satisfied**, code quality); every finding fixed or parked with a
|
|
294
303
|
ruling; **every parked finding and implementer concern harvested into the
|
|
295
304
|
carry-over ledger** — nothing stays only in the scratch workspace, which is
|
|
@@ -304,7 +313,8 @@ never that the work was skipped quietly.
|
|
|
304
313
|
- **Invoke:** the host test runner (see `conventions.md` → *Lint + test*); the
|
|
305
314
|
built-in [`tdd.md`](tdd.md) cycle for any uncovered gap — failing test first,
|
|
306
315
|
same as stage 5.
|
|
307
|
-
- **GATE (auto):** the
|
|
316
|
+
- **GATE (auto):** **the hygiene gate green over the whole tree**, its six counts
|
|
317
|
+
printed beside their floors; the **full** suite is green (not just the new tests); new/changed code
|
|
308
318
|
is covered; no `skip`/`xfail` smuggling a red suite past the gate. Never advance
|
|
309
319
|
to deploy on a red or partial run. **The carry-over count is printed beside this
|
|
310
320
|
verdict** — a ratchet nobody prints is a TODO with a better name
|
|
@@ -374,7 +384,7 @@ never that the work was skipped quietly.
|
|
|
374
384
|
- **Docs living in another repository** are outward: propose the edit, get an
|
|
375
385
|
explicit go, then open a PR there. No go → the exact edit goes in the carry-over
|
|
376
386
|
ledger.
|
|
377
|
-
- **GATE (auto):** **the propagation matrix walked for every change type this run
|
|
387
|
+
- **GATE (auto):** **the hygiene gate green**, counts printed; **the propagation matrix walked for every change type this run
|
|
378
388
|
produced**, with every settled thing recorded under an id and every answered
|
|
379
389
|
question resolved; **the documentation gate green, its ratchet counts printed and
|
|
380
390
|
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 |
|
|
@@ -21,7 +21,7 @@ decision was made and *why* — not filling out sections.
|
|
|
21
21
|
## When this directory IS the register
|
|
22
22
|
|
|
23
23
|
An ADR set and `docs/DECISIONS.md` are **two shapes of one decision home**, and a
|
|
24
|
-
project has exactly one (
|
|
24
|
+
project has exactly one (the skill's own `references/documentation.md`).
|
|
25
25
|
If `docs/adr/` already holds an `NNNN-*.md`, that is the register — record it in
|
|
26
26
|
`docs/DOCMAP.md` and never seed a second home beside it.
|
|
27
27
|
|
|
@@ -117,6 +117,7 @@ is not neutral — it is a scheduled interruption.
|
|
|
117
117
|
| run-wide | Model for this run | … (most capable available unless overridden; per-stage overrides here) |
|
|
118
118
|
| run-wide Escalation | … | cost of being wrong: decide alone while it stays inside the repository and reversible; escalate price, legal posture, promise, money, reputation, irreversible outward acts. Project exceptions? |
|
|
119
119
|
| run-wide | Decide autonomously vs escalate to me | … |
|
|
120
|
+
| run-wide Pacing | Run mode: item-by-item with no check-in between items, and on what interval? (the skill's `references/continuity.md`; read `pipeline.json` → `run.loop` first) | … (**absent ⇒ off**; it never collapses a manual gate or an outward act) |
|
|
120
121
|
| 0 Harvest | Doc sources beyond this repo — other repos, hosted docs, the knowledge wiki, the code graph; and may stage 9 write to them? | … (another repo is outward: propose + PR, never a direct push; graph built / not built) |
|
|
121
122
|
| 0 Setup audit | … (yes / no — recorded either way) | doc map absent or stale: run the entry audit over the existing documentation before building on it (the skill's `references/setup.md`)? Asked once; a refusal is recorded and never re-asked |
|
|
122
123
|
| 0 Docs regime | Where settled things live (register or ADR set — one home, never both); who may write it; lease mechanism present, or is this run `ungated`? Gate command + ratchet floors; may this run raise a floor? | … |
|
|
@@ -47,7 +47,7 @@ is the true statement.
|
|
|
47
47
|
- **The set may only shrink.** If it grew, the run log gets one sentence saying why.
|
|
48
48
|
A ratchet that grows silently is a TODO with a better name.
|
|
49
49
|
- **A finding class that appears twice stops belonging here** and becomes a check in
|
|
50
|
-
the host's lint or CI (
|
|
50
|
+
the host's lint or CI (the skill's own `references/audit.md` → *A class that
|
|
51
51
|
repeats twice becomes a gate*). This ledger is for what cannot be automated, not
|
|
52
52
|
for what nobody automated.
|
|
53
53
|
|
|
@@ -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
|
|
|
@@ -8,7 +8,7 @@ operator's configuration silently.
|
|
|
8
8
|
raises the odds the model selects it and cannot make selection mandatory — the choice
|
|
9
9
|
stays with the model, case by case. Only an instruction makes routing binding. That
|
|
10
10
|
instruction therefore has to be *installed*, which is exactly how a workflow decision
|
|
11
|
-
ends up living outside the bundle (
|
|
11
|
+
ends up living outside the bundle (the skill's own `references/portability.md`).
|
|
12
12
|
Shipping it as a template is what lets it travel.
|
|
13
13
|
|
|
14
14
|
**Keep the exclusions identical** to the skill's `description` and to the
|