oh-my-opencode 4.17.1 → 4.18.0

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 (80) hide show
  1. package/.agents/skills/codex-qa/SKILL.md +2 -0
  2. package/dist/cli/get-local-version/types.d.ts +1 -1
  3. package/dist/cli/index.js +55 -25
  4. package/dist/cli-node/index.js +55 -25
  5. package/dist/index.js +23 -17
  6. package/dist/skills/frontend/SKILL.md +1 -1
  7. package/dist/skills/frontend/references/design/README.md +9 -0
  8. package/dist/skills/frontend/references/design/design-system-architecture.md +4 -2
  9. package/dist/skills/frontend/references/design/layout-skill.md +107 -0
  10. package/dist/skills/programming/SKILL.md +12 -2
  11. package/package.json +14 -13
  12. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  13. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  14. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  16. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  17. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  18. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  19. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  21. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  22. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  23. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +7 -5
  25. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  26. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  28. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  30. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  31. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  32. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  33. package/packages/omo-codex/plugin/components/ultrawork/directive.md +37 -10
  34. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  35. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  36. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +37 -10
  37. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +37 -10
  38. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  39. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
  41. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +4 -4
  42. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  65. package/packages/omo-codex/plugin/package-lock.json +13 -13
  66. package/packages/omo-codex/plugin/package.json +1 -1
  67. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +1 -1
  68. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +9 -0
  69. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +4 -2
  70. package/packages/omo-codex/plugin/skills/frontend/references/design/layout-skill.md +107 -0
  71. package/packages/omo-codex/plugin/skills/programming/SKILL.md +12 -2
  72. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +37 -10
  73. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
  74. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +4 -4
  75. package/packages/omo-codex/scripts/install-dist/install-local.mjs +24 -12
  76. package/packages/shared-skills/skills/frontend/SKILL.md +1 -1
  77. package/packages/shared-skills/skills/frontend/references/design/README.md +9 -0
  78. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +4 -2
  79. package/packages/shared-skills/skills/frontend/references/design/layout-skill.md +107 -0
  80. package/packages/shared-skills/skills/programming/SKILL.md +12 -2
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/omo-codex-plugin",
3
- "version": "4.17.1",
3
+ "version": "4.18.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sisyphuslabs/omo-codex-plugin",
9
- "version": "4.17.1",
9
+ "version": "4.18.0",
10
10
  "workspaces": [
11
11
  "components/codegraph",
12
12
  "components/comment-checker",
@@ -93,7 +93,7 @@
93
93
  },
94
94
  "components/codegraph": {
95
95
  "name": "@sisyphuslabs/codex-codegraph",
96
- "version": "4.17.1",
96
+ "version": "4.18.0",
97
97
  "bin": {
98
98
  "omo-codegraph": "dist/cli.js"
99
99
  },
@@ -112,7 +112,7 @@
112
112
  },
