task-pipeline-skill 1.72.0 → 1.74.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.
Files changed (25) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/CONTRIBUTING.md +61 -0
  3. package/README.md +1 -0
  4. package/SKILL-CARD.md +1 -1
  5. package/package.json +4 -3
  6. package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
  7. package/plugins/task-pipeline/agents/verifier-product.md +96 -0
  8. package/plugins/task-pipeline/agents/verifier-seam.md +98 -0
  9. package/plugins/task-pipeline/agents/verifier-unit.md +86 -0
  10. package/plugins/task-pipeline/agents/verifier.md +9 -0
  11. package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +1 -0
  12. package/plugins/task-pipeline/skills/task-pipeline/graph.schema.json +167 -72
  13. package/plugins/task-pipeline/skills/task-pipeline/references/acceptance.md +23 -0
  14. package/plugins/task-pipeline/skills/task-pipeline/references/audit.md +15 -6
  15. package/plugins/task-pipeline/skills/task-pipeline/references/certification.md +146 -0
  16. package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +9 -0
  17. package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
  18. package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +9 -3
  19. package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +1 -1
  20. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +6 -0
  21. package/plugins/task-pipeline/skills/task-pipeline/references/work-graph.md +3 -0
  22. package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +326 -6
  23. package/plugins/task-pipeline/skills/task-pipeline/templates/retro.md +1 -1
  24. package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +22 -8
  25. package/plugins/task-pipeline/skills/task-pipeline/templates/verification.md +38 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,181 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.74.0 — 2026-08-20 — a node is closed by three readings, not one
