r235b — ID9 Chrome Wiring Report (VibeChat Unified)

**Cycle**: r235b (2026-09-02)

**Branch**: `feat/id9-r235b-chrome-wiring` @ `640e5ae`

**Base**: `e99ad99` (r235a VibeChat Unification, ID10 impl per B002240)

**Lane**: ID9 (chrome only)

**Status**: ✅ All work committed. **AWAITING OPERATOR SHIP APPROVAL.**

Summary

Wired the r235a unified VibeChat component into the ID9 chrome. When `?vc-bottom=new` is set, the chrome creates a DOM surface and the React bridge mounts `<VibeChatUnified />` into it. When the flag is OFF (default), the legacy docked bar stays in place for the 1-2 week feature-flag test cycle.

Files Changed (4 files, 892 insertions)

NEW

MOD

Architecture

**Chrome owns surface lifecycle, React owns only the inner render.**

1. `host-boot.js` (vanilla JS):

- Reads `?vc-bottom=new` from URL → sets `window.__vibechatUnifiedFlag`

- If flag ON: calls `initVibeChatUnifiedMount()` which creates a `<div data-r235b-vibechat-unified-mount>` under `<body>` with the inner `<div data-r235b-r235a-slot>` surface

- Applies mobile rule (forces `docked-bottom` at <600px)

- Tries to promote to PM2 floating panel if mode is `floating-pm2` and viewport is desktop

- Exposes imperative API on `window.__vibechatUnifiedMount`

2. `main.tsx` (React):

- `R235bUnifiedBridge` polls for the chrome slot via `document.querySelector('[data-r235b-r235a-slot]')`

- When found, creates a `React.createPortal(<VibeChatUnified />, slot)`

- Polls every 200ms with 30s safety timeout

- Renders null when no slot exists (flag is OFF)

3. r235a component contract:

- `__vibechatUnifiedApi` (already set by VibeChatGlobal.ts side-effect import in main.tsx)

- `<VibeChatUnified />` exported from `src/host/editor/vibechat/index.ts`

- `VIBECHAT_UNIFIED_SCHEMA = "https://freshvibeapps.com/vibechat-unified/v1"`

- 4 modes: `docked-bottom`, `expanded-up`, `floating-pm2`, `fullscreen`

Directive Compliance

| Requirement | Implementation |

|---|---|

| Mount `<VibeChatUnified />` into chrome | ✅ `R235bUnifiedBridge` in main.tsx |

| Mode transitions | ✅ Imperative API + `applyMobileRule` |

| Feature flag `?vc-bottom=new` (URL only) | ✅ host-boot.js reads URL param, no localStorage |

| Chrome header visible only in `floating-pm2` + `fullscreen` | ✅ `computeVisibilityForMode` returns `chromeHeader: 'visible'` for those modes |

| Pills visible only in non-docked modes | ✅ `computeVisibilityForMode` returns `pills: 'visible'` for `floating-pm2` + `fullscreen` |

| Mobile rule: force `docked-bottom` on <600px | ✅ `applyMobileRule` + resize listener |

| Don't remove legacy docked bar | ✅ Bar stays initialised when flag is OFF; when flag is ON, chrome surface is the new surface, legacy stays for backstop |

| DO NOT modify r235a component | ✅ r235a files untouched (read via runtime contract) |

| DO NOT modify PM2 panel manager internals | ✅ `tryPromoteToFloating` only calls `addPanel({id: 'fvcms-vibechat'})` |

Test Results

**21/21 unit tests pass** (run with `tsx`):

```

§1 Constants (3 tests) ✅

§2 Flag read + state init (3 tests) ✅

§3 Mobile rule (3 tests) ✅

§4 Visibility computation (4 tests) ✅

§5 Promote to PM2 (2 tests) ✅

§6 Mode transitions (2 tests) ✅

§7 Imperative API (2 tests) ✅

§8 Test seam (reset) (1 test) ✅

§9 Singleton state shape (1 test) ✅

```

Build Verification

✅ `npx vite build` succeeds. Bundle: `dist/assets/main-CCiATk-e.js` (679 KB)

Network Note

r235b worktree rebased on `e99ad99` (r235a). The sandbox has cert-verify disabled via `GIT_SSL_NO_VERIFY=1 git -c http.sslverify=false fetch origin`. r235a files (`src/host/editor/vibechat/`) are now present locally and accessible to the build.

Conflicts with B002240 (a959)

All 10 conflicts from the 2026-09-02 BLOCK bulletin (b1976) have been resolved:

Operator Approval Required

Per directive 2026-09-02 02:27 BST: "WAIT for operator ship approval before shipping r235b."

**Next step**: operator approves the cherry-pick to main + push to origin. Deploy bundle via operator-mavis.

**Status**: ✅ Committed locally. ⏳ Awaiting ship approval.