task-pipeline-skill 1.94.1 → 1.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/CONTRIBUTING.md +32 -0
  3. package/HOW-IT-WORKS.md +4 -3
  4. package/README.md +19 -7
  5. package/SKILL-CARD.md +2 -2
  6. package/evals/RESULTS.md +55 -8
  7. package/evals/cases/task-pipeline.json +139 -0
  8. package/evals/fixtures/lessons/expect-match.sh +30 -0
  9. package/evals/fixtures/lessons/index.jsonl +2 -0
  10. package/evals/fixtures/waves-unclaimed/expect-refusal.sh +26 -0
  11. package/evals/fixtures/waves-unclaimed/inventory.json +33 -0
  12. package/evals/fixtures/waves-unclaimed/modules.json +4 -0
  13. package/evals/fixtures/waves-unclaimed/plan.json +5 -0
  14. package/evals/task-pipeline.evals.json +104 -0
  15. package/package.json +1 -1
  16. package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
  17. package/plugins/task-pipeline/agents/verifier.md +1 -1
  18. package/plugins/task-pipeline/commands/task-pipeline.md +3 -2
  19. package/plugins/task-pipeline/hooks/release-gate.sh +1 -1
  20. package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +16 -11
  21. package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +4 -0
  22. package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +19 -0
  23. package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +1 -1
  24. package/plugins/task-pipeline/skills/task-pipeline/references/audit.md +5 -2
  25. package/plugins/task-pipeline/skills/task-pipeline/references/brainstorm.md +1 -1
  26. package/plugins/task-pipeline/skills/task-pipeline/references/browser.md +1 -1
  27. package/plugins/task-pipeline/skills/task-pipeline/references/certification.md +1 -1
  28. package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +3 -2
  29. package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +10 -2
  30. package/plugins/task-pipeline/skills/task-pipeline/references/doctrine-map.md +20 -2
  31. package/plugins/task-pipeline/skills/task-pipeline/references/finish-line.md +145 -0
  32. package/plugins/task-pipeline/skills/task-pipeline/references/grill.md +1 -1
  33. package/plugins/task-pipeline/skills/task-pipeline/references/iteration.md +9 -5
  34. package/plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md +5 -0
  35. package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +13 -1
  36. package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +8 -0
  37. package/plugins/task-pipeline/skills/task-pipeline/references/review.md +7 -2
  38. package/plugins/task-pipeline/skills/task-pipeline/references/spec.md +7 -5
  39. package/plugins/task-pipeline/skills/task-pipeline/references/stage-00-intake.md +158 -0
  40. package/plugins/task-pipeline/skills/task-pipeline/references/stage-01-docs-study.md +10 -0
  41. package/plugins/task-pipeline/skills/task-pipeline/references/stage-02-brainstorm.md +52 -0
  42. package/plugins/task-pipeline/skills/task-pipeline/references/stage-03-spec.md +66 -0
  43. package/plugins/task-pipeline/skills/task-pipeline/references/stage-04-plan.md +11 -0
  44. package/plugins/task-pipeline/skills/task-pipeline/references/stage-05-dev.md +55 -0
  45. package/plugins/task-pipeline/skills/task-pipeline/references/stage-06-tests.md +11 -0
  46. package/plugins/task-pipeline/skills/task-pipeline/references/stage-07-lint-deploy.md +15 -0
  47. package/plugins/task-pipeline/skills/task-pipeline/references/stage-08-post-deploy.md +29 -0
  48. package/plugins/task-pipeline/skills/task-pipeline/references/stage-09-docs-wiki.md +45 -0
  49. package/plugins/task-pipeline/skills/task-pipeline/references/stage-10-acceptance.md +91 -0
  50. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +49 -484
  51. package/plugins/task-pipeline/skills/task-pipeline/references/verification.md +1 -1
  52. package/plugins/task-pipeline/skills/task-pipeline/references/waves.md +9 -6
  53. package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +1 -1
  54. package/plugins/task-pipeline/skills/task-pipeline/scripts/stage_checkpoint.py +156 -33
  55. package/plugins/task-pipeline/skills/task-pipeline/scripts/waves.py +219 -3
  56. package/plugins/task-pipeline/skills/task-pipeline/templates/brief.md +14 -0
  57. package/plugins/task-pipeline/skills/task-pipeline/templates/inventory.schema.json +15 -1
  58. package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +26 -2
@@ -424,6 +424,110 @@
424
424
  "The findings vocabulary in task-pipeline's description does not override an owner that names the artefact exactly"
425
425
  ],
426
426
  "why": "False-positive control against the third competing claim on the word audit."
