task-pipeline-skill 1.33.0 → 1.38.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.
Files changed (26) hide show
  1. package/CHANGELOG.md +332 -0
  2. package/CONTRIBUTING.md +66 -0
  3. package/README.md +23 -0
  4. package/SKILL-CARD.md +1 -1
  5. package/cursor/rules/task-pipeline.mdc +34 -9
  6. package/evals/task-pipeline.evals.json +87 -0
  7. package/package.json +3 -2
  8. package/plugins/task-pipeline/.claude-plugin/plugin.json +2 -2
  9. package/plugins/task-pipeline/commands/task-pipeline.md +167 -56
  10. package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +6 -5
  11. package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +133 -21
  12. package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +12 -5
  13. package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +32 -3
  14. package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +10 -0
  15. package/plugins/task-pipeline/skills/task-pipeline/references/exposure.md +8 -1
  16. package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +18 -0
  17. package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +10 -5
  18. package/plugins/task-pipeline/skills/task-pipeline/references/learned.md +3 -1
  19. package/plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md +52 -2
  20. package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
  21. package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +190 -0
  22. package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +90 -4
  23. package/plugins/task-pipeline/skills/task-pipeline/references/review.md +56 -0
  24. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +123 -12
  25. package/plugins/task-pipeline/skills/task-pipeline/templates/README.md +1 -0
  26. package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +77 -0
@@ -140,6 +140,16 @@ whole cycle that no gate reads. It cites the measurement or it is not written
140
140
  `B-NNN`, not a description**: *"next up: B-014"* can be checked against the file,
141
141
  *"next up: the export fix"* cannot.
142
142
 
143
+ **And it is printed, in one fixed shape** ([`progress.md`](progress.md)):
144
+
145
+ ```
146
+ ▶ <topic> · <module> (N/M) · <id> <stage> <gate> · iter <N> · gates <N/M> · next B-NNN
147
+ ```
148
+
149
+ The iteration number is a count of `iter:` lines in `.task-pipeline/run.md`, not a
150
+ number the agent is carrying. After a compaction the agent's count is gone and the
151
+ file's is not — which is the argument the build ledger already won one stage down.
152
+
143
153
  ## Parked at a manual gate
144
154
 
145
155
  A fixed interval firing into a `manual` gate is a nag. Five minutes later it fires
@@ -37,9 +37,16 @@ defect. Until then it is named honestly.
37
37
  ## The components, each named
38
38
 
39
39
  ```
40
- exposure: 99 unverified · never checked · 31 releases since the last human confirmation
40
+ exposure: N unverified · never checked · N releases carry one
41
41
  ```
42
42
 
43
+ **The example carries no numbers, deliberately.** It said `99 unverified` and
44
+ `31 releases since the last human confirmation` until 2026-08-10 — one figure lifted
45
+ from this repository's live count, which drifts, and one wording the code has never
46
+ printed. A worked example that disagrees with its own output teaches the wrong format
47
+ to every reader who trusts the doctrine over the terminal, and this one disagreed in
48
+ both directions at once.
49
+
43
50
  - **unverified** — rows whose `Human` reads `never`.
44
51
  - **since** — days since the newest `Human` date. When **no** row has ever been
45
52
  confirmed, this prints the literal **`never checked`**, not `0 days`: zero would read
@@ -272,6 +272,24 @@ one flipped a row whose cell was already empty, so nothing was planted. A silent
272
272
  check is a claim about two things, and the probe is the one to doubt first — prove
273
273
  your edit landed in the text the check actually parses.
274
274
 
275
+ **Three assertions, and the third is the one hand-rolled probes keep missing.** A
276
+ plant that trips some *other* check has proved that other check works. Three probes
277
+ in one day passed that way: one removed 1 of 3 identical lines and left the shape
278
+ intact; one decremented a number inside an **already-released** section; one deleted
279
+ the shouted spelling of a phrase and left the lowercase one. Each landed somewhere
280
+ real and demonstrated nothing about the guard it was written for. So:
281
+
282
+ 1. **the substitution landed** — `replace` matching nothing returns the string
283
+ unchanged and raises nothing;
284
+ 2. **the exit code is non-zero** — not a `FAIL` line on stdout;
285
+ 3. **the message that fired belongs to the guard under test** — named up front, not
286
+ recognised afterwards.
287
+
288
+ `test/probe.py` does all three (`npm run test:probe` self-tests the harness, including
289
+ its own failure branches, because a harness whose failure path has never executed is
290
+ the thing it exists to stop). Declare the plant as `Plant(label, path, old, new,
291
+ expect=<the guard's own words>)` rather than hand-rolling a fourth copy of the loop.
292
+
275
293
  **Record the probe.** One line per section, in the change that ships the check.
