Commit Graph

50 Commits

Author SHA1 Message Date
vasyansk d62628be8d fix(daemon): reseed id counter on restore + heal duplicate leaves
Root cause of the multi-focus/multi-search/linked-terminal bug: the in-memory
id counter resets to 0 each daemon start, but restore() never advanced it past
restored ids. After a restart new_surface_id() re-minted existing ids → the same
surface_id appeared twice in a layout tree (rendered as two panels sharing focus,
search bar, and output channel — one ends up blank). Session-persistence made
restarts routine, surfacing the latent bug.

- restore() now reseeds the counter to max(restored id)+1
- ops::dedupe_leaves heals an already-corrupted persisted tree on load

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:46:04 +07:00
vasyansk 3317b24d18 fix(daemon): gate NullSnapshotStore behind cfg(test) — silence release dead_code warning
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:34:10 +07:00
vasyansk ce6a8d56be fix(daemon,app): graceful-shutdown final snapshot pass + StoppedSnapshot detach cleanup
Addresses final-review findings: Cmd::Shutdown now snapshots all live surfaces
synchronously before exit (spec graceful-shutdown requirement); StoppedSnapshot
calls detachSurface on unmount to release the bridge output channel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:24:53 +07:00
vasyansk 31c08b5387 feat(daemon): RestartSurface honors resume — swap to resume_args when mapped
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 16:05:53 +07:00
vasyansk 60383cd543 feat(daemon): snapshot ticker + writer wiring + stopped-attach reads disk + cleanup on close
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:00:39 +07:00
vasyansk 69f2e73832 feat(daemon): snapshot writer task (Save/Remove over one channel)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:49:29 +07:00
vasyansk 0674872c9d feat(daemon): actor Snapshot message + dirty tracking + final snapshot on exit
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:47:40 +07:00
vasyansk 1a7d04aab0 feat(daemon): [resume] config map + snapshot_interval_secs with built-in defaults
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:38:30 +07:00
vasyansk bd36a83db2 feat(daemon): per-surface JSON snapshot store (atomic write, corrupt-tolerant)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:36:21 +07:00
vasyansk 4419f5660e wip: in-progress changes (grid, config, wizard, settings, pty) before session-persistence
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 15:28:19 +07:00
vasyansk f9a565a712 feat(app): clear all events from the Event Center (red trash icon)
Adds Cmd::ClearEvents + Evt::EventsCleared: the daemon drops the persistent
event log (keeping next_id monotonic), persists, and broadcasts so every
client empties its list. A red trash icon next to 'Mark all read' triggers it;
disabled when the list is empty. Threaded through proto, the daemon handler,
the Tauri bridge, and socketBridge. Includes an EventLog::clear test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:38:35 +07:00
vasyansk cf7410b46a feat(app): version handshake — GUI restarts a stale running daemon
The daemon outlives the GUI, so after an update an OLD daemon can keep serving
the socket and the new GUI just connects to it (stale code — e.g. the missing
TERM fix). Both binaries are now stamped with the git build id (build.rs):
the daemon reports it in `health.build`, and on connect the bridge compares it
to the GUI's own SPACESH_BUILD; on mismatch it shuts the daemon down and lets
ensure_daemon respawn the bundled (matching) one. No-op for unstamped dev
builds or daemons too old to report a build. Build id is shown in Settings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:39:46 +07:00
vasyansk ad29665352 feat(spaceshd): GetConfig/SetConfig handlers with live ConfigChanged broadcast 2026-06-14 09:35:27 +07:00
vasyansk 80113da066 feat(spaceshd): config terminal+appearance sections and save 2026-06-14 09:20:24 +07:00
vasyansk 7b47052a6f feat(spaceshd): pinned workspace field
Add a `pinned` bool to the workspace model, threaded through proto
(Workspace + WorkspaceView + SetWorkspaceMeta), the registry, the
set_workspace_meta handler, persistence, the CLI mapping, and the Tauri
bridge. serde(default) keeps existing state.json compatible (pinned=false).
Backs the sidebar Favorites section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:56:09 +07:00
vasyansk a30ec1cc7f fix(spaceshd): lazy PTY spawn so prompts render at the correct size
A new surface spawned its shell immediately at the default 80x24, so the
shell (e.g. zsh + powerlevel10k) printed its first prompt before the GUI
fit-resized the panel. The post-print resize reflowed the grid, leaving the
prompt invisible until Enter and shifted by a line.