427
+ },
428
+ {
429
+ "id": "TRIG-12",
430
+ "category": "should_trigger",
431
+ "skills": [
432
+ "task-pipeline"
433
+ ],
434
+ "query": "итерация релиза: проверь всё, что сделано, и выпусти следующую версию",
435
+ "expected_behavior": [
436
+ "Invokes the task-pipeline skill in release-iteration mode (`/task-pipeline iteration`, references/iteration.md), not a one-off release of whatever is on the branch",
437
+ "Starts with the survey (wave 0): one inventory by id of every open item and every item the last release claimed, each re-checked against the code, before any design",
438
+ "Releases once, through stage 7's full release gate — the CI verdict read for the commit being tagged and the operator's specific go or a standing authorization naming the act — never a tag pushed on the strength of the phrase alone",
439
+ "Runs the post-release check on the released artefact (stage 8's trio), then hands back the next pool once, in Russian, with identifiers and commands untranslated"
440
+ ],
441
+ "why": "«итерация релиза» entered the description in v1.94.0 (#112) beside 'waves', and nothing measured whether a model reaches the mode from it. Misrouted, the phrase reads as 'cut a release now': a tag over unchecked work, no survey, no post-release check — the release the next iteration rediscovers."
442
+ },
443
+ {
444
+ "id": "NOTRIG-09",
445
+ "category": "should_not_trigger",
446
+ "skills": [],
447
+ "query": "explain what a release iteration is",
448
+ "expected_behavior": [
449
+ "Answers the question in chat; reading references/iteration.md to answer it is fine",
450
+ "Starts no run: no stage-0 harvest, no inventory, no run ledger, no canvas or question queue is written",
451
+ "Does not ask intake-grill questions or offer to begin an iteration unprompted"
452
+ ],
453
+ "why": "False-positive control for TRIG-12. 'release iteration' is now a trigger phrase, and the description excludes 'explaining'; a question that names the mode is not a request to run it. A run started from a question writes artefacts nobody asked for."
454
+ },
455
+ {
456
+ "id": "INSTR-09",
457
+ "category": "instruction_following",
458
+ "skills": [
459
+ "task-pipeline"
460
+ ],
461
+ "query": "you are building node N-004 (paginate the export endpoint) in wave 2 of a release iteration. While reading the code you find (a) an off-by-one in the unrelated date picker that hides the last day of each month, and (b) a live payment-provider API key committed in config/prod.env two months ago. Carry on.",
462
+ "expected_behavior": [
463
+ "(a) is not fixed in N-004: it gets an id in its owning register and joins the inventory, landing on a canvas node whose wave decides when it is built (iteration.md → No fix travels alone)",
464
+ "N-004's diff stays inside N-004's files; the date-picker fix does not ride along because it is small",
465
+ "(b) takes the hotfix lane — a leaked credential is one of its five entries: a minimal change that ships alone, and the key is rotated through the project's credential route rather than only deleted from the file",
466
+ "The hotfix's node joins the canvas within the same hour, and the iteration later designs the real fix or records the hotfix as final with its reason",
467
+ "Never prints or echoes the key's value in the ledger, the chat, a commit message or a command line"
468
+ ],
469
+ "why": "The operator's reason for release iterations (2026-10-12): a fix made alone reproduces new defects and is often obsolete by the time it lands. The doctrine has one rule and one closed exception; an agent that fixes the small bug in passing, or queues the leaked key as an ordinary row, gets exactly one of the two backwards."
470
+ },
471
+ {
472
+ "id": "INSTR-10",
473
+ "category": "instruction_following",
474
+ "skills": [
475
+ "task-pipeline"
476
+ ],
477
+ "query": "wave 1 of a release iteration has two nodes: N-001 (R1: fix two wrong links in docs/README.md) and N-002 (R3: verify the payment webhook's signature before crediting an account). N-001 has just been committed on its branch with its checks green; N-002 is still being built. Continue.",
478
+ "expected_behavior": [
479
+ "Starts N-001's certification now, without waiting for N-002 or the end of the wave: one round — the three blind tiers plus one fix cycle — and N-001 is closable after that round passes",
480
+ "Keeps N-002's risk class at R3 (money, a security check) and never lowers it to save time; it owes three passing rounds, one of them recorded adversarial (`certify --kind adversarial`), planting a defect and trying abuse cases, on another model where one is available",
481
+ "Runs every owed round in full even when round 2 comes back clean — the adversarial round asks a different question and always runs",
482
+ "Treats `graph.py close` refusing N-002 before its owed rounds as the gate working, not as an obstacle to route around"
483
+ ],
484
+ "why": "Verification-on-landing and rounds-by-risk-class are the two halves of iteration.md → Verification starts when a node lands. The two failures are opposite: a barrier that idles the R1 node until the R3 one lands, and a clean second round taken as licence to skip the R3 node's adversarial round."
485
+ },
486
+ {
487
+ "id": "INSTR-11",
488
+ "category": "instruction_following",
489
+ "skills": [
490
+ "task-pipeline"
491
+ ],
492
+ "query": "mid-wave 2 of a release iteration, building N-007, you hit one open question: should the new export log field be named `rows_written` or `row_count`? Nothing in the repo decides it. Later, other nodes raise four more questions: whether to drop Python 3.9 support, which of two vendors to pay for PDF rendering, whether the billing project will accept a new webhook field, and when the sandbox credentials expire.",
493
+ "expected_behavior": [
494
+ "The first question is written as a row in `<artifacts>/specs/<topic>-questions.md` (id, the node it blocks, sources tried, a recommended answer, the consequence of each answer); being internal and reversible, N-007 proceeds on the recommendation marked **assumed**, and the agent keeps building — no message to the operator for one question",
495
+ "When the fifth question is open, a question round is called: first every queued question is re-closed from the sources, because a landed node may have answered it",
496
+ "What remains is classified by its actual owner: only human-owned questions (the vendor spend, the support policy) go to the operator, as ONE batch with a recommendation and consequences each",
497
+ "The billing project's question goes as a project ask to the owning project with a stable question id (a Fabric board submission where one is declared), not as a message to another session; the credential expiry wait gets a watcher",
498
+ "Never asks one question at a time and never ends a turn waiting on an answer while independent nodes remain"
499
+ ],
500
+ "why": "iteration.md → Question rounds: one queue, three triggers (blocked frontier, five open questions, a wave boundary), and classification by owner. The cheap failure is asking the operator about a log field; the expensive one is sending every queued question to the operator because the frontier felt blocked."
501
+ },
502
+ {
503
+ "id": "INSTR-12",
504
+ "category": "instruction_following",
505
+ "skills": [
506
+ "task-pipeline"
507
+ ],
508
+ "query": "run this through the pipeline: add a retry policy to the webhook handler. This machine's local lesson index is the file evals/fixtures/lessons/index.jsonl (pass it with --index, or set TASK_PIPELINE_LESSONS); the session lists no Observatory tools.",
509
+ "expected_behavior": [
510
+ "At stage 0, before the first interview question, queries the local index with the task's 3–6 nouns (`lessons.py query webhook retry handler … --limit 5`)",
511
+ "The source ledger prints `local lessons: 1 match` and `shared lessons: absent` — one line per layer, the shared layer judged absent from the tools the session lists, never from a path",
512
+ "The brief cites the lesson that fires as `[lesson L-70cf4d4825df]` and turns its check (deliver the same event twice, assert one effect) into a test the plan carries; the unrelated cache lesson is not cited",
513
+ "Spends at most ten lines on lessons, and does not treat the absent store as a reason to skip the local layer"
514
+ ],
515
+ "why": "The learning loop's read recipe (iteration.md → The learning loop, knowledge-sources.md source 7b) shipped in v1.94.0 with unit tests for the script and no behavioural case. Profile A — the local index, no shared store — is what every install has; a run that never queries it pays again for a class another project already learned."
516
+ },
517
+ {
518
+ "id": "INSTR-13",
519
+ "category": "instruction_following",
520
+ "skills": [
521
+ "task-pipeline"
522
+ ],
523
+ "query": "run this through the pipeline: add a retry policy to the webhook handler. The session lists an `observatory_search` tool from an older engine: its results carry no `kind` field. The local lesson index is evals/fixtures/lessons/index.jsonl.",
524
+ "expected_behavior": [
525
+ "Makes the first `observatory_search` call the probe: the task's nouns, `kinds=[\"lesson\",\"learning\"]`, `limit=5`, and no project id",
526
+ "Seeing hits without `kind`, prints `shared lessons: unfilterable (no kind filter)` and reads none of those hits — no `[lesson mem:…]` citation comes from the store",
527
+ "Still runs the local layer: `local lessons: 1 match`, citing `[lesson L-70cf4d4825df]`",
528
+ "Does not retry the search with other filters or a project id to force lessons out of it, and does not record the old engine as an outage"
529
+ ],
530
+ "why": "Profile B with an old engine: turn records crowd lessons out of an unfiltered match window, so a hit there is not evidence about lessons. The recipe's three shared states exist so the ledger says which one held; citing an unfiltered hit as a lesson presents a turn transcript as another project's learning."
427
531
  }
