task-pipeline-skill 1.39.0 → 1.41.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,170 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.41.0 — one line per run is a slope, not a bound
4
+
5
+ `retro.md` is read **in full** at stage 0, and its own doctrine called both read sections
6
+ *bounded by construction*. Measured 2026-08-10:
7
+
8
+ ```
9
+ standing instructions ~1 234 tok capped at ten
10
+ run stamps ~2 099 tok 27 rows, capped by nothing
11
+ recent log ~12 441 tok queried, not read
12
+ ```
13
+
14
+ The v1.38.0 audit found exactly this shape in the narrative log and moved it out of the
15
+ floor. It left the neighbour in the same file, with the same property, because the
16
+ neighbour's growth is **tidy** — one line per run. A tidy slope is still a slope: at a
17
+ hundred runs the stamp table alone is ~7 800 tokens of a floor the doctrine believes is
18
+ bounded.
19
+
20
+ **The cap is ten, and the cold trigger is why.** It reads *the last five run stamps*, so
21
+ ten is that with a margin and a rotated row can never be one the trigger needed. Eighteen
22
+ stamps rotated into `docs/superpowers/retro/2026-Q3.md`, whole, append-only. The stamp
23
+ section went **2 099 → 1 088 tok**, the read portion **3 333 → 2 335**, and the stage-0
24
+ floor to roughly **35 300**.
25
+
26
+ ### The reader found six ways past the cap, and one of them was the doctrine's own command
27
+
28
+ R-005's reader defeated the first cap guard six ways, each planted and watched passing:
29
+
30
+ - **a second `## Run stamps — …` heading in the same file** held forty more rows and
31
+ passed. The guard read a *section*; stage 0 reads the **file**;
32
+ - **one leading space** on a row — still a valid table row — hid it from `startswith("|")`;
33
+ - **the stamp command this doctrine ships** (`printf '%s · %s\n' …`) appends prose, not a
34
+ table row. An agent obeying the shipped instruction literally produced forty stamps the
35
+ guard could not see, with `unlooked: 0`;
36
+ - **`templates/retro.md`** ships the same table to every host project and was outside the
37
+ corpus — the fourth hand-written list this repository has caught, against an invariant
38
+ that says corpora are discovered;
39
+ - **rotation by deletion** passed: a stamp removed from `retro.md` and absent from the
40
+ archive is history destroyed, and the guard could not tell moving from deleting;
41
+ - and the row check — already narrowed twice — was defeated by **swapping the two items
42
+ around the `·`**, which is the standing instructions' `max 10` answering for the stamps
43
+ again, on the other side of the separator.
44
+
45
+ Both guards are rewritten to count by **predicate over a discovered corpus**: three stamp
46
+ shapes (table row, list item, and the `<date> · <sha>` line the doctrine writes), every
47
+ file carrying a `## Run stamps` section, and the doctrine's cap read from the segment that
48
+ names the stamps rather than from anywhere in the row.
49
+
50
+ **Four surfaces had never learned the rule** — the live section's own intro, stage 10's
51
+ prune, `templates/retro.md`, and `templates/retro-archive.md`, which had no destination
52
+ section for a rotation the doctrine names. And stage 0 still called *one line per run* a
53
+ bound. All five now say the same thing.
54
+
55
+ **A hand-written count in this release was wrong.** It said twenty-one stamps rotated;
56
+ computed, it is **eighteen** (28 → 10). In a repository whose loudest canon is *compute,
57
+ never restate*.
58
+
59
+ ### Two predicates that were answered by their neighbours
60
+
61
+ Both found by their own probes, both the same shape as the defects the last release's
62
+ reader named:
63
+
64
+ - the first version asked whether the file contained *"one line each"* and *"bounded by
65
+ construction"*. After the fix both survived **only inside the sentences criticising
66
+ them**, and the guard fired on its own correction. A predicate that cannot tell a claim
67
+ from its refutation has a false-positive budget above zero, which `gates.md` sets at
68
+ zero;
69
+ - scoped to the table row, it was then answered by the **standing instructions' own
70
+ `max 10`** in the same cell. It now reads only the part of the row after `Run stamps`.
71
+
72
+ - Guards: 248 → **250**.
73
+
74
+ ## v1.40.0 — the loop had a cadence and no queue
75
+
76
+ `run.loop` said how **often** to continue. It never said **what the next item is**, so an
77
+ armed mode still left the run choosing its next move by recollection — `learned.md` rule
78
+ 16, once per fire. And nothing scheduled the next turn at all: on 2026-08-10 a run of
79
+ this pipeline wrote *«продолжаю без остановки»* and the turn ended, because a sentence
80
+ about future behaviour is not a wakeup. That run is this release's occasion and its
81
+ evidence.
82
+
83
+ ### The queue is stage 2's
84
+
85
+ The **module map** when the brief was a platform, the plan's task list otherwise. Both
86
+ already existed, both were already ordered, and neither had ever been named as the thing
87
+ the loop walks. `run.loop.queue` names it; `run.loop.arm` says where the mode is armed,
88
+ and the default for a queue-bearing run is **after decomposition** — arming at preflight
89
+ arms a loop with nothing to walk.
90
+
91
+ Arming is a consequence, not a request, for the same reason the mode is recorded rather
92
+ than asked for: a capability the operator must remember to switch on is one they forget
93
+ on exactly the run that needed it.
94
+
95
+ **What arming does not change is stated where it could be missed.** The four stops are
96
+ the four stops; a `manual` gate still waits; an outward act still needs its own specific
97
+ authorization. *A generic flag is not a specific authorization*, and arming a queue is
98
+ the most generic flag there is — guarded, because that sentence is what the deploy floor
99
+ rests on.
100
+
101
+ ### `mode: dynamic`
102
+
103
+ `interval` was the only mode while a fixed tick was the only primitive. A harness that
104
+ can schedule its own next turn picks each delay from what it is waiting for, and **prints
105
+ the delay it chose** — the disclosure that replaces an interval run's job id. A run
106
+ silent about its pacing cannot be told apart from one that quietly stopped, which is the
107
+ claim this file already forbade for harnesses with no primitive at all.
108
+
109
+ ### The goal is re-read between items, not only the board
110
+
111
+ Each iteration already re-measured the work-list, which answers *what is open*. It did
112
+ not answer *whether the open thing still serves what this run was for*. A queue built at
113
+ stage 2 outlives the reason it was built, because the operator learns things between
114
+ items and says so. So the bottom of an iteration now quotes the goal, states whether the
115
+ next item still serves it, and re-orders or re-scopes when it does not — a row that stops
116
+ serving the goal leaves for the board with its reason.
117
+
118
+ A queue re-derived only by `age` and `sev` is honest about priority and silent about
119
+ purpose. Both numbers can be right while the run finishes something the operator stopped
120
+ wanting two items ago.
121
+
122
+ ### The reader found the contradiction, not the bug
123
+
124
+ R-005's reader defeated all five guards and then read the doctrine as a reader rather
125
+ than its author. Part 1a said *"arming is a consequence, not a request"* and stated its
126
+ trigger as a fact about the **work** — a queue with more than one item — with no
127
+ antecedent about configuration. Two sections above, the same file says **Default off.
128
+ Silence arms nothing, exactly as silence authorizes no deploy**, and `grill.md`'s deploy
129
+ floor is explicitly said to rest on that distinction. Read cold, Part 1a arms a loop in a
130
+ project with no `pipeline.json` at all.
131
+
132
+ The contradiction was in the phrasing, not the intent — `stages.md` had already restated
133
+ the rule with the antecedent intact. **Part 1a gave way**, and a guard now requires it to
134
+ state its precondition, because five guards checked for the presence of strings and not
135
+ one would have noticed either reading.
136
+
137
+ What the reader took apart in the guards, all verified by planting the defect and
138
+ watching `PASS`:
139
+
140
+ - **a deleted contract was a skip, not a failure.** Removing the whole `run.loop` block
141
+ left CI green with two tidy `unlooked` lines — and `run` allows additional properties,
142
+ so the example still conformed while meaning nothing;
143
+ - **`_loop_block` searched instead of addressing.** A deprecated top-level `loop` earlier
144
+ in file order answered for the real contract;
145
+ - **presence tests let the release's own thesis be reverted.** `arm` existed, so setting
146
+ the example back to `preflight` passed;
147
+ - **`if _qv and …` short-circuited itself** — an open string in place of the queue enum
148
+ passed, which is precisely the failure the guard was written for;
149
+ - **the floor guard had never tested its own rule.** Its phrase entered the file in
150
+ v1.11.0, twenty-nine releases earlier; both doctrinal statements could be deleted and a
151
+ Rationalizations row kept it green;
152
+ - **the dynamic-disclosure regex was content-blind** — a sentence keeping the words and
153
+ inverting the obligation passed — and it triggered off the prose word, so renaming the
154
+ mode switched the guard off;
155
+ - **`arm the mode` matched a bullet forbidding arming**, and `"loop"` matched any
156
+ sentence about any loop. Both now key on the schema's own tokens.
157
+
158
+ Guards 233 → **248**: seven more probes than the first pass shipped with, six of them for
159
+ fail sites that did not exist until the reader's findings were fixed.
160
+
161
+ ### Also
162
+
163
+ - **A guard was listing the legal modes instead of reading them.** Adding `dynamic`
164
+ failed the guard on a correct example — a check enforcing its own staleness. It now
165
+ reads the enum out of the schema.
166
+ - Guards: 233 → **248**, one per new fail site, each with its planted defect.
167
+
3
168
  ## v1.39.0 — the skill could not be reached by the word "audit"
