Skip to content

Allow stopped Sandbox Identity changes with retained Sessions - #16373

Open
agentydragon-agent wants to merge 2 commits into
agentydragon:develfrom
agentydragon-agent:sandbox-identity-recheck
Open

agentydragon-agent wants to merge 2 commits into
agentydragon:develfrom
agentydragon-agent:sandbox-identity-recheck

Conversation

@agentydragon-agent

@agentydragon-agent agentydragon-agent commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

A stopped Sandbox can change Identity even while Sessions retain its old Identity. EnvironmentService now patches spec.identity directly with the Sandbox UID and Kubernetes resourceVersion; the CRD's existing CEL rule is the only stopped-state gate. Sessions retain their recorded Identity and re-check the live binding at admission, placement and delivery, so their next command fails execution_identity_changed after a rebind.

Removes dispatcher.Store.ChangeSandboxIdentity, its retained-Session refusal, the in-process guard interface/wiring, and the duplicate stopped-state check in the Kubernetes mutation helper. The existing Session admission and delivery checks are unchanged. This is the behavior change before moving EnvironmentService, following the decided cut line in plan PR #16368.

Caller audit: repository-wide search found one production caller of ChangeSandboxIdentity, EnvironmentService, supplied the Store by Gateway main in the same binary. The other callers were its Store tests and an EnvironmentService test fake. The app calls the unchanged SetEnvironmentIdentity API through its generated facade; Action executors do not call the removed method or rebind API. No removed caller crosses a process boundary, so no caller-image pin is required before removal. Pin a Gateway image containing this behavior before the separate EnvironmentService move; no new Environments-to-Sessions call is introduced. No CRD, database, API or manifest change is needed.

Validation:

  • Changed-file PYTHON_CPU_COUNT=2 pre-commit run --files ... passed; commit hooks passed.
  • bbr build //agentplane/runner_gateway:main_lib //agentplane/system_tests/scenarios:dispatcher //agentplane/environments/v1:environment_service_pb2_grpc — passed (invocation).
  • Initial remote bbr test //agentplane/runner_gateway/environments/internal/management:unit_test //agentplane/runner_gateway/environments:environments_test //agentplane/runner_gateway/dispatcher:store_integration_test //agentplane/system_tests/suites:test_dispatcher --test_output=errors. The three Go targets passed in d6685423. The system scenario passed its rebind/command assertions but failed teardown because the replacement Identity was deleted before its referencing Sandbox. Cleanup now groups Identity lifetimes so the Sandbox is deleted first. The corrected full bbr test //agentplane/system_tests/suites:test_dispatcher --test_output=errors passed (1/1 target, 290.6s; ae770da0).
  • The existing Dispatcher system suite's queue scenario now places a real Session, verifies that the live CRD refuses an Identity change while running, stops the Sandbox, changes Identity through EnvironmentService, and submits input to the retained Session. It checks that the Session still records its original Identity and the command fails execution_identity_changed without Runtime admission. This scenario must pass before merge; the old Store refusal would reject its stopped rebind.
  • A mutation test races a policy update against the Identity patch, requiring an optimistic-lock conflict and a retry that preserves both changes. Existing manager/UID authorization, creation receipts, live-Identity fencing, queue, wake and replica scenarios remain covered.

Rollout: deploy/pin this Gateway behavior before the EnvironmentService extraction. Rollback: revert the Gateway image pin; the old image conservatively refuses a rebind with conflicting retained Sessions again. Preserve Sandbox Identity choices, Session rows and History; failed commands remain failed, with no replay or data migration. After Flux applies the pin, check inventory/Session reads, normal command delivery, stopped-state admission, queue/wake and History. Required system test: full //agentplane/system_tests/suites:test_dispatcher.

Deliberately leaves out the undecided Runtime check comparing a Session's recorded Identity with its own credential, the EnvironmentService move, caller-authentication changes, Session Identity rebinding, and any new cross-component RPC. It does not grant environment-management authority to Identity callers.

Rebased onto 9a955bd3 after Gateway service SVID admission #16364, preserving its service-role fields and authentication behavior. Conflict resolution only adjusts the existing removal/budget change to that base. Post-rebase bbr test //agentplane/runner_gateway/environments/internal/management:unit_test //agentplane/runner_gateway/environments:environments_test //agentplane/runner_gateway/dispatcher:store_integration_test //agentplane/system_tests/suites:test_dispatcher --test_output=errors — all four executed and passed (ee7652bc); the full Dispatcher system suite passed in 222.0s. Pre-commit passed; full current-head CI pending.

Latest rebase: head a36b49798ff47c173a4789b805ad64c607a4161f is based on a68e0de510d6ea10184705ab453a69259cb85801, including service-SVID admission and the system-test root-key renewal correction #16377. The rebase was conflict-free; range-diff showed unchanged patches, and it was published with --force-with-lease. GitHub confirms this head. Current-head pre-commit, Nix and artifact/import checks passed; Bazel and visual review are running, with no reported failures as of 2026-11-14 08:29 UTC. The focused RBE results above remain prior-base validation. Fresh origin/devel is 6e591f383864ab47d7a9370abb4bd6637f0faee3; its two additional commits change image pins and a Dispatcher documentation note, and GitHub reports this PR mergeable.