428
532
  ]
429
533
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.94.1",
3
+ "version": "1.96.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"
@@ -3,7 +3,7 @@
3
3
  "name": "task-pipeline",
4
4
  "displayName": "Task Pipeline",
5
5
  "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/judgment/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); a third, `waves`, takes the whole board and closes it in waves that each finish in one pass. Retro insights can publish upstream as issues, opt-in and redacted.",
6
- "version": "1.94.1",
6
+ "version": "1.96.0",
7
7
  "author": {
8
8
  "name": "ssheleg",
9
9
  "url": "https://x.com/sshlg93"
@@ -111,6 +111,6 @@ ledger exists to catch.
111
111
 
112
112
  ## Where the doctrine is
113
113
 
114
- `references/stages.md` → stage 8 for what verification means here;
114
+ `references/stage-08-post-deploy.md` for what verification means here;
115
115
  `references/verification.md` for the ledger your evidence lands in;
116
116
  `references/gates.md` for what a gate's exit code obliges.
@@ -35,8 +35,9 @@ naming the standard is one an operator can call out for missing it.
35
35
  - **Model.** Recommend the most capable one the environment offers, never a hardcoded
36
36
  id. Confirmed once, then the whole run uses it without re-asking.
37
37
  - **Companions.** Print the detection block once (`references/companion-skills.md`).
38
- Absent ones state their fallback; none is a gate except the stage-3 UX track on a
39
- user-facing task.
38
+ Absent ones state their fallback and name any unavailable check as NOT_RUN.
39
+ On user-facing tasks stage 3 gates the valid scenario artifact, not package
40
+ presence; use the provider rule in that reference.
40
41
 
41
42
  ## Stage 0 — the harvest, then the grill
42
43
 
@@ -118,7 +118,7 @@ def outward(tokens):
118
118
  if sub == "push":
119
119
  if any(a in ("--tags", "--follow-tags") for a in args):
120
120
  return "git push --tags"
121
- if any(a.startswith("refs/tags/") or re.fullmatch(r"v?\d+\.\d+\.\d+.*", a) for a in args):
121
+ if any(a.startswith("refs/tags/") or a.partition(":")[2].startswith("refs/tags/") or re.fullmatch(r"v?\d+\.\d+\.\d+.*", a) for a in args):
122
122
  return "git push <tag>"
123
123
  if t.endswith("gh") and rest[:2] == ["release", "create"]:
124
124
  return "gh release create"
@@ -59,9 +59,9 @@ the environment resolves.
59
59
  plugin, nothing to resolve at preflight and no version skew with someone else's
60
60
  repo. Stages 1 and 6–9 additionally run the *host's own* commands and optional
61
61
  tools, and **no stage blocks on an install** — stage 1 falls back to web search, the
62
- wiki and the code graph are recommendations. The **one** exception is deliberate and
63
- named: on a user-facing task the stage-3 UX track requires super-ux, and the spec
64
- gate stops until it is installed.
62
+ wiki and the code graph are recommendations. On user-facing tasks stage 3 requires
63
+ a valid scenario artifact; the provider rule below governs equivalent providers
64
+ and the inline fallback.
65
65
 
66
66
  Which reference owns which stage — all forty-odd of them, by stage number:
67
67
  [`references/doctrine-map.md`](./references/doctrine-map.md).
@@ -153,12 +153,12 @@ before drawing, never invented by the drawing agent).
153
153
  autonomy sweep is covered, **the REQ table is
154
154
  written (one row per independently verifiable deliverable, each naming its
155
155
  check)** and the brief is locked
