lazycodex-ai 4.16.0 → 4.16.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.
Files changed (112) hide show
  1. package/dist/cli/codex-ulw-loop.d.ts +8 -0
  2. package/dist/cli/get-local-version/types.d.ts +1 -1
  3. package/dist/cli/index.js +260 -207
  4. package/dist/cli-node/index.js +260 -207
  5. package/docs/reference/web-terminal-visual-qa.md +39 -45
  6. package/package.json +3 -2
  7. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  8. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  9. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  10. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  12. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  14. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  16. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  17. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  18. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  19. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  20. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  21. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +2 -2
  22. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  23. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  25. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/ultrawork/directive.md +18 -10
  29. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  30. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +18 -10
  32. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +11 -0
  33. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +22 -1
  34. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +4 -0
  35. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +18 -10
  36. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +15 -2
  37. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +2 -1
  38. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +89 -27
  39. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +6 -0
  40. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +55 -9
  41. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.d.ts +15 -0
  42. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.js +33 -0
  43. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +10 -3
  44. package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +15 -11
  45. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
  46. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
  48. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +7 -7
  49. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +17 -2
  50. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +2 -1
  51. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +59 -11
  52. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-snapshot.ts +38 -0
  53. package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +11 -3
  54. package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +15 -7
  55. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +16 -0
  56. package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +260 -2
  57. package/packages/omo-codex/plugin/components/ulw-loop/test/steering-snapshot.test.ts +124 -0
  58. package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +101 -2
  59. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  78. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  80. package/packages/omo-codex/plugin/package-lock.json +13 -13
  81. package/packages/omo-codex/plugin/package.json +1 -1
  82. package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +10 -6
  83. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +9 -9
  84. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +7 -3
  85. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +1 -1
  86. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +24 -2
  87. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/README.md +2 -2
  88. package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
  89. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -2
  90. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +18 -10
  91. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
  92. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +7 -7
  93. package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -0
  94. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +13 -17
  95. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +4 -4
  96. package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +1 -1
  97. package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +22 -9
  98. package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
  99. package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +52 -0
  100. package/packages/omo-codex/scripts/install-dist/install-local.mjs +65 -39
  101. package/packages/shared-skills/skills/frontend/SKILL.md +9 -9
  102. package/packages/shared-skills/skills/frontend/references/design/README.md +7 -3
  103. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +1 -1
  104. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +24 -2
  105. package/packages/shared-skills/skills/frontend/references/designpowers/README.md +2 -2
  106. package/packages/shared-skills/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
  107. package/packages/shared-skills/skills/start-work/SKILL.md +2 -2
  108. package/packages/shared-skills/skills/visual-qa/SKILL.md +13 -17
  109. package/script/qa/strip-ansi.mjs +10 -0
  110. package/script/qa/web-terminal-visual-qa.mjs +112 -195
  111. package/script/qa/xterm-live-terminal.mjs +180 -0
  112. package/script/qa/web-terminal-renderer.mjs +0 -218
@@ -16,11 +16,19 @@ Every frontend project MUST have a `DESIGN.md` at its root. This file is the sin
16
16
 
17
17
  ## DESIGN.md Structure
18
18
 
19
- The file has 7 sections. Every section is mandatory. Skip nothing.
19
+ The file has 8 sections plus a greenfield-only `## 0. Research Log`. Every section is mandatory. Skip nothing.
20
20
 
21
21
  ```markdown
22
22
  # [Project Name] Design System
23
23
 
24
+ ## 0. Research Log (greenfield only)
25
+
26
+ One line per research lane, written before the sections below — a lane with no line did not run:
27
+ - Embedded refs: shortlisted [2-3 Layer B candidates] → picked [Layer A] + [Layer B] because [reason]
28
+ - Lazyweb: [N] queries, [M] screens viewed → [layout grammar taken]
29
+ - Imagen drafts: [paths] → picked [draft] as the reference-fidelity contract
30
+ - Skipped lanes: [lane] — [tool/network reason]
31
+
24
32
  ## 1. Atmosphere & Identity
25
33
 
26
34
  One paragraph. What this product FEELS like. Not what it does — how it feels to use.
@@ -165,6 +173,19 @@ If borders:
165
173
 
166
174
  If tonal-shift:
167
175
  Surfaces use progressively lighter/darker shades. No borders, no shadows.
176
+
177
+ ## 8. Accessibility Constraints & Accepted Debt
178
+
179
+ ### Constraints
180
+ - WCAG target: [e.g. 2.2 AA] — contrast floor [4.5:1 body / 3:1 large text], visible focus on every
181
+ interactive element, full keyboard reachability, `prefers-reduced-motion` respected (Section 6).
182
+
183
+ ### Accepted Debt
184
+ | Item | Location | Why accepted | Owner / Exit |
185
+ |------|----------|--------------|--------------|
186
+ | [debt] | [file/screen] | [reason + user sign-off] | [when it gets fixed] |
187
+
188
+ New debt is recorded here at the moment it is accepted — never silently.
168
189
  ```