276
294
  Otherwise the next reader has to redo it to know whether it was ever done.
277
295
 
@@ -64,15 +64,20 @@ Rules for the list:
64
64
  - **The wiki and the graph are optional; the harvest is not.** With no wiki, no
65
65
  graph and no doc repos, the harvest is sources 1 and 3–7 and takes two minutes.
66
66
  Skipping it is never the answer.
67
- - **Source 7 is read in full, not queried.** It is capped at ten instructions
68
- precisely so that this is cheap, and it is the one source that *binds* the run
69
- rather than informing it.
67
+ - **Source 7 is read in full where it is bounded, and queried where it is not.** The
68
+ standing instructions (capped at ten) and the run stamps (one line each) are read;
69
+ the *Recent log* is queried by the task's nouns like the archive. Measured on the
70
+ file this doctrine was written for, the log was **74% of it** — and it is the one
71
+ section nothing caps. Source 7 is still the source that *binds* the run rather than
72
+ informing it; that is why the part which binds must stay cheap enough to read.
70
73
 
71
74
  ## The retro's standing instructions — an instruction source, not background
72
75
 
73
76
  `docs/superpowers/retro.md` ([`retrospective.md`](retrospective.md)) is the one
74
- harvested source that is **read in full rather than queried**: the standing
75
- instructions are capped at ten precisely so that this is cheap. They are what
77
+ harvested source whose binding part is **read in full rather than queried**: the
78
+ standing instructions are capped at ten precisely so that this is cheap, and the run
79
+ stamps are one line each. Its narrative log is queried, not read — an uncapped section
80
+ inside a source that binds is how the capped part stops being read. They are what
76
81
  previous runs of this pipeline got wrong *in this project* — the rules no check
77
82
  could decide — and they bind this run.
78
83
 
@@ -47,6 +47,7 @@ to be enforced and is not is the same failure as a gate that prints `FAIL` and e
47
47
  | 19 | **An empty measurement is a refused measurement** | any command run to establish a fact — a count, a probe, a query, a suite | assert the output is **non-empty and shaped as expected** before reading meaning into it; a command that printed nothing did not answer | the run quotes the output it read, not the conclusion it drew from silence |
48
48
  | 20 | **When a thing exists twice, ask which one is used — not whether they agree** | any artifact with a second copy: two build files, a vendored library, a schema and its mirror, doctrine in two documents | find the **consumer** and read what it names; the copies cannot answer this about themselves | the run names the file the build/test/deploy actually reads, quoted from the consumer |
49
49
  | 21 | **A step that consumes what a later step produces is a deadlock, not an ordering** | any sequence where a check reads state another step writes — a prune reading stamps, a gate reading a ledger, a report reading counts | trace each input to the step that writes it; if that step is downstream, the check has never run on real data | every check names the step that produces its input, and that step is upstream of it |
50
+ | 22 | **An operation that changes nothing reports the same as one that changed everything** | a substitution whose needle is absent · a command whose output is suppressed at a decision point · an edit issued against a shape the file does not have | assert the effect, not the call: a replacement must report how many times it matched, and a command whose exit code governs the next step is never piped or silenced | four incidents in two programmes, each invisible until something downstream failed — an import that never landed, a doctrine phrase worded differently, a `gh` call refused behind `>/dev/null`, and a test piped to `head` so that `$?` belonged to `head` |
50
51
 
51
52
  ---
52
53
 
@@ -189,6 +190,7 @@ answer would have exposed it in a minute.
189
190
  | 0 Inventory · 9 Docs · any register write | 8 (compute), 14 (targets resolve — including every commit SHA in the retro), 15 (identity before a lease) — see [`documentation.md`](documentation.md) |
190
191
  | 0 Harvest · any run resuming from a summary | 16 — the work-list and every inherited state claim re-derived before use, [`knowledge-sources.md`](knowledge-sources.md) → *Carried-in claims* |
191
192
  | any check you write | 4, 5, 7, 10, 11 — the procedure is [`gates.md`](gates.md) |
193
+ | **every stage** · any edit, any command whose result is read | 22 — a no-op is indistinguishable from success unless the effect is asserted; never suppress the output of a command a decision depends on |
192
194
  | 3 Spec · 4 Plan | 2 (both directions), 8 (compute, never restate) |
193
195
  | 5 Dev | 9 (generators seed green), 12 (tests create their own state), 13 (local infra) |
