task-pipeline-skill 1.79.1 → 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 (36) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +4 -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/gate-observer.sh +19 -2
  13. package/plugins/task-pipeline/skills/evidence-docs/SKILL.md +1 -0
  14. package/plugins/task-pipeline/skills/project-audit/SKILL.md +7 -2
  15. package/plugins/task-pipeline/skills/task-pipeline/SKILL.md +49 -56
  16. package/plugins/task-pipeline/skills/task-pipeline/pipeline.example.json +1 -1
  17. package/plugins/task-pipeline/skills/task-pipeline/references/acceptance.md +1 -1
  18. package/plugins/task-pipeline/skills/task-pipeline/references/adoption.md +15 -4
  19. package/plugins/task-pipeline/skills/task-pipeline/references/artifacts.md +4 -2
  20. package/plugins/task-pipeline/skills/task-pipeline/references/brainstorm.md +6 -1
  21. package/plugins/task-pipeline/skills/task-pipeline/references/build.md +11 -1
  22. package/plugins/task-pipeline/skills/task-pipeline/references/certification.md +7 -0
  23. package/plugins/task-pipeline/skills/task-pipeline/references/companion-skills.md +33 -2
  24. package/plugins/task-pipeline/skills/task-pipeline/references/documentation.md +2 -2
  25. package/plugins/task-pipeline/skills/task-pipeline/references/exposure.md +7 -3
  26. package/plugins/task-pipeline/skills/task-pipeline/references/gates.md +17 -185
  27. package/plugins/task-pipeline/skills/task-pipeline/references/model-tiering.md +13 -0
  28. package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
  29. package/plugins/task-pipeline/skills/task-pipeline/references/probing.md +202 -0
  30. package/plugins/task-pipeline/skills/task-pipeline/references/progress.md +8 -4
  31. package/plugins/task-pipeline/skills/task-pipeline/references/spec.md +60 -1
  32. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +32 -54
  33. package/plugins/task-pipeline/skills/task-pipeline/references/work-graph.md +1 -1
  34. package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +28 -5
  35. package/plugins/task-pipeline/skills/task-pipeline/templates/backlog.md +6 -2
  36. package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +2 -2
@@ -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
@@ -149,9 +151,10 @@ never that the work was skipped quietly.
149
151
 
150
152
  ```
151
153
  1. files the request names, resolved git ls-files -- <paths> -> N
152
- 2. any of them a public contract the version-synced surfaces,
153
- pipeline.schema.json, the
154
- 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
155
158
  3. behaviour a user or a caller observes changes -> yes/no
156
159
  ```
157
160
 
@@ -336,55 +339,18 @@ never that the work was skipped quietly.
336
339
  never rebuild from scratch. If the chain already exists and is validated (e.g.
337
340
  the task entered from super-ux), just verify (linter green) and embed it into
338
341
  the spec; only build the parts that are missing.
339
- - **COPY track — how it sounds.** Every string a product's user will read is written
340
- through super-ux's `copywriting`, against the brand pack (`docs/brand/voice.md`,
341
- `terminology.md`, `facts.md`). No pack ⇒ `/brand-init` **before** the first string,
342
- not after: a voice reverse-engineered from copy already written is a description of
343
- what happened, not a decision. In scope: interface strings, errors, empty states,
344
- the landing, pricing, the user-facing changelog. **Out of scope, and saying so is
345
- what keeps the track honest:** commit messages, PR descriptions, code comments, a
346
- developer README, internal docs. Running a brand pack over a line in a contributors'
347
- changelog is the fastest way to teach an agent to route around the track.
348
- - **VISUAL track — how it looks.** Where the task has a visual surface, the visual
349
- layer goes through `sheleg-design` ([`companion-skills.md`](companion-skills.md)):
350
- tokens and themes, typography and rhythm, motion and how it degrades to rest, the
351
- boundary with Figma (tokens as variables, never raw values carried across). Not
352
- through it: a purely structural change — what sits where is the UX track's — text,
353
- a backend, an internal script.
354
- - **Each track's refusal is a sentence, never a silence.** *"Без дизайна" / "as is"*
355
- ends the visual track; *"без бренда" / "draft"* ends the copy track. Either one is
356
- the operator's to make and costs nothing — but it is **recorded in the brief and
357
- said out loud in the close-out**, because a track skipped silently and a track that
358
- ran are the same thing in a transcript. This is the `⊘` rule one layer up: a skip
359
- nobody can see is indistinguishable from work that happened.
360
- - **Three tracks, three questions, and they do not substitute for each other.** super-ux
361
- decides what the interface must **do**; `copywriting` how it **sounds**;
362
- `sheleg-design` how it **looks**. Until 2026-08-10 this stage named only the first,
363
- so a run designed a flow, then wrote its strings by taste and picked its values at the
364
- keyboard — and every gate in the pipeline reported green over both.
365
- - **Two of the three are a parallel layer, and the third is their only real dependency.**
366
- COPY and VISUAL both consume the UX track's scenarios; **neither consumes the other**.
367
- Copy is written against the brand pack and the scenarios, not against tokens; the visual
368
- is built from the frame and the style pack, not from strings. Writing them in a line —
369
- which this file did until 2026-08-15 — teaches a run to wait for a result that never
370
- arrives. The order is `UX → { COPY ∥ VISUAL }`, and the only thing crossing each of
371
- those two arrows is **the scenario set**.
372
- - **Their convergence needs a check, and it has a real contradiction to catch.** Both land
373
- on the same screen, so the failure is not that one is wrong: it is that each is right
374
- alone and they disagree together. Before the spec is committed, compare the two outputs
375
- and record the answer:
376
- 1. **A string the layout has no room for** — a label, an error or an empty state longer
377
- than the frame's element, at the frame's own width.
378
- 2. **A state one track has and the other does not** — copy for an empty state the design
379
- never drew, or a loading state drawn with no string.
380
- 3. **Two names for one thing** — the design system's component name against the
381
- terminology file's noun, where a user reads both.
382
- 4. **A tone the visual contradicts** — a calm, plain register on a screen whose motion
383
- and colour say urgency.
384
- `Tracks converge: clean` is the answer most runs write, and writing it is the point —
385
- a check whose silence is indistinguishable from not having run is not evidence. This is
386
- the same rule the harvest applies at stage 0 and the build applies to a fanned-out group
387
- 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.
388
354
  - **Spec:** write the approved design to
389
355
  `<artifacts>/specs/YYYY-MM-DD-<topic>-design.md` and commit it. Lock all
390
356
  shared contracts (types, schemas, signatures, file layout). For UI tasks the
@@ -468,6 +434,12 @@ never that the work was skipped quietly.
468
434
  implementer that wrote it is still dispatched. The matrix pointed this companion at
469
435
  stages 5–6 from the day it was added and **this stage had never named it** — found by
470
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.
471
443
  - **Integration closes the stage:** sync with the base branch, re-run the full suite
472
444
  on the result, land it the project's way (merge, or a PR — outward, so it needs a
473
445
  go), remove the worktree. Stages 7–9 act on the integrated result, so a branch the
@@ -493,7 +465,9 @@ never that the work was skipped quietly.
493
465
  same as stage 5.
494
466
  - **GATE (auto):** **the hygiene gate green over the whole tree**, its six counts
495
467
  printed beside their floors; the **full** suite is green (not just the new tests); new/changed code
496
- 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
497
471
  to deploy on a red or partial run. **The carry-over count is printed beside this
498
472
  verdict** — a ratchet nobody prints is a TODO with a better name
499
473
  ([`audit.md`](audit.md)) — **and so are the disclosures**, `abstained` and
@@ -590,6 +564,10 @@ never that the work was skipped quietly.
590
564
  on anything but `success`, and one of the three states stated — including **`no run
591
565
  found`**, out loud, because a project without CI is a legitimate state and not a
592
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.
593
571
  - **GATE (auto):** clean boot confirmed, or an **honest degradation report** with next
594
572
  steps — never silent success. **The CI verdict is one of the reported facts, with
595
573
  its run id** — "CI is green" written without a command behind it prints the same
@@ -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 37 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
@@ -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 37 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 37 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