169
190
 
170
191
  ## Creation Workflow
@@ -173,7 +194,7 @@ Surfaces use progressively lighter/darker shades. No borders, no shadows.
173
194
 
174
195
  1. **Select references before taste** — no visual reference means `_INDEX.md` shortlist of 2-3 Layer B candidates, then exactly one Layer A style skill and one Layer B brand/design-system reference. Use `open-design` only when the curated set has no fit.
175
196
  2. **Assemble from references** — extract tokens, layout grammar, component anatomy, states, motion, and taste decisions, then recombine them into project-specific primitives. Customize for the user's product; never copy logos, trademarked assets, or brand-specific copy.
176
- 3. **Define the system** — atmosphere, palette, typography, spacing, and one depth strategy, grounded in the selected references and product semantics.
197
+ 3. **Define the system** — atmosphere, palette, typography, spacing, and one depth strategy, grounded in the selected references and product semantics. Sanity-check the palette and type pairing with one `ui-ux-db` domain search (CLI in `references/ui-ux-db/README.md`).
177
198
  4. **Document initial primitives** — only components you are about to build, including variants and states.
178
199
  5. **Write it to `DESIGN.md`** at project root.
179
200
  6. **Build a primitive showcase first** — exercise each primitive's default, hover, active, focus, disabled, loading, empty, and error states at mobile/tablet/desktop widths before composing product screens.
@@ -199,6 +220,7 @@ After every component implementation, check:
199
220
  - [ ] Component reused 2+ times? Documented in Section 5.
200
221
  - [ ] Motion follows the timing table. No arbitrary durations.
201
222
  - [ ] Component visual QA passed for each primitive and required state before product screens were composed.
223
+ - [ ] Section 8 accessibility constraints hold for the new component; any new debt is recorded in Section 8, not silently accepted.
202
224
 
203
225
  ## Memory Management
204
226
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  This is an internal frontend ruleset, not a standalone skill. `/frontend` remains the only public activation point for web UI, UX, visual design, accessibility, design QA, and frontend implementation routing.
4
4
 
5
- Load this reference from the frontend router when a task needs design operating-layer guidance: personas, cognitive accessibility, critique, design debt, handoff, synthetic user testing, motion guidance, or designpowers-style role references.
5
+ Load this reference from the frontend router for EVERY implementation or redesign that creates or updates `DESIGN.md`, and whenever a task needs design operating-layer guidance: personas, cognitive accessibility, critique, design debt, handoff, synthetic user testing, motion guidance, or designpowers-style role references.
6
6
 
7
7
  The purpose of this ruleset is to enrich the existing frontend workflow while preserving its gates:
8
8
 
@@ -18,7 +18,7 @@ Read these files before applying designpowers guidance:
18
18
  1. `README.md` - this frontend integration contract.
19
19
  2. `routing.md` - how designpowers context feeds existing frontend, planning, execution, visual QA, and review routes.
20
20
  3. `orchestration.md` - shared state, Direct/Auto prompt semantics, safeguards, and role-reference rules.
21
- 4. Phase lane docs, loaded only when relevant:
21
+ 4. Phase lane docs `lane-c-review.md` loads with this README for every implementation or redesign heading into Phase Final review (it is the flatness/critique reviewer); the other lanes load only when their phase applies:
22
22
  - `lane-a-direction.md` for planning, direction, discovery, personas, taste, and accessibility constraints.
23
23
  - `lane-b-execution.md` for execution, UI build prompts, frontend handoff, and implementation evidence.
24
24
  - `lane-c-review.md` for visual QA, design critique, review gates, and objective evidence before judgment.
@@ -44,7 +44,7 @@ Lane B worker DoneClaims must include:
44
44
 
45
45
  - Exact changed files and the `frontend` references loaded.
46
46
  - The real-surface QA invocation required by `start-work` for the UI surface, with captured artifact path.
47
- - Screenshot, browser, HTTP, or tmux artifacts appropriate to the visible surface.
47
+ - Screenshot, browser, HTTP, or xterm.js web-terminal artifacts appropriate to the visible surface.
48
48
  - Accessibility evidence from the existing OpenAgent frontend path, such as Lighthouse, react-doctor, keyboard checks, or other plan-required checks.
