task-pipeline-skill 1.3.0 → 1.3.2

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,78 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.3.2 — 2026-07-29
4
+
5
+ **The 26 negative self-tests could not be run anywhere except CI** — which meant
6
+ that on the maintainer's own machine, a new guard could never be watched rejecting
7
+ its planted defect. The project's own `references/audit.md` demands exactly that
8
+ (*plant the defect, watch the check fail, then trust the green*), and the tooling
9
+ made it impossible at the one moment it is worth most: while the guard is being
10
+ written.
11
+
12
+ Found by running the full CI suite locally during a validity re-check. Nine of the
13
+ 26 failed — every one on BSD sed, none on a repo defect.
14
+
15
+ ### Added
16
+ - **`test/negatives.py`** — runs every negative self-test locally, zero
17
+ dependencies, same as the validator. `npm run test:negatives`, or
18
+ `npm run test:all` for validator-then-guards. The corruptions are **read from
19
+ `.github/workflows/validate.yml`, never duplicated** — a second copy of a
20
+ corruption is a second thing to drift.
21
+ - **It tells a broken test from a guard that didn't fire.** If a planted defect
22
+ changed nothing, the validator passing means the *test* proved nothing, not that
23
+ the guard is dead. That case now reports `BROKEN`, with the fix pointed at the
24
+ workflow. This is the failure mode that hid the sed problem in the first place: a
25
+ no-op corruption reads exactly like a broken guard.
26
+ - It also refuses to run if it finds fewer than 20 tests — a parser or format change
27
+ that silently matched nothing would otherwise report zero failures and look like
28
+ success.
29
+
30
+ ### Fixed
31
+ - **Every `sed -i` corruption in the workflow is now python.** BSD sed needs an
32
+ argument GNU sed refuses, and `0,/re/` does not exist on BSD at all — there it
33
+ edits nothing *silently*, and the test reads as a guard that failed to fire.
34
+ Nine steps converted; CI and a laptop now run the identical script.
35
+
36
+ ### Validator
37
+ - **`sed -i` in `.github/workflows/validate.yml` is now a failure**, and
38
+ `test/negatives.py` must exist. Without both, the guards drift back to
39
+ CI-only and stop being provable where they are written.
40
+ - The new self-test builds the forbidden token at runtime, because spelling it
41
+ literally would make the workflow trip the guard it is testing. Verified the
42
+ honest way: a clean copy passes, and the injected copy is the *only* reason the
43
+ corrupted one fails — a self-test that passes because the base is already red
44
+ proves nothing.
45
+
46
+ ## v1.3.1 — 2026-07-29
47
+
48
+ ### Stage 10 closes on the parent repository, not only on the one you edited
49
+
50
+ A submodule is finished when its parent says so. A parent records each submodule as a pointer to
51
+ one commit, and moving the submodule does not move the pointer — so work can be committed, pushed,
52
+ green in CI and marked done in its own roadmap while a clone of the parent still gets the commit
53
+ before it. Neither repository looks wrong alone; the disagreement lives between them, which is why
54
+ it survives every check that runs inside one.
55
+
56
+ Stage 10's gate now requires every repository — parent included — to be clean, pushed and pointed
57
+ at, with the plain-git commands given and `/agent-sync finish` named for projects that have it.
58
+
59
+ The rule reaches all seven surfaces that carry the stage-10 close-out: `SKILL.md`, the gate in
60
+ `references/stages.md`, the doctrine in `references/acceptance.md`, the machine-readable check in
61
+ `pipeline.example.json`, `build.md`, `conventions.md`, the slash command and the Cursor rule.
62
+
63
+ ### Validator — the class that caused this is now a check
64
+
65
+ This close-out has failed to reach every surface **twice**: v0.17.1 fixed it for the third review
66
+ verdict, and this release's own first pass declared the parent-repository rule in `SKILL.md`,
67
+ `build.md` and `conventions.md` while `acceptance.md`, `stages.md` and the config — *the three
68
+ places that actually define the gate* — never heard of it. A gate that says "now requires X" only
69
+ where X is not enforced is inert, and no existing check saw it: the validator compared stage ids,
70
+ names and gate types, never gate content.
71
+
72
+ Twice is a category, so it is a check now. Whatever close-out concept `SKILL.md` names, the
73
+ surfaces that enforce stage 10 must name it too. Proven against a planted defect, with a CI
74
+ negative self-test.
75
+
3
76
  ## v1.3.0 — 2026-07-29
