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.
- package/CHANGELOG.md +184 -2
- package/README.md +5 -3
- package/SKILL-CARD.md +1 -1
- package/cursor/rules/task-pipeline.mdc +3 -1
- package/evals/RESULTS.md +212 -9
- package/evals/evidence-docs.evals.json +109 -0
- package/evals/project-audit.evals.json +108 -0
- package/evals/run.py +47 -19
- package/package.json +1 -1
- package/plugins/task-pipeline/.claude-plugin/plugin.json +3 -2
- package/plugins/task-pipeline/commands/task-pipeline.md +2 -1
- package/plugins/task-pipeline/hooks/build-gate.sh +6 -1
- package/plugins/task-pipeline/hooks/gate-observer.sh +35 -14
- package/plugins/task-pipeline/hooks/release-gate.sh +70 -9
- package/plugins/task-pipeline/skills/evidence-docs/SKILL.md +33 -2
- package/plugins/task-pipeline/skills/project-audit/SKILL.md +7 -2
- package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +50 -57
- package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +4 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/acceptance.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/adoption.md +15 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +4 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/backlog.md +6 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/brainstorm.md +6 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/build.md +11 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/certification.md +7 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +33 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/documentation.md +2 -2
- package/plugins/task-pipeline/skills/task-pipeline/references/exposure.md +7 -3
- package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +17 -185
- package/plugins/task-pipeline/skills/task-pipeline/references/knowledge-sources.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/model-tiering.md +13 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +2 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/prioritisation.md +165 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/probing.md +202 -0
- package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +8 -4
- package/plugins/task-pipeline/skills/task-pipeline/references/spec.md +60 -1
- package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +52 -54
- package/plugins/task-pipeline/skills/task-pipeline/references/work-graph.md +1 -1
- package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +28 -5
- package/plugins/task-pipeline/skills/task-pipeline/templates/backlog.md +6 -2
- package/plugins/task-pipeline/skills/task-pipeline/templates/brief.md +4 -3
- package/plugins/task-pipeline/skills/task-pipeline/templates/retro.md +6 -4
- 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
|
|
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 **
|
|
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
|
|
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
|
-
|
|
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)
|
|
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
|
|
152
|
-
|
|
153
|
-
|
|
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
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
-
|
|
348
|
-
|
|
349
|
-
|
|
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;
|
|
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. `
|
|
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
|
-
|
|
553
|
-
|
|
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.
|
|
643
|
-
|
|
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
|
|
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
|
|
39
|
-
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
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
|
|
10
|
-
|
|
11
|
-
negotiable. The **
|
|
12
|
-
the task's nouns and never
|
|
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
|
|
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
|
|
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
|