P1b: SQLite project, content-addressed assets, autosave and undo

This commit is contained in:
Emil
2026-11-01 01:19:55 +03:00
parent 4f4ee05a3a
commit 0acf11ccb0
22 changed files with 1405 additions and 32 deletions
+45
View File
@@ -53,3 +53,48 @@ The screenshots are `preview-p1-quarry.png`, `preview-p1-nave.png` and the empty
## Next
P1b: a project folder with SQLite, content-addressed assets, autosave and reload, pattern-edit undo, recording, and waveform trim. Then the arrangement and the agent.
---
# P1b · project and history
P1b makes the session durable and reversible.
## Project folder
The daemon loads and saves a portable folder, by default `~/.local/share/zhila/descent-04` (override with `--project PATH`):
```
project.db SQLite: project row, cores, journal
assets/<id>.<ext> the encoded source bytes, content-hash named
```
The project row stores name, revision, bpm, gains and the pattern JSON. Cores store id, name, seal, asset path and source name. The journal stores every project edit with its actor and timestamp. On load the material crate decodes each asset again, so faces and peaks are derived from the bytes rather than trusted metadata; patterns that reference a missing face are sanitised with a warning.
Autosave happens after imports and after every project command (tempo, gains, pattern step/face, undo/redo). The save runs under the session lock; the project is small, the write is a transaction. Play/stop/strike are performance and do not trigger a save.
## Undo
The core keeps up to 64 snapshots of `(bpm, gains, pattern)`. Tempo, gain, step and face edits push a snapshot before applying. `undo` restores the previous snapshot and pushes the current one to redo; `redo` reverses. Imports are not undoable in P1b (the archive and the pattern faces would need a coordinated removal); the import remains. The journal records `undo`/`redo`; the history itself is not persisted, so a reloaded project starts with an empty undo stack.
## Protocol
- `State.project_name`, `State.can_undo`, `State.can_redo`.
- Commands `undo`, `redo`; event `history { can_undo, can_redo }`.
- The project path is server-side; only the name is in the state.
## Verification
- `cargo test --workspace`: 12 core tests (including undo/redo and project loading), 1 project store test, 2 material, 5 protocol.
- `node scripts/persistence-smoke.mjs`: spawns a daemon on a temporary project, imports the fixture, assigns a face, undoes/redoes, edits a step and tempo, restarts the daemon, and verifies the core (16 faces), face, step 96, 132 bpm, journal and `project.db`.
- `npm --prefix ui run project-smoke`: browser project name, import, face assign, undo → procedural, redo → face, screenshot.
- `npm --prefix ui run restored-smoke`: after restarting the daemon on the saved project, the browser shows the core, sixteen faces and the face on the row, with undo disabled.
Screenshots: `preview-p1b-quarry.png`, `preview-p1b-restored.png`.
## Limits
- Undo history is per session; no reconstruction from the journal yet.
- Only the source bytes are archived; trimming/recording will add derived assets.
- The engine is still the standalone daemon; the Tauri backend does not host it yet.
- A failed autosave logs a warning and leaves the in-memory session running; the surface does not yet show a save error.
+1 -1
View File
@@ -1,7 +1,7 @@
# ZHILA — план проекта
Черновик v1.1 · 1 ноября 2026
Статус: **P0 и P1a выполнены** (см. `P0.md`, `P1.md`). Демо `reference/demo/` — референс, не кодовая база.
Статус: **P0, P1a и P1b выполнены** (см. `P0.md`, `P1.md`). Демо `reference/demo/` — референс, не кодовая база.
**Зафиксировано 1 ноября:** descent — основной вид; камень = паттерн; климат — на жилу (концепт погоды остаётся); платформа v1 — Linux; UI — Svelte 5. Журнал — §9.
+6
View File
@@ -146,3 +146,9 @@ Events: `pattern_step`, `pattern_face`, `face_struck`.
- The archive id is a content hash prefix (`core-` + 10 hex); the same bytes get the same id.
- The import is journaled on the session as `import_core` (journal-only; clients cannot send it as a command).
- If the file cannot be decoded or is too short, the requester receives `error` with the import id.
## P1b additions
`State` adds `project_name` (string), `can_undo` and `can_redo` (bools). Commands `undo` and `redo` take no arguments; event `history { can_undo, can_redo }` is broadcast after them.
The project is loaded by the daemon from its `--project` folder and saved after every project command or import. The protocol only exposes the project name; the path, SQLite file and assets stay server-side. There is no explicit save command: autosave is the contract. The journal is persisted, but undo history is not reconstructed on load.