task-pipeline-skill 1.99.0 → 1.100.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 (22) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +2 -1
  3. package/SKILL-CARD.md +1 -1
  4. package/package.json +3 -3
  5. package/plugins/task-pipeline/.claude-plugin/plugin.json +1 -1
  6. package/plugins/task-pipeline/agents/design-tester.md +80 -0
  7. package/plugins/task-pipeline/skills/task-pipeline/pipeline.schema.json +73 -1
  8. package/plugins/task-pipeline/skills/task-pipeline/references/browser.md +28 -0
  9. package/plugins/task-pipeline/skills/task-pipeline/references/design-review.md +64 -0
  10. package/plugins/task-pipeline/skills/task-pipeline/references/doctrine-map.md +1 -0
  11. package/plugins/task-pipeline/skills/task-pipeline/references/finish-line.md +9 -3
  12. package/plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md +5 -0
  13. package/plugins/task-pipeline/skills/task-pipeline/references/portability.md +1 -0
  14. package/plugins/task-pipeline/skills/task-pipeline/references/retrospective.md +4 -0
  15. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +15 -2
  16. package/plugins/task-pipeline/skills/task-pipeline/scripts/backlog_sources.py +2 -3
  17. package/plugins/task-pipeline/skills/task-pipeline/scripts/design_gate.py +362 -0
  18. package/plugins/task-pipeline/skills/task-pipeline/scripts/graph.py +1 -1
  19. package/plugins/task-pipeline/skills/task-pipeline/scripts/resource_guard.py +338 -0
  20. package/plugins/task-pipeline/skills/task-pipeline/scripts/retro_budget.py +269 -30
  21. package/plugins/task-pipeline/skills/task-pipeline/scripts/waves.py +51 -4
  22. package/plugins/task-pipeline/skills/task-pipeline/templates/run.md +6 -4
package/CHANGELOG.md CHANGED
@@ -1,3 +1,63 @@
1
+ ## v1.100.0 (2026-10-22)
2
+
3
+ Guards: 530 → **530**; property checks 15 → **15**. No negative was added: the new checks are
4
+ `test/design_gate_test.py` (75 cases) and eight new cases in `test/waves_test.py`.
5
+
6
+ **The design review is a gate** (operator, 2026-10-22). Every node that changes an interface
7
+ owes it, in a single run, a wave or a release iteration.
8
+
9
+ - **`design-tester`** (`agents/design-tester.md`) walks the project's ten-portrait test panel
10
+ (super-ux 0.64.0, `docs/ux/test-panel.md`) through the changed scenarios: pass D over the
11
+ Figma frames or mockups before build (stage 3), pass B in a real browser over the build after
12
+ it (stage 6). Every step answers a fixed checklist (frame, primary action, controls, focus,
13
+ states, text fit, outcome); a `breaks` finding names the item that failed.
14
+ - **`scripts/design_gate.py`** gates the panel (ten portraits, each with basis, features,
15
+ composite scenarios, activation and retention), the report `design-review/1` (coverage of
16
+ scope scenario × persona, the checklist, no open `breaks`, one pass of each kind per round,
17
+ the resource receipt) and the recorded browser (`browser.storageState` outside the repo).
18
+ - **The tester's browser** (`references/browser.md`): asked once per project and recorded in
19
+ `pipeline.json` → `browser`, WebKit first, a prepared signed-in session, a fallback that is
20
+ said, never silent.
21
+ - **`scripts/resource_guard.py`**: load, memory pressure and free disk before a run (wait over
22
+ the line), caps on browsers, pages and dev servers, a ledger of what the run started, and a
23
+ close that touches only that, verified empty.
24
+ - **The stop, in every run**: stage 10 computes the finish verdict with `waves.py finish` over
25
+ the run's own ledger, and `run.finish.maxAgents` (default 2) fails a round whose `agents:`
26
+ line ran more sub-agents at once.
27
+ - `scripts/backlog_sources.py` no longer cites a private repository.
28
+ - **Reviewed under its own budget:** three blind unit readings found 4, then 2, then 2 `breaks`
29
+ (each fixed with a regression case); the round budget of 3 was reached, so no fourth reading
30
+ ran.
31
+
32
+ ## v1.99.1 (2026-10-21)
33
+
34
+ Guards: 530 → **530**; property checks 15 → **15**. No negative was added: the new cases
35
+ are regression tests in `test/audit_regressions/diet_retro_budget.py`.
36
+
37
+ **`retro_budget.py` reads an older retro's floor and migrates it** (B-149, from sshlg-skills
38
+ B-244). That retro writes its standing instructions as a numbered list and its run stamps as
39
+ `- **date: title** ...` narrative items beside a table. `check` printed `standing 0/10 rows`
40
+ for ten instructions, and `migrate` answered `nothing to move` on a 103,737-byte floor with 89
41
+ stamps, so stage 10's byte budget could never pass there.
42
+
43
+ - `check` counts numbered-list standing instructions and list stamps, measures each item
44
+ whole and names it (`standing row #6`, `stamp 2026-09-06 (list)`).
45
+ - `migrate` rotates the oldest stamps past the cap of ten, ordered by their date and never by
46
+ position, verbatim into the quarter's archive (a table row travels under its table's
47
+ header); keeps a long list item's bold rule and its `*(Retire ...)*` trigger and moves the
48
+ rest; keeps a long list stamp's bold title; and, only over budget, moves the Run stamps
49
+ section's loose prune paragraphs. Every move leaves a `sha256:` pointer that `verify` proves.
50
+ - Edge rules from two review rounds: a lead of several bold spans stays whole and a date-only
51
+ lead refuses by name; a same-day tie follows its own block's direction; a paragraph holding
52
+ a heading stays in the floor; the script's own pointer notes never move again, so a floor
53
+ still over budget is left alone by the next run; a table's header leaves only with the rows
54
+ this run rotated; a list marker, a quote, a rule or a `*(` note ends an item; a rule still
55
+ over 600 characters after its story moved is reported, never moved again.
56
+ - On a copy of the sshlg-skills retro: 103,737 bytes to 6,446, every pointer proved, no
57
+ non-blank line lost, the text outside the floor unchanged.
58
+ - Tests: `test/audit_regressions/diet_retro_budget.py` gains seventeen cases. Each failed on the
59
+ 1.99.0 script (it crashed with no archive to read) or on a reviewed intermediate version.
60
+
1
61
  ## v1.99.0 (2026-10-21)
