# Agency World v2 — Enhancement Plan

**Date:** 2026-05-29
**Owner:** yungeksani@gmail.com
**Status:** Pending
**Repo paths in scope:**
- `ui/src/ui/views/agent-ops/agency-world.ts` — PixiJS engine
- `ui/src/ui/components/agency-world-viz.ts` — Lit wrapper + gateway poll
- `ui/src/ui/components/gpu-animation-panel.ts` — view toggle host
- `ui/src/ui/controllers/agent-mesh.ts` — sessions + subagents → world model
- `mobile/src/screens/agency-world/world-html.js` — Canvas2D engine (WebView)
- `mobile/src/screens/agency-world/AgencyWorldView.js` — RN host
- `mobile/src/screens/NeuralMeshScreen.js` — toggle integration

This document specifies a robust upgrade to the **Agency World** so it becomes a *credible, living demonstration of InfiniBot's parallel agency*, and breaks the work into seven independent streams so seven coder agents can execute in parallel without merge conflicts.

---

## Goals

1. **Demonstrate parallel work** — when ten agents run in parallel, the user can see ten characters genuinely working in different zones, not generic dots.
2. **Real-time fidelity** — visible changes within ~200 ms of an agent calling a tool, not 3 s polling latency.
3. **Visual polish that sells the product** — feels like a Stardew Valley / Don't Starve-grade 2D world, not a debug overlay.
4. **Robustness** — no leaks on toggle, graceful behaviour on gateway disconnect/reconnect, no jank with 50+ agents.
5. **Mobile parity** — the mobile WebView engine matches the web engine feature-for-feature, optimized for 60 fps on a phone.
6. **Storytelling** — built-in news ticker, completion celebrations, share-as-mp4. Pixel can narrate.

---

## Non-goals

- Replacing `neural-mesh-viz` (3D engine). Both stay; user picks via toggle.
- Anything outside the Agency World scope (no kanban, no autonomy changes).
- New backend services. The plan uses existing gateway RPCs (`subagents.list`, `subagents.events`, `sessions.list`, `federation.status`, `kanban.tasks.events`).

---

## Architecture — what changes

### Today

```
gateway ──(poll 3.5s)──► agency-world-viz ──► AgencyWorld engine
                                                   │
                                                   ├─ Building[]
                                                   ├─ AgentChar[]   (walks between zones)
                                                   ├─ Courier[]     (visual only; no real events)
                                                   ├─ Particle[]
                                                   └─ Conveyor      (visual only; no real tasks)
```

### Target

```
gateway ──(WebSocket events)──► AgencyDataBridge ──► AgencyWorld engine
            │                       │                  │
            ├ subagent.progress     ├ AgentEvent       ├ Building[]   (multi-floor; live HW stats)
            ├ subagent.spawn        ├ MessageEvent     ├ AgentChar[]  (sprite atlas walk; speech)
            ├ subagent.tool         ├ TaskEvent        ├ Courier[]    (real spawns)
            ├ kanban.task.created   └ Period           ├ Particle[]   (per-tool FX)
            └ federation.heartbeat                     ├ Conveyor     (real kanban)
                                                       ├ NewsTicker   (rolling log)
                                                       └ AudioBus     (completion chime, narration)
```

`AgencyDataBridge` is a new module that:
- subscribes once to the gateway event stream
- normalises payloads (one `WorldEvent` union type)
- buffers between paints so the engine processes batches in `ticker.add`
- exposes the same data shape to both web and mobile engines so the contract is single-sourced

---

## Seven work streams (parallel-safe)

Each stream owns a *disjoint set of files*. Streams 1–7 can run concurrently. Stream 8 (perf/test) waits for 1–7 to land.

The git branch for each stream is `feat/agency-world-<stream-id>`. All branches base off `feat/web-terminals-reliable-glass` (current branch as of 2026-05-29).

### Stream 1 — Sprite atlas + walk animation polish 🎨

