Skip to content

SwiftUI App Structure & Naming — Reorg Plan

Milestone: SwiftUI App Structure & Naming (due 2026-08-22). PLAN doc for the milestone; no behavior changes here. Companion to the already-committed research_agent_search_audit_2571.md (surface consolidation) and the #104 vocabulary table (canonical names). Per iterate-never-replace: this proposes staged, additive moves — no folder is bulldozed, no shipped surface is broken in one step.

Why this doc

Two recurring smells in fichero/fichero/:

  1. Views-soup / dumping grounds — folders that grew past their name (Views/Library/ = 65 loose files), a Views/Components/ catch-all, and 15 loose files sitting at the Views/ root.
  2. Front-end ↔ back-end naming drift — the UI renamed concepts (Mind Palace → Spatial/Canvas; Researcher → Agent) but the wire contract and some stores still carry the old names.

This maps both, flags retired cruft, and stages the cleanup against milestone issues. It does not re-litigate the Researcher/Agent/Search collapse — that lives in research_agent_search_audit_2571.md and is design-gated.

Current front-end inventory

fichero/fichero/ top level:

Folder Role Notes
App/ (7) app lifecycle, window, AppState, ViewSettings holds one stray sheet (below)
Models/ (78) stores + domain types largest layer; @Observable/ObservableObject stores
Services/ (62) *Generated.swift API wrappers + manual services
Intents/ (3) App Intents
Views/ (15 loose + 24 subfolders) UI see below
Resources/ assets

Views/ subfolder sizes (.swift count):

65 Library      41 Workflow     28 Sidebar      17 Chat
16 Settings     15 Components    14 Activity     13 AIProviders
10 Automation    8 Search         7 Toolbars      7 ModelComparison
 5 Menu          5 MCPServers     5 Research       5 Spatial
 4 Actions       4 KnowledgeGraph 4 Notes         2 Agents
 2 Sheets        1 Auth           1 Capture        1 Integrations
                 1 Onboarding

Problem 1 — dumping grounds

Views/Library/ (65 files, flat)

The biggest offender. It mixes at least five distinct concerns under one flat folder: the document browser (LibraryView* — 9 files), PDF reading (PDF*Pane/View/Overlay — 6), representations (Representation*, StackedRepresentationPanes, RepresentationPicker), annotations/citations/ artifacts inspector panes (*InspectorPane, *ListView, *DetailView, Focused* — ~15), and image viewing (already partly split into Library/ImageViewer/, Library/ImageEditor/, Inspector/Document/).

Target: finish the split that ImageViewer//ImageEditor//DocumentInspector/ already started — - Views/Reader/PDF*, DocumentTextReader, PageContentPane, ImmersiveReaderView, reading layout. - Views/Library/Representations/Representation*, StackedRepresentationPanes, DisplayAttributesStrip (ties UI Reform — Representations milestone). - Views/Inspector/*InspectorPane, Annotation*, Citation*, Artifact*, Focused*, InspectorTab, InspectorPresenter (ties UI Reform — Inspector & Annotation milestone). - Keep LibraryView* + browser at Views/Library/ root.

Views/Components/ (15 files)

Genuine shared leaf views (FlowLayout, MarkdownText, StatusBadge, SplittablePane) sit next to feature-specific rows (ScheduleRow, TriggerRow, WorkflowExecutionRow*, WorkflowPreviewSheet) that belong with their feature (Library/Automation/, Workflow/). Target: move the feature rows out; keep Components/ for truly cross-feature primitives only.

Views/ root (15 loose files)

ContentView + its 10 extensions are fine as a cohesive set. But AdaptiveAppleShellHost, OpenAffordances, ContentViewHelperViews, DocumentTabView are the per-window shell — they read better under a Views/Shell/ (or App/) grouping. Low priority; cosmetic.

Problem 2 — front-end ↔ back-end naming drift

