task-pipeline-skill 1.38.0 → 1.40.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 +171 -0
- package/CONTRIBUTING.md +1 -1
- package/SKILL-CARD.md +1 -1
- package/cursor/rules/task-pipeline.mdc +11 -5
- package/evals/routing/RESULTS.md +79 -0
- package/evals/routing/render.py +130 -0
- package/evals/task-pipeline.evals.json +86 -0
- package/package.json +1 -1
- package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +7 -2
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +19 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/continuity.md +73 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +9 -1
- package/plugins/task-pipeline/skills/task-pipeline/templates/routing-rule.md +22 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,176 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.40.0 — the loop had a cadence and no queue
|
|
4
|
+
|
|
5
|
+
`run.loop` said how **often** to continue. It never said **what the next item is**, so an
|
|
6
|
+
armed mode still left the run choosing its next move by recollection — `learned.md` rule
|
|
7
|
+
16, once per fire. And nothing scheduled the next turn at all: on 2026-08-10 a run of
|
|
8
|
+
this pipeline wrote *«продолжаю без остановки»* and the turn ended, because a sentence
|
|
9
|
+
about future behaviour is not a wakeup. That run is this release's occasion and its
|
|
10
|
+
evidence.
|
|
11
|
+
|
|
12
|
+
### The queue is stage 2's
|
|
13
|
+
|
|
14
|
+
The **module map** when the brief was a platform, the plan's task list otherwise. Both
|
|
15
|
+
already existed, both were already ordered, and neither had ever been named as the thing
|
|
16
|
+
the loop walks. `run.loop.queue` names it; `run.loop.arm` says where the mode is armed,
|
|
17
|
+
and the default for a queue-bearing run is **after decomposition** — arming at preflight
|
|
18
|
+
arms a loop with nothing to walk.
|
|
19
|
+
|
|
20
|
+
Arming is a consequence, not a request, for the same reason the mode is recorded rather
|
|
21
|
+
than asked for: a capability the operator must remember to switch on is one they forget
|
|
22
|
+
on exactly the run that needed it.
|
|
23
|
+
|
|
24
|
+
**What arming does not change is stated where it could be missed.** The four stops are
|
|
25
|
+
the four stops; a `manual` gate still waits; an outward act still needs its own specific
|
|
26
|
+
authorization. *A generic flag is not a specific authorization*, and arming a queue is
|
|
27
|
+
the most generic flag there is — guarded, because that sentence is what the deploy floor
|
|
28
|
+
rests on.
|
|
29
|
+
|
|
30
|
+
### `mode: dynamic`
|
|
31
|
+
|
|
32
|
+
`interval` was the only mode while a fixed tick was the only primitive. A harness that
|
|
33
|
+
can schedule its own next turn picks each delay from what it is waiting for, and **prints
|
|
34
|
+
the delay it chose** — the disclosure that replaces an interval run's job id. A run
|
|
35
|
+
silent about its pacing cannot be told apart from one that quietly stopped, which is the
|
|
36
|
+
claim this file already forbade for harnesses with no primitive at all.
|
|
37
|
+
|
|
38
|
+
### The goal is re-read between items, not only the board
|
|
39
|
+
|
|
40
|
+
Each iteration already re-measured the work-list, which answers *what is open*. It did
|
|
41
|
+
not answer *whether the open thing still serves what this run was for*. A queue built at
|
|
42
|
+
stage 2 outlives the reason it was built, because the operator learns things between
|
|
43
|
+
items and says so. So the bottom of an iteration now quotes the goal, states whether the
|
|
44
|
+
next item still serves it, and re-orders or re-scopes when it does not — a row that stops
|
|
45
|
+
serving the goal leaves for the board with its reason.
|
|
46
|
+
|
|
47
|
+
A queue re-derived only by `age` and `sev` is honest about priority and silent about
|
|
48
|
+
purpose. Both numbers can be right while the run finishes something the operator stopped
|
|
49
|
+
wanting two items ago.
|
|
50
|
+
|
|
51
|
+
### The reader found the contradiction, not the bug
|
|
52
|
+
|
|
53
|
+
R-005's reader defeated all five guards and then read the doctrine as a reader rather
|
|
54
|
+
than its author. Part 1a said *"arming is a consequence, not a request"* and stated its
|
|
55
|
+
trigger as a fact about the **work** — a queue with more than one item — with no
|
|
56
|
+
antecedent about configuration. Two sections above, the same file says **Default off.
|
|
57
|
+
Silence arms nothing, exactly as silence authorizes no deploy**, and `grill.md`'s deploy
|
|
58
|
+
floor is explicitly said to rest on that distinction. Read cold, Part 1a arms a loop in a
|
|
59
|
+
project with no `pipeline.json` at all.
|
|
60
|
+
|
|
61
|
+
The contradiction was in the phrasing, not the intent — `stages.md` had already restated
|
|
62
|
+
the rule with the antecedent intact. **Part 1a gave way**, and a guard now requires it to
|
|
63
|
+
state its precondition, because five guards checked for the presence of strings and not
|
|
64
|
+
one would have noticed either reading.
|
|
65
|
+
|
|
66
|
+
What the reader took apart in the guards, all verified by planting the defect and
|
|
67
|
+
watching `PASS`:
|
|
68
|
+
|
|
69
|
+
- **a deleted contract was a skip, not a failure.** Removing the whole `run.loop` block
|
|
70
|
+
left CI green with two tidy `unlooked` lines — and `run` allows additional properties,
|
|
71
|
+
so the example still conformed while meaning nothing;
|
|
72
|
+
- **`_loop_block` searched instead of addressing.** A deprecated top-level `loop` earlier
|
|
73
|
+
in file order answered for the real contract;
|
|
74
|
+
- **presence tests let the release's own thesis be reverted.** `arm` existed, so setting
|
|
75
|
+
the example back to `preflight` passed;
|
|
76
|
+
- **`if _qv and …` short-circuited itself** — an open string in place of the queue enum
|
|
77
|
+
passed, which is precisely the failure the guard was written for;
|
|
78
|
+
- **the floor guard had never tested its own rule.** Its phrase entered the file in
|
|
79
|
+
v1.11.0, twenty-nine releases earlier; both doctrinal statements could be deleted and a
|
|
80
|
+
Rationalizations row kept it green;
|
|
81
|
+
- **the dynamic-disclosure regex was content-blind** — a sentence keeping the words and
|
|
82
|
+
inverting the obligation passed — and it triggered off the prose word, so renaming the
|
|
83
|
+
mode switched the guard off;
|
|
84
|
+
- **`arm the mode` matched a bullet forbidding arming**, and `"loop"` matched any
|
|
85
|
+
sentence about any loop. Both now key on the schema's own tokens.
|
|
86
|
+
|
|
87
|
+
Guards 233 → **248**: seven more probes than the first pass shipped with, six of them for
|
|
88
|
+
fail sites that did not exist until the reader's findings were fixed.
|
|
89
|
+
|
|
90
|
+
### Also
|
|
91
|
+
|
|
92
|
+
- **A guard was listing the legal modes instead of reading them.** Adding `dynamic`
|
|
93
|
+
failed the guard on a correct example — a check enforcing its own staleness. It now
|
|
94
|
+
reads the enum out of the schema.
|
|
95
|
+
- Guards: 233 → **248**, one per new fail site, each with its planted defect.
|
|
96
|
+
|
|
97
|
+
## v1.39.0 — the skill could not be reached by the word "audit"
|
|
98
|
+
|
|
99
|
+
`references/audit.md` has said since v0.1.0 that an audit may be **the whole task** —
|
|
100
|
+
stages 3–5 producing findings and fixes instead of a feature. No routing surface named
|
|
101
|
+
it. Every trigger noun was build-shaped, and the exclusion clause read *"Not for:
|
|
102
|
+
answering a question, explaining or reading code"* — which is the opening move of an
|
|
103
|
+
audit, a bug hunt, a production check and a PR review alike.
|
|
104
|
+
|
|
105
|
+
### Measured, not supposed
|
|
106
|
+
|
|
107
|
+
Ten routing queries, one fresh agent each, holding nothing but the competing skill
|
|
108
|
+
descriptions and one user sentence (`evals/routing/render.py`, results in
|
|
109
|
+
`evals/routing/RESULTS.md`). **7 / 10 before.** The three misses were the three the board
|
|
110
|
+
row predicted, and none of them was a failure to find a match — each agent **quoted this
|
|
111
|
+
skill's own exclusion clause back as the reason it refused**:
|
|
112
|
+
|
|
113
|
+
- «проверь, нет ли ошибок в обработчике вебхуков» → `none`
|
|
114
|
+
- «проверь, всё ли живо в проде после вчерашнего релиза» → `none`
|
|
115
|
+
- «посмотри PR #24 и скажи, что там не так» → `none`
|
|
116
|
+
|
|
117
|
+
A fourth result only the reasoning shows: «сделай аудит модуля оплат» *did* route, and
|
|
118
|
+
justified it by stretching the build verb `hardening`. A right answer resting on a
|
|
119
|
+
stretch is one rewording from a miss.
|
|
120
|
+
|
|
121
|
+
### The boundary is what a request ends in
|
|
122
|
+
|
|
123
|
+
Not whether it reads. An answer stops in the conversation; a change **or a finding**
|
|
124
|
+
lands in the tree, and the pipeline is what carries REQ rows, board rows and fixes
|
|
125
|
+
there. The description, the portable routing rule and the Cursor rule now all say so,
|
|
126
|
+
and `reading` is gone from the exclusions — guarded, so it cannot come back quietly.
|
|
127
|
+
|
|
128
|
+
### Two things the harvest found before the first grill question
|
|
129
|
+
|
|
130
|
+
- **`перевести` was locked into the verb list by the v1.9.0 design and never shipped.**
|
|
131
|
+
It existed in exactly one place in this repository: the design that locked it.
|
|
132
|
+
- **`REQ-003` was accepted `verified` anyway.** The evidence recorded was the clause's
|
|
133
|
+
*shape* and its character count — neither of which can see a missing member of the
|
|
134
|
+
list the REQ locked. An L1→L2 absence that passed an L5 check. A guard now reads the
|
|
135
|
+
locked list out of that design and compares it to the shipped surface, so the next
|
|
136
|
+
dropped verb is a failure rather than a week.
|
|
137
|
+
|
|
138
|
+
### The reader earned its standing instruction
|
|
139
|
+
|
|
140
|
+
R-005 exists because an author's probes only exercise the shapes the author already
|
|
141
|
+
thought of. Dispatched on the nine new guards, an independent reader defeated them
|
|
142
|
+
**fifteen ways**, each verified by planting the text and watching the validator still
|
|
143
|
+
print `PASS`. The three worth naming:
|
|
144
|
+
|
|
145
|
+
- **A presence test over a whole file proves a word exists, not that the rule says it.**
|
|
146
|
+
The reader deleted the entire boundary clause from the Cursor rule, put the old
|
|
147
|
+
exclusion back, added one unrelated sentence elsewhere containing the same four class
|
|
148
|
+
names — and the guard passed. Both cross-surface checks are now scoped to the
|
|
149
|
+
`## Routing` section.
|
|
150
|
+
- **A `should_not_trigger` control counted as coverage.** The eval-coverage guard joined
|
|
151
|
+
every query regardless of category, so deleting all four findings evals and mentioning
|
|
152
|
+
the words in one negative control certified *named and untested* — the exact state it
|
|
153
|
+
cites `B-046` for — as green. It now reads `should_trigger` queries only.
|
|
154
|
+
- **The anti-dormancy sentinel was itself one synonym from dormant**, and the
|
|
155
|
+
locked-verb guard read a regex out of a document this repo forbids maintaining, so
|
|
156
|
+
relabelling one heading in a superseded design would have silenced the check that
|
|
157
|
+
exists because a verb was silently dropped. Both now fail loudly instead.
|
|
158
|
+
|
|
159
|
+
Nine of the fifteen were in checks written that same hour. The tenth fail site had no
|
|
160
|
+
probe at all — ten branches, nine tests — which is the invariant the repository states
|
|
161
|
+
about itself and did not keep.
|
|
162
|
+
|
|
163
|
+
### Also
|
|
164
|
+
|
|
165
|
+
- **False-positive controls, because widening a vocabulary can steal work.** Three
|
|
166
|
+
competitors already claim the word *audit* — `seo-aeo-audit`, `ux-audit`,
|
|
167
|
+
`make-skill`. All three are eval cases now, and all three still won their query.
|
|
168
|
+
- **R-003 sweep** turned up the same word-map ceiling that produced R-006, in the
|
|
169
|
+
redaction-rule count: past ten it compared against the digit alone. It now accepts
|
|
170
|
+
either form and names both when it fails.
|
|
171
|
+
- Guards: 218 → **233**; eval cases 21 → **28**; description 956 → 1004 of 1024, paid for
|
|
172
|
+
by cutting mechanism prose that could not affect routing.
|
|
173
|
+
|
|
3
174
|
## v1.38.0 — the wall came down, and a green started meaning something
|
|
4
175
|
|
|
5
176
|
An audit of this skill measured nineteen problems and asked one question of all of them:
|
package/CONTRIBUTING.md
CHANGED
|
@@ -191,7 +191,7 @@ reads those files, and a partial read shows whichever sections come first.
|
|
|
191
191
|
`run` block in `pipeline.schema.json` carries the loop mode; the shipped example
|
|
192
192
|
sets `run.loop.mode` explicitly rather than omitting it, because the example is
|
|
193
193
|
what gets copied and an absent field reads as an oversight instead of a decision.
|
|
194
|
-
*(guard: `
|
|
194
|
+
*(guard: `run.loop.mode is` … `and the schema's legal set is`)*
|
|
195
195
|
|
|
196
196
|
**25. The run-wide mode is named by every stage that could be misled by it.**
|
|
197
197
|
`SKILL.md`, `references/grill.md`, `references/build.md`, `references/stages.md`
|
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.
|
|
15
|
+
| **Version** | 1.40.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` |
|
|
@@ -605,11 +605,17 @@ track that ran look identical afterwards.
|
|
|
605
605
|
|
|
606
606
|
## Routing — when this applies at all
|
|
607
607
|
|
|
608
|
-
**Work that
|
|
609
|
-
refactor, migration, integration, rewrite, adoption or
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
608
|
+
**Work that LANDS IN THE REPOSITORY goes through the pipeline**, in two shapes: a
|
|
609
|
+
**change** — a feature, fix, refactor, migration, integration, rewrite, adoption or
|
|
610
|
+
hardening pass — and a **finding that lands** — an audit, a bug hunt, a production
|
|
611
|
+
check, a PR review, whose output is REQ rows, board rows and fixes rather than an
|
|
612
|
+
answer in the chat. In any phrasing.
|
|
613
|
+
|
|
614
|
+
**Not** for a question, an explanation, mapping code for a person to read, a typo or a
|
|
615
|
+
one-line edit. **Reading is not the test**: an audit, a bug hunt and a PR review all
|
|
616
|
+
begin by reading and all three end in the tree. The operator opts out of a qualifying
|
|
617
|
+
task by saying "без пайплайна" or "quick"; when they do, say out loud that the cycle
|
|
618
|
+
was skipped at their request.
|
|
613
619
|
|
|
614
620
|
A borderline case is named, not silently chosen: state which route you are taking and
|
|
615
621
|
why, in one line.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Routing measurement — results
|
|
2
|
+
|
|
3
|
+
Method: `evals/routing/render.py`. Ten queries, **one fresh agent per query**, holding
|
|
4
|
+
nothing but the competing skill descriptions and one user sentence.
|
|
5
|
+
|
|
6
|
+
> **Limits, stated before the numbers.** This measures the **descriptions'**
|
|
7
|
+
> discriminating power, not Claude Code's selection machinery, which sees more than a
|
|
8
|
+
> description. A subagent is fresh in *context*, not blind in *disposition* — this is
|
|
9
|
+
> **not** the multi-model blind run board row `B-002` asks for and no line here may be
|
|
10
|
+
> quoted as one. And see §3: **one sample per cell is too few**, which this measurement
|
|
11
|
+
> found out about itself by being run twice.
|
|
12
|
+
|
|
13
|
+
## 1. The three runs
|
|
14
|
+
|
|
15
|
+
| id | query | expected | **A** before | **B** after | **C** after + prod pair |
|
|
16
|
+
|---|---|---|---|---|---|
|
|
17
|
+
| AUD-01 | сделай аудит модуля оплат | task-pipeline | ✓ *(stretch)* | ✓ | ✓ |
|
|
18
|
+
| BUG-01 | проверь, нет ли ошибок в обработчике вебхуков | task-pipeline | **none** | ✓ | ✓ |
|
|
19
|
+
| PRD-01 | проверь, всё ли живо в проде после вчерашнего релиза | task-pipeline | **none** | **none** | **none** |
|
|
20
|
+
| PRR-01 | посмотри PR #24 и скажи, что там не так | task-pipeline | **none** | ✓ | ✓ |
|
|
21
|
+
| FEA-01 | добавь экспорт в CSV на страницу отчётов | task-pipeline | ✓ | ✓ | **none** |
|
|
22
|
+
| QST-01 | объясни, как наш auth middleware решает… | none | ✓ | ✓ | ✓ |
|
|
23
|
+
| TYP-01 | поправь опечатку в заголовке README | none | ✓ | ✓ | ✓ |
|
|
24
|
+
| SEO-01 | сделай аудит лендинга — почему упал трафик | seo-aeo-audit | ✓ | ✓ | ✓ |
|
|
25
|
+
| UXA-01 | проверь, что код соответствует UX-сценариям | ux-audit | ✓ | ✓ | ✓ |
|
|
26
|
+
| SKL-01 | проверь, соответствует ли этот скил стандарту | make-skill | ✓ | ✓ | ✓ |
|
|
27
|
+
| | | | **7 / 10** | **9 / 10** | **8 / 10** |
|
|
28
|
+
|
|
29
|
+
- **A** — `9f67dcd`, description 956/1024, before any edit.
|
|
30
|
+
- **B** — the findings clause added, description 1004/1024.
|
|
31
|
+
- **C** — `production check` given its Russian pair, description 1008/1024.
|
|
32
|
+
|
|
33
|
+
## 2. What is robust, and what is one sample
|
|
34
|
+
|
|
35
|
+
**Robust — the same result in both after-runs, for the stated reason:**
|
|
36
|
+
|
|
37
|
+
- **BUG-01 and PRR-01: `none` → `task-pipeline`, 2/2.** In A both agents *quoted this
|
|
38
|
+
skill's own exclusion clause* as the reason they refused — *"matching task-pipeline's
|
|
39
|
+
own exclusion 'Not for: … explaining or reading code'"*, *"task-pipeline's is scoped to
|
|
40
|
+
'work changes the repository', not 'explaining or reading code'"*. In B and C both
|
|
41
|
+
quote the new clause instead. The mechanism named in the refusal is the mechanism the
|
|
42
|
+
change removed.
|
|
43
|
+
- **AUD-01's *reason* changed, 2/2.** It routed in A too — by stretching the build verb
|
|
44
|
+
`hardening`, with nothing in the description naming an audit. In B and C it quotes
|
|
45
|
+
`audit/аудит`. The answer did not move; the ground under it did.
|
|
46
|
+
- **PRD-01 never routed: 0/2 after.** Adding `/проверь прод` did not fix it. In C the
|
|
47
|
+
agent said the request has *"no repository change and no matching trigger"* and
|
|
48
|
+
reached for `checkup` before rejecting it. The production-check class is **named and
|
|
49
|
+
still unreachable** — the vocabulary was necessary and is not sufficient.
|
|
50
|
+
|
|
51
|
+
**One sample, and therefore not a result:**
|
|
52
|
+
|
|
53
|
+
- **FEA-01 flipped to `none` in C** on reasoning that does not parse — *"no such change
|
|
54
|
+
applies here because none of the listed skills is task-pipeline's own trigger"*. This
|
|
55
|
+
is a control that passed in A and B with the same description clause C did not touch.
|
|
56
|
+
Read as run-to-run variance, **not** as a regression caused by the edit — and the
|
|
57
|
+
measurement as built cannot prove that reading, which is the point of §3.
|
|
58
|
+
|
|
59
|
+
## 3. The measurement's own defect, found by running it twice
|
|
60
|
+
|
|
61
|
+
**One agent per query cannot separate an effect from noise.** Three runs produced three
|
|
62
|
+
totals — 7, 9, 8 — and one of the moves between them (FEA-01) is almost certainly the
|
|
63
|
+
sampler, not the surface. Had the run stopped at B, this file would have reported
|
|
64
|
+
**9/10 and a clean win**, and the claim would have rested on single samples exactly like
|
|
65
|
+
the one that flipped.
|
|
66
|
+
|
|
67
|
+
What a next version needs, in order of value:
|
|
68
|
+
|
|
69
|
+
1. **N ≥ 3 per cell, majority-scored**, so a flip is visible as a split rather than a
|
|
70
|
+
result. Cost scales linearly and the queries are tiny.
|
|
71
|
+
2. **The controls scored separately from the targets.** A false-positive control that
|
|
72
|
+
flips is a different fact from a target that does not move.
|
|
73
|
+
3. **The reason recorded, not only the answer.** Every real finding in this file came
|
|
74
|
+
from *why* an agent chose, not from what it chose — AUD-01 passed in A for a reason
|
|
75
|
+
that was one rewording from failing.
|
|
76
|
+
|
|
77
|
+
Filed as a board row rather than fixed here: the run that needed the harness is the run
|
|
78
|
+
that found the harness too thin, and rebuilding it mid-run would leave the before-numbers
|
|
79
|
+
measured by a different instrument than the after-numbers.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render the routing-measurement prompt for one query.
|
|
3
|
+
|
|
4
|
+
The measurement asks a fresh agent, holding nothing but competing skill
|
|
5
|
+
descriptions and one user sentence, which skill it would use. Run before a
|
|
6
|
+
description change and after it; the only thing that may differ between the two
|
|
7
|
+
runs is the text of the descriptions themselves — this file renders everything
|
|
8
|
+
else identically, which is the whole reason it is a file and not a paste.
|
|
9
|
+
|
|
10
|
+
python3 evals/routing/render.py <query-id> # prompt to stdout
|
|
11
|
+
python3 evals/routing/render.py --list # the queries and expectations
|
|
12
|
+
|
|
13
|
+
Honest limits, stated here because a reader of the results will not see them:
|
|
14
|
+
* this measures the DESCRIPTIONS' discriminating power, not Claude Code's own
|
|
15
|
+
selection machinery, which sees more than a description;
|
|
16
|
+
* a subagent carries its host's system prompt, so it is fresh in CONTEXT, not
|
|
17
|
+
in disposition — this is not the blind multi-model run board row B-002 asks
|
|
18
|
+
for, and no result here may be quoted as one.
|
|
19
|
+
"""
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import re
|
|
23
|
+
import subprocess
|
|
24
|
+
import sys
|
|
25
|
+
|
|
26
|
+
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
27
|
+
|
|
28
|
+
# task-pipeline's description is read from the WORKING TREE, so the after-run
|
|
29
|
+
# picks up the edit; the competitors are read from the installed plugins, so an
|
|
30
|
+
# unrelated release of theirs cannot silently move the baseline between runs.
|
|
31
|
+
SELF = os.path.join(ROOT, "plugins/task-pipeline/skills/task-pipeline/SKILL.md")
|
|
32
|
+
MARKETPLACES = os.path.expanduser("~/.claude/plugins/marketplaces")
|
|
33
|
+
COMPETITORS = ["super-ux", "sheleg-design", "make-skill", "agent-sync", "seo-aeo-audit"]
|
|
34
|
+
|
|
35
|
+
QUERIES = [
|
|
36
|
+
# id, the sentence, what should win, why it is here
|
|
37
|
+
("AUD-01", "сделай аудит модуля оплат", "task-pipeline",
|
|
38
|
+
"findings on code — audit.md:350 says an audit may be the whole task"),
|
|
39
|
+
("BUG-01", "проверь, нет ли ошибок в обработчике вебхуков", "task-pipeline",
|
|
40
|
+
"bug hunt — findings that land in the repo"),
|
|
41
|
+
("PRD-01", "проверь, всё ли живо в проде после вчерашнего релиза", "task-pipeline",
|
|
42
|
+
"production check"),
|
|
43
|
+
("PRR-01", "посмотри PR #24 и скажи, что там не так", "task-pipeline",
|
|
44
|
+
"PR review — review.md owns the reader contract"),
|
|
45
|
+
("FEA-01", "добавь экспорт в CSV на страницу отчётов", "task-pipeline",
|
|
46
|
+
"CONTROL+ — build work already routes; must not regress"),
|
|
47
|
+
("QST-01", "объясни, как наш auth middleware решает, какие роуты публичные", "none",
|
|
48
|
+
"CONTROL− — a question; the exclusion must survive the rewording"),
|
|
49
|
+
("TYP-01", "поправь опечатку в заголовке README", "none",
|
|
50
|
+
"CONTROL− — a one-line edit; the exclusion must survive"),
|
|
51
|
+
("SEO-01", "сделай аудит лендинга — почему упал трафик из поиска", "seo-aeo-audit",
|
|
52
|
+
"FALSE-POSITIVE test — 'аудит' must not be stolen from its owner"),
|
|
53
|
+
("UXA-01", "проверь, что код соответствует UX-сценариям", "ux-audit",
|
|
54
|
+
"FALSE-POSITIVE test — the scenario audit has an owner"),
|
|
55
|
+
("SKL-01", "проверь, соответствует ли этот скил стандарту", "make-skill",
|
|
56
|
+
"FALSE-POSITIVE test — skill-shaped audit has an owner"),
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _desc(path):
|
|
61
|
+
"""The description as the platform reads it: the frontmatter field, unwrapped."""
|
|
62
|
+
t = open(path, encoding="utf-8").read()
|
|
63
|
+
n = re.search(r"^name:\s*(.+)$", t, re.M)
|
|
64
|
+
d = re.search(r"^description:\s*(.+?)(?=^[a-z_-]+:\s|^---)", t, re.M | re.S)
|
|
65
|
+
if not (n and d):
|
|
66
|
+
return None
|
|
67
|
+
return n.group(1).strip(), " ".join(d.group(1).split()).strip("\"' ")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def choices():
|
|
71
|
+
out = []
|
|
72
|
+
s = _desc(SELF)
|
|
73
|
+
if s:
|
|
74
|
+
out.append(s)
|
|
75
|
+
for m in COMPETITORS:
|
|
76
|
+
base = os.path.join(MARKETPLACES, m)
|
|
77
|
+
if not os.path.isdir(base):
|
|
78
|
+
continue
|
|
79
|
+
found = subprocess.run(["find", base, "-name", "SKILL.md"],
|
|
80
|
+
capture_output=True, text=True).stdout.split()
|
|
81
|
+
for f in sorted(found):
|
|
82
|
+
got = _desc(f)
|
|
83
|
+
if got and got[0] not in {o[0] for o in out}:
|
|
84
|
+
out.append(got)
|
|
85
|
+
return out
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
PROMPT = """You are choosing which skill, if any, to invoke for one user request.
|
|
89
|
+
|
|
90
|
+
Below are the skills available, each with the description its author wrote. You have
|
|
91
|
+
nothing else — no repository, no history, no knowledge of these tools beyond what is
|
|
92
|
+
written here.
|
|
93
|
+
|
|
94
|
+
{catalogue}
|
|
95
|
+
|
|
96
|
+
The user says:
|
|
97
|
+
|
|
98
|
+
{query}
|
|
99
|
+
|
|
100
|
+
Answer with exactly two lines and nothing else:
|
|
101
|
+
|
|
102
|
+
SKILL: <the skill name, or the word none>
|
|
103
|
+
WHY: <one sentence, quoting the phrase in the description that decided it>
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def main():
|
|
108
|
+
if "--list" in sys.argv:
|
|
109
|
+
for qid, q, exp, why in QUERIES:
|
|
110
|
+
print(f"{qid:<8}{exp:<16}{q}\n{'':8}{'':16}# {why}")
|
|
111
|
+
return 0
|
|
112
|
+
if "--choices" in sys.argv:
|
|
113
|
+
for name, d in choices():
|
|
114
|
+
print(f"{name:<18}{len(d):>5} chars")
|
|
115
|
+
return 0
|
|
116
|
+
if len(sys.argv) < 2:
|
|
117
|
+
print(__doc__)
|
|
118
|
+
return 2
|
|
119
|
+
qid = sys.argv[1]
|
|
120
|
+
row = next((r for r in QUERIES if r[0] == qid), None)
|
|
121
|
+
if row is None:
|
|
122
|
+
print(f"unknown query id {qid}; --list to see them", file=sys.stderr)
|
|
123
|
+
return 2
|
|
124
|
+
cat = "\n".join(f"- **{name}** — {d}" for name, d in choices())
|
|
125
|
+
print(PROMPT.format(catalogue=cat, query=row[1]))
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
if __name__ == "__main__":
|
|
130
|
+
raise SystemExit(main())
|
|
@@ -307,6 +307,92 @@
|
|
|
307
307
|
"Does NOT invoke task-pipeline, does not open a grill and does not write a brief"
|
|
308
308
|
],
|
|
309
309
|
"why": "Answering a question is on the skill's own not-for list. The description carries that boundary and nothing has ever measured whether a model honours it."
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"id": "TRIG-07",
|
|
313
|
+
"category": "should_trigger",
|
|
314
|
+
"skills": [
|
|
315
|
+
"task-pipeline"
|
|
316
|
+
],
|
|
317
|
+
"query": "сделай аудит модуля оплат",
|
|
318
|
+
"expected_behavior": [
|
|
319
|
+
"Invokes the skill: the output is a finding that lands in the repository, which the description names",
|
|
320
|
+
"Runs audit.md's L0->L7 ladder as the whole task — stages 3-5 produce findings and fixes rather than a feature",
|
|
321
|
+
"Does NOT justify the routing by stretching a build verb such as 'hardening'"
|
|
322
|
+
],
|
|
323
|
+
"why": "Measured 2026-08-10: before the findings clause this routed, but its stated reason was the build-verb list. A right answer resting on a stretch is one rewording from a miss."
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"id": "TRIG-08",
|
|
327
|
+
"category": "should_trigger",
|
|
328
|
+
"skills": [
|
|
329
|
+
"task-pipeline"
|
|
330
|
+
],
|
|
331
|
+
"query": "проверь, нет ли ошибок в обработчике вебхуков",
|
|
332
|
+
"expected_behavior": [
|
|
333
|
+
"Invokes the skill rather than answering in the chat",
|
|
334
|
+
"Findings become REQ or board rows with file:line and a minimal fix, ordered by seam"
|
|
335
|
+
],
|
|
336
|
+
"why": "Measured before the change: answered `none`, quoting the exclusion clause 'explaining or reading code'. Reading is the opening move, not the boundary."
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
"id": "TRIG-09",
|
|
340
|
+
"category": "should_trigger",
|
|
341
|
+
"skills": [
|
|
342
|
+
"task-pipeline"
|
|
343
|
+
],
|
|
344
|
+
"query": "проверь, всё ли живо в проде после вчерашнего релиза",
|
|
345
|
+
"expected_behavior": [
|
|
346
|
+
"Invokes the skill; the verification trio, not one of three",
|
|
347
|
+
"Reads the CI run and the platform's own log command rather than assuming a verdict"
|
|
348
|
+
],
|
|
349
|
+
"why": "Measured before the change: answered `none` on the same exclusion. A production check ends in a verification row, which lands."
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"id": "TRIG-10",
|
|
353
|
+
"category": "should_trigger",
|
|
354
|
+
"skills": [
|
|
355
|
+
"task-pipeline"
|
|
356
|
+
],
|
|
357
|
+
"query": "посмотри PR #24 и скажи, что там не так",
|
|
358
|
+
"expected_behavior": [
|
|
359
|
+
"Invokes the skill and applies review.md's reviewer contract, including the three reader states",
|
|
360
|
+
"Prints NO READER rather than staying silent when no independent reader is available"
|
|
361
|
+
],
|
|
362
|
+
"why": "Measured before the change: answered `none` because no description offered a phrase for reviewing a PR, while review.md owns exactly that contract."
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"id": "NOTRIG-06",
|
|
366
|
+
"category": "should_not_trigger",
|
|
367
|
+
"skills": [],
|
|
368
|
+
"query": "сделай аудит лендинга — почему упал трафик из поиска",
|
|
369
|
+
"expected_behavior": [
|
|
370
|
+
"Routes to seo-aeo-audit, whose description names 'почему упал трафик' verbatim",
|
|
371
|
+
"The widened findings vocabulary does not claim an audit that has an owner"
|
|
372
|
+
],
|
|
373
|
+
"why": "False-positive control. Widening 'audit' must not steal work: gates.md sets the false-positive budget at zero."
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"id": "NOTRIG-07",
|
|
377
|
+
"category": "should_not_trigger",
|
|
378
|
+
"skills": [],
|
|
379
|
+
"query": "проверь, что код соответствует UX-сценариям",
|
|
380
|
+
"expected_behavior": [
|
|
381
|
+
"Routes to ux-audit, which owns the scenario audit",
|
|
382
|
+
"The findings vocabulary in task-pipeline's description does not override an owner that names the surface exactly"
|
|
383
|
+
],
|
|
384
|
+
"why": "False-positive control against the second competing claim on the word audit."
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"id": "NOTRIG-08",
|
|
388
|
+
"category": "should_not_trigger",
|
|
389
|
+
"skills": [],
|
|
390
|
+
"query": "проверь, соответствует ли этот скил стандарту",
|
|
391
|
+
"expected_behavior": [
|
|
392
|
+
"Routes to make-skill, which owns skill-shaped audits",
|
|
393
|
+
"The findings vocabulary in task-pipeline's description does not override an owner that names the artefact exactly"
|
|
394
|
+
],
|
|
395
|
+
"why": "False-positive control against the third competing claim on the word audit."
|
|
310
396
|
}
|
|
311
397
|
]
|
|
312
398
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "task-pipeline-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.40.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.
|
|
5
|
+
"version": "1.40.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ssheleg",
|
|
8
8
|
"url": "https://x.com/sshlg93"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: task-pipeline
|
|
3
|
-
description: "Runs a substantial task through a full delivery pipeline: an intake grill that
|
|
3
|
+
description: "Runs a substantial task through a full delivery pipeline: an intake grill that locks the request into a brief, then docs study, brainstorm, spec, plan, subagent build, tests, lint/deploy, post-deploy check, docs/wiki sync and acceptance. Use when work changes the repository — a feature, fix, refactor, migration, integration, rewrite, adoption or hardening; фича, фикс, рефактор, миграция, интеграция, доработать, починить, внедрить, перевести — or when the output is a finding that lands in it: audit/аудит, bug hunt/проверь ошибки, production check/проверь прод, PR review/ревью PR — or on 'run this through the pipeline' / 'прогони по конвейеру', 'the full cycle' / 'полный цикл', /task-pipeline. Two modes need no task at all: 'checkup' / 'чекап' reports what has shipped without a person confirming it and what to look at first; 'setup' audits the documentation a project already has. Not for: answering a question, explaining code, a typo or a one-line edit — say 'без пайплайна' / 'quick' to opt out."
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
],
|
|
47
47
|
"gate": {
|
|
48
48
|
"type": "manual",
|
|
49
|
-
"check": "the user approves the design AND the UI verdict is recorded (does the task touch a user-facing surface — web/mobile/CLI/TUI? this arms the stage-3 UX track). Every REQ is answered by the design, or explicitly dropped by the operator into the carry-over ledger. For a platform (several independent capabilities or shippable surfaces): the module map specs/<topic>-modules.md is committed and approved — brick criteria met or excepted in writing, dependency graph acyclic, build order topological with the walking skeleton first, every REQ mapped to exactly one module, cross-module contracts named with their owner. Single-module work records 'single module: <name>' instead — a skipped decomposition is a recorded decision, never an omission"
|
|
49
|
+
"check": "the user approves the design AND the UI verdict is recorded (does the task touch a user-facing surface — web/mobile/CLI/TUI? this arms the stage-3 UX track). Every REQ is answered by the design, or explicitly dropped by the operator into the carry-over ledger. For a platform (several independent capabilities or shippable surfaces): the module map specs/<topic>-modules.md is committed and approved — brick criteria met or excepted in writing, dependency graph acyclic, build order topological with the walking skeleton first, every REQ mapped to exactly one module, cross-module contracts named with their owner. Single-module work records 'single module: <name>' instead — a skipped decomposition is a recorded decision, never an omission. THE LOOP'S ARMING STATE IS PRINTED — armed with its queue and pacing, or not armed with the reason; the queue exists at this stage and a loop armed earlier has nothing to walk."
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
{
|
|
@@ -172,7 +172,12 @@
|
|
|
172
172
|
],
|
|
173
173
|
"_run_note": "Run-wide pacing. Shipped explicitly OFF so this example DEMONSTRATES the default rather than relying on its absence — see references/continuity.md. Set mode to 'interval' and a project stops being asked whether to run item-by-item; it still stops at every manual gate. There is no context-budget field here on purpose: the threshold is not measurable, so it is unconditional doctrine, not config.",
|
|
174
174
|
"run": {
|
|
175
|
-
"loop": {
|
|
175
|
+
"loop": {
|
|
176
|
+
"mode": "dynamic",
|
|
177
|
+
"queue": "module-map",
|
|
178
|
+
"arm": "after-decomposition",
|
|
179
|
+
"command": "/loop"
|
|
180
|
+
}
|
|
176
181
|
},
|
|
177
182
|
"release": {
|
|
178
183
|
"enabled": true,
|
|
@@ -48,18 +48,34 @@
|
|
|
48
48
|
"mode": {
|
|
49
49
|
"enum": [
|
|
50
50
|
"off",
|
|
51
|
-
"interval"
|
|
51
|
+
"interval",
|
|
52
|
+
"dynamic"
|
|
52
53
|
],
|
|
53
|
-
"description": "off (the default when absent) = the run pauses between items as it always did. interval =
|
|
54
|
+
"description": "off (the default when absent) = the run pauses between items as it always did. interval = armed with the harness's fixed-tick primitive, one item per fire. dynamic = the harness schedules its own next turn and the run picks each delay from what it is waiting for; a wait on nothing is minutes, not a tick nobody chose. interval and dynamic stop at the same four things — a manual gate, an unresolvable block, a genuine ambiguity, completion — and neither collapses an outward act: a generic flag is not a specific authorization."
|
|
54
55
|
},
|
|
55
56
|
"interval": {
|
|
56
57
|
"type": "string",
|
|
57
58
|
"pattern": "^[0-9]+[smhd]$",
|
|
58
|
-
"description": "Required when mode is 'interval'. Must divide its unit cleanly (5m, 10m, 2h — not 7m or 90m); a value that does not is rounded to the nearest that does, and the rounding is stated out loud. Pick the shortest interval that is longer than a typical item."
|
|
59
|
+
"description": "Required when mode is 'interval', and meaningless when it is 'dynamic' — a self-pacing run chooses each delay. Must divide its unit cleanly (5m, 10m, 2h — not 7m or 90m); a value that does not is rounded to the nearest that does, and the rounding is stated out loud. Pick the shortest interval that is longer than a typical item."
|
|
59
60
|
},
|
|
60
61
|
"command": {
|
|
61
62
|
"type": "string",
|
|
62
63
|
"description": "How this harness arms it, e.g. '/loop'. Harness-specific and therefore project-recorded rather than assumed: on a harness with no loop primitive, omit it — the mode then degrades to prose discipline plus the build ledger, and the run says so instead of implying it is armed."
|
|
64
|
+
},
|
|
65
|
+
"queue": {
|
|
66
|
+
"enum": [
|
|
67
|
+
"module-map",
|
|
68
|
+
"plan-tasks",
|
|
69
|
+
"none"
|
|
70
|
+
],
|
|
71
|
+
"description": "What the loop walks. A loop with no queue is a timer: it says how often to continue and never what the next item is, so the run picks its next move by recollection. module-map = stage 2's decomposition (a platform); plan-tasks = stage 4's task list; none = the mode is armed for the stage boundaries only. Doctrine: references/continuity.md -> Part 1a."
|
|
72
|
+
},
|
|
73
|
+
"arm": {
|
|
74
|
+
"enum": [
|
|
75
|
+
"preflight",
|
|
76
|
+
"after-decomposition"
|
|
77
|
+
],
|
|
78
|
+
"description": "When the mode is armed. preflight = at the top of the run, the original behaviour. after-decomposition = at the close of stage 2, once the queue exists and has more than one item — the default for a queue-bearing run, because a capability the operator must remember to switch on is one they forget on the run that needed it. Arming never collapses a manual gate or an outward action."
|
|
63
79
|
}
|
|
64
80
|
}
|
|
65
81
|
},
|
|
@@ -11,6 +11,7 @@ almost no window left, loses the middle of it, and re-derives what it already di
|
|
|
11
11
|
|
|
12
12
|
- The limit, before the capability
|
|
13
13
|
- Part 1 — the loop
|
|
14
|
+
- Part 1a — the queue is stage 2's, and the loop arms on it
|
|
14
15
|
- Arming it on Claude Code
|
|
15
16
|
- Other harnesses, and honest degradation
|
|
16
17
|
- What one iteration means
|
|
@@ -62,6 +63,62 @@ the boundary between one agent turn and the next. This file is that reach.
|
|
|
62
63
|
is never asked again — which is the entire point. A mode that has to be requested
|
|
63
64
|
every run is not a mode, it is a habit the operator maintains by hand.
|
|
64
65
|
|
|
66
|
+
## Part 1a — the queue is stage 2's, and the loop arms on it
|
|
67
|
+
|
|
68
|
+
**A loop with no queue is a timer.** Until this section existed, `run.loop` said *how
|
|
69
|
+
often* to continue and never said *what the next item is*, so the mode could be armed
|
|
70
|
+
and still leave the run picking its next move by recollection — which is the failure
|
|
71
|
+
[`learned.md`](learned.md) rule 16 is about, running once per fire.
|
|
72
|
+
|
|
73
|
+
**The queue is the module map** ([`decomposition.md`](decomposition.md)) when the brief
|
|
74
|
+
was a platform, and the plan's task list otherwise. Both already exist and both are
|
|
75
|
+
already ordered; neither was ever named as the thing the loop walks.
|
|
76
|
+
|
|
77
|
+
**Arming is the execution of a recorded decision, not a fresh request.** Where the mode
|
|
78
|
+
is **recorded** and the queue has more than one item, the loop arms **at the close of
|
|
79
|
+
stage 2** and the run says so in one line. It is not asked for at that point, for the
|
|
80
|
+
same reason the mode is recorded rather than requested: re-asking would rebuild the habit
|
|
81
|
+
the config exists to retire.
|
|
82
|
+
|
|
83
|
+
**Where nothing is recorded, nothing arms.** Silence arms nothing here too — this section
|
|
84
|
+
moves *where* a recorded mode is armed, from preflight to the close of stage 2. It does
|
|
85
|
+
not make arming unconditional, and a reader who takes it that way would arm a loop in a
|
|
86
|
+
project with no `pipeline.json` at all. That reading was in this section's first draft;
|
|
87
|
+
an independent reader found it before it shipped.
|
|
88
|
+
|
|
89
|
+
**What arming does NOT change, and this is load-bearing:** the four stops are the four
|
|
90
|
+
stops. A `manual` gate still waits. An outward or irreversible act still needs its own
|
|
91
|
+
specific authorization — *a generic flag is not a specific authorization*, and arming a
|
|
92
|
+
queue is the most generic flag there is. Arming decides only that the run does not stop
|
|
93
|
+
to ask *"shall I take the next one?"*.
|
|
94
|
+
|
|
95
|
+
**`mode: dynamic` — when the harness paces itself.** `interval` was the only mode while
|
|
96
|
+
the only primitive was a fixed tick. A harness that can schedule its own next turn
|
|
97
|
+
should: the run picks the delay from what it is waiting for, and a wait on nothing is a
|
|
98
|
+
wait of minutes rather than a fixed tick nobody chose. On a harness with neither
|
|
99
|
+
primitive the mode degrades to prose discipline plus the build ledger, and **the run
|
|
100
|
+
says which one it is running** — the rule below about claiming a capability you do not
|
|
101
|
+
have is unchanged and applies to `dynamic` first.
|
|
102
|
+
|
|
103
|
+
### The goal is re-read between items, not only the board
|
|
104
|
+
|
|
105
|
+
Each iteration already re-measures the work-list. That answers *what is open*. It does
|
|
106
|
+
not answer *whether the open thing still serves what this run was for* — and a queue
|
|
107
|
+
built at stage 2 outlives the reason it was built, because the operator learns things
|
|
108
|
+
between items and says so.
|
|
109
|
+
|
|
110
|
+
So at the bottom of an iteration, beside the re-derived `prio`:
|
|
111
|
+
|
|
112
|
+
1. re-read the brief's goal — one line, quoted, not recalled;
|
|
113
|
+
2. state whether the next item still serves it;
|
|
114
|
+
3. if it does not, **re-order or re-scope the queue and say what moved and why.** A row
|
|
115
|
+
that stops serving the goal leaves for the board with its reason
|
|
116
|
+
([`backlog.md`](backlog.md)), it does not get worked because it was next.
|
|
117
|
+
|
|
118
|
+
A queue re-derived only by `age` and `sev` is a queue that is honest about priority and
|
|
119
|
+
silent about purpose. Both numbers can be right while the run is finishing something the
|
|
120
|
+
operator stopped wanting two items ago.
|
|
121
|
+
|
|
65
122
|
## Arming it on Claude Code
|
|
66
123
|
|
|
67
124
|
```
|
|
@@ -85,10 +142,22 @@ silent failures otherwise:
|
|
|
85
142
|
stops on day eight is worse than one that was never armed, because the operator
|
|
86
143
|
believes work is still moving.
|
|
87
144
|
|
|
88
|
-
Where `run.loop.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
145
|
+
**Where it arms is `run.loop.arm`,** and there are two points because there are two
|
|
146
|
+
kinds of run:
|
|
147
|
+
|
|
148
|
+
- `preflight` — the top of the run. Right when the loop walks stage boundaries rather
|
|
149
|
+
than a list, and the only option before Part 1a existed.
|
|
150
|
+
- `after-decomposition` — the close of stage 2, once the queue exists and holds more
|
|
151
|
+
than one item. Arming earlier would arm a loop with nothing to walk.
|
|
152
|
+
|
|
153
|
+
Either way the run **prints the job id and the cancel command**, and arming is not a new
|
|
154
|
+
decision at that point — the config is the recorded authorization, and re-asking would
|
|
155
|
+
rebuild the habit the config exists to retire.
|
|
156
|
+
|
|
157
|
+
Under `mode: dynamic` there is no job id: the run schedules its own next turn each time
|
|
158
|
+
and prints **the delay it chose and why**, which is the same disclosure in the form that
|
|
159
|
+
mode has. A dynamic run that says nothing about its pacing is indistinguishable from a
|
|
160
|
+
run that quietly stopped.
|
|
92
161
|
|
|
93
162
|
## Other harnesses, and honest degradation
|
|
94
163
|
|
|
@@ -258,13 +258,21 @@ never that the work was skipped quietly.
|
|
|
258
258
|
its status, in build order with the walking skeleton first. Single-module work
|
|
259
259
|
records `single module: <name>` in the design and moves on — a skipped
|
|
260
260
|
decomposition is a decision, never an omission.
|
|
261
|
+
- **The queue exists here, so the loop arms here** ([`continuity.md`](continuity.md) →
|
|
262
|
+
*Part 1a*). Where `run.loop.arm` is `after-decomposition` and the map holds more than
|
|
263
|
+
one module, arm the mode at the close of this stage and print one line: the mode, and
|
|
264
|
+
either the job id and its cancel command (`interval`) or the delay chosen and why
|
|
265
|
+
(`dynamic`). Arming collapses no gate and authorizes no outward act; it decides only
|
|
266
|
+
that the run does not stop to ask *"shall I take the next one?"*. Single-module work
|
|
267
|
+
arms nothing and says so — a loop with one item is a timer.
|
|
261
268
|
- **GATE (manual):** the user approves the design, the UI verdict is recorded,
|
|
262
269
|
**every REQ is answered by the design** — a requirement the design doesn't
|
|
263
270
|
address is either covered now or explicitly dropped by the operator, with the
|
|
264
271
|
drop recorded in the carry-over ledger — **and, for a platform, the module map is
|
|
265
272
|
approved**: brick criteria met or excepted in writing, dependency graph acyclic,
|
|
266
273
|
build order topological, every REQ mapped to exactly one module, cross-module
|
|
267
|
-
contracts named with their owner
|
|
274
|
+
contracts named with their owner — **and the loop's arming state is printed**:
|
|
275
|
+
armed with its queue and pacing, or not armed with the reason.
|
|
268
276
|
|
|
269
277
|
## 3 — Spec — with UX track for user-facing tasks
|
|
270
278
|
- **Freedom: medium** — what the contract says is judgement; which contracts must be locked is a list ([`gates.md`](gates.md) → *Axis C*).
|
|
@@ -17,20 +17,32 @@ than no boundary.
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
## Routing —
|
|
20
|
+
## Routing — work that lands in the repository goes through the pipeline
|
|
21
21
|
|
|
22
|
-
**When `task-pipeline` is installed, any work that
|
|
23
|
-
through it** — without being asked for.
|
|
24
|
-
integration, a rewrite, an adoption, a hardening pass; in any language and any
|
|
25
|
-
phrasing. Saying *"run this through the pipeline"* is an accelerator, not a
|
|
26
|
-
precondition.
|
|
22
|
+
**When `task-pipeline` is installed, any work that LANDS IN THE REPOSITORY goes
|
|
23
|
+
through it** — without being asked for. Two shapes, not one:
|
|
27
24
|
|
|
28
|
-
**
|
|
29
|
-
|
|
25
|
+
- **a change** — a feature, a fix, a refactor, a migration, an integration, a rewrite,
|
|
26
|
+
an adoption, a hardening pass;
|
|
27
|
+
- **a finding that lands** — an audit, a bug hunt, a production check, a PR review. Its
|
|
28
|
+
output is not an answer in the chat: it is REQ rows, board rows, ledger rows and
|
|
29
|
+
fixes, and the pipeline is what carries them there.
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
In any language and any phrasing. Saying *"run this through the pipeline"* is an
|
|
32
|
+
accelerator, not a precondition.
|
|
33
|
+
|
|
34
|
+
**The boundary is what the request ENDS IN, and it cuts both ways.** An answer stops in
|
|
35
|
+
the conversation; a change or a finding lands in the tree. Not through the pipeline:
|
|
36
|
+
|
|
37
|
+
- a question and its answer, an explanation, mapping code so a person can read it;
|
|
32
38
|
- a typo, a one-line edit, a mechanical rename;
|
|
33
|
-
- reconnaissance or measurement that
|
|
39
|
+
- reconnaissance or measurement that lands nothing.
|
|
40
|
+
|
|
41
|
+
**Reading is not the test.** An audit, a bug hunt and a PR review all *begin* by reading,
|
|
42
|
+
and all three end in the repository. Excluding them because they read is how a skill with
|
|
43
|
+
a whole doctrine file for audits (`references/audit.md`) never gets reached by the word
|
|
44
|
+
*audit* — measured, not supposed: three of ten routing queries were refused with this
|
|
45
|
+
rule's own exclusion quoted back as the reason.
|
|
34
46
|
|
|
35
47
|
Running ten gated stages for one character is the fastest way to teach an agent to
|
|
36
48
|
route around the pipeline entirely.
|