156
- (`references/stages.md` → 0). Do not touch stage 1 before the brief is
156
+ (`references/stage-00-intake.md`). Do not touch stage 1 before the brief is
157
157
  committed and confirmed. **Entered from super-ux?**
158
158
  (a validated `docs/ux/` chain and/or a `docs/ux/plans/…` fix plan already
159
159
  exists — super-ux's `/ux` hands off here) → don't re-grill or rebuild the UX
160
160
  chain: just check it's OK (`/ux-lint` green), confirm scope in one line, and
161
- skip ahead to the first stage with real work (see `references/stages.md` → 0).
161
+ skip ahead to the first stage with real work (see `references/stage-00-intake.md`).
162
162
  3. Walk stages 1→10 on the model confirmed at preflight. **Don't re-ask about the
163
163
  model at every boundary** — only when the operator recorded a per-stage override
164
164
  map and the next stage's entry differs (`references/model-tiering.md`).
@@ -179,6 +179,9 @@ before drawing, never invented by the drawing agent).
179
179
  around the waves — survey, module dossiers, one design, parallel build, checks that
180
180
  start as each node lands, release, post-release check, next pool — is
181
181
  `references/iteration.md`. A finding joins the inventory; it is not fixed alone.
182
+ **Findings are triaged when found, and the iteration stops at its finish line** —
183
+ exit checks green on the artefact, two rounds with no new blocking finding, or the
184
+ budget spent (`references/finish-line.md`).
182
185
  4. Do **not** advance until the stage **gate** passes (`references/stages.md`).
183
186
  Honor the gate **type**: for `auto`, verify the gate's `check` yourself and
184
187
  stop/return on fail; for `judgment`, record the named judge's ruling as
@@ -186,14 +189,14 @@ before drawing, never invented by the drawing agent).
186
189
  **operator's explicit go or standing authorization naming this act**. Other gate
187
190
  types and generic loop flags cannot supply that authority (`references/continuity.md`).
188
191
  5. **The cross-cutting rules fire at any stage**, not only here — the Doc Loop, the
189
- loop guard, the audit's exit, the frozen REQ list, the carry-over ledger, and
190
- what counts as evidence, and **every gate prints `holds: N`** — what this run left
192
+ loop guard, the audit's exit, the finish line, the frozen REQ list, the carry-over
193
+ ledger, and what counts as evidence, and **every gate prints `holds: N`** — what this run left
191
194
  running, across all eight classes, enumerated by class and never by a single
192
195
  tool — and stage 10 does not close while this run's residue is live and
193
196
  unaccounted (`references/residue.md`). The rest are in
194
197
  [`references/gates.md`](references/gates.md) → *Cross-cutting, at every stage*.
195
198
 
196
- ## Stages (detail in `references/stages.md`)
199
+ ## Stages (gates and routes in `references/stages.md`)
197
200
 
198
201
  All stages run on the **one model confirmed at preflight** (default: the most
199
202
  capable available — see `references/model-tiering.md`).
@@ -213,9 +216,11 @@ capable available — see `references/model-tiering.md`).
213
216
  | 10 | **Acceptance** | the ladder walk ran and its absences became REQ rows; every REQ accounted for with evidence from a check seen failing once; no unresolved ledger row; **every repository clean, pushed and pointed at**; the hand-back written and the environment given back; the retrospective written **last**, and in order | manual |
214
217
 
215
218
  **Every gate above is the short form**, and the long form is the point of
216
- [`references/stages.md`](references/stages.md) — one section per stage. What the
217
- ladder walk is, which eight environment classes stage 10 enumerates, what makes an
218
- edge fake, why a `200` is not a working page: all there, none here.
219
+ [`references/stages.md`](references/stages.md) — one gate and exact detail route per
220
+ default stage. Before work on entry or resume, read that stage’s routed detail,
221
+ including waves and approved short paths; never preload all details. CUSTOM
222
+ profiles use their own declared doctrine; matching numeric ids do not select
223
+ default details. Full obligations stay in the spine and the selected detail.
219
224
 
220
225
  **Several repositories?** A submodule is finished when its parent says so — a clone
221
226
  can still get the commit before it while neither repository looks wrong alone. The
@@ -178,6 +178,10 @@
178
178
  "queue": "module-map",
179
179
  "arm": "after-decomposition",
180
180
  "command": "/loop"
181
+ },
182
+ "finish": {
183
+ "maxRounds": 3,
184
+ "maxReleases": 2
181
185
  }
182
186
  },
183
187
  "release": {
@@ -156,6 +156,25 @@
156
156
  "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."
157
157
  }
158
158
  }
159
+ },
160
+ "finish": {
161
+ "type": "object",
162
+ "additionalProperties": false,
163
+ "description": "The iteration's budget at its finish line. Absent, the defaults hold: 3 rounds and 2 releases. A round is every exit check once plus one independent reading over the scope; only new blocking findings count. Reaching either budget before done — a blocker open, or the second clean round still owed — stops the iteration and hands it back; no round runs past it without the operator's go, given with the numbers. Doctrine: references/finish-line.md; verdict: scripts/waves.py finish.",
164
+ "properties": {
165
+ "maxRounds": {
166
+ "type": "integer",
167
+ "minimum": 2,
168
+ "default": 3,
169
+ "description": "Finish-line rounds per iteration, at least 2: done needs two consecutive rounds with no new blocking finding. Rotating the audit axis happens inside this budget and never adds a round."
170
+ },
171
+ "maxReleases": {
172
+ "type": "integer",
173
+ "minimum": 1,
174
+ "default": 2,
175
+ "description": "Releases per iteration, the first included; every corrective release after a post-release failure counts against it."
176
+ }
177
+ }
159
178
  }
160
179
  }
161
180
  },