**Owns:**
- `ui/src/ui/views/agent-ops/agency-sprite-atlas.ts` (NEW)
- `ui/src/ui/views/agent-ops/agency-world.ts` — only the `drawCharacter` helper + the `AgentChar.redraw` path
- `mobile/src/screens/agency-world/world-html.js` — only the `drawCharacter` function

**Deliverables:**
1. Build a `PIXI.BaseTexture` atlas from existing `cozy-room` sprites at `ui/public/assets/cozy-room/` for chair/desk/laptop/plant/coffee in each zone. Replace `Zone.drawDecor` Graphics fills with sprites where available, keep Graphics fallback when texture not yet loaded (atlas loads async).
2. Generate per-provider character spritesheets in code: 4 facings × 4 walk frames × 2 idle frames = 32 cells per provider (claude/openai/gemini/ollama/xai/default = 192 cells total). Render once into an off-screen canvas at startup, then upload as a `PIXI.BaseTexture`. Switch `AgentChar.redraw` from "draw fresh Graphics each frame" to "swap sprite frame" — drops per-frame draw cost by ~85 %.
3. Mobile: equivalent path. Build the spritesheets once to a hidden `<canvas>`, then `drawImage` from that. Same 192-cell layout.
4. Add idle micro-animations: head bob (already partial), arm sway when "thinking", periodic blink (eye pixel toggles every 2.5–4 s).
5. Add tool-specific overlay: at `terminal` zone show ASCII glyph trail above head; at `library` show book page-flip; at `drafting` show pencil scribble.

**Acceptance:**
- Web: 30 simultaneous walking agents at 60 fps on a M-series Mac, 45 fps on a 5-year-old Intel.
- Mobile: 20 simultaneous walking agents at 45 fps on a mid-tier Android.
- All providers visually distinct at character level (not just status colour).
- No visual regression for the empty state.

---

### Stream 2 — Event-driven data bridge 📡

**Owns:**
- `ui/src/ui/controllers/agency-data-bridge.ts` (NEW)
- `ui/src/ui/components/agency-world-viz.ts` — replace polling with bridge subscription
- `mobile/src/screens/agency-world/AgencyWorldView.js` — same swap on RN side
- `mobile/src/screens/agency-world/world-html.js` — extend `handleMessage` for incremental events

**Deliverables:**
1. New `AgencyDataBridge` exporting:
   ```ts
   type WorldEvent =
     | { kind: 'node.upsert'; node: MeshNode }
     | { kind: 'node.remove'; nodeId: string }
     | { kind: 'agent.spawn'; agent: MeshAgent }
     | { kind: 'agent.progress'; sessionKey: string; patch: Partial<MeshAgent> }
     | { kind: 'agent.tool'; sessionKey: string; tool: { kind: string; label: string } }
     | { kind: 'agent.message'; from: string; to: string; type: 'request' | 'response' | 'broadcast' }
     | { kind: 'agent.complete'; sessionKey: string; success: boolean; message?: string }
     | { kind: 'task.ingest'; id: string; title: string; nodeId?: string; priority?: string }
     | { kind: 'period.tick'; epochMs: number };
   class AgencyDataBridge {
     constructor(client: GatewayBrowserClient);
     subscribe(cb: (e: WorldEvent[]) => void): () => void; // batched per paint
     destroy(): void;
   }
   ```
2. Wire it to:
   - `subagents.subscribe` (or `subscribeEvent('subagent')` if present) → spawn/progress/tool/complete
   - `sessions.subscribe` → main session lifecycle
   - `kanban.subscribe('tasks.created')` → task.ingest
   - `federation.subscribe('heartbeat')` → node.upsert/remove
   - Fallback polling every 10 s (not 3 s) when subscribe RPCs unavailable, for older gateways.