2
62
 
3
63
  Guards: 448 → **530**; property checks 15 → **15**. There are 82 new negatives in
package/README.md CHANGED
@@ -186,6 +186,7 @@ and the inline fallback satisfy the same [provider contract](plugins/task-pipeli
186
186
  | 5 Build | [`build.md`](plugins/task-pipeline/skills/task-pipeline/references/build.md) + [`review.md`](plugins/task-pipeline/skills/task-pipeline/references/review.md) — isolation, ledger, subagent loop, review rubric, fix loop |
187
187
  | 5–6 TDD | [`tdd.md`](plugins/task-pipeline/skills/task-pipeline/references/tdd.md) — the iron law, red/green/refactor, the suite gate |
188
188
  | 5, 6, 8 The browser | [`browser.md`](plugins/task-pipeline/skills/task-pipeline/references/browser.md) — the ref model both channels share, the four commands the look is made of, sessions, and the three different things *"tested in a browser"* means |
189
+ | 3, 6 The design review | [`design-review.md`](plugins/task-pipeline/skills/task-pipeline/references/design-review.md): the `design-tester` agent walks the ten-portrait test panel through the changed scenarios over the Figma frames or mockups before build and in the recorded browser after it, judges every step on a fixed checklist, and closes everything it started (`scripts/design_gate.py`, `scripts/resource_guard.py`) |
189
190
  | 10 Acceptance | [`acceptance.md`](plugins/task-pipeline/skills/task-pipeline/references/acceptance.md) — REQ coverage table, evidence rules, the closing question |
190
191
  | 10 + any audit | [`audit.md`](plugins/task-pipeline/skills/task-pipeline/references/audit.md) — the L0→L7 ladder and its seams, axis rotation, ratchets, proven checks |
191
192
  | any loop | [`loop-guard.md`](plugins/task-pipeline/skills/task-pipeline/references/loop-guard.md) — churn detection, caps, the break protocol |
@@ -641,7 +642,7 @@ must say so.
641
642
  ### Held to Anthropic's own Skill authoring guidance
642
643
 
643
644
  Audited against the four Agent Skills pages. Most of it already held — `name`
644
- 13/64 chars, `description` 386/1024 chars, `SKILL.md` 207/500 lines, all 53 references
645
+ 13/64 chars, `description` 386/1024 chars, `SKILL.md` 207/500 lines, all 54 references
645
646
  reachable from `SKILL.md` through explicit reference routes, and the bundle far under the 30 MB ceiling. What
646
647
  did not, now does:
647
648
 
package/SKILL-CARD.md CHANGED
@@ -12,7 +12,7 @@ harmless.
12
12
  |---|---|
13
13
  | **Purpose** | Runs a substantial task through ten gated delivery stages — intake grill, docs study, brainstorm, spec, plan, subagent build, tests, lint/deploy, post-deploy, docs+registers, acceptance — refusing to advance until each gate passes |
14
14
  | **Owner** | ssheleg ([github.com/ssheleg/task-pipeline](https://github.com/ssheleg/task-pipeline)) |
15
- | **Version** | 1.99.0 |
15
+ | **Version** | 1.100.0 |
16
16
  | **Surface** | Claude Code (filesystem skill + plugin) and the vercel `skills` CLI. **Not** uploaded to the Skills API; custom Skills do not sync across surfaces |
17
17
  | **Dependencies** | None required. Optional: `context7` (MCP), `figma` (MCP), super-ux, agent-sync, graphify, obsidian-wiki, and **one of two browser channels** — `playwright` (CLI or MCP) or `chrome-devtools` (MCP); either satisfies the browser step and neither is required. The engine is WebKit (Safari) first; `chrome-devtools` is Chrome-only, a stated exception. Every stage's doctrine ships in-repo; user-facing stage 3 requires a valid scenario artifact; super-ux is preferred, with equivalent-provider and inline-fallback paths under the provider contract |
18
18
  | **Evaluation status** | Suite authored, 5 categories. One recorded run, **self-observed by the author**; **zero blind runs on zero of three models** — the split, and the numbers, live in [`evals/RESULTS.md`](evals/RESULTS.md) and are computed by `evals/run.py` |
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "task-pipeline-skill",
3
- "version": "1.99.0",
3
+ "version": "1.100.0",
4
4
  "description": "Full-cycle delivery pipeline for coding agents: a mandatory built-in intake grill, then 10 gated stages (docs, brainstorm+decompose, spec, plan, build, tests, lint/deploy, post-deploy, docs/wiki, acceptance). Every stage's doctrine ships inside the skill — no companion plugin required. This package is the installer CLI.",
5
5
  "bin": {
6
6
  "task-pipeline": "bin/task-pipeline.js"
7
7
  },
8
8
  "scripts": {
9
- "test": "python3 test/validate.py && python3 test/graph_test.py && python3 test/project_audit_test.py && python3 test/packet_schema_test.py && python3 test/browser_claims_test.py && python3 test/visual_gate_test.py && python3 test/stage_checkpoint_test.py && python3 test/backlog_sources_test.py && python3 test/release_approval_test.py && python3 test/memory_nudge_test.py && python3 test/lessons_test.py && python3 test/waves_test.py && python3 test/continuity_doc_test.py && bash test/install_test.sh && npm run test:audit-regressions",
10
- "test:all": "python3 test/validate.py && python3 test/graph_test.py && python3 test/project_audit_test.py && python3 test/packet_schema_test.py && python3 test/browser_claims_test.py && python3 test/visual_gate_test.py && python3 test/stage_checkpoint_test.py && python3 test/backlog_sources_test.py && python3 test/release_approval_test.py && python3 test/memory_nudge_test.py && python3 test/lessons_test.py && python3 test/waves_test.py && python3 test/continuity_doc_test.py && bash test/install_test.sh && python3 test/negatives.py && npm run test:certify && npm run test:exposure && npm run test:probe && npm run test:anchors && npm run test:runner && npm run test:hooks && npm run test:artifacts && npm run test:docs && npm run test:audit-regressions",
9
+ "test": "python3 test/validate.py && python3 test/graph_test.py && python3 test/project_audit_test.py && python3 test/packet_schema_test.py && python3 test/browser_claims_test.py && python3 test/visual_gate_test.py && python3 test/stage_checkpoint_test.py && python3 test/backlog_sources_test.py && python3 test/release_approval_test.py && python3 test/memory_nudge_test.py && python3 test/lessons_test.py && python3 test/waves_test.py && python3 test/design_gate_test.py && python3 test/continuity_doc_test.py && bash test/install_test.sh && npm run test:audit-regressions",
10
+ "test:all": "python3 test/validate.py && python3 test/graph_test.py && python3 test/project_audit_test.py && python3 test/packet_schema_test.py && python3 test/browser_claims_test.py && python3 test/visual_gate_test.py && python3 test/stage_checkpoint_test.py && python3 test/backlog_sources_test.py && python3 test/release_approval_test.py && python3 test/memory_nudge_test.py && python3 test/lessons_test.py && python3 test/waves_test.py && python3 test/design_gate_test.py && python3 test/continuity_doc_test.py && bash test/install_test.sh && python3 test/negatives.py && npm run test:certify && npm run test:exposure && npm run test:probe && npm run test:anchors && npm run test:runner && npm run test:hooks && npm run test:artifacts && npm run test:docs && npm run test:audit-regressions",
11
11
  "test:negatives": "python3 test/negatives.py",
12
12
  "test:exposure": "python3 test/exposure_test.py",
13
13
  "test:probe": "python3 test/probe.py --self-test",
@@ -3,7 +3,7 @@
3
3
  "name": "task-pipeline",
4
4
  "displayName": "Task Pipeline",
5
5
  "description": "Runs a substantial task through a mandatory built-in intake grill, then 10 gated stages (docs, brainstorm+decompose, spec, plan, subagent build, tests, lint/deploy, post-deploy, docs/wiki, acceptance). Every stage's doctrine is built into the skill — no companion plugin required — with typed auto/judgment/manual gates, a frozen requirement spine that closes with evidence, a work board and a verification ledger that outlive a run, an exposure line naming what shipped unconfirmed, a progress rail computed from the project's own config, a proportional short path for small changes, a loop guard whose ceilings stop and record a blocker, and stage-3 tracks for what a product does, how it sounds and how it looks. Two modes need no task: `checkup` (what is unverified) and `setup` (audit existing docs); a third, `waves`, takes the whole board and closes it in waves that each finish in one pass. Retro insights can publish upstream as issues, opt-in and redacted.",
6
- "version": "1.99.0",
6
+ "version": "1.100.0",
7
7
  "author": {
8
8
  "name": "ssheleg",
9
9
  "url": "https://x.com/sshlg93"
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: design-tester
3
+ description: The design-review gate, owed by every node that changes an interface. Walks the project's test panel of ten personas through the changed scenarios, first over the Figma frames or mockups (pass D), then in a real browser over the build (pass B), judges every step on a fixed checklist, watches the machine's load and closes everything it started. Writes design-review/1. Use when a task-pipeline node with a surface_class other than none reaches stage 3 or stage 6. Not for code review (the unit tier) or the pixel diff of a contact sheet (verifier-visual).
4
+ model: inherit
5
+ tools: Read, Grep, Glob, Bash
6
+ ---
7
+
8
+ # Design tester: the interface, walked by the people it is for
9
+
10
+ You test what a user meets, not how it was built. The builder and the designer will never see
11
+ your reasoning, only your report, and you never see theirs: a tester who has read *the screen
12
+ is finished* sees a finished screen.
13
+
14
+ Your inputs, and nothing else is yours:
15
+
16
+ - the **test panel**, `docs/ux/test-panel.md`: ten portraits, each with its features, composite
17
+ scenarios (`SCR-` ids), activation funnel, retention loop and needs, and a basis line that
18
+ says evidence or hypothesis. Run `python3 scripts/design_gate.py panel docs/ux/test-panel.md`
19
+ first; a FAIL or NOT_RUN there stops you and goes back to super-ux `ux-foundation`;
20
+ - the **scenarios**, `docs/ux/scenarios.md`, and the node's scope: the `SCR-` ids it changes;
21
+ - the **design**: the Figma frames each step names (through the Figma MCP when connected:
22
+ `get_metadata`, `get_screenshot`) or, without Figma, the mockups the scenario links;
23
+ - the **build**: the URL or app the node runs, reached in the browser the project recorded
24
+ (`pipeline.json` → `browser`).
25
+
26
+ ## What you do
27
+
28
+ 1. **Bound the work before starting.** The scope's scenarios × the panel personas that use
29
+ them (`design_gate.py` computes it): that is the matrix, and the whole of it. One pass D
30
+ and one pass B per round; a re-review reads only the steps the fix touched. Findings
31
+ outside the scope are board rows, not your next task.
32
+ 2. **Pass D, design, before build.** For each scenario step and each persona: open its frame.
33
+ Is the step's screen there? Is the primary action present and visible without hunting? Is
34
+ every control the scenario needs on it? Does anything compete with the step's goal? Are the
35
+ empty, loading and error states designed? Does the text fit? Does the persona's funnel stage
36
+ or retention trigger land where the scenario says? Each answer is pass, fail, na or NOT_RUN
37
+ with its reason. The designer fixes; you re-read only what changed.
38
+ 3. **Pass B, build, after build.** Check the machine first:
39
+ `python3 scripts/resource_guard.py check`; exit 4 means wait, then run with fewer pages.
40
+ Start the browser the project chose (`references/browser.md` → *The tester's browser*),
41
+ record every process you start with `resource_guard.py record`, and drive each step as the
42
+ persona would: the same checklist, a capture per step, the engine named, the business
43
+ outcome read back (the order exists, the setting persisted, the message arrived). Compare
44
+ each capture with its Figma frame or mockup.
45
+ 4. **Sign-in walls are a human gate, said once.** Use the prepared session (Safari's signed-in
46
+ profile through `safaridriver`, or the `storageState` file outside the repository). A
47
+ missing sign-in, a one-time code or a consent is named in the report, the steps behind it
48
+ are NOT_RUN with that reason, and the rest of the matrix runs.
49
+ 5. **Close what you opened.** `resource_guard.py close` then `resource_guard.py verify --json`;
50
+ its output is the report's `resources` block. A live process is a failed gate, not a note.
51
+ 6. **Gate yourself.** `python3 scripts/design_gate.py report <report> --class <surface_class>
52
+ --panel docs/ux/test-panel.md --scope <ids> --require design|build`. Quote its output as
53
+ your first evidence. NOT_RUN is said as NOT_RUN.
54
+
55
+ ## The report, `design-review/1`
56
+
57
+ ```json
58
+ {"schema": "design-review/1", "surface": "<surface>", "panel_revision": "<sha>",
59
+ "rows": [{"scenario": "SCR-03", "step": 2, "persona": "P-04", "pass": "build", "round": 1,
60
+ "frame": "<figma node or mockup>", "capture": "<path>", "engine": "webkit",
61
+ "outcome": "order #… created, visible in history",
62
+ "checks": {"frame": "pass", "primary_action": "pass", "controls": "fail",
63
+ "focus": "pass", "states": "na", "text_fit": "pass", "outcome": "pass"},
64
+ "findings": [{"severity": "breaks", "what": "no way back from the summary",
65
+ "where": "summary screen", "status": "open"}]}],
66
+ "resources": {"started": [], "live": [], "closed": true}}
67
+ ```
68
+
69
+ `breaks` stops the user in a scenario step or contradicts the scenario's outcome. `risk` makes a
70
+ step slower, unclear or easy to get wrong. `nit` is polish. Taste is none of them: a finding
71
+ names the step, the persona and the checklist item it failed.
72
+
73
+ ## Three ways this goes wrong
74
+
75
+ - **The panel decided the product.** A hypothesis portrait chooses what to test, never what to
76
+ build. Say so when a finding rests only on a hypothesis.
77
+ - **The run kept going.** A fourth round, or steps outside the scope, is the loop the finish
78
+ line exists to stop (`references/finish-line.md`). Stop and hand back with the numbers.
79
+ - **The machine paid.** A browser left open, a dev server still listening, or a run started at
80
+ a load the guard said to wait on. The receipt is the proof, and it is checked.
@@ -81,6 +81,70 @@
81
81
  "description": "The resume point that survives context loss."
82
82
  }
83
83
  }
84
+ },
85
+ "browser": {
86
+ "type": "object",
87
+ "additionalProperties": false,
88
+ "description": "The browser the design-tester and every look use (since 1.100.0). Asked once per project at stage 0 when absent, then recorded here. WebKit first (references/browser.md). Doctrine: references/browser.md, The tester's browser.",
89
+ "required": [
90
+ "primary"
91
+ ],
92
+ "properties": {
93
+ "primary": {
94
+ "type": "string",
95
+ "enum": [
96
+ "safari",
97
+ "playwright-webkit",
98
+ "chrome-dev",
99
+ "playwright-chromium"
100
+ ],
101
+ "description": "The engine and driver tried first."
102
+ },
103
+ "fallback": {
104
+ "type": "array",
105
+ "items": {
106
+ "type": "string",
107
+ "enum": [
108
+ "safari",
109
+ "playwright-webkit",
110
+ "chrome-dev",
111
+ "playwright-chromium"
112
+ ]
113
+ },
114
+ "description": "Tried in order when the primary fails to start or attach twice; a fallback is said in every receipt, never silent."
115
+ },
116
+ "storageState": {
117
+ "type": "string",
118
+ "description": "Path of the Playwright signed-in state file. A credential: it must sit outside the repository (design_gate.py browser refuses a path inside it)."
119
+ },
120
+ "limits": {
121
+ "type": "object",
122
+ "additionalProperties": false,
123
+ "properties": {
124
+ "maxBrowsers": {
125
+ "type": "integer",
126
+ "minimum": 1,
127
+ "default": 2
128
+ },
129
+ "maxPages": {
130
+ "type": "integer",
131
+ "minimum": 1,
132
+ "default": 4
133
+ },
134
+ "maxServers": {
135
+ "type": "integer",
136
+ "minimum": 1,
137
+ "default": 1
138
+ },
139
+ "maxLoadRatio": {
140
+ "type": "number",
141
+ "minimum": 0.1,
142
+ "default": 1.0
143
+ }
144
+ },
145
+ "description": "What resource_guard.py enforces: browsers, pages and dev servers at once, and the load average per core over which a run waits."
146
+ }
147
+ }
84
148
  }
85
149
  },
86
150
  "definitions": {
@@ -180,6 +244,12 @@
180
244
  "minimum": 1,
181
245
  "default": 2,
182
246
  "description": "Releases per iteration, the first included; every corrective release after a post-release failure counts against it."
247
+ },
248
+ "maxAgents": {
249
+ "type": "integer",
250
+ "minimum": 1,
251
+ "default": 2,
252
+ "description": "Sub-agents running at once in one round (since 1.100.0). Each round's ledger carries `agents: <round> — <n>`, the most that ran together; waves.py finish fails a round over it. Two is the energy-saving default the operator set on 2026-10-22."
183
253
  }
184
254
  }
185
255
  }
@@ -235,7 +305,9 @@
235
305
  "type": "string",
236
306
  "minLength": 1,
237
307
  "pattern": "\\S",
238
- "not": {"pattern": "[\\r\\n]"},
308
+ "not": {
309
+ "pattern": "[\\r\\n]"
310
+ },
239
311
  "description": "Optional single-line source locator (for example docs/DECISIONS.md:42) for the standing authorization covering the tag and its publication. A locator, not a grant: how it is read and verified lives in references/deploy-targets.md, When the release waits for an approval."
240
312
  },
241
313
  "approval": {
@@ -32,6 +32,7 @@ which is how a run says *I checked the browser* and means *I ran the unit tests*
32
32
  - Reading a look vs gating on one
33
33
  - "Tested in a browser" is three different claims
34
34
  - The visual half — pixels against intent, under a contract
35
+ - The tester's browser: asked once, prepared, bounded, closed
35
36
  - Getting past a login, and past a backend
36
37
  - When the look finds something: debugging the spec that missed it
37
38
  - Evidence a reader can open
@@ -292,6 +293,33 @@ native screen's frames come from a simulator or a device (XCUITest snapshots, Co
292
293
  screenshot tests) and its accessibility from the platform's own audit; with neither, the
293
294
  native rows stay `NOT_RUN`, said in words.