4
169
 
5
170
  `references/audit.md` has said since v0.1.0 that an audit may be **the whole task** —
package/CONTRIBUTING.md CHANGED
@@ -191,7 +191,7 @@ reads those files, and a partial read shows whichever sections come first.
191
191
  `run` block in `pipeline.schema.json` carries the loop mode; the shipped example
192
192
  sets `run.loop.mode` explicitly rather than omitting it, because the example is
193
193
  what gets copied and an absent field reads as an oversight instead of a decision.
194
- *(guard: `no explicit run.loop.mode`)*
194
+ *(guard: `run.loop.mode is` … `and the schema's legal set is`)*
195
195
 
196
196
  **25. The run-wide mode is named by every stage that could be misled by it.**
197
197
  `SKILL.md`, `references/grill.md`, `references/build.md`, `references/stages.md`
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.39.0 |
15
+ | **Version** | 1.41.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` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.39.0",
3
+ "version": "1.41.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 closes with evidence, a work board and a verification ledger that outlive a run, an exposure line naming what shipped unconfirmed, a progress rail computed from the project's own config, a loop guard whose review ceiling measures rather than stops, and stage-3 tracks for what a product does, how it sounds and how it looks. Two modes need no task: `checkup` (what is unverified) and `setup` (audit existing docs). Retro insights can publish upstream as issues, opt-in and redacted.",
5
- "version": "1.39.0",
5
+ "version": "1.41.0",
6
6
  "author": {
7
7
  "name": "ssheleg",
8
8
  "url": "https://x.com/sshlg93"
@@ -46,7 +46,7 @@
46
46
  ],
47
47
  "gate": {
48
48
  "type": "manual",
49
- "check": "the user approves the design AND the UI verdict is recorded (does the task touch a user-facing surface — web/mobile/CLI/TUI? this arms the stage-3 UX track). Every REQ is answered by the design, or explicitly dropped by the operator into the carry-over ledger. For a platform (several independent capabilities or shippable surfaces): the module map specs/<topic>-modules.md is committed and approved — brick criteria met or excepted in writing, dependency graph acyclic, build order topological with the walking skeleton first, every REQ mapped to exactly one module, cross-module contracts named with their owner. Single-module work records 'single module: <name>' instead — a skipped decomposition is a recorded decision, never an omission"
49
+ "check": "the user approves the design AND the UI verdict is recorded (does the task touch a user-facing surface — web/mobile/CLI/TUI? this arms the stage-3 UX track). Every REQ is answered by the design, or explicitly dropped by the operator into the carry-over ledger. For a platform (several independent capabilities or shippable surfaces): the module map specs/<topic>-modules.md is committed and approved — brick criteria met or excepted in writing, dependency graph acyclic, build order topological with the walking skeleton first, every REQ mapped to exactly one module, cross-module contracts named with their owner. Single-module work records 'single module: <name>' instead — a skipped decomposition is a recorded decision, never an omission. THE LOOP'S ARMING STATE IS PRINTED — armed with its queue and pacing, or not armed with the reason; the queue exists at this stage and a loop armed earlier has nothing to walk."
50
50
  }
51
51
  },
52
52
  {
@@ -172,7 +172,12 @@
172
172
  ],
173
173
  "_run_note": "Run-wide pacing. Shipped explicitly OFF so this example DEMONSTRATES the default rather than relying on its absence — see references/continuity.md. Set mode to 'interval' and a project stops being asked whether to run item-by-item; it still stops at every manual gate. There is no context-budget field here on purpose: the threshold is not measurable, so it is unconditional doctrine, not config.",
174
174
  "run": {
175
- "loop": { "mode": "off", "interval": "5m", "command": "/loop" }
175
+ "loop": {
176
+ "mode": "dynamic",
177
+ "queue": "module-map",
178
+ "arm": "after-decomposition",
179
+ "command": "/loop"
180
+ }
176
181
  },
177
182
  "release": {
178
183
  "enabled": true,
@@ -48,18 +48,34 @@
48
48
  "mode": {
49
49
  "enum": [
50
50
  "off",
51
- "interval"
51
+ "interval",
52
+ "dynamic"
52
53
  ],
53
- "description": "off (the default when absent) = the run pauses between items as it always did. interval = the run is armed with the harness's own loop primitive and advances one item per fire, stopping only at a manual gate, an unresolvable block, a genuine ambiguity, or completion."
54
+ "description": "off (the default when absent) = the run pauses between items as it always did. interval = armed with the harness's fixed-tick primitive, one item per fire. dynamic = the harness schedules its own next turn and the run picks each delay from what it is waiting for; a wait on nothing is minutes, not a tick nobody chose. interval and dynamic stop at the same four things — a manual gate, an unresolvable block, a genuine ambiguity, completion — and neither collapses an outward act: a generic flag is not a specific authorization."
54
55
  },
55
56
  "interval": {
56
57
  "type": "string",
57
58
  "pattern": "^[0-9]+[smhd]$",
58
- "description": "Required when mode is 'interval'. Must divide its unit cleanly (5m, 10m, 2h — not 7m or 90m); a value that does not is rounded to the nearest that does, and the rounding is stated out loud. Pick the shortest interval that is longer than a typical item."
59
+ "description": "Required when mode is 'interval', and meaningless when it is 'dynamic' — a self-pacing run chooses each delay. Must divide its unit cleanly (5m, 10m, 2h — not 7m or 90m); a value that does not is rounded to the nearest that does, and the rounding is stated out loud. Pick the shortest interval that is longer than a typical item."
59
60
  },
60
61
  "command": {
61
62
  "type": "string",
62
63
  "description": "How this harness arms it, e.g. '/loop'. Harness-specific and therefore project-recorded rather than assumed: on a harness with no loop primitive, omit it — the mode then degrades to prose discipline plus the build ledger, and the run says so instead of implying it is armed."
64
+ },
65
+ "queue": {
66
+ "enum": [
67
+ "module-map",
68
+ "plan-tasks",
69
+ "none"
70
+ ],
71
+ "description": "What the loop walks. A loop with no queue is a timer: it says how often to continue and never what the next item is, so the run picks its next move by recollection. module-map = stage 2's decomposition (a platform); plan-tasks = stage 4's task list; none = the mode is armed for the stage boundaries only. Doctrine: references/continuity.md -> Part 1a."
72
+ },
73
+ "arm": {
74
+ "enum": [
75
+ "preflight",
76
+ "after-decomposition"
77
+ ],
78
+ "description": "When the mode is armed. preflight = at the top of the run, the original behaviour. after-decomposition = at the close of stage 2, once the queue exists and has more than one item — the default for a queue-bearing run, because a capability the operator must remember to switch on is one they forget on the run that needed it. Arming never collapses a manual gate or an outward action."
63
79
  }
64
80
  }
65
81
  },
@@ -11,6 +11,7 @@ almost no window left, loses the middle of it, and re-derives what it already di
11
11
 
12
12
  - The limit, before the capability
13
13
  - Part 1 — the loop
14
+ - Part 1a — the queue is stage 2's, and the loop arms on it
14
15
  - Arming it on Claude Code
15
16
  - Other harnesses, and honest degradation
16
17
  - What one iteration means
@@ -62,6 +63,62 @@ the boundary between one agent turn and the next. This file is that reach.
62
63
  is never asked again — which is the entire point. A mode that has to be requested
63
64
  every run is not a mode, it is a habit the operator maintains by hand.
64
65
 
66
+ ## Part 1a — the queue is stage 2's, and the loop arms on it
67
+
68
+ **A loop with no queue is a timer.** Until this section existed, `run.loop` said *how
69
+ often* to continue and never said *what the next item is*, so the mode could be armed
70
+ and still leave the run picking its next move by recollection — which is the failure
71
+ [`learned.md`](learned.md) rule 16 is about, running once per fire.
72
+
73
+ **The queue is the module map** ([`decomposition.md`](decomposition.md)) when the brief
74
+ was a platform, and the plan's task list otherwise. Both already exist and both are
75
+ already ordered; neither was ever named as the thing the loop walks.
76
+
77
+ **Arming is the execution of a recorded decision, not a fresh request.** Where the mode
78
+ is **recorded** and the queue has more than one item, the loop arms **at the close of
79
+ stage 2** and the run says so in one line. It is not asked for at that point, for the
80
+ same reason the mode is recorded rather than requested: re-asking would rebuild the habit
81
+ the config exists to retire.
82
+
83
+ **Where nothing is recorded, nothing arms.** Silence arms nothing here too — this section
84
+ moves *where* a recorded mode is armed, from preflight to the close of stage 2. It does
85
+ not make arming unconditional, and a reader who takes it that way would arm a loop in a
86
+ project with no `pipeline.json` at all. That reading was in this section's first draft;
87
+ an independent reader found it before it shipped.
88
+
89
+ **What arming does NOT change, and this is load-bearing:** the four stops are the four
90
+ stops. A `manual` gate still waits. An outward or irreversible act still needs its own
91
+ specific authorization — *a generic flag is not a specific authorization*, and arming a
92
+ queue is the most generic flag there is. Arming decides only that the run does not stop
93
+ to ask *"shall I take the next one?"*.
94
+
95
+ **`mode: dynamic` — when the harness paces itself.** `interval` was the only mode while
96
+ the only primitive was a fixed tick. A harness that can schedule its own next turn
97
+ should: the run picks the delay from what it is waiting for, and a wait on nothing is a
98
+ wait of minutes rather than a fixed tick nobody chose. On a harness with neither
99
+ primitive the mode degrades to prose discipline plus the build ledger, and **the run
100
+ says which one it is running** — the rule below about claiming a capability you do not
101
+ have is unchanged and applies to `dynamic` first.
102
+
103
+ ### The goal is re-read between items, not only the board
104
+
105
+ Each iteration already re-measures the work-list. That answers *what is open*. It does
106
+ not answer *whether the open thing still serves what this run was for* — and a queue
107
+ built at stage 2 outlives the reason it was built, because the operator learns things
108
+ between items and says so.
109
+
110
+ So at the bottom of an iteration, beside the re-derived `prio`:
111
+
112
+ 1. re-read the brief's goal — one line, quoted, not recalled;
113
+ 2. state whether the next item still serves it;
114
+ 3. if it does not, **re-order or re-scope the queue and say what moved and why.** A row
115
+ that stops serving the goal leaves for the board with its reason
116
+ ([`backlog.md`](backlog.md)), it does not get worked because it was next.
117
+
118
+ A queue re-derived only by `age` and `sev` is a queue that is honest about priority and
119
+ silent about purpose. Both numbers can be right while the run is finishing something the
120
+ operator stopped wanting two items ago.
121
+
65
122
  ## Arming it on Claude Code
66
123
 
67
124
  ```
