task-pipeline-skill 1.91.0 → 1.92.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 +91 -0
- package/HOW-IT-WORKS.md +7 -3
- package/README.md +30 -4
- package/SKILL-CARD.md +1 -1
- package/cursor/rules/task-pipeline.mdc +11 -2
- package/evals/RESULTS.md +1 -1
- package/evals/task-pipeline.evals.json +16 -1
- package/package.json +1 -1
- package/plugins/task-pipeline/.claude-plugin/plugin.json +2 -2
- package/plugins/task-pipeline/commands/task-pipeline.md +23 -8
- package/plugins/task-pipeline/hooks/build-gate.sh +6 -0
- package/plugins/task-pipeline/hooks/release-gate.sh +15 -0
- package/plugins/task-pipeline/skills/project-audit/SKILL.md +2 -1
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +9 -10
- package/plugins/task-pipeline/skills/task-pipeline/graph.schema.json +8 -2
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +4 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/brainstorm.md +6 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/build.md +2 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +12 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/decomposition.md +4 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/doctrine-map.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/grill.md +14 -5
- package/plugins/task-pipeline/skills/task-pipeline/references/planning.md +8 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +10 -6
- package/plugins/task-pipeline/skills/task-pipeline/references/waves.md +225 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/work-graph.md +3 -1
- package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +89 -5
- package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,94 @@
|
|
|
1
|
+
## v1.92.0 — wave mode: the whole board, closed in its final form; the grill asks one batch
|
|
2
|
+
|
|
3
|
+
The operator's standard approach, decided on 2026-10-11 and codified here: when the request
|
|
4
|
+
is not one change but **the whole board** — every open item plus the new requests — gather
|
|
5
|
+
it all first, close every question the sources can close, ask what remains **once, as one
|
|
6
|
+
batch, at the end of planning**, lay the target state over the current logic on one canvas,
|
|
7
|
+
plan from the post-integration target, and cut the work into waves that each close in one
|
|
8
|
+
pass, with a verification, finishing, fixing and documentation pass after every wave. The
|
|
9
|
+
run takes the next wave without asking, says once when the board is empty, and leaves every
|
|
10
|
+
sign-in, consent, payment, message to a person and outward act to a human. The rule under
|
|
11
|
+
all of it: **build the system in its final form, never through intermediate states.**
|
|
12
|
+
|
|
13
|
+
- **`references/waves.md`, invoked as `/task-pipeline waves`** — a third mode beside `setup`
|
|
14
|
+
and `checkup`, and a program mode of the existing stages, not a parallel cycle: stages 0–4
|
|
15
|
+
once for the whole board, 5–8 per wave, 9–10 to close the release. Wave 0 is the survey
|
|
16
|
+
(with `project-audit` as a read-only sidecar for whole-project scope) and **one
|
|
17
|
+
machine-readable inventory**, every item by id with `id`, `source`, `status`, `area`,
|
|
18
|
+
`node`, `wave`, `release`, verified against the code — stale rows closed with their
|
|
19
|
+
receipt, duplicates clustered — and assigned to nodes and waves by a committed,
|
|
20
|
+
deterministic script. The **canvas** extends the module map: as-is → to-be per module and
|
|
21
|
+
contract, the integration seams, REQ → module → wave, and what the new version deletes.
|
|
22
|
+
No wave builds a shape a later wave undoes; a stub only at a declared contract with its
|
|
23
|
+
removal named. Wave 1 is the walking skeleton, and each wave names its exit criteria
|
|
24
|
+
before it starts. The close pass after each wave certifies, re-measures `audit.md`'s
|
|
25
|
+
exit, runs **one fix round** under the loop guard's caps, documents through the Doc Loop
|
|
26
|
+
scoped to the wave, marks the canvas, re-reads the goal, and re-cuts what is left. A board
|
|
27
|
+
bigger than one release becomes a program, carried on the canvas by node, never dropped.
|
|
28
|
+
The name is not "dynamic": that word is `run.loop.mode: dynamic`'s.
|
|
29
|
+
- **The grill's default changed — a decision by the operator, 2026-10-11.** It was *one
|
|
30
|
+
question per turn, never bundle*. It is now: close from the sources first, then ONE batch
|
|
31
|
+
of the material unknowns, ordered by dependency, each with a recommended answer and what
|
|
32
|
+
each answer changes; one question at a time only where answers depend on each other
|
|
33
|
+
(then depth-first) or when the operator asks for it. The reason is the operator's time: a
|
|
34
|
+
question asked alone costs a human round and a batch costs one, and
|
|
35
|
+
`prioritisation.md` → *5. Where the human goes* already said so for the rest of the run.
|
|
36
|
+
Mirrored in `grill.md`, `stages.md` (stages 0 and 2), `brainstorm.md`, the command, the
|
|
37
|
+
Cursor rule, `HOW-IT-WORKS.md`, the README, `pipeline.example.json`, this repository's
|
|
38
|
+
`pipeline.json`, and eval TRIG-01; TRIG-11 is the wave-mode trigger in Russian.
|
|
39
|
+
- **`continuity.md`: a block or an ambiguity stops its node, not the run.** The node is
|
|
40
|
+
parked `needs-operator` with the question written into it, the run continues on what it
|
|
41
|
+
does not block, and stops only when the whole frontier is blocked. An empty board is said
|
|
42
|
+
once. A goal re-read that moves the queue is a committed `revisions` entry.
|
|
43
|
+
- **`planning.md`: a wave re-cut keeps the static-graph decision** — every move is a
|
|
44
|
+
`revisions` entry plus a canvas edit, written before the next pass begins.
|
|
45
|
+
**`decomposition.md`**: the whole board runs one wave at a time, and a wave may hold several
|
|
46
|
+
bricks. **`build.md`**: "fix wave" is now "fix round", so *wave* means one thing.
|
|
47
|
+
**`project-audit`**: its rows and sidecar are wave mode's wave-0 input.
|
|
48
|
+
- **The work graph carries the wave.** `graph.schema.json` gains `nodes[].wave` (an integer
|
|
49
|
+
from 1) and the `wave` revision verb. `graph.py validate` refuses an edge from a later
|
|
50
|
+
wave's node into an earlier wave's node; `graph.py next --wave N` prints that wave's
|
|
51
|
+
frontier only, exiting `3` when the wave is done; `graph.py wave N-007 --to 3 --why …`
|
|
52
|
+
moves a node, writes the revision, and refuses a move that breaks the order, a `done`
|
|
53
|
+
node, or no reason. `test/graph_test.py` 158 → **166** cases.
|
|
54
|
+
- **The description names the mode** and paid for it: `'waves' / 'волнами' closes the whole
|
|
55
|
+
board`, while the stage list reads *through gated stages: …* instead of *… with explicit
|
|
56
|
+
gates*, `docs study` reads `docs`, and `docs/wiki sync` reads `docs/wiki`. 901 → 909 of
|
|
57
|
+
1024 chars — inside the umbrella's 60-character reserve below the 970 working limit,
|
|
58
|
+
which a longer description would have broken. The `SKILL.md` body is 4736 tokens
|
|
59
|
+
(cl100k), one under where it was: the new sentence in *How to run* displaced a restated
|
|
60
|
+
kernel clause and the compiler sentence's tail.
|
|
61
|
+
|
|
62
|
+
Guards: 430 → **433**. The three new negatives live in `test/plant_wave.py` (the workflow
|
|
63
|
+
sits near GitHub's 512 000-byte limit) and disarm the order check, the `--wave` filter and
|
|
64
|
+
the re-cut's revision in turn; each was watched turning `test/graph_test.py` red before the
|
|
65
|
+
step was written.
|
|
66
|
+
|
|
67
|
+
## v1.91.2 — v1.91.1's payload, released with its no-stamp declaration
|
|
68
|
+
|
|
69
|
+
`release.yml` refused the `v1.91.1` tag's own tree: the release carried no run stamp and was
|
|
70
|
+
not named in `## Releases that carry no stamp`. Nothing was published under it. This
|
|
71
|
+
release is the same payload with the declaration in `docs/evidence/retro.md`. What v1.91.1
|
|
72
|
+
ships is described in its section below.
|
|
73
|
+
|
|
74
|
+
## v1.91.1 — the gates read the current run only, and a broken python3 stops only releases
|
|
75
|
+
|
|
76
|
+
- **The release gate read every run in the ledger.** `.task-pipeline/run.md` is
|
|
77
|
+
append-only, and a project that reuses it holds earlier runs above the current one.
|
|
78
|
+
The gate took the FIRST `stage: … tests … verdict pass` in the whole file, so a
|
|
79
|
+
previous release's pass admitted `git tag` while this run's tests were still PENDING
|
|
80
|
+
(reported 2026-10-11 from a live ledger: the 0.2.0 pass would have released 0.4.0).
|
|
81
|
+
Both gates now read only what follows the last `Run:` header — the line
|
|
82
|
+
`templates/run.md` writes once per run; a ledger with no header is one run.
|
|
83
|
+
- **The build gate had the same leak.** A previous run that had entered dev silenced
|
|
84
|
+
the ask for a new run still in planning. Same scoping, same fixture shape.
|
|
85
|
+
- **A failed python3 no longer blocks every command.** With the disk full, python3
|
|
86
|
+
could not start, and the gate refused `ls` in any project with a run in flight. A
|
|
87
|
+
shell pre-filter now lets a command through when it carries none of
|
|
88
|
+
`tag`/`push`/`release`/`publish`; anything that could release still fails closed.
|
|
89
|
+
|
|
90
|
+
Four fixtures in `test/release_gate_test.py`, each watched failing before its fix.
|
|
91
|
+
|
|
1
92
|
## v1.91.0 — the description says «баг», and a lightweight tag is refused before release
|
|
2
93
|
|
|
3
94
|
v1.90.0 added `bug` to the description so "fix this bug" routes here by name. The Russian
|
package/HOW-IT-WORKS.md
CHANGED
|
@@ -46,7 +46,9 @@ It is written in three parts, and the middle one was learned the hard way:
|
|
|
46
46
|
the pipeline entirely.
|
|
47
47
|
|
|
48
48
|
Two modes need no task at all. `checkup` reports what has shipped without anyone
|
|
49
|
-
confirming it. `setup` audits the documentation a project already has.
|
|
49
|
+
confirming it. `setup` audits the documentation a project already has. A third,
|
|
50
|
+
`waves`, takes the whole board as the task: one inventory, one canvas of the target
|
|
51
|
+
state, one batch of questions, then waves that each close in one pass.
|
|
50
52
|
|
|
51
53
|
**Honest limit:** this measures the *description's* discriminating power, not the
|
|
52
54
|
host's selection machinery. Three measured runs scored 7/10, 9/10 and 8/10 — and
|
|
@@ -92,8 +94,10 @@ An absent source that is never named looks identical to a source that was read.
|
|
|
92
94
|
|
|
93
95
|
### Then the grill, and it is mandatory
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
Close what the sources can answer, then ask the material unknowns in one batch,
|
|
98
|
+
ordered by dependency, each with a recommended answer — one at a time only where the
|
|
99
|
+
answers depend on each other or the operator asks — until every decision branch is
|
|
100
|
+
resolved.
|
|
97
101
|
|
|
98
102
|
- **Every answer is validated against the harvested sources.** The operator
|
|
99
103
|
outranks any document — but only *out loud*, and a document the run proves stale
|
package/README.md
CHANGED
|
@@ -214,9 +214,11 @@ an external provider.
|
|
|
214
214
|
|
|
215
215
|
### The intake grill (stage 0) — mandatory
|
|
216
216
|
|
|
217
|
-
Before any technical work, task-pipeline
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
Before any technical work, task-pipeline closes every question the codebase and the
|
|
218
|
+
project's records can answer, then asks you the rest in **one batch**, ordered by
|
|
219
|
+
dependency, each with a recommended answer — one at a time only where the answers
|
|
220
|
+
depend on each other or you ask for it — until every decision branch is resolved and
|
|
221
|
+
locked into a **task brief**. There is
|
|
220
222
|
no "clear enough task" exemption: no stage-1 work starts without a committed,
|
|
221
223
|
confirmed brief.
|
|
222
224
|
|
|
@@ -444,6 +446,29 @@ stopped. Each module's spec is a full dossier: architecture, entities and
|
|
|
444
446
|
ownership, contracts in and out with their failure behavior, business rules, edge
|
|
445
447
|
and failure cases, UI/Figma chain, limits, open questions.
|
|
446
448
|
|
|
449
|
+
### Wave mode — the whole board, closed in its final form
|
|
450
|
+
|
|
451
|
+
`/task-pipeline waves` takes every open item a project carries — the board, the
|
|
452
|
+
carry-over ledgers, plan rows, unverified rows, uncovered scenarios, other products'
|
|
453
|
+
asks — plus your new requests, and closes them **in the system's final form rather
|
|
454
|
+
than through intermediate states**
|
|
455
|
+
([`waves.md`](plugins/task-pipeline/skills/task-pipeline/references/waves.md)). It is
|
|
456
|
+
a program mode of the same stages: 0–4 once for the whole board, 5–8 once per wave,
|
|
457
|
+
9–10 to close.
|
|
458
|
+
|
|
459
|
+
Wave 0 is the survey: one machine-readable inventory of every item by id, each verified
|
|
460
|
+
against the code — stale rows closed with their receipt, duplicates clustered. Questions
|
|
461
|
+
are closed from the sources first; what is genuinely yours (access, consent, money,
|
|
462
|
+
direction, an outward act) reaches you **once, as one batch, at the end of planning**,
|
|
463
|
+
each with a recommendation. A committed **canvas** lays the target state over the
|
|
464
|
+
current logic — as-is → to-be per module and contract, the integration seams, REQ →
|
|
465
|
+
module → wave, and what the new version deletes. The work is then cut into waves that
|
|
466
|
+
each close in one pass, the walking skeleton first, each with its exit criteria named
|
|
467
|
+
before it starts, and no wave builds a shape a later wave undoes. After every wave a
|
|
468
|
+
close pass verifies, runs one fix round, updates the docs, re-measures the inventory
|
|
469
|
+
and re-cuts what is left. The run takes the next wave without asking; a board too big
|
|
470
|
+
for one release becomes a program, carried on the canvas, never dropped.
|
|
471
|
+
|
|
447
472
|
### Loop guard — churn is detected, not endured
|
|
448
473
|
|
|
449
474
|
Any repeating pass can start undoing the previous one: two shapes alternating, the
|
|
@@ -587,7 +612,7 @@ must say so.
|
|
|
587
612
|
### Held to Anthropic's own Skill authoring guidance
|
|
588
613
|
|
|
589
614
|
Audited against the four Agent Skills pages. Most of it already held — `name`
|
|
590
|
-
13/64 chars, `description`
|
|
615
|
+
13/64 chars, `description` 909/1024 chars, `SKILL.md` 277/500 lines, all 40 references
|
|
591
616
|
linked **directly** from `SKILL.md`, and the bundle far under the 30 MB ceiling. What
|
|
592
617
|
did not, now does:
|
|
593
618
|
|
|
@@ -942,6 +967,7 @@ recommendation, so you arm the whole run in one exchange. Detail:
|
|
|
942
967
|
| [`references/model-tiering.md`](plugins/task-pipeline/skills/task-pipeline/references/model-tiering.md) | model policy, the `/model` reminder, overrides |
|
|
943
968
|
| [`templates/`](plugins/task-pipeline/skills/task-pipeline/templates/README.md) | brief, carry-over ledger, `CONTEXT.md` and ADR skeletons, the doc map, both registers, the retro and its archive, the seeded `docgate.sh` and `hygiene.sh`, a worked hook |
|
|
944
969
|
| [`references/adoption.md`](plugins/task-pipeline/skills/task-pipeline/references/adoption.md) | the first run in a project: greenfield seeding, and the brownfield walkthrough |
|
|
970
|
+
| [`references/waves.md`](plugins/task-pipeline/skills/task-pipeline/references/waves.md) | wave mode: the whole board as one inventory, one canvas of the target state, one question batch, then waves that each close in one pass |
|
|
945
971
|
| [`references/setup.md`](plugins/task-pipeline/skills/task-pipeline/references/setup.md) | the entry audit: seven passes over the docs a project already has, offered once, output as a fix plan |
|
|
946
972
|
| [`references/portability.md`](plugins/task-pipeline/skills/task-pipeline/references/portability.md) | the manifest of workflow decisions and their homes in the bundle, and the boundary against a project's own answers |
|
|
947
973
|
| [`references/learned.md`](plugins/task-pipeline/skills/task-pipeline/references/learned.md) | rules earned by failure on a real multi-repository build, each with its incident, its check and its exit criterion — the table is the count, and it grows |
|
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.92.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, and **one of two browser channels** — `playwright` (CLI or MCP) or `chrome-devtools` (MCP); either satisfies the browser step and neither is required. 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` |
|
|
@@ -108,7 +108,9 @@ built, recommend it once and continue: `uv tool install graphifyy` →
|
|
|
108
108
|
|
|
109
109
|
**Phase 2 — grill the user against that harvest**, then run the rest without
|
|
110
110
|
mid-flight questions:
|
|
111
|
-
1.
|
|
111
|
+
1. Close what the sources answer; ask the rest in ONE batch, ordered by
|
|
112
|
+
dependency. One at a time only when answers depend on each other or the user
|
|
113
|
+
asks.
|
|
112
114
|
2. Give a recommended answer with every question (+ one-line rationale).
|
|
113
115
|
3. Explore the codebase before asking — if a search/read answers it, do that.
|
|
114
116
|
4. **Validate every answer against the harvest.** When what the user says
|
|
@@ -211,7 +213,14 @@ not authorize an outward, irreversible action — stage 7 stops and asks.
|
|
|
211
213
|
first**, then topologically, no cycles (a cycle means the cut is wrong). Every REQ
|
|
212
214
|
maps to exactly one module. Then stages 3→10 run **per module**, one brick at a
|
|
213
215
|
time; stages 0–2 run once, and the map's status column is the resume point. Single
|
|
214
|
-
module → record "single module: <name>" and move on.
|
|
216
|
+
module → record "single module: <name>" and move on. **If the request is the whole
|
|
217
|
+
board** (wave mode, `waves`) — every open item plus the new requests — stages 0–4
|
|
218
|
+
run once over one machine-readable inventory verified against the code and a
|
|
219
|
+
committed canvas of the target state; the work is cut into waves that each close
|
|
220
|
+
in one pass (wave 1 the walking skeleton, exit criteria named first); stages 5–8
|
|
221
|
+
run per wave with a close pass after each — verify, one fix round, docs,
|
|
222
|
+
re-measure, re-cut — and 9–10 close the release. No wave builds a shape a later
|
|
223
|
+
wave undoes.
|
|
215
224
|
3. **Spec** (manual) — write and commit the design; lock every shared contract
|
|
216
225
|
(types, schemas, signatures, file layout) plus a **Global Constraints** block of
|
|
217
226
|
exact values every later task inherits. Self-review it for placeholders,
|
package/evals/RESULTS.md
CHANGED
|
@@ -40,7 +40,7 @@ or it belongs in a check.
|
|
|
40
40
|
|
|
41
41
|
| Metric | Value | Computed by | As of |
|
|
42
42
|
|---|---|---|---|
|
|
43
|
-
| Evals authored |
|
|
43
|
+
| Evals authored | 47 (three suites: task-pipeline 29, evidence-docs 9, project-audit 9) | `python3 evals/run.py` → `suite: N evals` | 2026-10-11 |
|
|
44
44
|
| Categories covered | 5 of 5, per suite | the suite's own `category` fields | 2026-08-31 |
|
|
45
45
|
| Dated runs recorded | **4** | `python3 evals/run.py` → `recorded runs: N` | 2026-08-31 |
|
|
46
46
|
| …of those, **blind** | **3** | run headings not marked `self-observed` | 2026-08-31 |
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"expected_behavior": [
|
|
18
18
|
"Invokes the task-pipeline skill rather than starting to design or code inline",
|
|
19
19
|
"Runs the stage-0 knowledge harvest BEFORE the first interview question, and writes a source ledger",
|
|
20
|
-
"Asks interview questions one
|
|
20
|
+
"Asks the material interview questions in one batch ordered by dependency, each with a recommended answer, after closing what the sources answer",
|
|
21
21
|
"Writes no implementation code before a brief is committed and confirmed"
|
|
22
22
|
],
|
|
23
23
|
"why": "The explicit invocation phrase is the primary trigger; the failure it guards is jumping to code."
|
|
@@ -361,6 +361,21 @@
|
|
|
361
361
|
],
|
|
362
362
|
"why": "Measured before the change: answered `none` because no description offered a phrase for reviewing a PR, while review.md owns exactly that contract."
|
|
363
363
|
},
|
|
364
|
+
{
|
|
365
|
+
"id": "TRIG-11",
|
|
366
|
+
"category": "should_trigger",
|
|
367
|
+
"skills": [
|
|
368
|
+
"task-pipeline"
|
|
369
|
+
],
|
|
370
|
+
"query": "собери все задачи с доски и новые запросы и закрой их волнами",
|
|
371
|
+
"expected_behavior": [
|
|
372
|
+
"Recognises this as wave mode and runs `/task-pipeline waves`, not one change at a time",
|
|
373
|
+
"Builds one machine-readable inventory of every open item by id, each verified against the code, before any design",
|
|
374
|
+
"Closes questions from the sources first and asks the operator the rest once, as one batch with a recommendation each, at the end of planning",
|
|
375
|
+
"Cuts the work into waves that each close in one pass, wave 1 the walking skeleton, each with its exit criteria named before it starts"
|
|
376
|
+
],
|
|
377
|
+
"why": "Wave mode is the operator's standard approach for a whole board (2026-10-11). A model that cannot reach it from the natural Russian request runs the board item by item and builds the intermediate states the mode exists to refuse."
|
|
378
|
+
},
|
|
364
379
|
{
|
|
365
380
|
"id": "NOTRIG-06",
|
|
366
381
|
"category": "should_not_trigger",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "task-pipeline-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.92.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,8 +2,8 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "task-pipeline",
|
|
4
4
|
"displayName": "Task Pipeline",
|
|
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). Retro insights can publish upstream as issues, opt-in and redacted.",
|
|
6
|
-
"version": "1.
|
|
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.92.0",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "ssheleg",
|
|
9
9
|
"url": "https://x.com/sshlg93"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Run a task through task-pipeline's gated stages (intake grill → docs → brainstorm → spec → plan → build → tests → deploy → post-deploy → docs/wiki → acceptance). Also: `setup` — audit the docs you already have; `checkup` — what has shipped unverified, with no task running."
|
|
3
|
-
argument-hint: "<one-line task> | setup | checkup"
|
|
2
|
+
description: "Run a task through task-pipeline's gated stages (intake grill → docs → brainstorm → spec → plan → build → tests → deploy → post-deploy → docs/wiki → acceptance). Also: `waves` — close the whole board in waves; `setup` — audit the docs you already have; `checkup` — what has shipped unverified, with no task running."
|
|
3
|
+
argument-hint: "<one-line task> | waves | setup | checkup"
|
|
4
4
|
---
|
|
5
5
|
Run the task below through `task-pipeline`'s gated stages. **Every stage's doctrine
|
|
6
6
|
ships inside the skill** — no companion plugin is required for any of it. The
|
|
@@ -8,9 +8,10 @@ reference files are indexed in `SKILL.md`; this page is the run order, not the i
|
|
|
8
8
|
|
|
9
9
|
Task: $ARGUMENTS
|
|
10
10
|
|
|
11
|
-
> **
|
|
12
|
-
> `
|
|
13
|
-
>
|
|
11
|
+
> **Three other modes.** `waves` takes the whole board instead of one task;
|
|
12
|
+
> `setup` audits the documentation this project already has; `checkup` reports what
|
|
13
|
+
> has shipped without a person confirming it, with no task in flight. All three are at
|
|
14
|
+
> the bottom of this page.
|
|
14
15
|
|
|
15
16
|
**Idempotent entry — inspect state first, never restart blindly.** If a pipeline
|
|
16
17
|
TaskList from a previous run exists for this task, **resume** from the first incomplete
|
|
@@ -59,9 +60,10 @@ Pull what the project already knows about *this task*:
|
|
|
59
60
|
|
|
60
61
|
Write the **source ledger** into the brief: a row per source, or an explicit *none found*.
|
|
61
62
|
|
|
62
|
-
**Then the grill, and it is mandatory** (`references/grill.md`).
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
**Then the grill, and it is mandatory** (`references/grill.md`). Close what the sources
|
|
64
|
+
can, then ask the material unknowns in ONE batch, ordered by dependency, each with a
|
|
65
|
+
recommended answer — one at a time only where answers depend on each other or the
|
|
66
|
+
operator asks — until every decision branch is resolved.
|
|
65
67
|
|
|
66
68
|
- **Validate every answer against the harvested sources.** The operator outranks any
|
|
67
69
|
document — but only out loud, and a doc the run proves stale is logged for stage 9.
|
|
@@ -188,6 +190,19 @@ Where you ask it to file what it found, it appends board rows whose `Source` nam
|
|
|
188
190
|
checkup and its date — printing what it would add first, never silently.
|
|
189
191
|
Doctrine: `references/exposure.md`.
|
|
190
192
|
|
|
193
|
+
## `/task-pipeline waves`
|
|
194
|
+
|
|
195
|
+
**The whole board, closed in its final form.** Every open item — board, ledgers, plan
|
|
196
|
+
rows, unverified rows, uncovered scenarios, other products' asks — plus the new requests,
|
|
197
|
+
in one machine-readable inventory verified against the code. Questions are closed from
|
|
198
|
+
the sources; what remains goes to the operator once, as one batch, at the end of
|
|
199
|
+
planning. A committed canvas lays the target state over the current logic, and the work
|
|
200
|
+
is cut into waves that each close in one pass. Stages 0–4 run once, 5–8 per wave with a
|
|
201
|
+
close pass after each (verify, one fix round, docs, re-measure, re-cut), 9–10 to close.
|
|
202
|
+
It takes the next wave without asking, says once when the board is empty, and leaves
|
|
203
|
+
every sign-in, consent, payment and outward act to a person.
|
|
204
|
+
Doctrine: `references/waves.md`.
|
|
205
|
+
|
|
191
206
|
## `/task-pipeline setup`
|
|
192
207
|
|
|
193
208
|
**The entry audit instead of a feature.** Seven passes over the documentation this
|
|
@@ -54,6 +54,12 @@ try:
|
|
|
54
54
|
except Exception:
|
|
55
55
|
raise SystemExit(0)
|
|
56
56
|
|
|
57
|
+
# Only the CURRENT run speaks: everything after the last `Run:` header. A previous
|
|
58
|
+
# run that entered dev in a reused, append-only ledger silenced this gate (B-151).
|
|
59
|
+
_lines = text.splitlines()
|
|
60
|
+
_starts = [i for i, l in enumerate(_lines) if l.strip().startswith("Run:")]
|
|
61
|
+
text = "\n".join(_lines[_starts[-1]:] if _starts else _lines)
|
|
62
|
+
|
|
57
63
|
stage_lines = [l.strip() for l in text.splitlines() if l.strip().startswith("stage:")]
|
|
58
64
|
if not stage_lines:
|
|
59
65
|
raise SystemExit(0)
|
|
@@ -155,6 +155,14 @@ except Exception:
|
|
|
155
155
|
# gate cannot tell. Fail closed, and say which file to look at.
|
|
156
156
|
print("block\t%s\tthe run ledger could not be read" % act); raise SystemExit(0)
|
|
157
157
|
|
|
158
|
+
# B-151: the ledger is append-only, and a project that reuses it holds earlier runs
|
|
159
|
+
# above this one. Only the CURRENT run speaks — everything after the last `Run:`
|
|
160
|
+
# header (templates/run.md writes one per run); a ledger with no header is one run.
|
|
161
|
+
# A previous release's `verdict pass` admitted a tag whose own tests were PENDING.
|
|
162
|
+
_lines = text.splitlines()
|
|
163
|
+
_starts = [i for i, l in enumerate(_lines) if l.strip().startswith("Run:")]
|
|
164
|
+
text = "\n".join(_lines[_starts[-1]:] if _starts else _lines)
|
|
165
|
+
|
|
158
166
|
stage_lines = [l.strip() for l in text.splitlines() if l.strip().startswith("stage:")]
|
|
159
167
|
|
|
160
168
|
|
|
@@ -243,6 +251,13 @@ status=$?
|
|
|
243
251
|
# A gate that cannot reach its own decision must not wave the release through.
|
|
244
252
|
if [ $status -ne 0 ] || [ -z "$verdict" ]; then
|
|
245
253
|
[ -f "$ledger" ] || exit 0
|
|
254
|
+
# B-152: without python3 the command cannot be classified, and refusing EVERY
|
|
255
|
+
# Bash call took the whole session down on a full disk. A word-level pre-filter
|
|
256
|
+
# keeps fail-closed exactly where it matters: anything that could tag, push,
|
|
257
|
+
# release or publish is still refused; `ls` is not.
|
|
258
|
+
if ! printf '%s' "$input" | grep -Eq '(^|[^[:alnum:]_])(tag|tags|push|release|publish)([^[:alnum:]_]|$)'; then
|
|
259
|
+
exit 0
|
|
260
|
+
fi
|
|
246
261
|
echo "task-pipeline: the stage-7 gate could not run (python3 missing or failed), and this project has a run in flight ($ledger). Refusing an outward act rather than failing open." >&2
|
|
247
262
|
exit 2
|
|
248
263
|
fi
|
|
@@ -121,7 +121,8 @@ default is `Sev × Blast + age_bonus` ([`references/portable-method.md`](referen
|
|
|
121
121
|
the local priority contract) — and the operator accepts them. Effort never ranks inside an
|
|
122
122
|
audit: what a fix costs is the fixer's decision, not the finder's
|
|
123
123
|
([`references/portable-method.md`](references/portable-method.md)). An audit
|
|
124
|
-
that edits while it reads cannot be re-run to check itself.
|
|
124
|
+
that edits while it reads cannot be re-run to check itself. In `task-pipeline`'s wave
|
|
125
|
+
mode these rows and the JSON sidecar are wave 0's input: they join its inventory by id.
|
|
125
126
|
|
|
126
127
|
## A finding keeps its axes apart, or it is a guess wearing a verdict
|
|
127
128
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: task-pipeline
|
|
3
|
-
description: "Use when work changes the repository — feature, fix, bug, refactor, migration, integration, rewrite, adoption or hardening; фича, фикс, баг, рефактор, миграция, интеграция, доработать, починить, внедрить, перевести — or when the output is a finding that lands in it: audit/аудит, bug hunt/проверь ошибки, production check/проверь прод, PR review/ревью PR — or on 'run this through the pipeline' / 'прогони по конвейеру', 'the full cycle' / 'полный цикл', /task-pipeline. Runs a substantial task through
|
|
3
|
+
description: "Use when work changes the repository — feature, fix, bug, refactor, migration, integration, rewrite, adoption or hardening; фича, фикс, баг, рефактор, миграция, интеграция, доработать, починить, внедрить, перевести — or when the output is a finding that lands in it: audit/аудит, bug hunt/проверь ошибки, production check/проверь прод, PR review/ревью PR — or on 'run this through the pipeline' / 'прогони по конвейеру', 'the full cycle' / 'полный цикл', /task-pipeline. Runs a substantial task through gated stages: intake grill, docs, brainstorm, spec, plan, build, tests, deploy, post-deploy, docs/wiki, acceptance. 'waves' / 'волнами' closes the whole board; 'checkup' / 'чекап' lists unconfirmed releases; 'setup' audits docs. Not for: answering a question, explaining code, a typo, a one-line edit, a mechanical rename, reconnaissance that lands nothing — say 'no pipeline' / 'без пайплайна' to opt out."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: "Doctrine runs on any agent. The bundled scripts need python3; the run needs git. Missing either degrades, never blocks — the graph verbs and seeded gates go unused, and the run says so."
|
|
6
6
|
---
|
|
@@ -17,8 +17,8 @@ GAP CHECK before it is an interview: the brief is built from the request, the
|
|
|
17
17
|
files and the harvest first, and a question is asked only for a **material
|
|
18
18
|
unknown** the sources cannot answer. A complete brief yields ZERO intake
|
|
19
19
|
questions; what the request or the project already decided is recorded, never
|
|
20
|
-
re-negotiated. The interview, where gaps remain, is
|
|
21
|
-
sweeps stages 1→10 for anything that would stop the run later — then locks the
|
|
20
|
+
re-negotiated. The interview, where gaps remain, is ONE batch ordered by dependency
|
|
21
|
+
and sweeps stages 1→10 for anything that would stop the run later — then locks the
|
|
22
22
|
answers into a brief. Autonomy is bought there or not at all; every MATERIAL
|
|
23
23
|
question skipped at stage 0 comes back as an interruption at stage 6.
|
|
24
24
|
|
|
@@ -176,15 +176,16 @@ Three things the grill does beyond clarifying the request, each in full in
|
|
|
176
176
|
stages and gates run** (FIX-TP-03.02): the numbers "1→10" name the DEFAULT
|
|
177
177
|
profile, and a custom profile with three stages runs three — no hidden stage
|
|
178
178
|
0, 7 or 10 is injected because the default profile has one. What a custom
|
|
179
|
-
profile does NOT escape is the KERNEL (03.01):
|
|
180
|
-
|
|
181
|
-
drops the evidence gate. And an unknown MANDATORY capability blocks the
|
|
179
|
+
profile does NOT escape is the KERNEL (03.01): dropping stages never drops
|
|
180
|
+
the evidence gate. And an unknown MANDATORY capability blocks the
|
|
182
181
|
compile — a profile whose `must_understand` names a capability this runtime
|
|
183
182
|
does not have is refused, never run with the capability silently absent.
|
|
184
183
|
**Is the brief a platform rather than a change?** Then stage 2 also cuts it into
|
|
185
184
|
modules (`references/decomposition.md`) and stages 3→10 run **per module** in
|
|
186
185
|
build order, one brick at a time — stages 0–2 run once, and the module map's
|
|
187
186
|
status column is the resume point (`references/stages.md` → *The program loop*).
|
|
187
|
+
**The whole board?** `/task-pipeline waves` (`references/waves.md`): stages
|
|
188
|
+
0–4 once, 5–8 per wave, 9–10 to close.
|
|
188
189
|
4. Do **not** advance until the stage **gate** passes (`references/stages.md`).
|
|
189
190
|
Honor the gate **type**: for `auto`, verify the gate's `check` yourself and
|
|
190
191
|
stop/return on fail; for `judgment`, record the named judge's ruling as
|
|
@@ -272,7 +273,5 @@ substitute for, and a ResultEnvelope whose stale candidate (older revision, or
|
|
|
272
273
|
a superseded fence) re-plans instead of landing as current
|
|
273
274
|
(`scripts/packet.py validate-result`). The compiler's first stage maps an
|
|
274
275
|
audit report onto parent tasks (`scripts/context_packets.py compile|verify`):
|
|
275
|
-
ids derived from finding ids
|
|
276
|
-
|
|
277
|
-
status, and any row it cannot map blocks the whole compile rather than being
|
|
278
|
-
dropped silently.
|
|
276
|
+
ids derived from finding ids, never positional; evidence, limits and priority
|
|
277
|
+
kept apart from status; and any row it cannot map blocks the compile.
|
|
@@ -205,6 +205,11 @@
|
|
|
205
205
|
],
|
|
206
206
|
"description": "The class of the user-facing surface this node builds, copied from the brief's `surface_class` (stage 0). It selects the gate profile, and `graph.py certify` reads it: on `flagship` or `product` the fourth, `visual` tier report is REQUIRED, because none of unit, seam or product opens the contact sheet; on any other class a `visual` report is accepted when given. Absent on a node that builds no user-facing surface. `references/certification.md` → *The fourth reading*."
|
|
207
207
|
},
|
|
208
|
+
"wave": {
|
|
209
|
+
"type": "integer",
|
|
210
|
+
"minimum": 1,
|
|
211
|
+
"description": "Wave mode only (`references/waves.md`): the pass of stages 5–8 that closes this node, from 1, where wave 1 is the walking skeleton. `graph.py validate` refuses an edge from a later wave's node into an earlier wave's node, because an earlier wave waiting on a later one cannot close in its own pass; `graph.py next --wave N` prints that wave's frontier only; `graph.py wave` moves a node and records the re-cut as a revision. Absent outside wave mode."
|
|
212
|
+
},
|
|
208
213
|
"certification": {
|
|
209
214
|
"type": "object",
|
|
210
215
|
"description": "What the tiered certification recorded for this node. Written by `graph.py certify` on every round, pass or fail: a failing round that wrote nothing would erase the only evidence that a node is churning, which is the number the loop-guard ceiling reads.",
|
|
@@ -444,9 +449,10 @@
|
|
|
444
449
|
"park",
|
|
445
450
|
"close",
|
|
446
451
|
"invalidate",
|
|
447
|
-
"waive"
|
|
452
|
+
"waive",
|
|
453
|
+
"wave"
|
|
448
454
|
],
|
|
449
|
-
"description": "Which mutation made this entry. `close` joined the two on 2026-08-17 with T-5 — and it was caught by a probe rather than by the fixture asserting *the graph after a close still validates*, because `violations()` never reaches an enum. The same disagreement B-084 records, one field over. `invalidate` joined with PF-02.02: a REQ/interface/brief change supersedes a node and resets its downstream proofs."
|
|
455
|
+
"description": "Which mutation made this entry. `close` joined the two on 2026-08-17 with T-5 — and it was caught by a probe rather than by the fixture asserting *the graph after a close still validates*, because `violations()` never reaches an enum. The same disagreement B-084 records, one field over. `invalidate` joined with PF-02.02: a REQ/interface/brief change supersedes a node and resets its downstream proofs. `wave` joined with wave mode: the re-cut after every wave moves nodes between waves, and a move nobody recorded is the quiet re-plan `references/planning.md` refuses."
|
|
450
456
|
},
|
|
451
457
|
"node": {
|
|
452
458
|
"type": "string",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
],
|
|
19
19
|
"gate": {
|
|
20
20
|
"type": "manual",
|
|
21
|
-
"check": "MANDATORY stage — never skipped (only sanctioned bypass: the entry-from-super-ux short-circuit). PHASE 1, before the first question: harvest the knowledge sources (references/knowledge-sources.md) — code, THE CODE GRAPH when one is built (references/knowledge-graph.md: graphify query/affected/god-nodes answer reach, which grep cannot; detect graphify-out/graph.json — recommended, never required), CLAUDE.md/AGENTS.md, CONTEXT.md + docs/adr, docs/ + docs/ux, past pipeline briefs and carry-over ledgers, THE RETRO'S STANDING INSTRUCTIONS (docs/evidence/retro.md — read IN FULL, not queried: they are capped at ten and they BIND this run; stamp each one the moment it fires, since that date is the only evidence behind stage 10's cold-retirement rule — references/retrospective.md), the knowledge wiki when installed (obsidian-wiki — recommended, never required; detect ~/.obsidian-wiki/config), and any other repo or hosted doc system the project names as its docs — queried by this task's own terms, with the SOURCE LEDGER written into the brief (a row per source consulted, or an explicit 'none found'; THE GRAPH'S ROW CARRIES ITS MEASURED LAG — commits and days behind HEAD, the signal it was measured with (built_at_commit exact / mtime approximate / unresolvable), and the marker '⚠ not trusted for reach until refreshed' — that exact string, so one marker is greppable across every ledger — on anything but current. A bare build date does NOT satisfy this: it is the graph's own reply about itself, true and self-reported and silent about whether the graph describes the tree this run is about to change — references/knowledge-graph.md -> Measure the lag, and references/gates.md -> False success for the class). PHASE 2, the grill, built into the skill (references/grill.md) — no companion to install. Per its contract:
|
|
21
|
+
"check": "MANDATORY stage — never skipped (only sanctioned bypass: the entry-from-super-ux short-circuit). PHASE 1, before the first question: harvest the knowledge sources (references/knowledge-sources.md) — code, THE CODE GRAPH when one is built (references/knowledge-graph.md: graphify query/affected/god-nodes answer reach, which grep cannot; detect graphify-out/graph.json — recommended, never required), CLAUDE.md/AGENTS.md, CONTEXT.md + docs/adr, docs/ + docs/ux, past pipeline briefs and carry-over ledgers, THE RETRO'S STANDING INSTRUCTIONS (docs/evidence/retro.md — read IN FULL, not queried: they are capped at ten and they BIND this run; stamp each one the moment it fires, since that date is the only evidence behind stage 10's cold-retirement rule — references/retrospective.md), the knowledge wiki when installed (obsidian-wiki — recommended, never required; detect ~/.obsidian-wiki/config), and any other repo or hosted doc system the project names as its docs — queried by this task's own terms, with the SOURCE LEDGER written into the brief (a row per source consulted, or an explicit 'none found'; THE GRAPH'S ROW CARRIES ITS MEASURED LAG — commits and days behind HEAD, the signal it was measured with (built_at_commit exact / mtime approximate / unresolvable), and the marker '⚠ not trusted for reach until refreshed' — that exact string, so one marker is greppable across every ledger — on anything but current. A bare build date does NOT satisfy this: it is the graph's own reply about itself, true and self-reported and silent about whether the graph describes the tree this run is about to change — references/knowledge-graph.md -> Measure the lag, and references/gates.md -> False success for the class). PHASE 2, the grill, built into the skill (references/grill.md) — no companion to install. Per its contract: close what the code/docs answer first, then ONE batch of the material unknowns ordered by dependency, a recommended answer with each (one at a time only where answers depend on each other or the operator asks), contradictions reconciled; EVERY answer that touches a harvested source is validated against that source — the operator outranks any document, but only out loud, and the losing side is logged for the stage-9 doc update; domain awareness applied (terms challenged against CONTEXT.md, ADRs recorded for hard-to-reverse calls). The autonomy sweep is covered — every stage 1-10 has its blockers pre-resolved (docs sources, branch/tracker policy, test + lint commands, deploy target and authorization, log/health locations, docs+wiki targets, and for UI tasks the design surface: Figma on or text-only, is the Figma MCP connected, and if it is not — ship text-only or stop and connect it, since the UX chain degrades on its own and never blocks; plus, with Figma on, the DESIGN DESTINATION — which team/org by name and which file per surface, App / Web / ASO (the recorded one, a URL the operator gives, or creation in that named team with the creation explicitly authorized), written into the project's canonical record before the first frame, and never created while a recorded file resolves — an unreachable recorded file means stop and ask, never make a replacement) or is explicitly marked 'stop and ask here'. UI verdict recorded (arms super-ux), and for a user-facing task its SURFACE CLASS — surface_class: flagship | product | internal | ad — which selects the visual gate profile for stages 3, 6 and 10 (references/stages.md -> The surface class); model decision recorded. All of it locked into a committed task brief the operator confirms before stage 1. The REQ table is written — one row per independently verifiable deliverable, each naming how it is verified — and frozen: adding later is free, removing or narrowing needs the operator's explicit agreement. The carry-over ledger is seeded. PHASE 1b, THE DOCUMENTATION INVENTORY (references/documentation.md): four questions answered into docs/DOCMAP.md before the interview — where settled things live (the DECISION HOME, and there is exactly one per project: an existing docs/adr/ IS the register and is recorded as such, never duplicated), what each fact's single home is, what a change of type X obliges (THE PROPAGATION MATRIX, non-empty, every row naming the check that enforces it or the word 'review' with a one-line reason), and what proves it (the gate command). A project with no answers gets them seeded — registers, matrix and scripts/check-docs.sh from the skill's templates — and the seeding is recorded as the register's first entry; the seeded gate must exit 0 on its own seeds, because a project that starts red teaches everyone on day one that the gate is noise. The regime is recorded. PHASE 1c, RECONCILE: git says how it should be, the run record says how it turned out — read both for the area about to be touched and resolve every divergence (the document is stale, the record is wrong, or they genuinely disagree and that is a decision), because starting on an unresolved divergence means building against a system that does not exist. The retro's in-force sections are read IN FULL and its archive is QUERIED by the task's nouns."
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
{
|
|
@@ -33,6 +33,8 @@ docs/
|
|
|
33
33
|
YYYY-MM-DD-<topic>-brief.md # stage 0 — locked intake brief (grill output)
|
|
34
34
|
YYYY-MM-DD-<topic>-carryover.md # stage 0 seeds it; EVERY stage appends; stage 10 reads it
|
|
35
35
|
YYYY-MM-DD-<topic>-modules.md # stage 2 — module map + build order (platforms only)
|
|
36
|
+
YYYY-MM-DD-<topic>-inventory.json # wave mode, stage 0 — every open item by id, verified
|
|
37
|
+
YYYY-MM-DD-<topic>-canvas.md # wave mode, stages 2–3 — the target state over the current logic
|
|
36
38
|
YYYY-MM-DD-<topic>-design.md # stage 3 — the spec / module dossier (locks shared contracts)
|
|
37
39
|
YYYY-MM-DD-<topic>-acceptance.md # stage 10 — REQ coverage table + evidence
|
|
38
40
|
plans/
|
|
@@ -170,6 +172,8 @@ them is a finding, not a tie-break ([`knowledge-sources.md`](knowledge-sources.m
|
|
|
170
172
|
| 10 Board resolution | `<artifacts>/backlog.md` — every unresolved ledger row — homed `backlog` or still `open` — arrives with a real id, and the ledger row is updated to name it; priority re-derived ([`backlog.md`](backlog.md)) | the next run's harvest, and every loop iteration |
|
|
171
173
|
| 10 Retro rotation | `<artifacts>/retro/YYYY-QN.md` — entries older than five stamps, plus every retirement, each with its commit | queried by a later run's harvest |
|
|
172
174
|
| 2 Decompose | `specs/<topic>-modules.md` — module map, build order, contracts, per-module status (platforms only) | stages 3–10, every module's run |
|
|
175
|
+
| 0 Wave mode | `specs/<topic>-inventory.json` — every open item: id, source, status, area, node, wave, release ([`waves.md`](waves.md)) | stages 2–10, every wave's re-measure |
|
|
176
|
+
| 2–3 Wave mode | `specs/<topic>-canvas.md` — as-is → to-be per module and contract, the seams, REQ → module → wave, what is deleted | every wave, and each re-cut |
|
|
173
177
|
| 3 Spec | `specs/<topic>-design.md` — module dossier for a decomposed platform (+ links `docs/ux/*` for UI) | stage 4 |
|
|
174
178
|
| 4 Plan | `plans/<topic>.md` | stage 5 |
|
|
175
179
|
| 3 UX track | `docs/ux/{foundation,flows,screens,scenarios}.md` | stages 4–9 + `/ux-lint` |
|
|
@@ -56,9 +56,12 @@ not a question to re-open from scratch.
|
|
|
56
56
|
the platform's shape — the pieces, how they relate, what order they land in —
|
|
57
57
|
not the details of one corner; those belong to each module's own stage-3
|
|
58
58
|
dossier. Don't refine something that needs splitting first.
|
|
59
|
-
3. **Questions one
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
3. **Questions in one batch, after the sources.** What the brief, the code and the
|
|
60
|
+
recorded decisions cannot answer goes in one message, ordered by dependency, each
|
|
61
|
+
with a recommendation; one at a time only where an answer reshapes the next
|
|
62
|
+
question or the operator asks ([`grill.md`](grill.md)). Multiple choice where it
|
|
63
|
+
fits, open where it doesn't. Purpose, constraints, success criteria — anything the
|
|
64
|
+
brief left at design level.
|
|
62
65
|
4. **Propose 2–3 approaches with trade-offs**, lead with your recommendation and
|
|
63
66
|
the reason for it. **YAGNI ruthlessly** — strip anything the task doesn't need
|
|
64
67
|
from every option before presenting.
|
|
@@ -610,9 +610,9 @@ merge.
|
|
|
610
610
|
|
|
611
611
|
If it returns findings, dispatch **ONE** fix subagent with the complete list — not
|
|
612
612
|
one fixer per finding; per-finding fixers each rebuild context and re-run suites.
|
|
613
|
-
Then exactly **one** scoped re-review of the fix
|
|
613
|
+
Then exactly **one** scoped re-review of the fix round. Adjudicate residuals as in
|
|
614
614
|
the breaker: park with rulings, or stop on load-bearing ones. There is no second
|
|
615
|
-
fix
|
|
615
|
+
fix round.
|
|
616
616
|
|
|
617
617
|
## 6. Integrate, then finish
|
|
618
618
|
|
|
@@ -47,13 +47,21 @@ This spans the whole run, not one stage:
|
|
|
47
47
|
|
|
48
48
|
- the plan's tasks at stage 5;
|
|
49
49
|
- the boundary between stages whose gate is `auto`;
|
|
50
|
-
- the per-module program loop when the brief was a platform
|
|
50
|
+
- the per-module program loop when the brief was a platform, and the waves when
|
|
51
|
+
the request was the whole board ([`waves.md`](waves.md));
|
|
51
52
|
- the acceptance → retro tail, which is where runs most often stall one step from
|
|
52
53
|
finished.
|
|
53
54
|
|
|
54
55
|
It stops for exactly four things: a `manual` gate, a block it cannot resolve, a
|
|
55
56
|
genuine ambiguity the brief does not answer, and completion.
|
|
56
57
|
|
|
58
|
+
**A block or an ambiguity stops its node, not the run.** The node is parked with the
|
|
59
|
+
question as its reason, marked `needs-operator` and answerable without context
|
|
60
|
+
([`prioritisation.md`](prioritisation.md) → *5. Where the human goes*), and the run
|
|
61
|
+
continues on every node it does not block. The run itself stops only when the whole
|
|
62
|
+
frontier is blocked — `graph.py next` exits `4` — and the questions are then asked
|
|
63
|
+
together. **When the board is empty, say so once**, and do not invent work to fill it.
|
|
64
|
+
|
|
57
65
|
**This extends [`build.md`](build.md); it does not replace it.** That file's
|
|
58
66
|
*Continuous execution* rule governs the inside of a stage-5 execution — do not
|
|
59
67
|
check in between the tasks of a plan you were told to execute. It is unconditional
|
|
@@ -122,7 +130,9 @@ So at the bottom of an iteration, beside the re-derived `prio`:
|
|
|
122
130
|
2. state whether the next item still serves it;
|
|
123
131
|
3. if it does not, **re-order or re-scope the queue and say what moved and why.** A row
|
|
124
132
|
that stops serving the goal leaves for the board with its reason
|
|
125
|
-
([`backlog.md`](backlog.md)), it does not get worked because it was next.
|
|
133
|
+
([`backlog.md`](backlog.md)), it does not get worked because it was next. On a work
|
|
134
|
+
graph the move is a committed `revisions` entry — `park`, `add`, or `wave` between
|
|
135
|
+
waves — never an untracked edit.
|
|
126
136
|
|
|
127
137
|
A queue re-derived only by `age` and `sev` is a queue that is honest about priority and
|
|
128
138
|
silent about purpose. Both numbers can be right while the run is finishing something the
|
|
@@ -137,6 +137,10 @@ Rules for the loop:
|
|
|
137
137
|
lands, or build several and deploy once. Record it; don't decide it per module.
|
|
138
138
|
- **Update the map's status column as each module closes**, in the same commit as
|
|
139
139
|
that module's acceptance. The map is the resume point after a lost context.
|
|
140
|
+
- **The whole board is waves, not bricks** ([`waves.md`](waves.md)). When the request
|
|
141
|
+
is every open item rather than one platform, stages 0–2 still run once, the map
|
|
142
|
+
becomes the canvas, and the loop runs **one wave at a time**. A wave may hold several
|
|
143
|
+
bricks or nodes, provided they close together in one pass.
|
|
140
144
|
- **Loop discipline:** a module re-entering the same stage a third time trips the
|
|
141
145
|
loop guard ([`loop-guard.md`](loop-guard.md)) — stop, name the oscillation, and
|
|
142
146
|
fix the layer that owns it instead of iterating.
|
|
@@ -27,6 +27,7 @@ UX track on a user-facing task.
|
|
|
27
27
|
| 0 Intake grill | `references/grill.md` |
|
|
28
28
|
| 2 Brainstorm | `references/brainstorm.md` |
|
|
29
29
|
| 2 Decompose (platforms only) | `references/decomposition.md` |
|
|
30
|
+
| 0–10 · **wave mode** — the whole board: 0–4 once, 5–8 per wave, 9–10 to close | `references/waves.md` |
|
|
30
31
|
| 3 Spec | `references/spec.md` |
|
|
31
32
|
| 4 Plan | `references/planning.md` |
|
|
32
33
|
| the queue the loop walks | `references/work-graph.md` |
|
|
@@ -63,16 +63,25 @@ not depend on it.
|
|
|
63
63
|
|
|
64
64
|
Where material unknowns remain, interview the operator about THOSE until you
|
|
65
65
|
reach a **shared understanding**. Walk down each unresolved branch of the
|
|
66
|
-
decision tree, resolving dependencies between decisions
|
|
67
|
-
|
|
68
|
-
1. **
|
|
66
|
+
decision tree, resolving dependencies between decisions in order.
|
|
67
|
+
|
|
68
|
+
1. **Close from the sources first, then ask ONE batch.** Every material unknown
|
|
69
|
+
the harvest, the code and the recorded decisions leave open goes into one
|
|
70
|
+
message, ordered by dependency, each with a recommended answer and what each
|
|
71
|
+
answer changes. **One question at a time only when the answers depend on each
|
|
72
|
+
other** — the next question cannot be written until this one is answered, and
|
|
73
|
+
then you go depth-first — **or when the operator asks for it.** Decided by the
|
|
74
|
+
operator on 2026-10-11: a question asked alone costs a human round, and a batch
|
|
75
|
+
costs one. [`prioritisation.md`](prioritisation.md) → *5. Where the human goes*
|
|
76
|
+
says the same about the rest of the run.
|
|
69
77
|
2. **Recommend an answer with every question** (+ a one-line rationale). "What do
|
|
70
78
|
you think?" is lazy — you have the codebase in front of you, they don't.
|
|
71
79
|
3. **If the codebase can answer it, go read the codebase.** Spending the
|
|
72
80
|
operator's turn on something `grep`/`Read`/context7 would have told you is the
|
|
73
81
|
most common way to waste a grill.
|
|
74
|
-
4. **
|
|
75
|
-
|
|
82
|
+
4. **Prerequisites first.** Order the batch so prerequisite decisions come before
|
|
83
|
+
the ones they shape; where an answer can invalidate a later question, ask the
|
|
84
|
+
prerequisite alone and go depth-first.
|
|
76
85
|
5. **Reconcile contradictions immediately**, and chase dodges: "we'll decide
|
|
77
86
|
later" → "what's the latest you can decide and still ship?"
|
|
78
87
|
6. **Cover the autonomy sweep** (below). An unasked MATERIAL question is not
|
|
@@ -112,6 +112,14 @@ which grows but never reorders a stage. Discovery that lands in a committed
|
|
|
112
112
|
artifact is not a dynamic graph; discovery that changes what runs next, silently,
|
|
113
113
|
is.
|
|
114
114
|
|
|
115
|
+
**Wave mode's re-cut is the same kind of discovery** ([`waves.md`](waves.md)). After
|
|
116
|
+
each wave the remaining waves are re-cut from the re-measured inventory, and every move
|
|
117
|
+
lands twice in committed form: a `revisions` entry in the work graph (`graph.py wave`,
|
|
118
|
+
which refuses a move that breaks the wave order) and the canvas edit that says why. The
|
|
119
|
+
stage list does not change and no stage is invented; what changes is which nodes the
|
|
120
|
+
next pass of stages 5–8 takes, and that is written down before the pass begins. So the
|
|
121
|
+
static-graph decision holds.
|
|
122
|
+
|
|
115
123
|
## Task right-sizing
|
|
116
124
|
|
|
117
125
|
A task is the smallest unit that carries its own test cycle and is worth a fresh
|
|
@@ -71,6 +71,7 @@ a row pointing outside the bundle is the defect this file exists to catch.
|
|
|
71
71
|
| What a stage reads, and which host files bind it | `references/artifacts.md` |
|
|
72
72
|
| The design conversation, its hard gate, UI detection, user paths | `references/brainstorm.md` |
|
|
73
73
|
| Cutting a platform into modules, brick criteria, build order | `references/decomposition.md` |
|
|
74
|
+
| **Wave mode** — the whole board: the inventory, the canvas, the one question batch, the wave cut and the close pass | `references/waves.md` |
|
|
74
75
|
| What a spec must lock, the UX-track order, the module dossier | `references/spec.md` |
|
|
75
76
|
| The zero-context plan format, parallel groups, set equality | `references/planning.md` |
|
|
76
77
|
| The work graph: its fields, the verbs and their exit codes, and the three invariants a schema cannot state | `references/work-graph.md`, `scripts/graph.py`, `graph.schema.json` |
|
|
@@ -166,9 +166,10 @@ never that the work was skipped quietly.
|
|
|
166
166
|
([`progress.md`](progress.md)), and a skip nobody can see is indistinguishable from a
|
|
167
167
|
stage that was never entered.
|
|
168
168
|
- **How it runs: [`grill.md`](grill.md)** — the full doctrine, built into this
|
|
169
|
-
skill (nothing to install). In short:
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
skill (nothing to install). In short: close what the sources can, then ONE batch
|
|
170
|
+
of the material unknowns, ordered by dependency, a recommended answer with each —
|
|
171
|
+
one at a time only where answers depend on each other or the operator asks;
|
|
172
|
+
contradictions reconciled on the spot; **every answer that touches
|
|
172
173
|
a harvested source is checked against it** — the operator outranks any document,
|
|
173
174
|
but only out loud, and the losing side is logged for the stage-9 doc update; plus
|
|
174
175
|
**domain awareness**
|
|
@@ -267,9 +268,9 @@ never that the work was skipped quietly.
|
|
|
267
268
|
- **Freedom: high** — many designs are valid — this is the open field, and the only fixed thing is the gate ([`gates.md`](gates.md) → *Axis C*).
|
|
268
269
|
- **How it runs: [`brainstorm.md`](brainstorm.md)** — built into this skill. Read
|
|
269
270
|
the brief first (stage 0 already answered scope/constraints/done-criteria), then
|
|
270
|
-
explore the codebase, scope-check for decomposition,
|
|
271
|
-
approaches with a recommendation, design presented in sections and
|
|
272
|
-
section by section. **Hard gate:** no code, no scaffolding, no implementation
|
|
271
|
+
explore the codebase, scope-check for decomposition, the open questions in one
|
|
272
|
+
batch, 2–3 approaches with a recommendation, design presented in sections and
|
|
273
|
+
approved section by section. **Hard gate:** no code, no scaffolding, no implementation
|
|
273
274
|
skill before the operator approves the design — including on "obviously simple"
|
|
274
275
|
tasks.
|
|
275
276
|
- **UI detection (mandatory check):** decide whether the task touches any
|
|
@@ -859,6 +860,9 @@ module N → 3 spec (dossier) → 4 plan → 5 build → 6 tests → 7 lint+depl
|
|
|
859
860
|
- **Program done** when every row is `done` or `deferred` with an agreed home, the
|
|
860
861
|
cross-module contracts are covered by tests that cross the seam, and a final
|
|
861
862
|
acceptance covers the platform's whole REQ table — not module by module.
|
|
863
|
+
- **The whole board, rather than one platform, is wave mode**
|
|
864
|
+
([`waves.md`](waves.md)): stages 0–4 once over every open item, 5–8 per wave with a
|
|
865
|
+
close pass after each, 9–10 to close the release.
|
|
862
866
|
|
|
863
867
|
## Cross-cutting — the Doc Loop
|
|
864
868
|
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Wave mode — closing a whole board in its final form
|
|
2
|
+
|
|
3
|
+
`/task-pipeline waves`. The request is not one change but **the whole board**: every open
|
|
4
|
+
item a project carries — the board and its lanes, carry-over ledgers, plan rows,
|
|
5
|
+
verification rows still at `never`, scenarios with no coverage, open PRs and unlanded
|
|
6
|
+
branches, asks from other products' sessions — plus the operator's new requests.
|
|
7
|
+
|
|
8
|
+
**It is a program mode of the existing stages, not a parallel cycle.** Stages 0–4 run
|
|
9
|
+
once, for the whole board. Stages 5–8 run once per wave. Stages 9–10 close the release.
|
|
10
|
+
Every gate in [`stages.md`](stages.md) still holds where its stage runs; what the mode
|
|
11
|
+
changes is how many times each stage runs, and over what.
|
|
12
|
+
|
|
13
|
+
Decided by the operator on 2026-10-11 and first applied that day to a board of 701 items.
|
|
14
|
+
|
|
15
|
+
## Contents
|
|
16
|
+
|
|
17
|
+
- The rule it exists for
|
|
18
|
+
- How the stages map
|
|
19
|
+
- Wave 0 — the survey and the inventory
|
|
20
|
+
- The questions — closed from sources, then one batch
|
|
21
|
+
- The canvas — the target state over the current logic
|
|
22
|
+
- Cutting the waves
|
|
23
|
+
- The wave loop
|
|
24
|
+
- The close pass, after every wave
|
|
25
|
+
- A board bigger than one release
|
|
26
|
+
- What stays human
|
|
27
|
+
- The graph carries the wave
|
|
28
|
+
- Rationalizations
|
|
29
|
+
|
|
30
|
+
## The rule it exists for
|
|
31
|
+
|
|
32
|
+
**Build the system in its final form, never through intermediate states.** An
|
|
33
|
+
intermediate state that a later task undoes is the most expensive thing an agent
|
|
34
|
+
produces: the build, the review, the migration and the operator's attention are each
|
|
35
|
+
paid twice, and the second payment is for removing the first. So every item is designed
|
|
36
|
+
against the target state of the whole system first, and only then cut into waves.
|
|
37
|
+
|
|
38
|
+
**No wave builds a shape a later wave undoes.** A stub is allowed only at a contract the
|
|
39
|
+
canvas declares, and the wave that removes it is named beside it. A stub with no named
|
|
40
|
+
removal is the intermediate state this mode exists to refuse.
|
|
41
|
+
|
|
42
|
+
What the mode buys, in the order the operator named it: transparency (the inventory,
|
|
43
|
+
the canvas and the wave plan live in the repository, so any agent can resume from them),
|
|
44
|
+
stability, autonomy, predictability (each wave says before it starts what it closes and
|
|
45
|
+
how that is proved), minimum operator time, fast real results, and a strategy corrected
|
|
46
|
+
on real agents, tasks and users rather than on the first guess.
|
|
47
|
+
|
|
48
|
+
## How the stages map
|
|
49
|
+
|
|
50
|
+
| Stage | In wave mode |
|
|
51
|
+
|---|---|
|
|
52
|
+
| 0 Intake | once, over the whole board: wave 0's survey is the harvest, the inventory is the work-list, and every in-scope item becomes or joins a REQ row in the operator's words |
|
|
53
|
+
| 1 Docs study | once, for every contract the canvas locks |
|
|
54
|
+
| 2 Brainstorm + decompose | once: the canvas extends the module map ([`decomposition.md`](decomposition.md)), and the work graph carries each node's wave |
|
|
55
|
+
| 3 Spec | once: the canvas is the committed target-state spec; a wave's dossier may deepen a section, never contradict it |
|
|
56
|
+
| 4 Plan | once: every wave's nodes, its exit criteria and the question batch. Re-cut at each wave boundary, as a recorded revision |
|
|
57
|
+
| 5–8 Build → post-deploy | per wave, then the close pass |
|
|
58
|
+
| 9 Docs + wiki · 10 Acceptance | once, to close the release, over everything the waves shipped |
|
|
59
|
+
|
|
60
|
+
**One planning gate, not three prompts.** The manual gates of stages 0, 2 and 3 are
|
|
61
|
+
presented together at the end of planning: the brief, the canvas, the wave plan and the
|
|
62
|
+
question batch in one message. Each gate's criteria are still checked and printed; the
|
|
63
|
+
operator is asked once, and the go names the gates it closes. Silence closes none.
|
|
64
|
+
|
|
65
|
+
## Wave 0 — the survey and the inventory
|
|
66
|
+
|
|
67
|
+
**Harvest every source of open work, by id** ([`knowledge-sources.md`](knowledge-sources.md)
|
|
68
|
+
supplies the sources; [`backlog.md`](backlog.md) the board). For whole-project scope, run
|
|
69
|
+
`project-audit` beside it as a read-only sidecar: its HTML and JSON are measured
|
|
70
|
+
findings, and its proposed rows join the inventory like any other source. Where the
|
|
71
|
+
product has users, walk their real path in the built app on a fresh profile with
|
|
72
|
+
disposable data. A finding is a fact with a receipt, never an impression.
|
|
73
|
+
|
|
74
|
+
**One machine-readable inventory, nothing outside it.** A single committed file,
|
|
75
|
+
`<artifacts>/specs/YYYY-MM-DD-<topic>-inventory.json`, one entry per open item:
|
|
76
|
+
|
|
77
|
+
| Field | Carries |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `id` | the item's id in its owning register; an item without one gets one there, in the same change |
|
|
80
|
+
| `source` | where it was found, as `file:line` |
|
|
81
|
+
| `status` | as measured, not as the register last said |
|
|
82
|
+
| `area` | the part of the system it touches |
|
|
83
|
+
| `node` | the canvas node it lands on |
|
|
84
|
+
| `wave` | the wave that closes it, or the release it is carried to |
|
|
85
|
+
| `release` | this release, or `program` |
|
|
86
|
+
|
|
87
|
+
`blocked_by`, `closable_in_one_pass` and `notes` are worth carrying beside them.
|
|
88
|
+
|
|
89
|
+
**Verify each open item against the code before trusting it.** A stale item is closed
|
|
90
|
+
in its owning register with the receipt that shows it done; duplicates are clustered,
|
|
91
|
+
both ids kept. A register that says *open* about shipped work is the commonest thing a
|
|
92
|
+
survey finds.
|
|
93
|
+
|
|
94
|
+
**Assign nodes and waves with a committed script, not by hand.** A deterministic script
|
|
95
|
+
reads the inventory and a short wave table and writes `node`, `wave` and `release` onto
|
|
96
|
+
every entry, then prints the counts per wave and per node. A re-cut is then a diff to a
|
|
97
|
+
table and a re-run, which a reviewer can read; seven hundred hand-edited rows are not.
|
|
98
|
+
|
|
99
|
+
## The questions — closed from sources, then one batch
|
|
100
|
+
|
|
101
|
+
**Close every question from the sources first**, in this order: the operator's recorded
|
|
102
|
+
decisions, ADRs and principles, the knowledge base, the code, the neighbours' contracts.
|
|
103
|
+
A question answered there is recorded with its citation — *resolved from the sources,
|
|
104
|
+
not asked* — and closed. What remains is only what is genuinely the operator's: access,
|
|
105
|
+
consent, money, a business or product direction, an outward or irreversible act.
|
|
106
|
+
|
|
107
|
+
**Those go once, as one batch, at the end of planning.** Each carries a recommended
|
|
108
|
+
answer and the consequence of each answer, ordered so prerequisite decisions come first
|
|
109
|
+
([`grill.md`](grill.md)). A go that leaves a question unanswered takes the
|
|
110
|
+
recommendation, recorded as **assumed** in the decisions table, and every wave that
|
|
111
|
+
depends on an assumption names it — so a different answer later re-plans exactly those
|
|
112
|
+
waves and no others. After planning, a question is asked only at a wave boundary, in the
|
|
113
|
+
hand-back's DECISIONS WAITING ([`progress.md`](progress.md)).
|
|
114
|
+
|
|
115
|
+
**An outward or irreversible act is never assumed.** Publishing, tagging, deploying
|
|
116
|
+
without a specific standing authorization, sending to a person, spending money,
|
|
117
|
+
deleting data: each waits for the operator's explicit go, asked with a verified direct
|
|
118
|
+
link. A recommendation is not an authorization.
|
|
119
|
+
|
|
120
|
+
## The canvas — the target state over the current logic
|
|
121
|
+
|
|
122
|
+
One committed spec, `<artifacts>/specs/YYYY-MM-DD-<topic>-canvas.md`, extending the module
|
|
123
|
+
map. It is the current system with every change laid over it:
|
|
124
|
+
|
|
125
|
+
1. **As-is → to-be per module and per contract** — data and events, screens and flows,
|
|
126
|
+
cross-product links.
|
|
127
|
+
2. **The integration seams** — how each integration happens, who owns each side, what
|
|
128
|
+
crosses it.
|
|
129
|
+
3. **REQ → module → wave**, one row per requirement.
|
|
130
|
+
4. **Deleted or superseded** — what the new version removes (code, screens, contracts,
|
|
131
|
+
docs) and which wave removes it. A removal nobody wrote down survives the release.
|
|
132
|
+
|
|
133
|
+
**Every inventory item lands on a node.** An item that lands nowhere is either out of
|
|
134
|
+
scope — said, with a register row — or a gap in the canvas. **The canvas is reviewed for
|
|
135
|
+
coherence before any wave is cut:** two items that change one seam are designed together,
|
|
136
|
+
because designed apart they are the intermediate state the rule above forbids.
|
|
137
|
+
|
|
138
|
+
## Cutting the waves
|
|
139
|
+
|
|
140
|
+
**A wave holds only items closable in one pass.** All four, for every item in it:
|
|
141
|
+
|
|
142
|
+
- its dependencies are done, or inside the same wave;
|
|
143
|
+
- it touches one seam, or a few adjacent ones;
|
|
144
|
+
- its verification can run together with the rest of the wave;
|
|
145
|
+
- the wave leaves the system working — landable, with no half-built shape exposed.
|
|
146
|
+
|
|
147
|
+
**Wave 1 is the walking skeleton**: the thinnest end-to-end path of the target that
|
|
148
|
+
proves the spine, for the reason [`decomposition.md`](decomposition.md) gives for a
|
|
149
|
+
platform's first brick. **Each wave names its exit criteria before it starts** — the
|
|
150
|
+
tests, the live walk, the gates and the documents that will prove it closed. Criteria
|
|
151
|
+
written after the wave are a description of what happened.
|
|
152
|
+
|
|
153
|
+
An item that cannot close in one pass because it needs a person stays on its node,
|
|
154
|
+
marked `needs-operator`, out of every wave until it is unblocked.
|
|
155
|
+
|
|
156
|
+
## The wave loop
|
|
157
|
+
|
|
158
|
+
Each wave runs stages 5–8 as [`stages.md`](stages.md) states them, then the close pass.
|
|
159
|
+
Between waves the run does not ask whether to continue: it takes the next wave
|
|
160
|
+
([`continuity.md`](continuity.md)). A genuine ambiguity parks its node `needs-operator`
|
|
161
|
+
and the wave goes on with the nodes it does not block; the run stops only when the whole
|
|
162
|
+
frontier is blocked. When the board is empty, it says so once.
|
|
163
|
+
|
|
164
|
+
Research, the audit and the blind readings run in parallel agents; builds stay sequential
|
|
165
|
+
wherever two of them share a seam ([`planning.md`](planning.md) → file ownership).
|
|
166
|
+
|
|
167
|
+
## The close pass, after every wave
|
|
168
|
+
|
|
169
|
+
In this order, inside the wave rather than after it:
|
|
170
|
+
|
|
171
|
+
1. **Verify.** Every node the wave closes is certified ([`certification.md`](certification.md)),
|
|
172
|
+
and [`audit.md`](audit.md)'s exit is re-measured over what the wave touched.
|
|
173
|
+
2. **Finish and fix — one fix round**, inside [`loop-guard.md`](loop-guard.md)'s caps.
|
|
174
|
+
Every finding is fixed in the wave or parked with a board row and its reason; nothing
|
|
175
|
+
is dropped silently.
|
|
176
|
+
3. **Document** — the Doc Loop and the stage-9 propagation matrix
|
|
177
|
+
([`documentation.md`](documentation.md)), scoped to the wave: scenarios, decisions,
|
|
178
|
+
docs and registers in the same change as the code.
|
|
179
|
+
4. **Mark the canvas.** Each node the wave closed carries its status and the commit.
|
|
180
|
+
5. **Re-read the goal** ([`continuity.md`](continuity.md) → *The goal is re-read between
|
|
181
|
+
items, not only the board*). A re-plan is a committed `revisions` entry plus a canvas
|
|
182
|
+
edit, never a quiet change.
|
|
183
|
+
6. **Re-measure the inventory and re-cut the remaining waves** from the updated target —
|
|
184
|
+
never from the original guess. New findings join the inventory with ids.
|
|
185
|
+
|
|
186
|
+
## A board bigger than one release
|
|
187
|
+
|
|
188
|
+
**It becomes a program.** The release takes the first waves; the rest stays on the
|
|
189
|
+
canvas by node, `release: program`, never dropped. The next release resumes from the
|
|
190
|
+
canvas: it re-measures the inventory rather than re-running wave 0 from nothing, and its
|
|
191
|
+
waves are cut from the nodes still open.
|
|
192
|
+
|
|
193
|
+
## What stays human
|
|
194
|
+
|
|
195
|
+
Sign-ins, consents, payments, sending to people, and every outward or irreversible act.
|
|
196
|
+
The mode removes *"shall I continue?"* between waves and *"what did you mean?"* where a
|
|
197
|
+
source already says; it never removes a person from a decision that is theirs. A
|
|
198
|
+
`manual` gate still waits for its go ([`continuity.md`](continuity.md) → *The limit,
|
|
199
|
+
before the capability*).
|
|
200
|
+
|
|
201
|
+
## The graph carries the wave
|
|
202
|
+
|
|
203
|
+
The work graph ([`work-graph.md`](work-graph.md)) carries `wave` on a node, an integer from
|
|
204
|
+
1. Three verbs read it:
|
|
205
|
+
|
|
206
|
+
- `graph.py validate` refuses an edge from a later wave's node into an earlier wave's
|
|
207
|
+
node: an earlier wave that waits on a later one cannot close in its own pass.
|
|
208
|
+
- `graph.py next --wave N` prints wave N's frontier only. Exit `3` says the wave is done,
|
|
209
|
+
`4` that nothing in it is runnable, `1` that no node is in wave N.
|
|
210
|
+
- `graph.py wave N-007 --to 3 --why "<reason>"` moves a node and writes the revision.
|
|
211
|
+
It refuses a move that would break the order above.
|
|
212
|
+
|
|
213
|
+
Without python3 the same three facts live in the wave plan's table, checked by hand and
|
|
214
|
+
said to be checked by hand.
|
|
215
|
+
|
|
216
|
+
## Rationalizations
|
|
217
|
+
|
|
218
|
+
| Excuse | Reality |
|
|
219
|
+
|---|---|
|
|
220
|
+
| "Ship the simple version now, the real one later" | That is the intermediate state. Its removal is a second build nobody planned |
|
|
221
|
+
| "Ask each question as it comes up" | Each one is a human round. Close it from the sources, or put it in the batch |
|
|
222
|
+
| "The board is too big to inventory" | The inventory is the plan. Without it the waves are cut from memory |
|
|
223
|
+
| "Re-cutting waves makes the pipeline dynamic" | A re-cut is a committed revision and a canvas edit. The static-graph decision in [`planning.md`](planning.md) holds |
|
|
224
|
+
| "The recommendation is obviously right, so assume the deploy go" | An outward act is never assumed. Ask for the specific go once, in the batch |
|
|
225
|
+
| "Wave 3 can stub what wave 5 will build properly" | Only at a declared contract, with wave 5 named as the stub's removal |
|
|
@@ -41,6 +41,7 @@ turn of every loop.
|
|
|
41
41
|
| `nodes[].blocked_by` | what must PRODUCE first. The frontier obeys a satisfaction predicate, and only `done` satisfies — a PARKED producer blocks its consumer, because a park is a decision not to produce, not a production. `next` names each held consumer with the park's reason; a valid alternative producer is an explicit, versioned edge change, never an implicit unblock |
|
|
42
42
|
| `nodes[].touches` | what it **mutates**. Two runnable nodes writing one file is the false parallelism [`planning.md`](planning.md) refuses — *distinct is not the same as independent, and the check is what they touch, never what they are called* |
|
|
43
43
|
| `nodes[].check` | **how this node will be closed** — one command, or the named judgement where no command can decide it. Required on every node except a `parked` one. The certification's `unit` tier runs it and reports its output as the evidence row ([`certification.md`](certification.md) — three blind tiers close a node, not one reader); before this field existed that instruction pointed at an absence, leaving a verifier the two things it forbids — invent a check, or run everything (B-080) |
|
|
44
|
+
| `nodes[].wave` | wave mode only ([`waves.md`](waves.md)): the pass of stages 5–8 that closes the node, from 1. **An earlier wave may not wait on a later one** — `validate` refuses the edge, because a wave holds only what closes in its own pass |
|
|
44
45
|
| `nodes[].evidence` | required when `status` is `done`. A node called done by assertion is what evidence exists to prevent |
|
|
45
46
|
| `nodes[].parked_reason` | required when `status` is `parked`. A park with no reason is indistinguishable, a week later, from work quietly dropped |
|
|
46
47
|
| `edges[].payload` | what the dependency hands over. **An edge carrying no named artifact is chronology drawn as architecture** |
|
|
@@ -54,11 +55,12 @@ conditional on the code, never merely sequenced after it.**
|
|
|
54
55
|
| Verb | Prints | Codes |
|
|
55
56
|
|---|---|---|
|
|
56
57
|
| `validate` | every violation, in a stable order | `0` clean · `1` any |
|
|
57
|
-
| `next` | the frontier, ordered by how much each node unblocks — **and nothing else
|
|
58
|
+
| `next` | the frontier, ordered by how much each node unblocks — **and nothing else**. `--wave N` narrows it to one wave, and `3` then means *this wave is done* | `0` printed · `3` all done · `4` nothing runnable · `1` with `--wave`, a wave no node is in |
|
|
58
59
|
| `coverage` | each requirement with the nodes serving it | `0` covered · `1` a gap, named |
|
|
59
60
|
| `goal` | the release goal | `0` · `3` unstated |
|
|
60
61
|
| `add` | the id it allocated | `0` · `1` refused |
|
|
61
62
|
| `park` | the id and the reason | `0` · `1` refused |
|
|
63
|
+
| `wave` | wave mode's re-cut: the node, its old and new wave, and the reason, which enters the revision log. Refuses a move that would leave an earlier wave waiting on a later one, and a `done` node | `0` · `1` refused |
|
|
62
64
|
| `certify` | the round, and on a failure every `breaks` finding with its fix and its check | `0` every owed tier passed — three, or four with `visual` on a flagship or product node · `1` a tier failed, a required one is missing, or a report is malformed |
|
|
63
65
|
| `close` | the goal, the new frontier count, and what was not verified | `0` · `1` refused **or the verdict stops the run** |
|
|
64
66
|
| `producer` | what produced this proof — actor, model, runtime, skill, config, commit, trace | `0` |
|
|
@@ -88,7 +88,7 @@ TERMINAL = {"done", "parked"}
|
|
|
88
88
|
NO_GRAPH = {"producer", "doctrine"}
|
|
89
89
|
# One place, and the schema enumerates the same three. Two homes for this set is
|
|
90
90
|
# what let `close` write a verb the format forbade.
|
|
91
|
-
REVISION_VERBS = {"add", "park", "close", "invalidate", "waive"}
|
|
91
|
+
REVISION_VERBS = {"add", "park", "close", "invalidate", "waive", "wave"}
|
|
92
92
|
# Parking a node PROMOTES its dependents: `parked` is terminal, so anything
|
|
93
93
|
# blocked on it becomes runnable even though the payload it waited on never
|
|
94
94
|
# arrived. That is deliberate — `can_continue_around` in the verdict is the
|
|
@@ -276,6 +276,13 @@ def violations(graph):
|
|
|
276
276
|
f"{', '.join(SURFACE_CLASSES)}. A class nobody recognises requires no "
|
|
277
277
|
"visual reading, so a typo here would drop the tier that reads the pixels")
|
|
278
278
|
|
|
279
|
+
# Wave mode (`references/waves.md`). `True` is an int in Python, and a wave of
|
|
280
|
+
# `true` is a typo the ordering check below would read as wave 1.
|
|
281
|
+
if "wave" in n and not _is_wave(n["wave"]):
|
|
282
|
+
out.append(f"{nid}: wave is {n['wave']!r} — it must be an integer from 1. A wave "
|
|
283
|
+
"nobody can order is a node no `next --wave` ever prints, and the "
|
|
284
|
+
"order check below cannot see it")
|
|
285
|
+
|
|
279
286
|
if n.get("status") == "done":
|
|
280
287
|
ev = n.get("evidence")
|
|
281
288
|
if not isinstance(ev, list) or not [e for e in ev
|
|
@@ -322,6 +329,17 @@ def violations(graph):
|
|
|
322
329
|
"and a dependency handing over nothing is the one this check "
|
|
323
330
|
"exists to refuse")
|
|
324
331
|
|
|
332
|
+
# An earlier wave may not wait on a later one: a wave holds only what closes in one
|
|
333
|
+
# pass, and a node whose producer is scheduled after it cannot close in its own.
|
|
334
|
+
wave_of = {n.get("id"): n.get("wave") for n in nodes if _is_wave(n.get("wave"))}
|
|
335
|
+
for e in graph.get("edges") or []:
|
|
336
|
+
src, dst = e.get("from"), e.get("to")
|
|
337
|
+
if src in wave_of and dst in wave_of and wave_of[src] > wave_of[dst]:
|
|
338
|
+
out.append(f"edge {src}→{dst}: {src} is in wave {wave_of[src]} and {dst} in "
|
|
339
|
+
f"wave {wave_of[dst]} — an earlier wave would wait on a later one, "
|
|
340
|
+
"so it cannot close in its own pass (references/waves.md). Move "
|
|
341
|
+
f"{dst} to wave {wave_of[src]} or later, or {src} earlier")
|
|
342
|
+
|
|
325
343
|
for i, r in enumerate(graph.get("revisions") or []):
|
|
326
344
|
if not isinstance(r, dict):
|
|
327
345
|
out.append(f"revisions[{i}] is not an object")
|
|
@@ -603,6 +621,11 @@ TIERS = ("unit", "seam", "product")
|
|
|
603
621
|
VISUAL_TIER = "visual"
|
|
604
622
|
ALL_TIERS = TIERS + (VISUAL_TIER,)
|
|
605
623
|
SURFACE_CLASSES = ("flagship", "product", "internal", "ad")
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def _is_wave(w):
|
|
627
|
+
"""A wave is an integer from 1 — and never a bool, which Python counts as an int."""
|
|
628
|
+
return isinstance(w, int) and not isinstance(w, bool) and w >= 1
|
|
606
629
|
VISUAL_REQUIRED = ("flagship", "product")
|
|
607
630
|
TIER_KEYS = ("node", "tier", "verdict", "scope", "confirms", "findings",
|
|
608
631
|
"evidence", "not_examined")
|
|
@@ -746,13 +769,24 @@ def cmd_next(graph, args):
|
|
|
746
769
|
if violations(graph):
|
|
747
770
|
die("graph does not validate — run `validate` first", 1)
|
|
748
771
|
nodes = graph.get("nodes") or []
|
|
772
|
+
wave = getattr(args, "wave", None)
|
|
773
|
+
if wave is not None:
|
|
774
|
+
# One wave's frontier, and its own endings: `3` is *this wave is done*, which is
|
|
775
|
+
# the close pass's cue, not the release's (references/waves.md).
|
|
776
|
+
if not _is_wave(wave):
|
|
777
|
+
die("--wave %r is not a wave — waves are integers from 1" % wave, 2)
|
|
778
|
+
nodes = [n for n in nodes if n.get("wave") == wave]
|
|
779
|
+
if not nodes:
|
|
780
|
+
die("no node is in wave %d — nothing to walk, and an empty wave is a wave plan "
|
|
781
|
+
"and a graph that disagree" % wave)
|
|
749
782
|
if nodes and all(n.get("status") in TERMINAL for n in nodes):
|
|
750
783
|
return 3
|
|
751
|
-
|
|
784
|
+
in_scope = {n.get("id") for n in nodes}
|
|
785
|
+
ready = [n for n in frontier(graph) if n.get("id") in in_scope]
|
|
752
786
|
# A consumer whose producer is PARKED is not runnable and never will be by
|
|
753
787
|
# itself (FIX-PF-04.01) — say so BEFORE the empty-frontier exit, or the one
|
|
754
788
|
# moment the operator most needs the reason is the one moment it is silent.
|
|
755
|
-
_by_id = {n.get("id"): n for n in nodes}
|
|
789
|
+
_by_id = {n.get("id"): n for n in graph.get("nodes") or []}
|
|
756
790
|
for n in nodes:
|
|
757
791
|
if n.get("status") in TERMINAL or n.get("status") == "running":
|
|
758
792
|
continue
|
|
@@ -1123,6 +1157,46 @@ def cmd_park(graph, args):
|
|
|
1123
1157
|
return 0
|
|
1124
1158
|
|
|
1125
1159
|
|
|
1160
|
+
def cmd_wave(graph, args):
|
|
1161
|
+
"""Move one node to another wave, carrying the reason — wave mode's re-cut.
|
|
1162
|
+
|
|
1163
|
+
The re-cut after every wave is a plan revision, and a revision nobody recorded is
|
|
1164
|
+
the dynamic graph `references/planning.md` refuses. So the move is a verb: it
|
|
1165
|
+
writes the `revisions` entry, and it refuses a move that would leave an earlier
|
|
1166
|
+
wave waiting on a later one (`references/waves.md`).
|
|
1167
|
+
"""
|
|
1168
|
+
guard(graph, args.graph)
|
|
1169
|
+
why = (args.why or "").strip()
|
|
1170
|
+
if not why:
|
|
1171
|
+
die("`wave` needs a --why with something in it: a re-cut nobody gave a reason for "
|
|
1172
|
+
"is the quiet edit the revision log exists to refuse — nothing was written")
|
|
1173
|
+
if any(c in why for c in "\n\r"):
|
|
1174
|
+
die("--why contains a line break — nothing was written")
|
|
1175
|
+
if not _is_wave(args.to):
|
|
1176
|
+
die("--to %r is not a wave — waves are integers from 1. Nothing was written"
|
|
1177
|
+
% args.to)
|
|
1178
|
+
node = next((n for n in graph.get("nodes") or [] if n.get("id") == args.node), None)
|
|
1179
|
+
if node is None:
|
|
1180
|
+
die("no node %s in this graph — nothing was written" % args.node)
|
|
1181
|
+
if node.get("status") == "done":
|
|
1182
|
+
die("%s is done, in wave %s — moving it would rewrite which pass closed it. "
|
|
1183
|
+
"Nothing was written" % (args.node, node.get("wave")))
|
|
1184
|
+
if node.get("wave") == args.to:
|
|
1185
|
+
die("%s is already in wave %d — nothing was written" % (args.node, args.to))
|
|
1186
|
+
|
|
1187
|
+
before = node.get("wave")
|
|
1188
|
+
node["wave"] = args.to
|
|
1189
|
+
revise(graph, "wave", args.node, why)
|
|
1190
|
+
bad = violations(graph)
|
|
1191
|
+
if bad:
|
|
1192
|
+
die("moving %s to wave %d would break the graph — nothing was written:\n %s"
|
|
1193
|
+
% (args.node, args.to, "\n ".join(bad)))
|
|
1194
|
+
save(args.graph, graph)
|
|
1195
|
+
print("%s: wave %s → %d: %s" % (args.node, before if before is not None else "none",
|
|
1196
|
+
args.to, why))
|
|
1197
|
+
return 0
|
|
1198
|
+
|
|
1199
|
+
|
|
1126
1200
|
def next_id(known):
|
|
1127
1201
|
"""The next id, from the MAXIMUM rather than from the count.
|
|
1128
1202
|
|
|
@@ -1359,7 +1433,7 @@ def cmd_producer(graph, args):
|
|
|
1359
1433
|
def cmd_doctrine(graph, args):
|
|
1360
1434
|
"""Which doctrine this run actually read — B-061.
|
|
1361
1435
|
|
|
1362
|
-
The bundle is
|
|
1436
|
+
The bundle is 40 reference files. A run reads some subset and nothing recorded which,
|
|
1363
1437
|
so **a skipped file and a read one were indistinguishable** — the class every guard in
|
|
1364
1438
|
this repository exists to catch, left standing over the doctrine itself.
|
|
1365
1439
|
|
|
@@ -1825,6 +1899,7 @@ VERBS = {
|
|
|
1825
1899
|
"coverage": (cmd_coverage, "every requirement and the nodes serving it; exits 1 on a gap"),
|
|
1826
1900
|
"add": (cmd_add, "add a node mid-run"),
|
|
1827
1901
|
"park": (cmd_park, "park a node, carrying the reason"),
|
|
1902
|
+
"wave": (cmd_wave, "move a node to another wave, carrying the reason"),
|
|
1828
1903
|
"certify": (cmd_certify, "require three independent tier reports (four on a "
|
|
1829
1904
|
"flagship or product surface), then emit the verdict "
|
|
1830
1905
|
"`close` consumes"),
|
|
@@ -1905,6 +1980,15 @@ def main(argv=None):
|
|
|
1905
1980
|
made["waive"].add_argument("--by", required=True,
|
|
1906
1981
|
help="the identity that authorized the exception")
|
|
1907
1982
|
|
|
1983
|
+
made["next"].add_argument("--wave", type=int, default=None,
|
|
1984
|
+
help="print only this wave's frontier; exit 3 when the wave "
|
|
1985
|
+
"is done (references/waves.md)")
|
|
1986
|
+
p_wave = made["wave"]
|
|
1987
|
+
p_wave.add_argument("node")
|
|
1988
|
+
p_wave.add_argument("--to", type=int, required=True, help="the wave to move it to")
|
|
1989
|
+
p_wave.add_argument("--why", required=True,
|
|
1990
|
+
help="why the re-cut moved it — it enters the revision log")
|
|
1991
|
+
|
|
1908
1992
|
p_park = made["park"]
|
|
1909
1993
|
p_park.add_argument("node")
|
|
1910
1994
|
# `required=True` makes the MISSING flag a usage error (exit 2). The empty and
|
|
@@ -1916,7 +2000,7 @@ def main(argv=None):
|
|
|
1916
2000
|
verbs = {k: v[0] for k, v in VERBS.items()}
|
|
1917
2001
|
if args.verb in NO_GRAPH:
|
|
1918
2002
|
return verbs[args.verb](None, args)
|
|
1919
|
-
if args.verb in ("add", "park", "close", "certify", "invalidate", "waive"):
|
|
2003
|
+
if args.verb in ("add", "park", "wave", "close", "certify", "invalidate", "waive"):
|
|
1920
2004
|
# The READ happens inside the lock too. Loading first and locking second is the
|
|
1921
2005
|
# same lost update with an extra step: the stale copy is already in memory.
|
|
1922
2006
|
with held(args.graph):
|
|
@@ -20,7 +20,7 @@ Run: `<topic>` · started `<YYYY-MM-DD>` · module map: `<path or "none">`
|
|
|
20
20
|
|
|
21
21
|
## `read:` — which doctrine this run actually opened
|
|
22
22
|
|
|
23
|
-
The bundle is
|
|
23
|
+
The bundle is 40 reference files and nothing recorded which of them a run read, so **a
|
|
24
24
|
skipped file and a read one were indistinguishable** — the class every guard in this
|
|
25
25
|
pipeline exists to catch, left standing over the doctrine itself.
|
|
26
26
|
|
|
@@ -48,7 +48,7 @@ same claim one level down.
|
|
|
48
48
|
|---|---|---|
|
|
49
49
|
| `unmeasured — no run ledger` | there is no ledger | nothing to read from |
|
|
50
50
|
| `unmeasured — the ledger carries no read: lines` | the hook is absent, **or** the run opened no doctrine | two opposite facts, and the ledger cannot separate them, so neither is claimed |
|
|
51
|
-
| `N of
|
|
51
|
+
| `N of 40 reference files read — unattested`, then each unread one | the hook is installed and fired | the count alone says there is a gap, not where — and `unattested` says the ledger cannot name who wrote the lines |
|
|
52
52
|
|
|
53
53
|
**It is a disclosure: no floor, no direction, never a target.** A run that needs four files
|
|
54
54
|
and reads four is not worse than one that reads thirty — and the moment the number becomes
|