mcp: project-plan — petal-dispatch — Lovebug Session Switcher
This commit is contained in:
parent
3f2a3581a7
commit
5dcd2baba3
53
Sources/Dev/petal-dispatch-session-switcher.md
Normal file
53
Sources/Dev/petal-dispatch-session-switcher.md
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
created: '2026-06-26'
|
||||||
|
path: Sources/Dev
|
||||||
|
project: petal-dispatch-session-switcher
|
||||||
|
tags:
|
||||||
|
- petal-dispatch
|
||||||
|
- lovebug
|
||||||
|
- dispatch
|
||||||
|
- claude-code
|
||||||
|
- python
|
||||||
|
- session-management
|
||||||
|
type: project-plan
|
||||||
|
---
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Give Travis a Sessions surface on the petal-dispatch web UI for the Lovebug/unassigned seat that can list every claude session attached to that seat, switch between them without restarting the service, and clear to a fresh session — with an explicit per-session model choice and a live readout of the model each session is actually running.
|
||||||
|
|
||||||
|
The deeper goal is to permanently kill the uuid-orphan bug. Today a service restart stranded the live fused session because `claude --resume <uuid>` forks a brand-new leaf session uuid on disk, and nothing captured that leaf. The restart resumed the parent uuid recorded in `state.json` and the whole working conversation (session 7578ad2d, which had just merged work and triggered the restart) was orphaned. The split app already mitigates this by stamping the live uuid onto `dispatch_harness.thread_active.lovebug_session_uuid` on every spawn; the fused app never got that fix. This feature turns that mitigation into a real multi-session registry plus a UI, on both apps.
|
||||||
|
|
||||||
|
## Locked Decisions
|
||||||
|
|
||||||
|
- **List lives in Postgres.** New table `dispatch_harness.lovebug_session`, migration 012. Each row is one logical session and carries: a surrogate integer id, the current leaf session uuid, a thread_id seat marker, a display name, the chosen model, the model actually observed from system_init, a per-thread active flag, and created plus last-active timestamps. The list is allowed to grow for now; delete and prune are deferred.
|
||||||
|
- **The thread_id field is deliberate forward-compat.** The app was just branched into a split topology. The split keys Lovebug sessions by thread, with the Lovebug seat as sentinel -53. Carrying thread_id on every registry row keeps the table a superset of the split's existing per-thread model and eases the eventual migration. For now only the Lovebug seat is in scope; the column is kept generalizable.
|
||||||
|
- **A registry row is a logical session, not a uuid.** When a resume forks a new leaf, the row's uuid is updated in place. A clear inserts a new row. This single rule is what makes the orphan bug structurally impossible going forward.
|
||||||
|
- **Sub-decision A — the database owns the active pointer, not just the list.** The spawn path resolves the active uuid and the active model for the Lovebug seat from the database. The state.json unassigned bucket becomes a mirror, not the authority.
|
||||||
|
- **Leaf plus model capture is the core fix.** Extend the existing spawn-stamp so that when system_init arrives, both the live session_id and the reported model are written back onto the active row. Without this the registry drifts exactly the way state.json drifted today.
|
||||||
|
- **Switching is restart-free.** A switch updates the active pointer, stops the claude child, and respawns it on resume with the chosen model. The FastAPI service never goes down. A clear mints a fresh uuid and spawns with a session-id and no resume, mirroring the CLI clear behavior of create-new-then-switch.
|
||||||
|
- **Switch and clear are blocked while the seat is busy.** No queue — a turn or worker in flight means the action is refused, reusing the existing worker-busy guard.
|
||||||
|
- **Model selector.** Activation carries an explicit model choice passed through as the model flag. Each row shows the model it is actually running. The model list is pulled from the claude-api reference rather than hardcoded.
|
||||||
|
- **Build order.** Split app first, prove it works, then replicate the whole feature into the fused app. Travis is not cutting over to the split yet, and the orphan bug lives in the fused, so the fused needs the leaf-capture fix plus the registry and Sessions tab too.
|
||||||
|
- **Surface scope.** Web only for this round. The Android client is shared in the same repo but is a later pass. Because static/index.html is shared between both surfaces, the new Sessions tab degrades gracefully when its endpoints are absent, so the not-yet-wired surface is never broken.
|
||||||
|
|
||||||
|
## Open Items
|
||||||
|
|
||||||
|
- Source of the per-session display name: user-typed versus Lovebug-derived from the first user message. Start simple, refine after first use.
|
||||||
|
- Optional extra per-row stats (model, context fill at last turn, message count) — nice to have, deferred until the core works.
|
||||||
|
- Delete and prune of old sessions — deferred; the list grows for now.
|
||||||
|
- The cross-host deploy of the split ingress half, which runs on a separate LXC at 10.0.11.30, may not be reachable from the build host. Deploy steps that cannot be performed get documented and coordinated rather than forced.
|
||||||
|
- The fused deploy restart in phase 2 could orphan the live session one last time; by then the registry and disk-scan exist so it is recoverable, and after that restart the fused has leaf-capture and the bug is gone.
|
||||||
|
|
||||||
|
## Phases
|
||||||
|
|
||||||
|
- **Phase 1 — Split.** Migration 012, registry module, list/switch/clear/scan/rename endpoints wired into the ingress backend, the spawn-stamp extension for leaf plus model capture, the Sessions tab in static/index.html with the model selector, busy-guard on switch and clear. Build, zero-check, a-review on a large diff, feature branch, deploy to the split via an executor restart, verify, seed via scan.
|
||||||
|
- **Phase 2 — Fused.** Replicate the wiring into the fused petal_dispatch app: add the missing leaf-capture, point the spawn path at the shared registry, enable the Sessions tab on the fused surface. Deploy via a fused restart, sequenced so the current session is captured first.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Scope and decisions captured live in trellis thread 607 (slug lovebug-session-switcher), which gets concise checkpoints as phases land.
|
||||||
|
- The one-off recovery of today's orphaned fused session 7578ad2d is kept OUT of this feature. It is a manual rescue — swap the unassigned session_uuid in ~/.config/petal-dispatch/state.json and restart the fused service while it is stopped so the edit is not clobbered.
|
||||||
|
- Topology reminder: petal-dispatch.service (fused) and petal-executor.service (split back end) are two independent systemd units sharing one codebase via imports. The executor restarts freely; the fused is the never-restart unit, and restarting it kills the live supervisor session, so phase 2's restart is sequenced carefully.
|
||||||
|
- House rules in force: feature branch never main, zero-check after Python edits, a-review on diffs over fifty lines, surface deploy and sudo gaps rather than escalating.
|
||||||
|
- Related plans: petal-dispatch-phase-4-2-ingress-split (the split topology this builds on) and petal-dispatch-asks-panel (the prior feature, same pattern of plan-plus-thread).
|
||||||
Loading…
Reference in New Issue
Block a user