@@ -85,10 +142,22 @@ silent failures otherwise:
85
142
  stops on day eight is worse than one that was never armed, because the operator
86
143
  believes work is still moving.
87
144
 
88
- Where `run.loop.mode` is `interval`, the **preflight arms it** and prints the job
89
- id and the cancel command. Arming is not a new decision at that point — the config
90
- is the recorded authorization, and re-asking would rebuild the habit the config
91
- exists to retire.
145
+ **Where it arms is `run.loop.arm`,** and there are two points because there are two
146
+ kinds of run:
147
+
148
+ - `preflight` — the top of the run. Right when the loop walks stage boundaries rather
149
+ than a list, and the only option before Part 1a existed.
150
+ - `after-decomposition` — the close of stage 2, once the queue exists and holds more
151
+ than one item. Arming earlier would arm a loop with nothing to walk.
152
+
153
+ Either way the run **prints the job id and the cancel command**, and arming is not a new
154
+ decision at that point — the config is the recorded authorization, and re-asking would
155
+ rebuild the habit the config exists to retire.
156
+
157
+ Under `mode: dynamic` there is no job id: the run schedules its own next turn each time
158
+ and prints **the delay it chose and why**, which is the same disclosure in the form that
159
+ mode has. A dynamic run that says nothing about its pacing is indistinguishable from a
160
+ run that quietly stopped.
92
161
 