Questions for Claude

None.

agentydragon pushed a commit that referenced this pull request Nov 14, 2026


#16341 removed the Dispatcher's readers for pre-Identity and
unattributed rows after #16340 dropped them, so the
transition-tolerance-removal track left the plan and the ledger
re-pinned. Stage 1 now holds only the Gateway connection Lease.

Since 1f3e26c, #16341, #16355, #16360, #16363, #16367 and #16369
edited 44 Go files; none were added, removed or moved and no .proto
changed. The evidence's History correction no longer names the removed
track. The identity-fence step names its open PR, #16373.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AYUnw7BD5YA1NAuqjeKKSm
agentydragon added a commit that referenced this pull request Nov 14, 2026
…16368)

This is the single open batch PR for the Agentplane organization plan.
Later plan updates go here as commits until it merges.

## Commit 1: identity-fence decision

The operator decided at 2026-11-14 07:20 UTC, answering the card "Choose
how an Identity change is fenced against Session admission": "okay,
sounds good to me, sessions can re-check".

- **Cut line "Identity change against Session admission"** is now
DECIDED.
- There is no call between the binaries. EnvironmentService patches
`spec.identity` under optimistic locking.
  - The Sandbox CRD already admits that change only while nothing runs.
- Sessions re-check the live Identity at admission, placement and every
delivery.
- The retained-Session refusal goes. Session rows are never deleted, so
the refusal blocked any rebind once a Session had existed.
- Proposed addition, not decided: the Runtime refuses to run a retained
Session whose recorded Identity differs from the Identity on its own
credential.
- **`environment-service-to-sandbox-controller`.**
- Contract item (c) is decided. Dropping
`dispatcher.Store.ChangeSandboxIdentity` and its refusal is its own
behavior PR, ahead of the move.
- The gate, the preserved invariants and the next-PR step change to
match.
- **Stale path fixed.** The automatic-wake card now says
`environments/internal`.
- **Evidence.** `evidence.json` records the operator's words and the
checked facts: the CRD's stopped-only rule, and the Runtime's
re-admission check.

## Commit 2: explicit-resume decision

The operator decided at 2026-11-14 07:41 UTC, card "Choose how explicit
resume is rate-bounded once EnvironmentService has no database": "No
budget for explicit resume".

- **Cut line "Rate bound for explicit resume"** is now DECIDED. Explicit
resume reserves no wake budget; only the Sessions binary's automatic
wake keeps one.
- A resume acts only on a fully stopped Sandbox, and the generation
token makes a stale resume return `STATE_CHANGED` (`lifecycle/SPEC.md`).
So a Sandbox is resumed at most once per stop.
  - An Identity's `resume` grant scopes which Sandboxes it may wake.
- Dropping the reservation from the explicit path is a behavior PR
before EnvironmentService moves.
- **`environment-service-to-sandbox-controller`.** Contract item (b) and
the preserved invariants now name only automatic wake's rate limit.
- **Keep-alive holds.** The proposal names Identities with a `resume`
grant beside operators as explicit resumers.
- **Operator questions.** The queue is empty, and the page says "None
open." when it is.
- **Transition tolerances.** History's empty Sandbox UID is listed as
live again. A central Session may have no Sandbox, and #16356 and #16336
were closed unmerged.
- **Evidence.** The operator's words are recorded. A verified fact that
predated #16346 no longer says only operators may resume.

## Commit 3: burn down `transition-tolerance-removal` and re-pin at
`7867f81780`

- **Track landed whole.** #16341 removed the Dispatcher's readers for
Sessions without an execution Identity or caller attribution, and for
dispatch rows without stored attribution, after #16340 dropped those
rows. The track, its next-PR entry and its ledger references are gone.
Stage 1 now holds only the Gateway connection Lease.
- **Ledger re-pinned.** Since `1f3e26cc58`, #16341, #16355, #16360,
#16363, #16367 and #16369 edited 44 Go files. None were added, removed
or moved, and no `.proto` changed. #16369 added History migration
`00019_service_writer.sql`.
- **Evidence.** The History correction no longer names the removed
track. The identity-fence step names its open PR, #16373.
- **Home page.** The reviewed-source line names the new pin.

## Checks

- Structural check against the `7867f81780` inventory: PASS. All 1,275
Go files match, in 223 directories.
- `prettier@3.9.10 --check`: clean.
- Local Playwright: the report, the home page and the target-binaries
section render at desktop and mobile, in light and dark, with no
overflow and no console errors. All seven cut-line cards render, and the
empty question list shows "None open.".
- No source behaviour tests were run; this is a planning-only change.

---
🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01AYUnw7BD5YA1NAuqjeKKSm

---------