49
49
  - A short design trace: which persona, design principle, token, or state requirement each major UI decision satisfies.
50
50
  - Cleanup receipts for any browser session, server, tmux session, temporary artifact, or process used during QA.
@@ -108,10 +108,10 @@ Each sub-task message must include:
108
108
  4. Automated verification commands to run.
109
109
  5. One Manual-QA channel, named with the exact tool and exact invocation (the literal `curl`, `send-keys`, `browser:control-in-app-browser` action, `page.click`, payload, selectors, and the binary observable that decides PASS/FAIL), not "verify it works". A LIGHT checkbox needs one real-surface proof of its deliverable, and auxiliary surfaces (CLI stdout, DB state diff, parsed config dump) are first-class when the surface is CLI- or data-shaped:
110
110
  - HTTP call: `curl -i` against the live endpoint.
111
- - tmux: a `tmux` session driven with `send-keys`, dumped via `capture-pane`.
111
+ - Terminal / TUI: drive a real pty; `tmux send-keys` is fine for a boot/behavior smoke, but color/layout/CJK evidence goes through the xterm.js web terminal below, NEVER `tmux capture-pane`.
112
112
  - Browser use: in Codex, use `browser:control-in-app-browser` first when available and the scenario does not need an authenticated or persistent user browser profile; otherwise drive the real page with Chrome, or agent-browser (https://github.com/vercel-labs/agent-browser) when Chrome is unavailable.
113
113
  - Computer use: OS-level GUI automation against the running desktop app when the surface is not a page.
114
- - TUI visual evidence: when a tmux/TUI claim needs visual QA or PR proof, run `node script/qa/web-terminal-visual-qa.mjs --from-file <capture.txt> --evidence-dir <dir>` and attach `terminal.png` plus `metadata.json`.
114
+ - TUI visual evidence: when a TUI claim needs visual QA or PR proof, run `node script/qa/web-terminal-visual-qa.mjs --command "<cmd>" --input "{Enter}" --evidence-dir <dir>` (real pty rendered through xterm.js in Chrome) and attach `terminal.png` plus `metadata.json`.
115
115
  6. The adversarial classes that apply to this sub-task (from the 9 ultraqa classes) and how each is probed.
116
116
  7. Required artifact path and cleanup receipt.
117
117
 
@@ -56,9 +56,10 @@ exercises the surface; capture the artifact.
56
56
  1. HTTP call — hit the live endpoint with `curl -i` (or a
57
57
  Playwright APIRequestContext); capture status line + headers +
58
58
  body.
59
- 2. tmux `tmux new-session -d -s ulw-qa-<criterion>`, drive with
60
- `send-keys`, dump via `tmux capture-pane -pS -E -`; transcript
61
- is the artifact.
59
+ 2. Terminal / TUI - drive a real pty and prove it through the
60
+ xterm.js web terminal (see the TUI visual QA note below). tmux
61
+ `send-keys` is fine for a boot smoke; NEVER `tmux capture-pane`
62
+ for color / layout / CJK evidence, which degrades truecolor.
62
63
  3. Browser use — in Codex, use `browser:control-in-app-browser`
63
64
  first when available and no authenticated/persistent user browser
64
65
  profile is required. Otherwise use Chrome to drive the REAL page;
@@ -86,13 +87,13 @@ channel scenario when the behavior is user-facing. `--dry-run`,
86
87
  printing the command, "should respond", and "looks correct" never
87
88
  count.
88
89
 
89
- For TUI visual QA, terminal transcripts alone are not enough when a
90
- visual surface is being evaluated. In this repo, prefer
91
- `node script/qa/web-terminal-visual-qa.mjs --title "<surface>" --from-file <capture.txt> --evidence-dir <dir>`
92
- or the helper's `--command` tmux-backed PTY connector when available.
93
- Outside this repo, capture equivalent browser/computer-use rendered
94
- terminal evidence: screenshot, plain transcript, rendered HTML or action
95
- log, and cleanup receipt.
90
+ For TUI visual QA, render the terminal through the real xterm.js web
91
+ terminal and screenshot it - never a `tmux capture-pane` dump, which
92
+ degrades color and wide-glyph width. In this repo:
93
+ `node script/qa/web-terminal-visual-qa.mjs --title "<surface>" --command "<cmd>" --input "{Enter}" --evidence-dir <dir>`
94
+ (live pty + xterm.js in Chrome; `--from-file <capture>` replays a raw
95
+ stream). Outside this repo, capture equivalent browser-rendered terminal
96
+ evidence: screenshot + plain transcript + cleanup receipt.
96
97
 
97
98
  # Bootstrap (DO ALL FOUR BEFORE ANY OTHER WORK — NO SKIPPING)
98
99
 
@@ -294,6 +295,13 @@ evidence for that step. Do not start dependent implementation until the
294
295
  audit, research, or review result is integrated or explicitly recorded
295
296
  as inconclusive. Do not generate a plan before spawned research lanes
296
297
  that feed the plan have returned or been closed as inconclusive.
298
+ Spawn every independent child for the current wave first. After the wave
299
+ is launched, run `multi_agent_v1.wait_agent` for each spawned child until
300
+ each reaches terminal status (`completed`, `failed`, `blocked`, or
301
+ explicitly recorded inconclusive) before any dependent `update_plan`
302
+ transition, `create_goal` continuation, implementation tool call, plan
303
+ drafting, approval-gate work, PR handoff, or final response. A timeout is
304
+ not terminal status.
297
305
  Do not write the final answer, PR handoff, or completion summary while
298
306
  active child agents remain open. Use short `multi_agent_v1.wait_agent` cycles.
299
307
  After two silent waits send `TASK STILL ACTIVE: return <deliverable> or
@@ -22,7 +22,7 @@ This skill is intentionally compact. The full workflow lives in `references/full
22
22
  - Use the ulw-loop CLI state under `.omo/ulw-loop`; do not hand-edit goal state.
23
23
  - After any compaction or context loss, re-read brief + goals + ledger FIRST plus `omo ulw-loop status --json`, then resume; never re-plan from scratch.
24
24
  - If `omo ulw-loop create-goals` says the existing aggregate is already complete, start unrelated new work with a fresh `--session-id <new-id>` instead of steering or forcing the completed default state. Use `--force` only to intentionally overwrite completed evidence.
25
- - Every success criterion needs observable evidence from a real surface: a channel (tmux, HTTP, browser, computer-use) or, for CLI- or data-shaped criteria, an auxiliary surface (CLI stdout, DB diff, parsed config dump).
25
+ - Every success criterion needs observable evidence from a real surface: a channel (terminal/TUI via the xterm.js web terminal, HTTP, browser, computer-use) or, for CLI- or data-shaped criteria, an auxiliary surface (CLI stdout, DB diff, parsed config dump).
26
26
  - Record evidence through the CLI only after cleanup receipts are available.
27
27
  - Delegate code edits, test writes, fixes, and QA execution to right-sized Codex subagents when the workflow requires it.
28
28
  - Every `multi_agent_v1.spawn_agent` message starts with `TASK:`, then names `DELIVERABLE`, `SCOPE`, and `VERIFY`; put role and specialty instructions inside `message`; use `fork_context: false` unless full history is truly required.
@@ -19,16 +19,16 @@ Audit each pass, fail, block, steering change, and checkpoint in `.omo/ulw-loop/
19
19
  Run each criterion's real-surface proof yourself through the channel that faithfully exercises it; capture the artifact before recording PASS.
20
20
 
21
21
  1. **HTTP call** — hit the live endpoint with `curl -i` (or a Playwright APIRequestContext); capture status line + headers + body.
22
- 2. **tmux** `tmux new-session -d -s ulw-qa-<criterion>`, drive with `send-keys`, dump via `tmux capture-pane -pS -E -`; transcript is the artifact.
22
+ 2. **Terminal / TUI** - prove it through the xterm.js web terminal; tmux `send-keys` is fine for a boot smoke, but NEVER `tmux capture-pane` for color/layout/CJK evidence (it degrades truecolor).
23
23
  3. **Browser use** — in Codex, use `browser:control-in-app-browser` first when available and the scenario does not need an authenticated or persistent user browser profile. Otherwise use Chrome to drive the REAL page; if unavailable, use agent-browser. Capture action log + screenshot path. Never downgrade a browser-facing criterion.
24
24
  4. **Computer use** — for desktop/GUI apps, drive the running app via OS automation (computer-use, AppleScript, xdotool, etc.); capture action log + screenshot.
25
25
 
26
- For TUI visual QA, pair the tmux transcript with a browser-rendered terminal
27
- screenshot. In this repo run `node script/qa/web-terminal-visual-qa.mjs
28
- --from-file <capture.txt> --evidence-dir <dir>` and record `terminal.png`,
29
- `terminal.html`, `terminal.txt`, and `metadata.json` as the visual evidence
30
- bundle. This is mandatory when a PR or review needs to inspect the terminal
31
- screen, not just the text.
26
+ For TUI visual QA, render the terminal through the real xterm.js web terminal and
27
+ screenshot it - NEVER a `tmux capture-pane` dump (it degrades color and wide-glyph
28
+ width). In this repo run `node script/qa/web-terminal-visual-qa.mjs --command
29
+ "<cmd>" --input "{Enter}" --evidence-dir <dir>` (live pty + xterm.js in Chrome;
30
+ `--from-file` replays a raw stream) and record `terminal.png`, `terminal.txt`, and
31
+ `metadata.json`. Mandatory when a PR or review must inspect the terminal screen.
32
32
 
33
33
  Auxiliary surfaces (CLI stdout / DB state diff / parsed config dump) are first-class evidence for CLI- or data-shaped criteria; use a channel scenario when the behavior is user-facing. `--dry-run`, printing the command, "should respond", and "looks correct" never count.
34
34
 
@@ -67,6 +67,17 @@ Fan out read-only research before deciding. Every spawn names DELIVERABLE / SCOP
67
67
  multi_agent_v1.spawn_agent({"message":"TASK: act as an explorer. DELIVERABLE: ... SCOPE: ... VERIFY: ...","agent_type":"explorer","fork_context":false})
68
68
  ```
69
69
 
70
+ Spawn every independent child for the current wave first. After the wave
71
+ is launched, use `multi_agent_v1.wait_agent` for each child until each
72
+ reaches terminal status. A timeout is not terminal status. Do not start dependent planning, drafting, approval-gate work, or final handoff until each child result is integrated or recorded as inconclusive.
73
+
74
+ For work likely to exceed one wait cycle, require the child to send
75
+ `WORKING: <task> - <current phase>` before long passes and
76
+ `BLOCKED: <reason>` only when progress stops. A `multi_agent_v1.wait_agent`
77
+ timeout only means no new mailbox update arrived. Treat a running child as
78
+ alive. Fallback only when the child is completed without the deliverable,
79
+ ack-only after followup, explicitly `BLOCKED:`, or no longer running.
80
+
70
81
  Roles: `explorer` (internal patterns/conventions/tests), `librarian` (external docs/contracts), `metis` (gap analysis), `momus` (high-accuracy plan review). Full spawn/wait/fallback discipline is in `references/full-workflow.md`.
71
82
 
72
83
  ## Stop rules
@@ -79,32 +79,28 @@ For reference-fidelity work, repeat the capture and diff for every referenced vi
79
79
 
80
80
  ### TUI
81
81
 
82
- 1. Capture plain text and an ANSI-preserving copy:
83
-
84
- ```
85
- tmux capture-pane -p > capture.txt
86
- tmux capture-pane -e -p > capture-ansi.txt
87
- ```
88
-
89
- 2. When the TUI evidence will be attached to a PR or reviewed visually, render
90
- the capture through the browser helper from the repository root:
82
+ 1. Render the TUI through the REAL xterm.js web terminal and screenshot it -
83
+ NEVER `tmux capture-pane`, which degrades truecolor and misaligns wide (CJK)
84
+ glyphs. Run the command in a real pty and capture the browser render from the
85
+ repository root:
91
86
 
92
87
  ```
93
88
  node script/qa/web-terminal-visual-qa.mjs --title "TUI Visual QA" \
94
- --from-file capture.txt \
89
+ --command "<tui-command>" \
90
+ --input "{ArrowDown}" --input "{Enter}" \
95
91
  --evidence-dir .omo/evidence/<slug>/tui-web-terminal
96
92
  ```
97
93
 
98
- This produces `terminal.png`, `terminal.html`, `terminal.txt`,
99
- `terminal-ansi.txt`, and `metadata.json`. Treat this as the standard TUI visual
100
- artifact pattern for terminal screenshots. If the project is outside this repo,
101
- copy the same pattern: terminal capture -> browser-rendered page -> PNG +
102
- metadata with cleanup receipt.
94
+ Replay a saved raw stream with `--from-file <capture.ansi>` instead of
95
+ `--command`. This produces `terminal.png` (the true-color artifact),
96
+ `terminal.txt`, `terminal-ansi.txt`, and `metadata.json`. Treat this as the
97
+ standard TUI visual artifact pattern. Outside this repo, copy the pattern:
98
+ real pty -> xterm.js in a browser -> PNG + metadata with cleanup receipt.
103
99
 
104
- 3. Run the check with the REAL terminal width and keep the JSON:
100
+ 2. Run the width check on the produced text and keep the JSON:
105
101
 
106
102
  ```
107
- node "$SKILL_DIR/scripts/visual-qa.mjs" tui-check capture.txt --cols <N>
103
+ node "$SKILL_DIR/scripts/visual-qa.mjs" tui-check .omo/evidence/<slug>/tui-web-terminal/terminal.txt --cols <N>
108
104
  ```
109
105
 
110
106
  Key fields: `maxWidth`, `overflowLines[]`, `borderMisaligned`, `wideCharColumns[]`, `hasAnsi`.
@@ -68,7 +68,7 @@ test("#given aggregate SubagentStop hooks #when inspected #then start-work and L
68
68
  assert.equal(verifierGroups.length, 1);
69
69
  assert.equal(verifierGroups[0]?.groupIndex, 0);
70
70
  assert.equal(verifierGroups[0]?.handler.timeout, 10);
71
- assert.equal(verifierGroups[0]?.handler.statusMessage, "(OmO) Verifying LazyCodex Executor Evidence");
71
+ assert.match(verifierGroups[0]?.handler.statusMessage ?? "", /^\(OmO [^)]+\) Verifying LazyCodex Executor Evidence$/);
72
72
  });