294
295
 
296
+ ## The tester's browser: asked once, prepared, bounded, closed
297
+
298
+ The design-tester ([`design-review.md`](design-review.md)) and every look use the browser the
299
+ project recorded in `pipeline.json` → `browser`. When the block is absent, stage 0 asks the
300
+ operator once: which browser the tests use (`safari` through `safaridriver`,
301
+ `playwright-webkit`, `chrome-dev`, `playwright-chromium`) and the fallback order, WebKit first
302
+ as above. The answer is recorded and never asked again in that project.
303
+
304
+ - **Prepared session.** Safari reuses the operator's signed-in Safari through `safaridriver`
305
+ (Remote Automation enabled once: `sudo safaridriver --enable`, a human gate). Playwright
306
+ reuses `browser.storageState`, a file outside the repository (below). A sign-in, a one-time
307
+ code or a consent is named once; the steps behind it are NOT_RUN and the rest run.
308
+ - **Fallback, said.** A driver that fails to start or attach twice yields to the next in
309
+ `browser.fallback`; every capture names its engine.
310
+ - **Load.** `python3 scripts/resource_guard.py check` before starting and between scenarios:
311
+ exit 4 (load over cores × `limits.maxLoadRatio`, memory pressure, low disk) means wait, then
312
+ run with fewer pages.
313
+ - **Caps and close.** Every process the run starts (browser, driver, dev server) is recorded
314
+ with `resource_guard.py record`, which refuses past `limits.maxBrowsers`, `maxPages`,
315
+ `maxServers`; at the end `resource_guard.py close` then `verify` prove nothing is left. The
316
+ operator's own windows are never recorded, so they are never touched.
317
+ - **Where the ledger lives, and what to record.** `.task-pipeline/resources.json`, outside the
318
+ tracked tree. Record the process that stays alive (the browser, the server), not a launcher
319
+ that exits after starting it: a child reparented after its launcher died is not a
320
+ descendant any more, and `close` cannot find it. `record` refuses its own process, its
321
+ ancestors and pid 1; `close` never signals them.
322
+
295
323
  ## Getting past a login, and past a backend
