Skip to content

feat(startup): configurable scene load on boot and save-on-quit - #63

Merged
joschaschmiedt merged 3 commits into
mainfrom
feat/startup-config-load
Jul 6, 2026
Merged

feat(startup): configurable scene load on boot and save-on-quit#63
joschaschmiedt merged 3 commits into
mainfrom
feat/startup-config-load

Conversation

@joschaschmiedt

Copy link
Copy Markdown
Contributor

Summary

vstimd can now boot into a known scene with no client attached, controlled by a new rig-config [startup] section:

[startup]
# Named config in --config-dir to load at boot; "last" = the auto-saved
# last-session slot; omit / "" = empty scene.
load_config  = "center_target"
# Save the current scene on graceful shutdown, so load_config = "last" restores it.
save_on_quit = false

Behaviour

  • Startup load (main.rs): after VTL init, an explicit --config <path> still wins; otherwise [startup] load_config applies. "last" (case-insensitive) resolves to a reserved slot (_last_session). A missing last-session slot on first boot is a logged no-op; a missing named config logs an error.
  • Save on quit (main.rs): once the render loop returns (shutdown requested), the scene + VTL names are written to the last-session slot. Locks scene-then-vtl to match the ZMQ thread's order (ipc.rs), which is still live during shutdown — the reverse order would risk a deadlock mid-request.

Refactors (reuse / testability)

  • io_config: new LAST_SESSION_CONFIG constant and config_path(dir, name) — the single place the vstimd_<name>.config.json layout lives (previously duplicated).
  • SceneState::{load,save}_named_config — reusable methods shared by the LoadConfig command and the boot path.
  • rig_config: StartupRigConfig + StartupLoad enum with a custom deserializer.

Tests

  • server/tests/rig_config.rs: startup parsing (named, "last" incl. case-insensitivity, empty string, unknown-field rejection).
  • server/tests/startup_config.rs (new): save→load roundtrip, expected file path, dir auto-creation, not-found reporting, last-session slot roundtrip, replace-clears-previous.

Full cargo test suite green; cargo clippy clean (only a pre-existing unrelated warning). Smoke-tested against --null: first boot logs "no last-session config yet", writes the file on shutdown, second boot logs "loaded startup config '_last_session'". Docs updated in saving-loading.md and default-rig-config.toml.

🤖 Generated with Claude Code

joschaschmiedt and others added 3 commits July 6, 2026 07:14
Add a rig-config `[startup]` section so a rig can boot into a known scene
with no client attached:

- `load_config` — a named config in the config dir, or "last" for the
  auto-saved last-session slot; empty/omitted starts with an empty scene.
  An explicit `--config <path>` still overrides it.
- `save_on_quit` — writes the current scene + VTL names to the last-session
  slot on graceful shutdown, so `load_config = "last"` restores it next boot.

Save-on-quit locks scene-then-vtl to match the ZMQ thread's order, which is
still live during shutdown. A missing last-session slot on first boot is a
logged no-op, not an error.

Refactor for reuse/testability: extract `io_config::config_path` and
`LAST_SESSION_CONFIG`, and `SceneState::{load,save}_named_config` shared by
the LoadConfig command and the boot path.

Tests: rig_config startup parsing (named, "last" case-insensitive, empty,
unknown-field rejection) and a startup_config suite covering save/load
roundtrip, path layout, dir creation, not-found, and replace semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8SPHZX5iVSoKyJ7ZqRVtS
Spawn the real vstimd binary under --null, create a stimulus over ZMQ,
send SIGTERM, and assert the last-session config is written with the scene
content — exercising main.rs's shutdown path end to end. Complements the
unit-level startup_config.rs tests. Also asserts nothing is written when
save_on_quit is disabled. Linux-only (relies on the SIGTERM handler).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8SPHZX5iVSoKyJ7ZqRVtS
Extends the startup save/load feature:

- Quit-time save (save_on_quit) now writes TWO artifacts: it overwrites the
  _last_session slot AND writes a timestamped archive
  vstimd_<YYYYMMDDTHHMMSSZ>.config.json for history. Archives are never
  pruned; a warning is logged once >500 accumulate in one dir. The UTC
  timestamp is formatted with a small dependency-free civil-date helper
  (std has no datetime), with a note to switch to a crate if needs grow.

- Config dir now defaults to /var/lib/braemons/vstimd for a deployed rig
  (packaged systemd unit sets StateDirectory=braemons/vstimd + --config-dir).
  When that isn't writable (e.g. a non-root dev run) it falls back to
  ~/.local/braemons/vstimd, then the current directory, logging the choice.
  An explicit --config-dir is still honoured verbatim.

- Example rig configs (jetson-orin-nano, raspberry-pi-4/5) document the
  [startup] section, kept commented so they parse to no-load/no-save.

Tests: dependency-free UTC formatter and archive-name recogniser (unit);
config-dir writability + first-writable fallback and the two-artifact
session snapshot (integration); the save-on-quit e2e now also asserts the
timestamped archive; example configs are globbed through the real loader and
checked to default startup off. Also ignore stray root vstimd_*.config.json.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8SPHZX5iVSoKyJ7ZqRVtS
@joschaschmiedt
joschaschmiedt merged commit b3f2040 into main Jul 6, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant