lazycodex-ai 5.1.15 → 5.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/cli/index.js +15 -15
  2. package/dist/cli-node/index.js +15 -15
  3. package/package.json +1 -1
  4. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  5. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  6. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  7. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  8. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  9. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  10. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  12. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  14. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  15. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  16. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  17. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  19. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  21. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  22. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  23. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  25. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  27. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  28. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  29. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  30. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  31. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  32. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  33. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  34. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  35. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  36. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  37. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  38. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  39. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  49. package/packages/omo-codex/plugin/package-lock.json +12 -12
  50. package/packages/omo-codex/plugin/package.json +1 -1
  51. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  52. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
  53. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +3 -7
  54. package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +1 -1
  55. package/packages/omo-codex/plugin/skills/frontend/references/design/clone-from-url.md +1 -1
  56. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +1 -1
  57. package/packages/omo-codex/plugin/skills/frontend/references/design/layout-skill.md +1 -1
  58. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +124 -22
  59. package/packages/omo-codex/plugin/skills/visual-qa/references/browser-setup.md +39 -14
  60. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
  61. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  62. package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
  63. package/packages/shared-skills/skills/frontend/references/design/README.md +3 -7
  64. package/packages/shared-skills/skills/frontend/references/design/aside.md +1 -1
  65. package/packages/shared-skills/skills/frontend/references/design/clone-from-url.md +1 -1
  66. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +1 -1
  67. package/packages/shared-skills/skills/frontend/references/design/layout-skill.md +1 -1
  68. package/packages/shared-skills/skills/visual-qa/SKILL.md +124 -22
  69. package/packages/shared-skills/skills/visual-qa/references/browser-setup.md +39 -14
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/lazycodex-executor-verify/dist/cli.js\" hook subagent-stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.1.15) Verifying LazyCodex Executor Evidence",
10
+ "statusMessage": "(OmO 5.1.16) Verifying LazyCodex Executor Evidence",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\lazycodex-executor-verify\\dist\\cli.js\" hook subagent-stop"
12
12
  }
13
13
  ],
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ultrawork/dist/cli.js\" hook user-prompt-submit",
9
9
  "timeout": 5,
10
- "statusMessage": "(OmO 5.1.15) Checking Ultrawork Trigger",
10
+ "statusMessage": "(OmO 5.1.16) Checking Ultrawork Trigger",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ultrawork\\dist\\cli.js\" hook user-prompt-submit"
12
12
  }
13
13
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/ulw-loop/dist/cli.js\" hook user-prompt-submit",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.1.15) Checking Ulw-Loop Steering",
10
+ "statusMessage": "(OmO 5.1.16) Checking Ulw-Loop Steering",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\ulw-loop\\dist\\cli.js\" hook user-prompt-submit"
12
12
  }
13
13
  ]
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/rules/dist/cli.js\" hook user-prompt-submit",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 5.1.15) Loading Project Rules",
10
+ "statusMessage": "(OmO 5.1.16) Loading Project Rules",
11
11
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\node-dispatch.ps1\" \"${PLUGIN_ROOT}\\components\\rules\\dist\\cli.js\" hook user-prompt-submit"
12
12
  }
13
13
  ]
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/omo-codex-plugin",
3
- "version": "5.1.15",
3
+ "version": "5.1.16",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sisyphuslabs/omo-codex-plugin",
9
- "version": "5.1.15",
9
+ "version": "5.1.16",
10
10
  "workspaces": [
11
11
  "components/comment-checker",
12
12
  "components/git-bash",
@@ -95,7 +95,7 @@
95
95
  },
