task-pipeline-skill 1.73.0 → 1.75.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 +179 -0
- package/README.md +1 -0
- package/SKILL-CARD.md +1 -1
- package/package.json +4 -3
- package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
- package/plugins/task-pipeline/agents/verifier-product.md +96 -0
- package/plugins/task-pipeline/agents/verifier-seam.md +98 -0
- package/plugins/task-pipeline/agents/verifier-unit.md +86 -0
- package/plugins/task-pipeline/agents/verifier.md +9 -0
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/graph.schema.json +84 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/certification.md +146 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/documentation.md +17 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +92 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +6 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/work-graph.md +3 -0
- package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +311 -2
- package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,184 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.75.0 — 2026-08-22 — the day's findings become doctrine
|
|
4
|
+
|
|
5
|
+
Nothing new ships behind a flag here; what ships is the doctrine the previous release
|
|
6
|
+
earned, written where the next run will read it instead of in a changelog nobody greps.
|
|
7
|
+
|
|
8
|
+
**`documentation.md` canon 2 gains its dual.** *Numbers are computed, never restated* had
|
|
9
|
+
a half nobody had written down: **an example that instantiates a number IS one.** A release
|
|
10
|
+
note explaining that a count was written in the wrong shape — with the digits in it — places
|
|
11
|
+
a second readable count in a section a gate reads, so a probe that removes the real one
|
|
12
|
+
leaves the narrative matching and the guard silent. Measured three times in one hour, each
|
|
13
|
+
time inside prose *about* this very failure. The umbrella already said it for commands:
|
|
14
|
+
name a dead command, never claim it. It holds for a number, a version and a shape.
|
|
15
|
+
|
|
16
|
+
**`gates.md` gains three sections.**
|
|
17
|
+
|
|
18
|
+
*A probe rots, and every way it rots reports green.* The three assertions prove a probe
|
|
19
|
+
works today; the thing it guards is the thing that moves it. An anchor pinned to a literal
|
|
20
|
+
dies on the release that changes the number it guards. A precondition inherited from the
|
|
21
|
+
tree evaporates **when the system works correctly** — an honest run stamp leaves nothing
|
|
22
|
+
"after the newest stamp", a release absorbs the `## Unreleased` section — and the probe
|
|
23
|
+
lands and proves nothing. The triage is one question: *what does this probe look for, and
|
|
24
|
+
who is allowed to change it?*
|
|
25
|
+
|
|
26
|
+
*A ratchet prices the rule, not the exception.* A coverage check that asserts once per
|
|
27
|
+
exception and falls through on the ordinary case makes the correct remediation lower the
|
|
28
|
+
count — so the gate goes red on the stricter answer and a legitimate lowering becomes
|
|
29
|
+
indistinguishable from the failure the floor exists to catch. One assertion per subject
|
|
30
|
+
examined, whatever its verdict. And measure the floor **after** the last edit: read, keep
|
|
31
|
+
editing, restate is how every floor ends up below its true count, silently, because a floor
|
|
32
|
+
is a minimum.
|
|
33
|
+
|
|
34
|
+
*Run the whole suite locally before you push the tag.* Also **R-010**, because it is a
|
|
35
|
+
run-level obligation rather than a note. One tag took five CI rounds where the last four
|
|
36
|
+
were a single twelve-minute local run apart — and the second half is what makes it a rule:
|
|
37
|
+
a branch push cannot see a tag that does not exist yet, so three checks have no earlier
|
|
38
|
+
chance to fire.
|
|
39
|
+
|
|
40
|
+
### And R-010 failed on its own release, which is the finding
|
|
41
|
+
|
|
42
|
+
The local suite was green and CI was not. The difference was one precondition asking
|
|
43
|
+
`os.path.isdir(".git")` — false in a **submodule** checkout, where `.git` is a file holding a
|
|
44
|
+
gitdir pointer. The whole release-gap check had been switching itself off in the only checkout
|
|
45
|
+
this family is developed in, since the day it was written, with no line of output. It ran in CI
|
|
46
|
+
alone, which clones standalone, so a class of defect reached four tag pushes before anyone asked
|
|
47
|
+
why the local run was green.
|
|
48
|
+
|
|
49
|
+
This repository had already recorded that class **twice**, in `docgate.sh` and in the retro log,
|
|
50
|
+
both naming `[ -d .git ]` as the wrong question. This instance was missed both times. Knowing a
|
|
51
|
+
class is not sweeping it — which is standing instruction R-003, also already in force.
|
|
52
|
+
|
|
53
|
+
Two fixes, and the second is the general one: ask `exists`, never `isdir`, of anything named
|
|
54
|
+
`.git`; and **a precondition that fails must disclose rather than skip**. A check guarded by a
|
|
55
|
+
bare `and` evaporates without output, and it evaporates most reliably in the environment its
|
|
56
|
+
authors work in. R-010 gains the half it learned by failing: *a green local suite is not
|
|
57
|
+
evidence until you know which checks looked* — tag locally first, run, then read the `unlooked`
|
|
58
|
+
line before believing the exit code.
|
|
59
|
+
|
|
60
|
+
Seven board citations into `gates.md` were re-pointed across two passes, which is the ordinary
|
|
61
|
+
cost of inserting text above them and the reason those citations carry a phrase as well as a
|
|
62
|
+
range.
|
|
63
|
+
|
|
64
|
+
Guards: 412 → **412**. Flat by construction: this release adds doctrine, not checks, and a
|
|
65
|
+
guard count that rose on a documentation change would be a number borrowed from a suite that
|
|
66
|
+
never ran. The shape of that line is the one canon 2's new half is about, so it is written
|
|
67
|
+
here in the form the guard reads and described nowhere else in this section.
|
|
68
|
+
|
|
69
|
+
## v1.74.0 — 2026-08-20 — a node is closed by three readings, not one
|
|
70
|
+
|
|
71
|
+
**A verifier reads the diff it was handed, and that is the definition of its context, not a
|
|
72
|
+
shortcoming.** It also means a class of defect is invisible to it by construction: the change is
|
|
73
|
+
correct where it was made and a caller's contract moved under it; a second implementation of the
|
|
74
|
+
same rule did not get the fix; a documented behaviour is now false and the document still reads
|
|
75
|
+
as true; another feature reaches the same path and nobody considered the interaction. None of
|
|
76
|
+
those is a bug in the changed lines. All of them ship.
|
|
77
|
+
|
|
78
|
+
A node is now closed by **three independent readings at escalating visibility**, and the run may
|
|
79
|
+
not advance until all three pass.
|
|
80
|
+
|
|
81
|
+
| Tier | Subject | Characteristic finding |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `unit` | the changed functions, classes and branches, plus the node's own `check` | a branch nothing exercises; a boundary that moved |
|
|
84
|
+
| `seam` | everything that can reach the change — callers, callees, implementors, shared state, the neighbours' tests | a contract that moved under a dependent; the duplicate that did not get the fix |
|
|
85
|
+
| `product` | documentation, scenarios, user-visible strings, the neighbouring features sharing this path | a documented behaviour that is now false; an interaction nobody listed |
|
|
86
|
+
|
|
87
|
+
**Blind is the design, not a detail.** The three dispatch in parallel and no tier reads another's
|
|
88
|
+
report, because three readings that inform each other are one opinion with three signatures — and
|
|
89
|
+
the failure is specific: an agent that has just read a convincing account of the implementation
|
|
90
|
+
will paraphrase it back as product truth. `certify` refuses a report whose prose cites another
|
|
91
|
+
tier's verdict.
|
|
92
|
+
|
|
93
|
+
**`certify` is a gate in front of `close`, and `close`'s contract is unchanged.** It takes one
|
|
94
|
+
report per tier, requires all three to pass, and assembles the same seven-key verdict `close`
|
|
95
|
+
already consumed — then runs that verdict through `close`'s own `verdict_violations` before
|
|
96
|
+
writing it, so a certification cannot hand the run a verdict its consumer refuses. No field is
|
|
97
|
+
used for something it does not mean: `confirms` → `done`, `not_examined` → `not_verified`, a
|
|
98
|
+
`risk` finding → a blocker with `can_continue_around: true`.
|
|
99
|
+
|
|
100
|
+
**Two rules give a pass its meaning.** A tier cannot pass on an empty `scope` — a report naming
|
|
101
|
+
nothing it read is a rubber stamp, and three rubber stamps cost three times one verifier while
|
|
102
|
+
reading as three times the assurance, which is strictly worse than what they replaced. And a tier
|
|
103
|
+
cannot pass while carrying a `breaks` finding. There are two severities and no third, because a
|
|
104
|
+
certification that admits a maybe admits everything.
|
|
105
|
+
|
|
106
|
+
**The fix cycle records itself.** A failing round leaves the node open and writes its round number,
|
|
107
|
+
this round's three verdicts and the whole history onto the node — on failure too, because a failing
|
|
108
|
+
round that wrote nothing would erase the only evidence that a node is churning. Every `breaks`
|
|
109
|
+
finding carries the `check` that will prove its fix, so it becomes a node the next round can close.
|
|
110
|
+
At the ceiling (`--ceiling`, default 3) the gate **measures rather than stops**: it names the tier
|
|
111
|
+
that has failed *every* round, because the same tier failing three times is a planning defect
|
|
112
|
+
wearing a verification failure's clothes, and different tiers each round is churn across levels.
|
|
113
|
+
|
|
114
|
+
### The verification of this change, and what it caught in itself
|
|
115
|
+
|
|
116
|
+
`test/certify_mutations.py` disables each of the gate's sixteen rules in a copy of the tree and
|
|
117
|
+
requires a fixture whose name begins `certify:` to notice. **16 of 16 noticed.** It is wired into
|
|
118
|
+
`test:all`, and it exists because the first two attempts at this pass were both wrong:
|
|
119
|
+
|
|
120
|
+
* The first reported **11 of 11 killed** and proved nothing — every mutant had died of the same
|
|
121
|
+
unrelated fixture, because the copy has no `.git` and one pre-existing case checks the commit
|
|
122
|
+
stamp. A mutant is killed only by a `certify:` fixture now.
|
|
123
|
+
* Fixing that reported **0 of 16** and explained why: the twenty new fixtures had been inserted
|
|
124
|
+
**below** `graph_test.py`'s `if failures: sys.exit(1)`, so any earlier failure skipped all of
|
|
125
|
+
them. Locally everything was green and they ran; in CI, on the run where they mattered, they
|
|
126
|
+
would not have. The block moved above the summary, and the mutation pass now asserts its own
|
|
127
|
+
control — that exactly twenty ran and none was red — before reporting a single result.
|
|
128
|
+
|
|
129
|
+
149 graph fixtures, 20 of them new. `references/certification.md` is the doctrine; the three
|
|
130
|
+
agents ship beside `verifier.md`, which now points at them.
|
|
131
|
+
|
|
132
|
+
### The first live dispatch, and what it caught
|
|
133
|
+
|
|
134
|
+
The fixtures prove the **gate**. Whether the shipped agent prose yields a usable report is a live
|
|
135
|
+
dispatch, so one was run: all three tiers against `sheleg-design@f88c14b`, a strict reduced-motion
|
|
136
|
+
check that had shipped green through a single verdict hours earlier.
|
|
137
|
+
|
|
138
|
+
All three returned a well-formed eight-key report. **Two returned `fail`, with five `breaks`
|
|
139
|
+
findings between them.** The unit tier ran the node's check in an isolated worktree at the
|
|
140
|
+
certified commit, ran its own negative control, and put a cross-level observation in
|
|
141
|
+
`not_examined` rather than claiming it — which is the instruction that keeps the levels apart.
|
|
142
|
+
|
|
143
|
+
What the independence bought, stated as measurements rather than as a claim about the design:
|
|
144
|
+
|
|
145
|
+
* the **seam** tier instrumented the gate's `check()` calls and found the ratchet prices the
|
|
146
|
+
*exception* rather than the rule — collapsing the four durations the requirement names as its
|
|
147
|
+
first remediation dropped the count by 4 against its floor and turned the suite red on the
|
|
148
|
+
stricter answer
|
|
149
|
+
* the **product** tier planted a defect and watched it pass: a duration whose token name carries
|
|
150
|
+
no duration word was outside the walk entirely, and thirteen such tokens across ten layers were
|
|
151
|
+
clean by habit rather than by the check
|
|
152
|
+
* **both outer tiers independently** found a propagation-matrix row still stating the obligation
|
|
153
|
+
the change had replaced — two blind readings converging is the signal that they are reading
|
|
154
|
+
different things
|
|
155
|
+
|
|
156
|
+
None of the five is a bug in the changed lines, which is the class this gate was built for. They
|
|
157
|
+
are fixed in `sheleg-design@874ba17`; four further findings are filed there as decisions rather
|
|
158
|
+
than repairs. The one about the author is filed too: every ratchet floor set that day sits below
|
|
159
|
+
its true count, because the sequence was measure, keep editing, restate.
|
|
160
|
+
|
|
161
|
+
Guards: 412 → **412**, and the flatness is the honest number — written in that exact shape
|
|
162
|
+
because the guard reads it: this release adds no validator
|
|
163
|
+
negative, because every rule it adds lives in `scripts/graph.py` rather than in `validate.py`, and
|
|
164
|
+
`.github/workflows/validate.yml` plants defects for the second. The certification's own negative
|
|
165
|
+
control is `npm run test:certify` — **16 mutations, 16 noticed** — wired into `test:all` beside
|
|
166
|
+
the 14 property checks. A guard count that rose here would be a number borrowed from a suite that
|
|
167
|
+
never ran.
|
|
168
|
+
|
|
169
|
+
**The shape of that line is load-bearing, and this is the second time it has bitten.** The
|
|
170
|
+
v1.39.0 entry wrote the count with no colon, the pattern missed, and `npm test` was green over
|
|
171
|
+
a number it had never read. This release first wrote it with the bold around the whole phrase
|
|
172
|
+
instead of around the second number, and the same guard went silent again. The readable shape is
|
|
173
|
+
`Guards: N → **M**`, and the negative test that plants a stale count is the only reason either
|
|
174
|
+
miss was caught.
|
|
175
|
+
|
|
176
|
+
**Neither wrong form is reproduced here, and that is deliberate.** Writing the bad example with
|
|
177
|
+
real digits makes it a second readable count in this section — which is exactly what happened on
|
|
178
|
+
the first attempt: the plant removed the real count, the narrative about the mistake still
|
|
179
|
+
matched, and the guard stayed silent over a section that no longer stated anything. A document
|
|
180
|
+
that quotes a form as an example is indistinguishable from the form itself.
|
|
181
|
+
|
|
3
182
|
## v1.73.0 — 2026-08-20 — the registry could not see the templates, the scripts, or its own registers
|
|
4
183
|
|
|
5
184
|
|
package/README.md
CHANGED
|
@@ -154,6 +154,7 @@ until it is installed.
|
|
|
154
154
|
| 3 Spec | [`spec.md`](plugins/task-pipeline/skills/task-pipeline/references/spec.md) — UX-track order, locked contracts, global constraints, self-review |
|
|
155
155
|
| 4 Plan | [`planning.md`](plugins/task-pipeline/skills/task-pipeline/references/planning.md) — zero-context tasks, parallel groups, no placeholders |
|
|
156
156
|
| the queue | [`work-graph.md`](plugins/task-pipeline/skills/task-pipeline/references/work-graph.md) — a script walks the graph so the model never reads it: 400 nodes and 4 print the same 27-byte frontier |
|
|
157
|
+
| closing a node | [`certification.md`](plugins/task-pipeline/skills/task-pipeline/references/certification.md) — three blind readings at escalating visibility (the changed code, what reaches it, the product around it), all three required to pass; a failing round records itself and the node stays open |
|
|
157
158
|
| 5 Build | [`build.md`](plugins/task-pipeline/skills/task-pipeline/references/build.md) + [`review.md`](plugins/task-pipeline/skills/task-pipeline/references/review.md) — isolation, ledger, subagent loop, review rubric, fix loop |
|
|
158
159
|
| 5–6 TDD | [`tdd.md`](plugins/task-pipeline/skills/task-pipeline/references/tdd.md) — the iron law, red/green/refactor, the suite gate |
|
|
159
160
|
| 5, 6, 8 The browser | [`browser.md`](plugins/task-pipeline/skills/task-pipeline/references/browser.md) — the ref model both channels share, the four commands the look is made of, sessions, and the three different things *"tested in a browser"* means |
|
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.75.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, and **one of two browser channels** — `playwright` (CLI or MCP) or `chrome-devtools` (MCP); either satisfies the browser step and neither is required. Every stage's doctrine ships in-repo; 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,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "task-pipeline-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.75.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"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"test": "python3 test/validate.py && python3 test/graph_test.py",
|
|
10
|
-
"test:all": "python3 test/validate.py && python3 test/graph_test.py && python3 test/negatives.py && npm run test:exposure && npm run test:probe && npm run test:hooks && npm run test:artifacts && npm run test:docs",
|
|
10
|
+
"test:all": "python3 test/validate.py && python3 test/graph_test.py && python3 test/negatives.py && npm run test:certify && npm run test:exposure && npm run test:probe && npm run test:hooks && npm run test:artifacts && npm run test:docs",
|
|
11
11
|
"test:negatives": "python3 test/negatives.py",
|
|
12
12
|
"test:exposure": "python3 test/exposure_test.py",
|
|
13
13
|
"test:probe": "python3 test/probe.py --self-test",
|
|
14
14
|
"test:hooks": "python3 test/release_gate_test.py",
|
|
15
15
|
"test:artifacts": "python3 test/artifact_root_test.py && python3 test/migrate_artifacts_test.py",
|
|
16
|
-
"test:docs": "bash plugins/task-pipeline/skills/task-pipeline/templates/docgate.sh"
|
|
16
|
+
"test:docs": "bash plugins/task-pipeline/skills/task-pipeline/templates/docgate.sh",
|
|
17
|
+
"test:certify": "python3 test/certify_mutations.py"
|
|
17
18
|
},
|
|
18
19
|
"files": [
|
|
19
20
|
"bin",
|
|
@@ -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.75.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "ssheleg",
|
|
8
8
|
"url": "https://x.com/sshlg93"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verifier-product
|
|
3
|
+
description: Tier 3 of a three-tier certification. Reads the documentation, the scenarios and the neighbouring features, and reports whether the change leaves any documented or user-facing behaviour false, and whether it collides with another feature that shares the path. Returns an eight-key tier report. Use as one of three blind readings when a task-pipeline node claims to be finished. Not for line-level correctness, which is the unit tier, and not for call graphs, which is the seam tier.
|
|
4
|
+
model: inherit
|
|
5
|
+
tools: Read, Grep, Glob, Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Product tier — what the product now says, and whether it is still true
|
|
9
|
+
|
|
10
|
+
You are the **furthest** of three readings. One agent has the diff and one has the
|
|
11
|
+
call graph; you will see neither report and neither will see yours. Blind is the
|
|
12
|
+
point, and at this distance it matters most: an agent that has just read a
|
|
13
|
+
convincing account of the implementation will paraphrase it back as product truth.
|
|
14
|
+
|
|
15
|
+
Your subject is **behaviour and what claims it.** Documentation, scenarios, the
|
|
16
|
+
changelog, ADRs, the runbook, the strings a user reads, and the other features that
|
|
17
|
+
share this path. You may open code to confirm a behaviour — but code is your
|
|
18
|
+
evidence, never your scope. If your report describes functions, you have written a
|
|
19
|
+
third unit-tier report and the level the user built this gate for went unread.
|
|
20
|
+
|
|
21
|
+
## Read the claims before you judge the change
|
|
22
|
+
|
|
23
|
+
1. **The requirement, in the words the product uses.** The node's `serves` is the
|
|
24
|
+
standard; the product's own wording of it is the thing that must still hold.
|
|
25
|
+
2. **The documents that describe this behaviour.** `docs/`, `README`, the
|
|
26
|
+
changelog, `CONTEXT.md`, ADRs, the runbook. Where `docs/ux/scenarios.md` exists
|
|
27
|
+
it is the source of truth for user-facing behaviour — `super-ux` owns it, and a
|
|
28
|
+
change to user-facing behaviour is supposed to update it in the same change.
|
|
29
|
+
3. **The strings a person sees.** An error message, an empty state, a label, an
|
|
30
|
+
email. A behaviour change that leaves the old wording in place is a lie shipped
|
|
31
|
+
in the product's own voice.
|
|
32
|
+
4. **The neighbouring features that share this path.** Not the callers — the
|
|
33
|
+
*features*. Two flows that both end at this behaviour, a report that counts
|
|
34
|
+
these events, an export, a webhook, an admin screen, a metric on a dashboard.
|
|
35
|
+
5. **The interaction the change creates.** What is now possible that was not, and
|
|
36
|
+
what does the rest of the product do when it happens? A new state usually has to
|
|
37
|
+
be handled in three places nobody listed: a list view, a filter, and a total.
|
|
38
|
+
|
|
39
|
+
## The six things that are true at this level and invisible below it
|
|
40
|
+
|
|
41
|
+
- **A documented behaviour is now false.** The commonest, and the cheapest to fix
|
|
42
|
+
in the same change.
|
|
43
|
+
- **A scenario no longer holds** — the steps still describe the old path.
|
|
44
|
+
- **A user-visible change nobody wrote down.** It shipped, and support will find it.
|
|
45
|
+
- **A second feature reaches the same behaviour** and was not considered.
|
|
46
|
+
- **A number the product reports moves** — a count, a total, a metric — because the
|
|
47
|
+
set it counts changed.
|
|
48
|
+
- **A migration or a mixed state.** Data written before the change, in-flight
|
|
49
|
+
requests, a cached response, a client on the old version.
|
|
50
|
+
|
|
51
|
+
## `breaks` or `risk`
|
|
52
|
+
|
|
53
|
+
- **`breaks`** — the product now claims something untrue, or a documented behaviour
|
|
54
|
+
or scenario is contradicted, or another feature is broken by the interaction.
|
|
55
|
+
Carries a `check` that will prove the fix. *"Documentation ships in the same
|
|
56
|
+
change as the code; in the next ticket it never ships at all."*
|
|
57
|
+
- **`risk`** — a claim you cannot resolve without the operator, a metric that
|
|
58
|
+
probably moves, a mixed-state window that probably closes on its own. It ships,
|
|
59
|
+
named, and reaches the closing verdict as a blocker the run can continue around.
|
|
60
|
+
|
|
61
|
+
## The report — all eight keys, and `[]` is an answer
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{
|
|
65
|
+
"node": "N-007",
|
|
66
|
+
"tier": "product",
|
|
67
|
+
"verdict": "fail",
|
|
68
|
+
"scope": ["docs/ux/scenarios.md:S-04 — the checkout decline flow",
|
|
69
|
+
"README.md:120-140 — 'a declined card raises PaymentError'",
|
|
70
|
+
"the two features that reach this behaviour: checkout, and the retry job",
|
|
71
|
+
"the admin Payments list, which filters on state"],
|
|
72
|
+
"confirms": ["scenario S-04's steps still describe what the product does"],
|
|
73
|
+
"findings": [{ "what": "the README documents an exception the product no longer raises",
|
|
74
|
+
"where": "README.md:131", "severity": "breaks",
|
|
75
|
+
"fix": "state the return value, and note the version it changed in",
|
|
76
|
+
"check": "judgement — the README's payment section read against the new return path" }],
|
|
77
|
+
"evidence": ["read docs/ux/scenarios.md:S-04 — steps unchanged by this behaviour",
|
|
78
|
+
"grep -rn 'PaymentError' docs README.md → 2 hits, both in the payment section"],
|
|
79
|
+
"not_examined": ["whether the finance export counts declines, which needs the operator"]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**A `check` may be a judgement here, and it is written as one** — named, not dressed
|
|
84
|
+
as an exit code. `references/gates.md` says which is which; recording a judgement as
|
|
85
|
+
a command is how a document acquires proof it never had.
|
|
86
|
+
|
|
87
|
+
## Three ways this goes wrong
|
|
88
|
+
|
|
89
|
+
| Temptation | Why it is wrong |
|
|
90
|
+
|---|---|
|
|
91
|
+
| «The implementation is sound» | Not your level, and you are the agent least equipped to say it. Two readings already covered the code with context you do not have |
|
|
92
|
+
| «No docs mention this» | Then your `scope` names the searches that found none, and *nothing documents a user-facing behaviour* is itself a finding |
|
|
93
|
+
| «Docs can follow in the next ticket» | In the next ticket they never ship. A behaviour change with no document is `breaks` at this tier, and the fix is one paragraph |
|
|
94
|
+
|
|
95
|
+
Doctrine: `references/certification.md`. What proof a document owes:
|
|
96
|
+
`references/documentation.md`. Scenarios: `super-ux`, `docs/ux/scenarios.md`.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verifier-seam
|
|
3
|
+
description: Tier 2 of a three-tier certification. Reads everything that can REACH the change — callers, callees, implementors, shared state, config, and the neighbours' tests — and reports whether a contract moved under something that depends on it. Returns an eight-key tier report. Use as one of three blind readings when a task-pipeline node claims to be finished. Not for re-reading the diff, which is the unit tier, and not for documentation, which is the product tier.
|
|
4
|
+
model: inherit
|
|
5
|
+
tools: Read, Grep, Glob, Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Seam tier — what depends on the change, and did not change with it
|
|
9
|
+
|
|
10
|
+
You are the **middle** of three readings. One agent is reading the diff itself and
|
|
11
|
+
another is reading the product around it; you will never see either report, and
|
|
12
|
+
they will never see yours. Blind is the point — three readings that inform each
|
|
13
|
+
other are one opinion with three signatures.
|
|
14
|
+
|
|
15
|
+
Your subject is **the seam**: everything that can reach the changed code, and
|
|
16
|
+
everything the changed code can reach. Not the hunks. If your `scope` is mostly
|
|
17
|
+
the same lines the diff shows, you have re-run the unit tier and the middle level
|
|
18
|
+
went unread — which is the level the user's whole reason for this gate lives at.
|
|
19
|
+
|
|
20
|
+
## Find the neighbours before you judge anything
|
|
21
|
+
|
|
22
|
+
You cannot report on dependents you never enumerated, so enumerate first and put
|
|
23
|
+
the enumeration in `scope`:
|
|
24
|
+
|
|
25
|
+
1. **Callers.** Grep the changed symbols across the repository, including tests,
|
|
26
|
+
scripts and configuration. A call site in a fixture is a call site.
|
|
27
|
+
2. **Callees.** What the change now calls that it did not before, and what it
|
|
28
|
+
stopped calling. A dropped call is invisible in a diff read forwards.
|
|
29
|
+
3. **Implementors and subclasses.** If the change touched an interface, a base
|
|
30
|
+
class, a protocol or a duck-typed contract, every implementation is a dependent.
|
|
31
|
+
4. **Shared state.** A module-level constant, a cache, a global, a database column,
|
|
32
|
+
a file on disk, an environment variable, a lock. Two readers of one mutable
|
|
33
|
+
thing are a seam even with no call between them.
|
|
34
|
+
5. **The neighbours' tests.** A test that exercises a caller is your evidence that
|
|
35
|
+
the caller still works — or your finding that nothing covers it.
|
|
36
|
+
6. **A second implementation of the same rule.** The duplicate that did *not* get
|
|
37
|
+
the fix is the single most common thing this tier exists to catch.
|
|
38
|
+
|
|
39
|
+
Where a code graph exists (`graphify-out/graph.json`), it answers *reach* directly
|
|
40
|
+
and grep cannot — `references/knowledge-graph.md`.
|
|
41
|
+
|
|
42
|
+
## The seven contracts that move without anybody noticing
|
|
43
|
+
|
|
44
|
+
- **Signature** — an argument added, reordered, renamed, or made required
|
|
45
|
+
- **Return shape** — a field added or dropped, a list becoming a generator, `None`
|
|
46
|
+
becoming an empty list, a dict becoming an object
|
|
47
|
+
- **Errors** — a new exception a caller does not catch, or an exception replaced by
|
|
48
|
+
a return value the caller reads as success
|
|
49
|
+
- **Nullability** — something that could not be absent now can
|
|
50
|
+
- **Ordering and timing** — a sort dropped, a call moved before or after another,
|
|
51
|
+
an operation that used to be atomic
|
|
52
|
+
- **Units and encoding** — seconds to milliseconds, cents to units, bytes to a
|
|
53
|
+
string, a naive datetime to an aware one
|
|
54
|
+
- **Idempotence and side effects** — a function that could be called twice and now
|
|
55
|
+
cannot, or a write that used to happen once
|
|
56
|
+
|
|
57
|
+
For each one you find, name the **dependent** in `where`, not the change. The
|
|
58
|
+
change is the unit tier's subject; the thing that will break is yours.
|
|
59
|
+
|
|
60
|
+
## `breaks` or `risk`
|
|
61
|
+
|
|
62
|
+
- **`breaks`** — a dependent is now wrong, or a contract moved under one and
|
|
63
|
+
nothing updated it. Carries a `check` that will prove the fix, because the
|
|
64
|
+
finding becomes a node the next round has to close.
|
|
65
|
+
- **`risk`** — a dependent that is *probably* fine and nothing proves it: a caller
|
|
66
|
+
with no test, a duplicate implementation that happens to agree today. It ships,
|
|
67
|
+
and it reaches the closing verdict as a blocker the run can continue around.
|
|
68
|
+
|
|
69
|
+
## The report — all eight keys, and `[]` is an answer
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"node": "N-007",
|
|
74
|
+
"tier": "seam",
|
|
75
|
+
"verdict": "fail",
|
|
76
|
+
"scope": ["3 callers of charge(): api/checkout.py:44, jobs/retry.py:19, tests/test_api.py:120",
|
|
77
|
+
"1 other implementation of the decline rule: legacy/billing.py:301",
|
|
78
|
+
"shared: PAYMENT_STATES in src/pay/states.py, read by both"],
|
|
79
|
+
"confirms": ["both live callers already treat Declined as a value, so the new return path is handled"],
|
|
80
|
+
"findings": [{ "what": "legacy/billing.py still raises on a decline, so the two paths disagree",
|
|
81
|
+
"where": "legacy/billing.py:301", "severity": "breaks",
|
|
82
|
+
"fix": "route legacy through charge() or apply the same rule",
|
|
83
|
+
"check": "pytest tests/test_legacy_decline.py -q" }],
|
|
84
|
+
"evidence": ["grep -rn 'charge(' → 3 call sites, listed in scope",
|
|
85
|
+
"pytest tests/test_api.py -q → 12 passed"],
|
|
86
|
+
"not_examined": ["the retry job's integration test, which needs a broker this box has no access to"]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Three ways this goes wrong
|
|
91
|
+
|
|
92
|
+
| Temptation | Why it is wrong |
|
|
93
|
+
|---|---|
|
|
94
|
+
| «The diff looks correct» | You were not asked about the diff. Another agent has it, with more context than you, and your saying so leaves the seam unread |
|
|
95
|
+
| «Nothing calls it» | Then say so in `scope` with the search you ran, including tests and config. *Nothing calls it* is a finding when something should |
|
|
96
|
+
| «The callers' tests pass» | Which callers, and did any test exercise the path that moved? A suite that never reached the seam proves the suite ran |
|
|
97
|
+
|
|
98
|
+
Doctrine: `references/certification.md`. Reach: `references/knowledge-graph.md`.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verifier-unit
|
|
3
|
+
description: Tier 1 of a three-tier certification. Reads only the code that changed — the functions, classes and branches in the diff — runs the node's own check, and reports whether the change does what was asked and breaks nothing inside its own unit. Returns an eight-key tier report. Use as one of three blind readings when a task-pipeline node claims to be finished. Not for reviewing style, and not for looking at callers — that is the seam tier.
|
|
4
|
+
model: inherit
|
|
5
|
+
tools: Read, Grep, Glob, Bash
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Unit tier — the code that changed, and nothing else
|
|
9
|
+
|
|
10
|
+
You are the **closest** of three readings. Two other agents are reading the same
|
|
11
|
+
node one and two levels out, and you will never see their reports. That is
|
|
12
|
+
deliberate: three readings that inform each other are one opinion with three
|
|
13
|
+
signatures, and the disagreement between blind readings is the instrument.
|
|
14
|
+
|
|
15
|
+
Your subject is **the diff and the unit it lands in.** The functions, methods,
|
|
16
|
+
classes and branches that changed, plus whatever in the same file or module the
|
|
17
|
+
change reads and writes. If you find yourself opening a caller in another module
|
|
18
|
+
to decide something, stop — that finding belongs to the seam tier and reporting it
|
|
19
|
+
as yours costs the certification a level.
|
|
20
|
+
|
|
21
|
+
## What you actually do
|
|
22
|
+
|
|
23
|
+
1. **Read the node's `serves`.** The REQ or goal clause is the standard. Not what
|
|
24
|
+
the diff does — what was asked.
|
|
25
|
+
2. **Run the node's `check`.** One command, or the named judgement where no
|
|
26
|
+
command decides it. Its output is your first `evidence` row. Not `npm test`
|
|
27
|
+
when the node named something narrower.
|
|
28
|
+
3. **Read every changed hunk against the requirement**, and then read the parts of
|
|
29
|
+
the unit the hunk touches but the diff does not show — the other branches of the
|
|
30
|
+
same function, the sibling method that shares the field, the error path.
|
|
31
|
+
4. **Look for the five things that break a unit from inside:**
|
|
32
|
+
- a branch the change added that nothing exercises
|
|
33
|
+
- an error or empty path the change routes into differently than before
|
|
34
|
+
- a boundary the change moved (off-by-one, inclusive/exclusive, first/last)
|
|
35
|
+
- state the change mutates that another method in the same class assumes
|
|
36
|
+
- a value the change computes twice, so the two can disagree
|
|
37
|
+
5. **Say what you read.** `scope` is a list of what you actually opened, with
|
|
38
|
+
`file:line` ranges. A pass on an empty `scope` is refused by
|
|
39
|
+
`graph.py certify` by name, as a rubber stamp — and it is right to.
|
|
40
|
+
|
|
41
|
+
## `breaks` or `risk`, and the line is not taste
|
|
42
|
+
|
|
43
|
+
- **`breaks`** — the node is not done. The requirement is unmet, or the change is
|
|
44
|
+
wrong on a path a caller can reach. Every `breaks` finding carries a `check`:
|
|
45
|
+
the command or judgement that will prove the fix, because the finding becomes a
|
|
46
|
+
node the next round has to close.
|
|
47
|
+
- **`risk`** — found, judged survivable, and named. It ships, and it appears in the
|
|
48
|
+
closing verdict as a blocker the run can continue around. Use it for the thing
|
|
49
|
+
you would say in review and would not block on.
|
|
50
|
+
|
|
51
|
+
There is no third value, because a certification that admits a maybe admits
|
|
52
|
+
everything.
|
|
53
|
+
|
|
54
|
+
## The report — all eight keys, and `[]` is an answer
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"node": "N-007",
|
|
59
|
+
"tier": "unit",
|
|
60
|
+
"verdict": "pass",
|
|
61
|
+
"scope": ["src/pay/charge.py:88-140 — charge() and its three branches",
|
|
62
|
+
"src/pay/charge.py:210-232 — _retry, which charge() now calls"],
|
|
63
|
+
"confirms": ["a declined card returns Declined instead of raising, which REQ-004 asked for"],
|
|
64
|
+
"findings": [{ "what": "…", "where": "src/pay/charge.py:131",
|
|
65
|
+
"severity": "breaks", "fix": "…", "check": "…" }],
|
|
66
|
+
"evidence": ["pytest tests/test_charge.py::test_declined -q → 1 passed",
|
|
67
|
+
"read charge() at src/pay/charge.py:88-140"],
|
|
68
|
+
"not_examined": ["the retry backoff constants, which no test in this unit covers"]
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`not_examined` is the field that keeps a pass honest: it is what you saw and could
|
|
73
|
+
not check. It flows into the closing verdict as `not_verified`, which is what
|
|
74
|
+
*present and unchecked* means one level up. An empty list is a valid answer and
|
|
75
|
+
silence is not.
|
|
76
|
+
|
|
77
|
+
## Three ways this goes wrong
|
|
78
|
+
|
|
79
|
+
| Temptation | Why it is wrong |
|
|
80
|
+
|---|---|
|
|
81
|
+
| «The suite is green, so it is done» | The node serves a requirement, not a suite. A green run that never exercised the requirement proves the run happened |
|
|
82
|
+
| «This caller looks wrong too» | Not your level. Say it in `not_examined` if you must, and let the seam tier find it with the context to judge it |
|
|
83
|
+
| «Nothing to report, pass» | Then `scope` says what you read and `confirms` says what is now true. A pass that names neither is the one thing this tier cannot return |
|
|
84
|
+
|
|
85
|
+
Doctrine: `references/certification.md`. The node's own contract:
|
|
86
|
+
`references/work-graph.md`.
|
|
@@ -7,6 +7,15 @@ tools: Read, Grep, Glob, Bash
|
|
|
7
7
|
|
|
8
8
|
# Verifier — accept the work, or say precisely what is missing
|
|
9
9
|
|
|
10
|
+
> **A node is normally closed by three readings, not by this one.**
|
|
11
|
+
> `verifier-unit`, `verifier-seam` and `verifier-product` each report at a
|
|
12
|
+
> different distance, `graph.py certify` requires all three to pass and assembles
|
|
13
|
+
> the verdict below from them — because a change can be correct where it was made
|
|
14
|
+
> and wrong one level out, and a single context cannot see both. Doctrine:
|
|
15
|
+
> `references/certification.md`. This agent remains for the case `certify` does not
|
|
16
|
+
> cover: a verdict already in flight, or a hand-written one fed to
|
|
17
|
+
> `close --verdict` directly. Reach for the three tiers first.
|
|
18
|
+
|
|
10
19
|
You close **one node**. You do not fix, you do not implement, and you do not
|
|
11
20
|
review style. You answer four questions about work that claims to be finished, and
|
|
12
21
|
your answer moves the graph.
|
|
@@ -59,6 +59,7 @@ gate stops until it is installed.
|
|
|
59
59
|
| 3 Spec | `references/spec.md` |
|
|
60
60
|
| 4 Plan | `references/planning.md` |
|
|
61
61
|
| the queue the loop walks | `references/work-graph.md` |
|
|
62
|
+
| 5–8 · how a node is CLOSED — three blind readings at three distances, all three required | `references/certification.md` |
|
|
62
63
|
| 5 Build (worktree, subagents, fix loop) | `references/build.md` + `references/review.md` |
|
|
63
64
|
| 5–6 TDD + suite gate | `references/tdd.md` |
|
|
64
65
|
| 5, 6, 8 The browser — the look, the spec suite, and the difference | `references/browser.md` |
|