← Back ← Back
# VibeScope Master Plan — From VSIL Viewer to True VSIL Editor

> **Plan ID:** VSM-PLAN-v1.0
> **Date:** 2026-07-08
> **Owner:** Mavis (`413419604156710`, FreshVibe Studio root)
> **Repo:** `avidtech6/freshvibestudio`
> **Workspace:** VibeScope
> **Status:** DRAFT — Awaiting operator ratification

---

## 0. Executive Summary

VibeScope today is a **read-only VSIL viewer**: 6 cards mounted in a 2×2 grid that render mock VSIL scenes as static SVG, with no edit primitives, no paint surface, no layer manipulation, no animation controls, no Monaco/CodeMirror editor, and no WebGL2 shaders (only a probe). The Pact has always demanded a true paint/visual editor with three engines, two layouts, six engine subsystems (brush/transform/selection/layer/mask/animation), and live VSIL + VSIL-A round-trip.

This plan transforms VibeScope from viewer to editor across **9 Phases, 38 Tasks, 12 Tracks**, executed by a coordinated multi-agent team, with drift-check v3.3.x + AMEND-006 + DC-VC-FALLBACK-N as the continuous validation surface.

**Outcome at DONE**: VibeScope delivers a Photoshop-class visual editor in the browser — paint on `<canvas>`, manipulate layers live, two-way-bind properties, scrub a VSIL-A timeline with bezier interpolation, edit VSIL source in a real code editor with live re-evaluation, save/load/export to canonical VSIL IR — and ships with full Pact alignment (zero drift, zero stub markers, all engine subsystems implemented).

---

## 1. Pact Alignment Charter (Constitutional Requirements)

These are **law**, not suggestions. Every phase must satisfy them.

### 1.1 DNA Declarations (from `pact/workspaces/vibescope/workspace.md`)

| DNA Tag | Value | Phase Commitment |
|---|---|---|
| `@workspace` | `vibescope` | All work stays in `studio/modules/vibescope/src/` |
| `@layout` | `cinema,dev` | Phase 7+8 builds both layouts distinctively |
| `@layouts_count` | `2` | Cinema + Dev only — no third layout |
| `@default_layout` | `cinema` | First paint = Cinema |
| `@dev_layout_gate` | `super_admin` | Phase 8 hardens `DevLayoutGate` with real OAuth removal of `FUTURE-OAUTH-INTEGRATION` marker |
| `@engine_family` | `cinema_procedural,cinema_ultra,render` | All 3 engines implemented, no stubs |
| `@canonical_ir` | `vsil` | All edits round-trip through VSIL (no shadow state) |
| `@animation_ir` | `vsil-a` | Timeline drives the `evaluator.ts` engine, not local component state |
| `@chips` | `[editor,visual,timeline,inspector,docs,replay,diff,tests]` | All 8 fragments surface real editor functionality (not 6 mock cards + 2 stubs) |

### 1.2 Engine Subsystem Fragments (from `pact/platform/engines/cinema-procedural-engine.md`)

Six subsystems must each be implemented and routed through Cinema Procedural Engine:

| Subsystem Fragment | Phase | Implementation Surface |
|---|---|---|
| `cinema.engine.procedural.subsystem.brush.001` | Phase 4 | Brush engine in `paint/BrushEngine.ts` (real-time input → stroke) |
| `cinema.engine.procedural.subsystem.transform.001` | Phase 4 | Transform engine in `paint/TransformEngine.ts` (move/scale/rotate with snap-to guides) |
| `cinema.engine.procedural.subsystem.selection.001` | Phase 4 | Selection engine in `paint/SelectionEngine.ts` (marquee + lasso) |
| `cinema.engine.procedural.subsystem.layer.001` | Phase 5 | Layer model in `vsil/layerOps.ts` (reorder/group/composite) |
| `cinema.engine.procedural.subsystem.mask.001` | Phase 6 | Mask engine in `paint/MaskEngine.ts` (paint/link/mode-switch) |
| `cinema.engine.procedural.subsystem.animation.001` | Phase 6 | Animation engine in `paint/AnimationEngine.ts` (live keyframe drag, linear interp) |

### 1.3 Engine Quality Tiers (per cinema-procedural-engine.md §1)

- **Procedural Engine**: 16ms frame budget, Draft/Good quality, **MUST never block UI**, MUST degrade gracefully on failure.
- **Ultra Engine**: 33ms frame budget, Best quality, GPU-accelerated (WebGL2), bezier + AA + post-processing. MUST honestly fall back to procedural when WebGL2 unavailable (already implemented; verifier must confirm).
- **Render Engine**: Frame-by-frame, full quality, CLI-compatible bytes. MUST NOT require DOM.

### 1.4 Drift-Check Invariants (per `pact/scripts/audit-chip-system-v3.3.x.sh`)

These MUST stay at 0 drift violations through every phase:

| Rule | Severity | What it locks |
|---|---|---|
| DC-VC-1 | critical | Vibe Card is the only always-mounted Panel Card |
| DC-VC-2 | major | Each fragment maps to ≤3 surfaces |
| DC-VC-3 | major | Inner Card stack cap = 3 |
| DC-VC-7 | major | Surface grammar locked to canonical names |
| DC-VC-9 | minor | Fragment grammar canonical to DNA `@chips` |
| **DC-VC-FALLBACK-N** | critical | VibeScope must have **exactly 1** dispatcher emitting `data-vibescope-active-card` |
| AMEND-006 §3.1 | critical | Lowercase chip ids in registry (Title-Case display only) |

### 1.5 Read-Only Invariant (Inspector)

Per `pact/platform/inspector/inspector.md`: Inspector is a **read-only viewer**. Modifications happen in source surface. Phase 7 introduces Inspector *edit affordances* (Properties panel) **as a separate concern** bound to the source surface, NOT inside the Inspector card itself.

---

## 2. Current State vs DONE State