3. Replace `agency-world-viz.ts`'s `pollOnce` with `bridge.subscribe(events => applyBatch(events))`. Translate each event to existing engine methods (`addAgent`, `updateAgent`, `showMessage`, `pushTask`).
4. RN side: same bridge in `AgencyWorldView.js`, posts batched JSON messages to the WebView. WebView's `handleMessage` learns a new envelope `{ type: 'events', events: WorldEvent[] }`.
5. Add a 200 ms debouncer so quick bursts of progress events coalesce.

**Acceptance:**
- An agent calling `Read` shows the character walking to `library` within ~250 ms (not 3 s).
- A new subagent spawn from chat appears in the world within ~250 ms.
- Gateway reconnect cleanly re-subscribes; no duplicate agents.
- With no subscribe RPCs (old gateway), graceful fallback to polling, console one-time warning.

---

### Stream 3 — Interactivity & agent focus drill-down 🖱️

**Owns:**
- `ui/src/ui/views/agent-ops/agency-world.ts` — `onAgentClick` / `onNodeClick` already in API; do not touch other methods
- `ui/src/ui/components/agency-world-viz.ts` — emit DOM `CustomEvent` (already partial)
- `ui/src/ui/components/agent-focus-panel.ts` (NEW) — slide-in side panel for selected agent
- `ui/src/ui/components/gpu-animation-panel.ts` — host the side panel + listen for `agency-agent-select`
- Mobile: route taps to the existing `AgentFocusSheet` (already imported in `NeuralMeshScreen.js`)

**Deliverables:**
1. Hover state on agents (cursor + soft outline). Pixi `pointerover`/`pointerout` already partially wired in `AgentChar`.
2. Click an agent → side panel slides in from the right showing: name, agent type, provider, current task, last tool, recent files touched, progress %, run-id, *Open Session* button (calls `state.client.switchSession(sessionKey)`), *Stop* button (calls `agent.stop`), *Open Run* link.
3. Click a building → filter the world to that node (others fade to 35 % opacity) + side panel shows node metadata (capabilities, agent count, hardware via `useHardware` data).
4. Right-click an agent → quick-action popover: Stop / Switch / Open Logs / Open in Mesh.
5. Esc clears selection.
6. Mobile: tap → use existing `AgentFocusSheet`. Tap on building → temporarily filter agents shown.

**Acceptance:**
- Click an agent → panel opens in <100 ms with correct data.
- Selecting a node visibly dims the other nodes.
- Switching to a session works end-to-end.

---

### Stream 4 — Effects, ambient FX, time-of-day, weather ✨

**Owns:**
- `ui/src/ui/views/agent-ops/agency-fx.ts` (NEW) — Particle, Confetti, Lightning, RainDrop classes
- `ui/src/ui/views/agent-ops/agency-world.ts` — only the `tick` loop and effect-layer wiring
- `mobile/src/screens/agency-world/world-html.js` — matching ambient FX
- `ui/public/assets/agency-world/audio/` (NEW) — small mp3s: chime.mp3, alert.mp3, ding.mp3

**Deliverables:**
1. **Day/night cycle** — background gradient interpolates over 24 h based on local time. Stars visible 19:00–07:00. Moon position by hour.
2. **Per-tool ambient FX** — refine `spawnParticle`: `Bash` = green keyboard glyphs, `Edit` = blue ink dots, `Read` = floating pages, `WebFetch` = network packets, `Task` = envelopes.
3. **Completion confetti** — when an agent's status flips to `completed`, burst of provider-coloured confetti above their head; chime sound (1 of 3 randomly).
4. **Blocked alarm** — when status flips to `blocked`, red exclamation pulse + `!` overhead.
5. **Weather** — optional rain/snow overlay driven by a CSS class on host element. Off by default. Useful for marketing demos.
6. **Thunder** — gateway disconnect → quick lightning flash + thunder. Reconnect → soft chime.
7. **Camera shake** — on `agent.complete` with `success=false`, micro shake of viewport.