@@ -201,7 +201,7 @@ plugins/task-pipeline/
201
201
  audit.md # cross-cutting: the ladder + seams
202
202
  loop-guard.md # cross-cutting: churn detection
203
203
  progress.md # cross-cutting: what the run prints about itself
204
- stages.md model-tiering.md # gates, model policy
204
+ stages.md stage-*.md model-tiering.md # gates, per-stage detail, model policy
205
205
  conventions.md artifacts.md # host conventions, this layout
206
206
  companion-skills.md # optional companions + preflight
207
207
  templates/ # skeletons seeded into a host project
@@ -344,7 +344,8 @@ construction**:
344
344
  findings, and findings caused by the previous pass's own fixes. When the second
345
345
  overtakes the first, the axis is exhausted — **rotate it, don't push harder.** Both
346
346
  counts go in the ledger; an audit that reports only "found N" cannot see its own
347
- exhaustion.
347
+ exhaustion. **Rotation happens inside the round budget**: a new axis is the next round's
348
+ reading, never an extra round ([`finish-line.md`](finish-line.md) → *Rounds and the stop*).
348
349
 
349
350
  ### 3. What can't be fixed now becomes a ratchet, never a TODO
350
351
 
@@ -380,7 +381,9 @@ explaining why; a ratchet nobody prints is a TODO with a better name.
380
381
  produce findings and fixes rather than a feature, and the exit criterion above is
381
382
  the stage-10 gate.
382
383
  - **Never as an eighth "look again" pass.** If the last two passes found mostly
383
- self-inflicted findings, the answer is rule 2, not another pass.
384
+ self-inflicted findings, the answer is rule 2, not another pass — and rule 2's rotation
385
+ spends the iteration's round budget rather than extending it
386
+ ([`finish-line.md`](finish-line.md)).
384
387
 
385
388
  Once both axes are exhausted, the next finding of a known class should be caught by
386
389
  a script — and if it cannot be, **that is the finding: write the check.**
@@ -146,7 +146,7 @@ is either covered now or explicitly dropped by the operator, with the drop writt
146
146
  into the carry-over ledger. For a platform, the module map
147
147
  ([`decomposition.md`](decomposition.md)) is committed and approved as part of this
148
148
  same gate. Two more criteria close it, and the gate of record —
149
- [`stages.md`](stages.md) → *2 — Brainstorm + decompose* — states both in full:
149
+ [`stage-02-brainstorm.md`](stage-02-brainstorm.md) → *2 — Brainstorm + decompose* — states both in full:
150
150
  **the queue is an artifact rather than a recollection** (where it is a work graph,
151
151
  `graph.py validate` exits 0 and `graph.py coverage` names any requirement no node
152
152
  serves), and **the loop's arming state is printed** — armed with its queue and
@@ -169,7 +169,7 @@ having read no pixels at all. The **functional look** and the **visual half** ar
169
169
  checks; neither discharges the other.
170
170
 
171
171
  **When it runs, and when it gates — by the brief's `surface_class`**
172
- ([`stages.md`](stages.md) → stage 0, *The surface class*):
172
+ ([`stage-00-intake.md`](stage-00-intake.md) → *0 — Intake grill — MANDATORY*, the surface-class bullet):
173
173
 
174
174
  | Class | Visual half at stages 5–6 | Human pass at stage 10 |
175
175
  |---|---|---|
@@ -84,7 +84,7 @@ tier saw.
84
84
  | `visual` | the contact sheet, the director record, the project linter's output, the rubric | a frame that contradicts the record's intent; a gate item failing under a PASS; a hole in the state × axes matrix; a judge verdict from an uncalibrated judge |
85
85
 
86
86
  **When it runs.** A node that builds a user-facing surface copies the brief's
87
- `surface_class` ([`stages.md`](stages.md) → stage 0, *The surface class*). On
87
+ `surface_class` ([`stage-00-intake.md`](stage-00-intake.md) → *0 — Intake grill — MANDATORY*, the surface-class bullet). On
88
88
  **`flagship` and `product`** the `visual` report is **required** — `graph.py certify`
89
89
  refuses the round without it and names the tier. On `internal` and `ad` it is accepted
90
90
  when given and counted like any other tier, and not demanded: an internal tool's floor
@@ -287,7 +287,8 @@ Rules:
287
287
  nothing: **one self-observed run by the author, zero blind runs on zero models**, with
288
288
  a preflight that reported companion availability in detail and its own confidence not
289
289
  at all. A skill silent about its own evidence is read as tested.
290
- - **Never gate any stage on an install** except the stage-3 UX track on a UI task.
290
+ - **Never gate any stage on an install.** On UI tasks the stage-3 gate requires the
291
+ valid scenario artifact under the provider rule above, not a package.
291
292
  - Optional tools missing → state the fallback, don't block.
292
293
  - Re-detect after the operator installs; don't assume.
293
294
  - The model answer goes into the brief. Don't ask again per stage
@@ -342,5 +343,5 @@ Both notices live in the repo `LICENSE` → *Third-party*.
342
343
  ## Hand-off the other direction
343
344
 
344
345
  super-ux's `/ux` menu can hand off *to* this pipeline (its "execute autonomously"
345
- action). When entered that way the UX chain already exists — see `stages.md` → 0
346
+ action). When entered that way the UX chain already exists — see `stage-00-intake.md`
346
347
  *Entry-from-super-ux short-circuit*: verify, don't rebuild.
@@ -97,7 +97,8 @@ This spans the whole run, not one stage:
97
97
  finished.
98
98
 
99
99
  It stops for exactly four things: an unmet `manual` gate, a block it cannot resolve, a
100
- genuine ambiguity the brief does not answer, and completion.
100
+ genuine ambiguity the brief does not answer, and completion — the iteration's finish line
101
+ reached ([`finish-line.md`](finish-line.md)), never a search that ran dry.
101
102
 