| Capability | Current | DONE |
|---|---|---|
| Paint surface (`<canvas>` 2d context) | ❌ none | ✅ Full brush engine, layer-aware compositing |
| Layer add/delete/reorder via UI | ❌ none | ✅ DOM panel + drag-to-reorder + visibility/blend toggle |
| Properties two-way binding | ❌ Inspector is read-only | ✅ Inspector Props mode reads + writes to live VSILSource |
| Selection (marquee + lasso) | ❌ none | ✅ Click-hit + marquee drag + multi-select via shift |
| Transform handles (move/scale/rotate) | ❌ none | ✅ DOM handles anchored to selection bounds |
| Mask painting | ❌ none | ✅ Brush engine routed to mask channel |
| Animation keyframe drag | ❌ static dots | ✅ Drag-to-move keyframes on timeline, live re-eval |
| VSIL-A scrubber (frame, bezier interp) | ❌ visual preview has range slider | ✅ Timeline scrubber drives `evaluator.evaluateFrame()`, scene re-renders |
| Live source editor (Monaco/CM) | ❌ pretty-print + contenteditable | ✅ Monaco with VSIL syntax + live-eval-on-blur |
| Layer subsystem in engine | ❌ CinemaProcedural just renders | ✅ Layer ordering / grouping / compositing via VSIL ops |
| Cinema Ultra WebGL2 shader | ❌ probe + fallback only | ✅ Real shader pass for layer compositing |
| Render Engine CLI bytes | ✅ renders 1 frame | ✅ Multi-frame batch CLI driver |
| Cinema vs Dev layout differentiation | ❌ both 2×2 same cards | ✅ Cinema = paint-first, Dev = data-first |
| Save/load/export VSIL doc | ❌ no persistence | ✅ CodeX-backed save + JSON + SVG export |
| VSIL ↔ scene graph round-trip | partial (read only) | ✅ every edit mutates `VSILSource`, parser re-parses |
| All 8 chips work (Editor/Visual/Timeline/Inspector/Docs/Replay/Diff/Tests) | 6 work, Replay+Diff thin | ✅ All 8 fragments drive real editor functionality |

---

## 3. The 9 Phases

### Phase 1 — Foundation Lock + Studio Rename Hygiene
**Duration estimate**: 0.5 day
**Goal**: Lock the foundation: file-system shape, rename `<Workspace>` placeholders in pact templates, drift-check pristine.

**Inputs**:
- Current pact at `pact/workspaces/vibescope/` (5 files)
- Repo HEAD at `833d6c3`

**Outputs**:
- All `pact/workspaces/vibescope/*.md` files (already done — verified 2026-07-07 audit)
- Drift-check v3.3.x baseline = `32/0` PASS (verified)
- `studio/modules/vibescope/src/` directory tree confirmed

**Acceptance Criteria**:
- `bash pact/scripts/audit-chip-system-v3.3.x.sh` → 32/0 PASS (no regression)
- All 5 workspace pact files contain the VibeScope-specialised template (not the raw `<Workspace>` placeholder version)
- `@chips` array matches `studio/modules/vibescope/src/registry/fragmentRegistry.ts` `FRAGMENTS` exactly

**Drift-Checks**: DC-VC-1..9, DC-VC-FALLBACK-N, AMEND-006 §3.1
**Pact Compliance**: `vibescope.cockpit.scope.001`, `vibescope.workspace.behaviour.scope.001`, `vibescope.workspace.surfaces.definition.001`
**Risks**: Low. Risks already mitigated by Helper Mavis's prior pact-alignment work.
**Mitigations**: Pre-flight drift-check before any subsequent phase.

---

### Phase 2 — Editor Store: VSIL as Live Mutable Source of Truth
**Duration estimate**: 2-3 days
**Goal**: Replace mock JSON imports with a live `VSILSource` that flows through every edit. Every paint stroke, every Inspector tweak, every keyframe drag mutates the same store. The engines consume from the store; no shadow state.

**Inputs**:
- Current `vsil/parser.ts` (read-only)
- Current `vsil/sceneGraph.ts` (read-only)
- 8 mock JSON files at `studio/modules/vibescope/src/mocks/*.vsil.json`

**Outputs**:
- New `studio/modules/vibescope/src/state/VSILStore.ts` — Zustand-style store with `useVSILStore()` hook
- New `studio/modules/vibescope/src/vsil/vsilOps.ts` — pure mutation primitives (`addLayer`, `removeLayer`, `reorderLayer`, `updateTransform`, `setBlendMode`, `setVisibility`)
- All 6 mocks retained as initial state seeds (`useVSILStore.getState().loadVSIL('editor', editorMock)`)
- 6 cards (Editor/Visual/Inspector/Timeline/Docs/Replay) refactored to read from store instead of importing mocks directly
- Mutation log at `studio/modules/vibescope/src/state/mutationLog.ts` — every op pushed for undo/redo and audit

**Acceptance Criteria**:
- `useVSILStore` has 1 global instance per workspace session (proven via DevTools)
- All 6 cards reflect a single change in <16ms (procedural budget) when a mutation is dispatched
- Mutations are pure + immutable (Object.freeze per `vsil.a.layer.state.node.001`)
- `mutationLog.length` increases by 1 per op, never decreases until undo
- All existing tests still pass (parser, sceneGraph, evaluator, keyframeExtractor, assetRegistry)

**Drift-Checks**: DC-VC-1 (only 1 always-mounted Card), DC-VC-2 (≤3 surfaces per fragment), DC-VC-7 (canonical surface names)
**Pact Compliance**: `pipeline.vsil.definition.001`, `pipeline.vsil.scene_structure.001`, `cinema.engine.procedural.no.ui.block.001`, `cinema.engine.procedural.fidelity.draft.001`, `vsil.a.layer.state.node.001` (immutable)
**Risks**:
- **High**: Subtle aliasing between store state and derived scene graph. If a consumer reads from scene graph instead of store, mutations won't propagate.
- **Med**: React 18 concurrent mode + Zustand selectors can drop updates if selector shape is wrong.
**Mitigations**:
- Convention: all reads from `useVSILStore(s => s.layers)`. All writes go through `vsilOps.*` functions.
- Tests: `vsilOps.test.ts` proves mutation idempotence + scene graph re-derivation produces equivalent tree.
- Add `data-vsil-source-version` attribute on store consumer root for debugging.

---

### Phase 3 — Scrubber + VSIL-A Wired to Engine Pipeline
**Duration estimate**: 2 days
**Goal**: Replace the local `useState(0)` time slider in VisualCard with the real Scrubber. Scrubber drives `evaluator.evaluateFrame(source, tracks, anchor)` from `vsil/evaluator.ts`. Result feeds CinemaProcedural as the new FrameState.

**Inputs**:
- `src/scrubber/` types + hooks (`useFrameAt`, ScrubberAnchor, ScrubberBar, ScrubberControls)
- `src/vsil/evaluator.ts` (already implemented, deterministic + immutable + reversible)
- `src/vsil/keyframeExtractor.ts` (already implemented)
- New `src/mocks/timeline.tracks.json` (3-5 sample animation tracks)

**Outputs**:
- New `src/scrubber/ScrubberContext.tsx` — React context provider wrapping the workspace
- All 6 cards consume `useScrubber()` instead of `useState` for time
- CinemaProcedural accepts `FrameState` (post-evaluation) instead of raw `VSILSource`
- New `src/engines/CinemaProcedural.eval.ts` — engine variant that renders evaluated frame state with animated transforms
- New `src/__tests__/scrubber-evaluator-integration.test.tsx` — proves scrub → evaluate → render pipeline