296
324
 
297
325
  A surface behind auth is the usual reason a run skips the look. Both channels solve it,
@@ -0,0 +1,64 @@
1
+ # Design review: the interface, walked by its users, as a gate
2
+
3
+ Load this when a node's brief names a `surface_class` other than `none`, at stage 3 (before
4
+ build) and at stage 6 (after build), in a single run, a wave or a release iteration. The agent
5
+ is [`../../../agents/design-tester.md`](../../../agents/design-tester.md); its gate is
6
+ `scripts/design_gate.py`; its machine discipline is `scripts/resource_guard.py`; its browser is
7
+ [`browser.md`](browser.md) → *The tester's browser*.
8
+
9
+ ## Contents
10
+
11
+ - When it is owed
12
+ - The test panel
13
+ - The two passes and their gates
14
+ - The budget, and where findings go
15
+ - Degradation
16
+
17
+ ## When it is owed
18
+
19
+ Every node that changes what a user sees or does. On `flagship`, `product` and `ad` it is a
20
+ gate; on `internal` it is recommended and its report is checked for honesty, its coverage
21
+ reported. A recorded refusal (`Design review: declined: <reason>` in the brief) passes; an
22
+ unmentioned one does not. Waves and iterations owe it per UI node, never once per wave.
23
+
24
+ ## The test panel
25
+
26
+ `docs/ux/test-panel.md`, owned by super-ux (`ux-foundation` → *The test panel*): ten portraits,
27
+ each with **basis** (`evidence RE-NN` or `hypothesis`), **situation**, **goals**, **features**,
28
+ **scenarios** (composite chains by `SCR-` id), **activation** (steps to first value, and where
29
+ it drops), **retention** (trigger → action → reward → what brings it back) and **needs**
30
+ (device, engine, locale, accessibility). The operator confirms it once; a later change of the
31
+ product revises it. `python3 scripts/design_gate.py panel docs/ux/test-panel.md` gates it.
32
+
33
+ A portrait marked `hypothesis` chooses what to test, never what to build: the super-ux rule
34
+ that a synthetic persona is never evidence still holds.
35
+
36
+ ## The two passes and their gates
37
+
38
+ | Stage | Pass | Reads | Gate |
39
+ |---|---|---|---|
40
+ | 3, before build | D, design | Figma frames (Figma MCP) or, without Figma, the scenario's mockups | `design_gate.py report <r> --class <c> --panel docs/ux/test-panel.md --scope <ids> --require design` exits 0 |
41
+ | 6, after build | B, build | the running build in the recorded browser, each step captured, compared with its frame or mockup, the outcome read back | the same command with `--require build` exits 0, and `resource_guard.py verify` is clean |
42
+
43
+ The checklist every step answers: `frame`, `primary_action`, `controls`, `focus`, `states`,
44
+ `text_fit`, `outcome`: pass, fail, na or NOT_RUN with its reason. A failed item names its
45
+ finding; a `breaks` finding names its failed item. NOT_RUN is never PASS.
46
+
47
+ With Figma, pass D judges what the designer drew and pass B how it was built against it.
48
+ Without Figma, pass D judges the mockups and pass B the build against them.
49
+
50
+ ## The budget, and where findings go
51
+
52
+ One pass D and one pass B per surface per round; a re-review reads only the steps the fix
53
+ touched; at most `run.finish.maxRounds` rounds ([`finish-line.md`](finish-line.md)). A finding
54
+ in the scope goes to the builder (pass D: the designer); outside it, to the board with its
55
+ evidence. The review never widens the iteration.
56
+
57
+ ## Degradation
58
+
59
+ - **No panel**: NOT_RUN, and super-ux `ux-foundation` builds it; the gate does not pass on a
60
+ missing panel.
61
+ - **No Figma and no mockup**: pass D reads the scenario text alone and says so in every row's
62
+ `frame` (`scenario text only`); a gated class owes a mockup next round.
63
+ - **No browser channel**: pass B is NOT_RUN, the weaker claim recorded, never "tested".
64
+ - **A sign-in wall**: the human gate is named once, the steps behind it NOT_RUN, the rest run.
@@ -60,6 +60,7 @@ Custom profiles use their own declared stages and doctrine, with the kernel inta
60
60
  | **first run · the entry audit** (offered once) | `references/setup.md` |