194
196
  | 6 Tests | 4, 5, 10, 11 — every new check probed both ways, measured, and asserted on its exit code |
@@ -264,7 +266,7 @@ evidence that something left, and the log below says what.
264
266
 
265
267
  ### Retired
266
268
 
267
- **Numbers issued so far: 21.** This is the high-water mark, and it is the only number
269
+ **Numbers issued so far: 22.** This is the high-water mark, and it is the only number
268
270
  this file states about itself — deliberately, because the gap that proves a rule left
269
271
  cannot be computed from the table alone: **deleting the highest-numbered rule shrinks
270
272
  the maximum with it, and no gap ever opens.** That false negative shipped in the first
@@ -22,6 +22,7 @@ searches.
22
22
 
23
23
  - Bookkeeping — the thing that makes detection mechanical
24
24
  - Detection — any one of these trips the guard
25
+ - The review loop — a cap that measures rather than stops
25
26
  - The break protocol
26
27
  - When to stop and hand back
27
28
  - Rationalizations
@@ -30,7 +31,8 @@ searches.
30
31
 
31
32
  You cannot detect churn from memory, especially after compaction. Every repeating
32
33
  pass appends one line to the run's ledger (`.task-pipeline/build/<plan>/progress.md`
33
- for stage 5; `.task-pipeline/run.md` for stage-level and program-level loops):
34
+ for stage 5; `.task-pipeline/run.md` for stage-level and program-level loops —
35
+ **seeded at stage 0** from [`../templates/run.md`](../templates/run.md)):
34
36
 
