forgedock 1.0.17 → 1.0.19

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 (134) hide show
  1. package/README.md +34 -1
  2. package/bin/cross-publish.mjs +344 -0
  3. package/bin/forgedock.mjs +229 -17
  4. package/commands/autopilot.md +77 -0
  5. package/commands/design-bench.md +176 -0
  6. package/commands/design-render-critique-loop.md +103 -0
  7. package/commands/design.md +259 -0
  8. package/commands/issue.md +7 -3
  9. package/commands/orchestrate.md +121 -0
  10. package/commands/{resume.md → pipeline-resume.md} +4 -4
  11. package/commands/{status.md → pipeline-status.md} +1 -1
  12. package/commands/qa-sweep.md +37 -2
  13. package/commands/quality-gate.md +57 -0
  14. package/commands/review-pr-staging.md +158 -2
  15. package/commands/test-gate.md +929 -0
  16. package/commands/work-on/build/implement.md +5 -3
  17. package/commands/work-on.md +53 -3
  18. package/docs/CI.md +177 -0
  19. package/docs/FORGE-PROTOCOL.md +324 -0
  20. package/docs/articles/b-vs-c-baseline.md +210 -0
  21. package/docs/articles/full-abc-benchmark.md +205 -0
  22. package/docs/articles/selective-spec-loading-token-savings.md +140 -0
  23. package/docs/design/abc-benchmark.md +195 -0
  24. package/docs/design/design-architect-rationale.md +123 -0
  25. package/docs/design/design-memory.md +61 -0
  26. package/docs/design/design-spec-schema.md +248 -0
  27. package/docs/design/design-system-lint.md +150 -0
  28. package/docs/design/divergent-generation.md +106 -0
  29. package/docs/design/effects-appropriateness.md +133 -0
  30. package/docs/design/fixtures/briefs/README.md +47 -0
  31. package/docs/design/fixtures/briefs/cadence.md +39 -0
  32. package/docs/design/fixtures/briefs/plume.md +39 -0
  33. package/docs/design/fixtures/briefs/slipstream.md +39 -0
  34. package/docs/design/fixtures/briefs/tender.md +39 -0
  35. package/docs/design/fixtures/briefs/voltage.md +39 -0
  36. package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-1.html +364 -0
  37. package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-2.html +420 -0
  38. package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-3.html +506 -0
  39. package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-1.html +611 -0
  40. package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-2.html +594 -0
  41. package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-3.html +817 -0
  42. package/docs/design/fixtures/runs/b-vs-c-baseline/runs.json +175 -0
  43. package/docs/design/fixtures/runs/b-vs-c-baseline/scorecard.json +138 -0
  44. package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-1.html +373 -0
  45. package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-2.html +398 -0
  46. package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-3.html +595 -0
  47. package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-1.html +531 -0
  48. package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-2.html +565 -0
  49. package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-3.html +674 -0
  50. package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-1.html +388 -0
  51. package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-2.html +463 -0
  52. package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-3.html +568 -0
  53. package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-1.html +562 -0
  54. package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-2.html +618 -0
  55. package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-3.html +618 -0
  56. package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-1.html +364 -0
  57. package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-2.html +420 -0
  58. package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-3.html +506 -0
  59. package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-1.html +527 -0
  60. package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-2.html +445 -0
  61. package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-3.html +483 -0
  62. package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-1.html +611 -0
  63. package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-2.html +594 -0
  64. package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-3.html +817 -0
  65. package/docs/design/fixtures/runs/full-abc/runs.json +190 -0
  66. package/docs/design/fixtures/runs/full-abc/scorecard.json +144 -0
  67. package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-1.html +496 -0
  68. package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-2.html +447 -0
  69. package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-3.html +564 -0
  70. package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-1.html +373 -0
  71. package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-2.html +398 -0
  72. package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-3.html +595 -0
  73. package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-1.html +622 -0
  74. package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-2.html +475 -0
  75. package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-3.html +660 -0
  76. package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-1.html +531 -0
  77. package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-2.html +565 -0
  78. package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-3.html +674 -0
  79. package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-1.html +525 -0
  80. package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-2.html +446 -0
  81. package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-3.html +463 -0
  82. package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-1.html +388 -0
  83. package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-2.html +463 -0
  84. package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-3.html +568 -0
  85. package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-1.html +1068 -0
  86. package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-2.html +1423 -0
  87. package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-3.html +1245 -0
  88. package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-1.html +305 -0
  89. package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-2.html +327 -0
  90. package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-3.html +428 -0
  91. package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-1.html +1270 -0
  92. package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-2.html +1172 -0
  93. package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-3.html +1532 -0
  94. package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-1.html +315 -0
  95. package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-2.html +342 -0
  96. package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-3.html +353 -0
  97. package/docs/design/fixtures/runs/full-abc-opus/runs.json +190 -0
  98. package/docs/design/fixtures/runs/full-abc-opus/scorecard.json +144 -0
  99. package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-1.html +345 -0
  100. package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-2.html +396 -0
  101. package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-3.html +502 -0
  102. package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-1.html +408 -0
  103. package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-2.html +446 -0
  104. package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-3.html +496 -0
  105. package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-1.html +813 -0
  106. package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-2.html +711 -0
  107. package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-3.html +721 -0
  108. package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-1.html +280 -0
  109. package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-2.html +291 -0
  110. package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-3.html +366 -0
  111. package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-1.html +1010 -0
  112. package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-2.html +836 -0
  113. package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-3.html +930 -0
  114. package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-1.html +407 -0
  115. package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-2.html +396 -0
  116. package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-3.html +529 -0
  117. package/docs/design/foundations/brutalist.css +294 -0
  118. package/docs/design/foundations/editorial.css +300 -0
  119. package/docs/design/foundations/minimal-luxury.css +295 -0
  120. package/docs/design/foundations/technical.css +293 -0
  121. package/docs/design/foundations/warm-photo.css +299 -0
  122. package/docs/design/reference-corpus.md +1726 -0
  123. package/docs/design/render-critique-loop.md +57 -0
  124. package/docs/spec-graph-schema.md +397 -0
  125. package/package.json +1 -1
  126. package/scripts/bench-scorecard.mjs +239 -0
  127. package/scripts/build-spec-graph.mjs +509 -0
  128. package/scripts/check-native-conflicts.sh +200 -0
  129. package/scripts/classify-lane.sh +0 -7
  130. package/scripts/design-system-lint.mjs +993 -0
  131. package/scripts/graph-query.sh +486 -0
  132. package/scripts/transition-label.sh +10 -2
  133. package/scripts/validate-spec-graph.sh +423 -0
  134. package/templates/workflows/forgedock-review.yml +96 -0
