task-pipeline-skill 1.32.0 → 1.37.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.
@@ -4,18 +4,32 @@
4
4
  "title": "task-pipeline config",
5
5
  "description": "Generic contract for a pipeline config. An ordered list of stages; each stage is run by the host project's own skills/agents and guarded by a typed gate. The framework imposes no specific stages, skills, or gate assignments — those are entirely the host project's config. Copy pipeline.example.json and rewrite it to match your project.",
6
6
  "type": "object",
7
- "required": ["stages"],
7
+ "required": [
8
+ "stages"
9
+ ],
8
10
  "additionalProperties": true,
9
11
  "properties": {
10
- "version": { "type": "integer", "minimum": 1 },
12
+ "version": {
13
+ "type": "integer",
14
+ "minimum": 1
15
+ },
11
16
  "stages": {
12
17
  "type": "array",
13
18
  "minItems": 1,
14
19
  "description": "The pipeline stages, in order. Any number, any names — your project's real stages.",
15
- "items": { "$ref": "#/definitions/stage" }
20
+ "items": {
21
+ "$ref": "#/definitions/stage"
22
+ }
23
+ },
24
+ "release": {
25
+ "$ref": "#/definitions/release"
26
+ },
27
+ "run": {
28
+ "$ref": "#/definitions/run"
16
29
  },
17
- "release": { "$ref": "#/definitions/release" },
18
- "run": { "$ref": "#/definitions/run" }
30
+ "retro": {
31
+ "$ref": "#/definitions/retro"
32
+ }
19
33
  },
20
34
  "definitions": {
21
35
  "run": {
@@ -25,12 +39,17 @@
25
39
  "properties": {
26
40
  "loop": {
27
41
  "type": "object",
28
- "required": ["mode"],
42
+ "required": [
43
+ "mode"
44
+ ],
29
45
  "additionalProperties": true,
30
46
  "description": "Whether the run advances item by item without a discretionary check-in. It NEVER collapses a manual gate or an outward action — a generic flag is not a specific authorization.",
31
47
  "properties": {
32
48
  "mode": {
33
- "enum": ["off", "interval"],
49
+ "enum": [
50
+ "off",
51
+ "interval"
52
+ ],
34
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."
35
54
  },
36
55
  "interval": {
@@ -43,6 +62,18 @@
43
62
  "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."
44
63
  }
45
64
  }
65
+ },
66
+ "review": {
67
+ "type": "object",
68
+ "additionalProperties": true,
69
+ "description": "The review loop's ceiling. Absent, the default is 3 rounds per artifact. It is a DECISION POINT rather than a stop: at the cap the run prints new findings versus findings caused by its own fixes, per round, and either the pair ends the loop or the operator continues it out loud. A flat stop would be wrong — this repository's ten-round runs were still finding real defects on round nine. Doctrine: references/loop-guard.md.",
70
+ "properties": {
71
+ "maxRounds": {
72
+ "type": "integer",
73
+ "minimum": 1,
74
+ "description": "Rounds per artifact before the run stops reviewing and measures. Counted from the run ledger's `touch:` pass numbers, never from memory; a round that finds nothing ends the loop by definition and is not counted."
75
+ }
76
+ }
46
77
  }
47
78
  }
48
79
  },
@@ -50,48 +81,129 @@
50
81
  "type": "object",
51
82
  "additionalProperties": true,
52
83
  "description": "Optional release automation, entirely project-defined and INDIVIDUALLY TOGGLEABLE. Omit the whole object, or set enabled:false, to turn release automation off for this project. The framework ships an example workflow (.github/workflows/release.yml) whose job is gated on a repo variable so each project arms it on its own; this block is the declarative counterpart the orchestrator reads.",
53
- "required": ["enabled"],
84
+ "required": [
85
+ "enabled"
86
+ ],
54
87
  "properties": {
55
- "enabled": { "type": "boolean", "description": "Master on/off toggle. false (or the object omitted) = no release automation for this project." },
56
- "trigger": { "enum": ["tag", "manual", "push", "none"], "description": "What kicks off a release (e.g. a pushed vX.Y.Z tag, manual dispatch)." },
88
+ "enabled": {
89
+ "type": "boolean",
90
+ "description": "Master on/off toggle. false (or the object omitted) = no release automation for this project."
91
+ },
92
+ "trigger": {
93
+ "enum": [
94
+ "tag",
95
+ "manual",
96
+ "push",
97
+ "none"
98
+ ],
99
+ "description": "What kicks off a release (e.g. a pushed vX.Y.Z tag, manual dispatch)."
100
+ },
57
101
  "steps": {
58
102
  "type": "array",
59
- "items": { "type": "string", "minLength": 1 },
103
+ "items": {
104
+ "type": "string",
105
+ "minLength": 1
106
+ },
60
107
  "description": "Ordered release actions, in prose — project-specific (e.g. create a GitHub release, npm publish). Human-only steps (2FA publish) are named as such."
61
108
  },
62
109
  "verify": {
63
110
  "type": "array",
64
- "items": { "type": "string", "minLength": 1 },
111
+ "items": {
112
+ "type": "string",
113
+ "minLength": 1
114
+ },
65
115
  "description": "Post-release smoke checks that must pass after a release — this is the release's own post-deploy gate (stage 8 applied to shipping the package itself)."
66
116
  }
67
117
  }
68
118
  },