35
37
  ```
36
38
  touch: <file> — pass <N> (<stage|round|module>) — reason: <finding id / gate item>
@@ -40,6 +42,12 @@ One line per file per pass. The reason must name **what forced the edit** — a
40
42
  finding id, a failed gate item, an operator instruction. "Cleanup", "polish" and
41
43
  "while I was there" are not reasons; they are churn with better manners.
42
44
 
45
+ **This ledger was required here from the day the file shipped and written by no run
46
+ until 2026-08-10.** The detection below calls itself mechanical; with no ledger it had
47
+ no input at all, so the guard sat on rung 1 while every reader took it for rung 3
48
+ ([`gates.md`](gates.md) → *Axis B*). It is now seeded at stage 0 and named in that
49
+ stage's gate — which is the whole difference between a rule and a rule that runs.
50
+
43
51
  ## Detection — any one of these trips the guard
44
52
 
45
53
  1. **Revert-oscillation.** An edit restores something an earlier pass in this run
@@ -59,7 +67,48 @@ finding id, a failed gate item, an operator instruction. "Cleanup", "polish" and
59
67
 
60
68
  Caps that trip the guard by themselves: **5 fix rounds** per task
61
69
  ([`build.md`](build.md)), **2 re-entries** per stage per artifact, **3 passes** per
62
- module in the program loop.
70
+ module in the program loop, and **3 review rounds** per artifact — which is not a stop
71
+ but a measurement, below.
72
+
73
+ ## The review loop — a cap that measures rather than stops
74
+
75
+ The caps above govern loops that **edit**. A review loop does both: the reader finds,
76
+ the run fixes, the reader reads again. It had no cap at all until 2026-08-10, and this
77
+ repository's own run stamps say what that cost — **ten rounds, ten, eight, four,
78
+ three** — against a stated ceiling of two re-entries per stage. Nothing tripped,
79
+ because a review round was named in no cap.
80
+
81
+ **A flat cap would have been the wrong fix.** Every one of those runs recorded *"none
82
+ from my probes"* beside its count: the reader was still finding real defects on round
83
+ nine. Stopping at two would have shipped them.
84
+
85
+ So the cap is a **decision point**. Default **3 rounds** per artifact, recorded in
86
+ `pipeline.json` → `run.review.maxRounds`. On reaching it, stop reviewing and print the
87
+ pair [`audit.md`](audit.md) already defines — new findings, and findings caused by this
88
+ run's own fixes — per round:
89
+
90
+ ```
91
+ review cap reached — 3 rounds — artifact: test/validate.py
92
+ round 1: 12 new · 0 self-inflicted
93
+ round 2: 5 new · 1 self-inflicted
94
+ round 3: 1 new · 3 self-inflicted
95
+ ```
96
+
97
+ - **Self-inflicted ≥ new** — the axis is exhausted ([`audit.md`](audit.md) → *Every
98
+ pass changes the axis*). Stop. Every remaining finding becomes a board row with its
99
+ evidence ([`backlog.md`](backlog.md)); none is dropped.
100
+ - **New > self-inflicted** — the reader is still paying. Continuing is then the
101
+ operator's call, made with the numbers in hand rather than out of fatigue.
102
+
103
+ **The pair is the whole point.** A round count alone says how tired everyone is; the
104
+ pair says whether the loop still produces anything. Measured on one file once, the
105
+ guard's shapes and the run's own prose disagreed — one still paying, one exhausted —
106
+ so a single number would have stopped the half that was working and continued the half
107
+ that was not.
108
+
109
+ **Rounds are counted from the ledger, never from memory**: distinct `pass N` values on
110
+ `touch:` lines at the review stage. A round that finds nothing ends the loop by
111
+ definition and needs no counting.
63
112
 
64
113
  ## The break protocol
65
114
 
@@ -109,6 +158,7 @@ far cheaper than a third round of the same argument.
109
158
  | Excuse | Reality |
110
159
  |---|---|
111
160
  | "One more pass and it converges" | Two passes with the same reason already proved it doesn't. The disagreement is above the code. |
161
+ | "The reviewer is still finding things, so keep going" | Then say so with the pair: new versus self-inflicted, per round. If new still leads, that is an argument. Ten rounds with nobody counting is not. |
112
162
  | "I'll just revert to what worked" | That is the oscillation, not the exit. Name A and B first. |
113
163
  | "The reviewer keeps changing its mind" | Different findings on the same lines mean the requirement is ambiguous. That's a spec question. |
114
164
  | "Tidying while I'm in the file" | Untracked edits are what make churn invisible. One reason per change, in the ledger. |
@@ -52,6 +52,7 @@ a row pointing outside the bundle is the defect this file exists to catch.
52
52
  | The entry audit and what it inspects | `references/setup.md` |
53
53
  | The ladder, seams, axis rotation, ratchets | `references/audit.md` |
54
54
  | Loop detection and its caps | `references/loop-guard.md` |
55
+ | **What the run prints about itself** — the header block, the rail, the iteration line | `references/progress.md` |
55
56
  | **The run mode** — item-by-item pacing, default off, what it never collapses | `references/continuity.md` |
56
57
  | **The context budget** — the evidence rule and what a flush actually updates | `references/continuity.md` |
57
58
  | **The board** — the work-list between runs, its computed priority, and the ledger seam it resolves | `references/backlog.md` |
@@ -0,0 +1,190 @@
1
+ # Progress — saying which pipeline this is, and where in it
2
+
3
+ **One job: make the run's position a printed fact instead of something the operator
4
+ reconstructs from the last thing that scrolled past.**
5
+
6
+ A pipeline that never says where it is has one specific failure, and it is not
7
+ confusion — it is that **a stage looks done because nothing printed**.
8
+ [`stages.md`](stages.md) opens with a checklist for exactly that reason and then marks
9
+ it *"copy it, tick it"*: an instruction with no gate behind it, which is rung 1
10
+ behaving like rung 3 ([`gates.md`](gates.md) → *Axis B*). This file is that checklist
11
+ promoted to something the run must emit.
12
+
13
+ **Boundary.** This file decides **what is printed and when**. It decides nothing about
14
+ what is true: every number on the block has a home somewhere else and is read from
15
+ there. A progress line that computes its own counts is the fourth copy of the truth,
16
+ and [`continuity.md`](continuity.md) already says what happens to those — nobody
17
+ maintains them and the next run reads them as current.
18
+
19
+ ---
20
+
21
+ ## Contents
22
+
23
+ - The two boundaries, and only those two
24
+ - The header block
25
+ - The iteration line
26
+ - The rail is computed, never eleven
27
+ - What each glyph means
28
+ - Every number is borrowed
29
+ - Absent is a word, never a zero
30
+ - The run ledger this reads from
31
+ - Rationalizations
32
+
33
+ ## The two boundaries, and only those two
34
+
35
+ **Task start** and **iteration close**. Nothing else.
36
+
37
+ An iteration is already defined — *one item taken to its gate*
38
+ ([`continuity.md`](continuity.md) → *What one iteration means*) — and that definition
39
+ is what makes this cheap. Printing per agent turn would put a bar above every tool
40
+ call, and a block that appears fifty times a run is a block nobody reads, including
41
+ the one time it says something.
42
+
43
+ Between the two boundaries the run prints whatever it normally prints. This file adds
44
+ no narration.
45
+
46
+ ## The header block
47
+
48
+ Emitted **once, before stage 0's first question**, and again whenever the module
49
+ changes:
50
+
51
+ ```
52
+ task-pipeline v1.34.0 · pipeline-audit · module P1 «the progress print» (1 of 4)
53
+ 0 ✓ 1 ✓ 2 ✓ 3 ▶ 4 · 5 · 6 · 7 · 8 · 9 · 10 ·
54
+ ███████░░░░░░░░░░░░░░░░░░░ gates 3/11 · now 3 Spec · manual
55
+ board B-028 · carry-over 0 rows · exposure 99 never · unlooked 0
56
+ ```
57
+
58
+ Four lines, and each one answers a question an operator otherwise has to ask:
59
+
60
+ | Line | Answers |
61
+ |---|---|
62
+ | 1 | *which skill, which version, which programme, which module of how many* |
63
+ | 2 | *which stages are closed, which one is live* |
64
+ | 3 | *how far along, what is running now, will it stop for me* |
65
+ | 4 | *what is queued, what is deferred, what nobody has confirmed* |
66
+
67
+ **The module segment is omitted when there is no module map.** A task that stage 2
68
+ never decomposed has no module, and printing `(1 of 1)` turns an absence into a claim
69
+ — the shape [`audit.md`](audit.md) is built around. Where stage 2 recorded `single
70
+ module: <name>` ([`decomposition.md`](decomposition.md)), print that phrase instead.
71
+
72
+ ## The iteration line
73
+
74
+ Emitted at the **close** of every iteration, one line:
75
+
76
+ ```
77
+ ▶ pipeline-audit · P1 (1/4) · 5 Dev auto · iter 3 · gates 5/11 · next B-025
78
+ ```
79
+
80
+ **`next` cites a `B-NNN`, never a description.** That rule is
81
+ [`continuity.md`](continuity.md)'s and it is the reason this line exists at all:
82
+ *"next up is X"* was already the one sentence in a loop that no gate reads. A board id
83
+ can be checked against `docs/superpowers/backlog.md`; *"next up: the export fix"*
84
+ cannot.
85
+
86
+ **Nothing queued is `next —`, printed.** A loop that reaches an empty board says so;
87
+ omitting the field is indistinguishable from forgetting it.
88
+
89
+ ## The rail is computed, never eleven
90
+
91
+ The stage ids on the rail come from the project's `pipeline.json` → `stages[]`. They
92
+ are **not** the eleven in [`../pipeline.example.json`](../pipeline.example.json), which
93
+ is this plugin's *example* flow — a host project replaces it with its own stages
94
+ (`SKILL.md` → *Bring your own skills*).
95
+
96
+ A bar reading `gates 5/11` in a project with six stages is a false success in the
97
+ purest form the pipeline has: a summary that is confidently wrong about the thing it
98
+ summarises, printed in the place designed to be trusted at a glance.
99
+
100
+ So the rail carries **no stage count of its own**. Read the array, print what is in it.
101
+ Six stages give six positions.
102
+
103
+ ## What each glyph means
104
+
105
+ | Glyph | Means | Written when |
106
+ |---|---|---|
107
+ | `✓` | the stage's **gate passed** | the gate's own verdict was recorded |
108
+ | `▶` | in flight | the stage was entered and its gate has not returned |
109
+ | `·` | not entered | — |
110
+ | `✗` | entered, gate returned a failure | the verdict said so |
111
+ | `⊘` | skipped, **with the reason on the same run's record** | the short path, or a stage the brief excluded |
112
+
113
+ **`✓` means the gate passed — not that the stage was walked.** This is the whole
114
+ integrity of the block. A rail is a summary, and a summary is the easiest artefact in
115
+ a run to write from memory rather than from the record; a glyph set by recollection is
116
+ [`gates.md`](gates.md)'s *false success* with a nicer typeface. Derive each glyph from
117
+ the verdict the gate wrote, in the run ledger, and from nothing else.
118
+
119
+ **`⊘` may never be silent.** A skipped stage with no recorded reason is exactly what a
120
+ `·` looks like from outside, and the two mean opposite things.
121
+
122
+ ## Every number is borrowed
123
+
124
+ | Field | Its home |
125
+ |---|---|
126
+ | `board B-NNN` | `docs/superpowers/backlog.md` ([`backlog.md`](backlog.md)) |
127
+ | `carry-over N rows` | the run's carry-over ledger, as printed beside every gate verdict |
128
+ | `exposure N never` | `docs/superpowers/verification.md` ([`exposure.md`](exposure.md)) |
129
+ | `unlooked N` | the gate's own disclosure ([`gates.md`](gates.md) → *Disclosures*) |
130
+ | `gates N/M` | the run ledger's verdict rows, and `pipeline.json` → `stages[]` |
131
+
132
+ **None of these is recomputed here.** If a number on the block disagrees with the
133
+ number beside a gate verdict, the block is wrong — that direction, always, because the
134
+ gate looked and the block quoted.
135
+
136
+ This also settles what the block is *not*: it is neither a ratchet nor a disclosure of
137
+ its own ([`gates.md`](gates.md) → *Ratchets*, *Disclosures*). It sets no floor and
138
+ carries no target. It is a **restatement with a citation**, and the citation is the
139
+ only reason a restatement is allowed here at all.
140
+
141
+ ## Absent is a word, never a zero
142
+
143
+ Where a value does not exist, print the word:
144
+
145
+ ```
146
+ board — · carry-over 0 rows · exposure — · unlooked 0
147
+ ```
148
+
149
+ `exposure —` says *no verification ledger in this project*. `exposure 0` says *nothing
150
+ is unconfirmed*, which is the opposite claim, and it is the same inversion
151
+ [`exposure.md`](exposure.md) refuses when it prints `never checked` rather than
152
+ `0 days`. A zero standing in for an absence is how a project learns it is safe.
153
+
154
+ `carry-over 0 rows` **is** a real zero and prints as one: the ledger exists and holds
155
+ nothing.
156
+
157
+ ## The run ledger this reads from
158
+
159
+ `.task-pipeline/run.md`, seeded at stage 0 from
160
+ [`../templates/run.md`](../templates/run.md), one file per run.
161
+
162
+ It already had a second owner before this file existed:
163
+ [`loop-guard.md`](loop-guard.md) names it as the record that makes churn detection
164
+ **mechanical**, and calls it the only memory that survives compaction. It was never
165
+ written by any run — the detector had no input, and the guard was doctrine wearing a
166
+ script's clothes. One file serves both readers: the guard reads the `touch:` lines,
167
+ this block reads the verdict rows and the iteration counter.
168
+
169
+ Three kinds of line, appended, never rewritten:
170
+
171
+ ```
172
+ stage: 3 Spec — gate manual — verdict pass — 2026-08-10T14:02Z
173
+ iter: 3 — item B-025 — closed at gate 6
174
+ touch: test/validate.py — pass 2 (stage 7) — reason: F-014
175
+ ```
176
+
177
+ **The counter is a count of `iter:` lines, not a number the agent remembers.** After a
178
+ compaction the agent's memory of "iteration 3" is gone and the file's is not, which is
179
+ the entire argument for keeping it on disk rather than in the reply.
180
+
181
+ ## Rationalizations
182
+
183
+ | The excuse | What is actually true |
184
+ |---|---|
185
+ | "The operator can see the stages scroll by" | They can see that *something* printed. A stage that ended silently and a stage that never started look identical in a transcript, which is the failure `stages.md`'s checklist was written for and never enforced. |
186
+ | "A progress bar is decoration" | Then delete the numbers and keep the bar. The objection is really to the bar; the four borrowed counts are the payload, and they are the ones nobody prints today. |
187
+ | "I know which stage I'm on, I'll write the rail from memory" | Then the rail is a claim about the run rather than a reading of it, and it will be right until the run it most matters on. Derive it from the verdicts. |
188
+ | "There's no module map, I'll put (1 of 1)" | An undecomposed task has no module. `(1 of 1)` is an invented denominator, and a reader cannot tell it from a real one. |
189
+ | "Printing it every iteration is noise" | One line. The block is four, and it appears at task start. If that is noise, the run is emitting far worse elsewhere. |
190
+ | "The ledger is bureaucracy, I'll count iterations in my head" | Your head does not survive compaction. That is not a hypothetical here — it is why `loop-guard.md` asked for this file in the first place. |
@@ -18,7 +18,8 @@ justifies reading it protects one section while the file below it doubles.
18
18
 
19
19
  | Artifact | Parts | How it is read |
20
20
  |---|---|---|
21
- | `docs/superpowers/retro.md` — **one per project** | **Standing instructions** (max **10**) · **Recent log** (entries from the last five run stamps) · **Run stamps** | stage 0, **in full** — all three are bounded by construction |
21
+ | `docs/superpowers/retro.md` — **one per project** | **Standing instructions** (max **10**) · **Run stamps** (one line each) | stage 0, **in full** — both are bounded by construction |
22
+ | the same file's **Recent log** | entries from the last five run stamps — narrative, and capped by nothing | stage 0, **queried** by the task's nouns. It said *in full* until 2026-08-10, when it measured **74%** of the file: an uncapped section inside a binding source is what makes the capped part get skimmed |
22
23
  | `docs/superpowers/retro/YYYY-QN.md` — the archive | every entry and every retirement ever written, append-only | **queried** by the task's nouns; never read end to end |
23
24
 
24
25
  Seed the archive from [`../templates/retro-archive.md`](../templates/retro-archive.md).
@@ -37,6 +38,8 @@ file exists to stop.
37
38
  - The prune — mandatory, and it runs after the stamp
38
39
  - The loop closes at stage 0
39
40
  - Where a lesson goes when it is not about this project
41
+ - Publishing the insight — the skill learns from every project that runs it
42
+ - What may leave the project — the redaction list
40
43
  - Rationalizations
41
44
 
42
45
  ## Write the entry only for a divergence — and name the layer that owned it
@@ -227,9 +230,92 @@ rule that was retired for going cold is exactly the rule about to be re-learned.
227
230
 
228
231
  A lesson that would be true in any repository does not belong in one project's
229
232
  retro — it belongs in the pipeline's own doctrine
230
- ([`learned.md`](learned.md), which is exactly that list, earned the same way). Open
231
- an issue upstream and say so in the entry. A local file that accumulates universal
232
- rules is a fork of the skill that nobody named.
233
+ ([`learned.md`](learned.md), which is exactly that list, earned the same way). A local
234
+ file that accumulates universal rules is a fork of the skill that nobody named.
235
+
236
+ **This said *"open an issue upstream"* for eight releases and named no repository, no
237
+ trigger and no authorization** — an instruction on rung 1 that everybody read as done.
238
+ The rest of this section is the mechanism.
239
+
240
+ ## Publishing the insight — the skill learns from every project that runs it
241
+
242
+ **One job: stop a lesson dying in the repository that learned it.**
243
+
244
+ A retro entry is written per project and read by that project's next stage 0. The
245
+ pipeline itself never sees it. So a defect in the **skill** — a gate that loops, a
246
+ doctrine promising what nothing enforces, a rule firing on the wrong shape — is
247
+ rediscovered independently in every project and fixed in none of them.
248
+
249
+ **Opt-in, per project, off by default** — `pipeline.json` → `retro.publish`:
250
+
251
+ ```json
252
+ "retro": {
253
+ "publish": {
254
+ "repo": "ssheleg/task-pipeline",
255
+ "label": "retro-insight",
256
+ "redact": "strict"
257
+ }
258
+ }
259
+ ```
260
+
261
+ Absent, nothing is published, and nothing is asked. **Silence arms nothing** — the same
262
+ floor deploy authorization uses, and for the same reason: this is an **outward act**,
263
+ and an outward act taken from a generic flag is an outward act nobody authorized
264
+ ([`continuity.md`](continuity.md) → *The limit, before the capability*).
265
+
266
+ **The body is printed in full before the issue is opened, every time.** Not a summary
267
+ of it, not its title — the string that will be sent. The operator standing at stage
268
+ 10's manual gate is already reading; showing them what leaves the machine costs one
269
+ block and is the only moment anyone can stop it.
270
+
271
+ ```
272
+ ── would open issue ──────────────────────────────────────────
273
+ repo: ssheleg/task-pipeline
274
+ label: retro-insight
275
+ title: [retro] a queue is not a diagnosis
276
+ <the whole body, verbatim>
277
+ ──────────────────────────────────────────────────────────────
278
+ opening… → #24
279
+ ```
280
+
281
+ **No `gh`, no network, no permission?** Print the body, say the issue was **not**
282
+ opened, and carry the exact text in the carry-over ledger. That is the honest
283
+ degradation; a second transport is not.
284
+
285
+ ## What may leave the project — the redaction list
286
+
287
+ An issue is a **public artefact in someone else's repository**. What travels is the
288
+ *class*; what stays is everything that identifies where it happened.
289
+
290
+ | Goes | Stays |
291
+ |---|---|
292
+ | the class of failure, stated in the abstract | the file, the function, the line it happened in |
293
+ | which stage owned it and which stage surfaced it | the repository, organisation, branch or commit |
294
+ | the doctrine or guard that missed it, by its name **in this skill** | any host path, absolute or relative |
295
+ | the fix by grade, and the check that would catch it | the code, the config values, the data |
296
+ | whether an existing standing instruction fired | any person, company, customer or product name |
297
+
298
+ Five rules, numbered so a reader can point at one:
299
+
300
+ 1. **No host paths.** Only paths inside task-pipeline itself — `references/…`,
301
+ `templates/…`, `test/validate.py`. An absolute path names a machine.
302
+ 2. **No host identifiers**: repository, organisation, branch, commit, tag, issue or PR
303
+ number belonging to the project the run happened in.
304
+ 3. **No code, no configuration values, no data** — not a snippet, not a redacted
305
+ snippet. A shape can be described in a sentence.
306
+ 4. **No names**: person, company, customer, employer, product.
307
+ 5. **The title states the class, not the incident** — *"a queue is not a diagnosis"*,
308
+ never *"our export job looped"*.
309
+
310
+ **The printed text and the sent text are one string.** Redacting after the print, or
311
+ printing a cleaned-up version of what is actually sent, is the false-success shape this
312
+ bundle names outright ([`gates.md`](gates.md) → *False success*): a mechanism reporting
313
+ on itself rather than on what it did.
314
+
315
+ **When in doubt the rule is subtraction, not judgement.** An insight that survives
316
+ losing a detail is still an insight; a detail that leaks cannot be recalled from an
317
+ index. If removing it makes the entry incomprehensible, the entry was about the project
318
+ and not about the skill — keep it local.
233
319
 
234
320
  ## Rationalizations
235
321
 
@@ -16,6 +16,7 @@ install.
16
16
  - Reviewer inputs
17
17
  - Controller rules
18
18
  - The rubric
19
+ - The independent reader — dispatched by a stage, and read by its output
19
20
  - Prompt — task review
20
21
  - Prompt — scoped re-review
21
22
  - Prompt — final whole-branch review
@@ -118,6 +119,61 @@ Review in this order; stop reading the diff only when you've covered all of it.
118
119
  Formatting nits that don't change meaning are not findings. Praise is not a
119
120
  finding either.
120
121
 
122
+ ## The independent reader — dispatched by a stage, and read by its output
123
+
124
+ **One job: stop *"a reader was requested"* from standing in for *"a reader
125
+ reported"*.** Those are different facts and they look identical in a transcript.
126
+
127
+ The rule this implements has been a standing instruction since 2026-08-08: **a change
128
+ that adds or widens a check gets an independent reader before merge**, because your own
129
+ probes exercise only the shapes you already thought of. Its retirement condition, written
130
+ at birth, was that the reader be *dispatched by a stage rather than by the repository
131
+ happening to run a bot on pull requests*. This section is that stage.
132
+
133
+ **It happened.** Four pull requests of almost nothing but check work were opened on one
134
+ day; the review application reported **`skipping`** on every one, and twenty-two new
135
+ guards merged with the author's own probes as their only reading. Nothing was violated.
136
+ Nothing read the reviewer's output either.
137
+
138
+ ### Who counts as the reader, in order
139
+
140
+ 1. **A subagent this run dispatches** — the only option whose execution the run can
141
+ observe end to end. Give it the diff package, the rubric, and the sentence *"the
142
+ author wrote both the check and its probes; find what neither could see."*
143
+ 2. **A review bot on the pull request** — acceptable, and it is a **third party with no
144
+ contract**: it may skip, rate-limit, or answer about a stale commit. Read its
145
+ verdict, not the fact that it was triggered.
146
+ 3. **A person.**
147
+
148
+ ### The three states, and one of them is not silence
149
+
150
+ Record exactly one, beside the gate verdict:
151
+
152
+ ```
153
+ reader: 6 findings, 4 confirmed — read, and it had something
154
+ reader: none found — read, and it did not
155
+ reader: NO READER — <why> — nobody read it
156
+ ```
157
+
158
+ **`NO READER` is a printed state, never an omission.** It carries the same law as
159
+ `dormant` and `skip` ([`gates.md`](gates.md) → *Progressive arming*): a mechanism that
160
+ prints nothing when it looked at nothing is indistinguishable from one that looked and
161
+ found nothing. Where the change added or widened a check, `NO READER` is also carried
162
+ into the close-out as an open requirement rather than a footnote — reporting a gap is
163
+ honest and is not a fix.
164
+
165
+ ### What the reader is asked
166
+
167
+ Not *"review this."* The dispatch names the blind spot it exists to cover:
168
+
169
+ - the probes and the checks were written from **one model of the problem** — where does
170
+ that model not reach?
171
+ - which shapes does the check's **scope** exclude, and is any of them shipped?
172
+ - is there a form of the defect that has **no pair to compare** — a single cell, a lone
173
+ directive, an absence?
174
+
175
+ Those three questions come from findings no author-written probe has ever produced here.
176
+
121
177
  ## Prompt — task review
122
178
 
123
179
  > You are reviewing one task of an implementation plan. Read, in order: