task-pipeline-skill 1.95.0 → 1.97.1

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 (40) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +13 -9
  3. package/SKILL-CARD.md +2 -2
  4. package/cursor/rules/task-pipeline.mdc +11 -7
  5. package/evals/RESULTS.md +55 -8
  6. package/evals/cases/task-pipeline.json +139 -0
  7. package/evals/fixtures/lessons/expect-match.sh +30 -0
  8. package/evals/fixtures/lessons/index.jsonl +2 -0
  9. package/evals/fixtures/waves-unclaimed/expect-refusal.sh +26 -0
  10. package/evals/fixtures/waves-unclaimed/inventory.json +33 -0
  11. package/evals/fixtures/waves-unclaimed/modules.json +4 -0
  12. package/evals/fixtures/waves-unclaimed/plan.json +5 -0
  13. package/evals/task-pipeline.evals.json +104 -0
  14. package/package.json +1 -1
  15. package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
  16. package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +5 -2
  17. package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +4 -0
  18. package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +19 -0
  19. package/plugins/task-pipeline/skills/task-pipeline/references/audit.md +5 -2
  20. package/plugins/task-pipeline/skills/task-pipeline/references/browser.md +48 -11
  21. package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +32 -24
  22. package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +7 -1
  23. package/plugins/task-pipeline/skills/task-pipeline/references/doctrine-map.md +1 -0
  24. package/plugins/task-pipeline/skills/task-pipeline/references/finish-line.md +145 -0
  25. package/plugins/task-pipeline/skills/task-pipeline/references/iteration.md +9 -5
  26. package/plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md +5 -0
  27. package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
  28. package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +8 -0
  29. package/plugins/task-pipeline/skills/task-pipeline/references/review.md +7 -2
  30. package/plugins/task-pipeline/skills/task-pipeline/references/stage-05-dev.md +2 -2
  31. package/plugins/task-pipeline/skills/task-pipeline/references/stage-08-post-deploy.md +3 -2
  32. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +9 -4
  33. package/plugins/task-pipeline/skills/task-pipeline/references/tdd.md +3 -2
  34. package/plugins/task-pipeline/skills/task-pipeline/references/waves.md +9 -6
  35. package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +1 -1
  36. package/plugins/task-pipeline/skills/task-pipeline/scripts/waves.py +219 -3
  37. package/plugins/task-pipeline/skills/task-pipeline/templates/brief.md +14 -0
  38. package/plugins/task-pipeline/skills/task-pipeline/templates/browser-claims.json +5 -5
  39. package/plugins/task-pipeline/skills/task-pipeline/templates/inventory.schema.json +15 -1
  40. package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +26 -2
