task-pipeline-skill 1.99.1 → 1.101.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 +93 -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 +76 -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 +5 -1
  15. package/plugins/task-pipeline/skills/task-pipeline/references/stages.md +17 -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 +424 -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 +46 -3
  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.101.0 (2026-10-22)
2
+
3
+ Guards: 530 → **530**; property checks 15 → **15**. No negative was added: the new checks are
4
+ sixteen cases in `test/design_gate_test.py` (75 to 91) and two in
5
+ `test/audit_regressions/diet_retro_budget.py`.
6
+
7
+ **Cancel parity and one clear offer on every subscription surface** (hub milestone M2, G8 and
8
+ R2, from GP Bullhound's *Consumer Subscription Software*, October 2025, p. 18).
9
+
10
+ - **`design_gate.py report --subscription`** (or a `subscription` block in the report): the
11
+ surface sells, renews or cancels a subscription, so the report counts the steps to subscribe
12
+ and to cancel. Cancelling in more steps fails, a `pass` over a longer cancel path is refused, a
13
+ paywall without one clear offer fails, a check not walked is NOT_RUN with its reason, and a
14
+ missing pause is reported, not gated. `agents/design-tester.md` step 7 walks it;
15
+ `references/design-review.md` and the stage 6 gate in `references/stages.md` state it.
16
+ - **The panel counts locals and tourists** (M2 R3): `design_gate.py panel` requires each
17
+ retention line to open with `local` or `tourist` and at least two of each, the rule super-ux
18
+ 0.65.0's test panel template states.
19
+ - **One rotation line per archive** (B-151, found as sshlg-skills B-246): `retro_budget.py
20
+ migrate` re-archives a quarter's earlier rotated stamps with the new ones as one block and
21
+ keeps one `Rotated out:` line per archive, so a floor that rotates every run no longer grows by
22
+ a line per run. A line whose block no longer hashes stays as its own line.
23
+ - **Board:** B-044 collapsed into B-040 (the same finding); B-150 files the two fw07 branches
24
+ kept on purpose.
25
+ - **A test that raced the clock:** `test/lessons_test.py` H1 waited a fixed 1.5 s for a rebuild
26
+ to reach its read; under a load average near 300 the add ran first and the tagged gate failed
27
+ on negative 502. The rebuild's read now blocks on a handshake until the add has landed (431
28
+ cases).
29
+
30
+ ## v1.100.0 (2026-10-22)
31
+
32
+ Guards: 530 → **530**; property checks 15 → **15**. No negative was added: the new checks are
33
+ `test/design_gate_test.py` (75 cases) and eight new cases in `test/waves_test.py`.
34
+
35
+ **The design review is a gate** (operator, 2026-10-22). Every node that changes an interface
36
+ owes it, in a single run, a wave or a release iteration.
37
+
38
+ - **`design-tester`** (`agents/design-tester.md`) walks the project's ten-portrait test panel
39
+ (super-ux 0.64.0, `docs/ux/test-panel.md`) through the changed scenarios: pass D over the
40
+ Figma frames or mockups before build (stage 3), pass B in a real browser over the build after
41
+ it (stage 6). Every step answers a fixed checklist (frame, primary action, controls, focus,
42
+ states, text fit, outcome); a `breaks` finding names the item that failed.
43
+ - **`scripts/design_gate.py`** gates the panel (ten portraits, each with basis, features,
44
+ composite scenarios, activation and retention), the report `design-review/1` (coverage of
45
+ scope scenario × persona, the checklist, no open `breaks`, one pass of each kind per round,
46
+ the resource receipt) and the recorded browser (`browser.storageState` outside the repo).
47
+ - **The tester's browser** (`references/browser.md`): asked once per project and recorded in
48
+ `pipeline.json` → `browser`, WebKit first, a prepared signed-in session, a fallback that is
49
+ said, never silent.
50
+ - **`scripts/resource_guard.py`**: load, memory pressure and free disk before a run (wait over
51
+ the line), caps on browsers, pages and dev servers, a ledger of what the run started, and a
52
+ close that touches only that, verified empty.
53
+ - **The stop, in every run**: stage 10 computes the finish verdict with `waves.py finish` over
54
+ the run's own ledger, and `run.finish.maxAgents` (default 2) fails a round whose `agents:`
55
+ line ran more sub-agents at once.
56
+ - `scripts/backlog_sources.py` no longer cites a private repository.
57
+ - **Reviewed under its own budget:** three blind unit readings found 4, then 2, then 2 `breaks`
58
+ (each fixed with a regression case); the round budget of 3 was reached, so no fourth reading
59
+ ran.
60
+
1
61
  ## v1.99.1 (2026-10-21)