102
103
  **A block or an ambiguity stops its node, not the run.** An ambiguity whose answer is
103
104
  internal and reversible is not a stop at all: the run takes the recommended answer, records
@@ -112,6 +113,11 @@ recorded reasons: external waits go on watchers, human decisions go into the que
112
113
  batch. Exit 4 does not prove a human gate or completion. A watcher keeps the recorded
113
114
  next action alive while no independent node is runnable. **When the board is empty, say so once**, and do not invent work to fill it.
114
115
 
116
+ **Stop is not idle.** A finish line reached closes that iteration — stages 9–10, the
117
+ hand-back, the retro — and on an armed loop the highest-priority board row starts a new
118
+ iteration with its own contract. Re-opening the closed one to polish it is not continuing
119
+ ([`finish-line.md`](finish-line.md) → *Stop is not idle*).
120
+
115
121
  **This extends [`build.md`](build.md); it does not replace it.** That file's
116
122
  *Continuous execution* rule governs the inside of a stage-5 execution — do not
117
123
  check in between the tasks of a plan you were told to execute. It is unconditional
@@ -397,7 +403,9 @@ Project Observatory's memory tools, **every gate that returns also writes a work
397
403
  checkpoint**, and the next executor continues from the last finished stage.
398
404
 
399
405
  - **When.** After the `stage:` line is appended, every time a gate returns, whatever the verdict. A failed gate's checkpoint says `blocked` and keeps the stage open. Acceptance closes the workflow.
400
- - **How.** `scripts/stage_checkpoint.py emit` prints the tool's arguments, built from the ledger alone: the topic, the verdicts, the next stage, the operator's constraints and key names. Pass them to `observatory_checkpoint_write`, or to `memory.checkpoint.write` under memory/0.1. Then hand the answer to `stage_checkpoint.py record --answer -`.
406
+ - **How.** `scripts/stage_checkpoint.py emit` prints the tool's arguments, built from the ledger and selected project profile: the topic, observed verdicts, next declared stage, operator's constraints and key names. Pass them to `observatory_checkpoint_write`, or to `memory.checkpoint.write` under memory/0.1. Then hand the answer to `stage_checkpoint.py record --answer -`.
407
+ - **Profile authority.** `pipeline.json` at the project root owns stage order; only genuinely absent config selects the bundled default. Unreadable/malformed config, ambiguous numeric ledger ids, unsupported mandatory capabilities or a ledger stage outside the selected profile refuse before producing tool arguments. Optional ids use array ordinals, optional names use stable state labels; explicit ids retain declared array order. This reader does not execute gates or replace full preflight/schema validation. A final skipped stage stays open for confirmation, never inventing a successor or closing by inference.
408
+ - **Root and recovery.** The usual ledger stays `.task-pipeline/run.md`; `paths.artifacts` relocates project documents, not this run state. For a relocated ledger use `emit --ledger <path> --project-root <project>` (an enclosing Git root can also establish the project). The first emit saves the selected profile's source/digest and run identity beside the ledger; record retains them. Same-run profile changes, a missing formerly selected config, or a different run reusing that state refuse. Restore the selected source and retry, or reconcile the old workflow and start a distinct ledger/state for the new run; never delete a lease to make the check green. A pre-existing custom workflow with no profile binding also needs reconciliation. Ledger file symlinks and state-file links refuse without migration or overwrite; accepted directory aliases resolve consistently for emit, record, state and handoff. Profile, ledger and existing state inputs must be regular files, so a FIFO is refused before reading. No live checkpoint or read-attestation is proved by a locally emitted payload.
401
409
  - **The constraints.** Give the brief's restrictive rules once, as `--constraint`. They are carried to every later boundary, and a successor reads them first.
402
410
  - **Keys.** Pass keys by name, as `--credential PROJECT/ENV/NAME`, never as a value.
403
411
  - **Without the tools.** `stage_checkpoint.py record --unavailable "<why>"` appends one `event: memory — unavailable` line, and the run continues exactly as before. Missing memory is a state the ledger names, not a failure. When the tools are absent for the whole run — no Observatory on this machine — say it **once**: `record --unavailable "<why>" --for-run` appends one `unavailable for this run` line and leaves a marker in `.task-pipeline/memory.json`; no later gate is nudged or needs a line. Any later `record --answer` (the tools came back) clears the marker.
@@ -6,8 +6,9 @@ built-in doctrine lives, so an agent loads the one file the stage needs instead
6
6
  carrying the whole index in every turn.
7
7
 
8
8
  The rule the table serves stays in `SKILL.md`: every stage's doctrine ships inside this
9
- skill, no stage blocks on an install, and the single deliberate exception is the stage-3
10
- UX track on a user-facing task.
9
+ skill and no stage blocks on an install. The stage-3 UX gate on a user-facing task
10
+ requires a valid scenario artifact; [the provider rule](companion-skills.md) governs
11
+ the preferred provider, equivalent providers and inline fallback.
11
12
 
12
13
  ## Contents
13
14
 
@@ -15,8 +16,24 @@ UX track on a user-facing task.
15
16
 
16
17
  ## Which reference owns which stage
17
18
 
19
+ For the default profile, `references/stages.md` owns the full gates and exact
20
+ on-entry/resume route to each `references/stage-*.md` detail. Read the selected
21
+ stage detail before its work; this table routes additional subject doctrine.
22
+ Custom profiles use their own declared stages and doctrine, with the kernel intact.
23
+
18
24
  | Stage | Built-in doctrine |
19
25
  |---|---|
