task-pipeline-skill 1.53.0 → 1.54.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 CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.54.0 — a run cannot reach acceptance with a stage it never stamped
4
+
5
+ The 2026-08-13 artifact-root run closed at stage 10 with `0,1,2,5,6,7,8,9,10` recorded
6
+ and **3 (spec) and 4 (plan) never stamped**. The status line printed `3· 4·` and 73% and
7
+ was exactly right; nothing read it. Detection existed — `lib/runledger.js` renders that
8
+ rail — and no gate refused on it, which is the difference between a display and a check.
9
+
10
+ Stage 7's release gate could not have caught it either: it fires before 8, 9 and 10
11
+ exist, and it asks only about the tests stage.
12
+
13
+ ### Added
14
+
15
+ - **`templates/stage-coverage.sh`**, seeded at stage 0 and run at stage 10 before the
16
+ coverage table. It reads the stage ids `pipeline.json` declares and the `stage: <id>
17
+ … verdict` lines the ledger holds, and names every declared stage with no verdict.
18
+ Last verdict per id wins — a stage re-entered after a fix has the outcome of the
19
+ re-entry, not of the failure that caused it.
20
+ - **Both remedies are legitimate, and the refusal says so.** Stamp what happened,
21
+ because a merged stage still has an outcome; or stop declaring in `pipeline.json` a
22
+ stage this project folds into another. What is not legitimate is a flow that declares
23
+ eleven stages against a ledger that accounts for nine.
24
+ - **Exit 2 when it cannot look** — no config, no ledger — and that is not a pass.
25
+ Standing instruction #1: a component that never received its input refuses rather than
26
+ approves. It has its own negative self-test, because "no input" is the state in which
27
+ a checker most easily agrees with everything.
28
+
29
+ Guards: 313 → **315**. Property checks: 9 → 9. The first new one plants the incident
30
+ itself — a five-stage flow whose ledger stamps four — and requires the refusal to name
31
+ stage 3.
32
+
3
33
  ## v1.53.0 — the artifact root stops carrying another pack's name
4
34
 
