task-pipeline-skill 1.78.4 → 1.80.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 (43) hide show
  1. package/CHANGELOG.md +184 -2
  2. package/README.md +5 -3
  3. package/SKILL-CARD.md +1 -1
  4. package/cursor/rules/task-pipeline.mdc +3 -1
  5. package/evals/RESULTS.md +212 -9
  6. package/evals/evidence-docs.evals.json +109 -0
  7. package/evals/project-audit.evals.json +108 -0
  8. package/evals/run.py +47 -19
  9. package/package.json +1 -1
  10. package/plugins/task-pipeline/.claude-plugin/plugin.json +3 -2
  11. package/plugins/task-pipeline/commands/task-pipeline.md +2 -1
  12. package/plugins/task-pipeline/hooks/build-gate.sh +6 -1
  13. package/plugins/task-pipeline/hooks/gate-observer.sh +35 -14
  14. package/plugins/task-pipeline/hooks/release-gate.sh +70 -9
  15. package/plugins/task-pipeline/skills/evidence-docs/SKILL.md +33 -2
  16. package/plugins/task-pipeline/skills/project-audit/SKILL.md +7 -2
  17. package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +50 -57
  18. package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +4 -4
  19. package/plugins/task-pipeline/skills/task-pipeline/references/acceptance.md +1 -1
  20. package/plugins/task-pipeline/skills/task-pipeline/references/adoption.md +15 -4
  21. package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +4 -2
  22. package/plugins/task-pipeline/skills/task-pipeline/references/backlog.md +6 -0
  23. package/plugins/task-pipeline/skills/task-pipeline/references/brainstorm.md +6 -1
  24. package/plugins/task-pipeline/skills/task-pipeline/references/build.md +11 -1
  25. package/plugins/task-pipeline/skills/task-pipeline/references/certification.md +7 -0
  26. package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +33 -2
  27. package/plugins/task-pipeline/skills/task-pipeline/references/documentation.md +2 -2
  28. package/plugins/task-pipeline/skills/task-pipeline/references/exposure.md +7 -3
  29. package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +17 -185
  30. package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +1 -1
  31. package/plugins/task-pipeline/skills/task-pipeline/references/model-tiering.md +13 -0
  32. package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +2 -0
  33. package/plugins/task-pipeline/skills/task-pipeline/references/prioritisation.md +165 -0
  34. package/plugins/task-pipeline/skills/task-pipeline/references/probing.md +202 -0
  35. package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +8 -4
  36. package/plugins/task-pipeline/skills/task-pipeline/references/spec.md +60 -1
  37. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +52 -54
  38. package/plugins/task-pipeline/skills/task-pipeline/references/work-graph.md +1 -1
  39. package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +28 -5
  40. package/plugins/task-pipeline/skills/task-pipeline/templates/backlog.md +6 -2
  41. package/plugins/task-pipeline/skills/task-pipeline/templates/brief.md +4 -3
  42. package/plugins/task-pipeline/skills/task-pipeline/templates/retro.md +6 -4
  43. package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +2 -2
@@ -0,0 +1,202 @@
1
+ # Probing — how a check is proven before it is trusted
2
+
3
+ The authoring doctrine for **probes**: planting a defect, watching the guard
4
+ refuse it, and keeping that proof alive after the release that ships it. It
5
+ lived inside [`gates.md`](gates.md) until 2026-08-31 and moved here whole —
6
+ one home, routed from the gate doctrine it proves. The law it serves is
7
+ [`audit.md`](audit.md)'s exit criterion: **a green from a check nobody has
8
+ watched fail is not evidence.**
9
+
10
+ ## Contents
11
+
12
+ - Probing — plant, run, restore
13
+ - A probe rots, and every way it rots reports green
14
+ - The neighbour probe — plant the evidence outside the subject
15
+ - A green probe is evidence only if the mutation is known to have landed
16
+ - Rationalizations
17
+
18
+ ## Probing — plant, run, restore
19
+
20
+ The law is [`audit.md`](audit.md)'s exit criterion. The procedure is this, and it is
21
+ not optional for a check you intend to trust:
22
+
23
+ ```bash
24
+ cp -R . /tmp/probe && cd /tmp/probe
25
+ python3 - <<'PY' # plant IN PYTHON, never sed -i
26
+ p = "docs/DECISIONS.md"
27
+ s = open(p).read().replace("DEC-0001", "DEC-9999", 1)
28
+ open(p, "w").write(s)
29
+ PY
30
+ bash scripts/check-docs.sh; echo "planted -> exit=$?" # MUST be non-zero
31
+ cd - && bash scripts/check-docs.sh; echo "clean -> exit=$?" # MUST be 0
32
+ ```
33
+
34
+ **Assert on `$?`, not on a `FAIL` line in the output.** A line on stdout is a
35
+ decoration; the exit code is what CI reads.
36
+
37
+ **Doubt the probe before you doubt the check.** On the project this comes from,
38
+ **four of five** silent probes were the probe's fault: one added a *definition*
39
+ where the check looks for an unresolved *reference*; one edited a string whose
40
+ whitespace did not match; one hit the first prose mention instead of the table row;
41
+ one flipped a row whose cell was already empty, so nothing was planted. A silent
42
+ check is a claim about two things, and the probe is the one to doubt first — prove
43
+ your edit landed in the text the check actually parses.
44
+
45
+ **Three assertions, and the third is the one hand-rolled probes keep missing.** A
46
+ plant that trips some *other* check has proved that other check works. Three probes
47
+ in one day passed that way: one removed 1 of 3 identical lines and left the shape
48
+ intact; one decremented a number inside an **already-released** section; one deleted
49
+ the shouted spelling of a phrase and left the lowercase one. Each landed somewhere
50
+ real and demonstrated nothing about the guard it was written for. So:
51
+
52
+ 1. **the substitution landed** — `replace` matching nothing returns the string
53
+ unchanged and raises nothing;
54
+ 2. **the exit code is non-zero** — not a `FAIL` line on stdout;
55
+ 3. **the message that fired belongs to the guard under test** — named up front, not
56
+ recognised afterwards.
57
+
58
+ `test/probe.py` does all three (`npm run test:probe` self-tests the harness, including
59
+ its own failure branches, because a harness whose failure path has never executed is
60
+ the thing it exists to stop). Declare the plant as `Plant(label, path, old, new,
61
+ expect=<the guard's own words>)` rather than hand-rolling a fourth copy of the loop.
62
+
63
+ **Record the probe.** One line per section, in the change that ships the check.
64
+ Otherwise the next reader has to redo it to know whether it was ever done.
65
+
66
+ ---
67
+
68
+ ## A probe rots, and every way it rots reports green
69
+
70
+ The three assertions above prove a probe works **today**. They say nothing about the
71
+ day after, and a probe is uniquely exposed: the thing it guards is the thing that moves
72
+ it. Four rotted in one release on 2026-08-22, and none of them failed loudly — two
73
+ reported `caught`, two reported nothing at all.
74
+
75
+ **1. The anchor is a literal the guarded thing moves.** A probe pinned to *"the bundle
76
+ is N reference files"* stops landing the day a reference file is added — on the release
77
+ that changes the very number it guards. Same for a version, a count word, a phrase a
78
+ release rewrites. **Derive the anchor**: read whatever the text currently says and make
79
+ *that* wrong. A probe that computes `wrong = stated - 1` never needs maintaining.
80
+
81
+ **2. The precondition is inherited from the tree rather than created.** This is the
82
+ subtle one, because it is triggered by the system working correctly. A probe that
83
+ narrows a declared gap to expose *releases after the newest run stamp* has nothing to
84
+ expose the moment a release writes an honest stamp — the newest release is now the
85
+ newest stamp. A probe requiring an `## Unreleased` section has none the moment a release
86
+ absorbs it. **Both landed. Both proved nothing.** A probe must construct the state it
87
+ needs: remove the stamp, write the section, then plant the defect.
88
+
89
+ **3. The document quotes the form the probe removes.** Covered as
90
+ [`documentation.md`](documentation.md) canon 2's second half, and it belongs here too
91
+ because the probe is where it surfaces: prose describing the wrong shape *with real
92
+ values in it* is a second instance of the shape. The probe deletes the real one, the
93
+ narrative still matches, the guard is silent.
94
+
95
+ **How to see it before CI does.** Ask one question per probe: *what does this probe
96
+ LOOK FOR, and who is allowed to change it?* Where the answer is "the thing it guards",
97
+ it is rotting already. A repository-wide sweep is one grep — a two-digit literal inside
98
+ a needle, an `assert` or a `replace` — and the triage is: the number a probe **writes**
99
+ is correct, the number it **looks for** is the defect.
100
+
101
+ ## The neighbour probe — plant the evidence outside the subject
102
+
103
+ A probe proves a guard rejects **the phrasing its author had in mind**. That is less than
104
+ it looks, and the gap has one shape: **a check answered by text that is not its subject.**
105
+
106
+ Measured on one project in one session, six times, each found by a reader planting a
107
+ defect and watching `PASS`:
108
+
109
+ | The check's subject | The text that answered it instead |
110
+ |---|---|
111
+ | stage 2 names the loop's arming | *"it **arms** the UX track"*, present since an earlier release |
112
+ | the section states the authorization floor | the same phrase in a Rationalizations row, and in a section written twenty-nine releases before |
113
+ | the run stamps have a cap | the **standing instructions'** `max 10`, in the same table cell — and, once the check was narrowed past it, the same cap moved to the other side of the `·` |
114
+ | a section read in full stays capped | rows under **one** heading, while a second heading in the same file held forty more |
115
+
116
+ Every one of those guards had a probe. Every probe fired. The probes and the guards were
117
+ written in the same hour from the same reading, so they shared the same blind spot.
118
+
119
+ **So a guard that reads a scoped span owes a second probe, and it plants in two places at
120
+ once:**
121
+
122
+ 1. **break the subject** — remove the thing the guard is about;
123
+ 2. **plant the guard's own evidence next door** — in a sibling section, an adjacent table
124
+ cell, a rationalizations row, the other side of a separator;
125
+ 3. require the guard to **still fail**.
126
+
127
+ A guard that passes step 3 is reading its subject. A guard that goes green is reading the
128
+ neighbourhood, and the ordinary probe cannot tell the difference — which is why this one
129
+ is separate rather than a stricter version of it.
130
+
131
+ **Step 2 means the literal the predicate matches *today*, read out of the guard.** The
132
+ first three neighbour probes written against this section got that wrong on their first
133
+ run: one planted the needles of two **retired** predicates, which proves the guards that
134
+ used to exist were neighbour-answerable and says nothing about the one that does. A
135
+ neighbour probe keyed to a needle the guard no longer reads is the same defect it was
136
+ written to catch, one level up.
137
+
138
+ **A probe that only deletes is not a neighbour probe.** It may still be a correct probe —
139
+ but if it relies on copies that already sit next door, it must **assert they are there**.
140
+ Otherwise a later edit removes them and the probe quietly becomes a delete-only test that
141
+ still passes, having stopped testing the thing it is named for.
142
+
143
+ **Positional narrowing is not scoping.** Three of the six were "fixed" by cutting the
144
+ search down to a row, then to everything after a phrase, and fell each time to text that
145
+ was still inside the cut. Scope by *what the span is about*: split to the cell, then to
146
+ the item, then match on flattened text so an emphasis marker cannot hide the boundary.
147
+
148
+ **And state the span in the guard.** One line above the predicate — *what it reads, and
149
+ where that ends*. It costs nothing and it is the only part of a check a later reader can
150
+ disagree with before the defect arrives.
151
+
152
+ ## A green probe is evidence only if the mutation is known to have landed
153
+
154
+ Also reported from another project, three times in one day, each caught only because the
155
+ result was too good:
156
+
157
+ 1. a scripted substitution missed on indentation — the file was unchanged and the probe
158
+ measured nothing;
159
+ 2. an assertion written against a bare identifier kept matching the **import line** after
160
+ the field it guarded was deleted;
161
+ 3. a file-extension alternation matched the longer extension as though it were the
162
+ shorter, reporting nine live files as missing.
163
+
164
+ In all three the observable was identical to success. *"See it fail once"* has an unstated
165
+ precondition — **that the thing you changed is the thing the check reads** — and a planted
166
+ defect that did not land produces the same green as a check that cannot fail.
167
+
168
+ **So a probe that mutates an existing file asserts its plant landed, in the same breath as
169
+ planting it.** A probe that writes a whole file has no such question: the file exists or
170
+ the command failed. This repository measured itself while writing this section and got the
171
+ number wrong three times. A hand-rolled classifier said *206 of 206 already carry it*.
172
+ The guard written from the rule said **22 did not** — and was itself too narrow, matching
173
+ one spelling of the assertion, so six probes that already had it in lower case were
174
+ called defective. A sweep then "fixed" those six and **corrupted five**, splitting live
175
+ statements. The true figure was **16**, and it took the guard, a compile check and a
176
+ restore from git to find it.
177
+
178
+ Two things are worth keeping from that. **The check corrected the measurement that
179
+ motivated it** — which is the argument for writing checks rather than counting by hand.
180
+ And **a check keyed to one spelling of a rule is *The neighbour probe*'s class, above**: it
181
+ reported as defective the probes that obeyed the rule in different words. **Every**
182
+ mutating probe carries the assertion now — the figure is deliberately not
183
+ written here. A first draft said *201*, which was true of the branch point and false in
184
+ the same commit, because the twelve probes added for this release are themselves mutating
185
+ probes. The guard computes it; a number in prose beside a check that can count is the
186
+ class this bundle calls restating instead of computing. Probes that write a whole file
187
+ need none: the file exists or the command
188
+ failed.
189
+
190
+ **Prefer an assertion that names the construct over one that names a substring of it.** A
191
+ guard written against a bare identifier survives the deletion of everything it guarded,
192
+ because the identifier still appears in an import. That is case 2 above and it is the same class as
193
+ [`gates.md`](gates.md) → *A ratchet's matcher is itself a check, and it needs a near-miss*, one level down.
194
+
195
+ ## Rationalizations
196
+
197
+ | Excuse | Reality |
198
+ |---|---|
199
+ | "The check is green, that's evidence" | Only if you have seen it red. An unproven check is a decoration that reports success. |
200
+ | "I probed it when I wrote it" | A probe proves the check works today. The thing it guards is the thing that moves it — re-read the anchor on every release that touches the subject. |
201
+ | "The plant obviously landed, the file is smaller" | `replace` matching nothing returns the string unchanged and raises nothing. Assert the mutation landed, in the same breath as planting it. |
202
+ | "Some guard fired, so the probe passed" | A plant that trips some *other* check has proved that other check works. The message that fired must belong to the guard under test, named up front. |
@@ -63,7 +63,7 @@ changes:
63
63
  task-pipeline v1.34.0 · pipeline-audit · module P1 «the progress print» (1 of 4)
