Enforce single-driver-per-session across frontends (TUI/HTTP/K8s) with explicit handoff
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 152
- Forks
- 16
- Avg merge
- 14h 48m
- Merged PRs (30d)
- 536
Description
Problem
mecatl's session model is implicitly one driver + decoupled observers/readers — but "single driver" is not actually enforced. Run-start IS serialized per session (runEntryMu same-process, optional SessionLease cross-process), yet live-run signaling (Cancel, Approve, ResumeApproval, CancelChild, and the new steer/steer_cancel) is NOT client-scoped: any Converse stream attached to the session can fire control frames at the one live run. Steer is the newest writer, but the shared-control shape predates it.
This was surfaced while discussing the steer message_id correlation (steerMsgIDs FIFO matches drain→sender by text-match): it assumes ONE writer's batching discipline, so a second concurrent writer (TUI + HTTP, or two TUIs) can mis-correlate an echo. That's a steer-specific symptom of the general multi-writer hole.
Goal
Enforce "one driver at a time per session" across ALL clients, with explicit handoff. Observers/readers (Subscribe / EventLog.Read replay) attached but read-only. Switching frontends (TUI → HTTP → TUI) should be a clean close-one/open-the-other, not concurrent driving.
Why it matters for disconnect/reconnect
The architecture supports decoupled readers + a driver, and client takeover across frontends is desirable (the TUI↔HTTP switch). But until ownership is scoped, two drivers racing prompts/steers is a confusion vector, and the durable-subscription surface doesn't yet broadcast run events to Subscribers (see the live-feed gap — a second reader can't follow a driver's run live without polling).
Notes
- Live-run control frames today ride the same
Conversestream the starter opened; a second stream firing them is un-scoped. - Steer's single-slot inbox serializes the value, but not the writer.
- This likely wants an ADR (multi-frontend session ownership) — a driver-scoping contract + an ownership/handoff event, plus the live-feed-to-subscription gap closed, so observers see a driver's events (incl. steer acks/echo) broadcast rather than polled.
Relates to #512 (steer) — steer is the forcing case; not solved there.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the Go session entry points named here: Converse, Subscribe, EventLog.Read, run-control frames, runEntryMu, and SessionLease. Read the steer correlation path and related issue #512, then define the ownership and handoff contract plus live-feed behavior in an ADR. Done means one driver is enforced across frontends, handoff is explicit, and observers receive the driver's events.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, backend-api-design, distributed-systems
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100