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.
- package/CHANGELOG.md +332 -0
- package/CONTRIBUTING.md +66 -0
- package/README.md +23 -0
- package/SKILL-CARD.md +1 -1
- package/cursor/rules/task-pipeline.mdc +34 -9
- package/evals/task-pipeline.evals.json +87 -0
- package/package.json +3 -2
- package/plugins/task-pipeline/.claude-plugin/plugin.json +2 -2
- package/plugins/task-pipeline/commands/task-pipeline.md +167 -56
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +6 -5
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +133 -21
- package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +12 -5
- package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +32 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +10 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/exposure.md +8 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +18 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +10 -5
- package/plugins/task-pipeline/skills/task-pipeline/references/learned.md +3 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md +52 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +190 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +90 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/review.md +56 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +123 -12
- package/plugins/task-pipeline/skills/task-pipeline/templates/README.md +1 -0
- 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:
|
|
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,
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
75
|
-
instructions are capped at ten precisely so that this is cheap
|
|
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:
|
|
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**) · **
|
|
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).
|
|
231
|
-
|
|
232
|
-
|
|
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:
|