task-pipeline-skill 1.28.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,211 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.30.0 — the cap that would have measured the wrong axis
4
+
5
+ The audit's last finding was that `references/learned.md` has no retirement rule while
6
+ `retro.md` caps its standing instructions at ten. Four measurements, each with a
7
+ differently-shaped command — the rotation axis shipped one release ago, turned on the
8
+ backlog that proposed it:
9
+
10
+ | The belief | The command | Result |
11
+ |---|---|---|
12
+ | rules accumulate | table rows per tag: 15 → 18 → 21 → 21 | **flat for four releases** |
13
+ | growth *is* rules | words per section, v1.23.0 → HEAD | **+223, every word in the binding map** |
14
+ | the long incidents duplicate the retro | each incident's distinctive tokens against the whole retro corpus | **zero hits** — they are other projects' events, held nowhere else here |
15
+
16
+ A cap of N would have squeezed the axis that had not moved in four releases. A word
17
+ budget would have cut the incidents, which are the only record of those events anywhere
18
+ in this repository. **The finding was right that something was missing and wrong about
19
+ what.**
20
+
21
+ **Why the retro is capped and this file is not**, now written down where the proposal
22
+ keeps arriving: a cap belongs to a file you must *finish reading*. The retro's standing
23
+ instructions are read in full at stage 0 — bounded by construction, or the last one is
24
+ never reached. `learned.md` is entered by citation from twenty-three surfaces and its
25
+ binding map is that entrance. **A file you enter through an index needs its index to be
26
+ right, not its length to be short.**
27
+
28
+ **Two triggers retire a rule, and neither is a count:** the conditions can no longer
29
+ occur in any project the skill runs on, or the rule is **subsumed** — a merge, where the
30
+ absorbing rule names the absorbed one and every binding-map row is repointed in the same
31
+ change. Explicitly *not* "it became a check": that trigger is right for a standing
32
+ instruction, whose job is to be read until the machine takes over, but here the rule is
33
+ the **reason** the check exists, and a check whose reason was deleted is the next thing
34
+ somebody removes as noise.
35
+
36
+ Numbers are never reused and never closed up, so a departure shows as a **gap**, and a
37
+ gap the `### Retired` log does not name now fails the build. The log exists while empty
38
+ and says so — an absent log and an empty one are indistinguishable from outside, and
39
+ only one of them means nothing has been retired.
40
+
41
+ And the file's **shape** is now printed beside the validator's verdict — rules,
42
+ incidents, incident words, binding rows — as a disclosure: computed, no floor, no
43
+ direction, **never a target**. Growth becomes visible without becoming a lever.
44
+
45
+ **The review found a guard that could not catch the thing it was built for.** Deleting
46
+ the **highest-numbered** rule shrinks `max()` with it, so no gap ever opens — reproduced
47
+ by deleting rule 21 and getting a clean pass. My own probe had planted in the middle of
48
+ the list, which is the shape the author imagines. The mark now comes from the file
49
+ (`Numbers issued so far: N`, the one number this file states about itself), and the log
50
+ match is anchored to each line's leading number, because a bare digit anywhere in the
51
+ body — *"subsumed by 9"* — masked a genuine deletion of rule 9.
52
+
53
+ The disclosure was also wrong before it was right: one write-up covers two rules
54
+ (*"4 and 5 · Probes"*), so it printed 18 where the file has 19 write-ups covering 20
55
+ rules. It now says **rules with an incident**, because a label that does not say what
56
+ it counts is a number nobody can check.
57
+
58
+ **Swept the same class one level out.** The cold-retirement guard's corpus was a
59
+ hand-written list of seven files. Made **discovered** instead — any shipped surface that
60
+ states the condition must state both units — and it immediately found **thirteen**,
61
+ including `README.md`, the Cursor rule, `gates.md` and `learned.md` itself. A narration
62
+ of the old wording is exempt by the repository's existing convention: a double-quoted
63
+ span is a citation, and rule 21's incident is left as it happened.
64
+
65
+ **The class turned out to be the module.** Three guards in this file held
66
+ hand-written corpora; all three had missed a shipped surface, and each miss was found
67
+ by a reader or by a sweep rather than by the guard. All three are now **discovered**:
68
+ the cold trigger (7 listed → 13 found), the disclosure verdicts (5 listed → README's
69
+ worked `GATE 10` block found), and the claim registry (widened in v1.29.0 after the
70
+ same shape). *Nobody notices a corpus that is too small, because everything inside it
71
+ passes.*
72
+
73
+ **A high-water mark the same change can lower is not one.** A reader found the residual
74
+ hole and named why a file-only validator cannot see it: the evidence is in the previous
75
+ commit. Deleting rule 21 *and* editing the mark to 20 makes both numbers agree and no
76
+ gap opens. The check now reads the mark at `HEAD` through git — and where git cannot
77
+ answer, it **prints** the skip as an `unlooked` disclosure rather than going quiet,
78
+ because a check that falls silent outside a checkout reads exactly like a check that
79
+ passed. That assertion is a property, not a rejection, so it lives outside the negative
80
+ suite: the suite requires each of its tests to watch a guard *reject* something, and it
81
+ reported the mismatch itself.
82
+
83
+ **A citation naming two guards was checked as zero.** The extractor matched a single
84
+ backtick span followed by `)*`, so invariant 43 — the one this release added, naming
85
+ two — contributed nothing to the checked set and sat silently exempt from the invariant
86
+ that every citation must resolve. Found by a reader, on the invariant whose subject is
87
+ exactly that. Every literal inside a `*(guard: …)*` parenthetical is now checked, and
88
+ one branch that had shipped unprobed (a rule numbered above the mark) now has its plant.
89
+
90
+ The two discovered corpora also collapsed into one `_discover_md(skip, predicate)` —
91
+ second occurrence of the walk, and this file's own rule promotes a class at the second,
92
+ not the third.
93
+
94
+ **A check that only worked in the window its own test used.** The cross-commit
95
+ comparison read the mark at `HEAD` — identical to the working tree on a *committed*
96
+ checkout, which is what CI runs. It fired only in the local pre-commit window, and the
97
+ self-test exercised exactly that window. A reader committed the coordinated edit and
98
+ watched it pass. The mark is now compared against **every value this file's history has
99
+ held**, and the test commits its plant.
100
+
101
+ Two more from the same round: emptying the rules table wholesale made the list falsy and
102
+ skipped the entire check (`rules 0`, PASS), and `docs/superpowers/plans/` was missing
103
+ from the frozen-record exclusions its two siblings carry.
104
+
105
+ **CI caught what the local gate could not see.** Renaming the skip's message left the
106
+ property check grepping for the old string — and that step lives only in the workflow,
107
+ because it asserts something *is printed* rather than watching a guard reject. The local
108
+ runner had no way to run it, so `npm run test:all` was green while CI failed on a string
109
+ this same change had renamed. `test/negatives.py` now runs property checks too: a step
110
+ that exists only in CI is a step the author's gate is blind to.
111
+
112
+ **Both directions, on the guard about both directions.** The check found gaps the log
113
+ did not name, and never the reverse: a number *listed as retired* whose row is still in
114
+ the table — the log naming the wrong rule, or a retired rule quietly back. A gap has one
115
+ side and so does a resurrection; only the second pass sees it. This is `learned.md`'s own
116
+ rule 2 applied to the guard that guards `learned.md`.
117
+
118
+ **And the number the exclusion moved.** Adding `docs/superpowers/plans/` to the frozen
119
+ records took the discovered corpus from fourteen to thirteen, and two documents kept the
120
+ old number — so the corpus's own size is now a claim-registry class, computed from the
121
+ walk. The registry's evaluation moved below the walks that feed it: declared early,
122
+ computed late.
123
+
124
+ The local runner also learned to treat property checks as their own category — selected
125
+ by `-k`, shown by `--list`, and reported under their own heading rather than as *"the
126
+ validator accepted a planted defect"*, which is false for a check that plants nothing.
127
+ Its verdict now names what ran: `PASS: all 0 guards` over an empty selection is the
128
+ refused measurement this repository has a rule about.
129
+
130
+ **A probe that could no longer fail for its own reason.** The anchor test planted a log
131
+ line whose *body* held the digit it was about — and the high-water mark lives in that
132
+ same section, so a bare-digit scan picked up `21`, tripped a different check, and the
133
+ test failed either way. It could not tell a working anchor from a regressed one, which a
134
+ reader demonstrated by reverting the anchor. The mark is now stripped before the log is
135
+ parsed (it was never a log entry), the plant uses a number that is not a live row, and
136
+ reverting the anchor makes the test pass again — which is what "this test measures that"
137
+ means.
138
+
139
+ Two more of the same family: the rule-row regex existed twice, byte-identical, one for
140
+ the registry and one for the disclosure — rule 8's class inside the file that enforces
141
+ it, now one pattern with two readers. And property checks got the floor the negative
142
+ suite has had for releases: rename the only one and the category empties in silence,
143
+ which is the failure property checks were added to close, one level up.
144
+
145
+ Guards: 144 → **156**, plus 1 property check now covered locally.
146
+
147
+ ## v1.29.0 — a sixth axis, and three summaries that each read as complete
148
+
149
+ **Re-derivation** joins the rotation axes in `references/audit.md`: take a number the
150
+ audit already produced and produce it again with a command of a **different shape**,
151
+ then print both. Not a second opinion — a second route. Re-running the same command is
152
+ a spell-check of the first run; only a differently-shaped question can come back
153
+ disagreeing. The exit criterion is **the pair printed**, never "verified", never
154
+ "matches": a re-derivation reported as agreement is a claim about a measurement nobody
155
+ can see.
156
+
157
+ It earns its place from this repository's own record. Four applications, two of which
158
+ refuted something already written down as fact: the version invariant called *four-way*
159
+ over five surfaces (`CONTRIBUTING.md:74`), and a graph shrink read as legitimate
160
+ tightening at 864→839 and as erosion at 839→798 — **same procedure, opposite answers**,
161
+ which is the whole argument for the axis in one line.
162
+
163
+ **The enumeration had already drifted, and nothing could see it.** Measured before
164
+ writing a word of doctrine: `audit.md` defined five axes, the Cursor rule summarised
165
+ four, `README.md` three. Each reads as complete on its own, because a list of three
166
+ orthogonal things is a convincing list of three orthogonal things. The Cursor rule now
167
+ carries all six (it is self-contained by contract and cannot point anywhere); README
168
+ stops enumerating and names the file that defines them — deleting a restatement is how
169
+ every count this repository got wrong has been fixed.
170
+
171
+ A guard now derives the axis keys from `audit.md` **at check time** and requires any
172
+ paragraph naming three or more of them to name all of them. Its first run accused
173
+ `stages.md` of enumerating three axes whose three hits were 595 lines apart and meant
174
+ different things; scoped to the file, the predicate was measuring vocabulary rather
175
+ than enumeration. The unit is the paragraph, and the guard says so in its own comment.
176
+
177
+ **Found by the false positive:** `gates.md` was titled *the two axes* over a file with
178
+ Axis A, B and C, and four surfaces repeated the two. README's prose named two and never
179
+ mentioned degrees of freedom at all — the axis about how much latitude an instruction
180
+ leaves, which is the one that gets skipped. All five sites corrected, and the count is
181
+ now computed from the `## Axis` headings by a claim-registry class, alongside a second
182
+ class for the rotation axes. The two are separated by the qualifier, because *axes* is
183
+ polysemous in this corpus and a guard that conflates them reports drift that is not
184
+ there.
185
+
186
+ **Review round.** Four findings, none from the probes. The guard re-read thirty
187
+ surfaces already cached; the flattening idiom had been hand-rolled three times, each
188
+ commenting on the earlier ones, and is now `_flatten()`; the spec claimed a guard that
189
+ did not exist. And the Cursor rule's **own** Gates section still said *"two axes"* with
190
+ no Axis C — introduced by this PR, fifty lines below an edit it made. Nothing could see
191
+ it: the claim registry's corpus held neither the Cursor rule nor the command file.
192
+ Both are in it now, and the count moved 6 → 7 the moment they were.
193
+
194
+ A second round found the same I/O class at two more call sites. Re-deriving its size
195
+ with a differently-shaped command — an instrumented run rather than a grep of the
196
+ source — put it at **25 reads of `audit.md` and 668 `.md` opens per run**, against an
197
+ estimate of "3+". The two named sites are fixed; the rest is carry-over row 12 with the
198
+ number attached, because a class measured and then left unmentioned is the TODO this
199
+ repository refuses to keep.
200
+
201
+ A third round found the sibling of the idiom the second round found — the
202
+ paragraph-split, hand-rolled at four sites — now `_paragraphs()`, whose docstring says
203
+ what the unit costs. And it found that the new **rotation axes** claim class was
204
+ permanently dormant and had **never been watched failing**: dormant is green by design
205
+ here, which is exactly why a dormant class still needs its plant. It has one now.
206
+
207
+ Guards: 136 → **144**.
208
+
3
209
  ## v1.28.0
4
210
 
5
211
  ### Eight ways to say "I don't know", and no way to count them
package/CONTRIBUTING.md CHANGED
@@ -329,7 +329,52 @@ guess. This repository had eight vocabularies for declining to claim and, until
329
329
  zero counters.
330
330
  *(guard: `a worked GATE verdict omits `)*
331
331
 
332
- **39. Every invariant above names the guard that enforces it, and that guard exists.**This list claims to be *what the validator enforces*; it was eight guards behind when
332
+ **39. A surface that enumerates the rotation axes enumerates all of them.** The axes
333
+ live once, in `references/audit.md`; the Cursor rule summarises them because it is
334
+ self-contained by contract and can point nowhere. Measured on 2026-08-09: the
335
+ definition held five, the Cursor rule four, README three — and each read as complete,
336
+ because a list of three orthogonal things is a convincing list of three orthogonal
337
+ things. The keys are derived from `audit.md` at check time, never hand-listed, and the
338
+ unit is the **paragraph**: scoped to the file, the first version of this guard reported
339
+ `stages.md` for three hits 595 lines apart that meant three different things. Either
340
+ name every axis or stop enumerating and name the file.
341
+ *(guard: `enumerates the rotation axes but names`)*
342
+
343
+ **40. Counts of "axes" are computed, and the two kinds are kept apart.** `gates.md` is
344
+ built on three of its own (context, enforcement, degrees of freedom) and `audit.md`
345
+ rotates between six; the word is polysemous in this corpus. `gates.md` was titled *"the
346
+ two axes"* over a file with Axis A, B and C, and four surfaces repeated it — including
347
+ README's prose, which never mentioned degrees of freedom at all. Both counts are now
348
+ claim-registry classes, separated by the qualifier, so a guard cannot report drift
349
+ between two things that were never the same list.
350
+ *(guard: `gates.md's own axes`)*
351
+
352
+ **41. A rule leaves `learned.md` only on a logged line, and the log exists while
353
+ empty.** Numbers are never reused and never closed up, so a departure shows as a gap in
354
+ the table; a gap the `### Retired` log does not name is a rule that vanished with its
355
+ incident, and the next run re-learns it at full price. The log is present and says
356
+ *none yet*, because an absent log and an empty one look identical from outside.
357
+ *(guard: `named in no line of`)*
358
+
359
+ **42. `learned.md` has no cap, and the reason is written down where the cap keeps being
360
+ proposed.** A cap belongs to a file read *in full* every run — that is the retro's
361
+ standing instructions. This one is entered by citation from twenty-three surfaces.
362
+ Measured before deciding: rules flat at 21 across four releases while the file grew, and
363
+ every word of the growth in the binding map. Its shape prints beside the verdict as a
364
+ **disclosure** — computed, no floor, no direction, never a target.
365
+ *(guard: `learned.md — rules `)*
366
+
367
+ **43. A guard's corpus is discovered, not hand-listed.** Three in `test/validate.py`
368
+ held written lists, and all three had missed a shipped surface: the cold trigger named
369
+ seven files where thirteen state the condition, the disclosure check named five where
370
+ `README.md` also prints a worked verdict, and the claim registry excluded the Cursor
371
+ rule and the command. Every miss was found by a reader or a sweep, never by the guard —
372
+ **nobody notices a corpus that is too small, because everything inside it passes.**
373
+ Exclusions are allowed and must each carry a reason in the code: a changelog narrates
374
+ old formats, `docs/superpowers/specs/` are point-in-time records.
375
+ *(guard: `a worked GATE verdict omits ` and `states the cold-retirement condition as` — both now run over corpora walked from disk, so a new surface joins by existing)*
376
+
377
+ **44. Every invariant above names the guard that enforces it, and that guard exists.**This list claims to be *what the validator enforces*; it was eight guards behind when
333
378
  an audit measured it. A claim of enforcement is now checked like any other claim.