73
73
 
74
74
  test("#given aggregate PostCompact hooks #when hooks are inspected #then LSP diagnostics cache reset is registered", async () => {
@@ -84,7 +84,7 @@ test("#given aggregate PostCompact hooks #when hooks are inspected #then LSP dia
84
84
 
85
85
  // then
86
86
  assert.equal(lspPostCompactHooks.length, 1);
87
- assert.equal(lspPostCompactHooks[0]?.handler.statusMessage, "(OmO) Resetting LSP Diagnostics Cache");
87
+ assert.match(lspPostCompactHooks[0]?.handler.statusMessage ?? "", /^\(OmO [^)]+\) Resetting LSP Diagnostics Cache$/);
88
88
  });
89
89
 
90
90
  test("#given aggregate hook commands #when inspected #then every command exposes a Codex status message", async () => {
@@ -218,7 +218,7 @@ test("#given aggregate PostToolUse hooks #when inspected #then CodeGraph init gu
218
218
  // then
219
219
  assert.equal(codegraphPostToolUseHooks.length, 1);
220
220
  assert.equal(codegraphPostToolUseHooks[0]?.matcher, "^(codegraph[._].*|mcp__codegraph__.*)$");
221
- assert.equal(codegraphPostToolUseHooks[0]?.handler.statusMessage, "(OmO) Checking CodeGraph Init Guidance");
221
+ assert.match(codegraphPostToolUseHooks[0]?.handler.statusMessage ?? "", /^\(OmO [^)]+\) Checking CodeGraph Init Guidance$/);
222
222
  });