4
77
 
5
78
  **One design file, in a named team, decided before anything is drawn.** Left to
@@ -211,8 +211,20 @@ not authorize an outward, irreversible action — stage 7 stops and asks.
211
211
  watched fail against a planted defect is not evidence at all**. Then ask out
212
212
  loud, list in hand: *here's what you asked for, here's what shipped, here's
213
213
  what's deferred and where it lives — what's missing?* Ask it even when the
214
- table is green. Gate: ladder walk ran, no REQ `unknown`, no ledger row without
215
- a home, user signs off.
214
+ table is green.
215
+ **In a project of several repositories, close the parent too.** A parent records
216
+ each submodule as a **pointer to one commit**, and moving the submodule does not
217
+ move the pointer — so the work is committed, pushed, CI green and done in its own
218
+ roadmap, while anyone cloning the parent gets the commit *before* the change.
219
+ Neither repository looks wrong on its own; the disagreement exists only between
220
+ them, which is why it survives every check that runs inside one. Require this to
221
+ report nothing, for the parent as well as every submodule:
222
+ `git submodule status` (no line starting `+` — a `+` is the missing bump),
223
+ `git -C <repo> status --porcelain`, `git -C <repo> log @{u}..HEAD --oneline`.
224
+ The fix is two commands and the second is the one that gets forgotten:
225
+ `git -C <submodule> push`, then `git add <submodule> && git commit`.
226
+ Gate: ladder walk ran, no REQ `unknown`, no ledger row without
227
+ a home, every repository clean/pushed/pointed-at, user signs off.
216
228
 
217
229
  Cross-cutting: answer from the brief's autonomy section rather than re-asking, log every deferral in the ledger, never narrow the task silently, track
218
230
  tasks, conventional commits, honest degradation (never claim a failed/skipped step
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
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"
7
7
  },
8
8
  "scripts": {
9
- "test": "python3 test/validate.py"
9
+ "test": "python3 test/validate.py",
10
+ "test:negatives": "python3 test/negatives.py",
11
+ "test:all": "python3 test/validate.py && python3 test/negatives.py"
10
12
  },
