---
id: LSN-0031
ticket: studio-project-local-studio-state-under-dot-studio
title: Project-Local Studio State and Lifecycle-Safe Layout Restoration
created: 2026-04-04
tags: [studio, project-session, project-state, persistence, dot-studio, shell, layout, setup, javafx]
---

## Context

Studio needed a real project-local persistence boundary for user organization and setup.
The team wanted the opened project itself to carry Studio-local state under `.studio/`, including:

- project-local setup such as the Prometeu runtime path,
- shell and workspace organization,
- open editor tabs and the active tab,
- and restorable workspace layout such as split pane positions and mini-panel visibility.

The discussion explicitly rejected mixing that concern with global launcher preferences or with project-local activity history.
It also rejected treating every UI surface as an independent persistence owner.

## Key Decisions

### Make `.studio/` the Canonical Project-Local Studio Root

**What:**
The durable Studio state for one opened project now lives under `.studio/` in the project root, with a single-file main store at `.studio/state.json`.

**Why:**
That makes project-local setup and organization travel with the project instead of leaking into application-global storage.
It also gives Studio one canonical place to evolve project-local state over time.

**Trade-offs:**
The single-file approach is simpler to maintain and migrate in the first wave, but it concentrates multiple categories into one schema that must stay disciplined.

### Keep `StudioProjectSession` as the Persistence Owner

**What:**
`StudioProjectSession` owns loading and saving of the main project-local Studio store.
Workspaces and shell surfaces may consume and update in-memory snapshots, but they do not redefine the persistence lifecycle.

**Why:**
The opened project session is the real lifetime of per-project Studio state.
That state must survive workspace switching while still remaining scoped to one opened project window.

**Trade-offs:**
This adds an explicit project-session boundary, but that is better than letting shell views or workspaces become accidental persistence roots.

### Split Main Session State from Project-Local Activity

**What:**
The main store covers `projectLocalSetup`, `shellLayout`, `openShellState`, and `editorRestoration`.
Project-local activity remains under `.studio/`, but outside the main session store and with its own persistence policy.

**Why:**
Layout and setup are snapshot-like session restoration concerns.
Activity is more naturally log-like and should not distort the main contract.

**Trade-offs:**
This introduces two project-local persistence shapes under `.studio/`, but it keeps the session store narrow and predictable.

### Treat JavaFX Layout Restore as a Lifecycle Problem, Not Just a Codec Problem

**What:**
The final implementation had to restore split pane state only when the relevant JavaFX surfaces were actually ready, suppress state capture while applying the restore, and reapply pending layout after the project stage was shown.

**Why:**
The persisted values were being loaded correctly, but JavaFX initialization and default layout passes could still overwrite them with default divider positions such as `0.35`.
The real bug was lifecycle ordering, not file persistence.

**Trade-offs:**
The implementation became slightly more explicit about initialization phases, but it avoided unreliable "save more often" workarounds and kept the persistence lifecycle simple.

## Final Implementation

The final implementation established a project-local Studio state contract across specs, code, and tests:

- `docs/specs/studio/1. Studio Shell and Workspace Layout Specification.md` now defines shell restore/save behavior around the project session.
- `docs/specs/studio/5. Code Editor Workspace Specification.md` now treats cross-session editor restoration as part of the accepted contract.
- `docs/specs/studio/8. Project-Local Studio State Specification.md` defines the `.studio` root, the single-file main store, wave-1 categories, exclusions, and fallback behavior.
- `ProjectLocalStudioState` and `ProjectLocalStudioStateService` define the versioned single-file state model at `.studio/state.json`.
- `StudioProjectSession` and `StudioProjectSessionFactory` own load/save lifecycle for the state store.
- `MainView`, `EditorWorkspace`, and `AssetWorkspace` restore layout from the session-owned snapshot and keep the in-memory snapshot synchronized during the session.
- layout save remains lifecycle-driven: update memory on change, save on workspace switch, save on project close, load on project open, and apply on workspace open.
- fallback to safe defaults keeps project open resilient when `.studio/state.json` is absent, invalid, or incompatible.

## Patterns and Algorithms

### Pattern: Put Project-Scoped UI Persistence Under the Project Session

When Studio state belongs to one opened project but must survive workspace focus changes, the owner should be a project session object.
That is the right boundary for:

- project-local setup,
- restorable shell/workspace layout,
- open workspace selection,
- and editor restoration state.

### Pattern: Keep Persistence Lifecycle Simple and Explicit

The durable lifecycle is:

1. load on project-session open,
2. apply on workspace mount,
3. keep the latest state in memory during the session,
4. save on workspace switch,
5. save on project or Studio close.

This is better than broad autosave logic when the accepted contract only needs stable session restoration.

### Pattern: Reapply Layout Only After the UI Surface Is Real

For JavaFX `SplitPane` and docked layout surfaces, correct restoration requires more than reading the saved divider values.
The implementation must:

- defer application until the control has real dimensions,
- suppress state-capture callbacks while restore is in progress,
- and reapply pending layout after stage show if the initial layout pass can still override the restored values.

## Pitfalls

- Do not push project-local Studio state into launcher-global preferences just because there is no UI for editing all categories yet.
- Do not let individual workspaces or panels become persistence owners; they should only expose capture and restore snapshots.
- Do not mix project-local activity history into the main session restoration file.
- Do not "fix" layout restore bugs by adding generic debounce-based save behavior when the real issue is UI lifecycle ordering.
- Do not capture layout again during teardown if the session already has the correct in-memory snapshot; late teardown capture can overwrite valid persisted values with defaults.

## References

- `DEC-0016` Project-local Studio state under `.studio`
- `PLN-0037` Project-local Studio state store and restoration
- `docs/specs/studio/1. Studio Shell and Workspace Layout Specification.md`
- `docs/specs/studio/5. Code Editor Workspace Specification.md`
- `docs/specs/studio/8. Project-Local Studio State Specification.md`
- `prometeu-studio/src/main/java/p/studio/projectstate/ProjectLocalStudioState.java`
- `prometeu-studio/src/main/java/p/studio/projectstate/ProjectLocalStudioStateService.java`
- `prometeu-studio/src/main/java/p/studio/projectsessions/StudioProjectSession.java`
- `prometeu-studio/src/main/java/p/studio/window/MainView.java`
- `prometeu-studio/src/main/java/p/studio/window/StudioWindowCoordinator.java`
- `prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java`
- `prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java`

## Takeaways

- Project-local Studio organization belongs under `.studio/`, not in global app state.
- `StudioProjectSession` is the correct owner for per-project Studio persistence.
- JavaFX layout restoration succeeds only when persistence and UI lifecycle are aligned, not when persistence is merely present.
