Skip to content

@/lib/stores Audit — Sprint 10

Generated: 2026-03-10 · HoloScript Studio packages/studio/src/lib/stores/

Store Inventory

StoreFileSizeExporters via barrelUnique importersRisk
useSceneStoresceneStore.ts61 L72🔴 Critical
useSceneGraphStoresceneGraphStore.ts178 L40🔴 High
useEditorStoreeditorStore.ts94 L31🟡 High
useCharacterStorecharacterStore.ts145 L23🟡 Medium
usePanelVisibilityStorepanelVisibilityStore.ts218 L1🟢 Low
useBuilderStorebuilderStore.ts147 L5🟢 Low
useAIStoreaiStore.ts37 L4🟢 Low
usePlayModeplayModeStore.ts238 L❌ (direct import)4🟢 Low

Note: usePlayMode is NOT exported from index.ts — components import directly from playModeStore. This is a minor inconsistency.


Slice Catalogue

useSceneStore 🔴 (72 importers)

Owns: HoloScript source code, compiled R3F tree, parse errors, scene metadata.

FieldTypePurpose
codestringRaw HoloScript source
r3fTreeR3FNode | nullCompiled render tree
errors{message, line?}[]Parse/compile errors
metadataSceneMetadataid, name, timestamps
isDirtybooleanUnsaved changes flag

Safe to extend: Yes — append new fields to SceneMetadata or add compilationStats. Do not restructure code/r3fTree without auditing all 72 consumers.

Risk area: setCode always sets isDirty: true and mutates metadata timestamp — any optimization of this hot path affects all editors.


useSceneGraphStore 🔴 (40 importers)

Owns: Scene node tree, transient Three.js refs, material mutations.

FieldTypePurpose
nodesSceneNode[]All scene objects
nodeRefsRecord<id, Object3D>Live Three.js refs (not serialized)

Actions: addNode, removeNode, moveNode, updateNodeTransform, updateNode, addTrait, removeTrait, setTraitProperty, setNodeRef, applyTransientTransform, applyTransientMaterial

Safe to extend: New fields on SceneNode are safe. Adding new action methods is safe. nodes array mutation pattern (map/filter) is consistent.

Risk area: applyTransientTransform — mutates Three.js objects and Zustand state in one set() call. Side effects are invisible to React devtools.

Risk area: nodeRefs — raw any typed; growth of this map is never cleaned up when nodes are removed.

Recommendation: Add cleanup in removeNode to also delete nodeRefs[id].


useEditorStore 🟡 (31 importers)

Owns: UI mode state, gizmo mode, panel booleans, spatial blame tooltip.

FieldTypePurpose
studioModeStudioModecreator / artist / filmmaker / expert / character / scenarios
gizmoModeGizmoModetranslate / rotate / scale
artModeArtModenone / sketch / paint / generative
showGovernancePanel, showConformancePanelbooleanRight-rail panel toggles
spatialBlameTooltip{visible, x, y, content}Blame overlay state
selectedObjectId/Namestring | nullScene selection

Overlap warning: showGovernancePanel + showConformancePanel are here, but 44 other panels live in usePanelVisibilityStore. There are two systems managing panel visibility.

Recommendation: Migrate showGovernancePanel + showConformancePanel into usePanelVisibilityStore as governance + conformance panel keys. This is a future cleanup; it requires updating 2 importers (editorStore, create/page.tsx).


useCharacterStore 🟡 (23 importers)

Owns: Character rig, recording, morph targets, skin, wardrobe.

Safe to extend: Add new panelMode values, new wardrobe slots, new morph target properties. The equippedItems: Partial<Record<WardrobeSlot, WardrobeItem>> pattern is extensible.

Risk area: setGlbUrl resets bone/animation state as side effect — any importer that sets glbUrl must accept bone/animation state clearing.


usePanelVisibilityStore 🟢 (1 direct importer)

Owns: 44 panel open/closed booleans via PanelKey union type.

Generated pattern: each key gets ${key}Open, set${Key}Open(), toggle${Key}Open().

Safe to extend: Add new PanelKey union members. The factory pattern auto-generates all 3 members. No importer refactoring needed.

For P5 (Agent Monitor Panel): Add 'agentMonitor' to PanelKey. Zero-refactor cost.


useBuilderStore 🟢 (5 importers)

Owns: Grid snap toggle, grid size, builder mode, hotbar slots.

snapToGrid() + snapPosition() are pure functions exported alongside the store — now also consumed by useDragSnap (P3).

Safe to extend: New hotbar shapes, new builder modes.


useAIStore 🟢 (4 importers)

Owns: AI inference status, Ollama status, model name, prompt history.

Small and focused. Safe to extend with agent cycle state — but see P5 recommendation below for why a separate agentStore is better.


usePlayMode 🟢 (4 importers)

Owns: Play state machine, game state (score/lives/level), scene snapshot for revert.

Inconsistency: Not exported from index.ts barrel — importers use from '@/lib/stores/playModeStore' directly.

Recommendation: Add to index.ts export. Low risk (4 files, same import just path changes).


Duplication Risks

RiskLocationSeverity
Two panel visibility systemseditorStore (governance + conformance) vs panelVisibilityStore (44 others)Medium
usePlayMode not in barrelDirect imports bypass @/lib/storesLow
nodeRefs memory leakremoveNode doesn't clean nodeRefs[id]Low-Medium
sceneStore.setCode side effectsTimestamp + isDirty mutation on every keystroke (hot path)Low

Safe Extension Points for P5 & P6

P5: Agent Monitor Panel

  • Do: Add 'agentMonitor' to PanelKey in panelVisibilityStore.ts
  • Do: Create new agentStore.ts (separate slice, low blast radius)
  • Don't: Extend useAIStore — different ownership domain

P6: Simple Material Panel

  • Do: Call useSceneGraphStore.applyTransientMaterial() — already supports material writes
  • Do: Call useSceneGraphStore.setTraitProperty() for persistence
  • Don't: Add new top-level state to sceneGraphStore for material — use node traits

Recommendations Summary

  1. Add usePlayMode to index.ts — trivial, removes inconsistency
  2. Fix nodeRefs leak in removeNode — one-line fix, prevents memory bloat in large scenes
  3. Future: Migrate showGovernancePanel/showConformancePanel from editorStorepanelVisibilityStore
  4. Future: Consider debouncing sceneStore.setCode to reduce re-render frequency on hot path

Released under the MIT License.