113
113
  "components/comment-checker": {
114
114
  "name": "@code-yeongyu/codex-comment-checker",
115
- "version": "4.17.1",
115
+ "version": "4.18.0",
116
116
  "license": "MIT",
117
117
  "bin": {
118
118
  "omo-comment-checker": "dist/cli.js"
@@ -133,7 +133,7 @@
133
133
  },
134
134
  "components/git-bash": {
135
135
  "name": "@sisyphuslabs/codex-git-bash-hook",
136
- "version": "4.17.1",
136
+ "version": "4.18.0",
137
137
  "bin": {
138
138
  "omo-git-bash-hook": "dist/cli.js"
139
139
  },
@@ -148,7 +148,7 @@
148
148
  },
149
149
  "components/lazycodex-executor-verify": {
150
150
  "name": "@code-yeongyu/codex-lazycodex-executor-verify",
151
- "version": "4.17.1",
151
+ "version": "4.18.0",
152
152
  "license": "MIT",
153
153
  "bin": {
154
154
  "lazycodex-executor-verify": "dist/cli.js"
@@ -165,7 +165,7 @@
165
165
  },
166
166
  "components/lsp": {
167
167
  "name": "@code-yeongyu/codex-lsp",
168
- "version": "4.17.1",
168
+ "version": "4.18.0",
169
169
  "license": "MIT",
170
170
  "dependencies": {
171
171
  "@code-yeongyu/lsp-daemon": "file:../../../../lsp-daemon"
@@ -185,7 +185,7 @@
185
185
  },
186
186
  "components/rules": {
187
187
  "name": "@code-yeongyu/codex-rules",
188
- "version": "4.17.1",
188
+ "version": "4.18.0",
189
189
  "license": "MIT",
190
190
  "dependencies": {
191
191
  "picomatch": "^4.0.3"
@@ -207,7 +207,7 @@
207
207
  },
208
208
  "components/start-work-continuation": {
209
209
  "name": "@code-yeongyu/codex-start-work-continuation",
210
- "version": "4.17.1",
210
+ "version": "4.18.0",
211
211
  "license": "MIT",
212
212
  "bin": {
213
213
  "omo-start-work-continuation": "dist/cli.js"
@@ -224,7 +224,7 @@
224
224
  },
225
225
  "components/teammode": {
226
226
  "name": "@sisyphuslabs/codex-teammode",
227
- "version": "4.17.1",
227
+ "version": "4.18.0",
228
228
  "devDependencies": {
229
229
  "@types/node": "^25.9.3",
230
230
  "bun-types": "^1.3.1",
@@ -237,7 +237,7 @@
237
237
  },
238
238
  "components/telemetry": {
239
239
  "name": "@code-yeongyu/codex-telemetry",
240
- "version": "4.17.1",
240
+ "version": "4.18.0",
241
241
  "license": "MIT",
242
242
  "bin": {
243
243
  "omo-telemetry": "dist/cli.js"
@@ -255,7 +255,7 @@
255
255
  },
256
256
  "components/ultrawork": {
257
257
  "name": "@code-yeongyu/codex-ultrawork",
258
- "version": "4.17.1",
258
+ "version": "4.18.0",
259
259
  "license": "MIT",
260
260
  "bin": {
261
261
  "omo-ultrawork": "dist/cli.js"
@@ -273,7 +273,7 @@
273
273
  },
274
274
  "components/ulw-loop": {
275
275
  "name": "@code-yeongyu/codex-ulw-loop",
276
- "version": "4.17.1",
276
+ "version": "4.18.0",
277
277
  "license": "MIT",
278
278
  "bin": {
279
279
  "omo-ulw-loop": "dist/cli.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/omo-codex-plugin",
3
- "version": "4.17.1",
3
+ "version": "4.18.0",
4
4
  "description": "Aggregate Codex plugin root for OMO components.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -32,7 +32,7 @@ Every implementation must choose one of these branches before UI code changes:
32
32
  - **Embedded references:** use `references/design/_INDEX.md` to shortlist 2-3 plausible Layer B references, then read exactly one Layer A style skill and one Layer B reference in full — every line, no partial reads (they are 200-500 lines; a sliced read produces the flattened token set this gate exists to prevent). Log the shortlist, the pick, and why. Use `open-design` only when the curated set has no fit; add `ui-ux-db` lookups for palette/type/domain questions.
33
33
  - **Lazyweb real-product screens:** READ `references/design/lazyweb.md` FIRST and run its recipe verbatim — do not improvise curl calls against lazyweb.com; the recipe mints its own anonymous token. Log the queries run, how many screens you actually VIEWED, and the layout grammar harvested — never pixel copies.
34
34
  - **Imagen concept drafts:** generate 2-3 imagen concept drafts, each seeded with the loaded Layer A + Layer B tokens (palette, type, material); pick the strongest and treat the chosen draft as the reference-fidelity contract. Log the draft paths and the pick.
35
- Synthesize every lane into `DESIGN.md`. Treat sources as source material, not mood labels: extract tokens, layout grammar, component anatomy, interaction states, motion, and taste decisions, then recombine them into project-specific primitives. Never freestyle past the selected references, never copy logos or brand-specific copy. Then run the Primitive Showcase Gate (`references/design/README.md` Phase 0) before any product screen.
35
+ Synthesize every lane into `DESIGN.md`. Treat sources as source material, not mood labels: extract tokens, layout grammar, component anatomy, interaction states, motion, and taste decisions, then recombine them into project-specific primitives. Before laying out sections, inventory the content blocks and assign each a job — hook, explain, prove, compare, convert, navigate, retain — then order sections by the visitor's decision path, not by visual symmetry. Never freestyle past the selected references, never copy logos or brand-specific copy. Then run the Primitive Showcase Gate (`references/design/README.md` Phase 0) before any product screen.
36
36
  3. **Existing project with `DESIGN.md` or a component system:** read it, follow it, and update it before implementation only when the requested work needs a new token, primitive, state, motion rule, accessibility constraint, accepted debt, or reference-fidelity requirement.
37
37
  4. **Existing project with UI but no `DESIGN.md` and no reusable component layer:** STOP and ask the user one focused question: should you preserve the current look with copy-nearby styling, or extract a real `DESIGN.md` plus reusable components before continuing? Do not silently choose.
38
38
 
@@ -169,6 +169,12 @@ Triggers (mid-conversation, not initial): "you keep leaving placeholders", "stop
169
169
 
170
170
  **Action:** Add `output-skill.md` on top of whatever is currently loaded. This stacks cleanly — it is purely about output completeness, not visual style.
171
171
 
172
+ ### Step 8 — Is the screen an app shell, not a scroll-the-page site?
173
+
174
+ Triggers: dashboard, settings, mail/inbox, list-detail, command surface, split panes, fixed sidebar + scrolling body, admin console — or the user reports a layout that breaks when content gets long, empty, or unbroken (panel won't scroll, footer pushed off-screen, horizontal overflow on mobile).
175
+
176
+ **Action:** Add `layout-skill.md` on top of whatever style skill you selected in Steps 1-2. It carries scroll-ownership doctrine, the two silently-failing CSS contracts (`min-block-size: 0` scroll shells, `minmax(min(…),100%)` grids), the named-primitive vocabulary, container-vs-viewport routing, and the content-stress matrix. It adds no visual direction — the style skill still owns taste. Skip it for pure scroll-the-document marketing pages, where `taste-skill` layout guidance already fits.
177
+
172
178
  ## Stacking rules (read this once, internalize it)
173
179
 
174
180
  1. **At most one Layer A *style* skill at a time.** A layout cannot be both `minimalist-skill` and `brutalist-skill` simultaneously — they encode opposite spacing and typography philosophies. Pick one.
@@ -177,6 +183,7 @@ Triggers (mid-conversation, not initial): "you keep leaving placeholders", "stop
177
183
  4. **`redesign-skill.md` replaces a style-skill** when the task is auditing, not building. Stack a Layer B brand if the user wants a specific direction.
178
184
  5. **`image-to-code-skill.md` pairs with one imagegen skill** for the full flow.
179
185
  6. **Layer B (brand DESIGN.md) is orthogonal to Layer A.** You can pair any Layer A skill with any Layer B brand. Use Layer B as the source of color/type/component tokens; let Layer A drive the execution discipline.
186
+ 7. **`layout-skill.md` stacks on top of any style skill** for app-shell / dashboard / split-pane work. It owns spatial structure and scroll ownership only — no visual direction — so it never conflicts with the style skill you picked.
180
187
 
181
188
  ## Anti-patterns — do not do these
182
189
 
@@ -218,6 +225,8 @@ Once references are loaded, before writing any UI code:
218
225
  | "Generate a brand identity board for {company}" | `imagegen-brandkit.md` |
219
226
  | "Stop using placeholders" | Add `output-skill.md` to current stack |
220
227
  | "Also output a DESIGN.md doc" | Add `stitch-skill.md` to current stack |
228
+ | "Build a dashboard / settings / inbox / app shell" | one style skill (usually `taste-skill.md`) + `layout-skill.md` |
229
+ | "Panel won't scroll / footer pushed off-screen / mobile overflow" | Add `layout-skill.md` to current stack |
221
230
 
222
231
  ## Phase Final — Design QA (MANDATORY, runs after implementation)
223
232
 
@@ -116,7 +116,7 @@ All spacing derives from a base of **4px**.
116
116
  - Breakpoints: sm 640px, md 768px, lg 1024px, xl 1280px, 2xl 1536px
117
117
 
118
118
  ### Rules
119
- - No magic numbers. Every spacing value maps to a token.
119
+ - Tokenize design *intent* spacing steps, content width, gutters, section gaps, density steps. Keep browser *mechanics* raw: `auto`, `%`, `min-content`, `max-content`, `fit-content`, `clamp()`, viewport/container units, intrinsic sizing. A `clamp(1rem, 4vw, 2rem)` gap or a `minmax(min(16rem, 100%), 1fr)` track is mechanics, not a magic number — do not force it into a token.
120
120
  - Asymmetric spacing is intentional, not accidental — document why.
121
121
 
122
122
  ## 5. Components
@@ -130,6 +130,7 @@ Document reusable patterns before implementation for greenfield work, and as the
130
130
  - **States**: default, hover, active, focus, disabled, loading, empty, error
131
131
  - **Accessibility**: keyboard, ARIA, contrast
132
132
  - **Motion**: entry/exit animations
133
+ - **Layout**: spatial primitive (stack / cluster / sidebar / grid / shell…) and, if the component scrolls or pins anything, its scroll owner
133
134
 
134
135
  Greenfield starts with the primitives you are about to build, assembled from
135
136
  the selected references' component anatomy and adapted to the user's product.
@@ -214,13 +215,14 @@ After every component implementation, check:
214
215
 
215
216
  - [ ] All colors reference tokens from Section 2. No raw hex outside `DESIGN.md`.
216
217
  - [ ] All font sizes match Section 3 scale. No arbitrary sizes.
217
- - [ ] All spacing values are multiples of `--space-1` (4px). No magic numbers.
218
+ - [ ] Spacing intent maps to a token (Section 4); browser mechanics (`clamp()`, intrinsic sizing, container units) stay raw. No arbitrary px.
218
219
  - [ ] Interactive elements have all required states from Section 5 and Section 6.
219
220
  - [ ] Depth treatment matches the chosen strategy from Section 7.
220
221
  - [ ] Component reused 2+ times? Documented in Section 5.
221
222
  - [ ] Motion follows the timing table. No arbitrary durations.
222
223
  - [ ] Component visual QA passed for each primitive and required state before product screens were composed.
223
224
  - [ ] Section 8 accessibility constraints hold for the new component; any new debt is recorded in Section 8, not silently accepted.
225
+ - [ ] Survives content stress: empty, long label, unbroken string. Reflows to one readable column at 375px with no horizontal scroll of primary content.
224
226
 
225
227
  ## Memory Management
226
228
 
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: layout-skill
3
+ description: "Layer A layout-mechanics reference. Stacks on any style skill when the screen is an app shell, dashboard, settings, list-detail, mail/inbox, or any layout with fixed regions plus a scrolling body — or when a layout breaks under long, empty, or unbroken content. Owns spatial structure and scroll ownership; owns zero visual taste. Load it alongside a style skill; it does not replace one."
4
+ ---
5
+
6
+ # Layout Mechanics
7
+
8
+ Style skills decide how a surface looks. This file decides how it is *built to hold content* — what scrolls, what stays fixed, how regions shrink, and which layouts survive real data. It stacks on top of any Layer A style skill (`taste-skill`, `minimalist-skill`, `soft-skill`, `brutalist-skill`, `gpt-tasteskill`) and any Layer B brand. It adds ZERO visual direction — color, type, shadow, radius, and motion still come from the style skill and `DESIGN.md`.
9
+
10
+ Load this when the screen is an **application shell**, not a scroll-the-whole-document marketing page: dashboards, settings, list-detail, mail/inbox, command surfaces, split panes, sidebars — or when a page that looked fine breaks the moment content gets long, empty, or unbroken.
11
+
12
+ ## 1. Scroll ownership — decide this BEFORE writing layout CSS
13
+
14
+ The single most common agent-built layout bug is an app shell where the wrong thing scrolls: the whole page scrolls when only a panel should, two panels fight over the scrollbar, or a "fixed" header scrolls away. Prevent it by naming ownership up front, in `DESIGN.md` and in the component:
15
+
16
+ - **What scrolls?** Name the ONE element that owns vertical scroll for this region.
17
+ - **What stays fixed?** Header, sidebar, footer, toolbar — list them.
18
+ - **Where is height determined?** The scroll container needs a bounded height ancestor, or it will grow instead of scroll.
19
+
20
+ Rules:
21
+
22
+ - **One scroll container per region unless each extra one has a named job.** Nested scrollbars with no declared responsibility are a defect — the user loses track of what a wheel/trackpad gesture will move.
23
+ - Full-height shells are bounded by `100dvh`/`100dvb` (dynamic viewport units), never `100vh` — `vh` causes the iOS Safari address-bar jump.
24
+ - A sticky element (`position: sticky`) follows document scroll; a fixed shell region (grid row/column with `overflow: auto` body) owns its own scroll. Do not mix the two models in one region without reason.
25
+
26
+ ## 2. The two CSS contracts agents get wrong
27
+
28
+ These two are worth memorizing verbatim. They fail *silently* — the layout looks right until content arrives.
29
+
30
+ **Bounded scroll shell** (fixed header/footer, scrolling body):
31
+
32
+ ```css
33
+ .shell {
34
+ display: grid;
35
+ grid-template-rows: auto minmax(0, 1fr) auto; /* header / body / footer */
36
+ max-block-size: 100dvb;
37
+ }
38
+ .shell__body {
39
+ min-block-size: 0; /* WITHOUT THIS the grid child refuses to shrink and overflow never fires */
40
+ overflow: auto;
41
+ }
42
+ ```
43
+
44
+ The `min-block-size: 0` (or `min-height: 0`) on the scroll child is the fix for "why won't my panel scroll — it just pushes the footer off-screen." A grid/flex child's default `min-*-size: auto` refuses to shrink below its content. The same applies to a flex column: the scrollable child needs `min-height: 0`.
45
+
46
+ **Overflow-safe intrinsic grid** (repeat as many columns as fit, no media queries):
47
+
48
+ ```css
49
+ .grid {
50
+ display: grid;
51
+ gap: var(--gap);
52
+ grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
53
+ }
54
+ ```
55
+
56
+ The inner `min(16rem, 100%)` is load-bearing: plain `minmax(16rem, 1fr)` forces a 16rem track even when the container is 12rem wide, causing horizontal overflow on narrow screens. `min(…, 100%)` lets the track collapse below its floor when space is genuinely tight. Use `auto-fit` to stretch the last row, `auto-fill` to keep empty tracks.
57
+
58
+ ## 3. Named layout primitives (shared vocabulary)
59
+
60
+ Build shells from these named primitives instead of ad-hoc fl/grid. Naming them makes `DESIGN.md` Section 5 and subagent handoffs precise ("wrap it in a `sidebar` with a 20rem aside" beats "put it on the left, roughly"). Lineage: Every Layout + web.dev one-line layouts.
61
+
62
+ | Primitive | Spatial job | Core mechanic |
63
+ |---|---|---|
64
+ | **stack** | Vertical rhythm between siblings | flex column + `gap`, or `> * + *` margin |
65
+ | **cluster** | Wrapping row of items (tags, actions) | `flex-wrap: wrap` + `gap`; wraps before overflow |
66
+ | **content-limiter** | Readable prose measure inside a fluid parent | `max-inline-size: ~65ch; margin-inline: auto` |
67
+ | **sidebar** | Narrow aside + fluid main, wraps when tight | flex; aside fixed basis, main `min-inline-size` floor, both `flex-wrap` |
68
+ | **switcher** | N equal regions: row when roomy, stack when tight, NO breakpoint | flex + `min()` basis so it flips at a content threshold |
69
+ | **cover** | Centered region between optional header/footer, min viewport tall | grid rows `auto 1fr auto`, `min-block-size: 100dvb` |
70
+ | **frame** | Media held to an aspect ratio | `aspect-ratio` + `object-fit: cover` |
71
+ | **reel** | Row that scrolls horizontally instead of wrapping | `overflow-inline: auto` + `scroll-snap`; declare keyboard access |
72
+ | **imposter** | Overlay centered over a parent without changing document order | `position: absolute` + translate; do not use to reorder focus |
73
+ | **overlay-stack** | Several layers intentionally in one cell | single grid cell, all children `grid-area: 1/1` |
74
+ | **scroll-body-shell** | Fixed shell regions, only the body scrolls | §2 bounded scroll shell |
75
+ | **fixed-sidenav-shell** | Side nav stays put, main scrolls | grid columns `auto 1fr`, main is the scroll owner |
76
+ | **list-detail** | Explorable list beside its detail region | two-column grid, each pane's scroll ownership named |
77
+ | **sticky-aside** | Support content stays visible during a long read | `position: sticky; top:` on the aside, document scroll |
78
+
79
+ ## 4. Container-local vs viewport-level responsiveness
80
+
81
+ Ask **what the layout is responding to** before reaching for a breakpoint:
82
+
83
+ - The component should adapt to *its own available width* (a card that's wide in main but narrow in a rail) → wrap it in `container-type: inline-size` and use `@container`. This is correct far more often than agents assume, because a component rarely knows the viewport it lands in.
84
+ - The *page frame itself* changes (sidebar collapses, columns drop) → `@media`.
85
+
86
+ Prefer intrinsic adaptation (`switcher`, `sidebar`, intrinsic grid, `clamp()`) over any query — the best breakpoint is often none. Use breakpoint *names* for layout states, never device names (`--bp-wide`, not `--bp-ipad`).
87
+
88
+ ## 5. Recipes are spatial models, not product categories
89
+
90
+ Choose a layout by the screen's spatial shape, not its product label. A settings page and a docs app both want `fixed-sidenav-shell`; a support inbox and a file browser both want `list-detail`; a metrics view and a photo gallery both want an intrinsic grid. Do not invent a bespoke "dashboard layout" when `page-grid + intrinsic grid + cluster` already covers it — and do not force marketing-page structure (hero, zigzag, bento) onto a task app.
91
+
92
+ ## 6. Content stress — the layout is not done until it survives this
93
+
94
+ Landing pages fail on taste; app shells fail on *content*. Before declaring any layout done, stress every region against:
95
+
96
+ - **Empty** — no rows, no avatar, no value. Does the region collapse gracefully or leave a broken frame?
97
+ - **Long label** — a 40-char name in a 12-char slot. Truncate (`text-overflow: ellipsis`) or wrap by design, never by accident.
98
+ - **Long paragraph** — does the measure stay readable, or does text run 200 chars wide?
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.
101
+ - **Direction** — if the app supports RTL, the layout uses logical properties (`margin-inline`, `inset-inline-start`) so it mirrors correctly.
102
+
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.
104
+
105
+ ## Boundary
106
+
107
+ This file owns spatial structure only. It never sets color, typography, shadow, radius, or motion values — those trace to `DESIGN.md` and the loaded style skill. If you find yourself adding a brand color to a layout primitive, stop: the primitive stays layout-only and the styling wraps or composes around it.
@@ -104,9 +104,17 @@ Mocks are a last resort, not a default. The priority order:
104
104
  - **Deterministic**: no `sleep`, no wall-clock dependence, no order dependence (`-shuffle=on`, pytest-randomly, vitest random seed). Inject a `Clock`. Subscribe to the event, do not poll for it. Time-based flake is a bug, not a test issue.
105
105
  - **Isolated**: every test starts from a known fixture and tears down. `t.TempDir()`, `t.Setenv()`, transactional rollback for DB tests. Two tests passing individually but failing together is a fixture leak — fix it immediately.
106
106
 
107
- ### Prompt tests follow the same rule
107
+ ### Prompt tests: NEVER assert prose
108
108
 
109
- When tests cover LLM prompts or agent outputs, assert on **parsed structure, decisions, or rule data**, never on exact prompt strings. Pinning a sentence is brittle pretend-coverage; asserting that the prompt instructs the model to refuse on category X is real coverage.
109
+ **FORBIDDEN NO EXCEPTIONS: a test MUST NOT assert natural-language prompt text.** `expect(prompt).toContain("based on GPT-5.6")`, `not.toContain("old wording")`, `toMatchSnapshot()` on prose, grepping a sentence fragment — every one of these is pretend-coverage. It stays green while the behavior it claims to guard breaks, then blocks every legitimate rewording until someone bumps the pinned string. A reviewer MUST block it as HIGH; deleting such a test is a fix, not a coverage loss. "A nearby test already does it" is not a defense — that test is the disease, not the convention.
110
+
111
+ Assert ONLY what a machine consumes:
112
+
113
+ - the builder's routing decision — `expect(getPromptSource(model)).toBe("gpt-5-6")`, never the sentence that routing produces
114
+ - a structural token the runtime dispatches on — a tool name, a tag like `<agent-identity>`, a parsed frontmatter field
115
+ - the conditional the code enforces — skill loaded → tool present; `verbose=false` → directive absent
116
+
117
+ If no machine consumes the text, there is no seam: write NO test and say so in the PR; review guards prose. When you DELEGATE test-writing, hand the child the behavior the test must distinguish ("fails if override precedence breaks"), never a ready-made assertion string, prompt fragment, or marker to copy — a prescribed mechanism that is wrong gets implemented faithfully, and the error ships with a green suite.
110
118
 
111
119
  ### Anti-patterns the skill rejects
112
120
 
@@ -119,6 +127,8 @@ When tests cover LLM prompts or agent outputs, assert on **parsed structure, dec
119
127
  | Snapshot tests for everything | Locks formatting, not behavior. | Snapshots for *structure* (CLI help, JSON shape). Assertions for *behavior*. |
120
128
  | Removing a failing test to "unblock CI" | You just deleted a bug report. | Fix the code or fix the test — never delete to silence. |
121
129
  | `assert result is not None` and stopping there | Passes when result is garbage. | Assert the *value*, not its existence. |
130
+ | Expected value derived from the output under test (`expect(config.prompt).toBe(getPrompt(config.model))` when the criterion is about `config.prompt`) | Recomputes a projection of the output and compares it to itself — passes even when the artifact is built from the wrong input. | Derive the expected value from the test's *input*: `expect(config.prompt).toBe(getPrompt(inputModel))` (independent known-good builder fed the fixture's input), or a stable builder routing decision. |
131
+ | Override/precedence fixture equal to its fallback (override == system default) | The assertion passes whether or not the code honored the override — precedence is never exercised. | Make every value the code must select, preserve, or override differ from its fallback. Prove it: temporarily force the regression the test names, watch it fail, revert. |
122
132
  | Single happy-path E2E, no edges | Most bugs live on edges. | Edges are unit-test territory — but include at least one E2E that exercises an error path. |
123
133
 
124
134
  ---
@@ -128,9 +128,13 @@ are delegating never justify a planner: plan directly in the notepad.
128
128
  Never spawn `plan` before the discovery wave has returned.
129
129
 
130
130
  ## 1. Create the goal with binding success criteria
131
- Call `create_goal` (or open your reply with a `# Goal` block treated as
132
- binding) using exactly `objective`. Do not include `status`. Goals are
133
- unlimited; never invent a numeric budget or limit.
131
+ You MUST register the goal with the `create_goal` tool NOT prose,
132
+ NOT the notepad, NOT the plan: the registered goal is the binding
133
+ contract for the whole run, and skipping it is a defect. Call it with
134
+ exactly `objective`; do not include `status`. Only when no goal tool
135
+ exists on this surface, open your reply with a `# Goal` block treated
136
+ as binding. Goals are unlimited; never invent a numeric budget or
137
+ limit.
134
138
  The criteria MUST list, upfront:
135
139
  - The user-visible deliverable in one line, and the tier with its
136
140
  justification.
@@ -143,6 +147,9 @@ The criteria MUST list, upfront:
143
147
  - For each criterion, the failing-first proof (test id or scenario)
144
148
  that will be captured RED BEFORE the implementation and GREEN after.
145
149
  Evidence added after the green code does NOT satisfy this.
150
+ - WHEN TO STOP, in one line: "I'll stop right away when <the exact
151
+ observable state that ends this run>". The Stop rules bind to this
152
+ line — the moment it holds, you stop.
146
153
 
147
154
  These scenarios are the contract. You are not done until every one of
148
155
  them PASSES with its evidence captured.
@@ -243,6 +250,18 @@ Until every success criterion PASSES with its evidence captured:
243
250
  scenario captured failing when no test seam exists. It must fail
244
251
  for the RIGHT reason (not a syntax error, not a missing import).
245
252
  Paste RED output into the notepad. No production code yet.
253
+ TEST-ONLY TARGET (regression coverage for behavior that is already
254
+ correct): there is no natural RED and no production change to make
255
+ — this is the sole exception to the production-RED/GREEN steps.
256
+ Substitute a mutation proof: temporarily force the exact regression
257
+ each new assertion names (revert the fix commit or break the seam,
258
+ never committed), capture the assertion failing, then revert the
259
+ mutation and capture GREEN. An assertion that stays green under its
260
+ mutation is not coverage — fix the fixture (a value equal to the
261
+ default it must override proves nothing) or assert the artifact the
262
+ criterion names, never an expected value re-derived from the output
263
+ under test. Reverting the probe IS the GREEN; skip step 3's
264
+ production change for a TEST-ONLY task and go to step 4.
246
265
  PROSE TARGET (prompt, SKILL.md, rule, markdown): the wording is
247
266
  NOT the behavior — never pin sentences, phrase presence/absence,
248
267
  or word/char counts. PIN only a machine-consumed value (parsed
@@ -251,7 +270,8 @@ Until every success criterion PASSES with its evidence captured:
251
270
  two shipped copies. A pure-prose change with no machine consumer
252
271
  has NO seam: ship it on review + QA-by-read, NO test — a text grep
253
272
  is pretend-coverage, not RED proof.
254
- 3. GREEN: write the SMALLEST production change that flips RED→GREEN.
273
+ 3. GREEN (skip for TEST-ONLY reverting the mutation is GREEN): write
274
+ the SMALLEST production change that flips RED→GREEN.
255
275
  Before GREEN work that depends on external review, PR, issue, or
256
276
  branch state, refresh current branch/PR/issue state and preserve existing ordering/policy;
257
277
  separate compatibility detection from policy changes unless the goal
@@ -389,10 +409,11 @@ message + present for approval.
389
409
  revert, capture the proof failing, then redo the change. Exempt
390
410
  only: pure formatting, comment-only edits, dependency bumps with no
391
411
  behavior delta, rename-only moves — justify each in `## Findings`.
392
- - A test that mirrors its implementation asserting mocks were
393
- called, pinning a constant, or unable to fail under any plausible
394
- regression is NOT evidence. Prefer a real-surface proof with no
395
- new test over a tautological test.
412
+ - A test that cannot fail for the regression it names is NOT
413
+ evidence: mock-call assertions, pinned constants, a fixture equal
414
+ to the default it must override, an expected value re-derived from
415
+ the output under test. Prefer a real-surface proof with no new
416
+ test over a tautological one.
396
417
  - Refactors: characterization tests pinning current observable
397
418
  behavior FIRST, green against the old code, green throughout.
398
419
  - Smallest correct change. No drive-by refactors.
@@ -414,9 +435,15 @@ message + present for approval.
414
435
  - After each result, ask whether the user's core request can now be
415
436
  answered with useful evidence in hand. If yes, answer now — skip any
416
437
  remaining retrieval, ceremony, or verification that adds no evidence.
417
- - Stop ONLY when every scenario PASSES with captured evidence, every
438
+ - The STOP GOAL: every scenario PASSES with captured evidence, every
418
439
  cleanup receipt is recorded, notepad is current, and (if gate
419
- triggered) reviewer approved unconditionally.
440
+ triggered) reviewer approved unconditionally. Above ALL of that, the
441
+ decisive test — outranking every other consideration — is: are the
442
+ completion conditions FUNDAMENTALLY fulfilled, is the user's problem
443
+ ACTUALLY SOLVED in observable behavior? If no, you are NOT done,
444
+ whatever the ledger says. If yes, deliver the final message and STOP
445
+ — no hesitation, no extra verification pass, no polish loop. Work
446
+ past the stop goal is scope creep, not diligence.
420
447
  - Leftover QA state (live process, `tmux` session, browser context,
421
448
  bound port, temp file / dir) means NOT done. Tear it down, record
422
449
  the receipt, then continue.
@@ -14,7 +14,7 @@ This skill is intentionally compact. The full workflow lives in `references/full
14
14
  ## Required First Steps
15
15
 
16
16
  1. Open `references/full-workflow.md`.
17
- 2. Read through **Bootstrap** (including its tier triage), **Execution Loop**, and the **Manual-QA channels** table before running any ULW command or recording evidence.
17
+ 2. Read through **Bootstrap** (including its tier triage), **Execution Loop**, the **Manual-QA channels** table, and the **Stop Rules** before running any ULW command or recording evidence.
18
18
  3. If the task has code edits, tests, QA, or commit work, follow the full workflow's delegation and evidence rules. Tests alone never prove done.
19
19
 
20
20
  ## Non-Negotiables
@@ -129,7 +129,7 @@ First survey available skills: read every loosely-relevant skill's description,
129
129
  Then run tier triage per goal — rigor (LIGHT/HEAVY below) and shape (`delivery` default, or `research` when the deliverable is a cited answer, not an artifact) — and record both in an `annotate_ledger` steering entry. Default is LIGHT — a narrow change inside existing layers. Take HEAVY only on a fact you can point to: a new module / abstraction / domain model; auth, security, or session; an external integration; a DB schema or migration; concurrency, transaction boundaries, or cache invalidation; a cross-domain refactor; or the user signaled care or demanded review. When unsure, take HEAVY; upgrade the moment a HEAVY fact surfaces, never downgrade mid-run.
130
130
  Planning depends on unresolved design uncertainty, not the rigor tier: after discovery, spawn the `plan` agent only when unclear boundaries, competing decompositions, or uncertain dependency ordering remain; otherwise plan directly, including for HEAVY goals with a known procedure. HEAVY goals carry 3+ successCriteria covering happy path, edge, regression, and adversarial risk. LIGHT goals carry 1-2 successCriteria (happy path + the riskiest edge) with one real-surface proof of the deliverable.
131
131
  Research-shape goals change the cycle: BEFORE each investigation, read this goal's prior ledger findings and open hypotheses, then extend them — never re-investigate an answered question (the ledger is your research notebook). Record findings via `annotate_ledger` with their source (`file:line`, command output, doc URL) as `--evidence`. Track hypotheses as `HYPOTHESIS[id]: <claim> | status: open`, flipped to `confirmed`/`refuted` only on an observed source. A research criterion passes on a cited answer — skip QA-channel, cleanup, and commit, but keep source-observability (never "looks correct"). Keep hypotheses inside the user's stated question; a scope-widening one is an `add_subgoal` proposal you surface, never silent creep. For a `research`-shape goal you MAY load `ulw-research` without hesitation — otherwise explicit-request-only, a research-shape goal IS that explicit demand. Research-only: never for a `delivery` goal. It composes with the librarian routing above — `ulw-research` for saturation (many parallel sources, recursive expansion), a single `librarian` for one lookup.
132
- For each criterion, define upfront: `id`, exact `scenario` (tool + inputs + binary pass/fail), `expectedEvidence` artifact path, adversarial classes, stop condition, and Manual-QA channel. Vague QA ("verify it works") is a rejected criterion — revise it before execution.
132
+ For each criterion, define upfront: `id`, exact `scenario` (tool + inputs + binary pass/fail), `expectedEvidence` artifact path, adversarial classes, stop condition, and Manual-QA channel. Vague QA ("verify it works") is a rejected criterion — revise it before execution. Every goal also declares, in one line, WHEN TO STOP: "stop right away when <the exact observable state that ends this goal>". A goal without that line is rejected — revise it before execution; the Stop Rules bind to it.
133
133
  For optimization work, capture baseline speed before changes plus behavior/regression proof. Every attempt records speed, behavior/regression, and the keep/revert/iterate decision.
134
134
  A criterion's adversarial classes are the ultraqa classes a fact about the change triggers: malformed input, prompt injection, cancel/resume, stale state, dirty worktree, hung or long commands, flaky tests, misleading success output, repeated interruptions. Record untriggered classes as not-applicable in one line.
135
135
  Use channel-table evidence verbs — not vibes.
@@ -151,7 +151,7 @@ Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures a
151
151
 
152
152
  | get_goal result | action |
153
153
  |-----------------|--------|
154
- | no active goal | Call `create_goal` with objective only from `instruction.json.objective`; do not copy lifecycle fields such as `status`. |
154
+ | no active goal | You MUST call `create_goal` — goal registration goes through the tool, never prose — with objective only from `instruction.json.objective`; do not copy lifecycle fields such as `status`. |
155
155
  | same aggregate objective active | Continue the current ulw-loop story. |
156
156
  | different goal active | STOP. Checkpoint blocked and surface the conflict. |
157
157
  4. If retrying failed work, run `omo ulw-loop complete-goals --retry-failed --json`.
@@ -160,7 +160,7 @@ Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures a
160
160
  ### Per-Criterion Cycle
161
161
  1. PLAN: read `criterion.scenario`, `criterion.expectedEvidence`, prior ledger entries, and safety bounds. Identify which tasks in the current wave are independent.
162
162
  2. Register atomic todos via `update_plan` — one ultra-granular step per action, `path: <action> for <criterion> - verify by <check>`. Call `update_plan` on every transition (start → `in_progress`, finish → `completed`); exactly one `in_progress`, mark completed immediately, never batch, never let the rendered plan lag behind reality.
163
- 3. DELEGATE-IN-PARALLEL: dispatch every independent task in the wave at once via right-sized `spawn_agent` workers (Delegation table). Each worker captures evidence failing-first: when the task touches EXISTING behavior, PIN it FIRST — a characterization test that asserts the current observable behavior and PASSES on the unchanged code, as rigorous as the new-behavior scenario (exact inputs, exact observable, exact assertion). Then RED through the cheapest faithful channel — a unit test where a seam exists, an integration/e2e test where the behavior lives in wiring, or the criterion's scenario captured failing when no test seam exists — failing for the RIGHT reason (no syntax/import error). A test that mirrors its implementation (mock-call assertions, pinned constants, cannot fail under plausible regression) is not evidence; use the scenario as the failing proof instead. **When the target is PROSE (a prompt, `SKILL.md`, rule, or markdown/instruction file), the "observable behavior" is NOT the wording** — never pin sentences, phrase presence/absence, or word/char counts. PIN only a value a MACHINE consumes (a parsed frontmatter field, a sentinel token a hook greps, the doc's JSON sample run through its real validator), or guard two shipped copies with one `toBe` equality; a pure-prose change with no machine consumer has NO seam, so ship it on review + Manual-QA-by-read with NO automated test (a text grep there is pretend-coverage, not a RED proof). Then the SMALLEST GREEN change; before GREEN work that depends on external review, PR, issue, or branch state, refresh current branch/PR/issue state, preserve existing ordering/policy, and separate compatibility detection from policy changes unless the goal explicitly asks to change policy. A GREEN far larger than the criterion implies means the proof was too coarse — instruct a split. Serialize only on a NAMED dependency.
163
+ 3. DELEGATE-IN-PARALLEL: dispatch every independent task in the wave at once via right-sized `spawn_agent` workers (Delegation table). Each worker captures evidence failing-first: when the task touches EXISTING behavior, PIN it FIRST — a characterization test that asserts the current observable behavior and PASSES on the unchanged code, as rigorous as the new-behavior scenario (exact inputs, exact observable, exact assertion). Then RED through the cheapest faithful channel — a unit test where a seam exists, an integration/e2e test where the behavior lives in wiring, or the criterion's scenario captured failing when no test seam exists — failing for the RIGHT reason (no syntax/import error). A test that cannot fail for the regression it names (mock-call assertions, pinned constants, a fixture equal to the default it must override, an expected value re-derived from the output under test) is not evidence; use the scenario as the failing proof instead. TEST-ONLY tasks (regression coverage for behavior that is already correct) have no natural RED — require a mutation proof: temporarily force the exact regression each new assertion names, capture the assertion failing, revert the mutation, capture GREEN; an assertion that stays green under its mutation is not coverage. **When the target is PROSE (a prompt, `SKILL.md`, rule, or markdown/instruction file), the "observable behavior" is NOT the wording** — never pin sentences, phrase presence/absence, or word/char counts. PIN only a value a MACHINE consumes (a parsed frontmatter field, a sentinel token a hook greps, the doc's JSON sample run through its real validator), or guard two shipped copies with one `toBe` equality; a pure-prose change with no machine consumer has NO seam, so ship it on review + Manual-QA-by-read with NO automated test (a text grep there is pretend-coverage, not a RED proof). Then the SMALLEST GREEN change (none for a TEST-ONLY task — reverting the probe is GREEN; go to integration); before GREEN work that depends on external review, PR, issue, or branch state, refresh current branch/PR/issue state, preserve existing ordering/policy, and separate compatibility detection from policy changes unless the goal explicitly asks to change policy. A GREEN far larger than the criterion implies means the proof was too coarse — instruct a split. Serialize only on a NAMED dependency.
164
164
  4. INTEGRATE + CRITICAL SELF-QA + GIT CHECKPOINT (EVERY WORKER RETURN): do NOT trust the worker's report. Read the diff yourself, re-run its tests, and run LSP diagnostics on the changed files. Treat "done" as a claim to disprove. If the diff drifts, the test is hollow, or evidence is missing, RESPAWN the worker with the specific failure context. Once the work unit is verified, use `git-master` before staging: inspect recent repository commits and touched-path history to infer commit language, Conventional Commit scope, message shape, and unit size. Stage only that unit's files and commit in the observed style; do not carry verified work forward into a later omnibus commit. If no git-tracked files changed or committing is unsafe, record the no-commit reason as evidence. Forward every finding/learning to subsequent workers.
165
165
  5. EXECUTE-AS-SCENARIO: ACTUALLY run the Manual-QA scenario the criterion named (channel table above). Run it yourself for the orchestrator check; for heavier flows dispatch a dedicated QA execution worker (`lazycodex-worker-medium` by default; `lazycodex-worker-high` when the QA flow itself is hard) whose ONLY job is to drive the channel and write the artifact to the named evidence path. If the scenario FAILS, respawn the implementing worker with the captured failure — do not hand-patch around it.
166
166
  6. CAPTURE: collect the observable artifact path: transcript, stdout, screenshot, assertion, status+body, diff, or parsed dump. No artifact written at the evidence path — not done; record BLOCKED and respawn QA.
@@ -239,7 +239,7 @@ Structured prompt directives accepted: `OMO_ULW_LOOP_STEER: { ... }`, `omo.ulw-l
239
239
  15. Every verified work unit that touched git-tracked files must leave either an atomic `git-master`-style commit hash or explicit no-commit blocker evidence before the next unit starts.
240
240
 
241
241
  ## Stop Rules
242
- - All goals complete plus every plan criterion `pass` plus final quality gate clean: DONE.
242
+ - STOP GOAL: all goals complete plus every plan criterion `pass` plus final quality gate clean. The decisive test — outranking every other consideration — is whether the completion conditions are FUNDAMENTALLY fulfilled and the user's problem ACTUALLY SOLVED in observable behavior; a `pass` ledger never substitutes for it. The moment both hold, checkpoint, report, and STOP — no extra review cycles, no evidence regeneration, no polish.
243
243
  - 3x same criterion failure: checkpoint failed, surface diagnosis.
244
244
  - 5 cycles on one goal without required criteria passing: checkpoint failed, surface.
245
245
  - Safety boundary such as destructive command, secret exfiltration, or production write: block and surface a safe substitute.
@@ -5903,7 +5903,7 @@ var package_default;
5903
5903
  var init_package = __esm(() => {
5904
5904
  package_default = {
5905
5905
  name: "@oh-my-opencode/omo-codex",
5906
- version: "4.17.1",
5906
+ version: "4.18.0",
5907
5907
  type: "module",
5908
5908
  private: true,
5909
5909
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -7519,9 +7519,11 @@ function collectCommands(value, commands) {
7519
7519
 
7520
7520
  // packages/omo-codex/src/install/codex-cache-install.ts
7521
7521
  async function installCachedPlugin(input) {
7522
+ const env = input.env ?? process.env;
7523
+ const npmInstallEnv = sanitizeNpmInstallEnv(env);
7522
7524
  if (input.buildSource !== false) {
7523
- await maybeRunNpmInstall(input.sourcePath, input.runCommand);
7524
- await maybeRunNpmBuild(input.sourcePath, input.runCommand);
7525
+ await maybeRunNpmInstall(input.sourcePath, input.runCommand, npmInstallEnv);
7526
+ await maybeRunNpmBuild(input.sourcePath, input.runCommand, env);
7525
7527
  }
7526
7528
  const targetPath = join12(input.codexHome, "plugins", "cache", input.marketplaceName, input.name, input.version);
7527
7529
  const tempPath = createTempSiblingPath(targetPath);
@@ -7531,10 +7533,10 @@ async function installCachedPlugin(input) {
7531
7533
  await rewriteCachedPackageLocalFileDependencies(tempPath, input.sourcePath);
7532
7534
  await copyBundledMcpRuntimeDists({ pluginRoot: tempPath, sourceRoot: input.sourcePath });
7533
7535
  await copyRootRuntimeDists({ pluginRoot: tempPath, sourcePath: input.sourcePath });
7534
- await maybeRunNpmInstall(tempPath, input.runCommand, ["ci", "--omit=dev"]);
7536
+ await maybeRunNpmInstall(tempPath, input.runCommand, npmInstallEnv, ["ci", "--omit=dev"]);
7535
7537
  await removeCachedManagedNpmBinShims(tempPath);
7536
7538
  if (input.buildSource === false)
7537
- await maybeRunNpmSyncSkills(tempPath, input.runCommand);
7539
+ await maybeRunNpmSyncSkills(tempPath, input.runCommand, env);
7538
7540
  await assertNoRemovedSparkshellPromptReferences(tempPath);
7539
7541
  await rewriteCachedMcpManifest(tempPath, input.sourcePath);
7540
7542
  await rewriteCachedManifestRoot(tempPath, tempPath, targetPath);
@@ -7546,12 +7548,12 @@ async function installCachedPlugin(input) {
7546
7548
  }
7547
7549
  return { name: input.name, version: input.version, path: targetPath };
7548
7550
  }
7549
- async function maybeRunNpmInstall(cwd, runCommand, args = ["install"]) {
7551
+ async function maybeRunNpmInstall(cwd, runCommand, env, args = ["install"]) {
7550
7552
  if (!await fileExistsStrict(join12(cwd, "package.json")))
7551
7553
  return;
7552
- await runCommand("npm", args, { cwd });
7554
+ await runCommand("npm", args, { cwd, env });
7553
7555
  }
7554
- async function maybeRunNpmBuild(cwd, runCommand) {
7556
+ async function maybeRunNpmBuild(cwd, runCommand, env) {
7555
7557
  if (!await fileExistsStrict(join12(cwd, "package.json")))
7556
7558
  return;
7557
7559
  const packageJson = JSON.parse(await readFile8(join12(cwd, "package.json"), "utf8"));
@@ -7560,9 +7562,9 @@ async function maybeRunNpmBuild(cwd, runCommand) {
7560
7562
  const scripts = packageJson.scripts;
7561
7563
  if (!isPlainRecord(scripts) || typeof scripts.build !== "string")
7562
7564
  return;
7563
- await runCommand("npm", ["run", "build"], { cwd });
7565
+ await runCommand("npm", ["run", "build"], { cwd, env });
7564
7566
  }
7565
- async function maybeRunNpmSyncSkills(cwd, runCommand) {
7567
+ async function maybeRunNpmSyncSkills(cwd, runCommand, env) {
7566
7568
  if (!await fileExistsStrict(join12(cwd, "package.json")))
7567
7569
  return;
7568
7570
  const packageJson = JSON.parse(await readFile8(join12(cwd, "package.json"), "utf8"));
@@ -7571,7 +7573,10 @@ async function maybeRunNpmSyncSkills(cwd, runCommand) {
7571
7573
  const scripts = packageJson.scripts;
7572
7574
  if (!isPlainRecord(scripts) || typeof scripts["sync:skills"] !== "string")
7573
7575
  return;
7574
- await runCommand("npm", ["run", "sync:skills"], { cwd });
7576
+ await runCommand("npm", ["run", "sync:skills"], { cwd, env });
7577
+ }
7578
+ function sanitizeNpmInstallEnv(env) {
7579
+ return Object.fromEntries(Object.entries(env).filter(([key]) => key.toLowerCase() !== "npm_config_allow_scripts"));
7575
7580
  }
7576
7581
  function createTempSiblingPath(targetPath) {
7577
7582
  return join12(dirname5(targetPath), `.tmp-${basename3(targetPath)}-${process.pid}-${Date.now()}`);
@@ -9600,6 +9605,10 @@ async function readDistributionManifest(repoRoot) {
9600
9605
  }
9601
9606
  }
9602
9607
  function resolveLazyCodexPluginVersion(input) {
9608
+ const override = input.versionOverride?.trim();
9609
+ if (override !== undefined && override.length > 0) {
9610
+ return override;
9611
+ }
9603
9612
  if (input.marketplaceName === "sisyphuslabs" && input.pluginName === "omo" && input.distributionManifest !== undefined) {
9604
9613
  return input.distributionManifest.version;
9605
9614
  }
@@ -10240,6 +10249,7 @@ async function runCodexInstaller(options = {}) {
10240
10249
  return;
10241
10250
  });
10242
10251
  const buildSource = await shouldBuildSourcePackages(repoRoot);
10252
+ const versionOverride = env2.LAZYCODEX_DEV_VERSION?.trim() || undefined;
10243
10253
  const gitBashResolution = await prepareGitBashForInstall({
10244
10254
  platform,
10245
10255
  env: env2,
@@ -10266,13 +10276,15 @@ async function runCodexInstaller(options = {}) {
10266
10276
  manifestVersion: manifest.version,
10267
10277
  marketplaceName: marketplace.name,
10268
10278
  pluginName: entry.name,
10269
- distributionManifest
10279
+ distributionManifest,
10280
+ versionOverride
10270
10281
  });
10271
10282
  validatePathSegment(version2, "plugin version");
10272
10283
  log(`Building ${entry.name}@${version2}`);
10273
10284
  const plugin = await installCachedPlugin({
10274
10285
  buildSource,
10275
10286
  codexHome,
10287
+ env: env2,
10276
10288
  marketplaceName: marketplace.name,
10277
10289
  name: entry.name,
10278
10290
  runCommand,
@@ -32,7 +32,7 @@ Every implementation must choose one of these branches before UI code changes:
32
32
  - **Embedded references:** use `references/design/_INDEX.md` to shortlist 2-3 plausible Layer B references, then read exactly one Layer A style skill and one Layer B reference in full — every line, no partial reads (they are 200-500 lines; a sliced read produces the flattened token set this gate exists to prevent). Log the shortlist, the pick, and why. Use `open-design` only when the curated set has no fit; add `ui-ux-db` lookups for palette/type/domain questions.
33
33
  - **Lazyweb real-product screens:** READ `references/design/lazyweb.md` FIRST and run its recipe verbatim — do not improvise curl calls against lazyweb.com; the recipe mints its own anonymous token. Log the queries run, how many screens you actually VIEWED, and the layout grammar harvested — never pixel copies.
34
34
  - **Imagen concept drafts:** generate 2-3 imagen concept drafts, each seeded with the loaded Layer A + Layer B tokens (palette, type, material); pick the strongest and treat the chosen draft as the reference-fidelity contract. Log the draft paths and the pick.
35
- Synthesize every lane into `DESIGN.md`. Treat sources as source material, not mood labels: extract tokens, layout grammar, component anatomy, interaction states, motion, and taste decisions, then recombine them into project-specific primitives. Never freestyle past the selected references, never copy logos or brand-specific copy. Then run the Primitive Showcase Gate (`references/design/README.md` Phase 0) before any product screen.
35
+ Synthesize every lane into `DESIGN.md`. Treat sources as source material, not mood labels: extract tokens, layout grammar, component anatomy, interaction states, motion, and taste decisions, then recombine them into project-specific primitives. Before laying out sections, inventory the content blocks and assign each a job — hook, explain, prove, compare, convert, navigate, retain — then order sections by the visitor's decision path, not by visual symmetry. Never freestyle past the selected references, never copy logos or brand-specific copy. Then run the Primitive Showcase Gate (`references/design/README.md` Phase 0) before any product screen.
36
36
  3. **Existing project with `DESIGN.md` or a component system:** read it, follow it, and update it before implementation only when the requested work needs a new token, primitive, state, motion rule, accessibility constraint, accepted debt, or reference-fidelity requirement.
37
37
  4. **Existing project with UI but no `DESIGN.md` and no reusable component layer:** STOP and ask the user one focused question: should you preserve the current look with copy-nearby styling, or extract a real `DESIGN.md` plus reusable components before continuing? Do not silently choose.
38
38