**Acceptance Criteria**:
- Scrubbing Timeline triggers `evaluateFrame` per frame
- Procedural engine renders at <16ms (Draft) on 60-frame composition with 4 animated layers (Playwright measured)
- Evaluation is deterministic (same source + same tracks + same time = same frame, verified by snapshot test)
- Bezier interpolation works for `transform.translate` track (visible curve in motion)
- `useFrameAt(frame)` hook returns same value on identical input (no clock-drift)

**Drift-Checks**: DC-VC-1, DC-VC-7
**Pact Compliance**: `vsil.a.composition.union.001`, `vsil.a.interpolation.001`, `cinema.engine.procedural.vsil.reduced.001`, `cinema.engine.procedural.vsil.a.001`, `cinema.engine.procedural.latency.16ms.001`
**Risks**:
- **High**: Evaluator + Procedural engine coupling could break the 16ms budget at high track count.
- **Med**: Scrubber state must NOT live in the local `useState` of VisualCard — must be global.
**Mitigations**:
- Performance test: 100 tracks × 10 keyframes < 5ms (per evaluator's own perf comment).
- Lint rule (custom ESLint): disallow `useState<number>` in `modules/vibescope/src/cards/` for time values.
- Document the Scrubber context shape in `src/scrubber/README.md`.

---

### Phase 4 — Paint Surface + Brush + Transform + Selection Engines
**Duration estimate**: 4-5 days (largest phase)
**Goal**: Implement the 3 interaction subsystems (brush, transform, selection) on a real HTML5 `<canvas>`. The paint surface becomes the primary editing surface in VisualCard.

**Inputs**:
- Phase 2's `vsilOps` (mutations)
- Phase 3's evaluated FrameState (so animated transforms are visible during paint)
- `pact/platform/engines/cinema-procedural-engine.md` subsystem fragments

**Outputs**:
- New `studio/modules/vibescope/src/paint/PaintSurface.tsx` — `<canvas>` mount with pointer event dispatch
- New `studio/modules/vibescope/src/paint/BrushEngine.ts` — implements `cinema.engine.procedural.subsystem.brush.001`: stamp-based brush with size/opacity/hardness, 60fps, linear interp on `transform.translate`
- New `studio/modules/vibescope/src/paint/TransformEngine.ts` — implements `cinema.engine.procedural.subsystem.transform.001`: move/scale/rotate handles with snap-to guides
- New `studio/modules/vibescope/src/paint/SelectionEngine.ts` — implements `cinema.engine.procedural.subsystem.selection.001`: marquee rect + lasso via Path tool
- New `studio/modules/vibescope/src/paint/HitTest.ts` — point-in-layer test (rect / ellipse / path / text)
- VisualCard refactored: `<PaintSurface>` replaces the SVG dump; brush + selection drive `vsilOps` mutations
- Selection state in `state/SelectionStore.ts` (separate from VSILStore — selection is ephemeral)
- New tests: `paint/brush.test.ts`, `paint/transform.test.ts`, `paint/selection.test.ts`, `paint/hitTest.test.ts`

**Acceptance Criteria**:
- Mouse down on canvas → brush stamp at pointer → drag → continuous strokes
- Each stroke dispatched as `vsilOps.addPaintLayer` or `vsilOps.updateLayer(path)` (depending on mode)
- Click on a layer → SelectionStore.selected = layerId → handles render
- Drag a handle → real-time `vsilOps.updateTransform` → Procedural re-renders <16ms
- Marquee drag → multi-select → Inspector updates to multi-selection state
- Hit-test: clicking inside a rect's bounds selects it; clicking outside deselects
- All 3 engines emit `data-paint-engine-output` attribute with engine name
- Brush + transform + selection work in 2D space (camera is 2D for now; 3D camera deferred to Phase 9)

**Drift-Checks**: DC-VC-1, DC-VC-2, DC-VC-7, AMEND-006
**Pact Compliance**:
- `cinema.engine.procedural.subsystem.brush.001` (real-time, 60fps, low-latency)
- `cinema.engine.procedural.subsystem.transform.001` (60fps, snap-to)
- `cinema.engine.procedural.subsystem.selection.001` (marquee + lasso, real-time preview)
- `cinema.engine.procedural.no.ui.block.001`
- `cinema.engine.procedural.latency.16ms.001`
- `cinema.engine.procedural.failure.budget.001`
- `cinema.engine.procedural.security.sandbox.001`

**Risks**:
- **Critical**: Canvas performance — paint at 60fps with 50+ layers risks dropped frames. The Procedural engine MUST degrade gracefully per `failure.budget.001`.
- **High**: Pointer event semantics differ between mouse, touch, pen — must handle all three.
- **Med**: Transform handles need to know layer bounds; getting bounds from procedural SVG (already in DOM) vs from VSILStore (canonical) is a tension.
**Mitigations**:
- Procedural engine MUST be the source of truth for render — bounds derived from `vsilOps.getBounds(layer)`, not from DOM measurement.
- Pen pressure wired via `PointerEvent.pressure` (browser-supported, falls back to 0.5 if absent).
- Performance test: 50 layers + active brush = <16ms/frame (Playwright measured, fail test if >33ms).
- Hit-test runs against scene graph (pre-computed per-frame), not against live DOM.

---

### Phase 5 — Layer Panel UI + Layer Ops
**Duration estimate**: 2 days
**Goal**: A real layer panel that lets the user add/delete/reorder layers, toggle visibility, change blend mode, group/ungroup. Lives in the Inspector's AST mode and the Cinema layout's Structure cell.

**Inputs**:
- Phase 2's `vsilOps`
- Phase 4's SelectionStore

**Outputs**:
- New `studio/modules/vibescope/src/cards/layer-panel/LayerPanel.tsx` — DOM tree with drag-to-reorder, visibility toggle, blend-mode dropdown
- New `studio/modules/vibescope/src/cards/layer-panel/LayerRow.tsx`
- New `studio/modules/vibescope/src/vsil/layerOps.ts` — `addLayer`, `removeLayer`, `reorderLayer`, `groupLayers`, `ungroupLayer`, `setBlendMode`, `setVisibility`, `renameLayer`, `duplicateLayer`
- Inspector AST mode shows the live tree (already exists; rewires to read from VSILStore)
- CinemaLayout's Structure cell now points at LayerPanel (instead of duplicating InspectorCard)
- DevLayout's Structure cell uses DataCard (renamed) showing layer state in JSON form
- New tests: `vsil/layerOps.test.ts`, `cards/layer-panel/LayerPanel.test.tsx`

**Acceptance Criteria**:
- Click "+" → `vsilOps.addLayer('rect')` → new layer appears in tree + in canvas within 16ms
- Drag layer row → `vsilOps.reorderLayer(fromIndex, toIndex)` → z-order updates → render reflects
- Toggle visibility eye → `vsilOps.setVisibility(id, false)` → layer hidden in canvas
- Group selection → `vsilOps.groupLayers([id1, id2])` → new group layer with children
- Inspector AST mode shows the live tree reflecting all ops
- All ops are reversible via mutationLog (undo button)

**Drift-Checks**: DC-VC-1, DC-VC-2, DC-VC-3 (inner stack cap)
**Pact Compliance**: `cinema.engine.procedural.subsystem.layer.001` (ordering, grouping, basic compositing), `pipeline.vsil.scene_structure.001`, `vsil.a.composition.union.001`
**Risks**:
- **Med**: Group operations can produce invalid parent-child states (e.g., group within group).
- **Med**: Blend mode values not in canonical enum (e.g., `multiply-strong`).
**Mitigations**:
- `layerOps` enforces `VALID_BLEND_MODES` enum; invalid input → throws VSILParseError.
- Group operations validate: cannot group a parent with its descendant (would create cycle).
- Layer ops produce versioned operations (`vsil.layer.op.v1`) for the mutationLog.

---

### Phase 6 — Mask + Animation Subsystems
**Duration estimate**: 3 days
**Goal**: Two more engine subsystems. Mask paints a mask channel on a layer. Animation makes the brush engine write to a `float-keyframe-track` (live keyframe drag).

**Inputs**:
- Phase 4's BrushEngine (now also writes to mask channels)
- Phase 3's Scrubber + Evaluator pipeline
- `pact/platform/vsil/vsil-a.md` track types

**Outputs**:
- New `studio/modules/vibescope/src/paint/MaskEngine.ts` — implements `cinema.engine.procedural.subsystem.mask.001`: paint to mask, link mask to layer, switch mask mode (add/subtract/intersect)
- New `studio/modules/vibescope/src/paint/AnimationEngine.ts` — implements `cinema.engine.procedural.subsystem.animation.001`: live keyframe drag → writes new keyframe to track → linear interp during scrub
- New `studio/modules/vibescope/src/vsil/animationOps.ts` — `addKeyframe`, `moveKeyframe`, `removeKeyframe`, `setInterpolation`, `setBezierHandles`
- New `studio/modules/vibescope/src/cards/timeline/KeyframeRow.tsx` — draggable keyframe marker per track
- TimelineCard refactored: shows per-layer animated properties with draggable keyframes
- VisualCard mask mode toggle: brush routed to mask channel instead of layer channel
- New tests: `paint/mask.test.ts`, `paint/animation.test.ts`, `vsil/animationOps.test.ts`

**Acceptance Criteria**:
- Mask mode: paint strokes appear as mask only (not visible directly); linked layer uses mask for visibility
- Animation: select layer → choose "Animate Position" → timeline shows track at frame 0; drag the keyframe to frame 30 → Procedural engine interpolates `transform.translate` linearly
- Multiple keyframes on same track → drag one between two existing → ordering preserved, time clamped
- Bezier handles draggable on keyframes with `interpolation: 'bezier'`
- Scrubber playback animates position over 30 frames at 60fps

**Drift-Checks**: DC-VC-1, DC-VC-2, DC-VC-7
**Pact Compliance**:
- `cinema.engine.procedural.subsystem.mask.001` (paint, link, mode-switch)
- `cinema.engine.procedural.subsystem.animation.001` (live keyframe drag, linear interp; bezier = Ultra)
- `vsil.a.keyframe.tracks.001` (7 canonical track kinds — Phase 6 implements float + vec2; rest in Phase 9)
- `vsil.a.interpolation.001` (linear default for float-keyframe-track)

**Risks**:
- **High**: Animation engine performance during scrub — if every frame triggers full keyframe re-sort, will not hit 16ms budget.
- **Med**: Mask channel storage: where does the mask paint data live? Need a `maskChannel: Uint8Array` per layer.
**Mitigations**:
- Track ops are sorted once on insertion; subsequent reads are O(log n) binary search.
- Mask data stored as base64-encoded PNG on `layer.maskChannel` (1 channel, 8-bit).
- Performance test: scrub a 60-frame animation with 4 animated layers = <16ms/frame.

---

### Phase 7 — Properties Panel: Two-Way Binding to Live VSIL
**Duration estimate**: 2 days
**Goal**: Inspector's Props mode becomes a real two-way binding surface. Changes there mutate the live VSILSource; canvas re-renders in <16ms.

**Inputs**:
- Phase 2's `vsilOps`
- Phase 4's SelectionStore
- Existing `inspector/PropsMode.tsx` (currently read-only)

**Outputs**:
- PropsMode refactored: each property is a controlled input bound to `vsilOps.updateLayer`
- Per-property validators (e.g., `opacity` clamps 0..1, `scale` clamps >0)
- New `studio/modules/vibescope/src/inspector/propertyBindings.ts` — declarative property-to-op mapping
- New `studio/modules/vibescope/src/__tests__/props-two-way.test.tsx`
- Undo/redo wired to `mutationLog`

**Acceptance Criteria**:
- Change a layer's `fill` color → VSILStore updates → CinemaProcedural re-renders with new color in <16ms
- Change `opacity` slider → VSILStore updates → Procedural re-renders
- Multi-selection: changing common property (e.g., visible) applies to all selected layers
- Invalid input (e.g., opacity = -0.5) is clamped to valid range, never silently dropped
- Inspector remains read-only for ALL OTHER MODES (AST, State, Logs, Metadata) per pact

**Drift-Checks**: DC-VC-1, DC-VC-2
**Pact Compliance**: `cinema.engine.procedural.subsystem.adjustment.001` (Levels/Exposure/Saturation deferred to Phase 9; Phase 7 implements transform + fill/stroke)
**Risks**:
- **High**: Breaking the Inspector read-only invariant — if Props mode writes, does AST mode? It must NOT.
- **Med**: Two-way binding with controlled inputs + React 18 batching — fast typing could thrash.
**Mitigations**:
- Hard rule: only Props mode writes. AST, State, Logs, Metadata are read-only by static type + lint.
- Property writes are debounced 8ms (one frame at 60fps × 0.5) inside PropsMode for slider drags.
- Snapshot test: AST mode never emits a write op (verify via mock spy on `vsilOps`).

---

### Phase 8 — Cinema vs Dev Layout Differentiation + Editor Source View
**Duration estimate**: 3 days
**Goal**: Make Cinema and Dev layouts visibly distinct. Cinema = paint-first (PaintSurface + Properties + LayerPanel + Timeline). Dev = data-first (Monaco/CodeMirror with VSIL source + DataCard state tree + DevDesignerCard + LogsCard).

**Inputs**:
- All prior phases
- Existing `DevLayoutGate.tsx` (already gates super_admin)

**Outputs**:
- New `studio/modules/vibescope/src/dev/DesignerCard.tsx` — Monaco editor with VSIL JSON syntax; live-eval on blur (or 500ms debounce)
- New `studio/modules/vibescope/src/dev/DataCard.tsx` — live state tree (already named this way but needs real content)
- New `studio/modules/vibescope/src/dev/LogsCard.tsx` — mutation log + Procedural Engine telemetry (frame time, drop frames)
- New `studio/modules/vibescope/src/dev/useMonacoVSIL.ts` — Monaco language definition for VSIL (JSON schema-aware autocomplete)
- CinemaLayout: 4 cells = PaintSurface + PropertiesPanel + LayerPanel + TimelineCard (replaces current VisualCard/InspectorCard duplication)
- DevLayout: 4 cells = DesignerCard + DataCard + LogsCard + InspectorCard (single inspector, AST mode)
- `DevLayoutGate` hardened: production role check via `useUser().isSuperAdmin`; `FUTURE-OAUTH-INTEGRATION` marker **removed** (now: real role backend OR explicit deferral marker `OAUTH-DEFERRED-VS-2026Q3`)
- Footer-level layout switcher in CentreStage (per `vibescope.layout.switching.001`)
- New tests: `layouts/CinemaLayout.test.tsx`, `layouts/DevLayout.test.tsx`, `dev/DesignerCard.test.tsx`

**Acceptance Criteria**:
- CinemaLayout cells: paint surface (PaintSurface) + properties (Inspector Props) + structure (LayerPanel) + timeline (TimelineCard)
- DevLayout cells: designer (Monaco) + data (DataCard) + logs (LogsCard) + inspector (InspectorCard AST)
- Layout switcher in CentreStage footer toggles between Cinema + Dev without remounting chips
- Monaco editor: editing a layer's `transform.translate` in JSON re-evaluates → cinema_procedural re-renders in <16ms
- Monaco errors (invalid VSIL schema) → inline gutter markers, no console error storm
- Dev access denied to non-super-admin users with explicit fragment_id reference (already there)

**Drift-Checks**: DC-VC-1, DC-VC-2, DC-VC-3, DC-VC-7, DC-VC-FALLBACK-N (still 1 dispatcher)
**Pact Compliance**:
- `vibescope.layout.cinema.definition.001` (Preview · Properties · Structure · Timeline — now real cells, not stubs)
- `vibescope.layout.dev.definition.001` (Designer · Structure · Data · Logs)
- `vibescope.layout.cinema.access.001` (public)
- `vibescope.layout.dev.access.001` (super_admin only)
- `vibescope.layout.switching.001` (footer-level, not chip-driven)

**Risks**:
- **High**: Monaco bundle size — adds ~3MB to the studio bundle. Acceptable for VibeScope-only path.
- **High**: Cinema + Dev being so different increases the surface area of bug-finding.
- **Med**: Layout switcher persistence across sessions requires localStorage — verify no conflict with prior Chip-id persistence.
**Mitigations**:
- Monaco is dynamically `import()`'d inside DesignerCard (code-split). Not in main bundle.
- DevLayout only mounts when `useUser().isSuperAdmin === true`; no eager work for non-admins.
- Layout preference stored under `vibescope.layout.user_pref.v1` localStorage key (namespaced).

---

### Phase 9 — Cinema Ultra Real WebGL2 + Render CLI + Final Docs
**Duration estimate**: 3 days
**Goal**: Replace Cinema Ultra's probe-with-fallback with a real WebGL2 shader pass. Add a CLI driver for Render Engine. Final Pact documentation + drift-check sealing.

**Inputs**:
- Phase 2-8 outputs
- `pact/platform/engines/cinema-ultra-engine.md` (procedural best quality + bezier interp)
- Render engine already implemented (single frame only)

**Outputs**:
- New `studio/modules/vibescope/src/engines/CinemaUltra.webgl.ts` — actual WebGL2 program (vertex + fragment shaders) for layer compositing
- `CinemaUltra.tsx` refactored: WebGL2 path uses shader program; procedural path still falls back honestly
- New `studio/modules/vibescope/src/engines/renderCli.ts` — CLI driver that loops frames 0..duration*fps calling `RenderEngine.render`, writes to `dist/` as PNG (via canvas conversion) or SVG (already done)
- New `studio/modules/vibescope/src/engines/__tests__/ultra-webgl.test.ts` — proves WebGL2 path produces different output than procedural fallback (not just labeled)
- New `studio/modules/vibescope/src/engines/__tests__/render-cli.test.ts` — proves CLI driver produces N frames
- All 6 remaining VSIL-A track kinds implemented in `animationOps` (vec2/vec3/colour/path/string/discrete)
- Pact doc additions: `pact/workspaces/vibescope/editor-getting-started.md` (operator onboarding)
- Drift-check v3.3.x → still 32/0 PASS (now exercising real implementation)

**Acceptance Criteria**:
- WebGL2 detected → CinemaUltra emits `data-vibescope-ultra-gpu="true"` + uses real shader (output pixels differ from procedural fallback by measurable amount: e.g., AA on edges, bezier curve smoothness)
- WebGL2 absent → emits `data-vibescope-ultra-fallback="true"` (already works)
- Render CLI: `npx ts-node engines/renderCli.ts --source=mock --frames=60 --out=dist/` writes 60 SVG files
- All 7 track kinds have working `addKeyframe`/`moveKeyframe` ops
- Drift-check v3.3.x = 32/0 PASS, no new stubs, no `STUB:` markers in shipped code

**Drift-Checks**: DC-VC-1..9, DC-VC-FALLBACK-N, AMEND-006
**Pact Compliance**:
- `cinema.engine.procedural.subsystem.timeline.001` (already there, Phase 6 verifies)
- `cinema.engine.ultra.definition.001` (real shader)
- `cinema.engine.ultra.fallback.honest.001` (already there)
- `vsil.a.engine.bindings.001` (3 engines all implemented)
- `pipeline.vsil.definition.001` (VSIL never produces pixels — engines do)

**Risks**:
- **Critical**: WebGL2 shader bugs are hard to diagnose. Need strong test coverage of the shader output.
- **High**: CLI driver must work in jsdom-free Node (use `node-canvas` or write SVG only).
- **Med**: 7 track kinds × multiple ops = lots of new code. Risk of off-by-one in time clamping.
**Mitigations**:
- Shader output compared pixel-by-pixel to procedural fallback on a 4×4 test scene; difference > ε = GPU path proven.
- Render CLI ships with SVG-only output (deterministic, no native deps). PNG conversion deferred to separate ticket.
- Each track kind has its own `animationOps.test.ts` for time clamping, sort order, and out-of-range.

---

## 4. Dependency Graph (Phase-level)


PHASE 1  Foundation Lock
   ↓
PHASE 2  Editor Store (VSIL as live mutable source)
   ↓
PHASE 3  Scrubber + Evaluator wired to engines
   ↓
PHASE 4  Paint Surface + Brush + Transform + Selection engines
   ↓
PHASE 5  Layer Panel + Layer Ops
   ↓
PHASE 6  Mask + Animation engines + Timeline interaction
   ↓
PHASE 7  Properties Panel two-way binding
   ↓
PHASE 8  Cinema vs Dev differentiation + Monaco source editor
   ↓
PHASE 9  Cinema Ultra WebGL2 + Render CLI + Final Pact docs
   ↓
DONE
**Serial dependencies are mostly hard** — Phases 2→3→4→5→6→7 are all coupled by the `VSILStore` becoming real and paint operations starting to mutate it. Phases 8 and 9 are independent from each other but both depend on Phase 7. --- ## 5. Multi-Agent Track Map 12 Tracks. Multiple tracks can run concurrently within a single phase where their concerns don't overlap. | Track ID | Owner role | Phase coverage | Parallel-safe with | |---|---|---|---| | **T1 Pact Hygiene** | pact-alignment agent | Phase 1 | nothing (foundation) | | **T2 VSIL Store** | typescript-engineer | Phase 2 | T3 (different files) | | **T3 Scrubber + Evaluator** | typescript-engineer (specialist in deterministic + immutable) | Phase 3 | T2 once Phase 2 lands | | **T4 Brush Engine** | graphics-engineer (canvas + pointer events) | Phase 4 | T5 (separate module) | | **T5 Transform Engine** | graphics-engineer | Phase 4 | T4 | | **T6 Selection Engine** | graphics-engineer | Phase 4 | T4, T5 | | **T7 Layer Panel** | react-engineer | Phase 5 | T8 (different cards) | | **T8 Mask + Animation** | graphics-engineer + animation-specialist | Phase 6 | T7 (after Phase 5) | | **T9 Properties Two-Way** | react-engineer + form-specialist | Phase 7 | T10 (different files) | | **T10 Monaco + Dev Layout** | dev-tools-engineer | Phase 8 | T9 | | **T11 WebGL2 + Render CLI** | webgl-engineer + node-engineer | Phase 9 | T12 | | **T12 Tests + Drift-Check + Docs** | verifier (always-on) | All phases | everything | **Always-on**: - T12 (Verifier) runs drift-check after every producer commit. - Cross-track integration tests (3-substrate CardMount parity, VibeScope render parity) run before each phase-end. **Sequence within a phase**: 1. Producer track(s) write code + producer-self-tests. 2. T12 verifier runs drift-check + reviewer pass. 3. Phase gate: drift 32/0, no new STUB markers, integration test passes. 4. Phase commit → next phase begins. --- ## 6. Cross-Track Dependencies (Explicit) | Producer | Consumer | Artifact | |---|---|---| | T2 (VSIL Store) | T3, T4, T5, T6, T7, T9 | `useVSILStore` hook + `vsilOps` | | T3 (Scrubber + Evaluator) | T4, T6, T8 | `useScrubber` hook + `CinemaProcedural.eval` | | T4 (Brush) | T6 (Mask), T9 (Properties exposure) | `BrushEngine` API | | T5 (Transform) | T9 | `TransformEngine` API | | T6 (Selection) | T7 (Layer Panel), T9 | `SelectionStore` | | T7 (Layer Panel) | T8 (Mask visibility), T9 (multi-select) | `LayerPanel` component | | T8 (Animation) | T9 (keyframe inspector) | `KeyframeRow` component | | T9 (Properties) | T10 (Designer reads live state) | `propertyBindings` | | T10 (Monaco) | T11 (CLI reads same source format) | VSIL JSON schema | | T11 (WebGL2) | none (terminal) | shader output | | T12 (Verifier) | all | drift-check report | **Critical Path**: T2 → T3 → T4 → T6 → T7 → T8 → T9 → T10 → T11 = 9 sequential producer phases + verifier on every step. **Parallelizable**: T4 + T5 + T6 (within Phase 4); T8's Mask + Animation sub-tracks (within Phase 6); T10 + T11 (Phases 8 + 9 are independent of each other). --- ## 7. World-Class Acceptance Checklist For each phase, ALL items must hold: ### 7.1 Architectural Clarity - [ ] All new code lives under `studio/modules/vibescope/src/` (no leakage to other workspaces) - [ ] New components follow the CardMount factory shape (`{Component, fragmentId}`) - [ ] All data flows through VSILStore; no shadow state in components - [ ] React context providers co-located with consumers (not at App root) ### 7.2 Pact Alignment - [ ] Every new file references its governing pact fragment_id in the header comment - [ ] Engine subsystems each implement their declared fragment (brush/transform/selection/layer/mask/animation/timeline) - [ ] Three engines (procedural/ultra/render) all implement `VSILEngine` interface — no stubs - [ ] Two layouts (cinema/dev) distinct and gate-correct ### 7.3 Drift-Check Alignment - [ ] `bash pact/scripts/audit-chip-system-v3.3.x.sh` → 32/0 PASS - [ ] No new `STUB:` markers in shipped code (only in test fixtures if any) - [ ] No new `// TODO:` without a tracked ticket - [ ] DC-VC-FALLBACK-N: exactly 1 dispatcher emitting `data-vibescope-active-card` - [ ] AMEND-006 §3.1: lowercase chip ids in registry, Title-Case display in UI ### 7.4 Deterministic Acceptance Criteria - [ ] All new tests are deterministic (no `Math.random`, no `Date.now` in assertions) - [ ] Snapshot tests for evaluable artifacts (VSIL → FrameState → SVG) - [ ] Property-based test for `vsilOps` (idempotence + inverse) - [ ] Engine output reproducible across runs (same source → same pixels) ### 7.5 Multi-Agent Executability - [ ] Each Track has a single owner - [ ] Each Track can run in isolation (no hidden coupling to other Tracks except documented cross-track deps) - [ ] Each Track produces a mergeable commit + a verifier-readable report ### 7.6 Zero Ambiguity - [ ] No `any` types in shipped code - [ ] No commented-out code in shipped code - [ ] No "placeholder" strings that survive into DONE - [ ] Every public function has JSDoc + at least one test - [ ] Every CSS variable used (no hardcoded colors except engine output markings) --- ## 8. Final DONE Definition **VibeScope is DONE when ALL of the following are true:** ### 8.1 Functional DONE - [ ] User opens VisualCard in Cinema layout → sees real canvas with at least one layer rendered via CinemaProcedural Engine - [ ] User clicks on canvas → selects a layer (SelectionEngine) - [ ] User drags a transform handle → layer moves in real-time, <16ms per frame - [ ] User changes opacity in Properties panel → layer fades in real-time - [ ] User opens Layer Panel → adds a new rect layer → appears in canvas - [ ] User toggles visibility eye → layer hides - [ ] User toggles Cinema → Dev → DevLayout shows Monaco with live VSIL source - [ ] User edits source in Monaco → cinema_procedural re-evaluates on blur, canvas updates - [ ] User scrubs Timeline → frames advance, animated layers interpolate - [ ] User adds a keyframe via Animation engine → drags it → motion updates - [ ] User paints with brush → new layer OR mask channel updated - [ ] User clicks "Render" → CLI driver produces 60 SVG frames to `dist/` - [ ] User switches engine quality: Draft (procedural) / Good (procedural + sampling) / Best (Ultra WebGL2) - [ ] User saves → VSIL doc written to Codex storage - [ ] User reloads → doc loaded back, identical to saved state (deterministic round-trip) ### 8.2 Pact DONE - [ ] All 8 chips work: Editor/Visual/Timeline/Inspector/Docs/Replay/Diff/Tests - [ ] All 3 engines implemented: cinema_procedural, cinema_ultra, render - [ ] Both layouts differentiated: Cinema (paint-first) + Dev (data-first) - [ ] All 6 engine subsystem fragments have implementation: brush, transform, selection, layer, mask, animation - [ ] All 7 VSIL-A track kinds have addKeyframe/moveKeyframe ops: float, vec2, vec3, colour, path, string, discrete - [ ] 4 Cinema cells = paint + properties + structure + timeline - [ ] 4 Dev cells = designer + data + logs + inspector ### 8.3 Drift DONE - [ ] `bash pact/scripts/audit-chip-system-v3.3.x.sh` → 32/0 PASS (no regression) - [ ] No `STUB:` markers in shipped code - [ ] No `// TODO:` without a tracked ticket - [ ] No `FUTURE-OAUTH-INTEGRATION` marker (replaced with explicit deferral or real OAuth) - [ ] `studio/modules/vibescope/src/registry/fragmentRegistry.ts` registry has 8 entries matching DNA `@chips` - [ ] All 5 pact workspace files are VibeScope-specialised (no `<Workspace>` placeholders) ### 8.4 Test DONE - [ ] All existing tests still pass: parser, sceneGraph, evaluator, keyframeExtractor, assetRegistry, diff - [ ] New tests: - `vsilOps.test.ts` — 50+ tests covering all mutations - `paint/brush.test.ts` — 20+ tests for brush behavior - `paint/transform.test.ts` — 20+ tests for transform handles - `paint/selection.test.ts` — 15+ tests for selection - `paint/mask.test.ts` — 15+ tests for mask - `paint/animation.test.ts` — 15+ tests for keyframe ops - `vsil/animationOps.test.ts` — 50+ tests for 7 track kinds - `inspector/propertyBindings.test.tsx` — 20+ tests for two-way binding - `dev/DesignerCard.test.tsx` — 10+ tests for Monaco integration - `engines/ultra-webgl.test.ts` — 5+ tests for shader output - `engines/render-cli.test.ts` — 5+ tests for CLI driver - `layouts/CinemaLayout.test.tsx` — 5+ tests for cell composition - `layouts/DevLayout.test.tsx` — 5+ tests for Dev access - [ ] Visual regression tests: snapshot of full Cinema layout render (golden file) - [ ] Visual regression tests: snapshot of full Dev layout render (golden file) - [ ] Performance tests: 60fps paint with 50 layers, scrub with 4 animated layers - [ ] Integration test: 3-substrate CardMount parity (VibeCoder + VibeScope + FreshCards) — already passing in Phase 1, must hold through DONE ### 8.5 Build DONE - [ ] `vite build` clean, <30s - [ ] Bundle size: VibeScope route < 1.5MB (Monaco is dynamic-imported) - [ ] All 3 engines tree-shake correctly (procedural/ultra/render all in bundle) - [ ] CF Pages deploy succeeds, canonical URL `https://freshvibestudio.pages.dev/vibescope/...` works - [ ] 0 page errors, 0 console errors in Playwright smoke test ### 8.6 Operational DONE - [ ] `pact/workspaces/vibescope/editor-getting-started.md` written (operator onboarding) - [ ] `studio/modules/vibescope/README.md` updated with Phase 9 state - [ ] All tracked follow-up tickets from earlier sessions reviewed and either closed or explicitly deferred - [ ] Codex v0031.0 published with VibeScope Editor invariants (carry forward prior invariants + 10 new from this plan) --- ## 9. Estimated Effort + Risk Summary | Phase | Days | Risk | Critical-path | |---|---|---|---| | 1. Foundation Lock | 0.5 | Low | yes | | 2. Editor Store | 2-3 | Med | yes | | 3. Scrubber + Evaluator | 2 | High | yes | | 4. Paint + Brush + Transform + Selection | 4-5 | Critical | yes | | 5. Layer Panel | 2 | Med | yes | | 6. Mask + Animation | 3 | High | yes | | 7. Properties Two-Way | 2 | High | yes | | 8. Cinema vs Dev + Monaco | 3 | High | yes | | 9. WebGL2 + Render CLI + Docs | 3 | Critical | yes | | **Total** | **22-26 days** | | | **Critical risk concentration**: Phase 4 (canvas + pointer events + 3 engines) and Phase 6 (animation performance). Both have hard 16ms budgets. If either fails, the whole plan slips. **Risk concentration mitigation**: Use T12 verifier after every commit in Phases 4 + 6. Fail fast. Do not advance to next phase if performance gate fails. --- ## 10. Ratification This plan requires operator ratification before execution. Mavis will not begin Phase 1 implementation until the operator signals GO. **Signal options**: - "GO" — execute all 9 phases sequentially - "GO PHASES 1-3 ONLY" — execute foundation phases, pause for review - "MODIFY" — operator specifies changes to phases or tracks - "PARK" — save plan, defer to next session **Operator context**: The VibeScope thread is currently `413419604156710` (FreshVibe Studio root). Helper Mavis (`412100071272671`) is pact-alignment track. Coordination via `communicate` tool. --- ## 11. Appendices ### Appendix A — Drift-check rules (full list) Re-verified from `pact/scripts/audit-chip-system-v3.3.x.sh`: - DC-VC-1 critical — only 1 always-mounted Panel Card - DC-VC-2 major — ≤3 surfaces per fragment - DC-VC-3 major — Inner Card stack cap = 3 - DC-VC-4..6 major — (other cockpit invariant checks) - DC-VC-7 major — Surface grammar canonical - DC-VC-8 major — Workspace scope - DC-VC-9 minor — Fragment grammar canonical to DNA @chips - DC-VC-FALLBACK-N critical — exactly 1 dispatcher - AMEND-006 §3.1 critical — lowercase chip ids in registry ### Appendix B — Pact fragment inventory (excerpt) Per `pact/v3/freshvibe-cockpit-constitution-vibescope.md` §5, 10 canonical Cards collapse to 8 doctrinal fragments per workspace DNA. All 8 must surface real editor functionality at DONE. ### Appendix C — Engine subsystem fragment cross-reference Per `pact/platform/engines/cinema-procedural-engine.md`, 6 subsystems (brush, transform, selection, layer, mask, animation). Phase 4 + Phase 6 cover all 6. The 6th one — adjustment subsystem (Levels/Exposure/Saturation) — is **not in scope for this plan**; tracked as future ticket VS-009. ### Appendix D — Track owner assignments | Track | Owner | Specialty | |---|---|---| | T1 Pact Hygiene | Helper Mavis (`412100071272671`) | pact alignment | | T2 VSIL Store | Producer A | TypeScript + React state management | | T3 Scrubber + Evaluator | Producer A | Determinism + immutable systems | | T4 Brush Engine | Producer B | Canvas + pointer events | | T5 Transform Engine | Producer B | Canvas + bounds math | | T6 Selection Engine | Producer B | Canvas + hit-testing | | T7 Layer Panel | Producer C | React + drag-and-drop | | T8 Mask + Animation | Producer D | Animation + keyframes | | T9 Properties Two-Way | Producer C | React forms + controlled inputs | | T10 Monaco + Dev Layout | Producer E | Monaco + dev tools | | T11 WebGL2 + Render CLI | Producer F | WebGL shaders + Node CLI | | T12 Verifier | Helper Mavis + Producer G | Drift-check + integration tests | ### Appendix E — Code paths that will change
studio/modules/vibescope/src/
├── cards/
│   ├── VisualCard.tsx              # Phase 2 (reads store), Phase 4 (paint surface)
│   ├── EditorCard.tsx              # Phase 8 (Monaco) — replaced by DesignerCard
│   ├── InspectorCard.tsx           # Phase 5 (live tree), Phase 7 (props binding)
│   ├── TimelineCard.tsx            # Phase 6 (draggable keyframes)
│   ├── DocsCard.tsx                # Phase 9 (real VSIL reference)
│   ├── ReplayCard.tsx              # Phase 9 (real replay playback)
│   ├── DiffCard.tsx                # Phase 9 (real diff from VSILStore versions)
│   ├── TestsCard.tsx               # Phase 9 (real test runner)
│   ├── dev/
│   │   ├── DesignerCard.tsx        # NEW (Phase 8) — Monaco
│   │   ├── DataCard.tsx            # Phase 8 — real state tree
│   │   └── LogsCard.tsx            # NEW (Phase 8) — engine logs
│   └── layer-panel/
│       ├── LayerPanel.tsx          # NEW (Phase 5)
│       └── LayerRow.tsx            # NEW (Phase 5)
├── dev/
│   └── useMonacoVSIL.ts            # NEW (Phase 8)
├── engines/
│   ├── CinemaProcedural.tsx        # Phase 4 (re-routes to PaintSurface)
│   ├── CinemaProcedural.eval.ts    # NEW (Phase 3)
│   ├── CinemaUltra.tsx             # Phase 9 (real WebGL2 path)
│   ├── CinemaUltra.webgl.ts        # NEW (Phase 9)
│   ├── Render.tsx                  # Phase 9 (CLI driver glue)
│   └── renderCli.ts                # NEW (Phase 9)
├── inspector/
│   ├── PropsMode.tsx               # Phase 7 (two-way binding)
│   ├── ASTMode.tsx                 # Phase 5 (live tree)
│   ├── StateMode.tsx               # Phase 8 (real FrameState)
│   ├── LogsMode.tsx                # Phase 8 (engine telemetry)
│   ├── MetadataMode.tsx            # Phase 8 (real asset metadata)
│   └── propertyBindings.ts         # NEW (Phase 7)
├── layouts/
│   ├── CinemaLayout.tsx            # Phase 8 (real cell composition)
│   ├── DevLayout.tsx               # Phase 8 (Monaco + data + logs)
│   └── DevLayoutGate.tsx           # Phase 8 (OAuth marker removed)
├── paint/
│   ├── PaintSurface.tsx            # NEW (Phase 4)
│   ├── BrushEngine.ts              # NEW (Phase 4)
│   ├── TransformEngine.ts          # NEW (Phase 4)
│   ├── SelectionEngine.ts          # NEW (Phase 4)
│   ├── MaskEngine.ts               # NEW (Phase 6)
│   ├── AnimationEngine.ts          # NEW (Phase 6)
│   └── HitTest.ts                  # NEW (Phase 4)
├── state/
│   ├── VSILStore.ts                # NEW (Phase 2)
│   ├── SelectionStore.ts           # NEW (Phase 4)
│   └── mutationLog.ts              # NEW (Phase 2)
├── vsil/
│   ├── parser.ts                   # unchanged
│   ├── sceneGraph.ts               # unchanged
│   ├── evaluator.ts                # unchanged
│   ├── keyframeExtractor.ts        # unchanged
│   ├── assetRegistry.ts            # unchanged
│   ├── diff.ts                     # Phase 9 (real diff consumer)
│   ├── vsilOps.ts                  # NEW (Phase 2)
│   ├── layerOps.ts                 # NEW (Phase 5)
│   └── animationOps.ts             # NEW (Phase 6)
├── scrubber/
│   ├── types.ts                    # unchanged
│   ├── useFrameAt.ts               # unchanged
│   ├── ScrubberAnchor.tsx          # unchanged
│   ├── ScrubberBar.tsx             # Phase 6 (draggable keyframes)
│   ├── ScrubberControls.tsx        # unchanged
│   └── ScrubberContext.tsx         # NEW (Phase 3)
└── registry/
    └── fragmentRegistry.ts         # unchanged (already aligned)
### Appendix F — Plan execution logging Every phase completion writes a short report to `/workspace/.mavis/plans/vibescope-master-plan/phase-NN-report.md` with: drift-check output, new tests added, screenshots of new functionality, and any drift / stub markers remaining. Verifier (T12) signs off in the same file. --- ## END OF PLAN