61
61
  | **what travels with the bundle vs stays in a project** | `references/portability.md` |
62
62
  | 0–10 · when an iteration stops — the contract fixed at intake, triage at discovery, the round budget | `references/finish-line.md` |
63
+ | 3, 6 · the design review: the test panel walked over the design, then the build, in the recorded browser, bounded and closed | `references/design-review.md` |
63
64
  | any repeating loop | `references/loop-guard.md` |
64
65
  | run-wide · what the run **leaves running and leaves behind** — every gate, and stage 10 | `references/residue.md` |
65
66
  | run-wide · what the run **prints about itself** — the rail, the iteration line | `references/progress.md` |
@@ -24,9 +24,10 @@ fields, written at stage 0 and confirmed with the brief:
24
24
  - **Exit checks** — REQ ids, each with its command, run on the artefact the operator uses:
25
25
  released, installed or deployed. A branch is not that artefact.
26
26
  - **Scope** — the modules, paths and REQ ids this iteration may change, as a list.
27
- - **Budget** — `run.finish.maxRounds` (default 3) and `run.finish.maxReleases` (default 2),
28
- from `pipeline.json` ([`../pipeline.schema.json`](../pipeline.schema.json)), and
29
- `run.finish.cleanRounds`: 1, or 2 when the iteration carries an R3 or flagship node.
27
+ - **Budget** — `run.finish.maxRounds` (default 3), `run.finish.maxReleases` (default 2) and
28
+ `run.finish.maxAgents` (sub-agents at once, default 2, since 1.100.0), from `pipeline.json`
29
+ ([`../pipeline.schema.json`](../pipeline.schema.json)), and `run.finish.cleanRounds`: 1,
30
+ or 2 when the iteration carries an R3 or flagship node.
30
31
  - **Base** — the commit the iteration started from.