69
119
  "stage": {
70
120
  "type": "object",
71
- "required": ["state", "skills", "gate"],
121
+ "required": [
122
+ "state",
123
+ "skills",
124
+ "gate"
125
+ ],
72
126
  "additionalProperties": true,
73
127
  "properties": {
74
- "id": { "type": "integer", "description": "Optional ordinal." },
75
- "state": { "type": "string", "minLength": 1, "description": "Unique stable key for the stage." },
76
- "name": { "type": "string", "description": "Optional human label." },
77
- "model": { "type": "string", "description": "Optional model for the stage. Prefer a provider-agnostic token over a vendor id, which goes stale as generations ship and may not exist on the operator's provider at all: 'default' = the model confirmed for this run (recommended: the most capable reasoning model the environment offers), 'inherit' = whatever the operator is currently on. A literal id is allowed but treated as an example, not a contract." },
128
+ "id": {
129
+ "type": "integer",
130
+ "description": "Optional ordinal."
131
+ },
132
+ "state": {
133
+ "type": "string",
134
+ "minLength": 1,
135
+ "description": "Unique stable key for the stage."
136
+ },
137
+ "name": {
138
+ "type": "string",
139
+ "description": "Optional human label."
140
+ },
141
+ "model": {
142
+ "type": "string",
143
+ "description": "Optional model for the stage. Prefer a provider-agnostic token over a vendor id, which goes stale as generations ship and may not exist on the operator's provider at all: 'default' = the model confirmed for this run (recommended: the most capable reasoning model the environment offers), 'inherit' = whatever the operator is currently on. A literal id is allowed but treated as an example, not a contract."
144
+ },
78
145
  "skills": {
79
146
  "type": "array",
80
147
  "minItems": 1,
81
148
  "description": "The skill(s)/agent(s) that execute this stage. Any names your environment resolves — this is where you plug in your OWN skills.",
82
- "items": { "type": "string", "minLength": 1 }
149
+ "items": {
150
+ "type": "string",
151
+ "minLength": 1
152
+ }
83
153
  },
84
154
  "gate": {
85
155
  "type": "object",
86
- "required": ["type", "check"],
156
+ "required": [
157
+ "type",
158
+ "check"
159
+ ],
87
160
  "additionalProperties": true,
88
161
  "description": "Condition that must pass before advancing to the next stage.",
89
162
  "properties": {
90
163
  "type": {
91
- "enum": ["auto", "manual"],
164
+ "enum": [
165
+ "auto",
166
+ "manual"
167
+ ],
92
168
  "description": "auto = the orchestrator verifies `check` itself (pass/fail); manual = wait for the operator's explicit go."
93
169
  },
94
- "check": { "type": "string", "minLength": 1, "description": "The gate condition, in prose." }
170
+ "check": {
171
+ "type": "string",
172
+ "minLength": 1,
173
+ "description": "The gate condition, in prose."
174
+ }
175
+ }
176
+ }
177
+ }
178
+ },
179
+ "retro": {
180
+ "type": "object",
181
+ "additionalProperties": true,
182
+ "description": "Optional. Governs what the retrospective does BEYOND writing to the project's own docs/superpowers/retro.md, which always happens. Omit it and nothing leaves the repository: silence arms nothing, exactly as it authorises no deploy.",
183
+ "properties": {
184
+ "publish": {
185
+ "type": "object",
186
+ "required": [
187
+ "repo"
188
+ ],
189
+ "additionalProperties": true,
190
+ "description": "Publish skill-level insights as issues on the skill's own repository, so a defect in the pipeline is fixed once instead of rediscovered in every project. OFF unless this object is present — opening an issue in another repository is an outward act, and an outward act taken from a generic flag is one nobody authorized. The body is printed in full before it is sent, and the printed string and the sent string are the same string. Doctrine, including the five redaction rules: references/retrospective.md.",
191
+ "properties": {
192
+ "repo": {
193
+ "type": "string",
194
+ "pattern": "^[^/\\s]+/[^/\\s]+$",
195
+ "description": "owner/name of the SKILL's repository, never the host project's."
196
+ },
197
+ "label": {
198
+ "type": "string",
199
+ "description": "Applied to every issue opened this way, so the operator can read them as a stream rather than find them by accident."
200
+ },
201
+ "redact": {
202
+ "enum": [
203
+ "strict"
204
+ ],
205
+ "description": "Only 'strict' exists, and it is not a level among others: the five rules are the contract. A field with one legal value is here so a reader asking 'what redaction applies?' finds the answer in the config rather than assuming none does."
206
+ }
95
207
  }
96
208
  }
97
209
  }
@@ -59,9 +59,15 @@ at a glance.
59
59
  > external skill**. A host project may relocate the root via its `CLAUDE.md`; keep
60
60
  > the shape, keep the slugs.
61
61
 
62
- Loop-bearing runs also keep a **git-ignored** run ledger at `.task-pipeline/run.md`
63
- stage-level and program-level repeat touches, one line each, so the loop guard can
64
- detect churn after a lost context (see [`loop-guard.md`](loop-guard.md)).
62
+ **Every** run keeps a **git-ignored** run ledger at `.task-pipeline/run.md`, seeded at
63
+ stage 0 from [`../templates/run.md`](../templates/run.md). Three line shapes: a
64
+ `stage:` verdict when a gate returns, an `iter:` line when an iteration closes, and a
65
+ `touch:` line per file per repeating pass. Two readers depend on it —
66
+ [`loop-guard.md`](loop-guard.md) detects churn from the `touch:` lines after a lost
67
+ context, and [`progress.md`](progress.md) derives the stage rail and the iteration
68
+ counter from the other two. It was described as loop-only until 2026-08-10 and, in
69
+ practice, written by no run at all: the guard that calls its own detection *mechanical*
70
+ had no input in any run to date.
65
71
 
66
72
  Stage 5 also creates a **git-ignored** scratch workspace per plan at
67
73
  `.task-pipeline/build/<plan-basename>/` — ledger, task briefs, implementer reports,
@@ -158,12 +164,13 @@ plugins/task-pipeline/
158
164
  acceptance.md retrospective.md # stage 10 (close-out, then the retro)
159
165
  audit.md # cross-cutting: the ladder + seams
160
166
  loop-guard.md # cross-cutting: churn detection
167
+ progress.md # cross-cutting: what the run prints about itself
161
168
  stages.md model-tiering.md # gates, model policy
162
169
  conventions.md artifacts.md # host conventions, this layout
163
170
  companion-skills.md # optional companions + preflight
164
171
  templates/ # skeletons seeded into a host project
165
- hygiene.sh # -> scripts/check-hygiene.sh (stages 5, 6, 9)
166
- README.md brief.md carryover.md context.md adr.md retro.md
172
+ hygiene.sh docgate.sh # -> scripts/check-hygiene.sh, check-docs.sh
173
+ README.md # the index and it is the list, not a copy of it
167
174
  cursor/rules/task-pipeline.mdc # Cursor channel (self-contained rule)
168
175
  bin/task-pipeline.js # npx installer (package task-pipeline-skill)
169
176
  install.sh # POSIX installer
@@ -43,7 +43,8 @@ better, plus one that is required only for user-facing work.
43
43
 
44
44
  | Skill / tool | Needed for | Required? | Install |
45
45
  |---|---|---|---|
46
- | **super-ux** (`ux-foundation`, `ux-flows`, `ux-scenarios`, `ux-audit`, `/ux`, `/ux-lint`) | stage 3 UX track | **Required for any user-facing task** | `/plugin marketplace add ssheleg/super-ux` → `/plugin install super-ux@super-ux` (or `npx skills add ssheleg/super-ux`) |
46
+ | **super-ux** (`ux-foundation`, `ux-flows`, `ux-scenarios`, `ux-audit`, `/ux`, `/ux-lint` — **and the copy half**: `copywriting`, `brand-voice`, `/brand-init`, `/copy`, `/brand-lint`, plus `/vision`) | stage 3 — the **UX track** *and* the **COPY track**. This row named six surfaces until 2026-08-10 while super-ux shipped eight skills and fifteen commands: the whole brand-and-copy half was invisible to this pipeline, so a run built scenarios and screens and then wrote the interface strings by taste | **Required for any user-facing task** | `/plugin marketplace add ssheleg/super-ux` → `/plugin install super-ux@super-ux` (or `npx skills add ssheleg/super-ux`) |
47
+ | **sheleg-design** (`/sheleg-design`) | stage 3 — the **VISUAL track**: tokens and themes, typography and rhythm, motion and how it degrades to rest, the visual language a brand is recognised by. It answers *how it looks*, which no other companion here answers — `super-ux` decides what the interface must do, `copywriting` how it sounds. Before 2026-08-10 this skill appeared once in the whole bundle, as a name in a list | **Recommended** on any task with a visual surface; never a gate. Absent → the run says the visual layer shipped **undesigned**, which is the honest name for picking values at the keyboard | `/plugin marketplace add ssheleg/sheleg-design` → `/plugin install sheleg-design@sheleg-design-skill` |
47
48
  | **context7** (MCP — call tools fully qualified: `context7:resolve-library-id`, `context7:query-docs`) | stage 1 docs study | Recommended (web-search fallback) | connect the context7 MCP server |
48
49
  | **Figma** (MCP) | stage 3 UX track, when the project designs visually — super-ux mirrors each `SCR-` screen/state into a frame | Optional, **UI + Figma-on only**. Absent → super-ux degrades to text-only *by itself and never blocks*, so shipping a UI feature with no mockups becomes a silent scope call — which is why the stage-0 sweep decides it | connect the Figma MCP server (`/mcp`, or your claude.ai connectors) |
49
50
  | **[obsidian-wiki](https://github.com/ar9av/obsidian-wiki)** (`wiki-query`, `wiki-update`) | **stage 0 harvest** (query what's already known) **+ stage 9 sync** | **Recommended** — never a gate; absent → harvest runs on repo docs alone | `pip install obsidian-wiki` → `obsidian-wiki setup --vault /path/to/your/vault` |
@@ -77,9 +78,16 @@ exchange:
77
78
 
78
79
  ```
79
80
  Pipeline companions (stage doctrine is built in — nothing to install for it):
80
- ✗ super-ux — this task looks user-facing; required for the UX track:
81
+ ✗ super-ux — this task looks user-facing; required for the UX track,
82
+ and it also owns the COPY track (copywriting, brand-voice):
81
83
  /plugin marketplace add ssheleg/super-ux
82
84
  /plugin install super-ux@super-ux
85
+ ✗ sheleg-design — this task has a visual surface; it owns the VISUAL track:
86
+ tokens, themes, typography, rhythm, motion and its rest state:
87
+ /plugin marketplace add ssheleg/sheleg-design
88
+ /plugin install sheleg-design@sheleg-design-skill
89
+ (running without it — the visual layer ships undesigned,
90
+ and the close-out says so in those words)
83
91
  ✓ context7 — ready
84
92
  ✗ Figma MCP — this task is user-facing and the project designs in Figma
85
93
  (docs/ux/foundation.md → Design tooling). Without it the
@@ -120,7 +128,16 @@ Install the ✗ items you want, answer the model line, then say "continue".
120
128
  Rules:
121
129
 
122
130
  - Only flag **super-ux** when the task implies a UI (the stage-0 grill decides;
123
- when unsure, flag it — a false positive costs one install).
131
+ when unsure, flag it — a false positive costs one install). It arms **two** tracks,
132
+ not one: the UX chain and the copy layer.
133
+ - **sheleg-design**: flag it when the task has a **visual** surface — a page, a screen,
134
+ a themed component, a landing, a dashboard. Detect via a resolving `/sheleg-design`.
135
+ **A CLI, a library, a backend service or an internal script does not flag it**, and
136
+ neither does a purely structural change to an existing screen: choosing a palette for
137
+ a log parser is how a recommendation is taught to be noise, and this bundle already
138
+ spent a rule learning that about the browser. Absent → the run continues and says the
139
+ visual layer shipped **undesigned**; that is a weaker claim and the close-out records
140
+ it as one, exactly as it does for a surface verified by reading the diff.
124
141
  - **obsidian-wiki**: detect via `~/.obsidian-wiki/config` or a resolving
125
142
  `wiki-query`/`wiki-update`. Present → say `✓ ready` and use it in the harvest.
126
143
  Absent → print the two install lines **once** and continue; never ask twice in a
@@ -129,9 +129,26 @@ works a stale board for as long as the loop runs. One command, at the top of the
129
129
  iteration, recorded ([`knowledge-sources.md`](knowledge-sources.md) → *Carried-in
130
130
  claims*; [`learned.md`](learned.md) rule 16).
131
131
 
132
+ **The work-list is `docs/superpowers/backlog.md`** ([`backlog.md`](backlog.md)), and the
133
+ other half of the same measurement is the exposure line ([`exposure.md`](exposure.md)).
134
+ Counted at the top of the iteration, re-derived at the bottom — `age` moves on its own,
135
+ so the re-derivation is the only moment the board stops being stale.
136
+
132
137
  This is also where a loop's report goes wrong most quietly. *"Next up is X"* at the
133
138
  end of an iteration is a claim about the board, and it is the one sentence in the
134
- whole cycle that no gate reads. It cites the measurement or it is not written.
139
+ whole cycle that no gate reads. It cites the measurement or it is not written — **a
140
+ `B-NNN`, not a description**: *"next up: B-014"* can be checked against the file,
141
+ *"next up: the export fix"* cannot.
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.
135
152
 
136
153
  ## Parked at a manual gate
137
154
 
@@ -0,0 +1,110 @@
1
+ # Exposure — how much unverified work has piled up, and what to look at
2
+
3
+ **One job: turn the verification ledger into a number somebody can act on, without
4
+ pretending it is a probability.**
5
+
6
+ [`verification.md`](verification.md) records whether a person ever confirmed each shipped
7
+ REQ. This file turns that record into the line printed beside every verdict, and into the
8
+ list `/task-pipeline checkup` hands an operator.
9
+
10
+ ---
11
+
12
+ ## Contents
13
+
14
+ - Why it is not a probability
15
+ - The components, each named
16
+ - The check-list, and how it is ordered
17
+ - `/task-pipeline checkup`
18
+ - What the loop does with it
19
+ - Rationalizations
20
+
21
+ ## Why it is not a probability
22
+
23
+ The request that produced this file asked for *"the probability of an error"*. That is
24
+ not computable from these inputs, and a number presenting itself as `P(defect)` is the
25
+ false-success class this repository has spent its whole history removing: an estimate
26
+ wearing a measurement's clothes.
27
+
28
+ **So no percentage, ever** — the guard rejects a `%` on the exposure line. What ships is
29
+ a **vector with its components named**, and the reason is not fussiness: a single score
30
+ invites a threshold, and a threshold here is a target on `never`, which is the one thing
31
+ [`verification.md`](verification.md) says may never have one.
32
+
33
+ It could become a real probability later. `verification.md` is exactly the journal that
34
+ would make calibration possible after enough runs carry both a confirmation date and a
35
+ defect. Until then it is named honestly.
36
+
37
+ ## The components, each named
38
+
39
+ ```
40
+ exposure: N unverified · never checked · N releases carry one
41
+ ```
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
+
50
+ - **unverified** — rows whose `Human` reads `never`.
51
+ - **since** — days since the newest `Human` date. When **no** row has ever been
52
+ confirmed, this prints the literal **`never checked`**, not `0 days`: zero would read
53
+ as *checked today*, which is the opposite of the truth and exactly the kind of quiet
54
+ inversion this pipeline exists to prevent.
55
+ - **releases** — tags cut since that date, or since the first shipped row when there is
56
+ no date. It is the component an operator feels: *"how much has gone out on top of
57
+ something nobody looked at."*
58
+
59
+ Every component is derived from files in the repository. None is estimated.
60
+
61
+ ## The check-list, and how it is ordered
62
+
63
+ The list is the deliverable — a number without it tells somebody they have a problem and
64
+ not where. Ordered by what the repository can defend:
65
+
66
+ 1. **Oldest first**, by `Shipped in`. The longest-unconfirmed row is the one whose
67
+ context is most gone, and whose author is least likely to remember it.
68
+ 2. **Tie-broken by blast radius** where the board carries a row for it, reusing
69
+ [`backlog.md`](backlog.md)'s own stated input rather than inventing a weight here.
70
+
71
+ No third factor. A ranking with an unstated input is the hand-assigned priority the board
72
+ already refuses.
73
+
74
+ ## `/task-pipeline checkup`
75
+
76
+ A **mode of the command**, like `setup` — not a new command, because a second command
77
+ costs every surface a command touches and this repository has learned what that means.
78
+
79
+ **It runs with no task in flight, and that is the point.** Accumulated unverified work is
80
+ invisible *precisely because nobody is running a pipeline*; a check that only exists
81
+ inside a run can never say *"stop, fourteen things are unconfirmed."* So it takes no
82
+ brief, opens no grill, and writes nothing on its own.
83
+
84
+ It prints four sections, each read from a file this pipeline already keeps: the exposure
85
+ line and its check-list, the board's open rows by computed priority, the carry-over
86
+ ledgers' unresolved count, and the code graph's staleness where one exists.
87
+
88
+ **Where the operator asks it to file findings**, it appends board rows whose `Source`
89
+ names the checkup and its date — so a row a machine created is distinguishable from one a
90
+ run surfaced. It prints what it would add first. Never silently.
91
+
92
+ ## What the loop does with it
93
+
94
+ [`continuity.md`](continuity.md) has always required each iteration to re-measure the
95
+ work-list, and said that *"next up is X"* is a claim about the board that no gate reads.
96
+ [`backlog.md`](backlog.md) is that board; the exposure line is the other half of the same
97
+ measurement, and both are read at the top of an iteration and re-derived at the bottom.
98
+
99
+ An iteration that reports what it will do next **cites the file**: `B-014`, not *"the
100
+ export fix"*.
101
+
102
+ ## Rationalizations
103
+
104
+ | The excuse | What is actually true |
105
+ |---|---|
106
+ | "Give me one number, I'll decide the threshold" | The threshold would be a target on `never`, and the column would start lying within a week. The components are one line; read them. |
107
+ | "A percentage is easier to communicate" | It is easier to communicate because it says more than is known. That is the whole objection. |
108
+ | "The check-list is long, just show the top three" | Then the fourth is never checked and nobody knows it exists. Print it all; the operator can stop reading. |
109
+ | "Checkup duplicates stage 8" | Stage 8 verifies **this run's** deploy. Checkup asks what has accumulated across all of them, which no stage is ever in a position to ask. |
110
+ | "We'll run checkup when something breaks" | After a break you know where to look. The list exists for before. |
@@ -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,10 +52,12 @@ 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` |
58
59
  | **The verification ledger** — what shipped, and whether a human ever confirmed it | `references/verification.md` |
60
+ | **Exposure** — the unconfirmed count as a named vector, never a probability, and the `checkup` mode | `references/exposure.md` |
59
61
  | The retro: prune, cap, commits, archive | `references/retrospective.md` |
60
62
  | Rules earned by failure | `references/learned.md` |
61
63
  | **The routing default and its boundary** | `templates/routing-rule.md` |