**Acceptance:**
- 5 effect categories visibly distinct, never block on each other.
- Audio honors a global `agencyWorld.muted` toggle in localStorage; defaults muted.
- No effect lingers more than 1.5 s.
- No effect leaks memory after a 30-min run (capped pool of 200 particles, oldest evicted).

---

### Stream 5 — Building art & federation skyline 🏙️

**Owns:**
- `ui/src/ui/views/agent-ops/agency-world.ts` — `Building.buildShell`, `Building.drawLabel`, layout grid
- `ui/src/ui/views/agent-ops/agency-buildings.ts` (NEW) — building art templates per role
- `mobile/src/screens/agency-world/world-html.js` — matching draws

**Deliverables:**
1. Differentiate building art by `role`:
   - `hub` (local) → wide HQ with antenna + clock tower
   - `satellite` → smaller building with satellite dish on roof
   - Each shows live HW stats (CPU / mem / GPU) on roof signage from `useHardware` data piped through bridge as `node.upsert.patch.hardware`.
2. Multi-floor: when an agent count > 12, building grows a 2nd floor (visible windows that light up per active agent).
3. Sidewalks + roads between buildings; courier path travels on the road, not straight-line through walls.
4. Signage with node name uses pixel-art glyph font (already in engine) + a small flag icon for the provider mix.
5. Reception zone (`entry`) gains a desk with a receptionist sprite who waves when a new agent enters.

**Acceptance:**
- Three roles visually distinct.
- Couriers visibly use roads (no through-wall lines).
- Hub building shows live CPU% to one decimal updating each second.

---

### Stream 6 — News ticker, time-lapse, share-to-mp4 📺

**Owns:**
- `ui/src/ui/components/agency-news-ticker.ts` (NEW)
- `ui/src/ui/views/agent-ops/agency-world.ts` — only the `tick` loop hook to broadcast events
- `ui/src/ui/views/agent-ops/agency-recorder.ts` (NEW) — `MediaRecorder` wrapper for canvas
- `ui/src/ui/components/gpu-animation-panel.ts` — mount ticker + record button
- Mobile: defer (web-only this stream)

**Deliverables:**
1. News ticker — bottom strip, marquee of last 8 events ("[15:42] claude-coder spawned · [15:42] research-bot reading 4 files · [15:43] tester ran 12 tests").
2. Time-lapse — record the last 60 s of canvas frames in memory; user clicks `⏪` → replays at 4×. ESC to exit.
3. Share-to-mp4 — `Record` button toggles `MediaRecorder.start()` on `app.view.captureStream(30)`. Records up to 60 s, then prompts download. Filename `agency-world-<iso>.webm`.
4. Demo mode — when `?demo=1` in URL, ignore gateway and emit synthetic events from a scripted source (12 agents, randomized tools, 30 messages, 5 task ingests). For marketing screen captures.

**Acceptance:**
- Ticker reads naturally, not jittery.
- Time-lapse replay is visually correct.
- Recorded webm plays in Chrome + VLC.
- `?demo=1` works without any backend running.

---

### Stream 7 — Mobile parity & WebView optimization 📱

**Owns:**
- `mobile/src/screens/agency-world/world-html.js` (entire file)
- `mobile/src/screens/agency-world/AgencyWorldView.js`
- `mobile/src/screens/agency-world/world-bridge.js` (NEW) — shared types

**Deliverables:**
1. Adopt the same `WorldEvent` union as Stream 2; align engine API names with web.
2. Replace per-frame full canvas redraw with dirty-rect redraw: track changed agent positions, only repaint those regions.
3. Use a hidden `<canvas>` sprite atlas (built once at boot) instead of redrawing each character.
4. Pinch-to-zoom snap targets: 0.5×, 1×, 1.5× — release snaps to nearest.
5. Double-tap to fit-all (calls `view: { op: 'fit' }`).
6. Haptic feedback on agent tap (via RN bridge `Haptics.selectionAsync()`).
7. Add `onError` to the WebView; if engine throws, render an "Engine crashed — tap to reload" overlay instead of a blank screen.
8. Memoize the HTML string so RN doesn't re-create the WebView on each props change.