31
32
 
32
33
  Widening the scope is a recorded revision of the brief, with the operator's go. A finding is
@@ -92,8 +93,13 @@ finding to count:
92
93
 
93
94
  ```
94
95
  round: <N> — <reading> — new blocking <k>
96
+ agents: <N> — <most sub-agents running at once in round N>
95
97
  ```
96
98
 
99
+ Every run computes its verdict, not only a wave: a single run's inventory is its one node
100
+ (`waves.py finish --inventory`), and a person checks the same three numbers the script read:
101
+ rounds n/max, agents n/max, exit checks k/m on the named artefact.
102
+
97
103
  ## Computing the verdict
98
104
 
99
105
  ```
@@ -43,6 +43,11 @@ One line per file per pass. The reason must name **what forced the edit** — a
43
43
  finding id, a failed gate item, an operator instruction. "Cleanup", "polish" and
44
44
  "while I was there" are not reasons; they are churn with better manners.
45
45
 
46
+ Fan-out is bookkept too (since 1.100.0): each round writes `agents: <N> — <k>`, the most
47
+ sub-agents that ran at once in it. `waves.py finish` charges the peak against
48
+ `run.finish.maxAgents` (2 by default), so a loop that tries to escape its round budget by
49
+ spawning wider is stopped by the same verdict ([`finish-line.md`](finish-line.md)).
50
+
46
51
  **This ledger was required here from the day the file shipped and written by no run
47
52
  until 2026-08-10.** The detection below calls itself mechanical; with no ledger it had
48
53
  no input at all, so the guard sat on rung 1 while every reader took it for rung 3
@@ -96,6 +96,7 @@ a row pointing outside the bundle is the defect this file exists to catch.
96
96
  | **Effect verification** — the `verified-by:` contract and the rubric item that blocks | `references/build.md`, `references/review.md` |
97
97
  | The TDD iron law and the suite gate | `references/tdd.md` |
98
98
  | How the browser look is taken, and how a spec suite sits beside it | `references/browser.md` |
99
+ | How the design review walks the test panel over the design, then the build, bounded and closed | `references/design-review.md` |
99
100
  | The REQ coverage table, evidence rules, the closing question | `references/acceptance.md` |
100
101
  | How the host project's own conventions are read | `references/conventions.md` |
101
102
  | Which companions exist, what is required, self-currency | `references/companion-skills.md` |
@@ -205,6 +205,10 @@ narrative are how a capped list grows anyway.
205
205
  `Because`) **verbatim** into the quarter's archive, each block carrying the sha256 of
206
206
  its text, and leaves a pointer; `retro_budget.py verify` proves every pointer. An
207
207
  instruction still over 600 characters after its story moved is rewritten by hand.
208
+ An older retro whose standing instructions are a numbered list and whose stamps are
209
+ `- **date: title**` items is read the same way: the item keeps its bold rule and its
210
+ `*(Retire ...)*` trigger, a stamp keeps its title, stamps past the cap rotate out by date,
211
+ and, over budget, the Run stamps section's loose prune paragraphs move.
208
212
  - **A prune that retires nothing writes its count into the stamp row**, as
209
213
  `prune: 0 retired`, and no paragraph. Repeated "nothing to prune" notes were a
210
214
  measured share of the floor.
@@ -110,7 +110,9 @@ never that the work was skipped quietly.
110
110
  harvested source has a recorded resolution** (which governs, and whether the doc
111
111
  is now stale), no open contradictions, **every
112
112
  autonomy-sweep row is answered or explicitly marked "stop and ask here"**, **a
113
- user-facing task's brief names its `surface_class`**, the
113
+ user-facing task's brief names its `surface_class`**, and on any class but `none`
114
+ **the project's test browser is recorded** (`pipeline.json` → `browser`, asked once,
115
+ [`browser.md`](browser.md) → *The tester's browser*), the
114
116
  **REQ table is written and every row names its check**, **the brief's `## Finish line`