64
64
  0 ✓ 1 ✓ 2 ✓ 3 ▶ 4 · 5 · 6 · 7 · 8 · 9 · 10 ·
65
65
  ███████░░░░░░░░░░░░░░░░░░░ gates 3/11 · now 3 Spec · manual
66
- board B-028 · carry-over 0 rows · exposure 99 never · unlooked 0
66
+ board B-028 · carry-over 0 rows · exposure N never · unlooked 0
67
67
  ```
68
68
 
69
69
  Four lines, and each one answers a question an operator otherwise has to ask:
@@ -142,7 +142,8 @@ its pair is task start and iteration close. The hand-back shares one and adds th
142
142
  end — a rail at task start has nothing to report, and a run that ends without a hand-back
143
143
  is the case this section exists for. A reader resolving *"both boundaries"* against the
144
144
  other section wrote one at task start, where TASK is the only field with content. The run
145
- writes a hand-back with **four sections and two lists**. It is a gate criterion **at stage
145
+ writes a hand-back with **the sections and the two counted lists below** the
146
+ block is the count, and a number restated here said *four* over a block of six. It is a gate criterion **at stage
146
147
  10**, not a good intention: this file already carried one instruction with no gate behind
147
148
  it (*"copy it, tick it"*), and the v1.37.0 audit found no run had ever obeyed it.
148
149
 
@@ -380,7 +381,7 @@ And why the verb prints
380
381
  `unmeasured` rather than `0` when there are no such lines is the same reason again — the
381
382
  hook being absent and the run reading nothing are **opposite facts** the ledger cannot
382
383
  separate, so it claims neither. A `0` there would be the reassuring answer to a question
383
- nobody asked, over 35 files nobody checked.
384
+ nobody asked, over a directory of reference files nobody checked.
384
385
 
385
386
  It is a disclosure: no floor, no direction, never a target. The moment the number becomes
386
387
  something to raise, a run will open files to raise it.
@@ -416,7 +417,10 @@ written by any run — the detector had no input, and the guard was doctrine wea
416
417
  script's clothes. One file serves both readers: the guard reads the `touch:` lines,
417
418
  this block reads the verdict rows and the iteration counter.
418
419
 
419
- Three kinds of line, appended, never rewritten:
420
+ Appended, never rewritten. The full set of line shapes is declared under
421
+ `## Lines` in [`../templates/run.md`](../templates/run.md) — the list is the count,
422
+ and a count restated here drifted once already. The three these two readers
423
+ consume:
420
424
 
