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 +165 -0
- package/CONTRIBUTING.md +1 -1
- package/SKILL-CARD.md +1 -1
- package/package.json +1 -1
- package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +7 -2
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +19 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +73 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +22 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +14 -3
- package/plugins/task-pipeline/skills/task-pipeline/templates/retro-archive.md +10 -0
- package/plugins/task-pipeline/skills/task-pipeline/templates/retro.md +3 -2
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: `
|
|
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.
|
|
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.
|
|
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.
|
|
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": {
|
|
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 =
|
|
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.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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** (
|
|
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
|
|
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
|
|
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
|
|
65
|
-
|
|
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
|
|---|---|---|---|---|
|