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.
- package/README.md +34 -1
- package/bin/cross-publish.mjs +344 -0
- package/bin/forgedock.mjs +229 -17
- package/commands/autopilot.md +77 -0
- package/commands/design-bench.md +176 -0
- package/commands/design-render-critique-loop.md +103 -0
- package/commands/design.md +259 -0
- package/commands/issue.md +7 -3
- package/commands/orchestrate.md +121 -0
- package/commands/{resume.md → pipeline-resume.md} +4 -4
- package/commands/{status.md → pipeline-status.md} +1 -1
- package/commands/qa-sweep.md +37 -2
- package/commands/quality-gate.md +57 -0
- package/commands/review-pr-staging.md +158 -2
- package/commands/test-gate.md +929 -0
- package/commands/work-on/build/implement.md +5 -3
- package/commands/work-on.md +53 -3
- package/docs/CI.md +177 -0
- package/docs/FORGE-PROTOCOL.md +324 -0
- package/docs/articles/b-vs-c-baseline.md +210 -0
- package/docs/articles/full-abc-benchmark.md +205 -0
- package/docs/articles/selective-spec-loading-token-savings.md +140 -0
- package/docs/design/abc-benchmark.md +195 -0
- package/docs/design/design-architect-rationale.md +123 -0
- package/docs/design/design-memory.md +61 -0
- package/docs/design/design-spec-schema.md +248 -0
- package/docs/design/design-system-lint.md +150 -0
- package/docs/design/divergent-generation.md +106 -0
- package/docs/design/effects-appropriateness.md +133 -0
- package/docs/design/fixtures/briefs/README.md +47 -0
- package/docs/design/fixtures/briefs/cadence.md +39 -0
- package/docs/design/fixtures/briefs/plume.md +39 -0
- package/docs/design/fixtures/briefs/slipstream.md +39 -0
- package/docs/design/fixtures/briefs/tender.md +39 -0
- package/docs/design/fixtures/briefs/voltage.md +39 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/runs.json +175 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/scorecard.json +138 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-1.html +562 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-2.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-3.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-1.html +527 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-2.html +445 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-3.html +483 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/full-abc/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-1.html +496 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-2.html +447 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-3.html +564 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-1.html +622 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-2.html +475 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-3.html +660 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-1.html +525 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-3.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-1.html +1068 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-2.html +1423 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-3.html +1245 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-1.html +305 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-2.html +327 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-3.html +428 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-1.html +1270 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-2.html +1172 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-3.html +1532 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-1.html +315 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-2.html +342 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-3.html +353 -0
- package/docs/design/fixtures/runs/full-abc-opus/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc-opus/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-1.html +345 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-3.html +502 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-1.html +408 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-3.html +496 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-1.html +813 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-2.html +711 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-3.html +721 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-1.html +280 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-2.html +291 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-3.html +366 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-1.html +1010 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-2.html +836 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-3.html +930 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-1.html +407 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-3.html +529 -0
- package/docs/design/foundations/brutalist.css +294 -0
- package/docs/design/foundations/editorial.css +300 -0
- package/docs/design/foundations/minimal-luxury.css +295 -0
- package/docs/design/foundations/technical.css +293 -0
- package/docs/design/foundations/warm-photo.css +299 -0
- package/docs/design/reference-corpus.md +1726 -0
- package/docs/design/render-critique-loop.md +57 -0
- package/docs/spec-graph-schema.md +397 -0
- package/package.json +1 -1
- package/scripts/bench-scorecard.mjs +239 -0
- package/scripts/build-spec-graph.mjs +509 -0
- package/scripts/check-native-conflicts.sh +200 -0
- package/scripts/classify-lane.sh +0 -7
- package/scripts/design-system-lint.mjs +993 -0
- package/scripts/graph-query.sh +486 -0
- package/scripts/transition-label.sh +10 -2
- package/scripts/validate-spec-graph.sh +423 -0
- 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
|
-
|
|
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
|
|
package/commands/orchestrate.md
CHANGED
|
@@ -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
|
|
package/commands/qa-sweep.md
CHANGED
|
@@ -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
|
-
**
|
|
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'
|