421
425
  ```
422
426
  stage: 3 Spec — gate manual — verdict pass — 2026-08-10T14:02Z
@@ -13,6 +13,7 @@ Writing the approved design down so a zero-context implementer — human or suba
13
13
 
14
14
  - Order of operations
15
15
  - UX track (user-facing tasks only)
16
+ - The COPY and VISUAL tracks, and their convergence
16
17
  - Write the spec
17
18
  - Module dossier — when the run is one brick of a platform
18
19
  - Self-review — before showing it
@@ -65,6 +66,60 @@ These skills are **idempotent** — extend the existing `docs/ux/` layers, never
65
66
  rebuild them. If the chain already exists and is validated (typically when the run
66
67
  entered from super-ux), verify it and embed it; build only what's missing.
67
68
 
69
+ ## The COPY and VISUAL tracks, and their convergence
70
+
71
+ Three tracks, three questions, and they do not substitute for each other:
72
+ super-ux decides what the interface must **do**; `copywriting` how it
73
+ **sounds**; `sheleg-design` how it **looks**. Until 2026-08-10 stage 3 named
74
+ only the first, so a run designed a flow, then wrote its strings by taste and
75
+ picked its values at the keyboard — and every gate in the pipeline reported
76
+ green over both.
77
+
78
+ - **COPY track — how it sounds.** Every string a product's user will read is written
79
+ through super-ux's `copywriting`, against the brand pack (`docs/brand/voice.md`,
80
+ `terminology.md`, `facts.md`). No pack ⇒ `/brand-init` **before** the first string,
81
+ not after: a voice reverse-engineered from copy already written is a description of
82
+ what happened, not a decision. In scope: interface strings, errors, empty states,
83
+ the landing, pricing, the user-facing changelog. **Out of scope, and saying so is
84
+ what keeps the track honest:** commit messages, PR descriptions, code comments, a
85
+ developer README, internal docs. Running a brand pack over a line in a contributors'
86
+ changelog is the fastest way to teach an agent to route around the track.
87
+ - **VISUAL track — how it looks.** Where the task has a visual surface, the visual
88
+ layer goes through `sheleg-design` ([`companion-skills.md`](companion-skills.md)):
89
+ tokens and themes, typography and rhythm, motion and how it degrades to rest, the
90
+ boundary with Figma (tokens as variables, never raw values carried across). Not
91
+ through it: a purely structural change — what sits where is the UX track's — text,
92
+ a backend, an internal script.
93
+ - **Each track's refusal is a sentence, never a silence.** *"Без дизайна" / "as is"*
94
+ ends the visual track; *"без бренда" / "draft"* ends the copy track. Either one is
95
+ the operator's to make and costs nothing — but it is **recorded in the brief and
96
+ said out loud in the close-out**, because a track skipped silently and a track that
97
+ ran are the same thing in a transcript. This is the rail's `⊘` rule one layer up: a
98
+ skip nobody can see is indistinguishable from work that happened.
99
+ - **Two of the three are a parallel layer, and the third is their only real dependency.**
100
+ COPY and VISUAL both consume the UX track's scenarios; **neither consumes the other**.
101
+ Copy is written against the brand pack and the scenarios, not against tokens; the visual
102
+ is built from the frame and the style pack, not from strings. Writing them in a line —
103
+ which this doctrine did until 2026-08-15 — teaches a run to wait for a result that never
104
+ arrives. The order is `UX → { COPY ∥ VISUAL }`, and the only thing crossing each of
105
+ those two arrows is **the scenario set**.
106
+ - **Their convergence needs a check, and it has a real contradiction to catch.** Both land
107
+ on the same screen, so the failure is not that one is wrong: it is that each is right
108
+ alone and they disagree together. Before the spec is committed, compare the two outputs
109
+ and record the answer:
110
+ 1. **A string the layout has no room for** — a label, an error or an empty state longer
111
+ than the frame's element, at the frame's own width.
112
+ 2. **A state one track has and the other does not** — copy for an empty state the design
113
+ never drew, or a loading state drawn with no string.
114
+ 3. **Two names for one thing** — the design system's component name against the
115
+ terminology file's noun, where a user reads both.
116
+ 4. **A tone the visual contradicts** — a calm, plain register on a screen whose motion
117
+ and colour say urgency.
118
+ `Tracks converge: clean` is the answer most runs write, and writing it is the point —
119
+ a check whose silence is indistinguishable from not having run is not evidence. This is
120
+ the same rule the harvest applies at stage 0 and the build applies to a fanned-out group
121
+ at stage 5 ([`build.md`](build.md) §4.2a); one shape, three places.
122
+
68
123
  ## Write the spec
69
124
 
70
125
  Path: `<artifacts>/specs/YYYY-MM-DD-<topic>-design.md`, committed, **same
@@ -212,7 +267,11 @@ and every REQ in the brief appears in at least one section.** For UI tasks it
212
267
  additionally requires: the chain (foundation → flows → screens → scenarios)
213
268
  designed, validated and approved; `/ux-lint` green; every user-facing requirement
214
269
  traced to a scenario ID — or an explicit waiver from the operator recorded in the
215
- spec.
270
+ spec. **Every user-facing string went through the COPY track or the refusal is
271
+ recorded, the visual layer went through the VISUAL track or the refusal is
272
+ recorded** — a recorded refusal passes and an unmentioned one does not — **and
273
+ where both tracks ran, their convergence check is recorded**: findings with the
274
+ ruling, or `Tracks converge: clean` (*The COPY and VISUAL tracks*, above).
216
275
 
217
276
  With Figma on, one more, and it is mechanical: **the canonical record names a file,
218
277
  and every frame link in `screens.md` carries that same file key.** Deep links are
@@ -2,7 +2,9 @@
2
2
 
3
3
  For each stage: what it does, what to invoke, artifacts, and the **GATE** that
4
4
  must pass before advancing. Each gate is tagged with its **type** — `auto` (the
5
- orchestrator verifies the check itself, pass/fail) or `manual` (wait for the
5
+ orchestrator verifies the check itself, pass/fail), `judgment` (a named judge
6
+ rules where no complete deterministic check exists — [`gates.md`](gates.md) →
7
+ *The judgment gate*) or `manual` (wait for the
6
8
  operator's explicit go). These stages (0 intake + 1→10) are the plugin's
7
9
  **example** flow, encoded in `pipeline.example.json` against the universal contract
8
10
  `pipeline.schema.json`; a host project replaces it with its own
@@ -66,6 +68,7 @@ never that the work was skipped quietly.
66
68
  - Cross-cutting — the Doc Loop
67
69
  - Cross-cutting — the loop guard
68
70
  - Cross-cutting — the audit
71
+ - A stage that produces text a user will read
69
72
 
70
73
  ## 0 — Intake grill — MANDATORY
71
74
  - **Freedom: medium** — the interview adapts to the answers; its two phases and their order do not ([`gates.md`](gates.md) → *Axis C*).
@@ -148,9 +151,10 @@ never that the work was skipped quietly.
148
151
 
149
152
  ```