334
379
  *(guard: `whose message does not appear in`)* — and a cited literal must lie inside
335
380
  a **single** string in `test/validate.py`: the check reads that file as text, so a
package/README.md CHANGED
@@ -131,7 +131,7 @@ until it is installed.
131
131
  | 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) |
132
132
  | 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 |
133
133
  | 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 |
134
- | 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 |
134
+ | 6–10 + any check you write | [`gates.md`](plugins/task-pipeline/skills/task-pipeline/references/gates.md) — the three axes, the promotion ladder, gate anatomy, the probe recipe, ratchet floors |
135
135
  | 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 |
136
136
 
137
137
  **Ported, not depended on.** Stage 0 is adapted from
@@ -431,8 +431,9 @@ Three rules keep the audit from becoming another loop:
431
431
  Measured over seven passes on a production repository, by pass six the audit was
432
432
  mostly repairing its own previous pass — while the finding count still looked
433
433
  healthy. So count both numbers every pass (new findings vs. self-inflicted ones);
434
- when the second overtakes the first, **rotate the axis** seams down one
435
- deliverable, then invariants across deliverables, then one class swept end to end.
434
+ when the second overtakes the first, **rotate the axis**. The axes are orthogonal
435
+ by construction and there is one list of them, in `references/audit.md` — this line
436
+ used to restate three of the five that existed, and read as complete while doing it.
436
437
  - **A class that repeats twice becomes a gate, not a note.** Once is an incident;
