CB-584: persist agentSessionId at acquire, expose it via bridge_spawn/bridge_list #71

Closed
agent wants to merge 0 commits from worker/cb584-session-resume-b62247-4 into main
Member

Closes the two missing links named in gitea issue #65: the session-resume chain was built end to end but connected at neither end.

Scope (per the ticket):

  1. MemberSession records agentSessionId, populated from PeerHandle at acquire (both the plain and worktree paths).
  2. PeerHandle.agentSessionId() loses its default — the same fix CB-571 (c00a86b) made for charterReceipt() one method above it. Both existing adapters (ClaudeCodeLauncher/HerdrPeerLauncher.WorkerHandle, OpenCodeLauncher.SessionAwareHandle) already overrode it, so this only closes the landmine for a future adapter that forgets to.
  3. agentSessionId is exposed on the roster (SessionManager.rosterView, so both bridge_list and GET /members show it), omitted when null (same pattern as charterSha256).
  4. bridge_spawn accepts sessionName/resumeSessionId, threaded through SessionManager.acquire into the SpawnRequest fields that already existed.
  5. A resumeSessionId is refused, naming Capability.SESSION_RESUME, when the resolved profile's adapter does not declare it — checked before spawning, so a non-supporting adapter never silently delivers a cold session.

Design decision not settled by the brief (flagging per instructions): a resumeSessionId now requires an explicit profile. A resumed conversation is tied to the specific backend that minted its id, and an unqualified spawn is routed by the placement policy to a candidate chosen at spawn time — there is no safe single adapter to check the capability against ahead of time. Refusing keeps the check honest rather than approximating it against the fleet-wide capability union (which would be wrong on a mixed fleet where only some adapters support resume).

New surface: PeerLauncher.capabilitiesFor(profileName) — distinct from the existing fleet-wide capabilities() union. HerdrPeerLauncher implements it as capabilities() (one instance = one adapter kind, so every profile it owns shares the capability set); CompositePeerLauncher routes it to the owning delegate. This was necessary infrastructure for criterion 5 and touches peer/PeerLauncher.java, which wasn't in the brief's explicit file list — flagging that too.

Out of scope, left open: issue #65 criterion 5 (carrying the session id on a failed ticket's ReleaseDetail, alongside worktree/branch/snapshot ref) — SessionManager.ReleaseDetail/release()/trySnapshot were just touched by CB-578 stage C and the brief asked me to stay out of them.

Honesty note: this ships the plumbing only. Whether a resumed session is actually useful after a usage-limit refusal is untested — I did not run a live resume end to end.

Test plan

  • mvn -f bridged/pom.xml clean install (unpiped): Tests run: 771, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS, exit 0.
  • New/changed tests: SessionManagerTest (agentSessionId recorded at acquire, roster exposure, resume-without-profile refusal, resume-without-capability refusal naming SESSION_RESUME, unchanged behavior with neither field), BridgeMcpTest (bridge_spawn threading sessionName/resumeSessionId end to end, roster shows the id, refusal without explicit profile), CompositePeerLauncherTest (fixed an anonymous PeerHandle that needed an explicit agentSessionId() override now that the default is gone).