@@ -0,0 +1,103 @@
1
+ ---
2
+ description: Render a design from its FORGE:DESIGN_SPEC, critique the rendered pixels against the perceptual negatives, and iterate until it passes or the budget is spent — the missing reward signal.
3
+ argument-hint: [<issue-number>|<spec-path>] [--max-iters <n>] [--viewports desktop,mobile]
4
+ ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
7
+
8
+ # /design-render-critique-loop — render → vision-critique → iterate
9
+
10
+ **Input**: $ARGUMENTS — the design issue number (reads its `FORGE:DESIGN_SPEC`) or a path to a spec, with optional
11
+ `--max-iters <n>` (default 3) and `--viewports` (default `desktop,mobile`).
12
+
13
+ This is the **missing reward signal** for the UI Taste Harness (milestone #13, issue #882). Every other lever in the
14
+ harness can still raw-dog the output — write a page and defend it blind. This loop is the only stage that *looks at the
15
+ rendered pixels* and feeds a correction back in. Without it the harness has no feedback loop; with it, a page measurably
16
+ improves across iterations.
17
+
18
+ Read [`docs/design/render-critique-loop.md`](../docs/design/render-critique-loop.md) (methodology) before running —
19
+ the thesis, the iteration protocol, the deterministic-floor-first ordering, the anti-Goodhart independence rule, and the
20
+ pass/budget criteria all live there. This command is the executable procedure over that methodology.
21
+
22
+ **No application runtime.** ForgeDock is a set of command specs. The only executable helper is the deterministic
23
+ linter `scripts/design-system-lint.mjs` (#884); the render + critique steps are agent-driven via the Playwright MCP.
24
+
25
+ ---
26
+
27
+ ## Inputs (read before iterating)
28
+
29
+ | Input | Source | Role |
30
+ |-------|--------|------|
31
+ | `FORGE:DESIGN_SPEC` | the design issue (#881) | the committed intent the render must satisfy |
32
+ | `scripts/design-system-lint.mjs` | #884 | deterministic floor — cheap, runs first |
33
+ | Perceptual negatives N3, N8–N12 | [`reference-corpus.md`](../docs/design/reference-corpus.md) (#880) | what the vision critic judges (the linter declares these out of scope) |
34
+ | Render protocol | the #878 / #875 Playwright protocol | desktop + mobile capture, same viewports as the benchmark |
35
+
36
+ ## The loop
37
+
38
+ ```
39
+ iteration = 0
40
+ while iteration < max_iters:
41
+ iteration += 1
42
+
43
+ # 1. Deterministic floor FIRST (cheap — never spend a render on a page that fails the linter)
44
+ Run: node scripts/design-system-lint.mjs --spec <spec> --html <generated> [--strict]
45
+ if HARD findings: fix against the spec, continue # do not render a lint-failing page
46
+
47
+ # 2. Render (Playwright MCP) — desktop + mobile, same protocol as #878
48
+ Capture the page at each viewport.
49
+
50
+ # 3. Vision critique — judge the RENDERED pixels against the perceptual negatives ONLY
51
+ For N3 (centered/no asymmetry), N8 (stock/generic 3D), N9 (glassmorphism overuse),
52
+ N10 (no visual hierarchy), N11 (abstract copy / no product shown), N12 (decorative effects
53
+ that serve nothing): is the negative present in the render? Each hit is a finding.
54
+ Also check the render against the spec's own acceptance + effects_plan justification (#885).
55
+
56
+ # 4. Emit the per-iteration trail
57
+ Post a FORGE:CRITIQUE annotation (schema below) with findings + verdict.
58
+
59
+ if no perceptual findings AND lint clean: PASS — break
60
+ else: translate each finding into a concrete spec/markup correction and iterate.
61
+
62
+ if iteration == max_iters and not PASS:
63
+ Post the final FORGE:CRITIQUE with verdict BUDGET-EXHAUSTED and the residual findings.
64
+ ```
65
+
66
+ ## Ordering rule (cheap gate first)
67
+
68
+ The deterministic linter (#884) runs **before** every render. A page that fails a hard lint rule is fixed before a
69
+ render is ever captured — rendering and vision-critiquing a page the linter already rejects wastes the expensive step.
70
+ The critic owns **only** the perceptual negatives the linter declares out of scope (N3, N8–N12); it never re-checks a
71
+ deterministic rule. No duplication.
72
+
73
+ ## Anti-Goodhart independence (non-negotiable)
74
+
75
+ The critic in this loop is **strictly independent** from the ABC benchmark judge (#878). The benchmark judge is the
76
+ *evaluator* — it scores finished arms blind against arm C to tell us whether the harness improved. If the loop
77
+ optimized against that same judge, the harness would be training on its own test set (Goodhart). The loop's critic
78
+ judges *one page against its own committed spec + the perceptual negatives*; the benchmark judge judges *arms against a
79
+ real reference*. They must never be the same judge. This is also distinct from the divergent-generation taste-judge
80
+ (#883), which scores pre-render candidate directions.
81
+
82
+ ## `FORGE:CRITIQUE` annotation
83
+
84
+ Registered in [FORGE-PROTOCOL](../docs/FORGE-PROTOCOL.md). One per iteration, so the improvement trajectory is an
85
+ auditable artifact:
86
+
87
+ ```markdown
88
+ <!-- FORGE:CRITIQUE -->
89
+ ## Critique — {product} · iteration {i}/{max}
90
+
91
+ **Lint floor:** {PASS | fixed N hard findings}
92
+ **Render:** desktop + mobile captured
93
+ **Perceptual findings:**
94
+ - N{n}: {what was observed in the render} → {correction}
95
+ **Verdict:** {PASS | ITERATE | BUDGET-EXHAUSTED}
96
+ <!-- FORGE:CRITIQUE:COMPLETE -->
97
+ ```
98
+
99
+ ## Success measure
100
+
101
+ The loop works when a page **improves across iterations** and when, on the seed set, *A-with-loop* scores higher than
102
+ *A-without-loop* and higher than *raw B*, measured against the #879 baseline via the ABC benchmark (#878). The loop is
103
+ the reward signal; the benchmark is the independent scoreboard that confirms it moved.
@@ -0,0 +1,259 @@
1
+ ---
2
+ description: Route a design GitHub issue through the UI Taste Harness — investigate → architect → implement → critique loop → user-feedback loop → close — accumulating FORGE:DESIGN_* annotations and driving the design:* label state machine to design:shipped.
3
+ argument-hint: <issue-number> | <brief-name>
4
+ ---
5
+ <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
7
+
8
+ # /design — the design pipeline (the `/work-on` analog for design)
9
+
10
+ **Input**: $ARGUMENTS — a design issue number, or a seed brief name (`cadence` | `tender` | `slipstream` | `voltage` | `plume`) which is opened as a design issue first.
11
+
12
+ This is the **spine of the UI Taste Harness** (milestone #13, issue #888). Just as a code task is a GitHub issue
13
+ routed `investigate → build → review → close` by [`/work-on`](work-on.md) — accumulating `FORGE:*` annotations, closed
14
+ at the end — a **design task is a GitHub issue routed through this design pipeline**, accumulating `FORGE:DESIGN_*`
15
+ annotations and closed when the design ships and passes its gates. It is a parallel track on the same "GitHub as a
16
+ knowledge graph" model, not a new paradigm.
17
+
18
+ **It IS arm A of the ABC benchmark (#878).** Running a brief through `/design` produces the arm-A output; the benchmark
19
+ rig (`/design-bench`) feeds briefs in and screenshots the result, so this command is what makes the benchmark runnable
20
+ end to end.
21
+
22
+ **Agent model policy**: `model: "claude-opus-4-6"` — Opus is the validated generation model. Benchmark #878 (90% A-vs-B pairwise win-rate with Opus vs 54% with Sonnet) confirms a tier-level quality difference. Both arm A and arm B MUST use the same model to keep the benchmark valid (see [design-bench](design-bench.md)).
23
+
24
+ **No application runtime.** ForgeDock is a set of command specs. `/design` *sequences* the already-built stage specs —
25
+ it does not reimplement them. The only executable helper in the chain is the deterministic linter
26
+ `scripts/design-system-lint.mjs` (#884).
27
+
28
+ ---
29
+
30
+ ## Universal Stage Dispatcher
31
+
32
+ <!-- This is the single source of truth for design:* transitions, mirroring /work-on's Universal Phase Dispatcher. -->
33
+
34
+ | Stage | design:* label | Terminal? | Reads | Produces | Spec it sequences |
35
+ |-------|----------------|-----------|-------|----------|-------------------|
36
+ | 1. design-investigate | `design:investigating` | No | the brief | `FORGE:DESIGN_CONTEXT` | corpus #880 (grammar) + memory #887 (what to diverge from) |
37
+ | 2. design-architect | `design:architecting` | No | `FORGE:DESIGN_CONTEXT` | `FORGE:DESIGN_RATIONALE` → `FORGE:DESIGN_CANDIDATES` → `FORGE:DESIGN_SPEC` | rationale #886, divergent generation #883, effects #885, schema #881 |
38
+ | 3. design-implement | `design:generating` | No | `FORGE:DESIGN_SPEC` | the generated page (artifact) | — (generate from the committed spec) |
39
+ | 4. design-critique | `design:critiquing` | No | the render + `FORGE:DESIGN_SPEC` | `FORGE:CRITIQUE` (per pass) | render→critique loop #882 (lint #884 floor + perf #875 + a11y) |
40
+ | 4.5. user-feedback | `design:awaiting-feedback` | No | `FORGE:CRITIQUE` + user input | `FORGE:USER_FEEDBACK` (per round) | this spec (#1044); bypassed in automated/benchmark runs |
41
+ | 5. design-close | `design:shipped` / `design:rejected` | **Yes** | `FORGE:USER_FEEDBACK` (latest) or `FORGE:CRITIQUE` | `FORGE:DESIGN_SHIPPED` | scorecard + write outcome to memory #887 |
42
+
43
+ **Continuation rule** (mirrors `/work-on`): after any stage completes, if the issue is not at a terminal label
44
+ (`design:shipped` or `design:rejected`), proceed to the next stage immediately. No intermediate stage is terminal.
45
+ `design:awaiting-feedback` is **non-terminal** — it resolves to Stage 5 when `FORGE:USER_FEEDBACK.satisfied == "yes"`
46
+ or when no user is present (automated runs bypass Stage 4.5 entirely).
47
+
48
+ ---
49
+
50
+ ## Stage 1 — design-investigate (`design:investigating`)
51
+
52
+ Parse the brief into message / audience / single objection. Pull the relevant grammar from the
53
+ [reference corpus](../docs/design/reference-corpus.md) (#880) and query [design-memory](../docs/design/design-memory.md)
54
+ (#887) for the recent signature moves / archetypes / palettes to **diverge** from. Post `FORGE:DESIGN_CONTEXT`.
55
+
56
+ ## Stage 2 — design-architect (`design:architecting`)
57
+
58
+ Run the [design-architect rationale](../docs/design/design-architect-rationale.md) (#886): the 7-element diary →
59
+ `FORGE:DESIGN_RATIONALE`. Then [divergent generation](../docs/design/divergent-generation.md) (#883): commit one
60
+ archetype, diverge into N directions, independent taste-judge selects → `FORGE:DESIGN_CANDIDATES`. The winning
61
+ direction + the [effects-appropriateness](../docs/design/effects-appropriateness.md) (#885) `effects_plan` are
62
+ committed into `FORGE:DESIGN_SPEC` ([schema](../docs/design/design-spec-schema.md), #881).
63
+
64
+ ## Stage 3 — design-implement (`design:generating`)
65
+
66
+ Generate the page from the committed `FORGE:DESIGN_SPEC`. The spec is the contract — no taste decisions are re-rolled
67
+ here; generation realizes the committed intent.
68
+
69
+ ### Foundation CSS injection <!-- Added: forge#1048 -->
70
+
71
+ Before generating the page, inject the archetype's pre-built CSS foundation as the starting stylesheet. Foundation files live in `docs/design/foundations/` and are keyed by `meta.archetype` from `FORGE:DESIGN_SPEC`:
72
+
73
+ | `meta.archetype` | Foundation file |
74
+ |---|---|
75
+ | `editorial-typographic` | `docs/design/foundations/editorial.css` |
76
+ | `technical-dense` | `docs/design/foundations/technical.css` |
77
+ | `minimal-luxury` | `docs/design/foundations/minimal-luxury.css` |
78
+ | `bold-brutalist` | `docs/design/foundations/brutalist.css` |
79
+ | `warm-photographic` | `docs/design/foundations/warm-photo.css` |
80
+
81
+ **Injection pattern**: read the foundation file content and include it in the generation prompt as the starting `<style>` block. Instruct the generator to:
82
+ 1. Use the foundation's component classes (`.btn`, `.card`, `.badge`, `.input`, `.reveal`) where applicable — do NOT replace them with generic Tailwind equivalents.
83
+ 2. Override `--color-accent` and `--color-bg` custom properties with the spec's `color.accent` and `color.background` values to apply the committed palette to the foundation.
84
+ 3. Extend, don't replace — additional styles may be added on top of the foundation, but the foundation's radius system, component state management (hover/active/focus), and micro-detail rules (::selection, scrollbar, focus rings) must be preserved.
85
+
86
+ The foundation is the professional baseline. The generator adds layout and content on top.
87
+
88
+ ## Stage 4 — design-critique (`design:critiquing`)
89
+
90
+ Run [`/design-render-critique-loop`](design-render-critique-loop.md) (#882): deterministic lint floor (#884) → Playwright
91
+ render (desktop + mobile, #875/#878) → vision critique of the perceptual negatives → iterate. Each pass posts a
92
+ `FORGE:CRITIQUE`. The loop continues until PASS or budget.
93
+
94
+ ## Stage 4.5 — user-feedback loop (`design:awaiting-feedback`) <!-- Added: forge#1044 -->
95
+
96
+ **Entry condition**: Stage 4 (design-critique) has completed — `FORGE:CRITIQUE` posted with verdict `PASS` or
97
+ `BUDGET-EXHAUSTED`. **Automated/benchmark bypass**: if no user is present (e.g., running as part of the ABC benchmark
98
+ arm A), skip this stage entirely and proceed to Stage 5.
99
+
100
+ **Purpose**: The automated critique loop (#882) closes the perceptual-quality loop. This stage closes the **brand
101
+ fit** loop — the part only the user can judge. Brand assets are user-supplied, emotional register is subjective, and
102
+ the best heroes are refined, not generated once. Stage 4.5 collects that input in a structured way and routes it to
103
+ surgical section-level re-generation rather than full-page regeneration.
104
+
105
+ ### Step 1 — Set label
106
+
107
+ ```bash
108
+ gh issue edit {NUMBER} {GH_FLAG} --add-label "design:awaiting-feedback" \
109
+ --remove-label "design:critiquing" 2>/dev/null || true
110
+ ```
111
+
112
+ ### Step 2 — Prompt the user with structured hero-specific questions
113
+
114
+ After Stage 4 completes, post a comment on the design issue asking the user the following questions. Present them
115
+ clearly — the user's answer drives the `FORGE:USER_FEEDBACK` structured fields.
116
+
117
+ ```
118
+ ## Hero feedback — your turn
119
+
120
+ The automated critique is complete. Now it's yours to shape.
121
+
122
+ Please answer any or all of the following:
123
+
124
+ 1. **Demo video or animation** — Do you have a product demo video or animation to feature in the hero?
125
+ (paste a URL, or say "none")
126
+
127
+ 2. **Primary emotion** — What's the primary feeling you want the hero to evoke?
128
+ Options: trust / speed / power / craft / play — or describe in your own words.
129
+
130
+ 3. **Brand assets** — Any assets to integrate?
131
+ - Logo SVG (paste URL or say "none")
132
+ - Brand hex colors (e.g. "#1a1a2e, #e94560" or "none")
133
+ - Product screenshots (paste URLs or say "none")
134
+
135
+ 4. **Anything that feels off?** — Any section, element, or overall impression that feels wrong or
136
+ off-brand? Describe it.
137
+
138
+ If you're happy with the design as-is, reply: **"looks good, ship it"** (or any affirmative) — that
139
+ sets `satisfied: yes` and moves to Stage 5 without re-generation.
140
+ ```
141
+
142
+ ### Step 3 — Parse the user's response into `FORGE:USER_FEEDBACK`
143
+
144
+ Read the user's reply and produce a structured `FORGE:USER_FEEDBACK` annotation. Map natural-language input to
145
+ the structured fields:
146
+
147
+ | User says | Maps to |
148
+ |---|---|
149
+ | Pastes a video URL | `feedback_type: asset`, `asset_url: <url>`, `section_target: "hero"` |
150
+ | "make it feel faster / more urgent" | `feedback_type: emotion`, `emotion_target: speed`, `modification: "increase motion intensity; tighten headline tracking"` |
151
+ | "add our logo" + SVG URL | `feedback_type: asset`, `asset_url: <url>`, `section_target: "nav"`, `modification: "replace text logo with SVG; adjust sizing"` |
152
+ | Provides brand hex colors (e.g. "#1a1a2e") | `feedback_type: asset`, `asset_url: none`, `section_target: "all"`, `modification: "update CSS custom properties: --color-accent: #… --color-bg: #…"` |
153
+ | Describes a layout change | `feedback_type: direction`, `modification: <parsed description>` |
154
+ | Freeform complaint | `feedback_type: freeform`, `freeform_notes: <verbatim>`, `modification: <best-effort parse>` |
155
+ | "looks good" / "ship it" / affirmative | `satisfied: yes` → proceed to Stage 5 without re-generation |
156
+
157
+ ```bash
158
+ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:USER_FEEDBACK -->
159
+ ## User Feedback — {product} · round {n}
160
+
161
+ **Section target:** {section_target}
162
+ **Feedback type:** {asset | emotion | direction | freeform}
163
+ **Asset URL:** {URL or "none"}
164
+ **Modification:** {structured description of what to change}
165
+ **Emotion target:** {trust | speed | power | craft | play | unchanged}
166
+ **Satisfied:** {yes | no}
167
+ **Freeform notes:** {verbatim user input not captured above}
168
+ <!-- FORGE:USER_FEEDBACK:COMPLETE -->"
169
+ ```
170
+
171
+ ### Step 4 — Route on `satisfied`
172
+
173
+ **If `satisfied: yes`** (user approved, or no feedback given): proceed immediately to Stage 5 (design-close).
174
+ No re-generation needed.
175
+
176
+ **If `satisfied: no`** (feedback given): proceed to Step 5 (surgical re-generation), then loop back to Step 2
177
+ for another feedback round.
178
+
179
+ ### Step 5 — Surgical re-generation (only when `satisfied: no`)
180
+
181
+ Use the `section_target` from `FORGE:USER_FEEDBACK` and the section IDs in `FORGE:DESIGN_SPEC →
182
+ layout_grammar.sections` to re-generate only the targeted section. See
183
+ [`docs/design/design-spec-schema.md`](../docs/design/design-spec-schema.md) for the full surgical re-generation
184
+ contract.
185
+
186
+ **Inputs for the re-generation agent**:
187
+ 1. The full current HTML page
188
+ 2. `FORGE:DESIGN_SPEC` (the committed spec — defines what all non-targeted sections must look like)
189
+ 3. `FORGE:USER_FEEDBACK` (the modification + asset URL for the targeted section)
190
+
191
+ **Re-generation rules**:
192
+ - Modify **only** the targeted section's markup and styles
193
+ - Do NOT change any other section's content, layout, or CSS
194
+ - Do NOT re-roll the committed `meta.archetype` or the signature move from `FORGE:DESIGN_RATIONALE`
195
+ - If the feedback requires an archetype change, note the constraint and propose the closest variant within the committed direction
196
+ - After re-generation, run the deterministic linter on the modified section to catch any regressions (#884)
197
+
198
+ **Asset integration**:
199
+
200
+ | Asset | Integration procedure |
201
+ |---|---|
202
+ | Video URL (mp4/webm) | Add `<video autoplay loop muted playsinline>` scaffold in the hero visual area; adjust hero layout CSS to accommodate; keep LCP budget ≤ 2000ms |
203
+ | Video embed URL (YouTube/Vimeo) | `<iframe>` with `loading="lazy"` and aspect-ratio container (`padding-top: 56.25%`); no autoplay |
204
+ | Logo SVG | Fetch the SVG; inline or `<img>` it at the nav/hero logo node; remove the text logo; set `width`/`height` to maintain proportional sizing; verify contrast against background (WCAG AA ≥ 4.5:1) |
205
+ | Brand hex colors | Update the relevant CSS custom properties (`--color-accent`, `--color-bg`, etc.); run contrast check on all text/background pairs; record the update in `effects_plan.per_section` if it affects a hero effect |
206
+ | Product screenshot/image | Replace the placeholder with `<img loading="lazy" decoding="async">` or a `background-image`; maintain existing aspect-ratio and sizing tokens from the spec |
207
+
208
+ **After re-generation**: loop back to Step 2 — prompt the user again for the next round.
209
+
210
+ ### Budget
211
+
212
+ Maximum **3 user-feedback rounds** per design issue (configurable). If the budget is exhausted with `satisfied: no`,
213
+ post a summary of the rounds and proceed to Stage 5 with the best version. The design was not fully approved by the
214
+ user — note this in the `FORGE:DESIGN_SHIPPED` annotation.
215
+
216
+ ---
217
+
218
+ ## Stage 5 — design-close (`design:shipped` | `design:rejected`)
219
+
220
+ Apply the **definition of done**: critique-rubric threshold met **and** perf budget (#875) **and** a11y check **and**
221
+ the divergence check vs memory (#887) — not a reskin of a prior design. If all pass → write the realized outcome
222
+ (archetype, signature move, palette/type/effects, learnings) to [design-memory](../docs/design/design-memory.md) (#887),
223
+ post `FORGE:DESIGN_SHIPPED`, set `design:shipped`, close the issue. Otherwise loop (back to Stage 4) or, if the budget is
224
+ exhausted with residual findings, set `design:rejected` with the reasons on the issue.
225
+
226
+ ---
227
+
228
+ ## Label state machine
229
+
230
+ ```
231
+ design:investigating → design:architecting → design:generating → design:critiquing → design:awaiting-feedback → design:shipped
232
+ │ ↑
233
+ └──── (loop ≤ 3) ───────┘
234
+ (automated bypass: skip awaiting-feedback)
235
+ └──────────────────────────────────────────→ design:rejected
236
+ ```
237
+
238
+ Terminal labels: `design:shipped`, `design:rejected`. These mirror `workflow:merged` / `workflow:invalid` on the code
239
+ track. `design:awaiting-feedback` is **non-terminal** — it resolves to `design:shipped` (via Stage 5) when the user
240
+ approves or the feedback budget is exhausted.
241
+
242
+ ## Definition of done
243
+
244
+ A design ships only when it passes **all** of: the critique rubric threshold, the perf budget (#875), the a11y check,
245
+ and the divergence check (#887). Anything short loops or is rejected with reasons recorded on the issue — so every
246
+ outcome, pass or fail, is auditable on the GitHub issue.
247
+
248
+ ## Why it matters
249
+
250
+ 1. **Traceability** — every design's thinking (`FORGE:DESIGN_RATIONALE`), the candidate set and the choice
251
+ (`FORGE:DESIGN_CANDIDATES`), each critique iteration (`FORGE:CRITIQUE`), each user-feedback round
252
+ (`FORGE:USER_FEEDBACK`), and the final outcome (`FORGE:DESIGN_SHIPPED`) live on the issue. You can read *why* a
253
+ page looks the way it does — the designer's diary, made auditable, reviewable by a non-designer.
254
+ 2. **It is arm A.** Driving the seed briefs (Cadence / Tender / Slipstream / Voltage / Plume) through `/design`
255
+ produces the arm-A outputs the benchmark (#878) scores against the real references — so building this makes the ABC
256
+ benchmark runnable end to end.
257
+ 3. **Brand fit.** The automated critique loop (#882) closes the perceptual-quality loop. Stage 4.5 closes the brand-fit
258
+ loop — the part only the user can judge. Brand assets are user-supplied, emotional register is subjective, and the
259
+ best heroes are refined through iteration. The feedback annotations make that iteration traceable and repeatable.
package/commands/issue.md CHANGED
@@ -205,10 +205,12 @@ Files that need changes (ordered by dependency — change these in this order):
205
205
 
206
206
  ## Acceptance Criteria
207
207
 
208
- - [ ] {Specific, testable criterion}
208
+ - [ ] {Specific, testable criterion} [type:api]
209
209
  - [ ] {Specific, testable criterion}
210
210
  - [ ] {No regressions in {related_feature}}
211
211
 
212
+ > **Test-type annotation** (optional): Append `[type:api]`, `[type:unit]`, `[type:e2e]`, or `[type:manual]` to each criterion. The test gate reads this annotation directly and skips regex inference. Omit it to rely on regex classification fallback.
213
+
212
214
  ## Dependencies
213
215
 
214
216
  {If this issue depends on other issues:}
@@ -281,8 +283,10 @@ Files that need changes (ordered by dependency):
281
283
 
282
284
  ## Acceptance Criteria
283
285
 
286
+ - [ ] {Specific, testable criterion} [type:api]
284
287
  - [ ] {Specific, testable criterion}
285
- - [ ] {Specific, testable criterion}
288
+
289
+ > **Test-type annotation** (optional): Append `[type:api]`, `[type:unit]`, `[type:e2e]`, or `[type:manual]` to each criterion. The test gate reads this annotation directly and skips regex inference. Omit it to rely on regex classification fallback.
286
290
 
287
291
  ## Context
288
292
 
@@ -307,7 +311,7 @@ Files that need changes (ordered by dependency):
307
311
  - Title MUST use conventional commit prefix: `fix:`, `feat:`, `refactor:`, `investigate:`, `docs:`
308
312
  - `## Problem` is MANDATORY — every issue must state what's wrong or what's needed
309
313
  - `## Affected Files` is MANDATORY — list actual file paths the investigator should read first
310
- - `## Acceptance Criteria` is MANDATORY — at least one testable `- [ ]` criterion
314
+ - `## Acceptance Criteria` is MANDATORY — at least one testable `- [ ]` criterion; each item MAY carry an optional `[type:api|unit|e2e|manual]` annotation for deterministic test-gate classification (omit to fall back to regex inference)
311
315
  - Domain-specific sections (Evidence Trail, Pattern Metadata, Validation Checklist, etc.) SHOULD be preserved — they add pipeline value. Add mandatory sections around them, not instead of them.
312
316
  - `## Prior Investigation` is OPTIONAL — include only when parent/sibling investigation Gist URLs are available. Each Gist URL must be wrapped in a `<!-- FORGE:PRIOR_GIST: {url} -->` annotation for machine-readable parsing by downstream agents. <!-- Added: forge#339 -->
313
317
 
@@ -552,6 +552,71 @@ If `UNEXPECTED_GROWTH > 500`, report the alert clearly before launching the next
552
552
 
553
553
  ---
554
554
 
555
+ ### Step 4A.pre.0: Pre-create milestone branches for the wave (MANDATORY before classify-lane) <!-- Added: forge#901 -->
556
+
557
+ **WHY THIS EXISTS**: Feature-lane milestone branches were created lazily — by whichever feature-lane agent reached its build phase first. When a wave launches N feature-lane agents in parallel, they each run the lane check at roughly the same time. Every agent that runs before the branch is first pushed observes "branch absent" and is misrouted (hard-fail / `needs-human`, or fallback to staging in older code paths). The result is a single milestone's PRs scattered across the milestone branch and staging — a branch-routing nondeterminism that recurs under parallelism.
558
+
559
+ The fix is deterministic: create every milestone branch the wave will target **once, up front, before any agent runs `classify-lane.sh`**. After this step, every agent's lane check sees the branch and routes consistently.
560
+
561
+ **When to run**: Before the classify-lane loop in Step 4A.pre, for every wave. The step is a no-op for pure fast-lane waves (no issue in the wave has a milestone).
562
+
563
+ **Requires bash 4+**: This snippet uses an associative array (`declare -A SEEN_MILESTONE_SLUG`) to de-dupe milestone slugs, so it must run under bash 4 or newer. Under a non-bash POSIX shell (`sh`/dash), `declare -A` fails and the de-dupe silently no-ops. This degrades gracefully — branch creation stays correct because the `git ls-remote --exit-code` exists-check below still skips any milestone branch that already exists; the only effect is redundant, idempotent `ls-remote`/`push` attempts for milestones referenced by more than one issue in the wave. Run this command's blocks under bash 4+. <!-- Added: forge#901 -->
564
+
565
+ ```bash
566
+ # Pre-create the origin milestone branch for every distinct milestone referenced by the wave.
567
+ # Slugification MUST byte-match scripts/classify-lane.sh — otherwise a branch is created that
568
+ # the classifier will not select. Keep these two slug pipelines identical.
569
+ git fetch origin
570
+
571
+ # Collect distinct milestone titles among the wave's issues
572
+ declare -A SEEN_MILESTONE_SLUG
573
+ for NUM in {wave_issue_numbers}; do
574
+ MILESTONE_TITLE=$(gh issue view "$NUM" -R {GH_REPO} --json milestone --jq '.milestone.title // empty' 2>/dev/null || echo "")
575
+ [ -z "$MILESTONE_TITLE" ] && continue # fast-lane issue — no milestone branch needed
576
+
577
+ # Slugify — IDENTICAL to classify-lane.sh: lowercase → spaces-to-hyphens →
578
+ # strip non-[a-z0-9-] → collapse hyphens → strip leading/trailing hyphens.
579
+ SLUG=$(echo "$MILESTONE_TITLE" \
580
+ | tr '[:upper:]' '[:lower:]' \
581
+ | tr ' ' '-' \
582
+ | tr -cd 'a-z0-9-' \
583
+ | sed 's/--*/-/g' \
584
+ | sed 's/^-//;s/-$//')
585
+
586
+ # Empty-slug guard (matches classify-lane.sh): a title with no ASCII letters/digits/hyphens
587
+ # would produce "milestone/", an invalid ref. Skip and let classify-lane surface the error.
588
+ if [ -z "$SLUG" ]; then
589
+ echo "WARN: milestone title '$MILESTONE_TITLE' (issue #$NUM) produced an empty slug — skipping pre-creation; classify-lane will hard-fail." >&2
590
+ continue
591
+ fi
592
+
593
+ # De-dupe: only attempt creation once per milestone
594
+ [ -n "${SEEN_MILESTONE_SLUG[$SLUG]:-}" ] && continue
595
+ SEEN_MILESTONE_SLUG[$SLUG]=1
596
+
597
+ LANE="milestone/$SLUG"
598
+ if git ls-remote --exit-code origin "$LANE" >/dev/null 2>&1; then
599
+ echo "Milestone branch '$LANE' already exists on origin — no action."
600
+ continue
601
+ fi
602
+
603
+ # Create-if-absent from the default branch (matches /milestone create).
604
+ # $DEFAULT_BRANCH is resolved from forge.yaml at the top of this command.
605
+ echo "Pre-creating milestone branch '$LANE' from origin/$DEFAULT_BRANCH …"
606
+ if git push origin "origin/$DEFAULT_BRANCH:refs/heads/$LANE" 2>/dev/null; then
607
+ echo "Created milestone branch '$LANE'."
608
+ elif git ls-remote --exit-code origin "$LANE" >/dev/null 2>&1; then
609
+ # A concurrent orchestrator (or an agent) created it first — harmless. Never force-push.
610
+ echo "Milestone branch '$LANE' was created concurrently — proceeding with the existing branch."
611
+ else
612
+ echo "ERROR: failed to pre-create milestone branch '$LANE' from origin/$DEFAULT_BRANCH." >&2
613
+ echo " classify-lane.sh will hard-fail for issues in this milestone until the branch exists." >&2
614
+ fi
615
+ done
616
+ ```
617
+
618
+ This step is the deterministic counterpart to fix #2 (atomic create-if-absent in the classifier): by guaranteeing the branch exists before the wave's lane checks run, no agent can observe a missing branch. `classify-lane.sh`'s hard-fail is intentionally preserved as the phantom-slug gate for any path that bypasses this step.
619
+
555
620
  ### Step 4A.pre: Classify lane for each issue (MANDATORY before spawning agents)
556
621
 
557
622
  Before building agent prompts, run `classify-lane.sh` for every issue in the current wave to compute `{LANE}` and `{PR_BASE}` deterministically. The script output is authoritative — the LLM MUST NOT override or reason around it.
@@ -932,6 +997,62 @@ They remain open in GitHub — they will be picked up by future `/orchestrate` o
932
997
 
933
998
  **If no review-finding issues were spawned:** Continue to next wave normally.
934
999
 
1000
+ ### Step 4C.5: Milestone lane-consistency check (run after each wave) <!-- Added: forge#901 -->
1001
+
1002
+ **WHY THIS EXISTS**: A milestone's feature-lane PRs must all target the same milestone branch. If a branch-routing race ever scatters them — some on the milestone branch, some on staging — the milestone branch becomes incomplete relative to staging, and the split is otherwise invisible until the milestone tries to ship. Step 4A.pre.0 prevents the split deterministically; this check detects any residual split so it surfaces immediately instead of at ship time.
1003
+
1004
+ **When to run**: After each wave completes, for any batch where at least one issue has a milestone. Skip for pure fast-lane batches. This check is **non-blocking** — it alerts; it does not auto-resolve or stop the pipeline.
1005
+
1006
+ ```bash
1007
+ # For each distinct milestone in the batch, assert all of its feature-lane PRs share one base.
1008
+ for NUM in {all_batch_issue_numbers}; do
1009
+ MILESTONE_TITLE=$(gh issue view "$NUM" -R {GH_REPO} --json milestone --jq '.milestone.title // empty' 2>/dev/null || echo "")
1010
+ [ -z "$MILESTONE_TITLE" ] && continue
1011
+
1012
+ SLUG=$(echo "$MILESTONE_TITLE" \
1013
+ | tr '[:upper:]' '[:lower:]' \
1014
+ | tr ' ' '-' \
1015
+ | tr -cd 'a-z0-9-' \
1016
+ | sed 's/--*/-/g' \
1017
+ | sed 's/^-//;s/-$//')
1018
+ [ -z "$SLUG" ] && continue
1019
+ EXPECTED_BASE="milestone/$SLUG"
1020
+
1021
+ # Collect the base branch of every PR that closes an issue in this milestone.
1022
+ # Iterate the milestone's issues and read each one's linked PR base.
1023
+ # Exclude CLOSED-unmerged PRs: a closed-but-not-merged PR is a superseded/abandoned
1024
+ # routing attempt and does NOT reflect the live lane. Keep only OPEN (in-flight) and
1025
+ # MERGED (landed) PRs. `gh pr list --state` cannot combine open+merged, so query all
1026
+ # and drop CLOSED in jq.
1027
+ BASES=$(gh pr list -R {GH_REPO} --state all --search "milestone:\"$MILESTONE_TITLE\"" \
1028
+ --json baseRefName,state --jq '.[] | select(.state != "CLOSED") | .baseRefName' 2>/dev/null | sort -u)
1029
+ # Fallback: if PR search by milestone is unavailable, derive from the issues' linked PRs.
1030
+ if [ -z "$BASES" ]; then
1031
+ BASES=$(for IN in {all_batch_issue_numbers}; do
1032
+ IM=$(gh issue view "$IN" -R {GH_REPO} --json milestone --jq '.milestone.title // empty' 2>/dev/null)
1033
+ [ "$IM" = "$MILESTONE_TITLE" ] || continue
1034
+ gh pr list -R {GH_REPO} --state all --search "$IN in:body" \
1035
+ --json baseRefName,state --jq '.[] | select(.state != "CLOSED") | .baseRefName' 2>/dev/null
1036
+ done | sort -u)
1037
+ fi
1038
+
1039
+ STRAY_BASES=$(echo "$BASES" | grep -v "^${EXPECTED_BASE}\$" | grep -v '^$' || true)
1040
+ if [ -n "$STRAY_BASES" ]; then
1041
+ echo "ALERT: milestone '$MILESTONE_TITLE' has feature-lane PRs split across multiple base branches." >&2
1042
+ echo " Expected base: $EXPECTED_BASE" >&2
1043
+ echo " Found bases:" >&2
1044
+ echo "$BASES" | sed 's/^/ - /' >&2
1045
+ echo " This indicates a branch-routing split — reconcile the stray PRs onto $EXPECTED_BASE" >&2
1046
+ echo " (rebase/cherry-pick the stray branch onto the milestone branch) before the milestone ships." >&2
1047
+ # Do NOT auto-stop or auto-resolve — surface the alert and let the user decide.
1048
+ else
1049
+ echo "Lane-consistency OK: all '$MILESTONE_TITLE' PRs target $EXPECTED_BASE."
1050
+ fi
1051
+ done
1052
+ ```
1053
+
1054
+ Report any `ALERT` lines prominently before advancing to the next wave. Reconciliation of an existing split is a manual/`/milestone`-assisted step — this check only ensures the split is never silent.
1055
+
935
1056
  ### Step 4D: Milestone integration build gate (MANDATORY between waves)
936
1057
 
937
1058
  **WHY THIS EXISTS**: Session Intelligence milestone shipped 116 PRs across multiple waves with zero integration testing. Each PR built in isolation — type errors from cross-PR interactions (wrong prop types, missing components, incompatible interfaces) were invisible until the milestone→staging merge broke the build with 4 distinct errors. This gate catches those failures early.
@@ -3,7 +3,7 @@ description: Recover full pipeline context after compaction — find the last ac
3
3
  argument-hint: [issue number (optional — auto-detects if omitted)]
4
4
  ---
5
5
 
6
- # /resume — Context Recovery After Compaction
6
+ # /pipeline-resume — Context Recovery After Compaction
7
7
 
8
8
  **Input**: $ARGUMENTS
9
9
 
@@ -110,7 +110,7 @@ for NUMBER in $(echo "$RECENT_ISSUES" | jq -r '.number'); do
110
110
  done
111
111
  ```
112
112
 
113
- If still no candidate: display `No recent active issues found. To resume work on a specific issue, run: /resume {issue-number}` and STOP.
113
+ If still no candidate: display `No recent active issues found. To resume work on a specific issue, run: /pipeline-resume {issue-number}` and STOP.
114
114
 
115
115
  ---
116
116
 
@@ -247,7 +247,7 @@ Invoke `/work-on` to re-enter the pipeline. Work-on reads GitHub state independe
247
247
  Skill("work-on", args="{TARGET_NUMBER}")
248
248
  ```
249
249
 
250
- **Why delegation works**: `/work-on` Phase 0B already implements "Determine resume point" — it reads issue labels and FORGE annotations to route to the correct phase. `/resume` provides the human-readable context summary, then hands off to `/work-on` for execution.
250
+ **Why delegation works**: `/work-on` Phase 0B already implements "Determine resume point" — it reads issue labels and FORGE annotations to route to the correct phase. `/pipeline-resume` provides the human-readable context summary, then hands off to `/work-on` for execution.
251
251
 
252
252
  **Do NOT re-implement pipeline logic here.** The context summary above is for the agent's working memory only — the actual phase routing and execution is owned by `/work-on`.
253
253
 
@@ -258,7 +258,7 @@ Skill("work-on", args="{TARGET_NUMBER}")
258
258
  | Condition | Response |
259
259
  |-----------|----------|
260
260
  | `forge.yaml` missing | Stop: "Run \`npx forgedock init\` to generate forge.yaml" |
261
- | No active issues + no FORGE annotations found | Stop: "No recent active issues found. Run \`/resume {number}\` with an explicit issue number, or \`/work-on next\` to start a new issue." |
261
+ | No active issues + no FORGE annotations found | Stop: "No recent active issues found. Run \`/pipeline-resume {number}\` with an explicit issue number, or \`/work-on next\` to start a new issue." |
262
262
  | Issue is closed with `workflow:merged` | Stop: "Issue #{number} is already merged. Nothing to resume." |
263
263
  | Issue is closed with `workflow:invalid` | Stop: "Issue #{number} was closed as invalid. Nothing to resume." |
264
264
  | Issue has `needs-human` label | Stop: "Issue #{number} is blocked (needs-human). Review the blocking comment and resolve it manually before resuming." |
@@ -3,7 +3,7 @@ description: Pipeline-wide situational awareness — groups open issues by workf
3
3
  argument-hint: [--repo <owner/repo> | --stale-days <N>]
4
4
  ---
5
5
 
6
- # /status — Pipeline Status
6
+ # /pipeline-status — Pipeline Status
7
7
 
8
8
  **Input**: $ARGUMENTS (optional: `--repo <owner/repo>`, `--stale-days <N>`)
9
9
 
@@ -16,9 +16,26 @@ You are the QA orchestrator. Auto-discover every page across the platform (dashb
16
16
 
17
17
  ---
18
18
 
19
+ ## Playwright MCP Prerequisite (MANDATORY — check before proceeding)
20
+
21
+ Playwright MCP is a **guaranteed ForgeDock dependency** for all browser automation commands. Before running any qa-sweep phase, verify it is available:
22
+
23
+ **Check**: Confirm `mcp__playwright__*` tools are available in your tool context (e.g., `mcp__playwright__browser_navigate`, `mcp__playwright__browser_snapshot`).
24
+
25
+ **If Playwright MCP tools are NOT available**: STOP immediately. Do not attempt to run qa-sweep — all browser automation steps will fail silently.
26
+
27
+ **Fix**:
28
+ 1. Register Playwright MCP: `claude mcp add playwright npx @playwright/mcp@latest`
29
+ 2. Restart Claude Code to reload MCP servers
30
+ 3. Verify with: `npx forgedock doctor` (Check 9 confirms registration)
31
+
32
+ See README.md Requirements section for full setup instructions.
33
+
34
+ ---
35
+
19
36
  ## Browser Tool Reference
20
37
 
21
- All browser automation uses Playwright MCP tools (`mcp__playwright__*`).
38
+ All browser automation uses Playwright MCP tools (`mcp__playwright__*`). Playwright MCP is a guaranteed ForgeDock dependency — see the Playwright MCP Prerequisite section above.
22
39
 
23
40
  | Action | Tool | Key param |
24
41
  |--------|------|-----------|
@@ -97,7 +114,25 @@ Depth-first, workflow-driven:
97
114
 
98
115
  ## Phase 0: Setup & Auth
99
116
 
100
- **Load config from forge.yaml** before running any checks:
117
+ **Playwright MCP prerequisite guard** — run BEFORE loading forge.yaml config:
118
+
119
+ Confirm `mcp__playwright__browser_navigate` (or any `mcp__playwright__*` tool) is available in your tool context. If Playwright MCP tools are absent:
120
+
121
+ ```
122
+ STOP. Playwright MCP is not registered in this Claude Code session.
123
+
124
+ Register it with: claude mcp add playwright npx @playwright/mcp@latest
125
+ Then restart Claude Code and run /qa-sweep again.
126
+
127
+ Verify installation with: npx forgedock doctor (Check 9 confirms Playwright MCP status)
128
+ See: README.md Requirements section for full setup instructions.
129
+ ```
130
+
131
+ Do not proceed past this guard if Playwright MCP tools are unavailable — all browser automation steps will fail silently without clear error messages.
132
+
133
+ ---
134
+
135
+ **Load config from forge.yaml** after confirming Playwright MCP is available:
101
136
 
102
137
  ```bash
103
138
  QA_CONFIG=$(python3 - <<'PYEOF'