93
162
  ## Other harnesses, and honest degradation
94
163
 
@@ -18,7 +18,7 @@ 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**) · **Run stamps** (one line each) | stage 0, **in full** — both are bounded by construction |
21
+ | `docs/superpowers/retro.md` — **one per project** | **Standing instructions** (max **10**) · **Run stamps** (max **10**, oldest rotate out) | stage 0, **in full** — both are bounded by a **cap**, which *one line each* never was |
22
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 |
23
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 |
24
24
 
@@ -32,6 +32,7 @@ file exists to stop.
32
32
 
33
33
  - Write the entry only for a divergence — and name the layer that owned it
34
34
  - Every lesson carries its commit
35
+ - The stamp table is capped at ten, and *one line per run* was never a cap
35
36
  - Rotation — the archive is how pruning stops losing things
36
37
  - Three grades of fix — take the highest one that can work
37
38
  - Stamp first, then prune, then write
@@ -79,6 +80,26 @@ mechanical: the project's documentation gate runs `git rev-parse --verify --quie
79
80
  <sha>^{commit}` over every backticked SHA in the retro and its archive
80
81
  ([`gates.md`](gates.md)).
81
82
 
83
+ ## The stamp table is capped at ten, and *one line per run* was never a cap
84
+
85
+ Measured 2026-08-10: standing instructions **~1 234 tok** behind a cap of ten, run stamps
86
+ **~2 099 tok over 27 rows** behind nothing. Both are read in full at stage 0 and both
87
+ were described as *bounded by construction*. One line per run is a **slope**: at a
88
+ hundred runs the stamp table alone is ~7 800 tokens of a floor the doctrine believes is
89
+ bounded.
90
+
91
+ This is the same shape the 2026-08-10 audit found in the narrative log and moved out of
92
+ the floor — and it left the neighbour in the same file, with the same property, because
93
+ the neighbour's growth is *tidy*. A tidy slope is still a slope.
94
+
95
+ **The cap is ten and the trigger is why.** The cold rule reads *the last five run
96
+ stamps*; ten is that with a margin, so a stamp rotating out can never be one the trigger
97
+ needed. At the eleventh, the oldest row moves — whole, with its verdict and its retro
98
+ column — into `docs/superpowers/retro/YYYY-QN.md` under `## Run stamps`, append-only,
99
+ like every other rotation. **The count is printed at the prune**, beside the standing
100
+ instructions' own count, so a table that stops rotating is visible rather than merely
101
+ large.
102
+
82
103
  ## Rotation — the archive is how pruning stops losing things