11
13
  "files": [
12
14
  "bin",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "task-pipeline",
3
3
  "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 must close with evidence, a loop guard that breaks churn, one provider-agnostic model confirmed up front, and an optional super-ux UX track for user-facing work.",
4
- "version": "1.3.0",
4
+ "version": "1.3.2",
5
5
  "author": {
6
6
  "name": "ssheleg"
7
7
  },
@@ -25,7 +25,7 @@ language, ADRs for hard-to-reverse calls) and covering the **autonomy sweep** (w
25
25
  would otherwise stop stages 1→10: docs sources incl. doc repos and the wiki, branch/tracker
26
26
  policy, test and lint commands, deploy target and authorization, log locations, docs/wiki targets, and for UI tasks whether the design is done visually in Figma or text-only, whether the Figma MCP is connected, and — if it isn't — whether to ship text-only or stop and connect it, since the UX chain degrades on its own and never blocks; **and with Figma on, the design destination: which team/org by name and which file** — the recorded one, a URL the operator gives, or creation in that named team explicitly authorized, written into the project's canonical record before the first frame, because a destination decided at drawing time is how a project ends up with three design files and no way to tell which is real. **Never create while a recorded file resolves; if it doesn't resolve, stop and ask — never create a replacement**) —
27
27
  until the brief is locked — including the **REQ table**, the request as an addressable list where every row names how it is verified — so the rest runs autonomously and the final stage can account for all of it. The list is frozen: adding is free, removing needs the operator's agreement. Anything deferred goes into the carry-over ledger the moment it's said. For any user-facing task, recommend/use
28
- **super-ux**. **If the brief describes a platform rather than a change**, stage 2 also cuts it into modules (`references/decomposition.md`) — module map committed, walking skeleton first, every REQ in exactly one module — and stages 3→10 then run per module, one brick at a time. **If any loop starts undoing an earlier pass** (same file edited twice for the same reason, a closed finding returning, a third entry into one stage), stop and run the loop guard (`references/loop-guard.md`): name both shapes, escalate to the layer that owns the conflict, re-plan the check as an ordered list, then go item by item. **The closing stage opens with the ladder walk** (`references/audit.md`): the REQ table finds what was named and lost, but a comparison needs two sides and an absence has one — so walk each REQ bottom-up through its rungs (decision → spec section → contract *and its failure behavior* → task → change → executed test → surface/docs), check the seam at each step, order findings by seam rather than by file, and turn every absence into a new REQ row **before** the coverage table is written. A green from a check nobody has watched fail against a planted defect is not evidence; a finding class seen twice becomes a script rather than a third ledger row; and the carry-over ledger's counts are printed beside every gate verdict, so "green" never reads as "verified". If a searching pass starts finding mostly what the previous pass's own fixes broke, the axis is exhausted — rotate it, don't look harder. Honor every stage gate by its type (`auto` = verify yourself;
28
+ **super-ux**. **If the brief describes a platform rather than a change**, stage 2 also cuts it into modules (`references/decomposition.md`) — module map committed, walking skeleton first, every REQ in exactly one module — and stages 3→10 then run per module, one brick at a time. **If any loop starts undoing an earlier pass** (same file edited twice for the same reason, a closed finding returning, a third entry into one stage), stop and run the loop guard (`references/loop-guard.md`): name both shapes, escalate to the layer that owns the conflict, re-plan the check as an ordered list, then go item by item. **The closing stage opens with the ladder walk** (`references/audit.md`): the REQ table finds what was named and lost, but a comparison needs two sides and an absence has one — so walk each REQ bottom-up through its rungs (decision → spec section → contract *and its failure behavior* → task → change → executed test → surface/docs), check the seam at each step, order findings by seam rather than by file, and turn every absence into a new REQ row **before** the coverage table is written. A green from a check nobody has watched fail against a planted defect is not evidence; a finding class seen twice becomes a script rather than a third ledger row; and the carry-over ledger's counts are printed beside every gate verdict, so "green" never reads as "verified". If a searching pass starts finding mostly what the previous pass's own fixes broke, the axis is exhausted — rotate it, don't look harder. **In a project of several repositories, stage 10 closes on the parent too:** a parent records each submodule as a pointer to one commit, and moving the submodule does not move the pointer — so the work can be committed, pushed and green while a clone of the parent still gets the commit before it. Neither repo looks wrong alone, which is why it survives every check that runs inside one. Require `git submodule status` with no line starting `+`, and every repo clean and pushed (`git -C <repo> status --porcelain`, `git -C <repo> log @{u}..HEAD`). The fix is two commands and the second gets forgotten: push the submodule, then `git add <submodule> && git commit`. Honor every stage gate by its type (`auto` = verify yourself;
29
29
  `manual` = wait for explicit go). Confirm the **model once at preflight** —
30
30
  recommend the most capable one the environment offers, never a hardcoded id — then
31
31
  run the whole pipeline on it without re-asking.