115
117
  is filled — objective, exit checks on the artefact the operator uses, scope, budget and
116
118
  base** ([`finish-line.md`](finish-line.md)), the carry-over ledger is
@@ -192,6 +194,11 @@ never that the work was skipped quietly.
192
194
  validator reads **NOT_RUN** beside the verdict — never PASS — and the gate stands on
193
195
  the floor, said so. A record saying `Mode: declined` with its reason is the refusal,
194
196
  and it passes.
197
+ **And the design review's pass D ran over the design before build** (since 1.100.0):
198
+ the `design-tester` walked the test panel through the changed scenarios over the Figma
199
+ frames or mockups, and `python3 scripts/design_gate.py report <design-review.json>
200
+ --class <surface_class> --panel docs/ux/test-panel.md --scope <SCR ids> --require design`
201
+ exits 0 ([`design-review.md`](design-review.md)).
195
202
  **Where both tracks ran, their convergence check is recorded** — findings with the
196
203
  ruling, or `Tracks converge: clean`; a screen where each track is right alone and they
197
204
  disagree together is the defect neither track's own review can see.
@@ -298,6 +305,10 @@ never that the work was skipped quietly.
298
305
  **one, two at most**, then `unresolved` to the person — never round three — and only
299
306
  external, specific feedback starts a round. The procedure and the order of the checks:
300
307
  [`browser.md`](browser.md) → *The visual half*.
308
+ **On any class but `none` the design review's pass B ran over the build** (since
309
+ 1.100.0): the same `design_gate.py report` with `--require build` exits 0, every capture
310
+ names its engine, and `python3 scripts/resource_guard.py verify <ledger>` is clean
311
+ ([`design-review.md`](design-review.md)).
301
312
  - **What the look finds is fixed here.** A rendering defect found at stage 6 is a
302
313
  stage-6 finding: fix it, look again, then call the stage green. Filing it to the
303
314
  board and advancing is how a run reports *checked in a browser* for a page it has
@@ -390,7 +401,9 @@ never that the work was skipped quietly.
390
401
  options rather than listed**, and the ambiguity count computed from the four
391
402
  registers with its ids ([`progress.md`](progress.md) → *The hand-back*) — a run
392
403
  that cannot say what happened has not finished, it has stopped; **the `finish:` line is
393
- printed beside this verdict — `done`, `budget` or `blocked`, with its block**
404
+ printed beside this verdict — `done`, `budget` or `blocked`, with its block** and
405
+ computed, in every run and not only in waves, by `scripts/waves.py finish` over this
406
+ run's ledger, which also fails a round whose `agents:` line exceeds `maxAgents`
394
407
  ([`finish-line.md`](finish-line.md) → *The finish block*); **the environment is
395
408
  given back** — all eight classes enumerated, everything this run started ended and
396
409
  verified by re-enumerating rather than by the teardown's reply, an earlier run of
@@ -12,9 +12,8 @@ the list the harvest works from.
12
12
  The rule a user of this skill reads is the shipped schema template,
13
13
  `templates/backlog-sources.schema.json` (backlog-sources/1), with `references/backlog.md` →
14
14
  *A declared source list*; this checker enforces it, plus the two rules a schema cannot state
15
- (a regular file reached through no symlink, no duplicate declaration). The Fabric workspace's
16
- `knowledge/backlog.md` (section *Source contract*; read at fabric-workspace d49b6f58,
17
- 2026-10-08) is its provenance: the template is compatible with it, with one deliberate
15
+ (a regular file reached through no symlink, no duplicate declaration). Fabric's
16
+ `knowledge/backlog.md` (its *Source contract* section, read on 2026-10-08) is its provenance: the template is compatible with it, with one deliberate
18
17
  widening — `repository` may be any `https://github.com/<owner>/<repo>`, because this skill runs
19
18
  outside one organisation.
20
19