Canonical direction is front-end-first (#104 vocabulary table). Current mismatches:

Concept Front-end name Back-end / wire name Verdict
Spatial/2D canvas SpatialView, Canvas*Store, Views/Spatial/ /api/canvas/folders/{id}/canvas-layout, /canvas-items, /arrange (api/routes/canvas.py, mounted at /api/canvas in main.py) Resolved. The backend rename off “mind-palace” happened (#2565). Only internal mind_palace_room_id / legacy-room backfill names remain in db.py.
Research workspace .research, ResearchStore, ResearchService /api/research via research_agents.py (no agent logic) Misleading backend name; see #2571 audit.
Agent (no surface yet) /api/agent-memory Target surface (EPIC #2067), not built.

Mind Palace drift — resolved: the backend endpoints were renamed to /api/canvas/... (api/routes/canvas.py), so the wire path now matches the front-end Canvas vocabulary. Only internal DB names (mind_palace_room_id, legacy-room backfill in db.py) still carry the old term; those are storage details, not the client contract.

Problem 3 — Mind Palace (retired 3D rooms) removal — SUPERSEDED by #3750

Current state (verified 2026-07-18): the 3D removal described in this section was reversed. Per Daniel’s #3750 decision (“canvas = 2D, space = 3D, both LIVE”), a RealityKit 3D renderer was restored (#3088/#3104) and Space is a live view mode again: enum case .space, “as Space” with ⌘5 (ViewMenuCommands.swift), routed to Views/Space/SpaceSceneView.swift from LibraryView (case .space:). So, contrary to the text below: - RealityKit IS importedViews/Space/{SpaceSceneView,CanvasScene3DRenderer,CanvasSpaceView}.swift, Views/Canvas/{CanvasSceneView,CanvasOrtho2DRenderer}.swift, Models/SpaceTheme.swift. - The persisted "RealityKit" value now decodes to the live .space case (not migrated to canvas) — App/ViewDisplayMode.swift:39. - The 2D view mode enum is .canvas (renamed from .map); .space is the 3D peer. - Naming still spans three folders (Views/Canvas, Views/Space, Views/Spatial); consolidating them is the open work in #3750.

The historical account of what the removal pass did is kept below for context.

Mind Palace — the 3D-RealityKit “rooms” feature (AI-arranged pages/notes in a RealityKit volume) — was retired in this pass. At the time it was superseded by the 2D spatial library view (Spatial2DCanvas, then view mode “Canvas”). The two were easy to conflate because they share the SpatialNode data model and the .spatial/room vocabulary — the distinguishing line is the renderer:

Kept (live spatial view) Removed (Mind Palace 3D)
Views/Spatial/SpatialView.swift (2D projection) Views/Spatial/SpatialScene3D.swift (RealityKit renderer)
Views/Spatial/Spatial2DCanvas*, SpatialNodeThumbnail Models/SpatialTheme.swift (RealityKit colour bridge)
Services/SpatialLibraryProjector, Models/SpatialModels* FolderRealityKitSurface (in DocumentKGSurface.swift)
view mode “Canvas” (.map) view mode “Space” (.realitykit) + its ⌘5 menu button

What the removal did (this pass): - Deleted SpatialScene3D.swift, SpatialTheme.swift, and the FolderRealityKitSurface view; removed the “Space (⌘5)” View-menu button and the now-dead SpatialViewButton + libraryDisplayMode/ availableLibraryDisplayModes FocusedValues that only served it. - Kept the .realitykit enum case as a hidden decode-only alias (mirroring the already-retired .spatial alias, #2667) so persisted/@SceneStorage “RealityKit” values still decode and migrate to .map via normalizedViewDisplayMode() — no orphaned windows, every exhaustive switch keeps compiling. The RealityKit renderer is gone; the alias is one line. - No RealityKit import remained immediately after this pass — but RealityKit was later re-added when Space (3D) was restored (#3088/#3104/#3750); see the Current-state note at the top of this section.

Tension flagged (now resolved): EPIC #2667 wanted “Space (3D)” kept as a peer to “Canvas (2D)”. This pass removed it, but #3750 reinstated that goal — Canvas (2D) and Space (3D) are both live view modes now, so #2667’s intent stands.

Issue / milestone cleanup

Closed as retired (3D rooms): #1158, #1297, #1343, #1376, #1432, #1455, #1479,

1498, #271, #511.

Milestone “Mind Palace” (#12) intentionally NOT closed yet — it is a grab-bag, and 5 open issues are not the retired 3D feature. They need re-homing (board-organizer) before the milestone can close:

  • #2299 — backend Mind Palace cleanup (dead /rooms/* routes + MCP + CLI). Still real, undone — the backend half of this removal; codex/backend lane.
  • #2788 (Node Model milestone) — retire parallel mind-palace room storage; backend, tracks the same teardown.
  • #1433 — wire 6 Notes endpoints (Notes, not Mind Palace).
  • #1755 — georeference maps (a Maps feature; the 3D-globe uses RealityKit but it is not the rooms feature).
  • #821 — Apple-Intelligence Tool protocol (backend, mis-milestoned).
  • #2300 — guardrail debt (mis-milestoned).

Problem 4 — other retired / stale cruft

  • SidebarMode numbering comments are fossils (App/ViewSettings.swift): cases run 1,2,3,4,5,6,8,9 — a 7 gap, and shortcut() stops at 6 so research/knowledgeGraph have no ⌃⌘ shortcut. Either stale comments or missing shortcuts. Cheap, self-contained fix — candidate first slice.
  • CollectionWorkspaceStub.swift (Views/Library/) — name says stub; verify it’s still referenced or delete (dead-code check before removal).
  • IntegrationsPlaceholderSheet.swift in App/ — a placeholder sheet living in the lifecycle folder; belongs in Views/Sheets/ or Views/Integrations/, or is removable if Integrations shipped.
  • Views/Integrations/ (1 file) — single-file folder; fold into a sibling if it stays a placeholder.

Target structure (staged, additive)

Nothing moves in this doc. Order of operations, smallest-first:

  • Stage 1 (this milestone, #2571-adjacent): the SidebarMode comment/shortcut fossil fix — pure, self-contained, testable. Confirm/close #2571 against the existing audit.
  • Stage 2 (UI Reform milestones): split Views/Library/ into Reading/, Representations/, Inspector/ as those milestones touch the files anyway — reorg rides the feature work instead of a risky standalone move (needs add-swift-file.rb pbxproj re-registration per moved file).
  • Stage 3 (Components hygiene): relocate feature-specific rows out of Views/Components/.
  • Stage 4 (naming, backend-gated #2565): Mind Palace → Spatial endpoint rename in the backend lane; FE regenerates the client, drops the legacy method names. Not a FE-initiated change.

Scope note

Folder moves require scripts/add-swift-file.rb re-registration (main target uses PBX file references, not sync’d groups) — so each move is a real diff, not a free git mv. That’s why Stage 2/3 ride existing feature work rather than a big standalone reshuffle. The one thing safe to do standalone now is the SidebarMode fossil fix.