Epic: strengthen the UI review sub-loop inputs and shape
Umbrella follow-on to #97. The designer/vision review loop (#122), UI smoke
harness (#124), and artifact contract (#125) are in place and working, and the
v0.9 /loop-review-ui live-browser flagship (#1114, Stages 0-6) ships on top of
the same shared primitives — captureNamedUiState() and the named-state
artifact contract. This epic addresses a structural gap: the review loop is
screenshot-first, but the strongest and cheapest review signal — rendered
semantics — is not captured or consumed. It also aligns the loop with the
fan-out/converge shape used elsewhere.
Problem
state.json carries provenance only; no semantic/a11y snapshot of the render.
ui-vision-review.md asks the model to judge contrast from pixels — a
computable fact delegated to an unreliable proxy, contradicting the
"an assertion must be able to fail on the defect" doctrine in
skills/docs/ui-e2e-scoping-step.md.
- No console/network error capture per named state. (The live
/loop-review-ui
drive captures response/requestfailed/pageerror + a server-log tail, but
only at the walk level — errors are not attributed to the named state they
occurred in, and the shared fixture-backed named-state path captures nothing.)
designer vs vision is an either/or route, not parallel lenses.
- Named states are single-viewport render states; interaction states
(focus/hover/validation) and mobile/desktop pairing aren't first-class.
- Findings don't reference which acceptance criterion they map to, so
ui_review_satisfied is a gestalt call rather than auditable coverage.
Acceptance criteria
"Strengthened" means each of these is true and independently verifiable:
Definition of done
Non-goals
Unresolved questions
Operator/RFC calls needed before slicing (RFC team: lead dev, specialized dev,
systems architect):
- Per-state capture seam (Stage 3). Does per-state console/network capture
attach to the shared captureNamedUiState() (so the fixture-backed smoke
path gains it too), or only re-attribute the live drive's existing walk-level
capture per state? This decides whether Stage 3 touches
test/playwright/harness/webkit-smoke-harness.mjs or only the drive.
- axe severity policy (Stage 2). Is any axe violation a fail-closed
must-fix, or only serious/critical impact levels? Drives the a11y lens
severity mapping.
- Lens execution model (Stage 5). Sequential passes over one captured
bundle vs truly parallel model calls, and whether converge/dedupe is a new
contract module or an extension of the existing report/designer-review route.
- AC-ref source + coverage bar (Stage 6).
acceptanceCriterionRef draws
from the AC list validateUiDesignerReviewInput already requires — but what
is the coverage bar for ui_review_satisfied: every AC touched by >=1 lens,
or every must-fix AC backed by a passing/covered named state?
- Slug change = migration? (Stage 4). The current path contract normalizes
sliceId + stateName only; encoding viewport/interaction changes
deterministic paths — confirm this is an additive schemaVersion bump with
no back-compat obligation for already-emitted artifacts.
Proposed stages
Six independently-shippable, one-PR stages, dependency-ordered. Stages 1-4 each
enrich the shared named-state artifact and can gate/merge on their own; Stage 5
consumes the enriched bundle; Stage 6 layers traceability on the findings.
Stage 1 — Semantic snapshot artifact
captureNamedUiState() emits a sibling snapshot.json (accessibility tree)
next to screenshot.png + state.json; the artifact contract is extended and
both review modes accept it as input. Depends on: none (foundation).
- Mini-AC:
snapshot.json is emitted for every named state and its path is
deterministic per the contract.
- Mini-AC:
docs/ui-artifact-contract.md documents the artifact; schemaVersion
bumped.
- Mini-AC: fail-closed seam rejects a bundle missing/malformed
snapshot.json
where it is required.
Stage 2 — Computed a11y facts, not eyeballed
Add @axe-core/playwright; emit axe.json per named state; remove contrast (and
other computable a11y facts) from the vision template's visual-judgment list; a11y
findings cite axe evidence. Depends on: Stage 1 (shared artifact contract
already extended).
- Mini-AC:
axe.json is emitted per named state and validated fail-closed.
- Mini-AC:
ui-vision-review.md no longer instructs contrast judgment; the
designer-review-loop doc reflects the axe-evidence rule.
- Mini-AC: axe severity->finding-severity mapping is documented and tested.
Stage 3 — Console + network capture per named state
Record per-state console errors and failed requests into the bundle as a review
input and a fail-closed signal; reconcile with the live drive's existing
walk-level response/requestfailed/pageerror + server-log capture.
Depends on: Stage 1.
- Mini-AC: each named state carries its captured console/network errors.
- Mini-AC: a captured error is a fail-closed signal, never silently dropped.
- Mini-AC: no double-reporting between per-state capture and the drive's
walk-level classifier.
Stage 4 — Interaction states + viewport in the contract
Bake viewport and interaction-state (focus/hover/error) into the deterministic
state slug so responsive and stateful defects are distinct, reviewable states.
Depends on: Stage 1 (path/slug contract).
- Mini-AC: the slug encodes viewport + interaction-state; the path contract doc
is updated.
- Mini-AC: two states differing only by viewport or interaction-state produce
distinct artifact directories.
- Mini-AC: fail-closed seam rejects a malformed/duplicate slug.
Stage 5 — Lens fan-out + converge
Run a11y / layout-geometry / visual / interaction lenses in parallel over one
enriched bundle and converge to a deduped findings set, keeping the existing
outcome enum and fail-closed contract. Depends on: Stages 1-4 (needs the
semantic, axe, console, and viewport/interaction inputs to be worth splitting).
- Mini-AC: the four lenses run against one bundle and converge to a deduped set.
- Mini-AC: the outcome enum
(continue_ui_fix_loop/ui_review_satisfied/blocked_needs_human_decision)
and fail-closed contract are unchanged.
- Mini-AC: dedupe/converge logic is a pure, tested seam.
Stage 6 — Acceptance-criterion traceability
Add acceptanceCriterionRef to findings and require per-criterion coverage
before ui_review_satisfied. Depends on: Stage 5 (converged finding shape).
- Mini-AC: every finding carries
acceptanceCriterionRef.
- Mini-AC:
ui_review_satisfied is blocked until per-criterion coverage is met;
the bar is enforced in the fail-closed seam.
- Mini-AC: coverage is auditable from the emitted result (which AC each finding
maps to, and which ACs are covered).
Epic: strengthen the UI review sub-loop inputs and shape
Umbrella follow-on to #97. The designer/vision review loop (#122), UI smoke
harness (#124), and artifact contract (#125) are in place and working, and the
v0.9
/loop-review-uilive-browser flagship (#1114, Stages 0-6) ships on top ofthe same shared primitives —
captureNamedUiState()and the named-stateartifact contract. This epic addresses a structural gap: the review loop is
screenshot-first, but the strongest and cheapest review signal — rendered
semantics — is not captured or consumed. It also aligns the loop with the
fan-out/converge shape used elsewhere.
Problem
state.jsoncarries provenance only; no semantic/a11y snapshot of the render.ui-vision-review.mdasks the model to judge contrast from pixels — acomputable fact delegated to an unreliable proxy, contradicting the
"an assertion must be able to fail on the defect" doctrine in
skills/docs/ui-e2e-scoping-step.md./loop-review-uidrive captures
response/requestfailed/pageerror+ a server-log tail, butonly at the walk level — errors are not attributed to the named state they
occurred in, and the shared fixture-backed named-state path captures nothing.)
designervsvisionis an either/or route, not parallel lenses.(focus/hover/validation) and mobile/desktop pairing aren't first-class.
ui_review_satisfiedis a gestalt call rather than auditable coverage.Acceptance criteria
"Strengthened" means each of these is true and independently verifiable:
screenshot.png+state.json, a semanticsnapshot.json(accessibility tree) emitted bycaptureNamedUiState(), and both review modes accept it as a first-classinput. Defined in
docs/ui-artifact-contract.md, not redefined per stage.axe.json(from@axe-core/playwright);ui-vision-review.mdno longer lists contrast(or any computable a11y fact) among its visual-judgment items, and a11y
findings cite
axe.jsonevidence instead of eyeballing pixels.surfaced as a review input and a fail-closed signal (a captured error is
not silently dropped).
(focus/hover/error) in its deterministic slug, so responsive and stateful
defects are distinct, reviewable states.
lenses over one bundle and converges to a deduped findings set, preserving
the existing outcome enum and fail-closed contract.
acceptanceCriterionRef, andui_review_satisfiedrequires per-criterion coverage rather than a gestalt call.
scripts/loop/ui-designer-review-contract.mjs) and extends the relevantcontract doc rather than redefining the artifact shape.
Definition of done
docs/ui-artifact-contract.md(the Define screenshot/state artifact contract and CI promotion rules for UI slices #125 contract) documents each newsibling artifact;
schemaVersionis bumped where the artifact shapechanges; the deterministic path/slug contract is updated for the
viewport/interaction extension.
@axe-core/playwrightis added as a dev dependency and runs per namedstate;
docs/ui-designer-review-loop.mdand the vision template reflectthe axe-evidence rule.
not depend on a live browser or live API;
npm run verifyis green perstage.
.claude/**is regenerated from sources (scripts/claude/generate-claude-assets.mjs);no hand-edited generated assets.
(drive-session row tagging / teardown row-drop) is respected — neither is
duplicated or reimplemented here.
Non-goals
(unchanged from Extract a minimal reusable local Playwright/WebKit smoke harness/template for UI work #124).
dev-loop.sibling artifacts and extends the slug, keeping
screenshot.png+state.jsonintact.
Unresolved questions
Operator/RFC calls needed before slicing (RFC team: lead dev, specialized dev,
systems architect):
attach to the shared
captureNamedUiState()(so the fixture-backed smokepath gains it too), or only re-attribute the live drive's existing walk-level
capture per state? This decides whether Stage 3 touches
test/playwright/harness/webkit-smoke-harness.mjsor only the drive.must-fix, or only
serious/criticalimpact levels? Drives the a11y lensseverity mapping.
bundle vs truly parallel model calls, and whether converge/dedupe is a new
contract module or an extension of the existing report/designer-review route.
acceptanceCriterionRefdrawsfrom the AC list
validateUiDesignerReviewInputalready requires — but whatis the coverage bar for
ui_review_satisfied: every AC touched by >=1 lens,or every must-fix AC backed by a passing/covered named state?
sliceId+stateNameonly; encoding viewport/interaction changesdeterministic paths — confirm this is an additive
schemaVersionbump withno back-compat obligation for already-emitted artifacts.
Proposed stages
Six independently-shippable, one-PR stages, dependency-ordered. Stages 1-4 each
enrich the shared named-state artifact and can gate/merge on their own; Stage 5
consumes the enriched bundle; Stage 6 layers traceability on the findings.
Stage 1 — Semantic snapshot artifact
captureNamedUiState()emits a siblingsnapshot.json(accessibility tree)next to
screenshot.png+state.json; the artifact contract is extended andboth review modes accept it as input. Depends on: none (foundation).
snapshot.jsonis emitted for every named state and its path isdeterministic per the contract.
docs/ui-artifact-contract.mddocuments the artifact;schemaVersionbumped.
snapshot.jsonwhere it is required.
Stage 2 — Computed a11y facts, not eyeballed
Add
@axe-core/playwright; emitaxe.jsonper named state; remove contrast (andother computable a11y facts) from the vision template's visual-judgment list; a11y
findings cite axe evidence. Depends on: Stage 1 (shared artifact contract
already extended).
axe.jsonis emitted per named state and validated fail-closed.ui-vision-review.mdno longer instructs contrast judgment; thedesigner-review-loop doc reflects the axe-evidence rule.
Stage 3 — Console + network capture per named state
Record per-state console errors and failed requests into the bundle as a review
input and a fail-closed signal; reconcile with the live drive's existing
walk-level
response/requestfailed/pageerror+ server-log capture.Depends on: Stage 1.
walk-level classifier.
Stage 4 — Interaction states + viewport in the contract
Bake viewport and interaction-state (focus/hover/error) into the deterministic
state slug so responsive and stateful defects are distinct, reviewable states.
Depends on: Stage 1 (path/slug contract).
is updated.
distinct artifact directories.
Stage 5 — Lens fan-out + converge
Run a11y / layout-geometry / visual / interaction lenses in parallel over one
enriched bundle and converge to a deduped findings set, keeping the existing
outcome enum and fail-closed contract. Depends on: Stages 1-4 (needs the
semantic, axe, console, and viewport/interaction inputs to be worth splitting).
(
continue_ui_fix_loop/ui_review_satisfied/blocked_needs_human_decision)and fail-closed contract are unchanged.
Stage 6 — Acceptance-criterion traceability
Add
acceptanceCriterionRefto findings and require per-criterion coveragebefore
ui_review_satisfied. Depends on: Stage 5 (converged finding shape).acceptanceCriterionRef.ui_review_satisfiedis blocked until per-criterion coverage is met;the bar is enforced in the fail-closed seam.
maps to, and which ACs are covered).