Closes the two missing links named in gitea issue #65: the session-resume chain was built end to end but connected at neither end. **Scope (per the ticket):** 1. `MemberSession` records `agentSessionId`, populated from `PeerHandle` at acquire (both the plain and worktree paths). 2. `PeerHandle.agentSessionId()` loses its `default` — the same fix CB-571 (`c00a86b`) made for `charterReceipt()` one method above it. Both existing adapters (`ClaudeCodeLauncher`/`HerdrPeerLauncher.WorkerHandle`, `OpenCodeLauncher.SessionAwareHandle`) already overrode it, so this only closes the landmine for a future adapter that forgets to. 3. `agentSessionId` is exposed on the roster (`SessionManager.rosterView`, so both `bridge_list` and `GET /members` show it), omitted when null (same pattern as `charterSha256`). 4. `bridge_spawn` accepts `sessionName`/`resumeSessionId`, threaded through `SessionManager.acquire` into the `SpawnRequest` fields that already existed. 5. A `resumeSessionId` is refused, naming `Capability.SESSION_RESUME`, when the resolved profile's adapter does not declare it — checked *before* spawning, so a non-supporting adapter never silently delivers a cold session. **Design decision not settled by the brief (flagging per instructions):** a `resumeSessionId` now requires an *explicit* `profile`. A resumed conversation is tied to the specific backend that minted its id, and an unqualified spawn is routed by the placement policy to a candidate chosen at spawn time — there is no safe single adapter to check the capability against ahead of time. Refusing keeps the check honest rather than approximating it against the fleet-wide capability union (which would be wrong on a mixed fleet where only some adapters support resume). **New surface:** `PeerLauncher.capabilitiesFor(profileName)` — distinct from the existing fleet-wide `capabilities()` union. `HerdrPeerLauncher` implements it as `capabilities()` (one instance = one adapter kind, so every profile it owns shares the capability set); `CompositePeerLauncher` routes it to the owning delegate. This was necessary infrastructure for criterion 5 and touches `peer/PeerLauncher.java`, which wasn't in the brief's explicit file list — flagging that too. **Out of scope, left open:** issue #65 criterion 5 (carrying the session id on a failed ticket's `ReleaseDetail`, alongside worktree/branch/snapshot ref) — `SessionManager.ReleaseDetail`/`release()`/`trySnapshot` were just touched by CB-578 stage C and the brief asked me to stay out of them. **Honesty note:** this ships the plumbing only. Whether a resumed session is actually useful after a usage-limit refusal is untested — I did not run a live resume end to end. ## Test plan - `mvn -f bridged/pom.xml clean install` (unpiped): **Tests run: 771, Failures: 0, Errors: 0, Skipped: 0** — **BUILD SUCCESS**, exit 0. - New/changed tests: `SessionManagerTest` (agentSessionId recorded at acquire, roster exposure, resume-without-profile refusal, resume-without-capability refusal naming SESSION_RESUME, unchanged behavior with neither field), `BridgeMcpTest` (bridge_spawn threading sessionName/resumeSessionId end to end, roster shows the id, refusal without explicit profile), `CompositePeerLauncherTest` (fixed an anonymous `PeerHandle` that needed an explicit `agentSessionId()` override now that the default is gone).
agent added 1 commit 2026-08-15 15:48:29 +02:00
CB-584: persist agentSessionId at acquire and expose it via bridge_spawn/bridge_list
CI / contract (pull_request) Successful in 1m5s
CI / build (pull_request) Successful in 2m18s
5d5b3bdc76
Wires up the two links that made session resume unreachable: MemberSession
now records agentSessionId from PeerHandle at acquire (both the plain and
worktree paths), and it survives onto the roster (rosterView, so both
bridge_list and GET /members show it). bridge_spawn accepts sessionName and
resumeSessionId, threading them into the SpawnRequest fields that already
existed but were never reachable from the MCP surface.

A resumeSessionId now requires an explicit profile (a resumed conversation
is tied to the specific backend that started it, so an unqualified spawn
routed by placement has no safe candidate to check) and is refused, naming
Capability.SESSION_RESUME, when that profile's adapter does not declare it
— PeerLauncher gains capabilitiesFor(profileName) so a mixed fleet is
checked per-adapter rather than against the fleet-wide capability union.

PeerHandle.agentSessionId() loses its default, the same fix CB-571 (c00a86b)
made for charterReceipt() one method above it — both existing adapters
already overrode it, so this only closes the landmine for a future one.

Out of scope, left for follow-up: carrying agentSessionId on a failed
ticket's ReleaseDetail (issue #65 criterion 5) — CB-578 stage C just
landed in that file and this ticket deliberately stayed out of it.
ltms closed this pull request 2026-08-15 16:01:04 +02:00
ltms deleted branch worker/cb584-session-resume-b62247-4 2026-08-15 16:01:04 +02:00
Some checks are pending
CI / contract (pull_request) Successful in 1m5s
CI / build (pull_request) Successful in 2m18s

Pull request closed

Sign in to join this conversation.