83
104
 
84
105
  At the prune, entries older than the last five run stamps **move** to
@@ -102,7 +102,7 @@ never that the work was skipped quietly.
102
102
  `affected` / `god-nodes` answer *reach*, which is what grep cannot), `CLAUDE.md`,
103
103
  `CONTEXT.md`/ADRs, `docs/` + `docs/ux/`, past pipeline briefs and carry-over
104
104
  ledgers, **the retro's standing instructions and run stamps** (`docs/superpowers/retro.md`,
105
- read **in full** — ten rows and one line per run, both bounded, and they bind this
105
+ read **in full** — ten standing rows and ten stamps, both bounded **by a cap**, and they bind this
106
106
  run; stamp each instruction as it fires. Its *Recent log* is **queried** by the
107
107
  task's nouns, not read: uncapped narrative inside a binding source is what makes the
108
108
  capped part get skimmed, [`retrospective.md`](retrospective.md)), the **knowledge wiki** if one is
@@ -258,13 +258,21 @@ never that the work was skipped quietly.
258
258
  its status, in build order with the walking skeleton first. Single-module work
259
259
  records `single module: <name>` in the design and moves on — a skipped
260
260
  decomposition is a decision, never an omission.
261
+ - **The queue exists here, so the loop arms here** ([`continuity.md`](continuity.md) →
262
+ *Part 1a*). Where `run.loop.arm` is `after-decomposition` and the map holds more than
263
+ one module, arm the mode at the close of this stage and print one line: the mode, and
264
+ either the job id and its cancel command (`interval`) or the delay chosen and why
265
+ (`dynamic`). Arming collapses no gate and authorizes no outward act; it decides only
266
+ that the run does not stop to ask *"shall I take the next one?"*. Single-module work
267
+ arms nothing and says so — a loop with one item is a timer.
261
268
  - **GATE (manual):** the user approves the design, the UI verdict is recorded,