437
438
  twice is a category, and a category belongs in lint or CI where nobody has to
438
439
  remember it. The third instance in a ledger is how a mechanical defect becomes
@@ -482,12 +483,17 @@ teaches everyone on day one that the gate is noise.
482
483
 
483
484
  ### Gates and hooks — how a rule becomes something that can say no
484
485
 
485
- Two axes, deliberately not conflated. **The stage gate type** (`auto` = verify it
486
+ Three axes, deliberately not conflated. **The stage gate type** (`auto` = verify it
486
487
  yourself; `manual` = wait for an explicit go) is about this pipeline. **The
487
488
  enforcement mechanism** is a ladder a rule climbs: a doctrine line → a review
488
489
  question → a script check (promote here once the class has occurred *twice*) → a CI
489
490
  step → a hook. A rule may sit on several rungs; what it may never do is *pretend* to
490
- be on a higher one.
491
+ be on a higher one. **Degrees of freedom** is the third and the one that gets
492
+ skipped: how much latitude the *instruction* leaves — an open field where many
493
+ routes reach a good answer, a narrow bridge where the guardrails are the wording.
494
+ Over-constraining costs as much as under-constraining and is harder to see, because
495
+ an agent following the letter past the point where the letter fits still reports
496
+ success.
491
497
 
492
498
  The skill ships the anatomy of a gate that cannot lie — non-zero exit on any
493
499
  failure, the verdict block last with nothing after it, a scope header saying what it
@@ -570,8 +576,10 @@ The last act of stage 10 is therefore a **retrospective**, written to
570
576
  `docs/superpowers/retro.md` — **one file per project, not per run**
571
577
  ([`retrospective.md`](plugins/task-pipeline/skills/task-pipeline/references/retrospective.md)).
572
578
  Every run **stamps and prunes** — in that order, because one retirement trigger
573
- counts firings across the last five run stamps and a prune ahead of the stamp reads a
574
- counter its own stage writes afterwards. Only a run that *diverged* writes an entry:
579
+ counts firings across the last five run stamps **or sixty days**, whichever comes
580
+ first, and a prune ahead of the stamp reads a counter its own stage writes afterwards.
581
+ The calendar is the unit that keeps moving when the stamp counter has stopped, which is
582
+ exactly when a stale rule matters most. Only a run that *diverged* writes an entry:
575
583
  symptom with evidence, the stage it surfaced at, the stage that **owned** it, the
576
584
  root cause, the fix, and the check that catches it the first time from now on.
577
585
 
@@ -587,7 +595,7 @@ root cause, the fix, and the check that catches it the first time from now on.
587
595
  Every standing
588
596
  instruction is checked against three retirement triggers — *it became a check* ·
589
597
  *every path or command it names is gone* · *it has not fired in the last five run
590
- stamps* — and the list is held to a **hard cap of ten**. At eleven, the oldest
598
+ stamps, or in sixty days* — and the list is held to a **hard cap of ten**. At eleven, the oldest
591
599
  never-fired rule goes; "but they all matter" is exactly the state in which the list
592
600
  stopped being read, and the ninth stale rule is what discredits the two that are
593
601
  load-bearing.
@@ -600,6 +608,7 @@ visible where it happened:
600
608
  ```
601
609
  GATE 10 acceptance: PASS — 14/14 REQ verified
602
610
  carry-over: 0 unresolved · retro: 7 standing (was 9) · retired 3 · added 1
611
+ abstained: 1 (1 cannot-verify) · unlooked: 0
603
612
  ```
604
613
 
605
614
  Stage 0 reads those standing instructions **in full** on the next run — which is
@@ -810,7 +819,7 @@ recommendation, so you arm the whole run in one exchange. Detail:
810
819
  | [`references/artifacts.md`](plugins/task-pipeline/skills/task-pipeline/references/artifacts.md) | the canonical document layout each stage writes to |
811
820
  | [`references/conventions.md`](plugins/task-pipeline/skills/task-pipeline/references/conventions.md) | how stages 6–10 read the host project's `CLAUDE.md`, and how the documentation regime is detected |
812
821
  | [`references/documentation.md`](plugins/task-pipeline/skills/task-pipeline/references/documentation.md) | the doc system: the inventory, registers and ids, SSOT, the Doc Loop, supersede semantics, the propagation matrix, intent vs as-built |
813
- | [`references/gates.md`](plugins/task-pipeline/skills/task-pipeline/references/gates.md) | the two axes, the promotion ladder, gate anatomy, the probe recipe, ratchet floors, where a gate runs |
822
+ | [`references/gates.md`](plugins/task-pipeline/skills/task-pipeline/references/gates.md) | the three axes, the promotion ladder, gate anatomy, the probe recipe, ratchet floors, where a gate runs |
814
823
  | [`references/deploy-targets.md`](plugins/task-pipeline/skills/task-pipeline/references/deploy-targets.md) | stages 7–8: writing the runbook when there is none, the template, per-platform deploy and log verbs, the verification trio |
815
824
  | [`references/hooks.md`](plugins/task-pipeline/skills/task-pipeline/references/hooks.md) | the `PreToolUse` contract, the fail-open hazard, placement, and the Claude-Code-only limit |
816
825
  | [`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 |
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.28.0 |
15
+ | **Version** | 1.30.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, 5 categories. One recorded run, **self-observed by the author**; **zero blind runs on zero of three models** — the split, and the numbers, live in [`evals/RESULTS.md`](evals/RESULTS.md) and are computed by `evals/run.py` |
@@ -336,9 +336,11 @@ not authorize an outward, irreversible action — stage 7 stops and asks.
336
336
  times and nothing notices it is the same one. So, in this order —
337
337
  **1. Stamp the run first** — date, topic, commit, verdict, counts. It costs one
338
338
  line and it is the only thing that makes the next step computable: one retirement
339
- trigger counts firings across the last five run stamps, so a prune placed ahead of
340
- the stamp reads a counter its own stage writes afterwards and can never run on real
341
- data.
339
+ trigger counts firings across the last five run stamps **or sixty days**, whichever
340
+ comes first, so a prune placed ahead of the stamp reads a counter its own stage
341
+ writes afterwards and can never run on real data. The calendar unit exists because
342
+ the stamp counter only moves when this pipeline runs — ten consecutive releases of
343
+ this repository carry no stamp at all.
342
344
  **2. Then prune.** Check every standing instruction against three
343
345
  retirement triggers: it became a check (a test, a lint rule, a gate, a hook →
344
346
  delete it; the check is the memory, and keeping both means it is read twice and
@@ -404,7 +406,11 @@ its own previous pass while the finding count still looked healthy. So:
404
406
  to end (every error path, every count, every status vocabulary), then — where a
405
407
  code graph exists — **the graph against the docs**, the only axis that finds an
406
408
  absence without reading for it (a hub no document names, an edge the docs deny, a
407
- doc naming a module the graph no longer has).
409
+ doc naming a module the graph no longer has), then **false success** (not "is this
410
+ check correct" but "what does this mechanism print when it did not look?"), then
411
+ **re-derivation** — a number the audit already produced, produced again by a
412
+ command of a *different shape*, with **both printed**; agreement asserted instead
413
+ of a pair printed is not a re-derivation.
408
414
  - **Audit bottom-up.** A missing artefact at a low rung makes everything above it
409
415
  meaningless; top-down you polish a surface for a contract that doesn't exist.
410
416
  - **A class that repeats twice becomes a check, not a note.** Once is an incident;
@@ -453,7 +459,7 @@ you *read*; the matrix names the documents you *owe*. The document nobody read i
453
459
  exactly the document nobody updated. At stage 9, walk the matrix for every change
454
460
  type the run produced, then run the documentation gate and print its ratchet counts.
455
461
 
456
- ## Gates: two axes, and how to build one that cannot lie
462
+ ## Gates: three axes, and how to build one that cannot lie
457
463
 
458
464
  **Axis A — the stage gate type.** `auto` = verify the check yourself and stop on
459
465
  fail; `manual` = wait for an explicit operator go. An auto gate never substitutes
@@ -465,6 +471,17 @@ script check (promote here once the class has occurred **twice**) → CI step
465
471
  A rule may sit on several rungs; what it may never do is *pretend* to be on a higher
466
472
  one — that is the same failure as a gate that prints `FAIL` and exits `0`.
467
473
 
474
+ **Axis C — degrees of freedom**, and it is the one that gets skipped. Axis B says how
475
+ hard a rule bites; this says how much latitude the *instruction* leaves. **high** —
476
+ prose direction, no prescribed sequence, for steps where many routes reach a good
477
+ answer. **medium** — a named order with room inside each step. **low** — run exactly
478
+ this, in this order, for a step that is fragile, irreversible, or must be identical
479
+ every time. Match the level to how fragile the step is, not to how important it feels:
480
+ an open field versus a narrow bridge. **Over-constraining costs as much as
481
+ under-constraining and is harder to see** — a high-freedom step written as low freedom
482
+ produces an agent that follows the letter past the point where the letter stopped
483
+ fitting, and reports success, because it did what it was told.
484
+
468
485
  **Before you run any check:** the base is green (or its known-red baseline is
469
486
  recorded); the check has been probed; you have read its scope header and know what
470
487
  it does **not** cover; you have read its ratchet floors.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.28.0",
3
+ "version": "1.30.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.28.0",
5
+ "version": "1.30.0",
6
6
  "author": {
7
7
  "name": "ssheleg",
8
8
  "url": "https://x.com/sshlg93"
@@ -335,7 +335,7 @@ automation is on — `pipeline.schema.json` is the only contract.
335
335
  - `references/acceptance.md` — the built-in stage-10 close-out: REQ coverage, evidence, sign-off
336
336
  - `references/retrospective.md` — stage 10's last act: the project retro (`docs/superpowers/retro.md`), the three grades of fix, the mandatory prune and its cap of ten
337
337
  - `references/documentation.md` — cross-cutting: the doc inventory, registers and ids, SSOT, the Doc Loop, supersede semantics, the propagation matrix, intent vs as-built
338
- - `references/gates.md` — cross-cutting: the two axes, the promotion ladder, gate anatomy, the probe recipe, ratchet floors, where a gate runs
338
+ - `references/gates.md` — cross-cutting: the three axes, the promotion ladder, gate anatomy, the probe recipe, ratchet floors, where a gate runs
339
339
  - `references/deploy-targets.md` — stages 7–8: writing the runbook when there is none, the template, per-platform deploy and log verbs, and the verification trio
340
340
  - `references/hooks.md` — agent-time enforcement: the PreToolUse contract, the fail-open hazard, placement, and the Claude-Code-only limit
341
341
  - `references/audit.md` — cross-cutting: the L0→L7 ladder and its seams (what was never written), axis rotation, ratchets, proven checks
@@ -291,6 +291,25 @@ construction**:
291
291
  every pass that reads for wrongness. Sweep the change for actions trusted by
292
292
  their own reply. Definition and the known shapes: [`gates.md`](gates.md) →
293
293
  *False success*.
294
+ 6. **Re-derivation** — take a number the audit has already produced and produce it
295
+ again with a command of a **different shape**, then print both. Not a second
296
+ opinion: a second *route*. Re-running the same command is a spell-check of the
297
+ first run; asking a different question that must land on the same number is the
298
+ only version that can come back disagreeing. The exit criterion is **the pair
299
+ printed** — never "verified", never "matches". A re-derivation reported as
300
+ agreement is a claim about a measurement nobody can see.
301
+
302
+ ```
303
+ claimed: the version invariant is four-way (CONTRIBUTING.md, prose)
304
+ re-derived: grep -rl '"version"\|^## v' --include='*.json' --include='*.md' . \
305
+ | wc -l -> 5 surfaces
306
+ verdict: REFUTED — the sentence and the corpus disagree; the corpus wins
307
+ ```
308
+
309
+ Rotate onto this axis when the reading axes go quiet **and the change carries
310
+ numbers**: counts in prose, thresholds, "N of M" claims, anything a document
311
+ asserts about itself. Its yield is not proportional to effort — it is
312
+ proportional to how long the first number went unchallenged.
294
313
 
295
314
  **The crossover is measurable, so measure it.** Every pass, count two numbers: new
296
315
  findings, and findings caused by the previous pass's own fixes. When the second
@@ -1,4 +1,4 @@
1
- # Gates — the two axes, and how to build one that cannot lie
1
+ # Gates — the three axes, and how to build one that cannot lie
2
2
 
3
3
  **One job: turn a rule into something that can say no.** [`audit.md`](audit.md)
4
4
  says a class seen twice *belongs in a script*; this file is where that script comes
@@ -20,6 +20,7 @@ to be enforced and is not is the same failure as a gate that prints `FAIL` and e
20
20
  - The two that are not in the table, and why
21
21
  - The one instruction that would have prevented the most
22
22
  - Where these bind in the pipeline
23
+ - What leaves this file, and why there is no cap
23
24
 
24
25
  ## The table — trigger · check · exit criterion
25
26
 
@@ -137,7 +138,7 @@ run's belief about the work-list against the register; the claim only ever appea
137
138
 
138
139
  **20 · The copy that wins.** A service had **two Dockerfiles**. One was added at the repository root by a run that checked whether a Dockerfile existed by looking where it expected one; `docker/Dockerfile` had been there all along, and `.github/workflows/ci.yml` says `file: docker/Dockerfile`. They disagreed about the port — 8080 at the root, 8000 in `docker/` — and the disagreement surfaced two days later as a **deployed service that answered nothing**, while `docker ps` said `Up` and `systemctl` said `active`. The built one also ran as root and copied the whole context, including `.git` and any `.env`; the hardened one was the one nobody built. Comparing the two files would have found the difference and not the direction. Only the workflow line says which one ships, and it is one grep. The same session hit this three more times: an autonomy sweep row added to the file that ASKS and not the file that RECORDS, twice, caught by a validator that knew to look at both.
139
140
 
140
- **21 · The prune that could not run.** A retrospective's standing-instruction list has a hard cap of ten and three retirement triggers, one of which is *it has not fired in the last five run stamps*. The stage's own instruction was **prune first, then stamp**. So the trigger read a counter the same stage wrote afterwards: on a fresh list it is unreadable, and it stays unreadable for as long as nobody stamps. Measured on a real project: the last retro entry was five days old, stamps per day ran 33, 20, 26, **3, 0** — the zero on a day with 107 commits — and the list sat at exactly **10 of 10**, so every run arrived at a stage that opened with a full list, an unusable trigger and a mandatory deletion. It was not skipped out of laziness. It was skipped because its first step could not be performed, and the cheap step that would have made it performable was queued behind it.
141
+ **21 · The prune that could not run.** A retrospective's standing-instruction list has a hard cap of ten and three retirement triggers, one of which was, at the time of this incident, "it has not fired in the last five run stamps" — it has since gained a second unit, and the incident is left as it happened. The stage's own instruction was **prune first, then stamp**. So the trigger read a counter the same stage wrote afterwards: on a fresh list it is unreadable, and it stays unreadable for as long as nobody stamps. Measured on a real project: the last retro entry was five days old, stamps per day ran 33, 20, 26, **3, 0** — the zero on a day with 107 commits — and the list sat at exactly **10 of 10**, so every run arrived at a stage that opened with a full list, an unusable trigger and a mandatory deletion. It was not skipped out of laziness. It was skipped because its first step could not be performed, and the cheap step that would have made it performable was queued behind it.
141
142
 
142
143
  The same class had already bitten that project twice from the other side, and its roadmap names the
143
144
  property exactly: seven rows read `blocked` on producers the dependency board recorded as delivered,
@@ -209,3 +210,71 @@ project buys go in its retro ([`retrospective.md`](retrospective.md) →
209
210
  lesson there that would be true in any repository belongs here instead, as an issue
210
211
  upstream. A local file that accumulates universal rules is a fork of this one that
211
212
  nobody named.
213
+
214
+ ---
215
+
216
+ ## What leaves this file, and why there is no cap
217
+
218
+ `docs/superpowers/retro.md` caps its standing instructions at **ten** and retires them
219
+ on three triggers. Somebody proposes the same cap here about once a programme. It is
220
+ the wrong instrument, and the reason is worth more than the rule.
221
+
222
+ **A cap belongs to a file you must finish reading.** The retro's standing instructions
223
+ are read *in full* at stage 0 of every run — bounded by construction, or the last one
224
+ is never reached. This file is never read in full: it is entered by citation from the
225
+ stages, and *Where these bind in the pipeline* is that entrance. **A file you enter
226
+ through an index needs its index to be right, not its length to be short.**
227
+
228
+ And the cap would have measured the axis that is not moving. Re-derived across releases
229
+ rather than recalled — **measured at each tag**, so these rows are history and cannot
230
+ go stale; the live shape is printed by `npm test` beside its verdict, and this file
231
+ states no number about itself:
232
+
233
+ | | v1.14.1 | v1.20.0 | v1.23.0 | v1.29.0 |
234
+ |---|---|---|---|---|
235
+ | rules in the table | 15 | 18 | 21 | **21** |
236
+ | words in the file | 2165 | 2987 | 3696 | **3919** |
237
+
238
+ Rules have been flat for four releases while the file grew — and every word of that
239
+ growth is in the binding map, the section that makes a rule *reachable*. Cutting there
240
+ shortens the index. The largest section by far is the incidents, and those are the only
241
+ record of those events anywhere in this repository — checked by taking each incident's
242
+ distinctive tokens against the whole retro corpus, which returned nothing. They are not
243
+ a compression target; they are the thing the rules are made of.
244
+
245
+ **Two triggers retire a rule, and neither is a count:**
246
+
247
+ 1. **The conditions cannot occur.** The tool, the layer, or the failure mode it names
248
+ is gone from every project the skill runs on — not "we have not hit it lately".
249
+ 2. **It is subsumed.** Another rule covers it entirely. This is a **merge**, not a
250
+ delete: the absorbing rule names the absorbed one, and every binding-map row that
251
+ pointed at the old number is repointed in the same change, or the map now sends a
252
+ stage to a rule that is not there.
253
+
254
+ Never *"it became a check"* — that trigger is right for a standing instruction, whose
255
+ whole purpose is to be read until the machine takes over. Here the rule is the reason
256
+ the check exists, and a check whose reason has been deleted is the next thing somebody
257
+ removes as noise.
258
+
259
+ **Every deletion is logged as one line**, in the same change, the same discipline as
260
+ the retro's prune: the rule's number, its name, which trigger fired, and the commit.
261
+ A rule that vanishes silently takes its incident with it, and the next run re-learns it
262
+ at full price. Numbers are never reused and never closed up — a gap in the table is the
263
+ evidence that something left, and the log below says what.
264
+
265
+ ### Retired
266
+
267
+ **Numbers issued so far: 21.** This is the high-water mark, and it is the only number
268
+ this file states about itself — deliberately, because the gap that proves a rule left
269
+ cannot be computed from the table alone: **deleting the highest-numbered rule shrinks
270
+ the maximum with it, and no gap ever opens.** That false negative shipped in the first
271
+ draft of this very section's guard and was found by a reader, not by its probe, which
272
+ had planted in the middle of the list.
273
+
274
+ *None retired yet.* Stated rather than omitted: an empty log and a missing log look
275
+ identical from outside, and only one of them means nothing has been retired. Each
276
+ retirement is one line, starting with the rule's number:
277
+
278
+ <!-- - **N · Name** — trigger: subsumed by M | conditions gone; `<commit>` -->
279
+
280
+ ---
@@ -5,6 +5,13 @@ done. It exists because the pipeline's gates are good at *this* run and blind
5
5
  across runs: the same class of failure can be caught, fixed and forgotten five
6
6
  times, and nothing in the flow notices it is the same one.
7
7
 
8
+ **The cap here is not a general rule about doctrine files.** It applies because these
9
+ instructions are read *in full* every run. [`learned.md`](learned.md) →
10
+ *What leaves this file, and why there is no cap* is the other case: a file entered by
11
+ citation, where the index must be right rather than the length short, and where a rule
12
+ retires on two triggers that are not counts. Proposing this cap for that file is a
13
+ recurring idea, and it is answered there with the measurement rather than an opinion.
14
+
8
15
  **Two artifacts, and the split is the point.** A file that is read *in full* every
9
16
  run may not contain anything that grows without limit — otherwise the cap that
10
17
  justifies reading it protects one section while the file below it doubles.