**Acceptance:**
- 60 fps with 15 agents on a mid Android.
- Pinch and pan feel native.
- Tapping a character buzzes haptics and opens AgentFocusSheet.

---

### Stream 8 — Perf hardening + tests + docs ✅

*Runs after 1–7 ship.*

**Owns:**
- `ui/src/ui/views/agent-ops/__tests__/agency-world.test.ts` (NEW)
- `ui/src/ui/views/agent-ops/__tests__/agency-data-bridge.test.ts` (NEW)
- `docs/agency-world.md` (NEW user-facing doc)
- Adds `data-testid` hooks where needed in `agency-world-viz.ts` only

**Deliverables:**
1. Vitest unit tests for: `inferZone`, `inferProvider`, courier path lerp, sprite atlas frame lookup, AgencyDataBridge event coalescing.
2. Playwright (or simulated DOM) smoke test: mount AgencyWorld, feed 50 fake agents over 5 s, assert no console errors and `agents.size === 50`.
3. Perf budget: assert `tick` runs in <8 ms median with 50 agents under jsdom.
4. Public-facing markdown doc at `docs/agency-world.md` with screenshots and the metaphor explained.
5. Add an `<agency-world-viz>` storybook-style demo route at `/grid?gpu=1` that auto-adds a GPU panel on load so QA can jump straight to it.

**Acceptance:**
- All tests green in CI.
- Perf budgets enforced (fails build on regression > 25 %).
- Docs render correctly in Github + on the project site.

---

## Cross-cutting conventions

All streams MUST:
- Branch from `feat/web-terminals-reliable-glass`.
- Commit with prefix `feat(agency-world): ` or `fix(agency-world): `.
- Add a one-paragraph entry to `docs/agency-world-v2-enhancement-plan.md` under "Changelog" when complete.
- Run `npx vite build` in `ui/` and confirm `agency-world-viz-*.js` chunk exists and contains their unique strings.
- Run `node --check` on touched `.js` files.

Forbidden across all streams:
- Editing `neural-mesh-viz.ts` or its assets.
- Adding new npm dependencies (already have pixi, gsap, react-native-webview).
- Removing the `MESH` toggle from `gpu-animation-panel.ts`.

---

## Parallel agent dispatch — exact prompts

Run **all seven in parallel** from a single message via `mcp__infinibot__claude_coder` (one call per stream). The kanban tasks (created alongside this doc) carry the same prompts as descriptions. The orchestrator picks them up via `subagents.list` and the world animates them in real time.

### Worktree isolation

Each agent gets its own worktree to keep merges sane:

```
git worktree add ../InfiniBot-aw1 feat/agency-world-sprites
git worktree add ../InfiniBot-aw2 feat/agency-world-bridge
git worktree add ../InfiniBot-aw3 feat/agency-world-interact
git worktree add ../InfiniBot-aw4 feat/agency-world-fx
git worktree add ../InfiniBot-aw5 feat/agency-world-buildings
git worktree add ../InfiniBot-aw6 feat/agency-world-ticker
git worktree add ../InfiniBot-aw7 feat/agency-world-mobile
```

Each `claude_coder` call passes `workdir: '<absolute path to that worktree>'` and a `projectHint` of `Agency World v2`.

### Prompts (copy-paste ready)

#### Agent 1 — Sprite atlas

```
You are building Stream 1 of the Agency World v2 plan
(docs/agency-world-v2-enhancement-plan.md in this repo).

Goal: replace per-frame PIXI.Graphics draws of characters and zone decor
with a pre-rendered sprite atlas. 192-cell character sheet (6 providers
× 4 facings × 4 walk frames × 2 idle frames). Build once at boot to
off-screen canvas; upload as a PIXI.BaseTexture; use PIXI.Sprite per
agent and swap textureFrame for animation. Also wire existing
cozy-room assets (ui/public/assets/cozy-room/) into Zone decoration
where available.

Files in scope (edit only these):
  - ui/src/ui/views/agent-ops/agency-sprite-atlas.ts  (NEW)
  - ui/src/ui/views/agent-ops/agency-world.ts          (drawCharacter / AgentChar.redraw / Zone.drawDecor ONLY)
  - mobile/src/screens/agency-world/world-html.js      (drawCharacter equivalent)

Constraints:
  - No new npm dependencies.
  - Keep the public AgencyWorld API unchanged.
  - Graceful Graphics fallback while atlas loading.
  - Idle micro-animations: head bob, periodic blink, arm sway when "working".
  - Per-tool overlay glyph above head: ASCII for terminal, page for library,
    pencil for drafting.

Acceptance:
  - 30 walking agents at 60fps on M-series Mac.
  - All providers visually distinct.
  - npx vite build succeeds; agency-world-viz-*.js chunk grows by <40KB.
  - No regression in empty-state render.

Commit prefix: feat(agency-world):  - end with a "Test plan" section in commit body.
```

#### Agent 2 — Event-driven data bridge

```
You are building Stream 2 of docs/agency-world-v2-enhancement-plan.md.

Goal: replace 3.5s polling in agency-world-viz with a WebSocket-event
bridge for sub-200ms latency. Define a WorldEvent union, batch per
paint, fall back to polling on old gateways.

Files in scope (edit only these):
  - ui/src/ui/controllers/agency-data-bridge.ts        (NEW)
  - ui/src/ui/components/agency-world-viz.ts           (swap pollOnce for bridge.subscribe)
  - mobile/src/screens/agency-world/AgencyWorldView.js (RN equivalent bridge)
  - mobile/src/screens/agency-world/world-html.js      (extend handleMessage envelope)

The WorldEvent type is specified in the plan doc — copy it verbatim.

Subscribe to: subagents.*, sessions.*, kanban.tasks.created,
federation.heartbeat. On gateways missing those subscribe methods,
poll at 10s with a one-time console warning.

Acceptance:
  - Calling Read in a coder agent moves the character to library <250ms.
  - Spawn a subagent from chat: appears in world <250ms.
  - Disconnect/reconnect gateway: world clears and refills, no dupes.
  - npx vite build clean.

Commit prefix: feat(agency-world):
```

#### Agent 3 — Interactivity & focus panel

```
You are building Stream 3 of docs/agency-world-v2-enhancement-plan.md.

Goal: make agents and buildings clickable. Selecting an agent opens a
slide-in focus panel. Selecting a building filters/dims the world.

Files in scope (edit only these):
  - ui/src/ui/views/agent-ops/agency-world.ts          (onAgentClick/onNodeClick wiring ONLY; do not touch render path)
  - ui/src/ui/components/agency-world-viz.ts           (emit CustomEvent for selection)
  - ui/src/ui/components/agent-focus-panel.ts          (NEW Lit element, 300px wide, slides in from right)
  - ui/src/ui/components/gpu-animation-panel.ts        (host the side panel + wire selection events)
  - mobile/src/screens/NeuralMeshScreen.js             (route tap → existing AgentFocusSheet)

Panel shows: name, type, provider, current task, last tool kind+label,
files touched (count + list), progress%, run-id, three action buttons:
Open Session / Stop / Open Run.

Building click → set selected node; engine fades non-selected nodes to
35% opacity (add a setNodeFilter(id|null) public method).

Acceptance:
  - Click agent → panel opens <100ms with correct data.
  - Click building → other nodes dimmed.
  - Esc clears selection.
  - Mobile: tap → AgentFocusSheet opens.

Commit prefix: feat(agency-world):
```

#### Agent 4 — Effects, day/night, weather, audio