The surface actor now defers the child spawn: it waits for the first Resize
(spawning at that geometry) or, for headless/CLI surfaces that never attach a
GUI, falls back to the spec geometry after 250ms. Attaches received before
the spawn get an empty-grid snapshot plus a live subscription; pre-spawn
input is buffered and replayed. Adds two deterministic tests via `stty size`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:34:28 +07:00
vasyansk 6a3875670a feat(spaceshd): configurable default shell
Add a config.rs module that loads optional ~/.spacesh/config.toml and a
default_shell() resolver: SPACESH_SHELL env -> config.toml default_shell ->
login shell from the passwd DB -> $SHELL -> /bin/sh. The passwd lookup
matters under launchd where $SHELL is absent. All plain-panel spawn sites in
server.rs now use the resolver. Adds toml + libc deps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:01:04 +07:00
vasyansk 21180ae9e0 fix(spaceshd): isolate instance-lock test via SPACESH_LOCK
lock_is_exclusive_within_process acquired the global ~/.spacesh/daemon.lock,
so it flaked whenever a real daemon was running. Add a SPACESH_LOCK env
override to lock_path() and point the test at a private temp file under a
serial() guard, making the suite deterministic regardless of a live daemon.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 08:00:35 +07:00
vasyansk 2b1ccaf31d feat: persisted per-workspace panel zoom (proto + daemon, auto-clear on removal)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 12:24:23 +07:00
vasyansk f7763a84fc feat: Health command — version, pid, started_at (proto + daemon) 2026-06-10 12:06:38 +07:00
vasyansk 615b90e887 test(daemon): event log survives cold daemon restart
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 08:35:38 +07:00
vasyansk 21fe1308cc feat(daemon): EventLog/MarkRead commands; Focus marks surface read
Wire Cmd::EventLog and Cmd::MarkRead to the live EventLog (replacing
NOT_IMPLEMENTED stubs). Cmd::Focus now calls mark_read for the surface,
persists the updated snapshot, and broadcasts Evt::EventsRead.
Add integration tests: event_log_query_and_mark_read and
focus_marks_surface_events_read.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 08:13:50 +07:00
vasyansk e83524a076 feat(daemon): record done/wait/error/exit into the event log
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 08:02:03 +07:00
vasyansk f7bf9fc5c6 wire(daemon): thread EventLog + EventPersister through serve/router
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 07:40:54 +07:00
vasyansk ac2d5ca1db feat(daemon): JsonEventStore + debounced EventPersister
Adds disk persistence for the event log: atomic temp+fsync+rename write,
corrupt-file backup, and a debounced coalescing persister task mirroring
state_store/persist patterns. 4 tests: round-trip, missing, corrupt, burst.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 07:27:23 +07:00
vasyansk 56ba1723b9 feat(daemon): EventLog ring model with read-flags
Add event_log.rs with EventLog (capped VecDeque, monotonic ids, mark_read,
snapshot/restore) and EventLogState. Register mod in main.rs. Stub
Cmd::EventLog and Cmd::MarkRead arms in server.rs to keep the exhaustive
match compiling; full wiring follows in Task 4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 07:21:26 +07:00
vasyansk c35585755e feat(daemon): actor OSC133/fallback detection → set_state, hook/shell spawn env, cleanup
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:05:18 +07:00
vasyansk 4396ad7909 feat(daemon): zsh OSC 133 shell integration via per-surface ZDOTDIR
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:00:48 +07:00
vasyansk 255fa27271 feat(daemon): versioned Claude Code hook adapter (per-surface CLAUDE_CONFIG_DIR)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 23:00:40 +07:00
vasyansk 6fc099bb6f test(daemon): serialize SPACESH_SOCK-mutating lifecycle tests to fix env race
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 22:23:41 +07:00
vasyansk 635f9f4356 feat(daemon): per-surface status (set_state/state), idle-on-spawn, SPACESH_SOCK override
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 22:13:50 +07:00
vasyansk 0328797bce chore(daemon): remove unused imports and dead placeholders
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:39:43 +07:00
vasyansk 62a65b691d feat(daemon): M2 command dispatch, layout events, cold-start restore, persistence wiring
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:25:35 +07:00
vasyansk b72f4cb3a5 feat(daemon): spawn_from_spec to (re)start surfaces from a persisted spec
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:22:29 +07:00
vasyansk d516414ac9 feat(daemon): registry owns workspaces/groups/trees + running/stopped surfaces
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:21:47 +07:00
vasyansk 7515516699 feat(daemon): debounced persist scheduler coalescing bursts into one save
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:20:58 +07:00
vasyansk 4f7ed2a5a3 feat(daemon): StateStore trait + atomic JSON store with corrupt-file backup
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:20:51 +07:00
vasyansk f1630633e5 test(daemon): serialize heavy socket/PTY integration tests
Process-wide serial lock around the socket-binding and PTY-spawning
integration tests in spaceshd. Running several at once on a many-core
box starved each other's async tasks and tripped timing assumptions,
causing ~1/10 flakes under cargo test --workspace. Unit tests stay
parallel. 0/20 spaceshd + 0/5 workspace runs after the change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:39:24 +07:00
vasyansk 77dfc69bd9 fix(daemon): remove pending broadcast on attach to prevent reattach double-render
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:33:11 +07:00
vasyansk a56d8d0fea test(daemon): harden reattach test with pre-connect socket guard
Add wait_for_socket before second client connect to handle scheduling
jitter when tests run in parallel under cargo test --workspace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:16:18 +07:00
vasyansk 18ac699951 feat(daemon): launchd user-agent install with KeepAlive
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:14:00 +07:00
vasyansk f49fc41cd1 test(daemon): reattach after disconnect repaints prior output from snapshot
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:13:32 +07:00
vasyansk 732eef96b5 feat(daemon): grid feed + output coalescing + snapshot-on-attach (M1)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:12:55 +07:00
vasyansk 8d0b5b8d1d test(daemon): make socket/pty integration tests robust under parallel load
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:05:23 +07:00
vasyansk 4b1a544923 feat(daemon): entrypoint with single-instance lock and lazy socket bind
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 20:00:41 +07:00
vasyansk 531f89eb73 feat(daemon): socket server with router task, command dispatch, event fan-out (M0)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 19:59:58 +07:00
vasyansk 2aedc6924d feat(daemon): registry for workspaces and surfaces with idempotent open
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 19:58:28 +07:00
vasyansk 32560ea364 feat(daemon): surface actor owning pty + broadcast fan-out (M0, no grid)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 19:57:53 +07:00
vasyansk cd1fe1dfd5 feat(daemon): lifecycle paths, single-instance lock, stale-socket cleanup
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 19:57:11 +07:00
vasyansk 31dddb3b6c chore: scaffold cargo workspace and crate skeletons
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 19:54:38 +07:00