package/CHANGELOG.md CHANGED
@@ -1,3 +1,73 @@
1
+ ## v1.97.1 — 2026-10-17
2
+
3
+ The same source as v1.97.0, plus its release declaration. The v1.97.0 tag failed its own release run
4
+ ([38976060549](https://github.com/ssheleg/task-pipeline/actions/runs/38976060549)): `retro.md` named no
5
+ release after its newest stamp, so the validator on the tag's tree refused and `publish` was skipped. Tags
6
+ are immutable here, so v1.97.0 stays as a failed tag and this patch carries the declaration (the v1.94.0 →
7
+ v1.94.1 precedent). No doctrine change.
8
+
9
+ ## v1.97.0 — 2026-10-16
10
+
11
+ Source candidate; not published.
12
+
13
+ Guards: 447 → **448**; property checks 15 → **15**.
14
+
15
+ The look now runs on WebKit first. Every browser example in the bundle opened the
16
+ tool's default engine, a Chromium-family browser, and `chrome-devtools` drives Chrome
17
+ only — so the look the pipeline taught was a Chromium look reported as *tested in a
18
+ browser*, and a layout that breaks only in Safari passed it. `references/browser.md`
19
+ opens with *Engine order — WebKit (Safari) first*: Safari through `safaridriver` for a
20
+ real browser, Playwright's `webkit` for automation (`playwright-cli open --browser
21
+ webkit`, the MCP's `--browser webkit`, read from each tool's `--help`), Safari Web
22
+ Inspector for debugging, and `open -a Safari` instead of the system default browser.
23
+ Chromium is a stated exception — Lighthouse, a CDP trace, a heap snapshot, a
24
+ Chrome-only API — named with its reason, on a temporary profile the run closes; a
25
+ surface checked only in Chromium is reported as exactly that. The recipe, the
26
+ sign-in example and the library example (`const { webkit } = require('playwright')`)
27
+ now open WebKit.
28
+
29
+ `references/companion-skills.md` makes `playwright` on WebKit the default look and
30
+ `chrome-devtools` the stated Chromium exception, in the matrix, the preflight lines
31
+ and the one rule for both channels; stages 5, 6 and 8, `tdd.md`, the README, the
32
+ Cursor rule, the skill card and the contact-sheet template say the same. `DEC-0007`
33
+ records the order: the engine is ranked, the tools' quality still is not, so
34
+ `DEC-0004` keeps its other two parts. The new guard holds the section above the model
35
+ and the recipe's `--browser webkit`; its negative self-test strips the flag and
36
+ requires the validator to refuse.
37
+
38
+ ## v1.96.0 — 2026-10-15
39
+
40
+ Source candidate; not published.
41
+
42
+ Guards: 439 → **447**; property checks 15 → **15**.
43
+
44
+ An iteration now stops at a finish line instead of when a search runs dry. The new
45
+ `references/finish-line.md` fixes a contract at intake — objective, exit checks run on
46
+ the released, installed or deployed artefact, scope, budget and base — in the brief's
47
+ `## Finish line`. Every finding is triaged when found: blocking only by one of four
48
+ tests (a red exit check, a hotfix-lane entry, a regression against base, a verified
49
+ REQ or published claim false on the artefact), deferred to the board with its
50
+ evidence, or noise as one ledger line. Severity words and effort never change the
51
+ class. An iteration is done after two consecutive rounds with no new blocking
52
+ finding; it stops at `run.finish.maxRounds` (default 3) or `maxReleases` (default 2)
53
+ with each open blocker handed back as a `needs-operator` row, and diverging rounds go
54
+ to the loop guard. Stopping is not idle: the next iteration starts from the board by
55
+ priority, never by re-opening the closed one.
56
+
57
+ `scripts/waves.py finish` computes the verdict from the inventory's new optional item
58
+ fields (`triage`, `test`, `round`, `base_repro`, plus `blast` and `rung` for known
59
+ risks) and prints the finish block; deferred and noise items never change it. The round
60
+ count is explicit — `--rounds` or the run ledger's new `round:` lines, which must agree
61
+ with the inventory — because a clean round leaves no item to count. Done needs the exit
62
+ checks supplied and green (`--checks`), stays within `maxRounds` (at least 2) unless the
63
+ operator's go is passed, and `next:` ranks every open non-blocking row by priority,
64
+ never by freshness. B2 also covers a defect that would be a hotfix-lane entry once
65
+ released; a defect in a node's own diff stays with review. Stage 0's
66
+ gate requires the finish line filled; stage 10's requires the `finish:` line printed.
67
+ Iteration, waves, loop-guard, audit, continuity, review and progress doctrine point to
68
+ the one rule, and seven validator guards hold its vocabulary, budgets, hotfix set,
69
+ brief fields, hand-back field and script enums equal across their surfaces.
70
+
1
71
  ## v1.95.0 — 2026-10-13
2
72
 
3
73
  Source candidate; not published.
package/README.md CHANGED
@@ -189,6 +189,7 @@ and the inline fallback satisfy the same [provider contract](plugins/task-pipeli
189
189
  | 10 Acceptance | [`acceptance.md`](plugins/task-pipeline/skills/task-pipeline/references/acceptance.md) — REQ coverage table, evidence rules, the closing question |
190
190
  | 10 + any audit | [`audit.md`](plugins/task-pipeline/skills/task-pipeline/references/audit.md) — the L0→L7 ladder and its seams, axis rotation, ratchets, proven checks |
191
191
  | any loop | [`loop-guard.md`](plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md) — churn detection, caps, the break protocol |
192
+ | when an iteration stops | [`finish-line.md`](plugins/task-pipeline/skills/task-pipeline/references/finish-line.md) — a contract fixed at intake (objective, exit checks on the artefact, scope, budget, base), every finding triaged when found — blocking, deferred or noise — and a stop at two rounds with no new blocking finding or at the budget, computed by `waves.py finish` |
192
193
  | every gate + stage 10 | [`residue.md`](plugins/task-pipeline/skills/task-pipeline/references/residue.md) — what the run leaves **running** and leaves **behind**: eight classes enumerated by class rather than by one tool, `holds: N` beside every verdict, and a teardown that ends what this run started and only reports what it did not |
193
194
  | run-wide reporting | [`progress.md`](plugins/task-pipeline/skills/task-pipeline/references/progress.md) — the header block at task start and the one-line iteration close: the stage rail computed from the project's own `pipeline.json`, every number borrowed from the gate that measured it |
194
195
  | run-wide pacing | [`continuity.md`](plugins/task-pipeline/skills/task-pipeline/references/continuity.md) — the loop mode (`run.loop`, default off, never collapses a manual gate) and the context budget (fires on a harness signal, never on an estimate) |
@@ -279,14 +280,17 @@ and neither can a **browser test suite**, which asserts what someone wrote down
279
280
  counted as coverage rather than as the look. Absent, the run says *"verified by reading
280
281
  the diff"* — a weaker claim, recorded as one.
281
282
 
282
- There are **two channels and either is enough**, ranked by nothing: `playwright`
283
- (`npm install -D @playwright/cli@latest`, or
284
- `claude mcp add playwright npx @playwright/mcp@latest`) needs no plugin and is already
285
- there on a project that runs Playwright, and its CLI half puts no tool schema in the
286
- context window; `chrome-devtools`
287
- (`/plugin install chrome-devtools-mcp@claude-plugins-official`) is the one that reaches
288
- past the page to Lighthouse and heap snapshots, and analyses a performance trace rather
289
- than only recording one. Neither is a gate,
283
+ There are **two channels and either is enough**, and the **engine** is ranked:
284
+ **WebKit (Safari) first, Chromium a stated exception**. `playwright`
285
+ (`npm install -D @playwright/cli@latest` then `playwright-cli open --browser webkit`, or
286
+ `claude mcp add playwright -- npx @playwright/mcp@latest --browser webkit`) is the default
287
+ look: it reaches WebKit, needs no plugin, is already there on a project that runs
288
+ Playwright, and its CLI half puts no tool schema in the context window; on macOS Safari
289
+ itself is driven through `safaridriver`. `chrome-devtools`
290
+ (`/plugin install chrome-devtools-mcp@claude-plugins-official`) drives Chrome only and is
291
+ reached for when the question needs Chrome — Lighthouse, heap snapshots, a performance
292
+ trace analysed rather than only recorded — said with the reason; a look taken only there
293
+ is reported as *checked in Chromium*. Neither is a gate,
290
294
  and a CLI, a library or a backend service is never offered either.
291
295
 
292
296
  **The wiki is [obsidian-wiki](https://github.com/ar9av/obsidian-wiki)** (Karpathy's
@@ -634,7 +638,7 @@ must say so.
634
638
  ### Held to Anthropic's own Skill authoring guidance
635
639
 
636
640
  Audited against the four Agent Skills pages. Most of it already held — `name`
637
- 13/64 chars, `description` 887/1024 chars, `SKILL.md` 272/500 lines, all 52 references
641
+ 13/64 chars, `description` 887/1024 chars, `SKILL.md` 275/500 lines, all 53 references
638
642
  reachable from `SKILL.md` through explicit reference routes, and the bundle far under the 30 MB ceiling. What
639
643
  did not, now does:
640
644
 
package/SKILL-CARD.md CHANGED
@@ -12,9 +12,9 @@ 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.95.0 |
15
+ | **Version** | 1.97.1 |
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
- | **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; user-facing stage 3 requires a valid scenario artifact; super-ux is preferred, with equivalent-provider and inline-fallback paths under the provider contract |
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. The engine is WebKit (Safari) first; `chrome-devtools` is Chrome-only, a stated exception. Every stage's doctrine ships in-repo; user-facing stage 3 requires a valid scenario artifact; super-ux is preferred, with equivalent-provider and inline-fallback paths under the provider contract |
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` |
19
19
 
20
20
  ## Risk-tier disclosure
@@ -272,13 +272,17 @@ not authorize an outward, irreversible action — stage 7 stops and asks.
272
272
  look. With a browser channel connected: load the surface, snapshot it, read the
273
273
  console and the network log, and quote what you read. Fix what the look finds
274
274
  before calling the stage green. Without one, say "verified by reading the diff" and
275
- treat that as the weaker claim it is. Two channels, either is enough, ranked by
276
- nothing: `npm install -D @playwright/cli@latest` (or
277
- `claude mcp add playwright npx @playwright/mcp@latest`) needs no plugin, and its CLI
278
- half puts no tool schema in the context window;
279
- `/plugin install chrome-devtools-mcp@claude-plugins-official` is the one that
280
- reaches Lighthouse and heap snapshots, and analyses a trace rather than only
281
- recording one. Never a gate. A CLI, a
275
+ treat that as the weaker claim it is. Two channels, either is enough, on WebKit
276
+ (Safari) first with Chromium a stated exception: `npm install -D @playwright/cli@latest`
277
+ then `playwright-cli open --browser webkit <url>` (or
278
+ `claude mcp add playwright -- npx @playwright/mcp@latest --browser webkit`) is the
279
+ default look, needs no plugin, and its CLI half puts no tool schema in the context
280
+ window; on macOS, Safari itself through `safaridriver`, and `open -a Safari <url>`
281
+ rather than the system default browser for a person.
282
+ `/plugin install chrome-devtools-mcp@claude-plugins-official` drives Chrome only and
283
+ is reached for when the question needs Chrome — Lighthouse, heap snapshots, a trace
284
+ analysed rather than only recorded — said with the reason; a look taken only there
285
+ is reported as "checked in Chromium", never "tested in a browser". Never a gate. A CLI, a
282
286
  library or a backend service is never offered either.
283
287
  7. **Lint + deploy** (manual) — lint clean AND suite green before deploy; deploy is
284
288
  outward → explicit user go, or the specific standing authorization recorded in
package/evals/RESULTS.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # Evaluation results — task-pipeline
2
2
 
3
3
  **Status: three suites authored (task-pipeline, evidence-docs, project-audit);
4
- one self-observed run and, since 2026-08-31, three blind trigger runs — one per
5
- model (haiku, sonnet, opus).** The instruction-following cases remain unexecuted
6
- and say so per run, because they need a full interactive session — a skip is not
7
- a pass, and a guessed verdict is worse than a vacant row.
4
+ one self-observed run, three blind trigger runs on 2026-08-31 — one per model
5
+ (haiku, sonnet, opus) — and three more on 2026-10-14 over the release-iteration
6
+ phrasings (TRIG-11, TRIG-12, NOTRIG-09), one per model.** The instruction-following
7
+ cases remain unexecuted and say so per run, because they need a full interactive
8
+ session — a skip is not a pass, and a guessed verdict is worse than a vacant row.
8
9
 
9
10
  **The numbers below are computed, not asserted.** `python3 evals/run.py` counts the
10
11
  suite and the dated run headings in this file. A value typed here that disagrees with
@@ -40,11 +41,11 @@ or it belongs in a check.
40
41
 
41
42
  | Metric | Value | Computed by | As of |
42
43
  |---|---|---|---|
43
- | Evals authored | 48 (three suites: task-pipeline 30, evidence-docs 9, project-audit 9) | `python3 evals/run.py` → `suite: N evals` | 2026-10-12 |
44
+ | Evals authored | 55 (three suites: task-pipeline 37, evidence-docs 9, project-audit 9) | `python3 evals/run.py` → `suite: N evals` | 2026-10-14 |
44
45
  | Categories covered | 5 of 5, per suite | the suite's own `category` fields | 2026-08-31 |
45
- | Dated runs recorded | **4** | `python3 evals/run.py` → `recorded runs: N` | 2026-08-31 |
46
- | …of those, **blind** | **3** | run headings not marked `self-observed` | 2026-08-31 |
47
- | Models exercised blind | **3 of 3** | distinct models across blind runs | 2026-08-31 |
46
+ | Dated runs recorded | **7** | `python3 evals/run.py` → `recorded runs: N` | 2026-10-14 |
47
+ | …of those, **blind** | **6** | run headings not marked `self-observed` | 2026-10-14 |
48
+ | Models exercised blind | **3 of 3** | distinct models across blind runs | 2026-10-14 |
48
49
 
49
50
  **The last three rows are the honest state of this skill's behavioural evidence**, and
50
51
  the split matters more than the total: a run the author watched, knowing the expected
@@ -84,6 +85,52 @@ model NAMED the skill; it does not prove a full session loaded and obeyed the
84
85
  doctrine — the INSTR cases need exactly that and are recorded as **not
85
86
  reproducible from this harness**, not guessed.
86
87
 
88
+ ## 2026-10-14 · haiku · blind trigger probes, subagent protocol
89
+
90
+ **3 of 3 scored cases passed** — the two release-iteration triggers and their
91
+ explanation control, added by release iteration 1 (node M-4, USR-015). Method as
92
+ 2026-08-31 below, with two differences, stated rather than implied: the offered
93
+ list was the family's **38** skills (the family grew from 28), built once from the
94
+ members' `SKILL.md` frontmatters on 2026-10-14 (sha256 `12115a8b24f1…`); and each
95
+ subagent reported the model id from its own system context, recorded here as the
96
+ model that answered: `claude-haiku-5-5`. The other new cases of that node
97
+ (INSTR-09…13, TP-OUT-006/007) were not run: the INSTR cases need a full interactive
98
+ session, and the two outcome cases need an executor driving an arm in a sandbox —
99
+ their fixtures' command checks are proven to pass and to fail by
100
+ `test/audit_regressions/usr_015_evals.py`, which is not a run of the case.
101
+
102
+ | id | verdict | what happened |
103
+ |---|---|---|
104
+ | TRIG-11 | pass | named `task-pipeline` — cited the description's 'waves'/'волнами' |
105
+ | TRIG-12 | pass | named `task-pipeline` — cited 'итерация релиза' in the description |
106
+ | NOTRIG-09 | pass | answered `none` — the description excludes explaining |
107
+
108
+ ## 2026-10-14 · sonnet · blind trigger probes, subagent protocol
109
+
110
+ **3 of 3 scored cases passed.** Same protocol and list as the haiku run above;
111
+ the answering model reported `claude-sonnet-5-5`.
112
+
113
+ | id | verdict | what happened |
114
+ |---|---|---|
115
+ | TRIG-11 | pass | named `task-pipeline` — matched «закрой их волнами» to the waves mode |
116
+ | TRIG-12 | pass | named `task-pipeline` — matched the 'release iteration' trigger |
117
+ | NOTRIG-09 | pass | answered `none` — a plain explanation question |
118
+
119
+ ## 2026-10-14 · opus · blind trigger probes, subagent protocol
120
+
121
+ **3 of 3 scored cases passed.** Same protocol and list as the haiku run above;
122
+ the answering model reported `claude-opus-5-5[1m]`.
123
+
124
+ | id | verdict | what happened |
125
+ |---|---|---|
126
+ | TRIG-11 | pass | named `task-pipeline` — the board closed «волнами» |
127
+ | TRIG-12 | pass | named `task-pipeline` — the board-closing release loop |
128
+ | NOTRIG-09 | pass | answered `none` — named the trigger phrase and the explaining exclusion |
129
+
130
+ INSTR-09…13 and TP-OUT-006/007: **not run** in any of the three 2026-10-14 runs,
131
+ for the reasons given under the haiku run. A trigger `pass` still means only that
132
+ the model NAMED the skill.
133
+
87
134
  ## 2026-08-31 · haiku · blind trigger probes, subagent protocol
88
135
 
89
136
  **39 of 42 scored cases passed** across the three suites' trigger-shaped
@@ -186,6 +186,145 @@
186
186
  }
187
187
  ]
188
188
  }
189
+ },
190
+ {
191
+ "schema_version": "outcome-case/1",
192
+ "id": "TP-OUT-006-waves-validate-refuses-unclaimed-item",
193
+ "skill": "task-pipeline",
194
+ "prompt": {
195
+ "text": "Wave 0 of `/task-pipeline waves`: the inventory, module table and wave plan are the frozen files under evals/fixtures/waves-unclaimed/. Check that every item lands on a node with the skill's own inventory validator, without editing the fixture, and write waves-validate.md with the command you ran, its exit code and what it reported (USR-015).",
196
+ "files": [
197
+ {
198
+ "path": "evals/fixtures/waves-unclaimed/inventory.json",
199
+ "sha256": "8ffb7e0f291c8cfb5b77910424b3dac55459e8f6b58f7e63343fcef258bb2102"
200
+ },
201
+ {
202
+ "path": "evals/fixtures/waves-unclaimed/modules.json",
203
+ "sha256": "9d13ac48c1c49d0bd315ed5643b68de993d1f33ee125e1e23718f6c60fe296d5"
204
+ },
205
+ {
206
+ "path": "evals/fixtures/waves-unclaimed/plan.json",
207
+ "sha256": "cc42a367ac8a966214beb21d8459c04e3704c461f220dd3a34d7f5fca315dbc8"
208
+ },
209
+ {
210
+ "path": "evals/fixtures/waves-unclaimed/expect-refusal.sh",
211
+ "sha256": "857a48783bdb89ff82c4d16d5adb93dc2159e3d89cb5b8dea069fa0439d60517"
212
+ }
213
+ ]
214
+ },
215
+ "environment": {
216
+ "model": "inherit",
217
+ "host": "any",
218
+ "case_digest": "480a16d5aef3efa8ae30e872e15948d2fa4906fd50211803295e5084d7403fe8"
219
+ },
220
+ "checks": {
221
+ "tool": [
222
+ {
223
+ "name": "python3 present",
224
+ "command": "python3 -c 'pass'"
225
+ },
226
+ {
227
+ "name": "bash present",
228
+ "command": "bash -c 'true'"
229
+ }
230
+ ],
231
+ "load_trace": {
232
+ "expect_loaded": [
233
+ "task-pipeline"
234
+ ],
235
+ "expect_not_loaded": []
236
+ },
237
+ "outcome": [
238
+ {
239
+ "name": "the validator still refuses the frozen fixture (inverted through the wrapper)",
240
+ "kind": "command-exit-0",
241
+ "target": "bash evals/fixtures/waves-unclaimed/expect-refusal.sh"
242
+ },
243
+ {
244
+ "name": "the record exists",
245
+ "kind": "artifact-exists",
246
+ "target": "waves-validate.md"
247
+ },
248
+ {
249
+ "name": "the record names the item that lands nowhere",
250
+ "kind": "artifact-contains",
251
+ "target": "waves-validate.md",
252
+ "expect": "B-103"
253
+ },
254
+ {
255
+ "name": "the record carries the refusal's exit code",
256
+ "kind": "artifact-contains",
257
+ "target": "waves-validate.md",
258
+ "expect": "exit 1"
259
+ }
260
+ ]
261
+ }
262
+ },
263
+ {
264
+ "schema_version": "outcome-case/1",
265
+ "id": "TP-OUT-007-lessons-query-fires-on-fixture",
266
+ "skill": "task-pipeline",
267
+ "prompt": {
268
+ "text": "Stage 0 of a run for 'add a retry policy to the webhook handler'. The machine's local lesson index is the frozen file evals/fixtures/lessons/index.jsonl and the session lists no shared lesson store. Read the lessons source the way the skill's harvest does, and write ledger.md with the source-ledger lines for both lesson layers and the citation of every lesson that fires (USR-015).",
269
+ "files": [
270
+ {
271
+ "path": "evals/fixtures/lessons/index.jsonl",
272
+ "sha256": "6b73a4345e8f361a565e15a93a1fa204a141f791b82946a08260d475b2aef28d"
273
+ },
274
+ {
275
+ "path": "evals/fixtures/lessons/expect-match.sh",
276
+ "sha256": "bed8b0b6363128108ff6a6985240d4b1fdeda01448ddc32ef32caa43c4e2a814"
277
+ }
278
+ ]
279
+ },
280
+ "environment": {
281
+ "model": "inherit",
282
+ "host": "any",
283
+ "case_digest": "eef134f07ba724082236b6a5977009dd744076ca7391fd562fe562bdd171cd88"
284
+ },
285
+ "checks": {
286
+ "tool": [
287
+ {
288
+ "name": "python3 present",
289
+ "command": "python3 -c 'pass'"
290
+ },
291
+ {
292
+ "name": "bash present",
293
+ "command": "bash -c 'true'"
294
+ }
295
+ ],
296
+ "load_trace": {
297
+ "expect_loaded": [
298
+ "task-pipeline"
299
+ ],
300
+ "expect_not_loaded": []
301
+ },
302
+ "outcome": [
303
+ {
304
+ "name": "the query over a copy of the fixture fires the webhook lesson only",
305
+ "kind": "command-exit-0",
306
+ "target": "bash evals/fixtures/lessons/expect-match.sh"
307
+ },
308
+ {
309
+ "name": "the ledger cites the lesson that fires",
310
+ "kind": "artifact-contains",
311
+ "target": "ledger.md",
312
+ "expect": "[lesson L-70cf4d4825df]"
313
+ },
314
+ {
315
+ "name": "the ledger states the local layer's count",
316
+ "kind": "artifact-contains",
317
+ "target": "ledger.md",
318
+ "expect": "local lessons: 1 match"
319
+ },
320
+ {
321
+ "name": "the ledger states the shared layer is absent",
322
+ "kind": "artifact-contains",
323
+ "target": "ledger.md",
324
+ "expect": "shared lessons: absent"
325
+ }
326
+ ]
327
+ }
189
328
  }
190
329
  ]
191
330
  }
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # TP-OUT-007's command check: `lessons.py query` over this fixture index must
3
+ # fire the webhook lesson (L-70cf4d4825df) on the task's nouns and leave the
4
+ # unrelated cache lesson (L-8487303c010c) out. The index is queried from a
5
+ # temporary copy, because the script takes a lock file beside the index and a
6
+ # check must not write into the tree it reads.
7
+ set -u
8
+ here="$(cd "$(dirname "$0")" && pwd)"
9
+ scripts="${TASK_PIPELINE_SCRIPTS:-$here/../../../plugins/task-pipeline/skills/task-pipeline/scripts}"
10
+ if [ ! -f "$scripts/lessons.py" ]; then
11
+ echo "expect-match: lessons.py not found under $scripts (set TASK_PIPELINE_SCRIPTS)" >&2
12
+ exit 2
13
+ fi
14
+ tmp="$(mktemp -d)"
15
+ cp "$here/index.jsonl" "$tmp/index.jsonl"
16
+ out="$(python3 "$scripts/lessons.py" query webhook retry handler --index "$tmp/index.jsonl")"
17
+ rc=$?
18
+ rm -f "$tmp/index.jsonl" "$tmp/.lock"
19
+ rmdir "$tmp"
20
+ if [ "$rc" -ne 0 ]; then
21
+ echo "expect-match: lessons.py query exited $rc" >&2
22
+ exit 1
23
+ fi
24
+ case "$out" in
25
+ *"[lesson L-8487303c010c]"*) echo "expect-match: the unrelated lesson fired" >&2; exit 1 ;;
26
+ esac
27
+ case "$out" in
28
+ *"[lesson L-70cf4d4825df]"*) exit 0 ;;
29
+ *) echo "expect-match: the webhook lesson did not fire: $out" >&2; exit 1 ;;
30
+ esac
@@ -0,0 +1,2 @@
1
+ {"check": "a test that renames and reads back through the cache", "classKey": "cache-invalidation-on-rename", "grade": 2, "id": "L-8487303c010c", "nouns": ["cache", "invalidation", "rename"], "origins": [{"commit": "2222222222222222222222222222222222222222", "lesson": {"check": "a test that renames and reads back through the cache", "classKey": "cache-invalidation-on-rename", "grade": 2, "nouns": ["cache", "rename", "invalidation"], "stages": {"owned": 4, "surfaced": 6}, "title": "a renamed record kept serving its old cached name", "trigger": "a rename that does not touch the cache key"}, "project": "p-0a1b2c3d4e5f", "retro": "docs/evidence/retro.md#cache-rename"}], "projects": 1, "schema": "lesson-index/1", "seen": 1, "stages": {"owned": 4, "surfaced": 6}, "status": "local", "title": "a renamed record kept serving its old cached name", "trigger": "a rename that does not touch the cache key"}
2
+ {"check": "a test that delivers the same event twice and asserts one effect", "classKey": "webhook-retry-without-idempotency-key", "grade": 2, "id": "L-70cf4d4825df", "nouns": ["delivery", "idempotency", "retry", "webhook"], "origins": [{"commit": "1111111111111111111111111111111111111111", "lesson": {"check": "a test that delivers the same event twice and asserts one effect", "classKey": "webhook-retry-without-idempotency-key", "grade": 2, "nouns": ["webhook", "retry", "idempotency", "delivery"], "stages": {"owned": 4, "surfaced": 6}, "title": "a retried delivery applied twice because the handler kept no idempotency key", "trigger": "a handler that retries or receives redelivered events"}, "project": "p-0a1b2c3d4e5f", "retro": "docs/evidence/retro.md#webhook-retry"}], "projects": 1, "schema": "lesson-index/1", "seen": 1, "stages": {"owned": 4, "surfaced": 6}, "status": "local", "title": "a retried delivery applied twice because the handler kept no idempotency key", "trigger": "a handler that retries or receives redelivered events"}
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+ # TP-OUT-006's command check: `waves.py validate` must REFUSE this fixture.
3
+ # The inventory holds three open items; the plan's only node closes two, so
4
+ # B-103 lands nowhere. Exit 0 means the refusal happened and named B-103;
5
+ # any other result — including the validator accepting the fixture, or the
6
+ # script not being found — is non-zero, so a missing tool can never pass.
7
+ set -u
8
+ here="$(cd "$(dirname "$0")" && pwd)"
9
+ scripts="${TASK_PIPELINE_SCRIPTS:-$here/../../../plugins/task-pipeline/skills/task-pipeline/scripts}"
10
+ if [ ! -f "$scripts/waves.py" ]; then
11
+ echo "expect-refusal: waves.py not found under $scripts (set TASK_PIPELINE_SCRIPTS)" >&2
12
+ exit 2
13
+ fi
14
+ err="$(python3 "$scripts/waves.py" validate \
15
+ --inventory "$here/inventory.json" \
16
+ --modules "$here/modules.json" \
17
+ --plan "$here/plan.json" 2>&1 >/dev/null)"
18
+ rc=$?
19
+ if [ "$rc" -ne 1 ]; then
20
+ echo "expect-refusal: waves.py validate exited $rc, expected 1" >&2
21
+ exit 1
22
+ fi
23
+ case "$err" in
24
+ *B-103*) exit 0 ;;
25
+ *) echo "expect-refusal: the refusal did not name B-103: $err" >&2; exit 1 ;;
26
+ esac
@@ -0,0 +1,33 @@
1
+ {
2
+ "schema": "inventory/1",
3
+ "topic": "eval-fixture-unclaimed-item",
4
+ "items": [
5
+ {
6
+ "id": "B-101",
7
+ "title": "export endpoint times out past ten thousand rows",
8
+ "area": "export",
9
+ "kind": "bug",
10
+ "severity": "high",
11
+ "evidence": "src/export.py:42 buffers the whole result set before the first byte",
12
+ "proposed_fix": "stream rows in pages"
13
+ },
14
+ {
15
+ "id": "B-102",
16
+ "title": "export CSV header omits the timezone",
17
+ "area": "export",
18
+ "kind": "gap",
19
+ "severity": "low",
20
+ "evidence": "src/export.py:17 writes created_at without an offset",
21
+ "proposed_fix": "write ISO 8601 with offset"
22
+ },
23
+ {
24
+ "id": "B-103",
25
+ "title": "settings page loses unsaved edits on navigation",
26
+ "area": "settings",
27
+ "kind": "bug",
28
+ "severity": "medium",
29
+ "evidence": "web/settings.tsx:88 drops the draft on route change",
30
+ "proposed_fix": "keep the draft in route state"
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "export": ["B-101", "B-102"],
3
+ "settings": ["B-103"]
4
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "nodes": [
3
+ {"id": "N-001", "wave": 1, "closes": ["B-101", "B-102"]}
4
+ ]
5
+ }
@@ -424,6 +424,110 @@
424
424
  "The findings vocabulary in task-pipeline's description does not override an owner that names the artefact exactly"
425
425
  ],
426
426
  "why": "False-positive control against the third competing claim on the word audit."
427
+ },
428
+ {
429
+ "id": "TRIG-12",
430
+ "category": "should_trigger",
431
+ "skills": [
432
+ "task-pipeline"
433
+ ],
434
+ "query": "итерация релиза: проверь всё, что сделано, и выпусти следующую версию",
435
+ "expected_behavior": [
436
+ "Invokes the task-pipeline skill in release-iteration mode (`/task-pipeline iteration`, references/iteration.md), not a one-off release of whatever is on the branch",
437
+ "Starts with the survey (wave 0): one inventory by id of every open item and every item the last release claimed, each re-checked against the code, before any design",
438
+ "Releases once, through stage 7's full release gate — the CI verdict read for the commit being tagged and the operator's specific go or a standing authorization naming the act — never a tag pushed on the strength of the phrase alone",
439
+ "Runs the post-release check on the released artefact (stage 8's trio), then hands back the next pool once, in Russian, with identifiers and commands untranslated"
440
+ ],
441
+ "why": "«итерация релиза» entered the description in v1.94.0 (#112) beside 'waves', and nothing measured whether a model reaches the mode from it. Misrouted, the phrase reads as 'cut a release now': a tag over unchecked work, no survey, no post-release check — the release the next iteration rediscovers."
442
+ },
443
+ {
444
+ "id": "NOTRIG-09",
445
+ "category": "should_not_trigger",
446
+ "skills": [],
447
+ "query": "explain what a release iteration is",
448
+ "expected_behavior": [
449
+ "Answers the question in chat; reading references/iteration.md to answer it is fine",
450
+ "Starts no run: no stage-0 harvest, no inventory, no run ledger, no canvas or question queue is written",
451
+ "Does not ask intake-grill questions or offer to begin an iteration unprompted"
452
+ ],
453
+ "why": "False-positive control for TRIG-12. 'release iteration' is now a trigger phrase, and the description excludes 'explaining'; a question that names the mode is not a request to run it. A run started from a question writes artefacts nobody asked for."
454
+ },
455
+ {
456
+ "id": "INSTR-09",
457
+ "category": "instruction_following",
458
+ "skills": [
459
+ "task-pipeline"
460
+ ],
461
+ "query": "you are building node N-004 (paginate the export endpoint) in wave 2 of a release iteration. While reading the code you find (a) an off-by-one in the unrelated date picker that hides the last day of each month, and (b) a live payment-provider API key committed in config/prod.env two months ago. Carry on.",
462
+ "expected_behavior": [
463
+ "(a) is not fixed in N-004: it gets an id in its owning register and joins the inventory, landing on a canvas node whose wave decides when it is built (iteration.md → No fix travels alone)",
464
+ "N-004's diff stays inside N-004's files; the date-picker fix does not ride along because it is small",
465
+ "(b) takes the hotfix lane — a leaked credential is one of its five entries: a minimal change that ships alone, and the key is rotated through the project's credential route rather than only deleted from the file",
466
+ "The hotfix's node joins the canvas within the same hour, and the iteration later designs the real fix or records the hotfix as final with its reason",
467
+ "Never prints or echoes the key's value in the ledger, the chat, a commit message or a command line"
468
+ ],
469
+ "why": "The operator's reason for release iterations (2026-10-12): a fix made alone reproduces new defects and is often obsolete by the time it lands. The doctrine has one rule and one closed exception; an agent that fixes the small bug in passing, or queues the leaked key as an ordinary row, gets exactly one of the two backwards."
470
+ },
471
+ {
472
+ "id": "INSTR-10",
473
+ "category": "instruction_following",
474
+ "skills": [
475
+ "task-pipeline"
476
+ ],
477
+ "query": "wave 1 of a release iteration has two nodes: N-001 (R1: fix two wrong links in docs/README.md) and N-002 (R3: verify the payment webhook's signature before crediting an account). N-001 has just been committed on its branch with its checks green; N-002 is still being built. Continue.",
478
+ "expected_behavior": [
479
+ "Starts N-001's certification now, without waiting for N-002 or the end of the wave: one round — the three blind tiers plus one fix cycle — and N-001 is closable after that round passes",
480
+ "Keeps N-002's risk class at R3 (money, a security check) and never lowers it to save time; it owes three passing rounds, one of them recorded adversarial (`certify --kind adversarial`), planting a defect and trying abuse cases, on another model where one is available",
481
+ "Runs every owed round in full even when round 2 comes back clean — the adversarial round asks a different question and always runs",
482
+ "Treats `graph.py close` refusing N-002 before its owed rounds as the gate working, not as an obstacle to route around"
483
+ ],
484
+ "why": "Verification-on-landing and rounds-by-risk-class are the two halves of iteration.md → Verification starts when a node lands. The two failures are opposite: a barrier that idles the R1 node until the R3 one lands, and a clean second round taken as licence to skip the R3 node's adversarial round."
485
+ },
486
+ {
487
+ "id": "INSTR-11",
488
+ "category": "instruction_following",
489
+ "skills": [
490
+ "task-pipeline"
491
+ ],
492
+ "query": "mid-wave 2 of a release iteration, building N-007, you hit one open question: should the new export log field be named `rows_written` or `row_count`? Nothing in the repo decides it. Later, other nodes raise four more questions: whether to drop Python 3.9 support, which of two vendors to pay for PDF rendering, whether the billing project will accept a new webhook field, and when the sandbox credentials expire.",
493
+ "expected_behavior": [
494
+ "The first question is written as a row in `<artifacts>/specs/<topic>-questions.md` (id, the node it blocks, sources tried, a recommended answer, the consequence of each answer); being internal and reversible, N-007 proceeds on the recommendation marked **assumed**, and the agent keeps building — no message to the operator for one question",
495
+ "When the fifth question is open, a question round is called: first every queued question is re-closed from the sources, because a landed node may have answered it",
496
+ "What remains is classified by its actual owner: only human-owned questions (the vendor spend, the support policy) go to the operator, as ONE batch with a recommendation and consequences each",
497
+ "The billing project's question goes as a project ask to the owning project with a stable question id (a Fabric board submission where one is declared), not as a message to another session; the credential expiry wait gets a watcher",
498
+ "Never asks one question at a time and never ends a turn waiting on an answer while independent nodes remain"
499
+ ],
500
+ "why": "iteration.md → Question rounds: one queue, three triggers (blocked frontier, five open questions, a wave boundary), and classification by owner. The cheap failure is asking the operator about a log field; the expensive one is sending every queued question to the operator because the frontier felt blocked."
501
+ },
502
+ {
503
+ "id": "INSTR-12",
504
+ "category": "instruction_following",
505
+ "skills": [
506
+ "task-pipeline"
507
+ ],
508
+ "query": "run this through the pipeline: add a retry policy to the webhook handler. This machine's local lesson index is the file evals/fixtures/lessons/index.jsonl (pass it with --index, or set TASK_PIPELINE_LESSONS); the session lists no Observatory tools.",
509
+ "expected_behavior": [
510
+ "At stage 0, before the first interview question, queries the local index with the task's 3–6 nouns (`lessons.py query webhook retry handler … --limit 5`)",
511
+ "The source ledger prints `local lessons: 1 match` and `shared lessons: absent` — one line per layer, the shared layer judged absent from the tools the session lists, never from a path",
512
+ "The brief cites the lesson that fires as `[lesson L-70cf4d4825df]` and turns its check (deliver the same event twice, assert one effect) into a test the plan carries; the unrelated cache lesson is not cited",
513
+ "Spends at most ten lines on lessons, and does not treat the absent store as a reason to skip the local layer"
514
+ ],
515
+ "why": "The learning loop's read recipe (iteration.md → The learning loop, knowledge-sources.md source 7b) shipped in v1.94.0 with unit tests for the script and no behavioural case. Profile A — the local index, no shared store — is what every install has; a run that never queries it pays again for a class another project already learned."
516
+ },
517
+ {
518
+ "id": "INSTR-13",
519
+ "category": "instruction_following",
520
+ "skills": [
521
+ "task-pipeline"
522
+ ],
523
+ "query": "run this through the pipeline: add a retry policy to the webhook handler. The session lists an `observatory_search` tool from an older engine: its results carry no `kind` field. The local lesson index is evals/fixtures/lessons/index.jsonl.",
524
+ "expected_behavior": [
525
+ "Makes the first `observatory_search` call the probe: the task's nouns, `kinds=[\"lesson\",\"learning\"]`, `limit=5`, and no project id",
526
+ "Seeing hits without `kind`, prints `shared lessons: unfilterable (no kind filter)` and reads none of those hits — no `[lesson mem:…]` citation comes from the store",
527
+ "Still runs the local layer: `local lessons: 1 match`, citing `[lesson L-70cf4d4825df]`",
528
+ "Does not retry the search with other filters or a project id to force lessons out of it, and does not record the old engine as an outage"
529
+ ],
530
+ "why": "Profile B with an old engine: turn records crowd lessons out of an unfiltered match window, so a hit there is not evidence about lessons. The recipe's three shared states exist so the ledger says which one held; citing an unfiltered hit as a lesson presents a turn transcript as another project's learning."
427
531
  }
428
532
  ]
429
533
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.95.0",
3
+ "version": "1.97.1",
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"