4
+
5
+ **A verifier reads the diff it was handed, and that is the definition of its context, not a
6
+ shortcoming.** It also means a class of defect is invisible to it by construction: the change is
7
+ correct where it was made and a caller's contract moved under it; a second implementation of the
8
+ same rule did not get the fix; a documented behaviour is now false and the document still reads
9
+ as true; another feature reaches the same path and nobody considered the interaction. None of
10
+ those is a bug in the changed lines. All of them ship.
11
+
12
+ A node is now closed by **three independent readings at escalating visibility**, and the run may
13
+ not advance until all three pass.
14
+
15
+ | Tier | Subject | Characteristic finding |
16
+ |---|---|---|
17
+ | `unit` | the changed functions, classes and branches, plus the node's own `check` | a branch nothing exercises; a boundary that moved |
18
+ | `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 |
19
+ | `product` | documentation, scenarios, user-visible strings, the neighbouring features sharing this path | a documented behaviour that is now false; an interaction nobody listed |
20
+
21
+ **Blind is the design, not a detail.** The three dispatch in parallel and no tier reads another's
22
+ report, because three readings that inform each other are one opinion with three signatures — and
23
+ the failure is specific: an agent that has just read a convincing account of the implementation
24
+ will paraphrase it back as product truth. `certify` refuses a report whose prose cites another
25
+ tier's verdict.
26
+
27
+ **`certify` is a gate in front of `close`, and `close`'s contract is unchanged.** It takes one
28
+ report per tier, requires all three to pass, and assembles the same seven-key verdict `close`
29
+ already consumed — then runs that verdict through `close`'s own `verdict_violations` before
30
+ writing it, so a certification cannot hand the run a verdict its consumer refuses. No field is
31
+ used for something it does not mean: `confirms` → `done`, `not_examined` → `not_verified`, a
32
+ `risk` finding → a blocker with `can_continue_around: true`.
33
+
34
+ **Two rules give a pass its meaning.** A tier cannot pass on an empty `scope` — a report naming
35
+ nothing it read is a rubber stamp, and three rubber stamps cost three times one verifier while
36
+ reading as three times the assurance, which is strictly worse than what they replaced. And a tier
37
+ cannot pass while carrying a `breaks` finding. There are two severities and no third, because a
38
+ certification that admits a maybe admits everything.
39
+
40
+ **The fix cycle records itself.** A failing round leaves the node open and writes its round number,
41
+ this round's three verdicts and the whole history onto the node — on failure too, because a failing
42
+ round that wrote nothing would erase the only evidence that a node is churning. Every `breaks`
43
+ finding carries the `check` that will prove its fix, so it becomes a node the next round can close.
44
+ At the ceiling (`--ceiling`, default 3) the gate **measures rather than stops**: it names the tier
45
+ that has failed *every* round, because the same tier failing three times is a planning defect
46
+ wearing a verification failure's clothes, and different tiers each round is churn across levels.
47
+
48
+ ### The verification of this change, and what it caught in itself
49
+
50
+ `test/certify_mutations.py` disables each of the gate's sixteen rules in a copy of the tree and
51
+ requires a fixture whose name begins `certify:` to notice. **16 of 16 noticed.** It is wired into
52
+ `test:all`, and it exists because the first two attempts at this pass were both wrong:
53
+
54
+ * The first reported **11 of 11 killed** and proved nothing — every mutant had died of the same
55
+ unrelated fixture, because the copy has no `.git` and one pre-existing case checks the commit
56
+ stamp. A mutant is killed only by a `certify:` fixture now.
57
+ * Fixing that reported **0 of 16** and explained why: the twenty new fixtures had been inserted
58
+ **below** `graph_test.py`'s `if failures: sys.exit(1)`, so any earlier failure skipped all of
59
+ them. Locally everything was green and they ran; in CI, on the run where they mattered, they
60
+ would not have. The block moved above the summary, and the mutation pass now asserts its own
61
+ control — that exactly twenty ran and none was red — before reporting a single result.
62
+
63
+ 149 graph fixtures, 20 of them new. `references/certification.md` is the doctrine; the three
64
+ agents ship beside `verifier.md`, which now points at them.
65
+
66
+ ### The first live dispatch, and what it caught
67
+
68
+ The fixtures prove the **gate**. Whether the shipped agent prose yields a usable report is a live
69
+ dispatch, so one was run: all three tiers against `sheleg-design@f88c14b`, a strict reduced-motion
70
+ check that had shipped green through a single verdict hours earlier.
71
+
72
+ All three returned a well-formed eight-key report. **Two returned `fail`, with five `breaks`
73
+ findings between them.** The unit tier ran the node's check in an isolated worktree at the
74
+ certified commit, ran its own negative control, and put a cross-level observation in
75
+ `not_examined` rather than claiming it — which is the instruction that keeps the levels apart.
76
+
77
+ What the independence bought, stated as measurements rather than as a claim about the design:
78
+
79
+ * the **seam** tier instrumented the gate's `check()` calls and found the ratchet prices the
80
+ *exception* rather than the rule — collapsing the four durations the requirement names as its
81
+ first remediation dropped the count by 4 against its floor and turned the suite red on the
82
+ stricter answer
83
+ * the **product** tier planted a defect and watched it pass: a duration whose token name carries
84
+ no duration word was outside the walk entirely, and thirteen such tokens across ten layers were
85
+ clean by habit rather than by the check
86
+ * **both outer tiers independently** found a propagation-matrix row still stating the obligation
87
+ the change had replaced — two blind readings converging is the signal that they are reading
88
+ different things
89
+
90
+ None of the five is a bug in the changed lines, which is the class this gate was built for. They
91
+ are fixed in `sheleg-design@874ba17`; four further findings are filed there as decisions rather
92
+ than repairs. The one about the author is filed too: every ratchet floor set that day sits below
93
+ its true count, because the sequence was measure, keep editing, restate.
94
+
95
+ Guards: 412 → **412**, and the flatness is the honest number — written in that exact shape
96
+ because the guard reads it: this release adds no validator
97
+ negative, because every rule it adds lives in `scripts/graph.py` rather than in `validate.py`, and
98
+ `.github/workflows/validate.yml` plants defects for the second. The certification's own negative
99
+ control is `npm run test:certify` — **16 mutations, 16 noticed** — wired into `test:all` beside
100
+ the 14 property checks. A guard count that rose here would be a number borrowed from a suite that
101
+ never ran.
102
+
103
+ **The shape of that line is load-bearing, and this is the second time it has bitten.** The
104
+ v1.39.0 entry wrote the count with no colon, the pattern missed, and `npm test` was green over
105
+ a number it had never read. This release first wrote it with the bold around the whole phrase
106
+ instead of around the second number, and the same guard went silent again. The readable shape is
107
+ `Guards: N → **M**`, and the negative test that plants a stale count is the only reason either
108
+ miss was caught.
109
+
110
+ **Neither wrong form is reproduced here, and that is deliberate.** Writing the bad example with
111
+ real digits makes it a second readable count in this section — which is exactly what happened on
112
+ the first attempt: the plant removed the real count, the narrative about the mistake still
113
+ matched, and the guard stayed silent over a section that no longer stated anything. A document
114
+ that quotes a form as an example is indistinguishable from the form itself.
115
+
116
+ ## v1.73.0 — 2026-08-20 — the registry could not see the templates, the scripts, or its own registers
117
+
118
+
119
+ **The claim registry had the class for this exact incident and it fired on nothing.** Three
120
+ shipped surfaces said *34 reference files* over a directory of 35 — `scripts/graph.py`'s
121
+ `doctrine` docstring and `templates/run.md` twice — and `npm test` printed
122
+ `reference files: dormant (truth 35)`. Two holes, either of which was enough: the pattern
123
+ knew only the word order ``N files under `references/` ``, and the corpus was eight named
124
+ files plus `references/**`, so `templates/` and `scripts/` were never opened. The class now
125
+ reads any phrasing of the count, the corpus reads the templates and the shipped script, and
126
+ the class is armed at three agreeing sites instead of dormant.
127
+
128
+ **And it never read this repository's own registers.** `docs/DOCMAP.md` names decisions,
129
+ open questions, the board, the ledger and the retro as the registers, and its propagation
130
+ matrix sends *a number stated in a living document* to this registry — which could not see
131
+ any of them. `docs/OPEN_QUESTIONS.md` said *the 250 guards* against a workflow defining 390,
132
+ in a phrasing the guard class already knew. Bringing them in refused 26 statements and every
133
+ one was narration, so a number inside a **dated item** is a record and exempt; on the board
134
+ the discriminator is the State cell rather than the date, because every row names the day it
135
+ was filed and B-001 — open — was stating its description budget and its reference-file count
136
+ as facts about now.
137
+
138
+ **Fourteen consecutive releases carry no run stamp**, `v1.60.1` through `v1.72.0`, and the
139
+ retro's honest-gap section named only `v1.16.0`–`v1.23.0`. Its *Measured, not recalled*
140
+ receipt could not produce the measurement: it grepped `docs/superpowers/retro.md`, removed at
141
+ v1.53.0, and grepped for a tag's own commit when a stamp names the commit the *run* ended on.
142
+ Rewritten as a tag-range walk that reads the archive too, and a guard now requires every
143
+ release after the newest stamp to be named in that section.
144
+
145
+ Also: `## Unreleased` is where the guard count lives between a tag and the next bump (B-104);
146
+ `Environment` is a required cell on every verification row with the vocabulary read out of
147
+ the shipped template (B-099's other half); the graph schema states its three node rules
148
+ behind `$ref`s and the checker follows them (B-079, proved by moving them, not by a fixture);
149
+ an open board row's `file:N-M` must quote the phrase it points at, which caught five stale
150
+ citations and then caught this change's own edits four more times; the acceptance ladder is
151
+ policy **`AP-1`** with an owner and an in-force date, and `gates.md`'s *the framework fixes
152
+ no stage count* is scoped to the pipeline's shape; `read:` and `gate:` are reported
153
+ **unattested** instead of claimed *never agent-written*, because the ledger is the file the
154
+ agent appends to at every stage; `validate.yml` no longer re-validates a SHA on its own tag
155
+ push; and every documented `npm` equation is compared against `package.json` — `CLAUDE.md`
156
+ had `npm test` as `validate.py` alone, dropping 129 graph cases.
157
+
158
+ **What the suite found that no reading did.** This change broke **thirteen** existing
159
+ plants and `npm run test:all` named every one: the two verification-header probes spell
160
+ the whole header and it gained a column; two CHANGELOG probes scoped themselves to
161
+ `^## v` and the count now lives in `## Unreleased`; **seven** graph-schema probes walk
162
+ `node.allOf` inline and the rules moved behind `$ref`. All thirteen were repaired by
163
+ deriving the guard's own scope rather than restating it, which is what `learned.md`'s
164
+ *sweep the class* asks for — and the sweep needed two rounds: six schema probes were
165
+ repaired together and the seventh surfaced on the next full run, having died on
166
+ `KeyError: 'if'` where the others had died on their own asserts. A class fixed in six of
167
+ seven places is the shape standing instruction R-003 exists for.
168
+
169
+ One of the twelve was sharper than the rest, and it was self-inflicted twice over: the
170
+ honesty note explaining that `OQ-0002` no longer restates a total put an ISO date in the
171
+ row, which made the row a dated record and **disarmed the register plant** — green over
172
+ exactly the stale total it had been written to catch. `OQ-####` now uses its `Status`
173
+ cell, the discriminator the board already uses, and the register plant sits on an open
174
+ board row where no prose edit beside it can turn it off.
175
+
176
+ Guards: 390 → **412** · property checks 9 → **14**
177
+
178
+
3
179
  ## v1.72.0 — a node says how it will be closed
4
180
 
5
181
  **B-080 closed, and with it the last of four requirements this pack's own manifesto named
package/CONTRIBUTING.md CHANGED
@@ -531,6 +531,67 @@ the two homes are compared directly: whatever the verifier is told to read off t
531
531
  must be a property the schema declares.
532
532
  *(guard: `node declares no` and `rule that can fire` and `off the node, and`)*
533
533
 
534
+ **57. The claim registry reads every surface that states a number, including the ones this
535
+ repository writes about itself.** Its corpus was eight named files plus `references/**`, so
536
+ `templates/` and `scripts/` were invisible — three shipped surfaces said "34 reference files"
537
+ over a directory of 35 while the class printed `dormant` — and so were the registers
538
+ `docs/DOCMAP.md` names, where `docs/OPEN_QUESTIONS.md` said "the 250 guards" against a
539
+ workflow defining 390. The corpus now covers both, and a class recognises every phrasing of
540
+ its count rather than one word order. A number inside a **dated item** in a register is a
541
+ record and exempt; on the board the discriminator is the **State** cell, because every row
542
+ names the day it was filed and an open row is a claim about now.
543
+ *(guard: `reference files` and `— derive the number or delete it`)*
544
+
545
+ **58. Every documented `npm` command means what `package.json` runs.** `CLAUDE.md` glossed
546
+ `npm test` as `python3 test/validate.py`, dropping `graph_test.py` and its 129 cases — the
547
+ suite-outside-the-run class stated the other way round — and called `npm run test:all` "both"
548
+ where it runs eight scripts. An equation is compared against the script body after one level
549
+ of `npm run` resolution, and a bare `npm run X` in a document about this repository must be a
550
+ script that exists. Portable doctrine under `plugins/` and `cursor/` is out of scope: it names
551
+ a host project's commands.
552
+ *(guard: `is glossed as` and `declares no such script`)*
553
+
554
+ **59. A release either carries a run stamp or is recorded as a gap, and the guard-count claim
555
+ has a home before the bump.** Fourteen consecutive releases had no stamp while the retro named
556
+ only `v1.16.0`–`v1.23.0`, and the receipt that was supposed to prove it grepped a path removed
557
+ at v1.53.0 for a tag's own commit — a stamp names the commit the *run* ended on. Scoped to the
558
+ trailing stretch: 84 of 117 tags predate the register and backfilling is forbidden. Separately,
559
+ the count guard reads the topmost `## ` section, so `## Unreleased` is where the number lives
560
+ between a tag and the next bump, and it must sit above every version heading.
561
+ *(guard: `release(s) after the newest run stamp` and `section sits below a released version`)*
562
+
563
+ **60. A `file:line` range in an open board row quotes the phrase it points at.** Five
564
+ citations resolved to real lines and pointed at other text; a line number is the most fragile
565
+ address a document carries, because every edit above it moves it and nothing notices. Closed
566
+ rows are records and are left alone; single-line citations cannot be quoted and are disclosed
567
+ as unanchored rather than failed.
568
+ *(guard: `quotes no phrase from it`)*
569
+
570
+ **61. Every evidence row records the environment it ran in, and a claim of provenance the
571
+ format cannot check is marked unattested instead.** `Observed at` said which tree a check saw
572
+ and nothing said where it ran, so a preview smoke test and a production one entered the record
573
+ in the same shape — in a pack whose own `learned.md` records a suite green on every author's
574
+ machine and 1039 failures on a clean runner. And `read:`/`gate:` were declared *hook-written,
575
+ never agent-written* while both land in the file the agent appends to at every stage: no writer
576
+ field, no provenance check, so the count is reported `unattested` and the claim is not made.
577
+ *(guard: `has no `Environment` cell` and `prints a count and never says`)*
578
+
579
+ **62. The acceptance ladder is a versioned policy with an owner, and `gates.md`'s
580
+ fixes-nothing sentence is scoped to the pipeline's shape.** Both rules stood unscoped side by
581
+ side for seventy releases — *the framework fixes no stage count and no gate assignment* beside
582
+ twelve fixed criteria — so a reader could take either as the whole rule, and a table accepted
583
+ under v1.20 doctrine was indistinguishable from one accepted under v1.70. The block carries
584
+ `AP-1`, an owner and an in-force date; an amendment moves the version and lands with a decision
585
+ row.
586
+ *(guard: `the acceptance policy carries no` and `stands unscoped beside`)*
587
+
588
+ **63. A heading may not declare a bound nothing enforces.** The retro's *Recent log* read
589
+ *entries from the last five run stamps* over 25 entries reaching back nine days, borrowing the
590
+ stamp section's wording without its cap — filed twice as B-060 and B-069 and disclosed by the
591
+ file about itself. Checked in the live retro and in `templates/retro.md`, which seeded the
592
+ false bound into every host project.
593
+ *(guard: `declares the bound` and `and nothing enforces it`)*
594
+
534
595
  ## Adding or changing doctrine
535
596
 
536
597
  - **Change one idea per PR.** These files are read by agents under load; a PR that
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.72.0 |
15
+ | **Version** | 1.74.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.72.0",
3
+ "version": "1.74.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.72.0",
5
+ "version": "1.74.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` |