223
223
 
224
224
  test("#given aggregate PostToolUse hooks #when inspected #then thread title hygiene is registered for created Codex threads", async () => {
@@ -235,7 +235,7 @@ test("#given aggregate PostToolUse hooks #when inspected #then thread title hygi
235
235
  // then
236
236
  assert.equal(threadTitleHooks.length, 1);
237
237
  assert.equal(threadTitleHooks[0]?.matcher, "^(create_thread|codex_app\\.create_thread)$");
238
- assert.equal(threadTitleHooks[0]?.handler.statusMessage, "(OmO) Checking Thread Title Hygiene");
238
+ assert.match(threadTitleHooks[0]?.handler.statusMessage ?? "", /^\(OmO [^)]+\) Checking Thread Title Hygiene$/);
239
239
  });
240
240
 
241
241
  test("#given aggregate plugin packaging #when inspected #then hooks and compatibility sentinels stay Python-free", async () => {
@@ -171,7 +171,7 @@ test("#given the bootstrap component #when its SessionStart registration is insp
171
171
  assert.equal(typeof handler.timeout, "number");
172
172
  assert(handler.timeout <= 60, `${label} bootstrap timeout must stay <= 60 seconds`);
173
173
  assert.equal(typeof handler.statusMessage, "string");
174
- assert.match(handler.statusMessage, /^\(OmO\) .+$/);
174
+ assert.match(handler.statusMessage, /^\(OmO [^)]+\) .+$/);
175
175
  }
176
176
  });