2
62
 
3
63
  Guards: 530 → **530**; property checks 15 → **15**. No negative was added: the new cases
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.1 |
15
+ | **Version** | 1.101.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.1",
3
+ "version": "1.101.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.1",
6
+ "version": "1.101.0",
7
7
  "author": {
8
8
  "name": "ssheleg",
9
9
  "url": "https://x.com/sshlg93"
@@ -0,0 +1,93 @@
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`, plus `--subscription`
53
+ when the surface sells, renews or cancels a subscription. Quote its output as your first
54
+ evidence. NOT_RUN is said as NOT_RUN.
55
+ 7. **A subscription surface: walk the way out.** Count the steps from the paywall to an active
56
+ subscription and from the account to a confirmed cancellation, as one persona, in the same
57
+ pass. Cancelling takes no more steps than subscribing (California's click-to-cancel rule
58
+ from 2025-07-01 says the same), the paywall makes one clear offer, and note whether a pause
59
+ is offered. This is the report's `subscription` block.
60
+
61
+ ## The report, `design-review/1`
62
+
63
+ ```json
64
+ {"schema": "design-review/1", "surface": "<surface>", "panel_revision": "<sha>",
65
+ "rows": [{"scenario": "SCR-03", "step": 2, "persona": "P-04", "pass": "build", "round": 1,
66
+ "frame": "<figma node or mockup>", "capture": "<path>", "engine": "webkit",
67
+ "outcome": "order #… created, visible in history",
68
+ "checks": {"frame": "pass", "primary_action": "pass", "controls": "fail",
69
+ "focus": "pass", "states": "na", "text_fit": "pass", "outcome": "pass"},
70
+ "findings": [{"severity": "breaks", "what": "no way back from the summary",
71
+ "where": "summary screen", "status": "open"}]}],
72
+ "resources": {"started": [], "live": [], "closed": true},
73
+ "subscription": {"cancel_parity": "pass", "subscribe_steps": 3, "cancel_steps": 2,
74
+ "cancel_path": "Settings > Subscription > Cancel", "single_offer": "pass",
75
+ "pause": "offered"}}
76
+ ```
77
+
78
+ `subscription` is present only on a subscription surface. `cancel_parity` and `single_offer` are
79
+ pass, fail or NOT_RUN (with `reason`); `pause` is offered, absent or na. A `pass` over a cancel
80
+ path longer than the subscribe path is refused.
81
+
82
+ `breaks` stops the user in a scenario step or contradicts the scenario's outcome. `risk` makes a
83
+ step slower, unclear or easy to get wrong. `nit` is polish. Taste is none of them: a finding
84
+ names the step, the persona and the checklist item it failed.
85
+
86
+ ## Three ways this goes wrong
87
+
88
+ - **The panel decided the product.** A hypothesis portrait chooses what to test, never what to
89
+ build. Say so when a finding rests only on a hypothesis.
90
+ - **The run kept going.** A fourth round, or steps outside the scope, is the loop the finish
91
+ line exists to stop (`references/finish-line.md`). Stop and hand back with the numbers.
92
+ - **The machine paid.** A browser left open, a dev server still listening, or a run started at
93
+ 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,76 @@
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** (opening with `local` or `tourist`, then trigger → action → reward →
30
+ what brings it back) and **needs** (device, engine, locale, accessibility). At least two
31
+ portraits are locals and two are tourists: lifetime value rests on the first, churn on the
32
+ second (since 1.101.0). The operator confirms it once; a later change of the product revises
33
+ it. `python3 scripts/design_gate.py panel docs/ux/test-panel.md` gates it.
34
+
35
+ A portrait marked `hypothesis` chooses what to test, never what to build: the super-ux rule
36
+ that a synthetic persona is never evidence still holds.
37
+
38
+ ## The two passes and their gates
39
+
40
+ | Stage | Pass | Reads | Gate |
41
+ |---|---|---|---|
42
+ | 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 |
43
+ | 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 |
44
+
45
+ The checklist every step answers: `frame`, `primary_action`, `controls`, `focus`, `states`,
46
+ `text_fit`, `outcome`: pass, fail, na or NOT_RUN with its reason. A failed item names its
47
+ finding; a `breaks` finding names its failed item. NOT_RUN is never PASS.
48
+
49
+ With Figma, pass D judges what the designer drew and pass B how it was built against it.
50
+ Without Figma, pass D judges the mockups and pass B the build against them.
51
+
52
+ ## A subscription surface: cancel parity and one clear offer
53
+
54
+ A surface that sells, renews or cancels a subscription (paywall, trial, pricing, cancel) adds
55
+ `--subscription` to both gates. The report's `subscription` block counts the steps to subscribe
56
+ and to cancel, walked by one persona in the same pass: cancelling takes no more steps than
57
+ subscribing (the click-to-cancel standard, law in California from 2025-07-01), the paywall makes
58
+ one clear offer, and whether a pause is offered is noted. A missing block, a longer cancel path
59
+ or a paywall without one offer fails; a check not walked is NOT_RUN with its reason; a missing
60
+ pause is reported, not gated. The block's fields are in `agents/design-tester.md`.
61
+
62
+ ## The budget, and where findings go
63
+
64
+ One pass D and one pass B per surface per round; a re-review reads only the steps the fix
65
+ touched; at most `run.finish.maxRounds` rounds ([`finish-line.md`](finish-line.md)). A finding
66
+ in the scope goes to the builder (pass D: the designer); outside it, to the board with its
67
+ evidence. The review never widens the iteration.
68
+
69
+ ## Degradation
70
+
71
+ - **No panel**: NOT_RUN, and super-ux `ux-foundation` builds it; the gate does not pass on a
72
+ missing panel.
73
+ - **No Figma and no mockup**: pass D reads the scenario text alone and says so in every row's
74
+ `frame` (`scenario text only`); a gated class owes a mockup next round.
75
+ - **No browser channel**: pass B is NOT_RUN, the weaker claim recorded, never "tested".
76
+ - **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` |
@@ -208,7 +208,11 @@ narrative are how a capped list grows anyway.
208
208
  An older retro whose standing instructions are a numbered list and whose stamps are
209
209
  `- **date: title**` items is read the same way: the item keeps its bold rule and its
210
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.
211
+ and, over budget, the Run stamps section's loose prune paragraphs move. Rotation keeps
212
+ **one `Rotated out:` line per archive**: a later rotation re-archives the quarter's
213
+ earlier rotated stamps with its own as one block and the line names that block, so a
214
+ floor that rotates every run does not grow by a line per run. A line whose block no
215
+ longer hashes stays as its own line.
212
216
  - **A prune that retires nothing writes its count into the stamp row**, as
213
217
  `prune: 0 retired`, and no paragraph. Repeated "nothing to prune" notes were a
214
218
  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,12 @@ 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)). A subscription surface (paywall, trial, pricing,
312
+ cancel) adds `--subscription`: cancelling takes no more steps than subscribing and the
313
+ paywall makes one clear offer (since 1.101.0).
301
314
  - **What the look finds is fixed here.** A rendering defect found at stage 6 is a
302
315
  stage-6 finding: fix it, look again, then call the stage green. Filing it to the
303
316
  board and advancing is how a run reports *checked in a browser* for a page it has
@@ -390,7 +403,9 @@ never that the work was skipped quietly.
390
403
  options rather than listed**, and the ambiguity count computed from the four
391
404
  registers with its ids ([`progress.md`](progress.md) → *The hand-back*) — a run
392
405
  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**
406
+ printed beside this verdict — `done`, `budget` or `blocked`, with its block** and
407
+ computed, in every run and not only in waves, by `scripts/waves.py finish` over this
408
+ run's ledger, which also fails a round whose `agents:` line exceeds `maxAgents`
394
409
  ([`finish-line.md`](finish-line.md) → *The finish block*); **the environment is
395
410
  given back** — all eight classes enumerated, everything this run started ended and
396
411
  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