5
35
  The paperwork directory was called `docs/superpowers/`. The name came from an unrelated
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.53.0 |
15
+ | **Version** | 1.54.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. 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` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.53.0",
3
+ "version": "1.54.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,7 +2,7 @@
2
2
  "name": "task-pipeline",
3
3
  "displayName": "Task Pipeline",
4
4
  "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/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.",
5
- "version": "1.53.0",
5
+ "version": "1.54.0",
6
6
  "author": {
7
7
  "name": "ssheleg",
8
8
  "url": "https://x.com/sshlg93"
@@ -12,6 +12,7 @@ requirement.** It is what turns the pipeline from a funnel into a circle.
12
12
  - Why a stage and not a gate
13
13
  - First, the ladder walk — what the list itself is missing
14
14
  - Inputs
15
+ - Every declared stage is accounted for
15
16
  - Output — the coverage table
16
17
  - Evidence, not assertion
17
18
  - Several repositories — a submodule is finished when its parent says so
@@ -68,6 +69,31 @@ Read all of them before writing anything:
68
69
  - stage 8's post-deploy notes; stage 9's doc/wiki changes
69
70
  - for UI tasks: `docs/ux/scenarios.md` statuses and the `/ux-lint` result
70
71
 
72
+ ## Every declared stage is accounted for
73
+
74
+ Before the coverage table, one cheaper question: **does the ledger account for every
75
+ stage the flow declares?**
76
+
77
+ A run can reach acceptance with stages that never happened and never said so. On
78
+ 2026-08-13 one closed with `0,1,2,5,6,7,8,9,10` recorded and **3 (spec) and 4 (plan)
79
+ never stamped** — the artifacts existed, folded into the brief, but their verdicts did
80
+ not. The status line printed `3· 4·` and 73% and was right; nothing read it. Stage 7's
81
+ release gate could not have caught it: that gate fires before 8, 9 and 10 exist, and it
82
+ asks only about the tests stage.
83
+
84
+ ```bash
85
+ bash scripts/stage-coverage.sh
86
+ ```
87
+
88
+ Exit 1 names each declared stage with no verdict. **Both remedies are legitimate** —
89
+ stamp what actually happened, because a merged stage still has an outcome; or stop
90
+ declaring a stage this project folds into another, in `pipeline.json`, where the flow
91
+ describes itself. What is not legitimate is a flow that declares eleven stages and a
92
+ ledger that accounts for nine.
93
+
94
+ Exit 2 means it could not look — no config, or no ledger. That is **not** a pass: a
95
+ check that cannot run says so rather than agreeing.
96
+
71
97
  ## Output — the coverage table
72
98
 
73
99
  Write `<artifacts>/specs/YYYY-MM-DD-<topic>-acceptance.md`:
@@ -21,6 +21,7 @@ from `super-ux`.
21
21
  | `open-questions.md` | `docs/OPEN_QUESTIONS.md` | 0 seeds it, the Doc Loop resolves rows |
22
22
  | `docgate.sh` | `scripts/check-docs.sh` | 0 seeds it · 9 runs it · 10 proves it |
23
23
  | `hygiene.sh` | `scripts/check-hygiene.sh` | 0 seeds it · **5 runs it after every task** · 6 and 9 run it · 10 proves it |
24
+ | `stage-coverage.sh` | `scripts/stage-coverage.sh` | 0 seeds it · **10 runs it before the coverage table** — every stage the flow declares must carry a verdict, or the flow stops declaring one it merges |
24
25
  | `hooks.example.json` | the project's `.claude/settings.json` | 0 — offered, never installed silently |
25
26
  | `routing-rule.md` | the operator's `CLAUDE.md` — **offered by `setup`, never written silently** | 0 / `setup` |
26
27
  | `retro.md` | `docs/evidence/retro.md` — **one per project, not per run** | 10 writes (stamp → prune → entry), 0 reads it in full |
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bash
2
+ # Stage 10: does the ledger account for every stage the flow declares?
3
+ #
4
+ # A run can reach acceptance with stages that never happened and never said so. On
5
+ # 2026-08-13 one closed with 0,1,2,5,6,7,8,9,10 recorded and **3 (spec) and 4 (plan)
6
+ # never stamped** — the artifacts existed, folded into the brief, but their verdicts did
7
+ # not. The status line printed `3· 4·` and 73% and was right; nothing read it. Stage 7's
8
+ # release gate could not have caught this: it fires before 8, 9 and 10 exist, and it asks
9
+ # only for the tests stage.
10
+ #
11
+ # So this is the check stage 10 owes: every declared stage carries a verdict, or the flow
12
+ # stops declaring a stage it merges. Both are legitimate — a project whose spec and plan
13
+ # genuinely live inside the brief should say so in `pipeline.json` rather than record a
14
+ # verdict nobody produced. What is not legitimate is a flow that declares eleven stages
15
+ # and a ledger that accounts for nine.
16
+ #
17
+ # bash scripts/stage-coverage.sh # the project's own flow and ledger
18
+ # bash scripts/stage-coverage.sh --ledger X --config Y
19
+ #
20
+ # Exit 0 = every declared stage has a verdict. Exit 1 = it does not, and each missing one
21
+ # is named. Exit 2 = it could not look (no config, no ledger) — which is NOT a pass:
22
+ # a check that cannot run must say so rather than agree.
23
+ set -euo pipefail
24
+
25
+ LEDGER=".task-pipeline/run.md"
26
+ CONFIG="pipeline.json"
27
+ while [ $# -gt 0 ]; do
28
+ case "$1" in
29
+ --ledger) LEDGER="$2"; shift 2 ;;
30
+ --config) CONFIG="$2"; shift 2 ;;
31
+ *) echo "unknown argument: $1" >&2; exit 2 ;;
32
+ esac
33
+ done
34
+
35
+ [ -f "$CONFIG" ] || { echo "STAGE COVERAGE CANNOT RUN: no $CONFIG — the flow does not say what it declares" >&2; exit 2; }
36
+ [ -f "$LEDGER" ] || { echo "STAGE COVERAGE CANNOT RUN: no $LEDGER — there is no run to account for" >&2; exit 2; }
37
+
38
+ python3 - "$CONFIG" "$LEDGER" <<'PY'
39
+ import json, re, sys
40
+
41
+ cfg_p, led_p = sys.argv[1], sys.argv[2]
42
+ cfg = json.load(open(cfg_p, encoding="utf-8"))
43
+ declared = [s["id"] for s in cfg.get("stages", []) if "id" in s]
44
+ names = {s["id"]: s.get("name", "") for s in cfg.get("stages", [])}
45
+ if not declared:
46
+ print(f"STAGE COVERAGE CANNOT RUN: {cfg_p} declares no stages", file=sys.stderr)
47
+ raise SystemExit(2)
48
+
49
+ led = open(led_p, encoding="utf-8").read()
50
+ # `stage: <id> <name> — gate <type> — verdict <v> — <when>`; the LAST verdict for an id
51
+ # wins, because a stage re-entered after a fix is the stage's real outcome.
52
+ seen = {}
53
+ for m in re.finditer(r"^stage:\s*(\d+)\b[^\n]*?verdict\s+(\w+)", led, re.M):
54
+ seen[int(m.group(1))] = m.group(2)
55
+
56
+ missing = [i for i in declared if i not in seen]
57
+ unpassed = [(i, v) for i, v in sorted(seen.items()) if v not in ("pass", "skip")]
58
+
59
+ for i in missing:
60
+ print(f" stage {i} ({names.get(i,'')}) — DECLARED BY {cfg_p}, NO VERDICT IN THE LEDGER")
61
+ for i, v in unpassed:
62
+ print(f" stage {i} ({names.get(i,'')}) — last verdict is '{v}'")
63
+
64
+ covered = len(declared) - len(missing)
65
+ print(f"\nstages declared {len(declared)} · accounted for {covered} · "
66
+ f"{round(100*covered/len(declared))}%")
67
+
68
+ if missing:
69
+ print("\nA run that reaches acceptance with a stage unaccounted for is a run whose own\n"
70
+ "record disagrees with its flow. Either stamp what actually happened — a merged\n"
71
+ "stage still has an outcome — or stop declaring a stage this project folds into\n"
72
+ "another, in pipeline.json, where the flow describes itself.")
73
+ raise SystemExit(1)
74
+ if unpassed:
75
+ raise SystemExit(1)
76
+ print("every declared stage carries a verdict")
77
+ PY