task-pipeline-skill 1.102.3 → 1.102.4

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,3 +1,26 @@
1
+ ## v1.102.4: the Bash hooks start no process in a project with no run
2
+
3
+ - **Fewer processes per Bash call, same verdicts** (hub board row B-310). The plugin's Bash
4
+ hooks run on every Bash call in every project, and they started processes before learning
5
+ there was no run. Measured by PID delta (macOS allocates PIDs in order; minimum of 25 runs,
6
+ the hook's own bash included): `release-gate.sh` 14, `gate-observer.sh` 3 and
7
+ `memory-nudge.sh` 1, so 18 per Bash call in a project with no `.task-pipeline/run.md`, and 23
8
+ with a run in flight (14, 4 and 5). Now 3 (1, 1, 1) and 11 (5, 4, 2). Three changes:
9
+ `release-gate.sh` tests for the ledger before it reads stdin or starts python3 (`-e`, as
10
+ the python asks `os.path.exists`, so a ledger path that is a directory is still refused as
11
+ unreadable); it splits its `state`, `act`, `why` verdict with parameter expansion instead
12
+ of three `printf | cut` pipelines, keeping `cut`'s field rules, and a verdict of more than
13
+ one line still goes through the pipelines; `gate-observer.sh` reads stdin after the ledger
14
+ test; `memory-nudge.sh` finds its scripts directory from `${BASH_SOURCE[0]}` without
15
+ `$(cd "$(dirname …)" && pwd)`. Behaviour is unchanged: a differential run of the old and
16
+ new hooks over 4,950 cases (eight ledger states, five flows, 30 payloads, python3 working
17
+ and broken) gave 0 differences in exit code, stdout, stderr and ledger contents.
18
+ `test/release_gate_test.py` (74, three new): with no ledger each Bash hook exits 0 silent and starts
19
+ none of python3, cat, cut, dirname (PATH shims log any start; red on 1.102.3, which started
20
+ cat and python3), a negative control that the shims do see python3, and a refusal that
21
+ carries its act and whole reason. `test/memory_nudge_test.py` (43, two new): the run marker is still
22
+ honoured when the hook is started by a bare name or a relative path.
23
+
1
24
  ## v1.102.3: stamp rows keep their table's shape, one rotation line per quarter, apply_patch gated
2
25
 
3
26
  Guards: 531 → **531**; property checks 15 → **15**. No negative was added: the new checks are
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.102.3 |
15
+ | **Version** | 1.102.4 |
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. 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` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.102.3",
3
+ "version": "1.102.4",
4
4
  "description": "Full-cycle delivery pipeline for coding agents: a mandatory built-in intake grill, then 10 gated stages (docs, brainstorm+decompose, spec, plan, build, tests, lint/deploy, post-deploy, docs/wiki, acceptance). Every stage's doctrine ships inside the skill — no companion plugin required. This package is the installer CLI.",
5
5
  "bin": {
6
6
  "task-pipeline": "bin/task-pipeline.js"
@@ -3,7 +3,7 @@
3
3
  "name": "task-pipeline",
4
4
  "displayName": "Task Pipeline",
5
5
  "description": "Runs a substantial task through a mandatory built-in intake grill, then 10 gated stages (docs, brainstorm+decompose, spec, plan, subagent build, tests, lint/deploy, post-deploy, docs/wiki, acceptance). Every stage's doctrine is built into the skill — no companion plugin required — with typed auto/judgment/manual gates, a frozen requirement spine that closes with evidence, a work board and a verification ledger that outlive a run, an exposure line naming what shipped unconfirmed, a progress rail computed from the project's own config, a proportional short path for small changes, a loop guard whose ceilings stop and record a blocker, 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.102.3",
6
+ "version": "1.102.4",
7
7
  "author": {
8
8
  "name": "ssheleg",
9
9
  "url": "https://x.com/sshlg93"
@@ -26,10 +26,12 @@
26
26
  # running a pipeline.
27
27
  set -uo pipefail
28
28
 
29
- input=$(cat 2>/dev/null || true)
30
29
  project="${CLAUDE_PROJECT_DIR:-$PWD}"
31
30
  ledger="$project/.task-pipeline/run.md"
31
+ # The ledger test comes before stdin is read (hub B-310): with no run, reading the
32
+ # payload starts `cat` for nothing. memory-nudge.sh has always exited unread here.
32
33
  [ -f "$ledger" ] || exit 0
34
+ input=$(cat 2>/dev/null || true)
33
35
 
34
36
  HOOK_INPUT="$input" python3 - "$ledger" "$project" <<'PY' 2>/dev/null || true
35
37
  import json, os, re, sys, datetime
@@ -20,7 +20,14 @@
20
20
  set -uo pipefail
21
21
  ledger="${CLAUDE_PROJECT_DIR:-$PWD}/.task-pipeline/run.md"
22
22
  [ -f "$ledger" ] || exit 0
23
- TP_SCRIPTS="$(cd "$(dirname "${BASH_SOURCE[0]}")/../skills/task-pipeline/scripts" 2>/dev/null && pwd)" \
23
+ # The scripts directory without `$(cd "$(dirname …)" && pwd)`, which started two
24
+ # processes per call (hub B-310). `dirname` of a bare name is `.`; a directory that
25
+ # does not exist leaves TP_SCRIPTS empty, as the failed `cd` did.
26
+ here=${BASH_SOURCE[0]}
27
+ case "$here" in */*) here=${here%/*} ;; *) here=. ;; esac
28
+ TP_SCRIPTS="$here/../skills/task-pipeline/scripts"
29
+ [ -d "$TP_SCRIPTS" ] || TP_SCRIPTS=
30
+ TP_SCRIPTS="$TP_SCRIPTS" \
24
31
  python3 - "$ledger" <<'PY' 2>/dev/null || true
25
32
  import hashlib, json, os, re, sys
26
33
  ledger = sys.argv[1]
@@ -43,11 +43,19 @@
43
43
  # crashing gate that fails open is worse than no gate, because it reads as one.
44
44
  set -uo pipefail
45
45
 
46
- input=$(cat 2>/dev/null || true)
47
-
48
46
  project="${CLAUDE_PROJECT_DIR:-$PWD}"
49
47
  ledger="$project/.task-pipeline/run.md"
50
48
 
49
+ # Narrowness 2, before anything else starts (hub B-310): with no ledger every path
50
+ # below ends in `skip` or `exit 0` with no output, so the answer is known without
51
+ # reading stdin or starting python3. `-e`, not `-f`: the python below asks
52
+ # `os.path.exists`, so a ledger path that is a directory still reaches it and is
53
+ # still refused as unreadable. Leaving stdin unread is what memory-nudge.sh has
54
+ # always done on a project with no run.
55
+ [ -e "$ledger" ] || exit 0
56
+
57
+ input=$(cat 2>/dev/null || true)
58
+
51
59
  decide() {
52
60
  # The payload travels in the environment, NOT on stdin: the heredoc below IS
53
61
  # stdin for `python3 -`, so a script fed that way can never also read the hook's
@@ -265,9 +273,27 @@ if [ $status -ne 0 ] || [ -z "$verdict" ]; then
265
273
  exit 2
266
274
  fi
267
275
 
268
- state=$(printf '%s' "$verdict" | cut -f1)
269
- act=$(printf '%s' "$verdict" | cut -f2)
270
- why=$(printf '%s' "$verdict" | cut -f3)
276
+ # The verdict is `state<TAB>act<TAB>why`, split in the shell itself rather than by
277
+ # three `printf | cut` pipelines (nine processes per call, hub B-310). Each value is
278
+ # what `cut -fN` gives on one line: a line with no tab is its own field 1, 2 and 3;
279
+ # a line with one tab has an empty field 3; a field ends at the next tab. A verdict
280
+ # of more than one line (a newline inside a declared command or stage id) keeps the
281
+ # original pipelines, so that case reads exactly as before.
282
+ case "$verdict" in
283
+ *$'\n'*)
284
+ state=$(printf '%s' "$verdict" | cut -f1)
285
+ act=$(printf '%s' "$verdict" | cut -f2)
286
+ why=$(printf '%s' "$verdict" | cut -f3) ;;
287
+ *$'\t'*)
288
+ state=${verdict%%$'\t'*}
289
+ rest=${verdict#*$'\t'}
290
+ act=${rest%%$'\t'*}
291
+ case "$rest" in
292
+ *$'\t'*) rest=${rest#*$'\t'}; why=${rest%%$'\t'*} ;;
293
+ *) why= ;;
294
+ esac ;;
295
+ *) state=$verdict act=$verdict why=$verdict ;;
296
+ esac
271
297
 
272
298
  case "$state" in
273
299
  skip|ok) exit 0 ;;
@@ -254,7 +254,7 @@ reusable part:
254
254
  | Narrowness | Why it is not a smaller feature |
255
255
  |---|---|
256
256
  | Only outward acts, never ordinary commits | stage 5 commits per task by design; a gate that fights the build loop is removed within a day |
257
- | Silent where no `.task-pipeline/run.md` exists | enabling the plugin must change nothing in a repository that runs no pipeline |
257
+ | Silent where no `.task-pipeline/run.md` exists, before python3 starts | enabling the plugin must change nothing in a repository that runs no pipeline, and every Bash call there pays these hooks: each Bash hook tests for the ledger before it reads stdin or starts python3, so one Bash call costs three bash processes instead of eighteen (hub B-310; `test/release_gate_test.py`, "with no ledger, the Bash hooks exit 0 silent and start no process") |
258
258
  | Reads the ledger, never reruns a suite | `progress.md` already makes the ledger append-only; a second source of truth about "did stage 6 pass" is the failure this file warns about below |
259
259
 
260
260
  **Fail-closed, deliberately.** Every non-zero exit code other than `2` is
@@ -254,7 +254,7 @@ reusable part:
254
254
  | Narrowness | Why it is not a smaller feature |
255
255
  |---|---|
256
256
  | Only outward acts, never ordinary commits | stage 5 commits per task by design; a gate that fights the build loop is removed within a day |
257
- | Silent where no `.task-pipeline/run.md` exists | enabling the plugin must change nothing in a repository that runs no pipeline |
257
+ | Silent where no `.task-pipeline/run.md` exists, before python3 starts | enabling the plugin must change nothing in a repository that runs no pipeline, and every Bash call there pays these hooks: each Bash hook tests for the ledger before it reads stdin or starts python3, so one Bash call costs three bash processes instead of eighteen (hub B-310; `test/release_gate_test.py`, "with no ledger, the Bash hooks exit 0 silent and start no process") |
258
258
  | Reads the ledger, never reruns a suite | `progress.md` already makes the ledger append-only; a second source of truth about "did stage 6 pass" is the failure this file warns about below |
259
259
 
260
260
  **Fail-closed, deliberately.** Every non-zero exit code other than `2` is