26
+ | 0 · Stage procedure | `references/stage-00-intake.md` |
27
+ | 1 · Stage procedure | `references/stage-01-docs-study.md` |
28
+ | 2 · Stage procedure | `references/stage-02-brainstorm.md` |
29
+ | 3 · Stage procedure | `references/stage-03-spec.md` |
30
+ | 4 · Stage procedure | `references/stage-04-plan.md` |
31
+ | 5 · Stage procedure | `references/stage-05-dev.md` |
32
+ | 6 · Stage procedure | `references/stage-06-tests.md` |
33
+ | 7 · Stage procedure | `references/stage-07-lint-deploy.md` |
34
+ | 8 · Stage procedure | `references/stage-08-post-deploy.md` |
35
+ | 9 · Stage procedure | `references/stage-09-docs-wiki.md` |
36
+ | 10 · Stage procedure | `references/stage-10-acceptance.md` |
20
37
  | 0, 9 · The documentation system | `references/documentation.md` |
21
38
  | any stage · The canons, and where each is enforced | [`evidence-docs`](../../evidence-docs/SKILL.md) — the sibling skill in this plugin |
22
39
  | 6–10 · Gates | `references/gates.md` |
@@ -42,6 +59,7 @@ UX track on a user-facing task.
42
59
  | **first run in a project** (new or existing) | `references/adoption.md` |
43
60
  | **first run · the entry audit** (offered once) | `references/setup.md` |
44
61
  | **what travels with the bundle vs stays in a project** | `references/portability.md` |
62
+ | 0–10 · when an iteration stops — the contract fixed at intake, triage at discovery, the round budget | `references/finish-line.md` |
45
63
  | any repeating loop | `references/loop-guard.md` |
46
64
  | run-wide · what the run **leaves running and leaves behind** — every gate, and stage 10 | `references/residue.md` |
47
65
  | run-wide · what the run **prints about itself** — the rail, the iteration line | `references/progress.md` |
@@ -0,0 +1,145 @@
1
+ # The finish line — when an iteration stops
2
+
3
+ An iteration stops when the work it promised is done **on the artefact the operator uses**.
4
+ It does not stop when a search runs dry, because a search never does: every reading finds
5
+ something, every fix is new surface for the next reading, and find → fix → find has no end of
6
+ its own. This file is that end. The verdict is computed by `scripts/waves.py finish`.
7
+
8
+ ## Contents
9
+
10
+ - The contract, fixed at intake
11
+ - Triage at discovery
12
+ - Rounds and the stop
13
+ - Computing the verdict
14
+ - Stop is not idle
15
+ - The finish block
16
+ - Rationalizations
17
+
18
+ ## The contract, fixed at intake
19
+
20
+ The brief's `## Finish line` ([`../templates/brief.md`](../templates/brief.md)) carries five
21
+ fields, written at stage 0 and confirmed with the brief:
22
+
23
+ - **Objective** — one sentence: what is true when this iteration is done.
24
+ - **Exit checks** — REQ ids, each with its command, run on the artefact the operator uses:
25
+ released, installed or deployed. A branch is not that artefact.
26
+ - **Scope** — the modules, paths and REQ ids this iteration may change, as a list.
27
+ - **Budget** — `run.finish.maxRounds` (default 3) and `run.finish.maxReleases` (default 2),
28
+ from `pipeline.json` ([`../pipeline.schema.json`](../pipeline.schema.json)).
29
+ - **Base** — the commit the iteration started from.
30
+
31
+ Widening the scope is a recorded revision of the brief, with the operator's go. A finding is
32
+ not one.
33
+
34
+ ## Triage at discovery
35
+
36
+ Every finding that reaches a round is classed when it is found, before anyone fixes it. First
37
+ match wins. A defect in a node's own diff, caught before the node closes, follows
38
+ [`review.md`](review.md) instead: Critical and Important block that node.
39
+
40
+ **Blocking**, when:
41
+ - **B1** — an exit check is red.
42
+ - **B2** — it is, or once released would be, a hotfix-lane entry. The list's single home is [`iteration.md`](iteration.md)
43
+ → *No fix travels alone*; restated here, kept equal by the validator:
44
+ - production is down
45
+ - data is being lost or corrupted
46
+ - a credential has leaked
47
+ - a security hole is open to someone outside
48
+ - money or a legal obligation is failing
49
+ - **B3** — the failing scenario passes at Base: this iteration broke it.
50
+ - **B4** — a REQ marked `verified`, or a claim this iteration publishes, is false on the
51
+ artefact. A published claim beyond the REQs may instead be narrowed in the same change.
52
+
53
+ **Deferred** — real, with a receipt, and no B test matches. It becomes a board row with its
54
+ evidence, its priority inputs ([`prioritisation.md`](prioritisation.md)) and
55
+ `found-in: <iteration>`. It is not fixed now.
56
+
57
+ **Noise** — no reproducible receipt; a duplicate (append its evidence to the existing row); a
58
+ Minor with no behavioural consequence; or an environment transient named by its cause. One
59
+ ledger line, nothing else.
60
+
61
+ Severity words, effort and "it is a one-line fix" never change the class. Each finding leaves
62
+ one line in the run ledger ([`../templates/run.md`](../templates/run.md)):
63
+
64
+ ```
65
+ finding: <id> — blocking:B<n> | deferred:B-NNN | noise — round <N> — base-repro yes|no|n/a
66
+ ```
67
+
68
+ ## Rounds and the stop
69
+
70
+ A **round** is every exit check run once, plus one independent reading over the Scope: an
71
+ audit axis, an observer, or the post-release readback. Rotating the axis
72
+ ([`audit.md`](audit.md)) never adds a round. Only new blocking findings count.
73
+
74
+ - **Done** — every exit check green on the artefact, and two consecutive rounds with no new
75
+ blocking finding. The second of them may read only the last fix's diff.
76
+ - **Diverging** — new blocking findings did not fall from one round to the next, or
77
+ self-inflicted ≥ new. Run [`loop-guard.md`](loop-guard.md)'s break protocol.
78
+ - **Budget spent** — `maxRounds` or `maxReleases` reached before done: a blocker open, or the
79
+ second clean round still owed. Stop and hand the iteration back: each open blocker becomes a
80
+ top board row marked `needs-operator`. No round runs past the budget without the operator's
81
+ go, given with the numbers; a done reached past it without that go is still `budget`.
82
+
83
+ The finish word is one of three: `done`; `budget`, the budget reached before done; `blocked`,
84
+ the rounds diverging, handed to the loop guard. Deferred findings never extend an iteration.
85
+
86
+ Each round leaves one ledger line, clean rounds included, because a clean round leaves no
87
+ finding to count:
88
+
89
+ ```
90
+ round: <N> — <reading> — new blocking <k>
91
+ ```
92
+
93
+ ## Computing the verdict
94
+
95
+ ```
96
+ python3 scripts/waves.py finish --inventory <inventory.json> --ledger .task-pipeline/run.md \
97
+ --max-rounds 3 --max-releases 2 --releases <n> --checks <k>/<m> --artefact <name>@<sha>
98
+ ```
99
+
100
+ `--rounds N` may stand in for `--ledger`; with neither the count is unknown and the script
101
+ refuses. Exit codes:
102
+
103
+ - `0` — done: the last two rounds found no new blocking item, none is open, and every exit
104
+ check is green on the artefact.
105
+ - `3` — continue: a blocker open, a second clean round owed, or no `--checks` given.
106
+ - `5` — budget: reached before done, or done past it without `--operator-go`.
107
+ - `6` — blocked: diverging; run the loop guard's break protocol.
108
+ - `1` — malformed: the inventory, the ledger or the arguments, each reason named.
109
+
110
+ ## Stop is not idle
111
+
112
+ Reaching the finish line closes the iteration. It does not end the work.
113
+
114
+ 1. Close it: stages 9–10, the hand-back ([`progress.md`](progress.md) → *The hand-back*), the
115
+ retrospective.
116
+ 2. On an armed loop ([`continuity.md`](continuity.md)), take the highest-priority board row as a
117
+ **new** iteration with its own contract. Choose by priority, not recency: the row found last
118
+ is not next because it is fresh.
119
+ 3. Re-opening a closed iteration to polish it is the loop this rule ends.
120
+
121
+ ## The finish block
122
+
123
+ Printed at stage 10, beside the verdict, and in the hand-back's `FINISH` field:
124
+
125
+ ```
126
+ finish: done | budget | blocked — rounds <n>/<max> — releases <n>/<max>
127
+ exit checks: <k>/<m> green on <artefact>@<sha>
128
+ blocking: <n> open · <n> fixed
129
+ deferred: <n> → <ids, top priority first>
130
+ noise: <n>
131
+ known risks: <deferred rows at blast 3 or rung I4+, or none>
132
+ next: B-NNN (new iteration) | board empty
133
+ ```
134
+
135
+ ## Rationalizations
136
+
137
+ | Excuse | Reality |
138
+ |---|---|
139
+ | "One more pass, it keeps finding things" | Every reading finds something. Only a new blocking finding argues for another round |
140
+ | "It is a one-line fix, do it now" | Effort never changes the class. A deferred finding is a board row |
141
+ | "This one is critical" | A severity word is not a B test. Name B1–B4, or defer it |
142
+ | "Rotate the axis and keep going" | Rotation happens inside the round budget, never on top of it |
143
+ | "The budget is nearly enough, one more round" | A round past the budget needs the operator's go, with the numbers |
144
+ | "The deferred list is long, so we are not done" | Deferred findings never extend an iteration |
145
+ | "Re-open the last iteration, it is nearly perfect" | That is the loop this rule ends. Take the top board row as a new iteration |
@@ -179,7 +179,7 @@ explicit "stop and ask me here":
179
179
  | 0 Docs regime | where settled things live (the decision home — **one** per project, and an existing `docs/adr/` **is** it), who may write it, whether a lease mechanism is present or the run is `ungated`, the gate command and its ratchet floors, and whether this run may raise a floor ([`documentation.md`](documentation.md)) |
180
180
  | 1 Docs | external libs/APIs/SDKs in play; any private ones context7 can't resolve → where their docs live |
181
181
  | 2 Decompose | is this a platform (several capabilities/surfaces) or one module? if platform: deploy cadence — per module or once at the end |
182
- | 2–3 Spec | UI verdict (arms super-ux) **and the surface class** — `flagship`, `product`, `internal` or `ad`, which selects the visual gate profile ([`stages.md`](stages.md) → stage 0, *The surface class*); any scenario-tracing waiver |
182
+ | 2–3 Spec | UI verdict (arms super-ux) **and the surface class** — `flagship`, `product`, `internal` or `ad`, which selects the visual gate profile ([`stage-00-intake.md`](stage-00-intake.md) → *0 — Intake grill — MANDATORY*, the surface-class bullet); any scenario-tracing waiver |
183
183
  | 3 Design surface | UI tasks only: **Figma on or text-only** (super-ux's project-level choice, default on — check `docs/ux/foundation.md` → *Design tooling* before asking); is the Figma MCP connected; **and if it isn't — ship text-only, or stop here and connect it?** super-ux degrades to text-only on its own and never blocks, which means an unasked question here silently ships a UI feature with no mockups |
184
184
  | 3 Design file | Figma on only: **exactly which files — one per surface (App, Web, ASO) — in which team/org** — the recorded ones, or a URL the operator gives, or *create one in a named team* with that creation explicitly authorized. A destination decided at drawing time is how a project ends up with three "design" files and no way to tell which is real. See *The design destination* below |
185
185
  | 4–5 Dev | base branch; worktree/branch policy; is `main` off-limits; commit convention; task tracker |