@mmerterden/multi-agent-pipeline 16.24.0 → 16.25.1
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/CHANGELOG.md +34 -0
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/design-check/SKILL.md +12 -3
- package/pipeline/multi-agent-refs/features/design-conformance.md +312 -0
- package/pipeline/multi-agent-refs/features/visual-evidence.md +5 -0
- package/pipeline/multi-agent-refs/phases/phase-4-review.md +1 -8
- package/pipeline/schemas/prefs.schema.json +35 -0
- package/pipeline/skills/.skill-manifest.json +2 -2
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,40 @@ Internal file-layout changes that don't affect the slash-command surface are sti
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [16.25.1] - 2026-09-09
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **A contract that named two consumers neither of which read it.** `features/design-conformance.md` shipped declaring Phase 4 review and `features/visual-evidence.md` as consumers; both were silent. That is the declared-but-inert class the prefs gate already hunts elsewhere - the contract looks wired, the surfaces never open it.
|
|
24
|
+
|
|
25
|
+
`visual-evidence.md` now points at it for what a capture is worth checking against: one contract owns getting the picture, the other owns reading it. Phase 4 Step 1.8 cites it in place of a hand-rolled list of eight visual-fidelity items (avatar icon, field grouping, character counter, header style, inline error layout, button height, indicator chip, placeholder copy) that were an ad-hoc subset of what the 30-group catalog now defines properly - so the step got shorter _and_ stricter, and carries the two rules that matter in review: a height or inset is measured rather than read from the token under test, and an unrun check is a fail-to-verify.
|
|
26
|
+
|
|
27
|
+
`smoke-design-conformance.sh` gained the check that would have caught it: every consumer the ref names must cite it back.
|
|
28
|
+
|
|
29
|
+
## [16.25.0] - 2026-09-09
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- **`features/design-conformance.md` - the component walk, ported as doctrine rather than as a tool.** An upstream corporate screen-QA skill landed with a catalog worth having, and the pipeline already owns this problem in `/multi-agent:design-check`. Copying the skill would have produced a second design auditor that drifts from the first, so what came across is the part that is portable: the catalog and the three mechanisms that turn "I looked at it" into a countable result.
|
|
34
|
+
|
|
35
|
+
**Enumerate first.** Build the component inventory once, before any comparison, with elements nested inside buttons, fields and cards exploded into their own rows - an inventory built by kind drops a button silently, and with it the button height and its label. Then fill every cell: a blank one is a fail-to-verify, never a pass.
|
|
36
|
+
|
|
37
|
+
**Measure, never read the token.** A `[MEASURE]` item compares the design against measured pixels or the resolved code value, never against the design tool's node box or the implementation's own layout token. The token is the thing under test; comparing it against itself is how a 48pt field ships against a 56pt design with every check green.
|
|
38
|
+
|
|
39
|
+
**Converge per component.** One pass catches roughly one defect on a component - the wrong font, and not that same label's wrong colour and inset. Each component is re-checked until two consecutive attempts find nothing new, capped at attempt 8 with a `did-not-converge` warning. The screen is done when every component converged, not when the walk reached the bottom.
|
|
40
|
+
|
|
41
|
+
Plus the four scope tags (`[MEASURE]` / `[CAPTURE+]` / `[A11Y]` / `[DYNAMIC]`, and a `[DYNAMIC]` item may never be silently passed), the 30 rule groups, and the bidirectional presence gate - which finds not only what the design has and the implementation lacks, but the neutral decoration the implementation adds that a one-directional colour comparison cannot see.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- **`design-check` no longer halts on a module without mock support.** The mock gate gains a snapshot lane: a screen that can be rendered from its own view model is auditable even when nothing can toggle a variant at runtime. The trade is stated rather than hidden - snapshots reach modules mocks cannot, and cannot flip an in-app variant - and a declined or impossible snapshot still halts rather than fabricating a comparison.
|
|
46
|
+
|
|
47
|
+
- **The report says what it did not check.** The per-variant record gains `walk`: how many components were inventoried, how many converged, which ones hit the cap, and every `[DYNAMIC]` / `[CAPTURE+]` item that was not run, with its reason. An empty not-verified list on a run that never captured a dark or mirrored pass is a false clean, so the skip is listed instead.
|
|
48
|
+
|
|
49
|
+
- **`derivedSkillSources` records what was reviewed and refused, not only what was taken.** New `derivationNote`, `excluded` and `notDerived` fields. `tools/openapi-regen` is marked deliberately-not-derived at 0.45.0 with its reason: the skill's value is being a driver around three named scripts in one repo, so renaming its brand words leaves a skill pointing at scripts nobody else has. Without that record, every drift review rediscovers the same candidate and re-argues it.
|
|
50
|
+
|
|
51
|
+
- **`smoke-design-conformance.sh`** - 26 checks. Half of them guard the doctrine against being trimmed back into a glance; the other half name the project assets that must never arrive: a vendored colour catalog carrying cabin-class names, a typography catalog carrying licensed font families, product screen names, upstream script names. Those leak by content and carry no brand word, so a gate that greps for a company name would pass them.
|
|
52
|
+
|
|
19
53
|
## [16.24.0] - 2026-09-09
|
|
20
54
|
|
|
21
55
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mmerterden/multi-agent-pipeline",
|
|
3
|
-
"version": "16.
|
|
3
|
+
"version": "16.25.1",
|
|
4
4
|
"description": "8-phase AI development pipeline with full orchestration on Claude Code, Copilot CLI and Codex CLI. Analysis, planning, TDD, CLI-aware parallel review with consensus surfacing + Fable triage, default-FAIL evidence gates, secret + intent guards, per-phase cost ledger, persistent learnings memory, wiki generation, commit automation. Token-preserving uninstall.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -50,7 +50,7 @@ Phase 4: Report → coverage gate + assemble + export HTML + PDF (+ Conf
|
|
|
50
50
|
|
|
51
51
|
- **iOS**: Xcode + an available Simulator. **Android**: Android SDK + a running emulator / device.
|
|
52
52
|
- **MCP**: the `multi-agent-toolkit` MCP server (>= 2.8.0, for `design_scenario_inventory`, the `design_report` coverage gate, and `design_visual_compare` region alignment). The Figma MCP (`mcp__claude_ai_Figma__*`) must be authenticated - the user supplies the Figma URL.
|
|
53
|
-
- The selected module must
|
|
53
|
+
- The selected module must be auditable: a **mock mode** (preferred - it can toggle variants) or a **snapshot capture** of the screen's view model. Phase 0 decides which. Neither → the command halts.
|
|
54
54
|
|
|
55
55
|
## Phase Tracker Contract (mandatory)
|
|
56
56
|
|
|
@@ -112,7 +112,7 @@ for l in sys.stdin:
|
|
|
112
112
|
extra_keys: <design-check-config mock.keys, if any> })
|
|
113
113
|
```
|
|
114
114
|
Branch on `supported`:
|
|
115
|
-
- **`false`** → **
|
|
115
|
+
- **`false`** → try the **snapshot lane** before halting: a screen renderable from its own view model (filled, flow-advanced, no backend) is auditable without a runtime mock switch. Ask via `AskUserQuestion`, persist `state.designCheck.captureLane = "snapshot"`. State the trade: snapshots reach modules mocks cannot and cannot flip an in-app variant, so the target set is whatever the view model can be built into. Declined or not renderable → **HALT** with `reason`. Never fabricate a comparison.
|
|
116
116
|
- **`"debug-only"`** → surface an `AskUserQuestion` warning: mocks are compiled into the Debug build via `#if DEBUG` DI, so **variants cannot be toggled at launch** - only the default Debug state is comparable. Options (label + description in `outputLanguage`): option 1 semantically "Continue" - audit the single default Debug state only; option 2 semantically "Cancel" - stop.
|
|
117
117
|
- **`true`** → continue. Persist `state.designCheck.mock = <detect result>` (mechanism, activation, variantsHint, evidence).
|
|
118
118
|
5. **SCENARIO INVENTORY (this is the audit's target set)**:
|
|
@@ -170,6 +170,10 @@ Persist the variant list + spec + PNG paths to `state.designCheck.variants[]`.
|
|
|
170
170
|
|
|
171
171
|
**This phase iterates `state.designCheck.scope.targetIds` - a finite list decided in Phase 0. It ends when every id is resolved, not when the UI stops offering new taps.** Tap-walking alone cannot reach a state that needs a different launch argument, a different scenario case, or a typed scenario code, which is exactly how a module's error / edge states go missing.
|
|
172
172
|
|
|
173
|
+
### 3.0 What every capture is compared against
|
|
174
|
+
|
|
175
|
+
**Read `$HOME/.claude/multi-agent-refs/features/design-conformance.md` before the first compare.** It owns the per-component catalog (30 groups), the scope tags, and the three rules that decide whether this phase produced a result or an impression: enumerate first (a blank cell is a fail-to-verify), measure never the token, and converge per component (two consecutive clean attempts, cap 8). The screen is done when every component converged - not when the walk reached the bottom.
|
|
176
|
+
|
|
173
177
|
### 3.1 Drive the inventory's plan, batch by batch
|
|
174
178
|
|
|
175
179
|
**Follow `inventory.plan`, filtered to the scoped ids.** Each batch is one launch: the batch's relaunch target opens a screen, and every in-app target for that same screen is then flipped while the app is already sitting there. Driving the plan is what makes full coverage affordable - target-by-target relaunching is what made earlier runs give up a third of the way in.
|
|
@@ -260,8 +264,13 @@ Before leaving this phase, compare `covered.length + skipped.length` against `sc
|
|
|
260
264
|
truncatedInventory: <inventory.truncated>,
|
|
261
265
|
floor: <config coverage.floor, optional> },
|
|
262
266
|
variants: [ { name, figmaNodeId, perceptualPct, passed, compareSize, liveSize,
|
|
263
|
-
images, findings, fixPrompt, componentRefs
|
|
267
|
+
images, findings, fixPrompt, componentRefs,
|
|
268
|
+
walk: { components: <inventoried>, converged: <count>,
|
|
269
|
+
didNotConverge: [ { component, attempts } ],
|
|
270
|
+
notVerified: [ { item, tag, reason } ] } } ] }
|
|
264
271
|
```
|
|
272
|
+
|
|
273
|
+
`walk` separates a checked screen from a viewed one. An empty `notVerified` on a run that never captured a dark or mirrored pass is a false clean - list the skip. Field definitions: `features/design-conformance.md`.
|
|
265
274
|
Pass **ids**, not counts: the engine then names each unaccounted target in the report instead of printing an anonymous tally. `compareSize` and `liveSize` come straight from the `design_visual_compare` result.
|
|
266
275
|
3. **Render + export**:
|
|
267
276
|
```
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# Design conformance - the component walk, and why a glance is not a pass
|
|
2
|
+
|
|
3
|
+
A design audit that reads a screen top to bottom and reports what looks wrong
|
|
4
|
+
finds about one defect per element: the wrong font on a label, and not that same
|
|
5
|
+
label's wrong colour and wrong inset. This file is the catalog, and the three
|
|
6
|
+
mechanisms that turn "I looked at it" into a countable result.
|
|
7
|
+
|
|
8
|
+
Derived generically from an upstream corporate screen-QA tool (recorded in
|
|
9
|
+
`derivedSkillSources`). The doctrine is portable. Its vendored design-token
|
|
10
|
+
catalogs and product examples are not, and were not taken - section 7 says what
|
|
11
|
+
replaces them.
|
|
12
|
+
|
|
13
|
+
Consumers: `/multi-agent:design-check` (the runner), Phase 4 review when a UI
|
|
14
|
+
diff is under review, and `features/visual-evidence.md` when a capture has to
|
|
15
|
+
prove a fix. Gate: `smoke-design-conformance.sh`.
|
|
16
|
+
|
|
17
|
+
## 1. Enumerate first, then fill every cell
|
|
18
|
+
|
|
19
|
+
Do **not** walk by finding, and do not walk by headline. Build the inventory
|
|
20
|
+
once, before any comparison:
|
|
21
|
+
|
|
22
|
+
1. Enumerate every visible element of the design frame - every text, icon,
|
|
23
|
+
image, field, **button**, container. An element nested inside a button, field
|
|
24
|
+
or card is **its own row**, marked as nested (`> in <Button>`), never folded
|
|
25
|
+
into its parent.
|
|
26
|
+
2. For each row, pre-fill the design side of every rule that applies and leave
|
|
27
|
+
the implementation side blank.
|
|
28
|
+
3. Fill every blank. **A blank cell is a fail-to-verify, not a pass**, and it is
|
|
29
|
+
reported as one.
|
|
30
|
+
|
|
31
|
+
The inventory is the coverage guarantee; the rule groups in section 5 are the
|
|
32
|
+
per-cell recipe. A truncated inventory makes the denominator wrong, so it is
|
|
33
|
+
reported as incomplete rather than as a clean percentage of a partial set.
|
|
34
|
+
|
|
35
|
+
## 2. Measure, never read the token
|
|
36
|
+
|
|
37
|
+
Every `[MEASURE]` item compares the design value against **measured pixels** in
|
|
38
|
+
the render, or against the implementation's resolved value in code. Never
|
|
39
|
+
against:
|
|
40
|
+
|
|
41
|
+
- the design tool's node box, which includes invisible padding, or
|
|
42
|
+
- the implementation's own layout token (a spacing or size constant, a
|
|
43
|
+
`.frame(height:)`, a Compose `Modifier.height`).
|
|
44
|
+
|
|
45
|
+
The token is the thing under test. Measuring it against itself is how a 48pt
|
|
46
|
+
field ships against a 56pt design with every check green. Button and field
|
|
47
|
+
**height** are the two a token lies about most often, because internal padding
|
|
48
|
+
and safe-area insets are not in it.
|
|
49
|
+
|
|
50
|
+
## 3. Adaptive per-component convergence
|
|
51
|
+
|
|
52
|
+
One pass over a component catches roughly one defect on it. So each component is
|
|
53
|
+
re-checked until **two consecutive attempts find nothing new on it**.
|
|
54
|
+
|
|
55
|
+
- Soft cap: reaching attempt 8 without two consecutive clean attempts stops that
|
|
56
|
+
component with a `did-not-converge` warning. There is never a ninth attempt.
|
|
57
|
+
- A component with a font, a margin and a colour defect needs about three
|
|
58
|
+
finding attempts plus two clean ones. A correct component needs the two clean
|
|
59
|
+
attempts and nothing more.
|
|
60
|
+
- The screen is done when **every** inventoried component has reached two
|
|
61
|
+
consecutive clean attempts or its cap with a warning. Not when the walk
|
|
62
|
+
reached the bottom.
|
|
63
|
+
|
|
64
|
+
On a fixing run, each attempt that found something is fixed, re-captured, and
|
|
65
|
+
its changes appended to one accumulating record, so the final artefact shows
|
|
66
|
+
what each attempt changed rather than one undifferentiated diff.
|
|
67
|
+
|
|
68
|
+
## 4. Scope tags - how an item is verified
|
|
69
|
+
|
|
70
|
+
| Tag | Meaning |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `[MEASURE]` | Verified in the primary capture: design value against measured pixels or resolved code value (section 2) |
|
|
73
|
+
| `[CAPTURE+]` | Needs an extra capture pass - a second appearance, a mirrored layout, another width, another state. Skipping one is allowed only if the skip is logged |
|
|
74
|
+
| `[A11Y]` | Accessibility lane: the platform accessibility tree (labels, element frames) plus contrast math |
|
|
75
|
+
| `[DYNAMIC]` | Behavioural or temporal; a static capture cannot show it. Verify by driving the device, or report `not verified (dynamic)`. **Never silently pass a `[DYNAMIC]` item** |
|
|
76
|
+
|
|
77
|
+
## 5. The catalog
|
|
78
|
+
|
|
79
|
+
Run every applicable item for **every** component, then every gap through
|
|
80
|
+
group 2, then the whole-screen passes (17, 21, 22, 24, 25). A check that was not
|
|
81
|
+
run is a fail-to-verify.
|
|
82
|
+
|
|
83
|
+
**1. Layout** `[MEASURE]` - x relative to container (never absolute y: the status
|
|
84
|
+
bar offsets it), y relative to an in-content span, L/R/T/B margin to parent,
|
|
85
|
+
centring, baseline alignment across a row, shared leading edge for siblings,
|
|
86
|
+
label-left/value-right rows aligned at both ends, safe-area and system-bar
|
|
87
|
+
clearance.
|
|
88
|
+
|
|
89
|
+
> **Horizontal-inset lane** - the most-skipped one. For every element record
|
|
90
|
+
> **both** sides of the leading inset: the design value and the measured
|
|
91
|
+
> implementation value. One side filled is a fail-to-verify. Then two cheap
|
|
92
|
+
> catches that need no per-element math: (a) **leading rail** - every loose,
|
|
93
|
+
> non-carded text in a vertical group must share one leading x; if the
|
|
94
|
+
> implementation puts a subtitle at the card's outer edge while the design insets
|
|
95
|
+
> it, the rail is broken; (b) **sibling consistency** - when sibling screens
|
|
96
|
+
> exist, diff the same element's inset across them; the outlier is usually the
|
|
97
|
+
> bug. A different text-wrap point is a symptom of a different margin, not only
|
|
98
|
+
> of a different font.
|
|
99
|
+
|
|
100
|
+
**2. Spacing** `[MEASURE]` - outer margins, inner padding, screen edge margin
|
|
101
|
+
(L=R symmetric), text-to-icon and icon-to-button and card-to-card and
|
|
102
|
+
section-to-section gaps, list/grid/row spacing, the vertical gap **between**
|
|
103
|
+
adjacent components (`next.y - (prev.y + prev.h)` against the stack spacing), and
|
|
104
|
+
every gap resolving to a spacing token - an off-scale value is a deviation.
|
|
105
|
+
|
|
106
|
+
**3. Size** `[MEASURE]` - width and height against the design (measured), min and
|
|
107
|
+
max honoured, `[CAPTURE+]` responsive widths (group 17), `[A11Y]` interactive tap
|
|
108
|
+
target at least 44x44pt on iOS / 48x48dp on Android, read from the accessibility
|
|
109
|
+
tree rather than assumed.
|
|
110
|
+
|
|
111
|
+
**4. Typography** `[MEASURE]` - family, size, weight, style, letter spacing, line
|
|
112
|
+
height, paragraph spacing, alignment, text transform, text decoration, foreground
|
|
113
|
+
colour, and truncation: ellipsis position, wrap and line limit matching the
|
|
114
|
+
design.
|
|
115
|
+
|
|
116
|
+
**5. Colours** `[MEASURE]` - background colour and opacity, gradient direction and
|
|
117
|
+
stops, blur effects, foreground and secondary text, disabled and placeholder,
|
|
118
|
+
border and divider and icon tint, brand accent and the status set
|
|
119
|
+
(success/error/warning/info), field prefix or affix colour, and no hardcoded
|
|
120
|
+
value: every colour resolves to a design token.
|
|
121
|
+
|
|
122
|
+
**6. Border** `[MEASURE]` - width, corner radius (scalar and per-corner), style
|
|
123
|
+
(solid vs dashed) and stroke alignment, colour and opacity.
|
|
124
|
+
|
|
125
|
+
**7. Shadow / elevation** `[MEASURE]` - present or not, colour and opacity, blur
|
|
126
|
+
radius, spread, x/y offset, and parity between the iOS shadow and the Android
|
|
127
|
+
elevation for the same surface.
|
|
128
|
+
|
|
129
|
+
**8. Shape** `[MEASURE]` - the drawn shape matches: radius about half the height
|
|
130
|
+
means a pill; radius about half the smaller side with a square box means a
|
|
131
|
+
circle.
|
|
132
|
+
|
|
133
|
+
**9. Icons** `[MEASURE]` - correct icon and variant, **size measured from the
|
|
134
|
+
rendered bounding box in both images**, never the node box or a size token,
|
|
135
|
+
weight (line vs filled), colour and tint, padding and alignment and icon-to-text
|
|
136
|
+
spacing, rotation and mirroring, rendering mode (a multicolour icon rendered as a
|
|
137
|
+
single-tint template is a defect), and vector crispness.
|
|
138
|
+
|
|
139
|
+
**10. Images** `[MEASURE]` - width and height, aspect ratio preserved, crop mode
|
|
140
|
+
(fill vs fit), no distortion, no unintended blur and sufficient resolution,
|
|
141
|
+
`[CAPTURE+]` placeholder and loading state.
|
|
142
|
+
|
|
143
|
+
**11. Buttons** `[MEASURE]` - width and **measured height** (from the rendered
|
|
144
|
+
fill rect, never a frame token), L/R/T/B spacing with L=R symmetry, the inner
|
|
145
|
+
label's font/weight/colour/alignment **as its own row**, the inner icon
|
|
146
|
+
(presence, size, colour, side, spacing), centring, fill and border and text
|
|
147
|
+
colour for the current state, `[CAPTURE+]` the state set (group 21).
|
|
148
|
+
|
|
149
|
+
> A button instance matches no text, icon or field kind, so an inventory built by
|
|
150
|
+
> kind drops it silently - and with it the button height and its nested label.
|
|
151
|
+
> The button is its own row and its children are exploded into their own rows.
|
|
152
|
+
|
|
153
|
+
**12. Input fields** `[MEASURE]` - **measured height** (the rendered field box,
|
|
154
|
+
never a frame or size token, because internal padding is not in the token),
|
|
155
|
+
width, border, radius, placeholder text and its colour and alignment, background
|
|
156
|
+
shade (read-only vs editable), prefix or affix and its colour, cursor and
|
|
157
|
+
selection colour, character counter behaviour, `[CAPTURE+]` focus/error/disabled/
|
|
158
|
+
filled states with the error message's colour, position and icon, `[DYNAMIC]`
|
|
159
|
+
keyboard type.
|
|
160
|
+
|
|
161
|
+
**13. Card** `[MEASURE]` - padding, radius, background, border, shadow, and
|
|
162
|
+
content grouping: **sample the pixel behind every text block** - is it inside a
|
|
163
|
+
card or bare on the page background? A design block in a card that the
|
|
164
|
+
implementation renders bare (or the reverse) is a wrong-container finding
|
|
165
|
+
(group 27) and is invisible to the text checks alone. Diff the design's container
|
|
166
|
+
list one-to-one against the implementation's card wrappers; the implementation
|
|
167
|
+
side is code-verifiable (inside a card helper vs placed bare in a stack with
|
|
168
|
+
padding). Never infer this from a glance.
|
|
169
|
+
|
|
170
|
+
**14. Lists** `[MEASURE]` - item height and internal padding, divider colour and
|
|
171
|
+
thickness and insets, inter-item spacing, section header grouping and order,
|
|
172
|
+
`[DYNAMIC]` scroll behaviour and lazy loading.
|
|
173
|
+
|
|
174
|
+
**15. Navigation / header** `[MEASURE]` - bar height, title text and alignment,
|
|
175
|
+
back affordance present and correct and mirrored under RTL, right-side actions
|
|
176
|
+
present and ordered, bottom navigation items with icons, labels and selected
|
|
177
|
+
state, `[DYNAMIC]` transition style.
|
|
178
|
+
|
|
179
|
+
**16. Scroll** - `[DYNAMIC]` smooth scroll, bounce, overscroll, sticky header,
|
|
180
|
+
scroll indicator; `[MEASURE]` the pinned header or footer position, which is the
|
|
181
|
+
static part a capture can check.
|
|
182
|
+
|
|
183
|
+
**17. Responsive** `[CAPTURE+]` - small, medium and large phone widths, tablet,
|
|
184
|
+
landscape. No overflow, clipping or distortion at any of them. Re-capture per
|
|
185
|
+
width rather than reasoning about it.
|
|
186
|
+
|
|
187
|
+
**18. Alignment** `[MEASURE]` - horizontal, vertical, and baseline across a row.
|
|
188
|
+
|
|
189
|
+
**19. Animation and motion** `[DYNAMIC]` - duration, curve, delay,
|
|
190
|
+
fade/scale/slide/rotation, loading and skeleton animation, haptics. Verify by
|
|
191
|
+
recording the device or report `not verified (dynamic)`.
|
|
192
|
+
|
|
193
|
+
**20. Visibility** `[CAPTURE+]` - hidden vs visible renders correctly; collapse
|
|
194
|
+
and expand states captured separately.
|
|
195
|
+
|
|
196
|
+
**21. States** `[CAPTURE+]` per interactive component - default, pressed, focus,
|
|
197
|
+
selected, active, disabled (colour, opacity, and genuinely not tappable),
|
|
198
|
+
loading, empty, error, success.
|
|
199
|
+
|
|
200
|
+
> **State fidelity gate**: the captured implementation must be in the exact state
|
|
201
|
+
> the design frame depicts - the same toggle position, the same filled or empty
|
|
202
|
+
> content, the same validation result. A capture in the wrong state is redone,
|
|
203
|
+
> not compared.
|
|
204
|
+
|
|
205
|
+
**22. Accessibility** `[A11Y]` - contrast at least 4.5:1 computed from sampled
|
|
206
|
+
colours, tap target at least 44x44pt / 48x48dp from the accessibility tree,
|
|
207
|
+
screen-reader label present and meaningful plus a meaningful hint, correct
|
|
208
|
+
traits, an accessibility identifier on every interactive element, and a logical
|
|
209
|
+
focus order. Whether to also audit scaled text sizes depends on the project: a
|
|
210
|
+
design system with fixed, non-scaling typography makes a forced-scale render
|
|
211
|
+
meaningless noise, so state which of the two the project is instead of assuming.
|
|
212
|
+
|
|
213
|
+
**23. Platform** - iOS: safe area, notch and Dynamic Island clearance, home
|
|
214
|
+
indicator, native navigation behaviour. Android: Material compliance, status and
|
|
215
|
+
navigation bar treatment, ripple.
|
|
216
|
+
|
|
217
|
+
**24. Dark appearance** `[CAPTURE+]` - capture a second image in the dark
|
|
218
|
+
appearance and re-run groups 3 to 9 against the dark design frame. Background,
|
|
219
|
+
text, icon, border, shadow, divider and gradient all adapt; no colour stuck at
|
|
220
|
+
its light value; contrast preserved.
|
|
221
|
+
|
|
222
|
+
**25. RTL and localization** - `[CAPTURE+]` a mirrored-locale capture: layout
|
|
223
|
+
mirrored, leading and trailing swapped, directional icons flipped; `[MEASURE]` no
|
|
224
|
+
hardcoded strings, every user-visible string from a localization key; a raw or
|
|
225
|
+
undefined key rendered on screen (the design shows copy, the implementation shows
|
|
226
|
+
`Screen.SomeKey`) reported in its own localization section; `[CAPTURE+]` a
|
|
227
|
+
long-language pass with no clipping. Compare the string's *presence and shape*,
|
|
228
|
+
not a translation of the same phrase against itself.
|
|
229
|
+
|
|
230
|
+
**26. Content and data formatting** `[MEASURE]` - number grouping, currency symbol
|
|
231
|
+
and position, date format, pluralization, and a long dynamic value that does not
|
|
232
|
+
overflow. The value itself (a name, a number, a masked field) is not under test.
|
|
233
|
+
|
|
234
|
+
**27. Component presence and order - hard gate** - the group the whole walk
|
|
235
|
+
exists for.
|
|
236
|
+
|
|
237
|
+
> Mechanical, not a glance. Number every element of the design list - text **and**
|
|
238
|
+
> every icon, avatar, badge, **and every button** - and for each write its
|
|
239
|
+
> implementation counterpart or `MISSING`. Report the count explicitly: "N design
|
|
240
|
+
> components -> N implementation counterparts". "Complete" may not be concluded
|
|
241
|
+
> without that list. A button and the label nested inside it are two presence
|
|
242
|
+
> checks, not one.
|
|
243
|
+
>
|
|
244
|
+
> - **Icons are the most-missed presence class.** For every icon in the design
|
|
245
|
+
> list, locate it in the render by measuring its coloured bounding box. A design
|
|
246
|
+
> icon with roughly no matching pixels is a missing-element finding.
|
|
247
|
+
> - **Recurse into nested blocks.** The gate applies to rows inside cards - masked
|
|
248
|
+
> rows, list rows, key/value rows - not only to top-level sections. "That card
|
|
249
|
+
> looks right" is not a pass.
|
|
250
|
+
> - **Bidirectional, and code-side too.** Diff design-to-implementation
|
|
251
|
+
> (`MISSING`) *and* implementation-to-design (`EXTRA`). The most-missed extra is
|
|
252
|
+
> a neutral decoration the implementation adds - a trailing chevron, an
|
|
253
|
+
> underline, a strikethrough - which a colour-based, one-directional comparison
|
|
254
|
+
> cannot see. Diff the implementation's explicit decoration modifiers against
|
|
255
|
+
> the design's own decoration nodes, independently of the string.
|
|
256
|
+
> - **A localization gap does not short-circuit this gate.** Raw keys are one
|
|
257
|
+
> finding; presence, icon, decoration and structure checks still run, because
|
|
258
|
+
> they read code and spec rather than the rendered string.
|
|
259
|
+
|
|
260
|
+
Findings: `MISSING` · `EXTRA` · `MISSING-BETWEEN` (dropped from the middle of the
|
|
261
|
+
sequence) · `REORDERED` · `WRONG-STATE` · `WRONG-CONTAINER` (present but grouped
|
|
262
|
+
differently, loose vs carded).
|
|
263
|
+
|
|
264
|
+
**28. Keyboard and input behaviour** `[DYNAMIC]` - keyboard avoidance with the
|
|
265
|
+
active field not covered, dismiss behaviour, and return/next moving to the
|
|
266
|
+
correct field.
|
|
267
|
+
|
|
268
|
+
**29. Analytics** - out of visual scope; checked only when the spec requires it.
|
|
269
|
+
|
|
270
|
+
**30. Cross-check matrix** - per component confirm x, y, w, h, the four margins,
|
|
271
|
+
the four paddings, font family/size/weight, line height, letter spacing, text
|
|
272
|
+
colour, background colour, border width/radius/colour, shadow, opacity, icon
|
|
273
|
+
size/colour/position, image size/crop, divider, alignment, responsive, state,
|
|
274
|
+
animation, safe area, and overflow. **Mark only deviations** on the image;
|
|
275
|
+
matches belong in the report text.
|
|
276
|
+
|
|
277
|
+
## 6. Output
|
|
278
|
+
|
|
279
|
+
Mark only problems on the image, with thin lines. Everything else goes in the
|
|
280
|
+
report, whose section keys are stable and whose prose follows `outputLanguage`:
|
|
281
|
+
|
|
282
|
+
| Key | Content |
|
|
283
|
+
|---|---|
|
|
284
|
+
| `deviations` | Numbered, each one a design value against the measured or coded implementation value |
|
|
285
|
+
| `localization` | Raw keys, missing keys, hardcoded strings |
|
|
286
|
+
| `matches` | What was checked and agreed |
|
|
287
|
+
| `ignored` | What was deliberately out of scope, with the reason |
|
|
288
|
+
| `not_verified` | **Every** `[DYNAMIC]` and `[CAPTURE+]` item that was not run, so the coverage gap is explicit rather than absent |
|
|
289
|
+
|
|
290
|
+
Each finding is `[what] + [why] + [how]` with `file:line` wherever the
|
|
291
|
+
implementation side is code-resolvable. A finding without a file reference is
|
|
292
|
+
still a finding; a finding without a "how" is a complaint.
|
|
293
|
+
|
|
294
|
+
## 7. What a token catalog is, and why none ships here
|
|
295
|
+
|
|
296
|
+
Comparing typography and colour exactly needs the project's own catalogs - the
|
|
297
|
+
resolved name, size, weight and line height of each typography style, and the
|
|
298
|
+
resolved value of each colour token. Those are **project assets**, not pipeline
|
|
299
|
+
content: they carry brand colours, licensed font names and product vocabulary.
|
|
300
|
+
|
|
301
|
+
So this contract defines the *interface*, never the data. The runner resolves
|
|
302
|
+
catalogs in this order and states which one it used:
|
|
303
|
+
|
|
304
|
+
1. A path in preferences (`designCheck.tokenCatalogs`), when the project exports
|
|
305
|
+
them.
|
|
306
|
+
2. Generated from the repo's own token definitions at run time.
|
|
307
|
+
3. Neither available -> typography and colour items degrade from `[MEASURE]` to
|
|
308
|
+
sampled-pixel comparison, and the report says so. They are not silently
|
|
309
|
+
passed.
|
|
310
|
+
|
|
311
|
+
A vendored catalog inside the pipeline would be one project's design system
|
|
312
|
+
shipped to every other project: wrong for all of them, and stale for its owner.
|
|
@@ -64,6 +64,11 @@ bash $HOME/.claude/scripts/capture-evidence.sh after \
|
|
|
64
64
|
--task "$TASK_ID" --platform "$PLATFORM" --label "<screen-slug>"
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
What the capture is worth checking against - the per-component catalog, the
|
|
68
|
+
measure-never-the-token rule, the presence gate - is
|
|
69
|
+
`$HOME/.claude/multi-agent-refs/features/design-conformance.md`. This contract
|
|
70
|
+
owns getting the picture; that one owns reading it.
|
|
71
|
+
|
|
67
72
|
The script boots the device if needed, launches the built app, cleans the status
|
|
68
73
|
bar (`ios_status_bar preset:clean` / the Android equivalent) so the frame carries
|
|
69
74
|
no clock, carrier or battery noise, captures at device resolution, downscales to
|
|
@@ -262,14 +262,7 @@ Visual-fidelity mismatches against the captured screenshot are BLOCKING findings
|
|
|
262
262
|
|
|
263
263
|
- Canonical component usage: the Code Connect-mapped component is used verbatim - a sound-alike substitute, a forked copy, or ad-hoc inline UI where a mapping exists is blocking (Phase 3 "Design fidelity contract")
|
|
264
264
|
- Inter-component spacing: gaps, paddings, and alignment BETWEEN components match the design's measured values mapped to spacing tokens - invented numeric values are blocking
|
|
265
|
-
-
|
|
266
|
-
- Field grouping (one rounded box vs two; separator vs gap)
|
|
267
|
-
- Character counter visibility
|
|
268
|
-
- Header style (size, weight, alignment)
|
|
269
|
-
- Inline error layout (icon presence, text colour, position relative to the input)
|
|
270
|
-
- Button height
|
|
271
|
-
- Indicator chip placement
|
|
272
|
-
- Placeholder copy and position
|
|
265
|
+
- Everything else: the 30-group catalog in `$HOME/.claude/multi-agent-refs/features/design-conformance.md`. Two of its rules carry into review: a height or inset is **measured**, never read from the token under test; an unrun check is a fail-to-verify, not a pass.
|
|
273
266
|
|
|
274
267
|
When `state.figmaAccess.tier === 3` (user-attached screenshot, no Code Connect snippet), the reviewer additionally sets `findings[i].severity = "blocking"` and `findings[i].tag = "review_blocking_tier3"` on every UI atom that lacks a confirmed canonical-component mapping. The triage step preserves these findings unless the user has explicitly cleared the open question.
|
|
275
268
|
|
|
@@ -1624,6 +1624,41 @@
|
|
|
1624
1624
|
"type": "string",
|
|
1625
1625
|
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
1626
1626
|
"description": "Upstream version whose drift is consciously accepted (port pending). The drift gate reports it but does not fail while upstream stays at this version; it re-fails the moment upstream moves past it."
|
|
1627
|
+
},
|
|
1628
|
+
"derivationNote": {
|
|
1629
|
+
"type": "string",
|
|
1630
|
+
"description": "What was taken and what deliberately was not. A derivation is rarely a copy: when the mechanism is portable but its runner is not, this is where that judgement lives, so the next drift review re-reads the decision instead of re-making it."
|
|
1631
|
+
},
|
|
1632
|
+
"excluded": {
|
|
1633
|
+
"type": "array",
|
|
1634
|
+
"items": {
|
|
1635
|
+
"type": "string"
|
|
1636
|
+
},
|
|
1637
|
+
"description": "Upstream paths deliberately left behind, each with its reason. Usually project assets rather than code - vendored design tokens, licensed font names, product screen names - which leak by content rather than by carrying a brand word, so a structural gate cannot see them."
|
|
1638
|
+
},
|
|
1639
|
+
"notDerived": {
|
|
1640
|
+
"type": "array",
|
|
1641
|
+
"description": "Upstream skills reviewed and knowingly not derived. Without this, every drift review rediscovers the same candidate and re-argues it.",
|
|
1642
|
+
"items": {
|
|
1643
|
+
"type": "object",
|
|
1644
|
+
"additionalProperties": false,
|
|
1645
|
+
"required": ["skill", "decision", "reason"],
|
|
1646
|
+
"properties": {
|
|
1647
|
+
"skill": {
|
|
1648
|
+
"type": "string"
|
|
1649
|
+
},
|
|
1650
|
+
"reviewedAtVersion": {
|
|
1651
|
+
"type": "string"
|
|
1652
|
+
},
|
|
1653
|
+
"decision": {
|
|
1654
|
+
"type": "string",
|
|
1655
|
+
"enum": ["deliberately-not-derived", "deferred"]
|
|
1656
|
+
},
|
|
1657
|
+
"reason": {
|
|
1658
|
+
"type": "string"
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1627
1662
|
}
|
|
1628
1663
|
}
|
|
1629
1664
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-09T18:15:55Z",
|
|
4
4
|
"skillCount": 207,
|
|
5
5
|
"entries": [
|
|
6
6
|
{
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"path": "shared/core/multi-agent-design-check/SKILL.md",
|
|
44
|
-
"sha256": "
|
|
44
|
+
"sha256": "28efc8b47d3d3fc052b91c7083870d2e7a3d604f65c5d476b9a21ba382e051e0"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"path": "shared/core/multi-agent-diff-explain/SKILL.md",
|
|
@@ -46,7 +46,9 @@ Phase 4: Report → coverage gate + assemble + export HTML + PDF (+ Conf
|
|
|
46
46
|
|
|
47
47
|
- **iOS**: Xcode + Simulator. **Android**: Android SDK + emulator / device.
|
|
48
48
|
- **MCP**: `multi-agent-toolkit` (>= 2.8.0, for `design_scenario_inventory`, the `design_report` coverage gate, and `design_visual_compare` region alignment) + an authenticated Figma MCP (`mcp__claude_ai_Figma__*`).
|
|
49
|
-
- The
|
|
49
|
+
- The module must be auditable: a **mock mode** (preferred - it can toggle variants) or a **snapshot capture** of the screen's view model. Neither → halt.
|
|
50
|
+
|
|
51
|
+
**What each capture is compared against** - the per-component catalog, the measure-never-the-token rule, and the two-consecutive-clean convergence rule that decides when a component is done - is `$HOME/.claude/multi-agent-refs/features/design-conformance.md`. Read it before the first compare. A check that was not run is a fail-to-verify, and every skipped `[DYNAMIC]` / `[CAPTURE+]` item is listed in the report rather than omitted.
|
|
50
52
|
|
|
51
53
|
## Phase Tracker (mandatory)
|
|
52
54
|
|