Co-authored-by: Claude <noreply@anthropic.com>
agentydragon pushed a commit that referenced this pull request Nov 14, 2026
Explicit `ResumeEnvironment` no longer reserves the shared wake budget,
for direct Pod-bound and service-SVID operator callers and Identity
resume grants. Automatic Dispatcher `RequestWake` alone reserves
`ReserveWakeAttempt`; authorization, operator-stop handling, generation
tokens and optimistic locking retain their existing behavior. This
implements the operator’s 2026-11-14 07:41 UTC decision, “No budget for
explicit resume,” recorded in [plan PR
#16368](#16368) at
`0675c37082`.

`TestExplicitResumeDoesNotReserveWakeBudget` exercises both caller paths
with exhausted and unavailable budget backends and requires successful
resume with zero reservations.
`TestEnvironmentServiceServiceRolesStayMethodScoped` checks a real mTLS
service operator resume succeeds with zero reservations; the
foreign-target Identity grant test also checks zero reservations.
Conflict and stale-token tests retain their coverage, with concurrent
requests synchronized at the Kubernetes patch boundary rather than
inside the budget fake.

Automatic wakes remain bounded:
`TestAutomaticWakeSharesPolicyBudgetAndReportsRetryFacts` still requires
rate limiting with retry facts when exhausted and refusal when the
backend is unavailable; Dispatcher’s
`TestMixedVersionWakeWritersShareCapAndGenerationDedup` and Store
integration tests cover durable shared reservations. Lifecycle SPEC,
README and interface comments now distinguish the two paths.

Validation:

- Changed-file pre-commit and all commit hooks passed (gofumpt applied
formatting first).
- `bbr test
//agentplane/runner_gateway/environments/lifecycle:lifecycle_test
//agentplane/runner_gateway/environments:environments_test
//agentplane/runner_gateway/dispatcher:store_integration_test
//agentplane/system_tests/suites:test_dispatcher --test_output=errors` —
all 4 passed
([45ead54e](https://app.buildbuddy.io/invocation/45ead54e-7d63-4603-837d-3ad3e388ce46));
3 executed, Dispatcher storage integration cached. The full Dispatcher
system suite passed in 228.3s and covers queue, automatic wake, delivery
and replica behavior. Full current-head GitHub CI pending.

Rollout: deploy/pin the Gateway behavior. No settings, manifests, schema
or wire contract changes, so no image-before-config ordering applies.
Independent of the identity-change fence PR #16373. Rollback: revert the
Gateway image pin; explicit resume again uses the existing budget.
Retain current Sandbox generations and budget rows. After Flux applies
the pin, check explicit resume and automatic queued wake, including
normal delivery and wake-budget retry facts.

Deliberately leaves out automatic-wake policy or limit changes,
settings/manifests, storage ownership or service extraction, Runtime
Identity defense, caller-authentication changes, and any new
cross-component call.

Rebased onto `9a955bd3` after Gateway service SVID admission #16364,
preserving its service roles and authentication behavior. Post-rebase
focused validation
([0fd41c23](https://app.buildbuddy.io/invocation/0fd41c23-f01e-45ce-9115-6f0652352163))
passed lifecycle, Dispatcher storage (cached), and the full Dispatcher
system suite (231.9s), but found the newly landed mTLS service-operator
test still asserting an explicit budget reservation. Commit `d63bccb9`
changes that assertion to zero reservations while retaining its
method-scoped authorization and desired-state checks. The corrected `bbr
test //agentplane/runner_gateway/environments:environments_test
--test_output=errors` executed and passed
([3fcea46b](https://app.buildbuddy.io/invocation/3fcea46b-f7c8-40f4-bb2d-b4f8d5f0c93a)).
Changed-file pre-commit and commit hooks passed. Full current-head CI
pending.

Latest rebase: head `9f01bc92f168218eb361383b6dd6c58b81224226` is based
on `a68e0de510d6ea10184705ab453a69259cb85801`, including service-SVID
admission and the system-test root-key renewal correction #16377. The
rebase was conflict-free; range-diff showed unchanged patches, and it
was published with `--force-with-lease`. GitHub confirms this head.
Current-head pre-commit, Nix and artifact/import checks passed; Bazel
and visual review are running, with no reported failures as of
2026-11-14 08:29 UTC. The focused RBE results above remain prior-base
validation. Fresh `origin/devel` is
`6e591f383864ab47d7a9370abb4bd6637f0faee3`; its two additional commits
change image pins and a Dispatcher documentation note, and GitHub
reports this PR mergeable.

## Questions for Claude

None.

---------

Co-authored-by: agentydragon-agent <260791330+agentydragon-agent@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

Visual review for a36b4979

Request a manual rerun from the CI run page.

Warning

1 target used the latest devel baseline (bb7c716f) because the PR base published none. Diffs there may include devel drift, not just this PR's changes.

No visual changes among the 1 affected Bazel test target. Open visual review.

This branch has not been deployed

No deployments
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