```
You are building Stream 4 of docs/agency-world-v2-enhancement-plan.md.

Goal: ambient & event-driven FX that make the world feel alive without
distracting. Day/night cycle. Per-tool particles. Completion confetti +
chime. Blocked alarm. Optional weather. Camera shake on failure.
Lightning on gateway disconnect.

Files in scope (edit only these):
  - ui/src/ui/views/agent-ops/agency-fx.ts            (NEW Particle/Confetti/Lightning/RainDrop classes)
  - ui/src/ui/views/agent-ops/agency-world.ts         (tick loop + effect layer wiring ONLY)
  - mobile/src/screens/agency-world/world-html.js     (matching ambient FX)
  - ui/public/assets/agency-world/audio/chime.mp3     (sourced free-license; add to repo)
  - ui/public/assets/agency-world/audio/alert.mp3
  - ui/public/assets/agency-world/audio/ding.mp3

Day/night: linear interp of background gradient over 24h local. Stars
visible 19:00–07:00 with parallax. Moon position by hour.

Audio: global mute toggle in localStorage 'agencyWorld.muted', default
true. Use AudioBuffer + AudioContext pool (no new <audio> per chime).

Constraints:
  - Particle pool capped at 200 (oldest evicted).
  - No effect lingers >1.5s except weather overlay.
  - Honor prefers-reduced-motion: skip camera shake.

Commit prefix: feat(agency-world):
```

#### Agent 5 — Building art & federation skyline

```
You are building Stream 5 of docs/agency-world-v2-enhancement-plan.md.

Goal: differentiate buildings by federation role and grow them with the
agent count. Show live HW stats on roof. Couriers travel on roads.

Files in scope (edit only these):
  - ui/src/ui/views/agent-ops/agency-buildings.ts     (NEW templates per role)
  - ui/src/ui/views/agent-ops/agency-world.ts         (Building.buildShell/drawLabel/layout grid)
  - mobile/src/screens/agency-world/world-html.js     (matching draws)

hub role → wide HQ with antenna + clock tower.
satellite role → smaller building with satellite dish on roof.
2nd floor when agentCount > 12; windows light per active agent.
Hardware stats (CPU/mem/GPU) on roof — pull via useHardware data already
piped through bridge as node.upsert.patch.hardware (Stream 2 dependency
for live updates; until then read once from a fresh fetch).

Sidewalks + roads between buildings. Add a getRoadPath(fromBld, toBld)
helper; Courier uses it instead of straight-line.

Receptionist sprite in entry zone waves on agent enter.

Commit prefix: feat(agency-world):
```

#### Agent 6 — News ticker, time-lapse, share-to-mp4, demo mode

```
You are building Stream 6 of docs/agency-world-v2-enhancement-plan.md.

Goal: storytelling features that turn the live world into marketing
material. News ticker, time-lapse, record-to-webm, demo mode.

Files in scope (edit only these):
  - ui/src/ui/components/agency-news-ticker.ts        (NEW Lit element, bottom strip marquee)
  - ui/src/ui/views/agent-ops/agency-recorder.ts      (NEW MediaRecorder wrapper)
  - ui/src/ui/views/agent-ops/agency-world.ts         (broadcast events to ticker via CustomEvent; no other changes)
  - ui/src/ui/views/agent-ops/agency-demo-source.ts   (NEW synthetic event generator)
  - ui/src/ui/components/gpu-animation-panel.ts       (mount ticker + record button + read ?demo=1)

Ticker: last 8 events, marquee 40px tall, "[hh:mm] agent action" style.
Time-lapse: 60s ring buffer of canvas frames (PIXI app.renderer.extract
.image()) replayable at 4x; ESC exits.
Record: MediaRecorder on app.view.captureStream(30); cap 60s; offer
download as agency-world-<iso>.webm.
Demo: when location.search includes 'demo=1' or 'agency-demo=1',
suppress gateway data and emit synthetic WorldEvents (12 agents, ~3
tool changes/sec, ~1 spawn/15s, ~1 message/2s, ~1 task ingest/8s).

Mobile: skip this stream entirely (web-only).

Commit prefix: feat(agency-world):
```

