Skip to content

Cooperate with sandy --update-sessions: sticky reconnect, restart-safe discovery, staleness surface (sandy 1.2.0) #36

Description

@rappdw

Sandy main (commit b9b00f8, ships as sandy 1.2.0, not yet tagged) adds sandy --update-sessions — fleet image-refresh + rolling restart of daemon sessions, built for a cron/launchd recipe (--idle-for 30 --yes). Long-lived daemon sessions (the 0.6.0/0.7.0 reality: many sessions, 24–48h+) otherwise never pick up agent updates. Sandy-ui needs cooperating UX so an overnight rolling restart reads as "seamless" instead of "my sessions died". Full handoff from the sandy workspace embedded below; sandy-side tracking is rappdw/sandy#41.

Work items

A. Sticky reconnect (the headline). When a daemon session's attach PTY exits 0 and the UI did NOT issue the stop: hold the panel in a "reconnecting…" state, poll --print-state light for the same sandbox key (stable across restarts — it's the workspace-path hash) for up to ~90s; on reappearance, silently re-run sandy --attach --workspace <ws> in the same panel (optionally noting "restarted for updates" when the discriminator confirms); on timeout, fall through to today's "session ended" state. Degrades gracefully pre-1.2.0: without the cron, nothing restarts overnight, and exit-0 means what it always meant.

B. Discovery-time reattach — verify, don't build. VSCode-closed-overnight already works via the 0.6.0 discovery flow provided we key on sandbox/workspace_path, never container ID. Add a fake-sandy integration case where the "container" is restarted (new id, same sandbox) between detach and relaunch.

C. Staleness badge + user-initiated update (optional). Full-mode --print-state now carries image_stale per container (deliberately NOT in light mode — poller budget untouched). Low-frequency/on-focus full poll → badge stale sessions → context action "Update & restart session" (sandy --update-sessions --yes --workspace W, scoped) and a global "Update all sessions" command streaming the plan/summary into an output channel or terminal.

D. Feature detection. Gate everything on --update-sessions in --print-schema cli_flags (same pattern as daemon detection, incl. the cache-format discipline).

Contract friction pushed upstream

The sandy.updated_at restart-discriminator is a container label, readable only via docker inspect — but sandy-ui deliberately never invokes docker (sandy is the sole interface; spec §non-goals). Filed as a sandy 1.2.0 ask: surface it as an additive running_containers[] field. Item A's "restarted for updates" note is gated on that landing; the reconnect itself is not.

Testing

fake-sandy fixture grows: an updated_at/restart knob (simulate stop→start with new container id + fresh updated_at), image_stale in full-mode print-state output, and --update-sessions in cli_flags behind a knob. Integration scenarios: attach → fixture restarts session → panel reconnects within window; restart never comes → clean session-ended; UI-issued stop does NOT trigger reconnect. Real-world validation per the handoff: sandy --update-sessions --rebuild --yes from a terminal while attached in a UI pane.


Original handoff from the sandy workspace (2026-07-16, verbatim)

[embedded below in the next comment for length]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions