task-pipeline-skill 1.95.0 → 1.96.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +2 -1
  3. package/SKILL-CARD.md +1 -1
  4. package/evals/RESULTS.md +55 -8
  5. package/evals/cases/task-pipeline.json +139 -0
  6. package/evals/fixtures/lessons/expect-match.sh +30 -0
  7. package/evals/fixtures/lessons/index.jsonl +2 -0
  8. package/evals/fixtures/waves-unclaimed/expect-refusal.sh +26 -0
  9. package/evals/fixtures/waves-unclaimed/inventory.json +33 -0
  10. package/evals/fixtures/waves-unclaimed/modules.json +4 -0
  11. package/evals/fixtures/waves-unclaimed/plan.json +5 -0
  12. package/evals/task-pipeline.evals.json +104 -0
  13. package/package.json +1 -1
  14. package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
  15. package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +5 -2
  16. package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +4 -0
  17. package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +19 -0
  18. package/plugins/task-pipeline/skills/task-pipeline/references/audit.md +5 -2
  19. package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +7 -1
  20. package/plugins/task-pipeline/skills/task-pipeline/references/doctrine-map.md +1 -0
  21. package/plugins/task-pipeline/skills/task-pipeline/references/finish-line.md +145 -0
  22. package/plugins/task-pipeline/skills/task-pipeline/references/iteration.md +9 -5
  23. package/plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md +5 -0
  24. package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
  25. package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +8 -0
  26. package/plugins/task-pipeline/skills/task-pipeline/references/review.md +7 -2
  27. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +6 -2
  28. package/plugins/task-pipeline/skills/task-pipeline/references/waves.md +9 -6
  29. package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +1 -1
  30. package/plugins/task-pipeline/skills/task-pipeline/scripts/waves.py +219 -3
  31. package/plugins/task-pipeline/skills/task-pipeline/templates/brief.md +14 -0
  32. package/plugins/task-pipeline/skills/task-pipeline/templates/inventory.schema.json +15 -1
  33. package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +26 -2
@@ -18,6 +18,7 @@
18
18
  - Documentation (the phase-1b inventory — the four questions)
19
19
  - Scope
20
20
  - Requirements (the REQ spine — every later stage traces to these IDs)
21
+ - Finish line (when this iteration stops)
21
22
  - Users & context
22
23
  - Decisions locked (the grill's output)
23
24
  - Autonomy (the sweep — stages 1→10 read this instead of asking)
@@ -101,6 +102,19 @@ not continuously): `open` → `planned` → `built` → `verified` \| `partial`
101
102
  > task in smaller terms is the failure this table exists to prevent: every gate
102
103
  > after it goes green on the shrunken task and nothing reports the loss.
103
104
 
105
+ ## Finish line (when this iteration stops)
106
+
107
+ The contract the iteration stops against (the skill's `references/finish-line.md`). Written
108
+ here, confirmed with the brief, and revised only as a recorded revision — a finding is not one.
109
+
110
+ - **Objective:** <one sentence: what is true when this iteration is done>
111
+ - **Exit checks:** <REQ id → command, run on the artefact the operator uses: released /
112
+ installed / deployed — one line each>
113
+ - **Scope:** <the modules, paths and REQ ids this iteration may change, as a list>
114
+ - **Budget:** `maxRounds` 3 · `maxReleases` 2 (the defaults; `pipeline.json` →
115
+ `run.finish` overrides them)
116
+ - **Base:** <the commit this iteration started from>
117
+
104
118
  ## Users & context
105
119
 
106
120
  - **Who / for what:** … (personas, the job being done)
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://github.com/ssheleg/task-pipeline/blob/main/plugins/task-pipeline/skills/task-pipeline/templates/inventory.schema.json",
4
4
  "title": "inventory/1 — <artifacts>/specs/YYYY-MM-DD-<topic>-inventory.json",
5
- "description": "Wave 0's single machine-readable inventory: one item per open item of the board, every survey and every harvested source (references/waves.md, Wave 0). scripts/waves.py merges audit outputs into it (`merge`), writes `module`, `node`, `wave` and `release` from two short tables (`assign`), counts it (`count`) and refuses an item that lands nowhere (`validate`). The document is an object carrying `schema` and `items`; a bare array of items is read as the items of one. Fields a project needs beyond these go under an `x-` prefix.",
5
+ "description": "Wave 0's single machine-readable inventory: one item per open item of the board, every survey and every harvested source (references/waves.md, Wave 0). scripts/waves.py merges audit outputs into it (`merge`), writes `module`, `node`, `wave` and `release` from two short tables (`assign`), counts it (`count`), refuses an item that lands nowhere (`validate`) and computes the finish-line verdict from the triaged items (`finish`, references/finish-line.md). The document is an object carrying `schema` and `items`; a bare array of items is read as the items of one. Fields a project needs beyond these go under an `x-` prefix.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": ["schema", "items"],
@@ -17,6 +17,14 @@
17
17
  "additionalProperties": false,
18
18
  "patternProperties": {"^x-[a-z0-9-]+$": {}},
19
19
  "required": ["id", "title", "area", "kind", "severity", "evidence", "proposed_fix"],
20
+ "allOf": [
21
+ {"if": {"required": ["triage"], "properties": {"triage": {"const": "blocking"}}},
22
+ "then": {"required": ["test", "round"]}},
23
+ {"if": {"required": ["test"]},
24
+ "then": {"required": ["triage"], "properties": {"triage": {"const": "blocking"}}}},
25
+ {"if": {"required": ["test", "base_repro"], "properties": {"test": {"const": "B3"}}},
26
+ "then": {"properties": {"base_repro": {"not": {"const": "yes"}}}}}
27
+ ],
20
28
  "properties": {
21
29
  "id": {
22
30
  "description": "The item's id in its owning register. An item without one gets one there, in the same change. Unique across the inventory.",
@@ -60,6 +68,12 @@
60
68
  "blocked_by": {"type": "array", "items": {"type": "string"}},
61
69
  "closable_in_one_pass": {"type": "boolean"},
62
70
  "needs_operator": {"type": "boolean"},
71
+ "triage": {"description": "The finish-line class, set when the finding is found (references/finish-line.md → Triage at discovery). Only blocking findings argue for another round; deferred and noise never affect `waves.py finish`.", "enum": ["blocking", "deferred", "noise"]},
72
+ "test": {"description": "The blocking test that matched, first match wins: B1 an exit check is red; B2 a hotfix-lane entry; B3 it passes at Base; B4 a verified REQ or a published claim is false on the artefact. Required on a blocking item, refused on any other.", "enum": ["B1", "B2", "B3", "B4"]},
73
+ "round": {"description": "The finish-line round that found it, from 1. Required on a blocking item.", "type": "integer", "minimum": 1},
74
+ "base_repro": {"description": "Does the failure reproduce at the iteration's Base? `no` is a finding this iteration caused (self-inflicted).", "enum": ["yes", "no", "n/a"]},
75
+ "blast": {"description": "Who it hurts, 1–3 (references/prioritisation.md, backlog `sev × blast`): a priority input of a deferred row. `waves.py finish` lists a deferred item at blast 3 as a known risk.", "type": "integer", "minimum": 1, "maximum": 3},
76
+ "rung": {"description": "The impact rung (references/prioritisation.md): a priority input of a deferred row. `waves.py finish` lists a deferred item at I4 or I5 as a known risk.", "enum": ["I1", "I2", "I3", "I4", "I5"]},
63
77
  "notes": {"type": "string"}
64
78
  }
65
79
  },
@@ -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 52 reference files and nothing recorded which of them a run read, so **a
23
+ The bundle is 53 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 52 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 |
51
+ | `N of 53 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
@@ -71,6 +71,9 @@ holds: <stage id> — <n> (<class: what, owner>; … or "none") — enumerated <
71
71
  gate: <stage id> — command "<cmd>" — exit <N> — <ISO-8601>
72
72
  event: <compact|session-end|subagent|memory> — <detail> — <ISO-8601>
73
73
  review: <stage id> — surface <name> — rounds <N> — <returned|approved|unresolved> — <ISO-8601>
74
+ finding: <id> — blocking:B<n> | deferred:B-NNN | noise — round <N> — base-repro yes|no|n/a
75
+ round: <N> — <reading> — new blocking <k>
76
+ finish: done|budget|blocked — rounds <n>/<max> — releases <n>/<max>
74
77
  read: references/<file>.md # hook-appended, deduped, UNATTESTED (no writer field)
75
78
  ```
76
79
 
@@ -118,6 +121,20 @@ read: references/<file>.md # hook-appended, deduped, UNATTESTED (no
118
121
  into the checkpoint of the stage it names, and stage 10 copies the last one per surface
119
122
  into the acceptance file. **It is a measurement, never a target**: a number of passes to
120
123
  beat is an instruction to stop showing the sheet.
124
+ - **`finding:`** — one line per finding, written when it is found and before anyone fixes
125
+ it (`references/finish-line.md` → *Triage at discovery*, inside the skill). The class is
126
+ one of `blocking`, `deferred`, `noise`; a blocking line names its test, one of `B1`, `B2`,
127
+ `B3`, `B4`; a deferred line names its board row. `base-repro` says whether the failure
128
+ reproduces at the iteration's Base — `no` is a finding this iteration caused.
129
+ - **`round:`** — one line per finish-line round, **clean rounds included**: its number, the
130
+ independent reading it ran (an audit axis, an observer, the post-release readback) and how
131
+ many new blocking findings it produced. A clean round leaves no `finding:` line, so without
132
+ this line two clean rounds and no rounds look the same; `scripts/waves.py finish --ledger`
133
+ counts the rounds from it and refuses when its counts disagree with the inventory.
134
+ - **`finish:`** — one line when the iteration stops, beside stage 10's verdict: `done`,
135
+ `budget` or `blocked`, with the rounds and releases spent against their budget. The
136
+ block under it is `references/finish-line.md` → *The finish block*;
137
+ `scripts/waves.py finish` computes the verdict from the inventory.
121
138
  - **`touch:`** — one line per file per pass, and the reason names **what forced the
122
139
  edit**: a finding id, a failed gate item, an operator instruction. *"Cleanup"*,
123
140
  *"polish"* and *"while I was there"* are not reasons; they are churn with better
@@ -141,11 +158,18 @@ event: compact — auto — 2026-08-10T11:58Z
141
158
  event: subagent — general-purpose — 2026-08-10T12:00Z
142
159
  gate: 6 — command "npm test" — exit 0 — 2026-08-10T12:02Z
143
160
  review: 6 — surface landing — rounds 1 — returned — 2026-08-10T12:02Z
161
+ finding: F-021 — blocking:B3 — round 1 — base-repro no
162
+ finding: F-022 — deferred:B-118 — round 1 — base-repro yes
163
+ round: 1 — exit checks + audit axis seams — new blocking 1
164
+ finding: F-023 — noise — round 2 — base-repro n/a
165
+ round: 2 — exit checks + observer — new blocking 0
166
+ round: 3 — exit checks + post-release readback — new blocking 0
144
167
  stage: 6 Tests — gate manual — verdict pass — 2026-08-10T12:03Z
145
168
  hand: 3 — task "add CSV export to the orders table" — done 2 — surfaced 1 — decisions 1 — amb 2 (OQ-0007, ledger row 4)
146
169
  scope 5f21ac3/node-24-linux/REQ-001,REQ-004 — unverified 1 (XLSX path: no fixture)
147
170
  holds: 5 — 2 (worktree: build-csv-export, this run; container: pg-test, this run) — enumerated 8/8 classes
148
171
  holds: 10 — none — enumerated 7/8 classes, unlooked: containers (no docker on this host)
172
+ finish: done — rounds 3/3 — releases 1/2
149
173
  ```
150
174
 
151
175
  The last two lines are a **trip**: the same file, two consecutive passes, the same