262
269
  **every REQ is answered by the design** — a requirement the design doesn't
263
270
  address is either covered now or explicitly dropped by the operator, with the
264
271
  drop recorded in the carry-over ledger — **and, for a platform, the module map is
265
272
  approved**: brick criteria met or excepted in writing, dependency graph acyclic,
266
273
  build order topological, every REQ mapped to exactly one module, cross-module
267
- contracts named with their owner.
274
+ contracts named with their owner — **and the loop's arming state is printed**:
275
+ armed with its queue and pacing, or not armed with the reason.
268
276
 
269
277
  ## 3 — Spec — with UX track for user-facing tasks
270
278
  - **Freedom: medium** — what the contract says is judgement; which contracts must be locked is a list ([`gates.md`](gates.md) → *Axis C*).
@@ -648,7 +656,10 @@ never that the work was skipped quietly.
648
656
  with a floor, neither ever a target ([`gates.md`](gates.md) → *Disclosures*); **the
649
657
  retrospective is written — stamped first, then pruned, then the entry; the
650
658
  list at or under its cap, every deletion logged in the archive with its commit,
651
- entries older than five run stamps rotated into `docs/superpowers/retro/`, the run
659
+ entries older than five run stamps rotated into `docs/superpowers/retro/` **and the
660
+ stamp table itself held to ten — at the eleventh the oldest stamp rotates whole into
661
+ the same archive, and both counts print beside the verdict** (the stamp table is read
662
+ in full at stage 0, so *one line per run* is a slope the prune has to stop), the run
652
663
  stamped with its commit, every SHA in either file resolvable, and the