177
177
 
@@ -128,7 +128,7 @@ function collectCommandHooks(hooks, source, version) {
128
128
  return commandHooks;
129
129
  }
130
130
 
131
- test("#given hook status label #when formatting #then prefixes OmO display namespace", async () => {
131
+ test("#given hook status label #when formatting #then prefixes OmO display namespace and version", async () => {
132
132
  // given
133
133
  const version = (await readRepoJson("package.json")).version;
134
134
  const label = "Checking Comments";
@@ -137,10 +137,10 @@ test("#given hook status label #when formatting #then prefixes OmO display names
137
137
  const message = formatLazyCodexHookStatusMessage(version, label);
138
138
 
139
139
  // then
140
- assert.equal(message, "(OmO) Checking Comments");
140
+ assert.equal(message, `(OmO ${version}) Checking Comments`);
141
141
  });
142
142
 
143
- test("#given hook status label with blank version #when formatting #then still prefixes OmO display namespace", () => {
143
+ test("#given hook status label with blank version #when formatting #then uses local OmO display version", () => {
144
144
  // given
145
145
  const version = " ";
146
146
  const label = "Checking Comments";
@@ -149,7 +149,7 @@ test("#given hook status label with blank version #when formatting #then still p
149
149
  const message = formatLazyCodexHookStatusMessage(version, label);
150
150
 
151
151
  // then
152
- assert.equal(message, "(OmO) Checking Comments");
152
+ assert.equal(message, "(OmO local) Checking Comments");
153
153
  });
154
154
 
155
155
  test("#given loose legacy status label #when normalizing #then removes OMO wording and title-cases label", async () => {
@@ -163,7 +163,7 @@ test("#given loose legacy status label #when normalizing #then removes OMO wordi
163
163
 
164
164
  // then
165
165
  assert.equal(normalized, "Checking Comments");
166
- assert.equal(message, "(OmO) Checking Comments");
166
+ assert.equal(message, `(OmO ${version}) Checking Comments`);
167
167
  });
168
168
 
169
169
  test("#given LazyCodex appears inside hook label #when normalizing #then product casing is preserved", async () => {
@@ -177,7 +177,7 @@ test("#given LazyCodex appears inside hook label #when normalizing #then product
177
177
 
178
178
  // then
179
179
  assert.equal(normalized, "Verifying LazyCodex Executor Evidence");
180
- assert.equal(message, "(OmO) Verifying LazyCodex Executor Evidence");
180
+ assert.equal(message, `(OmO ${version}) Verifying LazyCodex Executor Evidence`);
181
181
  });
182
182
 
183
183
  test("#given MCP appears inside hook label #when normalizing #then protocol casing is preserved", () => {
@@ -190,7 +190,20 @@ test("#given MCP appears inside hook label #when normalizing #then protocol casi
190
190
 
191
191
  // then
192
192
  assert.equal(normalized, "Recommending Git Bash MCP");
193
- assert.equal(message, "(OmO) Recommending Git Bash MCP");
193
+ assert.equal(message, "(OmO 4.10.0) Recommending Git Bash MCP");
194
+ });
195
+
196
+ test("#given versioned OmO status label #when normalizing #then it does not duplicate display prefix", () => {
197
+ // given
198
+ const label = "(OmO 4.16.0) checking comments";
199
+
200
+ // when
201
+ const parsed = parseLazyCodexHookStatusMessage(label);
202
+ const message = formatLazyCodexHookStatusMessage("4.16.1", label);
203
+
204
+ // then
205
+ assert.deepEqual(parsed, { version: "4.16.0", label: "checking comments" });
206
+ assert.equal(message, "(OmO 4.16.1) Checking Comments");
194
207
  });
195
208
  test("#given aggregate comment-checker hook #when status is inspected #then it uses OmO comments label", async () => {
196
209
  // given
@@ -201,7 +214,7 @@ test("#given aggregate comment-checker hook #when status is inspected #then it u
201
214
  const commentCheckerHook = hooks.find((hook) => hook.command.includes("components/comment-checker/dist/cli.js"));
202
215
 
203
216
  // then
204
- assert.equal(commentCheckerHook?.statusMessage, formatLazyCodexHookStatusMessage("", "Checking Comments"));
217
+ assert.equal(commentCheckerHook?.statusMessage, formatLazyCodexHookStatusMessage(commentCheckerHook?.version ?? "", "Checking Comments"));
205
218
  assert.doesNotMatch(JSON.stringify(aggregateManifests), /checking\s+OMO\s+comments/i);
206
219
  });
207
220
 
@@ -231,6 +244,6 @@ test("#given aggregate and component hooks #when status messages are inspected #
231
244
  const actualLabels = new Set(commandHooks.map((hook) => parseLazyCodexHookStatusMessage(hook.statusMessage)?.label));
232
245
  assert.deepEqual([...expectedLabels.values()].filter((label) => !actualLabels.has(label)), []);
233
246
  for (const hook of commandHooks) {
234
- assert.match(hook.statusMessage, /^\(OmO\) /);
247
+ assert.match(hook.statusMessage, /^\(OmO [^)]+\) /);
235
248
  }
236
249
  });
@@ -88,10 +88,10 @@ test("#given source package versions and component without hooks #when hook stat
88
88
  const aggregateHooks = await readJson(join(root, "hooks", "hooks.json"));
89
89
  const componentHooks = await readJson(join(root, "components", "comment-checker", "hooks", "hooks.json"));
90
90
  const lspHooks = await readJson(join(root, "components", "lsp", "hooks", "hooks.json"));
91
- assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking Comments");
92
- assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[1].statusMessage, "(OmO) Checking LSP Diagnostics");
93
- assert.equal(componentHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking Comments");
94
- assert.equal(lspHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking LSP Diagnostics");
91
+ assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO 0.1.0) Checking Comments");
92
+ assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[1].statusMessage, "(OmO 0.1.0) Checking LSP Diagnostics");
93
+ assert.equal(componentHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO 0.1.1) Checking Comments");
94
+ assert.equal(lspHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO 0.2.0) Checking LSP Diagnostics");
95
95
  });
96
96
 
97
97
  test("#given release version override #when hook status messages sync #then aggregate hooks use release version", async () => {
@@ -139,6 +139,6 @@ test("#given release version override #when hook status messages sync #then aggr
139
139
  // then
140
140
  const aggregateHooks = await readJson(join(root, "hooks", "hooks.json"));
141
141
  const componentHooks = await readJson(join(root, "components", "comment-checker", "hooks", "hooks.json"));
142
- assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking Comments");
143
- assert.equal(componentHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO) Checking Comments");
142
+ assert.equal(aggregateHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO 4.8.0) Checking Comments");
143
+ assert.equal(componentHooks.hooks.PostToolUse[0].hooks[0].statusMessage, "(OmO 4.8.0) Checking Comments");
144
144
  });
@@ -0,0 +1,52 @@
1
+ import assert from "node:assert/strict";
2
+ import { readFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import test from "node:test";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const root = dirname(dirname(fileURLToPath(import.meta.url)));
8
+
9
+ async function readUlwPlanCopies() {
10
+ const componentPath = join(root, "components", "ultrawork", "skills", "ulw-plan", "SKILL.md");
11
+ const packagedPath = join(root, "skills", "ulw-plan", "SKILL.md");
12
+ return [
13
+ { label: "component", path: componentPath, content: await readFile(componentPath, "utf8") },
14
+ { label: "packaged", path: packagedPath, content: await readFile(packagedPath, "utf8") },
15
+ ];
16
+ }
17
+
18
+ test("#given ulw-plan skill #when Codex delegation is inspected #then spawned planners block dependent work", async () => {
19
+ for (const copy of await readUlwPlanCopies()) {
20
+ assert.match(copy.content, /multi_agent_v1\.spawn_agent/, `${copy.label}: must document Codex spawning`);
21
+ assert.match(copy.content, /multi_agent_v1\.wait_agent/, `${copy.label}: must document Codex waiting`);
22
+ assert.match(
23
+ copy.content,
24
+ /Spawn every independent child for the current wave first/i,
25
+ `${copy.label}: must preserve independent spawn waves`,
26
+ );
27
+ assert.match(
28
+ copy.content,
29
+ /After the wave\s+is launched[\s\S]{0,240}multi_agent_v1\.wait_agent[\s\S]{0,240}terminal status/i,
30
+ `${copy.label}: must wait after the wave is launched`,
31
+ );
32
+ assert.doesNotMatch(
33
+ copy.content,
34
+ /Immediately after any `multi_agent_v1\.spawn_agent`/i,
35
+ `${copy.label}: must not serialize independent spawns`,
36
+ );
37
+ assert.match(copy.content, /terminal status/i, `${copy.label}: must wait until terminal status`);
38
+ assert.match(copy.content, /WORKING:/, `${copy.label}: must keep progress liveness guidance`);
39
+ assert.match(copy.content, /BLOCKED:/, `${copy.label}: must keep blocked liveness guidance`);
40
+ assert.match(
41
+ copy.content,
42
+ /timeout only means no new mailbox update arrived/i,
43
+ `${copy.label}: must frame wait timeouts as mailbox silence`,
44
+ );
45
+ assert.match(copy.content, /Fallback only when/, `${copy.label}: must keep explicit fallback conditions`);
46
+ assert.match(
47
+ copy.content,
48
+ /do not start dependent planning, drafting, approval-gate work, or final handoff/i,
49
+ `${copy.label}: must block dependent planning work before child results are integrated`,
50
+ );
51
+ }
52
+ });