Skip to content

feat(control): make the controller generation the repository's; a cold resume opens a placement generation - #35

Merged
jiashuoz merged 1 commit into
mainfrom
feat/control-controller-generation
Sep 3, 2026
Merged

feat(control): make the controller generation the repository's; a cold resume opens a placement generation#35
jiashuoz merged 1 commit into
mainfrom
feat/control-controller-generation

Conversation

@jiashuoz

@jiashuoz jiashuoz commented Sep 2, 2026

Copy link
Copy Markdown
Member

Summary

Task 1 of docs/superpowers/plans/2026-08-30-workspace-scope-and-generations.md — the plan's one coordinated control change, additive and non-behavioral on the wire (the #31 pattern).

  • control.Session.ControllerGeneration and control.SessionRepository.NextControllerGeneration: the controller lease is the repository's. AttachmentService drops its in-memory lease map; a viewer attaches under the row's value, a controller asks the repository for the next. The self-hosted adapter answers from a process-local table for now (Task 2 persists it).
  • Three doc sentences pin what the stores must implement: Transition advances PlacementGeneration when its RunnerID option names a runner; UpsertRunner is ErrStale below the stored generation; SetEnvironmentSnapshot is ErrStale on a moved hash and ErrNotFound on an absent environment.
  • Per the hosted PRD §9, a cold resume is a placement: ResumeSession's cold branch names the session's own runner so the repository opens a new generation for the new sandbox; a warm resume names none.

Test plan

  • TestControllerGenerationIsTheRepositorys (viewer 4 from the row, controller 5 from one repository call) and TestColdResumeOpensANewPlacementGeneration (cold → 2, warm → 1), both seen failing before the change
  • gofmt, go vet, go test ./control ./controlapp -race, go test ./internal/controld/... -race, scripts/check-public-control.sh (inventory unchanged, no new duplicate), make verify, git diff --check — run by the worker and rerun independently by the reviewer

🤖 Generated with Claude Code

https://claude.ai/code/session_01GiKRhqhzgJ1UDaLBynMyj3

…d resume opens a placement generation

The controller lease lived in a map inside AttachmentService, so a controld
restart handed session authority out again from zero and two replicas over one
store could grant the same generation twice. SessionRepository now owns it:
NextControllerGeneration advances a session's controller generation atomically
and returns it, Session carries ControllerGeneration, and the service keeps
none of its own — a viewer attaches under the row's value, a controller asks
the repository for the next.

The port docs pin the generation semantics the stores must implement, so the
hosted cell and the self-hosted stores cannot drift: Transition advances
PlacementGeneration whenever its RunnerID option names a runner, UpsertRunner
is ErrStale below the stored runner generation, SetEnvironmentSnapshot is
ErrStale on a moved setup hash and ErrNotFound on an absent environment.

That rule makes a cold resume a placement, per the hosted PRD §9: it starts a
new sandbox, so it names the session's own runner again and opens a new
generation, while a warm resume unpauses the sandbox it has and names none.

The self-hosted adapter answers NextControllerGeneration from a process-local
table on the host's side of the port, after confirming the row exists in the
store. Making it durable is Task 2 of the workspace-scope plan.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GiKRhqhzgJ1UDaLBynMyj3
@jiashuoz
jiashuoz merged commit 8ed15d2 into main Sep 3, 2026
@jiashuoz
jiashuoz deleted the feat/control-controller-generation branch September 3, 2026 01:30
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