653
664
  counts printed beside this verdict**; **where `pipeline.json` → `retro.publish` is
654
665
  set, the skill-level insight is published as an issue on the skill's repository —
@@ -39,3 +39,13 @@ history attached**, which is what this section is for.
39
39
  | Date | id | Instruction | Trigger that retired it | Commit |
40
40
  |---|---|---|---|---|
41
41
  | <YYYY-MM-DD> | R-000 | … | became a check (`npm run lint:paths`) | `<sha>` |
42
+
43
+ ## Run stamps
44
+
45
+ Rotated out of `retro.md` when its live table passes ten. Append-only, whole rows —
46
+ the cold trigger reads the last five in the live file, so a stamp here is one it no
47
+ longer needs. Moving is not deleting: a stamp that leaves `retro.md` and appears
48
+ nowhere is history destroyed, not archived.
49
+
50
+ | Date | Topic | Commit | Verdict | Retro |
51
+ |---|---|---|---|---|
@@ -61,8 +61,9 @@ shape the hygiene gate's check 6 exists to find.
61
61
 
62
62
  ## Run stamps
63
63
 
64
- One line per run, appended at stage 10. This is what makes "five runs" countable
65
- without it the cold-rule is a guess and the prune becomes a mood.
64
+ One line per run, appended at stage 10, **capped at ten** at the eleventh the
65
+ oldest rotates whole into the archive. One line per run is a slope, not a bound:
66
+ this section is read in full at stage 0.
66
67
 
67
68
  | Date | Topic | Commit | Verdict | Retro |
68
69
  |---|---|---|---|---|