#### Agent 7 — Mobile parity & WebView perf

```
You are building Stream 7 of docs/agency-world-v2-enhancement-plan.md.

Goal: mobile feature parity with the web engine, optimized for 60fps on
phones. Single shared WorldEvent contract.

Files in scope (edit only these):
  - mobile/src/screens/agency-world/world-html.js     (engine — full rewrite acceptable)
  - mobile/src/screens/agency-world/AgencyWorldView.js
  - mobile/src/screens/agency-world/world-bridge.js   (NEW shared types/util)

Adopt WorldEvent union from Stream 2; engine renames to match web names.
Dirty-rect repaint: track changed agent rects, only clear+redraw those
regions (use a separate canvas for stationary background).
Sprite atlas: build once to a hidden canvas; agents draw via drawImage.
Pinch zoom snap to 0.5/1/1.5; double-tap to fit-all (post
{ type:'view', op:'fit' }).
Haptics on agent tap via RN bridge (Haptics.selectionAsync()).
WebView onError → "Engine crashed — tap to reload" overlay.
Memoize HTML string.

Acceptance:
  - 60fps with 15 agents on a Pixel 6.
  - Pinch / pan feel native.
  - Tap buzzes + opens AgentFocusSheet.

Commit prefix: feat(agency-world):
```

### Sequence

```
T0: parallel spawn streams 1, 2, 3, 4, 5, 6, 7  (mcp__infinibot__claude_coder × 7)
T+30m: poll subagents_list, check status
T+90m: agents typically complete; review each PR
T+2h:  merge stream-by-stream into feat/web-terminals-reliable-glass
       (no rebases — squash merge in order 2, 1, 4, 5, 3, 6, 7)
T+3h:  spawn Stream 8 (tests + docs) — runs serial after merges
```

If any stream agent reports a blocker, respawn with `subagents.respawn` and the full original prompt + diff of partial progress.

---

## Risks & mitigations

| Risk | Mitigation |
|---|---|
| Two streams touch `agency-world.ts` at the same time → merge conflict | File-line scope above is strict; reviewers reject PRs that touch out-of-scope lines |
| Sprite atlas balloon main chunk | Atlas built at runtime, no static asset addition; chunk grows <40KB |
| Event bridge depends on RPC methods that don't exist yet | Stream 2 includes polling fallback; ships safely |
| Mobile perf regression with effects | Stream 4 is web-only audio; Stream 7 owns mobile perf budget |
| Confetti / camera shake annoy users | Honor `prefers-reduced-motion`; default audio muted |
| Time-lapse uses too much RAM | Cap ring buffer 60s × 30fps × 256×144 thumbnails ≈ 50MB; downsample to thumbnail; replay upscaled |

---

## Definition of done (DoD) for v2 ship

- All 7 streams merged.
- Stream 8 tests green in CI.
- A 30-second screen capture exists at `docs/assets/agency-world-v2.gif` with ≥10 agents working in parallel.
- README mentions Agency World as the headline mesh view.
- Memory `agent_ops_wiring.md` updated to reference v2 features.

---

## Autonomy goal

Persist this plan as an autonomy goal:
- `mcp__infinibot__kanban` project: **"Agency World v2"**, status `in_progress`.
- 8 child tasks (one per stream), status `backlog`, priority `high`.
- A workflow document `Agency World v2 — Parallel Dispatch` containing the seven prompts above.

The autonomy daemon picks up `pending` tasks tagged `auto-eligible: true` from this project at the next cron, spawning the matching coder agents into separate worktrees. Operator can intervene at any time via the kanban board.

---

## Changelog

*(append as streams ship)*

- 2026-05-29 — Plan written. Engine v1 already shipped (`feat/web-terminals-reliable-glass`).