@@ -194,7 +194,34 @@ capable available — see `references/model-tiering.md`).
194
194
  | 7 | Lint + deploy | host lint → deploy per host convention | lint clean + suite green before deploy; deploy needs a go (or the brief's specific standing authorization) | manual |
195
195
  | 8 | Post-deploy | tail deploy logs / health-check | clean boot or honest degradation report | auto |
196
196
  | 9 | Docs + wiki | host module docs/runbook rules → `wiki-update` ([obsidian-wiki](https://github.com/ar9av/obsidian-wiki), recommended) | every stale row of the stage-0 source ledger updated; docs synced; wiki synced | auto |
197
- | 10 | **Acceptance** | built in: [`references/audit.md`](references/audit.md) (ladder walk) → [`references/acceptance.md`](references/acceptance.md) (coverage table) | ladder walk ran, its absences became REQ rows; every REQ accounted for with evidence from a check seen failing once; ledger has no unresolved row; operator signs off | manual |
197
+ | 10 | **Acceptance** | built in: [`references/audit.md`](references/audit.md) (ladder walk) → [`references/acceptance.md`](references/acceptance.md) (coverage table) | ladder walk ran, its absences became REQ rows; every REQ accounted for with evidence from a check seen failing once; ledger has no unresolved row; **in a multi-repository project, every repository is clean, pushed and pointed at** (below); operator signs off | manual |
198
+
199
+
200
+ ### Stage 10 in a project of several repositories
201
+
202
+ **A submodule is finished when its parent says so.** A parent repository records each submodule as
203
+ a pointer to one commit, and moving the submodule does not move the pointer. So the work is
204
+ committed, pushed, its CI is green and its own roadmap says done — and anyone who clones the parent
205
+ gets the commit **before** the change. Nothing looks wrong in either repository on its own; the
206
+ disagreement exists only between them, which is why it survives every check that runs inside one.
207
+
208
+ Stage 10 does not close until:
209
+
210
+ ```bash
211
+ git submodule status # no line begins with '+' (a '+' is the missing bump)
212
+ git -C <each repo> status --porcelain && git -C <each repo> log @{u}..HEAD --oneline
213
+ ```
214
+
215
+ report nothing — for the parent as well as every submodule. Where
216
+ [agent-sync](https://github.com/appvillis-com/agent-sync) is installed, `/agent-sync finish` runs
217
+ exactly this plus *no lease left held*, and `--gates` adds the project's own gate commands.
218
+
219
+ The fix, when it fails, is two commands and the second is the one that gets forgotten:
220
+
221
+ ```bash
222
+ git -C <submodule> push
223
+ git add <submodule> && git commit -m "chore: bump <name> submodule — <why>"
224
+ ```
198
225
 
199
226
  ## Model — ask once, at preflight
200
227
 
@@ -158,7 +158,7 @@
158
158
  ],
159
159
  "gate": {
160
160
  "type": "manual",
161
- "check": "Close the circle. FIRST the LADDER WALK (references/audit.md), because the REQ table can only find what was named and lost — a comparison needs two sides and an absence has one: walk each REQ bottom-up through its rungs (decision -> spec section -> contract AND its failure behavior -> plan task -> change -> executed test -> surface/docs), check the seam at each step, order findings BY SEAM not by file, and turn every absence into a new REQ row with its check BEFORE the table is written; findings belonging to a lower layer go back to that layer (spec -> stage 3, plan -> stage 4); record the pass's two counts (new findings vs findings caused by this run's own fixes) so the next pass can tell whether the axis is exhausted. THEN the coverage table: every REQ has a status (verified / partial / deferred / dropped) — none unknown; every verified carries evidence (a passing test name, file:line, a command and its output, or a scenario ID) — 'done' without evidence is downgraded to partial, not upgraded, and a green from a check nobody has watched fail against a planted defect is not evidence at all; every partial names what is missing and where it is tracked; every deferred/dropped has the operator's agreement and, for deferred, a tracker entry; no carry-over row is left unresolved and the ledger's counts are printed beside this verdict, so 'green' never reads as 'verified'; and the operator answers the closing question — here is what you asked for, here is what shipped, here is what is deferred, what is missing? — and signs off"
161
+ "check": "Close the circle. FIRST the LADDER WALK (references/audit.md), because the REQ table can only find what was named and lost — a comparison needs two sides and an absence has one: walk each REQ bottom-up through its rungs (decision -> spec section -> contract AND its failure behavior -> plan task -> change -> executed test -> surface/docs), check the seam at each step, order findings BY SEAM not by file, and turn every absence into a new REQ row with its check BEFORE the table is written; findings belonging to a lower layer go back to that layer (spec -> stage 3, plan -> stage 4); record the pass's two counts (new findings vs findings caused by this run's own fixes) so the next pass can tell whether the axis is exhausted. THEN the coverage table: every REQ has a status (verified / partial / deferred / dropped) — none unknown; every verified carries evidence (a passing test name, file:line, a command and its output, or a scenario ID) — 'done' without evidence is downgraded to partial, not upgraded, and a green from a check nobody has watched fail against a planted defect is not evidence at all; every partial names what is missing and where it is tracked; every deferred/dropped has the operator's agreement and, for deferred, a tracker entry; no carry-over row is left unresolved and the ledger's counts are printed beside this verdict, so 'green' never reads as 'verified'; EVERY REPOSITORY IS CLOSED, THE PARENT INCLUDED — a submodule is finished only when its parent points at it, so 'git submodule status' shows no line starting with '+' and every repo is clean and pushed ('git -C <repo> status --porcelain' and 'git -C <repo> log @{u}..HEAD' both empty), because a parent records a submodule as a pointer to one commit and moving the submodule does not move the pointer: neither repo looks wrong alone and the disagreement survives every check that runs inside one; and the operator answers the closing question — here is what you asked for, here is what shipped, here is what is deferred, what is missing? — and signs off"
162
162
  }
163
163
  }
164
164
  ],
@@ -104,6 +104,42 @@ and the test-honesty rules apply one level down, raised to the level of intent:
104
104
  If the evidence for a requirement is "I read the code and it looks right", the
105
105
  status is `partial`, not `verified` — say so plainly rather than upgrading it.
106
106
 
107
+ ## Several repositories — a submodule is finished when its parent says so
108
+
109
+ A parent repository records each submodule as **a pointer to one commit**, and
110
+ moving the submodule does not move the pointer. So the work is committed, pushed,
111
+ its CI is green and its own roadmap says done — while anyone who clones the parent
112
+ gets the commit **before** the change.
113
+
114
+ Neither repository looks wrong on its own. The disagreement exists only *between*
115
+ them, which is why it survives every check that runs inside one — including this
116
+ stage, if this stage only ever looks at the repo it was working in.
117
+
118
+ Before the table is called complete, this reports nothing, **for the parent as well
119
+ as every submodule**:
120
+
121
+ ```bash
122
+ git submodule status # no line begins with '+' (a '+' is the missing bump)
123
+ git -C <each repo> status --porcelain
124
+ git -C <each repo> log @{u}..HEAD --oneline
125
+ ```
126
+
127
+ Where [agent-sync](https://github.com/appvillis-com/agent-sync) is installed,
128
+ `/agent-sync finish` runs exactly this plus *no lease left held*, and `--gates`
129
+ adds the project's own gate commands.
130
+
131
+ When it fails, the fix is two commands and **the second is the one that gets
132
+ forgotten**:
133
+
134
+ ```bash
135
+ git -C <submodule> push
136
+ git add <submodule> && git commit -m "chore: bump <name> submodule — <why>"
137
+ ```
138
+
139
+ A REQ whose evidence lives in an unpushed commit, or in a submodule the parent
140
+ doesn't point at yet, is `partial` — the evidence is not reachable by anyone but
141
+ you.
142
+
107
143
  ## The closing question
108
144
 
109
145
  The table is preparation. The stage exists for the question that follows it, asked
@@ -134,7 +170,10 @@ All of:
134
170
  ledger or here) and, for `deferred`, a tracker entry.
135
171
  7. **No carry-over row is left `unresolved`** — every one has a home, and the
136
172
  ledger's counts are printed with this verdict, not just filed.
137
- 8. **The operator answers the closing question** and signs off.
173
+ 8. **Every repository is closed, the parent included** `git submodule status`
174
+ shows no `+`, and each repo is clean and pushed. A submodule is finished when
175
+ its parent points at it.
176
+ 9. **The operator answers the closing question** and signs off.
138
177
 
139
178
  Manual by design. An automated check can prove the table is *well-formed*; only
140
179
  the person who asked can confirm it is *what they asked for*. Do not let a green
@@ -300,8 +300,22 @@ stages/agents/types (see SKILL.md → *Bring your own skills*).
300
300
  you asked for, here's what shipped, here's what's deferred and where it lives —
301
301
  what's missing?* Ask it even when the table is green; the operator holds context
302
302
  the brief never captured, and this is the cheapest moment in the run to hear it.
303
+ - **Several repositories: a submodule is finished when its parent says so.** A
304
+ parent records each submodule as a **pointer to one commit**, and moving the
305
+ submodule does not move the pointer — so the work is committed, pushed, CI green
306
+ and done in its own roadmap, while a clone of the parent still gets the commit
307
+ before it. Neither repo looks wrong alone; the disagreement lives between them,
308
+ which is why it survives every check that runs inside one. Before closing, this
309
+ reports nothing **for the parent as well as every submodule**:
310
+ `git submodule status` (no line starting `+`), plus `git -C <repo> status
311
+ --porcelain` and `git -C <repo> log @{u}..HEAD --oneline` per repo. With
312
+ [agent-sync](https://github.com/appvillis-com/agent-sync) installed,
313
+ `/agent-sync finish` runs exactly that. The fix is two commands and the second is
314
+ the forgotten one: `git -C <submodule> push`, then
315
+ `git add <submodule> && git commit`.
303
316
  - **GATE (manual):** the ladder walk ran and its absences became REQ rows before
304
- the table was written; **every check this gate leans on has been seen failing
317
+ the table was written; **every repository is closed the parent included:
318
+ `git submodule status` shows no `+`, each repo clean and pushed**; **every check this gate leans on has been seen failing
305
319
  once against a planted defect** (an unproven check's green is not evidence);
306
320
  every REQ has a status (none `unknown`); every `verified`
307
321
  carries evidence; every `partial` names what's missing and where it's tracked;