150
153
  1. files the request names, resolved git ls-files -- <paths> -> N
151
- 2. any of them a public contract the version-synced surfaces,
152
- pipeline.schema.json, the
153
- command, the README -> yes/no
154
+ 2. any of them a public contract the surfaces the host's release
155
+ syncs, its schema or config
156
+ contracts, a shipped command,
157
+ its README -> yes/no
154
158
  3. behaviour a user or a caller observes changes -> yes/no
155
159
  ```
156
160
 
@@ -335,55 +339,18 @@ never that the work was skipped quietly.
335
339
  never rebuild from scratch. If the chain already exists and is validated (e.g.
336
340
  the task entered from super-ux), just verify (linter green) and embed it into
337
341
  the spec; only build the parts that are missing.
338
- - **COPY track how it sounds.** Every string a product's user will read is written
339
- through super-ux's `copywriting`, against the brand pack (`docs/brand/voice.md`,
340
- `terminology.md`, `facts.md`). No pack `/brand-init` **before** the first string,
341
- not after: a voice reverse-engineered from copy already written is a description of
342
- what happened, not a decision. In scope: interface strings, errors, empty states,
343
- the landing, pricing, the user-facing changelog. **Out of scope, and saying so is
344
- what keeps the track honest:** commit messages, PR descriptions, code comments, a
345
- developer README, internal docs. Running a brand pack over a line in a contributors'
346
- changelog is the fastest way to teach an agent to route around the track.
347
- - **VISUAL track how it looks.** Where the task has a visual surface, the visual
348
- layer goes through `sheleg-design` ([`companion-skills.md`](companion-skills.md)):
349
- tokens and themes, typography and rhythm, motion and how it degrades to rest, the
350
- boundary with Figma (tokens as variables, never raw values carried across). Not
351
- through it: a purely structural change — what sits where is the UX track's — text,
352
- a backend, an internal script.
353
- - **Each track's refusal is a sentence, never a silence.** *"Без дизайна" / "as is"*
354
- ends the visual track; *"без бренда" / "draft"* ends the copy track. Either one is
355
- the operator's to make and costs nothing — but it is **recorded in the brief and
356
- said out loud in the close-out**, because a track skipped silently and a track that
357
- ran are the same thing in a transcript. This is the `⊘` rule one layer up: a skip
358
- nobody can see is indistinguishable from work that happened.
359
- - **Three tracks, three questions, and they do not substitute for each other.** super-ux
360
- decides what the interface must **do**; `copywriting` how it **sounds**;
361
- `sheleg-design` how it **looks**. Until 2026-08-10 this stage named only the first,
362
- so a run designed a flow, then wrote its strings by taste and picked its values at the
363
- keyboard — and every gate in the pipeline reported green over both.
364
- - **Two of the three are a parallel layer, and the third is their only real dependency.**
365
- COPY and VISUAL both consume the UX track's scenarios; **neither consumes the other**.
366
- Copy is written against the brand pack and the scenarios, not against tokens; the visual
367
- is built from the frame and the style pack, not from strings. Writing them in a line —
368
- which this file did until 2026-08-15 — teaches a run to wait for a result that never
369
- arrives. The order is `UX → { COPY ∥ VISUAL }`, and the only thing crossing each of
370
- those two arrows is **the scenario set**.
371
- - **Their convergence needs a check, and it has a real contradiction to catch.** Both land
372
- on the same screen, so the failure is not that one is wrong: it is that each is right
373
- alone and they disagree together. Before the spec is committed, compare the two outputs
374
- and record the answer:
375
- 1. **A string the layout has no room for** — a label, an error or an empty state longer
376
- than the frame's element, at the frame's own width.
377
- 2. **A state one track has and the other does not** — copy for an empty state the design
378
- never drew, or a loading state drawn with no string.
379
- 3. **Two names for one thing** — the design system's component name against the
380
- terminology file's noun, where a user reads both.
381
- 4. **A tone the visual contradicts** — a calm, plain register on a screen whose motion
382
- and colour say urgency.
383
- `Tracks converge: clean` is the answer most runs write, and writing it is the point —
384
- a check whose silence is indistinguishable from not having run is not evidence. This is
385
- the same rule the harvest applies at stage 0 and the build applies to a fanned-out group
386
- at stage 5 ([`build.md`](build.md) §4.2a); one shape, three places.
342
+ - **COPY track and VISUAL track a parallel layer after UX, and their
343
+ convergence.** Three tracks, three questions, none substituting for another:
344
+ super-ux decides what the interface must **do**, `copywriting` how it
345
+ **sounds** (every string a product's user will read, against the brand pack),
346
+ `sheleg-design` how it **looks** (tokens, themes, typography, motion). COPY and
347
+ VISUAL are **parallel** both consume the UX track's scenarios, neither
348
+ consumes the other and where both ran, **their convergence check is run and
349
+ recorded before the spec is committed**: each can be right alone and the two
350
+ disagree together on one screen. The full doctrine each track's scope and
351
+ out-of-scope, the refusal sentences, the four contradictions the check
352
+ catches is [`spec.md`](spec.md) → *The COPY and VISUAL tracks, and their
353
+ convergence*, its one home.
387
354
  - **Spec:** write the approved design to
388
355
  `<artifacts>/specs/YYYY-MM-DD-<topic>-design.md` and commit it. Lock all
389
356
  shared contracts (types, schemas, signatures, file layout). For UI tasks the
@@ -467,6 +434,12 @@ never that the work was skipped quietly.
467
434
  implementer that wrote it is still dispatched. The matrix pointed this companion at
468
435
  stages 5–6 from the day it was added and **this stage had never named it** — found by
469
436
  the guard comparing the two, not by a reader.
437
+ - **Domain companions, where the task is on their ground** — recommended, never a
438
+ gate ([`companion-skills.md`](companion-skills.md)): `sheleg-dev` when the build
439
+ wires money, tracking, sign-in or page speed; `agent-stack` when the thing being
440
+ built is an agent system; `telegram-dev` when Telegram is the platform rather
441
+ than the transport. Absent, the build ships on the host's own doctrine and the
442
+ close-out names the seam nobody checked.
470
443
  - **Integration closes the stage:** sync with the base branch, re-run the full suite
471
444
  on the result, land it the project's way (merge, or a PR — outward, so it needs a
472
445
  go), remove the worktree. Stages 7–9 act on the integrated result, so a branch the
@@ -492,7 +465,9 @@ never that the work was skipped quietly.
492
465
  same as stage 5.
493
466
  - **GATE (auto):** **the hygiene gate green over the whole tree**, its six counts
494
467
  printed beside their floors; the **full** suite is green (not just the new tests); new/changed code
495
- is covered; no `skip`/`xfail` smuggling a red suite past the gate. Never advance
468
+ is covered; **every check this run added or widened has been probed both ways —
469
+ seen rejecting a planted defect and passing the clean tree, asserted on its exit
470
+ code** ([`probing.md`](probing.md)); no `skip`/`xfail` smuggling a red suite past the gate. Never advance
496
471
  to deploy on a red or partial run. **The carry-over count is printed beside this
497
472
  verdict** — a ratchet nobody prints is a TODO with a better name
498
473
  ([`audit.md`](audit.md)) — **and so are the disclosures**, `abstained` and
@@ -589,6 +564,10 @@ never that the work was skipped quietly.
589
564
  on anything but `success`, and one of the three states stated — including **`no run
590
565
  found`**, out loud, because a project without CI is a legitimate state and not a
591
566
  green one.
567
+ - **A public surface a logged-out reader will see** can additionally be audited for
568
+ search and answer-engine visibility with `seo-aeo-audit`
569
+ ([`companion-skills.md`](companion-skills.md)) — recommended, never a gate;
570
+ absent, visibility ships unaudited and the close-out says so.
592
571
  - **GATE (auto):** clean boot confirmed, or an **honest degradation report** with next
593
572
  steps — never silent success. **The CI verdict is one of the reported facts, with
594
573
  its run id** — "CI is green" written without a command behind it prints the same
@@ -860,3 +839,22 @@ nothing.
860
839
  - **Whatever can't be fixed now becomes a ratchet** — a named, counted set that may
861
840
  only shrink, printed beside every gate verdict, so "green" never reads as
862
841
  "verified".
842
+
843
+ ## A stage that produces text a user will read
844
+
845
+ The framework has no opinion on which skills run where — `pipeline.json` is the contract —
846
+ but two facts about copy are worth stating once, because a stage author rediscovers them
847
+ expensively.
848
+
849
+ **Route it through the copy skill, not through the stage's own prose.** A stage that writes
850
+ interface strings, a landing page or a changelog entry for users is producing brand surface;
851
+ the registers, terminology and canonical facts that constrain it live in `docs/brand/`, and
852
+ a stage writing directly does not read them.
853
+
854
+ **The humanization pass is decided once, in the brand pack, not per run.**
855
+ `docs/brand/voice.md` carries an optional `Humanization pass:` field; absent means nobody
856
+ has been asked, and the copy skill asks once and records the answer. A pipeline that asks
857
+ every run has turned a settled decision into a prompt, and a pipeline that gates on a
858
+ marker count has turned a writing-quality signal into a verdict — the false positives fall
859
+ hardest on people writing in a second language, and nothing here fails a stage for them.
860
+ `npx sshlg-skills humanizers` lists what is installed.
@@ -40,7 +40,7 @@ turn of every loop.
40
40
  | `nodes[].serves` | the REQ or goal clause it exists for. A node serving neither is **parked with that as the reason** |
41
41
  | `nodes[].blocked_by` | what must close first. This is what the frontier obeys |
42
42
  | `nodes[].touches` | what it **mutates**. Two runnable nodes writing one file is the false parallelism [`planning.md`](planning.md) refuses — *distinct is not the same as independent, and the check is what they touch, never what they are called* |
43
- | `nodes[].check` | **how this node will be closed** — one command, or the named judgement where no command can decide it. Required on every node except a `parked` one. `agents/verifier.md` runs it and reports its output as the evidence row; before this field existed that instruction pointed at an absence, leaving the verifier the two things it forbids — invent a check, or run everything (B-080) |
43
+ | `nodes[].check` | **how this node will be closed** — one command, or the named judgement where no command can decide it. Required on every node except a `parked` one. The certification's `unit` tier runs it and reports its output as the evidence row ([`certification.md`](certification.md) — three blind tiers close a node, not one reader); before this field existed that instruction pointed at an absence, leaving a verifier the two things it forbids — invent a check, or run everything (B-080) |
44
44
  | `nodes[].evidence` | required when `status` is `done`. A node called done by assertion is what evidence exists to prevent |
45
45
  | `nodes[].parked_reason` | required when `status` is `parked`. A park with no reason is indistinguishable, a week later, from work quietly dropped |
46
46
  | `edges[].payload` | what the dependency hands over. **An edge carrying no named artifact is chronology drawn as architecture** |
@@ -549,8 +549,14 @@ SEVERITIES = ("breaks", "risk")
549
549
  # A tier report that quotes another tier's verdict was not written blind. Cheap
550
550
  # to detect and worth detecting: the failure it prevents is three reports that
551
551
  # agree because the second two read the first.
552
- CROSS_TIER = re.compile(r"\b(?:unit|seam|product)\s+tier\s+(?:passed|failed|says)"
553
- r"|\btier\s+\d\s+(?:passed|failed)"
552
+ # The verb set was `passed|failed|says` and the R-005 reader measured five
553
+ # citations sliding past it ("the seam tier confirmed…", "the unit tier said…",
554
+ # "the unit tier's verdict…"). Widened to the tense and possessive forms the
555
+ # reader planted; still a closed list on purpose — a looser net here starts
556
+ # matching a report's honest prose about its OWN tier.
557
+ CROSS_TIER = re.compile(r"\b(?:unit|seam|product)\s+tier(?:'s)?\s+"
558
+ r"(?:passed|failed|says|said|confirm\w*|verdict|report)"
559
+ r"|\btier\s+\d\s+(?:passed|failed|says|said|confirm\w*)"
554
560
  r"|as\s+the\s+(?:unit|seam|product)\s+tier", re.I)
555
561
 
556
562
 
@@ -639,13 +645,30 @@ def tier_violations(t):
639
645
  "— a fail that does not say what broke is a fail the next round "
640
646
  "cannot act on" % t["tier"])
641
647
 
642
- # Blind, and checked. Only the prose fields can carry it.
643
- for k in ("confirms", "evidence", "not_examined"):
648
+ # Blind, and checked. EVERY prose field can carry it — the scan covered three
649
+ # of the six and a citation hiding in `scope` or a finding's `what`/`fix`
650
+ # passed untouched (certification.md claims the refusal for the whole report).
651
+ for k in ("confirms", "evidence", "not_examined", "scope"):
644
652
  for i, e in enumerate(t[k]):
645
653
  if isinstance(e, str) and CROSS_TIER.search(e):
646
654
  out.append("tier report `%s[%d]` cites another tier's verdict (%r) — the "
647
655
  "three run blind, because three reports that read each other "
648
656
  "are one opinion with three signatures" % (k, i, e.strip()[:70]))
657
+ # Enumerated over the RAW list, so the index in this refusal and the index
658
+ # in the shape refusals above name the same object — the filtered list
659
+ # renumbered them (R-005 reader). `check` and `where` scan too: `check` is
660
+ # mandatory on every breaks finding, so it is the field a failing tier most
661
+ # reliably writes prose into.
662
+ for i, f in enumerate(t["findings"]):
663
+ if not isinstance(f, dict):
664
+ continue
665
+ for k in ("what", "fix", "check", "where"):
666
+ v = f.get(k)
667
+ if isinstance(v, str) and CROSS_TIER.search(v):
668
+ out.append("tier report `findings[%d].%s` cites another tier's verdict "
669
+ "(%r) — the three run blind, because three reports that read "
670
+ "each other are one opinion with three signatures"
671
+ % (i, k, v.strip()[:70]))
649
672
  return out
650
673
 
651
674
  # --- verbs --------------------------------------------------------------------
@@ -1042,7 +1065,7 @@ def cmd_producer(graph, args):
1042
1065
  def cmd_doctrine(graph, args):
1043
1066
  """Which doctrine this run actually read — B-061.
1044
1067
 
1045
- The bundle is 36 reference files. A run reads some subset and nothing recorded which,
1068
+ The bundle is 38 reference files. A run reads some subset and nothing recorded which,
1046
1069
  so **a skipped file and a read one were indistinguishable** — the class every guard in
1047
1070
  this repository exists to catch, left standing over the doctrine itself.
1048
1071
 
@@ -35,8 +35,12 @@
35
35
  `age_bonus` is `1` past 14 days and `2` past 30. The formula is repeated here on
36
36
  purpose — the inputs are in this table, so a reader checks the arithmetic rather than
37
37
  trusting the ranking. Full doctrine: the skill's `references/backlog.md`.
38
- - **State** — `open` · `in-flight` · `closed` · `dropped`. `dropped` needs the
39
- operator's agreement and the date they gave it.
38
+ - **State** — `open` · `in-flight` · `closed` · `dropped` · `parked <branch or
39
+ commit>` · `waived revisit: <condition>`. `dropped` needs the operator's
40
+ agreement and the date they gave it. `parked` claims work exists and must say
41
+ where — a branch or a commit, never a directory. `waived` is a decision, not
42
+ debt: it carries the condition that reopens it. Both long forms:
43
+ the skill's `references/backlog.md`.
40
44
  - **Home** — an issue id where a tracker exists, `—` where this file *is* the tracker.
41
45
 
42
46
  ## Closed
@@ -59,9 +59,10 @@ source is a recorded decision, an unquoted one is an undetected divergence.
59
59
  - **Knowledge wiki:** installed / not installed
60
60
  ([obsidian-wiki](https://github.com/ar9av/obsidian-wiki); recommended, never a gate)
61
61
  - **Retro, in force:** `docs/evidence/retro.md` — none / N standing instructions
62
- (read **in full**, together with the run stamps and the recent-log window; list
63
- which ones bind this run, and stamp each as it fires **with the commit** — that
64
- stamp is the only evidence behind stage 10's cold-retirement rule)
62
+ (read **in full**, together with the run stamps both bounded by construction;
63
+ the recent log is **queried** by this task's nouns, never read end to end; list
64
+ which instructions bind this run, and stamp each as it fires **with the commit**
65
+ — that stamp is the only evidence behind stage 10's cold-retirement rule)
65
66
  - **Retro archive:** `docs/evidence/retro/` — **queried** by this task's nouns;
66
67
  what it returned: … (or `nothing`)
67
68
  - **Code graph:** built / installed-not-built / not installed
@@ -6,10 +6,12 @@ The order is load-bearing: the cold-retirement trigger reads the stamp this stag
6
6
  writes, so a prune ahead of it can never run on real data (`learned.md` rule 21).
7
7
  Doctrine: `references/retrospective.md`.
8
8
 
9
- **What stage 0 reads in full:** *Standing instructions*, *Run stamps* and *Recent
10
- log* — all three are bounded by construction, which is why the cap is not
11
- negotiable. The **archive** (`docs/evidence/retro/YYYY-QN.md`) is *queried* by
12
- the task's nouns and never read end to end.
9
+ **What stage 0 reads in full:** *Standing instructions* and *Run stamps* both
10
+ are bounded by construction (ten rows, one line per run), which is why the cap is
11
+ not negotiable. The **Recent log** and the **archive**
12
+ (`docs/evidence/retro/YYYY-QN.md`) are *queried* by the task's nouns and never
13
+ read end to end: nothing caps the log's length, and an uncapped section read in
14
+ full is the volume that stops the ten binding rows being read.
13
15
 
14
16
  ## Standing instructions (max 10 — in force right now)
15
17
 
@@ -20,7 +20,7 @@ Run: `<topic>` · started `<YYYY-MM-DD>` · module map: `<path or "none">`
20
20
 
21
21
  ## `read:` — which doctrine this run actually opened
22
22
 
23
- The bundle is 36 reference files and nothing recorded which of them a run read, so **a
23
+ The bundle is 38 reference files and nothing recorded which of them a run read, so **a
24
24
  skipped file and a read one were indistinguishable** — the class every guard in this
25
25
  pipeline exists to catch, left standing over the doctrine itself.
26
26
 
@@ -48,7 +48,7 @@ same claim one level down.
48
48
  |---|---|---|
49
49
  | `unmeasured — no run ledger` | there is no ledger | nothing to read from |
50
50
  | `unmeasured — the ledger carries no read: lines` | the hook is absent, **or** the run opened no doctrine | two opposite facts, and the ledger cannot separate them, so neither is claimed |
51
- | `N of 36 reference files read — unattested`, then each unread one | the hook is installed and fired | the count alone says there is a gap, not where — and `unattested` says the ledger cannot name who wrote the lines |
51
+ | `N of 38 reference files read — unattested`, then each unread one | the hook is installed and fired | the count alone says there is a gap, not where — and `unattested` says the ledger cannot name who wrote the lines |
52
52
 
53
53
  **It is a disclosure: no floor, no direction, never a target.** A run that needs four files
54
54
  and reads four is not worse than one that reads thirty — and the moment the number becomes