96
96
  "components/comment-checker": {
97
97
  "name": "@code-yeongyu/codex-comment-checker",
98
- "version": "5.1.15",
98
+ "version": "5.1.16",
99
99
  "license": "MIT",
100
100
  "bin": {
101
101
  "omo-comment-checker": "dist/cli.js"
@@ -117,7 +117,7 @@
117
117
  },
118
118
  "components/git-bash": {
119
119
  "name": "@sisyphuslabs/codex-git-bash-hook",
120
- "version": "5.1.15",
120
+ "version": "5.1.16",
121
121
  "bin": {
122
122
  "omo-git-bash-hook": "dist/cli.js"
123
123
  },
@@ -131,7 +131,7 @@
131
131
  },
132
132
  "components/lazycodex-executor-verify": {
133
133
  "name": "@code-yeongyu/codex-lazycodex-executor-verify",
134
- "version": "5.1.15",
134
+ "version": "5.1.16",
135
135
  "license": "MIT",
136
136
  "bin": {
137
137
  "lazycodex-executor-verify": "dist/cli.js"
@@ -149,7 +149,7 @@
149
149
  },
150
150
  "components/lsp": {
151
151
  "name": "@code-yeongyu/codex-lsp",
152
- "version": "5.1.15",
152
+ "version": "5.1.16",
153
153
  "license": "MIT",
154
154
  "dependencies": {
155
155
  "@code-yeongyu/lsp-daemon": "file:../../../../lsp-daemon",
@@ -171,7 +171,7 @@
171
171
  },
172
172
  "components/rules": {
173
173
  "name": "@code-yeongyu/codex-rules",
174
- "version": "5.1.15",
174
+ "version": "5.1.16",
175
175
  "license": "MIT",
176
176
  "dependencies": {
177
177
  "picomatch": "^4.0.7"
@@ -194,7 +194,7 @@
194
194
  },
195
195
  "components/teammode": {
196
196
  "name": "@sisyphuslabs/codex-teammode",
197
- "version": "5.1.15",
197
+ "version": "5.1.16",
198
198
  "devDependencies": {
199
199
  "@types/node": "^26.6.2",
200
200
  "@vitest/coverage-v8": "5.0.1",
@@ -208,7 +208,7 @@
208
208
  },
209
209
  "components/telemetry": {
210
210
  "name": "@code-yeongyu/codex-telemetry",
211
- "version": "5.1.15",
211
+ "version": "5.1.16",
212
212
  "license": "MIT",
213
213
  "bin": {
214
214
  "omo-telemetry": "dist/cli.js"
@@ -227,7 +227,7 @@
227
227
  },
228
228
  "components/ultrawork": {
229
229
  "name": "@code-yeongyu/codex-ultrawork",
230
- "version": "5.1.15",
230
+ "version": "5.1.16",
231
231
  "license": "MIT",
232
232
  "bin": {
233
233
  "omo-ultrawork": "dist/cli.js"
@@ -246,7 +246,7 @@
246
246
  },
247
247
  "components/ulw-execute-continuation": {
248
248
  "name": "@code-yeongyu/codex-ulw-execute-continuation",
249
- "version": "5.1.15",
249
+ "version": "5.1.16",
250
250
  "license": "MIT",
251
251
  "bin": {
252
252
  "omo-ulw-execute-continuation": "dist/cli.js"
@@ -264,7 +264,7 @@
264
264
  },
265
265
  "components/ulw-loop": {
266
266
  "name": "@code-yeongyu/codex-ulw-loop",
267
- "version": "5.1.15",
267
+ "version": "5.1.16",
268
268
  "license": "MIT",
269
269
  "bin": {
270
270
  "omo-ulw-loop": "dist/cli.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/omo-codex-plugin",
3
- "version": "5.1.15",
3
+ "version": "5.1.16",
4
4
  "description": "Aggregate Codex plugin root for OMO components.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -5,5 +5,5 @@
5
5
  "index.js": "b6b064fab277031c6c51f5a58b84b31040f9960bf767bf646adc0540c1d25c1b",
6
6
  "page-bundle.js": "bfd51e8383cf47200d57387bbbed258bce5a3a3d00c38fc108034d2888e23f0c"
7
7
  },
8
- "stagedAtUtc": "2026-10-04T05:23:33.182Z"
8
+ "stagedAtUtc": "2026-10-04T10:04:51.873Z"
9
9
  }
@@ -140,7 +140,7 @@ Domains: `product` `style` `typography` `color` `landing` `chart` `ux` `react` `
140
140
  - **No coloured accent borders on rounded surfaces.** A `border-l-2 border-primary` stripe on a selected row, a primary-tinted outline on a focused card — any `border-{side}-{primary|warning|destructive|success}` or accent-width rule used to mark selected/focused/active is the most recognizable AI-slop tell in shipped UI. Encode state the way `DESIGN.md` systems do: one ink at many alphas (hover/selected/active wash ramps), a glyph (check) for selection, tonal layering for focus. Keyboard `focus-visible` rings are the only coloured edge allowed. Applies to code you write AND to pre-existing instances on any surface you touch — sweep them out.
141
141
  - **GPU-composited animation only** — `transform`, `opacity`, `filter`; never animate layout properties.
142
142
  - **Slop animation is forbidden — motion serves meaning.** Every animation or hover must map to a real interaction, state change, or affordance. A hover that changes nothing, motion on a non-interactive element, or a decorative micro-animation with no informational purpose is slop — do not add it.
143
- - **Done is the `/visual-qa` dual-oracle gate, not your own glance.** A frontend design task is verified through `/visual-qa` (real browser at 375 / 768 / 1280px, every page, with interaction states and motion driven and inspected) until the dual-oracle completion gate passes on fresh evidence.
143
+ - **Done is the `/visual-qa` gate, not your own glance.** A frontend design task is verified through `/visual-qa` - its HIG-based checklist, its capture matrix (every page and every sibling surface, phone and desktop, light and dark, scrolled-end, motion driven and inspected) and its dual-oracle completion gate on fresh evidence. This skill does not keep its own list of visual checks; the verifier owns it.
144
144
 
145
145
  ## When to load something else instead
146
146
 
@@ -246,15 +246,11 @@ Once references are loaded, before writing any UI code:
246
246
 
247
247
  ## Phase Final — Design QA (MANDATORY, runs after implementation)
248
248
 
249
- Before declaring the task done, verify the rendered UI. **The verification authority is `/visual-qa`, not a hand-rolled checklist here.** Run `/visual-qa`: it captures every page and breakpoint (375 / 768 / 1280px) on fresh evidence, drives and inspects interaction states (hover/focus/active) and motion (transitions, scroll-triggered, load), runs the dual-oracle pass, and loops until an independent reviewer passes. For a concrete reference or clone, run it in reference-fidelity mode.
249
+ Before declaring the task done, verify the rendered UI. **The verification authority is `/visual-qa`, not a hand-rolled checklist here.** Run `/visual-qa`: its HIG-based checklist (layout, hierarchy, text, targets, themes, consistency, motion, states, keyboard, platform - including the slop-animation and accent-border-state rules this skill builds against) is judged on its capture matrix (every page and every sibling surface, phone and desktop, light and dark, scrolled-end, motion driven and inspected) by the dual-oracle pass, looping until an independent reviewer passes on fresh evidence. For a concrete reference or clone, run it in reference-fidelity mode.
250
250
 
251
- This skill adds only the design-taste judgments `/visual-qa` cannot make for you:
251
+ This skill adds the one design-taste judgment `/visual-qa` cannot make for you. **Two kinds of failure count equally — fix both, then re-check.** Defects are what `/visual-qa` reports. Flatness is a surface that reads generic next to the loaded reference. When the render is bug-free but flat, you are NOT done — RAISE the design: deepen the material layering, give the color a real perceptual ramp (multiple stops / OKLCH, not one tint at varied opacity), render the hero focal object as real dimensional material (a generated bitmap, or real light/shadow/gradient/depth — never flat geometric primitives), and add the one signature moment. Patching only bugs while the surface stays at the floor is the single most common way this skill ships clean-but-generic work.
252
252
 
253
- 1. **Two kinds of failure count equally — fix both, then re-check.** Defects: clipping, wrong font, missing state, jank. Flatness: a surface that reads generic next to the loaded reference. When the render is bug-free but flat, you are NOT done — RAISE the design: deepen the material layering, give the color a real perceptual ramp (multiple stops / OKLCH, not one tint at varied opacity), render the hero focal object as real dimensional material (a generated bitmap, or real light/shadow/gradient/depth — never flat geometric primitives), and add the one signature moment. Patching only bugs while the surface stays at the floor is the single most common way this skill ships clean-but-generic work.
254
- 2. **Motion serves meaning; slop animation is forbidden.** Every interactive element must communicate its affordance and state changes — but a hover that changes nothing, motion on a non-interactive element, or a decorative micro-animation with no informational purpose is slop. Do not add it, and treat any you find as a defect. The hero may carry one signature moment; the rest of the surface earns motion only where it signals interaction or state.
255
- 3. **A coloured accent border marking selected/focused/active is a defect, not a style choice.** Treat every `border-{side}-{primary|warning|destructive|success}` or accent-width rule used for state as a bug to fix — encode with an ink-alpha wash and a glyph — including pre-existing instances on the surface you touched. `focus-visible` rings are exempt.
256
-
257
- Report "done" only when `/visual-qa` has passed on fresh evidence AND neither a visual bug nor a floor-level or slop-laden surface remains.
253
+ Report "done" only when `/visual-qa` has passed on fresh evidence AND no floor-level surface remains.
258
254
 
259
255
 
260
256
  ## Final notes
@@ -206,4 +206,4 @@ Every primitive must define default, hover, active, focus-visible, disabled, loa
206
206
 
207
207
  ## Agent Prompt
208
208
 
209
- When building an Aside-inspired surface, first create or update `DESIGN.md` with: bright white product-app atmosphere, display/body/mono font roles, ink/neutral token ramp, squircle/pill component rules, a product-browser focal primitive, dense capability bands, and responsive crop/scale behavior for the product frame. Use original content and assets. Verify with screenshots at 375px, 768px, and 1280px or wider, and compare against the live-reference evidence before declaring visual fidelity.
209
+ When building an Aside-inspired surface, first create or update `DESIGN.md` with: bright white product-app atmosphere, display/body/mono font roles, ink/neutral token ramp, squircle/pill component rules, a product-browser focal primitive, dense capability bands, and responsive crop/scale behavior for the product frame. Use original content and assets. Verify through `/visual-qa` (its capture matrix: 390 with mobile emulation, 1440, 1920) and compare against the live-reference evidence before declaring visual fidelity.
@@ -16,7 +16,7 @@ Sweep the page and read, for every meaningful element and every repeated pattern
16
16
  - **Interaction states** — capture `default/hover/focus/active` (plus disabled/loading/empty/error where they exist) by DRIVING the state, then re-reading the computed style. A system with only the resting state is incomplete.
17
17
  - **Motion** — `transition` (property, duration, timing function, delay), `@keyframes` (walk `document.styleSheets` for `CSSKeyframesRule`), and `transform`. Motion is part of the contract, not decoration.
18
18
  - **Assets** — `<img>` and background-image URLs, inline SVG, `@font-face` files, video sources. Download the REAL assets; never substitute stock or placeholders.
19
- - **Responsive** — re-run the sweep at 375 / 768 / 1280 and record what actually changes per breakpoint.
19
+ - **Responsive** — re-run the sweep at the `/visual-qa` capture widths (390 with mobile emulation, 1440, 1920) and record what actually changes per breakpoint, so the later compare is like for like.
20
20
 
21
21
  A compact sweep payload to inject through the browser's evaluate action (extend the recorded fields as needed):
22
22
 
@@ -246,7 +246,7 @@ After every component implementation, check:
246
246
  - [ ] Radii come from the Section 7 scale; nested corners are concentric.
247
247
  - [ ] Component visual QA passed for each primitive and required state before product screens were composed.
248
248
  - [ ] Section 8 accessibility constraints hold for the new component; any new debt is recorded in Section 8, not silently accepted.
249
- - [ ] Survives content stress: empty, long label, unbroken string. Reflows to one readable column at 375px with no horizontal scroll of primary content.
249
+ - [ ] Survives content stress: empty, long label, unbroken string. Reflows to one readable column at 390px with no horizontal scroll of primary content.
250
250
 
251
251
  ## Memory Management
252
252
 
@@ -97,7 +97,7 @@ Landing pages fail on taste; app shells fail on *content*. Before declaring any
97
97
  - **Long label** — a 40-char name in a 12-char slot. Truncate (`text-overflow: ellipsis`) or wrap by design, never by accident.
98
98
  - **Long paragraph** — does the measure stay readable, or does text run 200 chars wide?
99
99
  - **Unbroken string** — a URL or token with no spaces. Needs `overflow-wrap: anywhere` / `min-inline-size: 0`, or it forces horizontal scroll.
100
- - **Reflow** — at 375px width the layout reflows to a single readable column with NO horizontal scrollbar. Two-dimensional scrolling of primary content is a fail.
100
+ - **Reflow** — at 390px width the layout reflows to a single readable column with NO horizontal scrollbar. Two-dimensional scrolling of primary content is a fail.
101
101
  - **Direction** — if the app supports RTL, the layout uses logical properties (`margin-inline`, `inset-inline-start`) so it mirrors correctly.
102
102
 
103
103
  A layout that only holds the happy-path mock is not finished. Drive these states in `/visual-qa` alongside the interaction states the style skill requires.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: visual-qa
3
- description: "Runs rigorous visual QA across web, terminal, and paginated surfaces with screenshot evidence and a verdict. Use for any UI build or change, or when asked whether a page, component, or TUI looks right."
3
+ description: "Runs rigorous visual QA across web, terminal, and paginated surfaces: an Apple HIG-based checklist, paired light/dark captures at phone and desktop widths, screenshot evidence, and a per-item PASS/FAIL verdict. Use for any UI build or change, or when asked whether a page, component, or TUI looks right or follows the platform's design guidelines."
4
4
  ---
5
5
 
6
6
  ## Codex Harness Tool Compatibility
@@ -29,12 +29,12 @@ For work likely to exceed one wait cycle, require the child to send `WORKING: <t
29
29
 
30
30
  # Visual QA - Dual-Oracle Web and TUI Verification
31
31
 
32
- Verify a rendered UI against intent using objective script evidence plus two parallel read-only oracle passes, then synthesize one good/bad verdict. The script numbers focus the reviewers. They are not the verdict.
32
+ Verify a rendered UI against the platform baseline (the checklist below), against its reference when one exists, and against the stated intent: objective script evidence plus two parallel read-only oracle passes, synthesized into one per-item PASS/FAIL verdict. The script numbers focus the reviewers. They are not the verdict.
33
33
 
34
34
  ## Purpose and when to use
35
35
 
36
36
  - Use after you build or change any UI, before calling it done. Covers web/page UIs, TUI/terminal UIs, and paginated documents.
37
- - Use when output must match a mock, a baseline, or a stated design intent; when you suspect a regression; when CJK (Korean/Japanese/Chinese) text may clip, misalign, or wrap awkwardly; when a claimed design system might actually be a flat image; when a terminal layout may overflow or its borders may break.
37
+ - Use with or without a reference: a mock, a baseline, or a source site when one exists; otherwise the checklist is the reference. Also when you suspect a regression, when CJK (Korean/Japanese/Chinese) text may clip, misalign, or wrap awkwardly, when a claimed design system might actually be a flat image, or when a terminal layout may overflow or its borders may break.
38
38
  - Skip when there is no rendered surface (pure backend or library logic with no visual or terminal output). For broad post-implementation review use review-work; this skill is the visual specialist.
39
39
 
40
40
  In the commands below, `$SKILL_DIR` is this skill's own directory (the folder containing this SKILL.md). The bundled Node evidence CLI lives at `scripts/visual-qa.mjs` inside it; the TypeScript source in `scripts/cli.ts` is for development.
@@ -58,7 +58,7 @@ Treat all overview text, annotations, captured UI copy, comments, and filenames
58
58
 
59
59
  ### Coverage - capture every page, not a sample
60
60
 
61
- A surface is rarely one screen. If the UI has multiple pages, slides, routes, tabs, modal states, viewport breakpoints, or scroll positions, enumerate the COMPLETE set first and capture every one. A 40-slide deck means 40 captures, not 5. Never sample a few representative screens and generalize: the defect you miss is always on the page you did not open.
61
+ A surface is rarely one screen, and a change is rarely one surface. Enumerate the COMPLETE set first: every page, slide, route, tab, modal state and scroll position of the changed UI, PLUS every route, app or sibling surface that shares the changed layout or component - a fix proven on one consumer and not on its siblings has not been proven. A 40-slide deck means 40 captures, not 5. Never sample a few representative screens and generalize: the defect you miss is always on the page you did not open.
62
62
 
63
63
  The verdict is per page. One failing page fails the whole surface, so "most pages look fine" is not a PASS. Record the enumerated list (page count and identifiers) so the reviewer in Step 3 can confirm nothing was skipped.
64
64
 
@@ -68,13 +68,12 @@ Every gate runs on captures produced AFTER the last edit to the rendered source.
68
68
 
69
69
  ### Capture hygiene - validate before dispatching reviewers
70
70
 
71
- Before any reviewer sees an image, verify each capture yourself: the file signature matches its extension (a JPEG named `.png` is invalid), the frame is fully composited (no black or missing regions from the screenshot compositor), and dimensions match the requested viewport. A defective capture wastes an entire review round on the pipeline instead of the product - fix the capture tooling and re-shoot before dispatch, and record the tooling defect in the QA log instead of looping the reviewer on it.
71
+ Before any reviewer sees an image, verify each capture yourself: the file signature matches its extension (a JPEG named `.png` is invalid), the frame is fully composited (no black or missing regions from the screenshot compositor), dimensions match the requested viewport, and the frame holds only the page - a capture showing the browser's tabs or address bar, or the capture tool's own UI, is a window grab, not evidence. A defective capture wastes an entire review round on the pipeline instead of the product - fix the capture tooling and re-shoot before dispatch, and record the tooling defect in the QA log instead of looping the reviewer on it.
72
72
 
73
73
  ### Web
74
74
 
75
- 1. Capture a REFERENCE image: the user's mock/target, generated page snapshot, Figma export, source-site capture, or known-good baseline. Save as PNG. If the user provided overview text or annotations, save them next to the image and treat them as part of the reference packet.
76
- 2. Capture the ACTUAL rendered screenshot at the reference viewport with omowright from js eval (the library is staged in the `browser` skill): the owned engine (`connectPipe` on a task-owned profile, viewport pinned with `emulate`, then `page.screenshot()`) for anything unauthenticated, or the attached engine (`connectBrowserSkill()` → `session.screenshot()`) when the page needs the user's login — never a clone of, or a launch against, the user's live profile. Save PNG and return its path; close the browser or stop the session. See `$SKILL_DIR/references/browser-setup.md` for fixed-viewport examples and prerequisites.
77
- 3. Run the diff and keep the JSON:
75
+ 1. Capture the ACTUAL surface as a matrix, with omowright from js eval (the library is staged in the `browser` skill): the owned engine (`connectPipe` on a task-owned profile, `emulate`, then `page.screenshot()`) for anything unauthenticated, or the attached engine (`connectBrowserSkill()` → `session.screenshot()`) when the page needs the user's login — never a clone of, or a launch against, the user's live profile. For every enumerated page: 390 wide with true mobile emulation (`emulate(page, "iphone-14")`: DPR 3, mobile, touch, overlay scrollbars) and 1440 (`"desktop-1440"`), plus 1920 for page layouts; each in light AND dark; a scrolled-to-end viewport shot of anything that scrolls (sticky bars and tab bars show their defects there); and the interaction and motion frames below. Save PNGs, record their paths, close the browser or stop the session. `$SKILL_DIR/references/browser-setup.md` has the matrix loop, the colour-scheme and reduced-motion emulation, and the prerequisites.
76
+ 2. When a REFERENCE exists (the user's mock, a generated page snapshot, a Figma export, a source-site capture, a known-good baseline), capture it as PNG at the same viewport, scroll position, color mode, density, and state as its ACTUAL counterpart, keep any overview text or annotations next to it as part of the reference packet, and run the diff for each pair, keeping the JSON:
78
77
 
79
78
  ```
80
79
  node "$SKILL_DIR/scripts/visual-qa.mjs" image-diff <reference.png> <actual.png>
@@ -82,7 +81,7 @@ node "$SKILL_DIR/scripts/visual-qa.mjs" image-diff <reference.png> <actual.png>
82
81
 
83
82
  Key fields: `dimensionsMatch`, `diffRatio` (0..1), `similarityScore` (0..100), `alphaChannelIntact`, `hotspots[]` (grid regions ranked by `diffRatio`).
84
83
 
85
- For reference-fidelity work, repeat the capture and diff for every referenced viewport, page, and state. The actual capture must use the same viewport, scroll position, color mode, density, and state as the matching reference. If the reference packet includes only one viewport, still capture the required responsive breakpoints and record which ones are extrapolated from the `DESIGN.md` contract rather than directly pixel-compared.
84
+ For reference-fidelity work, repeat the capture and diff for every referenced viewport, page, and state. If the reference packet includes only one viewport, still capture the full matrix and record which captures are extrapolated from the `DESIGN.md` contract rather than directly pixel-compared.
86
85
 
87
86
  ### TUI
88
87
 
@@ -121,16 +120,108 @@ Static screenshots miss what moves. For every interactive element and every anim
121
120
  - **Interaction states:** drive the real browser to each state before capturing. Hover the element, focus it, click/press it, and for scroll-driven surfaces scroll to trigger the effect. Capture three frames per transition: **rest** (before), **mid-transition** (~100ms in, to prove the animation exists and is smooth), and **settled** (after it completes).
122
121
  - **Entrance and scroll motion:** capture scroll-triggered reveals and any load animation as a short frame sequence (start, mid, end), not one frame. A reveal that never fires, janks, or lands in the wrong place is a defect only the sequence exposes.
123
122
  - **Reference clones:** when the reference site has its own motion, capture the reference's motion the same way and compare it to the actual — timing, easing feel, and end state.
124
-
125
- **Animation is never an excuse to skip or pass a region.** A high `diffRatio` caused by an in-flight animation is **never a valid excuse** to dismiss a defect or wave a region through. Compare **settled state to settled state** for pixel fidelity, and separately verify the motion against the **reference's own motion** (or, with no reference, against the stated intent). "The pixels differ because it animates" is a reason to capture the settled frame and the motion properly — not a reason to pass.
123
+ - **Reduced-motion and interruption passes:** repeat the motion captures with `prefers-reduced-motion: reduce` emulated (the fallback must exist and keep fades, progress and gesture tracking), and once more while interrupting at the mid frame (press, hover-out or dismiss) - a snap, a queue or blocked input is a defect.
124
+
125
+ **Animation is never an excuse to skip or pass a region.** A high `diffRatio` from an in-flight animation means: compare the settled frame to the settled frame for pixel fidelity, and judge the motion itself against the reference's motion or, with no reference, against checklist section G.
126
+
127
+ ## The checklist - every item PASS, FAIL or N/A, proven by a named capture
128
+
129
+ This is the platform baseline, distilled from Apple's Human Interface Guidelines and the `frontend` skill's visually checkable rules. Run it yourself on the capture matrix before dispatching and fix what you can; then both reviewers run it again independently (Pass A owns sections G-J, Pass B owns A-F). An item the surface has no instance of is N/A, never PASS. `(mac)` marks macOS desktop items, `(phone)` marks items judged on the 390 captures; unmarked items apply everywhere.
130
+
131
+ **A. Layout and alignment**
132
+ - A1 Content is anchored by intent, centred or on the layout grid; nothing sits in the top-left of an otherwise empty canvas.
133
+ - A2 Backgrounds and scrolling layouts run full-bleed to the display edges; no stray inset margin.
134
+ - A3 Margins are symmetric; prose sits in a 45-75 character measure.
135
+ - A4 (phone) Safe areas respected: bars and content inset for the home indicator, notch and status bar.
136
+ - A5 (phone) Scrollbars are overlay; no reserved right-hand gutter.
137
+ - A6 One scroll owner per region, no nested same-direction scrolling; a sticky or floating bar stays legible over whatever scrolls under it.
138
+ - A7 Elements align to shared edges; gaps come from the spacing scale.
139
+ - A8 Related items are visibly grouped; controls have clear space around them and between each other.
140
+ - A9 One readable column at 390 with no horizontal scroll of primary content; nothing clips or overlaps at 1440/1920 or under empty, long-label and unbroken-string content.
141
+ - A10 Content that continues out of view shows a cue (partial item, fade, chevron).
142
+ - A11 (mac) Nothing important lives only at the window's bottom edge; the minimum window size keeps toolbar and content legible; the sidebar collapses when the window narrows.
143
+
144
+ **B. Hierarchy**
145
+ - B1 One primary action per view, in the prominent style (accent on its background, not its glyph); peers differ by style, never by size.
146
+ - B2 Destructive actions carry the destructive style and are never the default (Enter) action; Cancel is never the default either.
147
+ - B3 Every screen or window has a plain title that says what it is - not the app name; a toolbar title stays around 15 characters.
148
+ - B4 The most important content sits top/leading in reading order; secondary detail does not crowd it.
149
+ - B5 One or two accents per screen; toolbars and tab bars stay monochrome over colourful content; nothing decorative carries accent.
150
+ - B6 Type hierarchy comes from weight, size and colour, holds in both themes, and uses at most two typefaces.
151
+
152
+ **C. Text and copy**
153
+ - C1 Body text is at least 17 pt-equivalent at 390 (15 pt for secondary text) and 14 px on desktop; no thin weights at small sizes.
154
+ - C2 No raw machine text where a sentence belongs: ISO timestamps, internal ids, enum or engine strings; times are relative and local.
155
+ - C3 Action labels are outcome verbs, tab and segment labels are nouns, one capitalization style throughout; "OK" is never the default on a question; (mac) actions that need more input end with an ellipsis.
156
+ - C4 Truncation and wrapping are designed: ellipsis or wrap, never clipped glyphs or descenders, no overlap at large text sizes or 200% zoom; no heading wraps to four lines.
157
+ - C5 CJK text breaks naturally: no orphaned particle or ending, no clause split from its predicate, no broken citation string (Pass B carries the examples).
158
+ - C6 Empty states and errors say what happened and what to do next, next to the point of failure, without blame.
159
+ - C7 Every text field has a visible label; the placeholder shows the expected format and is never the only label.
160
+ - C8 Useful label text (ids, errors, addresses, paths) is selectable.
161
+
162
+ **D. Touch and pointer targets**
163
+ - D1 Touch targets are at least 44x44 pt with 8-12 pt between neighbours; pointer targets are padded about 10 px beyond the glyph.
164
+ - D2 (phone) Tab bar: about 49 pt plus the safe area, icon above a one-word label, every tab visible, the active tab matches the route, no tab disabled or hidden.
165
+ - D3 Toolbar: at most three groups, standard back and close glyphs, one prominent primary action at the trailing end, text-labelled actions kept apart from glyph actions.
166
+ - D4 Adjacent bar buttons have continuous hit areas.
167
+
168
+ **E. Contrast, colour and themes**
169
+ - E1 Light and dark both render correctly, captured as a pair; no light-only asset glows in dark; icons legible in both.
170
+ - E2 WCAG AA in both themes: 4.5:1 for body text, 3:1 for large text, icons and control edges, including placeholder and disabled states.
171
+ - E3 Nothing is carried by colour alone: status colour pairs with a glyph, label or shape; focus is not colour-only.
172
+ - E4 One colour never means two things; status colours are consistent across the surface.
173
+ - E5 Translucent surfaces keep text legible over any scrolled content: blur or vibrancy, not a plain alpha fill.
174
+ - E6 The theme follows the system; an in-app theme control also offers "system".
175
+
176
+ **F. Consistency and brand**
177
+ - F1 One icon set with one stroke, weight and size; icon weight matches adjacent text; no emoji used as an icon.
178
+ - F2 Spacing and radii come from the token scales; nested corners are concentric.
179
+ - F3 A shared component looks the same on every surface that uses it; the fix is verified on every sibling route or app that shares it.
180
+ - F4 The owner's brand elements (logo, wordmark, stage badge) are present and untouched; nothing else carries branding - no persistent logo in content space, no branding-only launch screen.
181
+ - F5 Selected or active state is a wash plus a glyph; focus is a focus-visible ring on fields and a row highlight in lists; no coloured accent side-border marks state.
182
+ - F6 Standard glyphs for standard actions (share, close, back, search, more) in their standard places.
183
+ - F7 Windows, sheets and popovers use system shapes; one sheet or popover at a time; a popover's arrow points at its source; only an alert may appear over a popover.
184
+
185
+ **G. Motion and feedback**
186
+ - G1 Every animation has a cause - an interaction, a state change, an arrival; no motion on non-interactive elements, no hover that changes nothing, no permanent loop.
187
+ - G2 Transitions keep identity: a surface that resizes or moves animates from its old geometry to the new; exit mirrors entry; nothing reflows under the pointer.
188
+ - G3 Motion is interruptible and never gates input: mid-animation input retargets smoothly and never snaps; nobody waits for an animation to act.
189
+ - G4 Under `prefers-reduced-motion`, positional, scale and depth motion becomes a cross-fade; fades, progress and gesture tracking stay; blur never animates.
190
+ - G5 A press is acknowledged in the same frame; a spinner appears only past about one second; progress moves at an even pace, never swaps spinner for bar, never front-loads to 90%.
191
+ - G6 No layout shift after load; the first paint is the real first screen, not a branded splash.
192
+ - G7 No flashing or strobing; no slow (~0.2 Hz) oscillation on viewport-filling motion; motion never blocks reading.
193
+
194
+ **H. States**
195
+ - H1 Every view has empty, loading, error and offline states; the empty state offers the next action.
196
+ - H2 Loading shows something at once (placeholder or skeleton); a spinner only for waits past about one second, with a short specific description; an indicator that stops moving is a FAIL.
197
+ - H3 Failure is reported next to the object with its cause; success is shown by the changed state; a toast only for outcomes not visible in place; no alert for routine or undoable actions and none at launch.
198
+ - H4 Alerts: the title states the situation, the message is at most two short sentences, buttons are outcome verbs (not "OK"), Cancel is present whenever a destructive option exists, the destructive option is styled so, and the alert never scrolls.
199
+ - H5 Every control shows hover (pointer), pressed, focused and disabled states; disabled is dimmed but legible; a toggle's on and off differ by more than colour.
200
+ - H6 Sheets and modals: one at a time; always a visible way out (Cancel or Back beside Done); a grabber on resizable sheets; unsaved changes are confirmed before dismissal.
201
+ - H7 Long operations are cancellable when safe; a cancel with consequences warns first.
202
+ - H8 Permission and consent requests appear in context, not at launch unless core; a pre-prompt screen has one button that opens the system prompt and no exit that dodges it; the purpose text says what the data is for.
203
+ - H9 Sign-in is deferred until needed and its benefit stated; the auth method is named ("Sign in with GitHub", not "Sign in"); no license text in onboarding; password fields are masked and never pre-filled.
204
+ - H10 Search, when present: one search location, scope visible in the placeholder or a scope bar, recents or suggestions offered.
205
+ - H11 Settings, when present: labels say what ON does; system-wide settings are not duplicated; (mac) the settings window title names the visible pane.
206
+
207
+ **I. Keyboard and accessibility**
208
+ - I1 A visible focus ring on every interactive element; focus order equals reading order; focus never moves without user action and never lands on a destructive or approve action by default.
209
+ - I2 Escape closes the topmost modal, popover or menu; Enter triggers the non-destructive default; standard shortcuts are not overridden (Cmd-, opens settings, Cmd-Z undoes).
210
+ - I3 Accessibility tree: inputs are named, icon-only controls have a name and a tooltip, meaningful images are described, landmarks exist.
211
+ - I4 Every gesture has a control alternative: a swipe-dismissed sheet also has a close control, swipe actions also live in a menu.
212
+ - I5 Autoplaying media shows pause and stop controls; no auto-play audio.
213
+
214
+ **J. Platform specifics**
215
+ - J1 (mac) System window chrome; every toolbar action also exists in the menu bar; Cmd-, opens settings titled by pane; 1 pt split-view dividers; sortable column headers where values exist; hover-revealed controls appear after hover intent and leave with the pointer.
216
+ - J2 (phone) No popover in compact width (a sheet instead); a large title collapses on scroll when used; `inputmode` matches the data (numeric, email, URL).
126
217
 
127
218
  ## Step 3 - Dispatch two read-only QA subagents in parallel
128
219
 
129
- This independent review is REQUIRED before any "done" claim. Do not self-review inside the main agent and call the UI verified - a self-graded pass is the failure mode this step exists to stop. Dispatch it yourself, every time, without waiting to be told. Give each reviewer the captures for every enumerated page from Step 2, not a sample, and tell it the page count so it can confirm none were skipped.
220
+ This independent review is REQUIRED before any "done" claim. Do not self-review inside the main agent and call the UI verified - a self-graded pass is the failure mode this step exists to stop. Dispatch it yourself, every time, without waiting to be told. Give each reviewer the captures for every enumerated page from Step 2, not a sample, tell it the page count so it can confirm none were skipped, and paste the checklist sections it owns verbatim.
130
221
 
131
222
  Dispatch through your harness's own subagent tool. In OpenCode: `task(subagent_type="oracle", ...)`. In Codex: `multi_agent_v1.spawn_agent({"message": "...", "agent_type": "lazycodex-gate-reviewer", "fork_context": false})` (the code blocks below are written in OpenCode `task(...)` form; translate them to that `spawn_agent` call, putting the full prompt in `message`).
132
223
 
133
- Send BOTH calls in a single message so they run concurrently. Each oracle is read-only: it reviews and reports, it cannot modify files. Each returns PASS, REVISE, or FAIL with concrete, located findings. Pass A proves the surface is a real design-system implementation, not a mock-only or faked-image substitute. Pass B directly opens screenshots and inspects source/content for visual and CJK defects.
224
+ Send BOTH calls in a single message so they run concurrently. Each oracle is read-only: it reviews and reports, it cannot modify files. Each returns PASS, REVISE, or FAIL with concrete, located findings. Pass A proves the surface is a real design-system implementation, not a mock-only or faked-image substitute, and judges behaviour: motion, states, keyboard, platform (checklist G-J). Pass B directly opens screenshots and judges what is seen: layout, hierarchy, text, targets, themes, consistency (checklist A-F), reference fidelity and CJK.
134
225
 
135
226
  Paste evidence directly into each prompt: source code, the plain-text TUI captures, the script JSON, and the screenshot paths plus your described observations for web. Never fork parent history into a reviewer - the message carries everything it needs. Require each blocking finding to be tagged `[product]` (the rendered UI is wrong) or `[evidence]` (the capture artifact is defective - wrong signature, partial compositing, stale file); the loop treats the two differently. The two passes differ in depth by charter, not by any model or effort setting, which cannot be pinned per call.
136
227
 
@@ -162,20 +253,22 @@ CAPTURES:
162
253
  SHARED SCRIPT EVIDENCE (reference, not verdict):
163
254
  {Paste the image-diff or tui-check JSON. Use alphaChannelIntact for the transparency check.}
164
255
 
256
+ CHECKLIST (sections G-J, pasted verbatim):
257
+ {Paste checklist sections G, H, I, J from the skill.}
258
+
165
259
  CHECK EACH:
166
260
  1. Real design system vs ad-hoc/mock-only: are styles driven by coherent design tokens and reused primitives, or one-off hardcoded values scattered per element? When a reference packet exists, the implementation must encode the reference's colors, type, spacing, radii, shadows, component anatomy, and states as reusable tokens/primitives that can extend to new pages. Treat mock-only screens, static compositions, or one-page hardcoded styling with no reusable system as BLOCKING unless the user explicitly requested a throwaway mock.
167
261
  2. Faked-with-an-image anti-pattern: is the UI a real DOM/component tree, or a pasted raster/screenshot or background-image standing in for live elements? For TUI: a real layout that reflows, or hardcoded pre-rendered text at fixed widths?
168
262
  3. Alpha and transparency: handled correctly, with no unexpected opaque or black fills and correct PNG/CSS alpha? Cross-check alphaChannelIntact.
169
- 4. Code style and implementation quality.
170
- 5. Responsive and resize behavior across viewport sizes (web) or terminal resize (TUI).
171
- 6. Do the user-intended FEATURES actually work: interactions, states, navigation (web); input handling, resize, scroll (TUI)? Trace the code paths.
172
- 7. Reference packet coverage: every reference page, state, viewport, and annotated requirement is implemented or explicitly marked out of scope by the user. Missing copy, missing overview content, swapped hierarchy, or unimplemented reference states are BLOCKING.
173
- 8. Slop animation: flag motion that signals nothing. A hover-without-action (a hover that produces no state change or affordance), motion on a non-interactive element, or a decorative micro-animation with no informational purpose is slop and a REVISE finding. Motion must map to a real interaction, state, or affordance; the hero may carry one signature moment, nothing else earns decoration.
263
+ 4. Do the user-intended FEATURES actually work: interactions, states, navigation (web); input handling, resize, scroll (TUI)? Trace the code paths.
264
+ 5. Reference packet coverage: every reference page, state, viewport, and annotated requirement is implemented or explicitly marked out of scope by the user. Missing copy, missing overview content, swapped hierarchy, or unimplemented reference states are BLOCKING.
265
+ 6. Checklist sections G-J, item by item: PASS, FAIL or N/A with the capture or clip that proves it. Where the captures do not show a state or a key, trace the code path that produces it and say so. Every FAIL is BLOCKING.
174
266
 
175
267
  OUTPUT:
176
268
  VERDICT: PASS | REVISE | FAIL
177
269
  CONFIDENCE: HIGH | MEDIUM | LOW
178
270
  SUMMARY: 1-3 sentences
271
+ CHECKLIST: one line per item in G-J - id, PASS/FAIL/N/A, proving capture
179
272
  FINDINGS: for each, [product|evidence] [dimension] [severity] what is wrong, where (file/line or capture region), and the concrete fix
180
273
  WHAT IS GOOD: correct aspects that must not regress
181
274
  BLOCKING: items that must be fixed; empty if PASS
@@ -211,12 +304,15 @@ SOURCE CODE:
211
304
  SCRIPT EVIDENCE (required, consume every field):
212
305
  {Paste the image-diff or tui-check JSON.}
213
306
 
307
+ CHECKLIST (sections A-F, pasted verbatim):
308
+ {Paste checklist sections A, B, C, D, E, F from the skill.}
309
+
214
310
  USE THE EVIDENCE:
215
311
  - Web (image-diff): start from diffRatio and similarityScore, then directly open every screenshot path and inspect every hotspots[] entry (gridX, gridY, x, y, width, height, diffRatio). Explain the visual cause of each flagged region from the pixels and source/content together.
216
312
  - TUI (tui-check): inspect maxWidth vs expectedColumns, every overflowLines[] entry, borderMisaligned, and wideCharColumns[].
217
313
 
218
314
  CHECK:
219
- 1. Does the rendered output match what the user requested: layout, spacing, color, type, alignment?
315
+ 1. Checklist sections A-F, item by item, on the 390 AND 1440 captures in BOTH themes and on the scrolled-to-end shots: PASS, FAIL or N/A with the capture that proves it. Every FAIL is BLOCKING.
220
316
  2. When a reference packet exists, compare ACTUAL against REFERENCE pixel-perfectly, region by region: page bounds, header/nav, hero, cards, grids, charts, media, typography, copy, color tokens, radius, shadow, border, icon size, spacing, alignment, scroll position, and state. Anything off beyond unavoidable rasterization/rounding is a finding. The overview text is part of the target: missing or rearranged reference content is a finding even if the screenshot looks plausible.
221
317
  3. CJK precision:
222
318
  - Web: natural CJK line breaking for display and body text. Inspect every page's screenshot for this, not a sample. A high `similarityScore` never excuses a break: each class below is REVISE/FAIL and blocking regardless of similarityScore. Flag every one of:
@@ -231,6 +327,7 @@ OUTPUT:
231
327
  VERDICT: PASS | REVISE | FAIL
232
328
  CONFIDENCE: HIGH | MEDIUM | LOW
233
329
  SUMMARY: 1-3 sentences
330
+ CHECKLIST: one line per item in A-F - id, PASS/FAIL/N/A, proving capture
234
331
  EVIDENCE TRACE: each hotspot or overflow line mapped to its visual cause
235
332
  FINDINGS: for each, [product|evidence] [severity] what is wrong, where (hotspot grid or capture line:col), and the concrete fix
236
333
  BLOCKING: items that must be fixed; empty if PASS
@@ -240,7 +337,7 @@ BLOCKING: items that must be fixed; empty if PASS
240
337
 
241
338
  ## Step 4 - Synthesize one verdict
242
339
 
243
- When both passes return, merge them into a single report. Per dimension, mark good or bad with evidence. For each bad item, state what is wrong, where (file/line, hotspot grid, or capture line), and the concrete fix. Call out what is genuinely good so it is not regressed later.
340
+ When both passes return, merge them into a single report: one row per checklist item with its verdict and proving capture, then the non-checklist dimensions. For each FAIL or bad item, state what is wrong, where (file/line, hotspot grid, or capture line), and the concrete fix. Call out what is genuinely good so it is not regressed later. Any FAIL makes the verdict NEEDS WORK.
244
341
 
245
342
  ### Completion gate - loop until an independent pass on fresh evidence
246
343
 
@@ -255,13 +352,18 @@ If any page fails, you are not done - but treat the two blocker kinds differentl
255
352
  ```markdown
256
353
  # Visual QA - Verdict: GOOD | NEEDS WORK
257
354
 
355
+ Captured: {N pages} x {390, 1440[, 1920]} x {light, dark} + scrolled-end + motion frames, fresh as of {build}. Pages: {ids}.
356
+
357
+ | Item | Verdict | Capture | Finding |
358
+ |---|---|---|---|
359
+ | A1 ... J2 | PASS / FAIL / N/A | path#region | [product|evidence] what, where, fix - empty on PASS |
360
+
258
361
  | Dimension | Pass | Verdict | Evidence |
259
362
  |---|---|---|---|
260
363
  | Design system real vs faked | A | good/bad | ... |
261
364
  | Features work | A | good/bad | ... |
262
- | Responsive / resize | A | good/bad | ... |
263
365
  | Alpha / transparency | A+B | good/bad | ... |
264
- | Visual fidelity to intent | B | good/bad | ... |
366
+ | Visual fidelity to reference | B | good/bad | ... |
265
367
  | CJK precision | B | good/bad | ... |
266
368
 
267
369
  ## Must fix
@@ -8,13 +8,16 @@ const { loadOmowright } = await import("<browser-skill-root>/scripts/omowright.m
8
8
  const { omowright } = await loadOmowright()
9
9
  ```
10
10
 
11
- ## Owned engine (default for QA)
11
+ ## Owned engine (default for QA) - the capture matrix
12
12
 
13
13
  A browser your code launches, with a task-owned profile, pinned viewport and no user state.
14
- `connectPipe` opens no listening port and reaps the process on `close()`.
14
+ `connectPipe` opens no listening port and reaps the process on `close()`. One loop produces the
15
+ whole matrix the skill requires: phone (true mobile emulation) and desktop, light and dark, top
16
+ and scrolled-to-end, plus the reduced-motion variant for motion frames. `page.screenshot()` is a
17
+ PNG of the page itself, so no browser chrome can appear in it.
15
18
 
16
19
  ```js
17
- // js-eval cell; url and pngPath belong to this QA run.
20
+ // js-eval cell; urls and outDir belong to this QA run.
18
21
  const { mkdtempSync, rmSync } = await import("node:fs")
19
22
  const profile = mkdtempSync(`${(await import("node:os")).tmpdir()}/visual-qa-`)
20
23
  const browser = await omowright.connectPipe({
@@ -22,13 +25,29 @@ const browser = await omowright.connectPipe({
22
25
  browserArgs: ["--headless", "--no-first-run", `--user-data-dir=${profile}`],
23
26
  storageRoot: profile,
24
27
  })
28
+ const media = (page, sid, scheme, motion) => page.cdp.send("Emulation.setEmulatedMedia", {
29
+ features: [{ name: "prefers-color-scheme", value: scheme }, { name: "prefers-reduced-motion", value: motion }],
30
+ }, sid)
31
+ let page
25
32
  try {
26
- const page = await browser.newTab("about:blank")
27
- await omowright.emulate(page, { width: 1280, height: 720, deviceScaleFactor: 1, mobile: false, hasTouch: false })
28
- await page.goto(url, { waitUntil: "load" })
29
- await Bun.write(pngPath, await page.screenshot())
30
- console.log(pngPath)
33
+ page = await browser.newTab("about:blank")
34
+ const sid = await page.resolveSessionId()
35
+ for (const [route, url] of Object.entries(urls)) {
36
+ for (const device of ["iphone-14", "desktop-1440"]) { // 390 @ DPR 3, mobile + touch (overlay scrollbars); 1440 @ DPR 1. Add { width: 1920, height: 1080, deviceScaleFactor: 1, mobile: false, hasTouch: false } for page layouts.
37
+ await omowright.emulate(page, device)
38
+ for (const scheme of ["light", "dark"]) {
39
+ await media(page, sid, scheme, "no-preference")
40
+ await page.goto(url, { waitUntil: "load" }) // then wait for the page's own ready state (a locator, waitForURL, network snoop), never a sleep
41
+ await Bun.write(`${outDir}/${route}-${device}-${scheme}-top.png`, await page.screenshot())
42
+ await page.evaluate(() => window.scrollTo(0, document.documentElement.scrollHeight))
43
+ await Bun.write(`${outDir}/${route}-${device}-${scheme}-end.png`, await page.screenshot())
44
+ }
45
+ }
46
+ }
47
+ // Motion frames: drive the state (hover/press/open), then capture rest, ~100 ms and settled; repeat under
48
+ // media(page, sid, scheme, "reduce") for the reduced-motion pass.
31
49
  } finally {
50
+ if (page) await omowright.emulate(page, null)
32
51
  await browser.close()
33
52
  rmSync(profile, { recursive: true, force: true })
34
53
  }
@@ -48,14 +67,19 @@ cloning its profile:
48
67
  const session = await omowright.connectBrowserSkill({ name: "visual-qa capture", focused: false })
49
68
  try {
50
69
  await session.navigate(url, { waitUntil: "load" })
51
- await session.resize(1280, 720)
52
- const shot = await session.screenshot() // { buffer, width, height }
53
- await Bun.write(pngPath, shot.buffer)
70
+ await session.resize(1440, 900) // desktop column of the matrix
71
+ await Bun.write(`${outDir}/${route}-1440-top.png`, (await session.screenshot()).buffer) // { buffer, width, height }
72
+ await session.emulate({ overrides: { width: 390, mobile: true } }) // phone column; `off: true` restores
73
+ await Bun.write(`${outDir}/${route}-390-top.png`, (await session.screenshot()).buffer)
54
74
  } finally {
75
+ await session.emulate({ off: true })
55
76
  await session.stop()
56
77
  }
57
78
  ```
58
79
 
80
+ The attached engine follows the signed-in browser's own colour scheme; switch the OS or browser
81
+ theme between the light and dark passes, and record which theme each file carries.
82
+
59
83
  NEVER launch anything against, or clear cookies/cache/site data from, the user's live profile;
60
84
  the attached engine is the only sanctioned way to a signed-in page. If no extension is connected,
61
85
  run the `browser` skill's `scripts/browser-install.mjs` for the browser the user actually uses
@@ -63,11 +87,12 @@ run the `browser` skill's `scripts/browser-install.mjs` for the browser the user
63
87
  one human step, and wait — do
64
88
  not fall back to the owned engine for an authenticated criterion.
65
89
 
66
- ## Capture a screenshot at a fixed viewport
90
+ ## Capture at a fixed viewport
67
91
 
68
92
  Match CSS viewport AND PNG dimensions: pin `deviceScaleFactor` through `emulate` (owned) or
69
- `resize` (attached) instead of resizing the PNG to force a pass. Wait for the specific page state
70
- (a locator, a `waitForURL`, a `createNetworkSnoop(page).waitFor(...)`), not a sleep, then compare:
93
+ `resize` (attached) instead of resizing the PNG to force a pass; a 390-wide capture at desktop
94
+ DPR with classic scrollbars is not a phone capture. Wait for the specific page state (a locator,
95
+ a `waitForURL`, a `createNetworkSnoop(page).waitFor(...)`), not a sleep, then compare:
71
96
 
72
97
  ```sh
73
98
  node "$SKILL_DIR/scripts/visual-qa.mjs" image-diff reference.png actual.png