Skip to content

ui: name the action that brings a sandbox back up in the list - #1257

Merged
ac-pplx merged 6 commits into
mainfrom
ac/admin-ui-sandbox-row-actions
Nov 2, 2026
Merged

ac-pplx merged 6 commits into
mainfrom
ac/admin-ui-sandbox-row-actions

Conversation

@ac-pplx

@ac-pplx ac-pplx commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Sandbox list rows now name the action they will run, and the status filter offers the states the list reports.

  • Row actions. Rows offered a single Wake for stopped, paused and suspended sandboxes, while the detail page offers Start, Resume and Restore. Wake accepts any of those paths, so the list never said whether a click resumes a sandbox in memory or restores it from a snapshot. Rows now offer the action that matches the state, using the same admin force actions as the detail page:

    • Start for created and stopped sandboxes;
    • Resume for paused ones;
    • Restore for suspended ones.

    The action keys on the row's status. The admin list fills both status and lifecycle from the node's reported state when a node reports one, falling back to the stored lifecycle for both (sandbox_to_cached_response in crates/libs/api/src/controllers/sandbox/responses.rs), so a list row does not carry the stored state separately. A sandbox stored as suspended whose node reports stopped therefore shows Start; the server handles that too, because start on a suspended sandbox goes through the restore machinery.

  • Status filter. It offered running, which the admin API never reports (it reports started), so choosing it matched nothing. It also lacked checkpointing and quarantined. It now offers the lifecycle states of SandboxLifecycle, the three node-only states unspecified, cleanup_required and cleanup_failed, and deleted, then any other status present in the loaded rows, so no row is reachable only with the filter cleared. A row without a status is offered, and filters, as unknown, matching its tag. The list shows a node's reported state when a node reports one (crates/libs/api/src/controllers/sandbox/responses.rs), and those three come from SandboxLifecycleState (crates/libs/sbx/mgr/protocol/src/lifecycle.rs). A delete through the API removes the sandbox's row (crates/libs/sandbox/teardown); a deleted row is one a Direct node's complete census no longer includes (crates/libs/internal/api/server/src/status.rs). It is kept for a retention period, and the admin list, which does not filter by lifecycle, returns it.

  • Rust pointer. A one-line comment on SandboxLifecycle in crates/libs/common/core/src/types/sandbox.rs says that ui/src/lib/sandbox-lifecycle.ts mirrors it. It changes no code.

Stack

Stacked on #1248 (sandbox list org column and filter), which is stacked on #1244 and #1242. Merge those first.

Validation

  • npm ci, npm test (86 pass, including src/lib/sandbox-lifecycle.test.mjs; new tests cover unspecified and the options taken from loaded rows), npx tsc --noEmit -p . (no errors) and npm run build in ui/. rustfmt --edition 2024 --check passes on crates/libs/common/core/src/types/sandbox.rs; cargo check was not run for the comment-only change.
  • The loop's status-filter step checks that the filter offers cleanup_required and cleanup_failed, adds a synthetic deleted tombstone row to the list answer (Kind runs the Kubernetes substrate, which keeps none), and checks that filtering on deleted shows it.
  • A Playwright loop against a local Kind stack with synthetic data, on this branch (the Kind space-ui image). Clicking Restore on the seeded suspended sandbox's row brings it back to started; the loop then suspends it again.
  • No new console errors, page errors or failed requests compared with main.
  • A Playwright run on the same Kind stack compared production builds of an earlier head of this branch and of this head, with the list answer's status replaced on two rows: unspecified, and hibernating, a synthetic status no list names. In the status filter, unspec now offers unspecified and hibern offers hibernating, where the earlier head offered neither. The unspecified row offers no bring-up action, before and after.

Visual confirmation

Hovering the action of a paused, a suspended and a stopped row. Before, each said "Wake". After, they say "Resume", "Restore" and "Start":

before
after

Videos: before, after.

Before After
before after

In the pairs below, before is an earlier head of this branch and after is this head, with synthetic statuses on two rows: unspecified and hibernating.

Typing unspec in the status filter: unspecified is now offered:

Before After
before after

Typing hibern: hibernating, a status only the loaded rows carry, is now offered:

Before After
before after

Severity and risk

Severity: low.

Risk: low. Rows now send the specific start, resume or restore request instead of wake. The admin routes accept it (202) and queue an operation. When the row is stale (the sandbox changed state since the list loaded), that operation can fail where wake would have taken any valid path; the console polls the operation, shows its failure, and the operator refreshes the list. Roll back by reverting the commits.

AI assistance: Claude Code wrote the code and this description, and ran the validation; a maintainer reviews it.

@github-actions github-actions Bot added the triage:pending Waiting for a maintainer to triage label Oct 31, 2026
@github-actions

Copy link
Copy Markdown

Thanks for opening this pull request! A SPACE maintainer will take a look.

CONTRIBUTING.md explains how we review outside contributions.

@ac-pplx
ac-pplx force-pushed the ac/admin-ui-sandbox-row-actions branch from bde9bd8 to 0dfbddf Compare October 31, 2026 22:32
@ac-pplx
ac-pplx force-pushed the ac/admin-ui-sandbox-row-actions branch from 0dfbddf to 4dd1e19 Compare November 1, 2026 05:00
@ac-pplx
ac-pplx marked this pull request as ready for review November 1, 2026 05:06
zibi-pplx
zibi-pplx previously approved these changes Nov 1, 2026

@zibi-pplx zibi-pplx left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The action names are clearer. One question about where the bring-up action comes from, and a gap in the filter.

  • [medium] Is Start correct when the node-reported status and the stored lifecycle disagree?
    ui/src/app/dashboard/sandboxes/page.tsx:408 and ui/src/lib/sandbox-lifecycle.ts:27: bringUpAction keys off status. As the description notes, status is the node's reported state whenever a node reports one. #1238's render table shows that a stored suspended with an observed stopped renders as stopped. In that case the row now sends force-start, where it used to send force-wake, which let the server pick the path from the stored lifecycle. Can you confirm what force-start does for a sandbox whose stored lifecycle is suspended: restore, cold boot, or reject? If it isn't a restore, there are two options:

    • key bringUpAction on lifecycle when it is present;
    • keep Wake when status and lifecycle disagree.
  • [low] The status filter is missing unspecified, and has no catch-all.
    ui/src/lib/sandbox-lifecycle.ts:20: nodes can report unspecified (it is in #1238's list of node statuses), but it isn't in SANDBOX_STATUS_FILTER_STATES. Any status outside the list can only be found with the filter cleared. Suggestion: add unspecified, or build the options from the known list plus any other statuses present in the loaded rows.

  • [nit] SANDBOX_LIFECYCLE_STATES is a hand-kept copy of SandboxLifecycle, whose own all list is already maintained by hand. A pointer comment on the Rust enum would help keep the copies in step.

@zibi-pplx
zibi-pplx dismissed their stale review November 1, 2026 22:30

Withdrawing this approval for now. Any feedback above still stands.

@ac-pplx
ac-pplx added this pull request to stack #1283 November 2, 2026 04:30

@johnlouie-pplx johnlouie-pplx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving with one nonblocking comment at 48d2847. I rechecked the response path and reran the seven focused lifecycle tests; they pass.

bringUpAction still prefers lifecycle, but sandbox_to_cached_response fills both lifecycle and status from the node's observed status, falling back to the stored lifecycle for both. A stored suspended sandbox observed as stopped therefore reaches this helper with both fields set to stopped, and it chooses Start. The differing-field test and Playwright fixture don't establish the claimed Restore behavior for real API responses.

Could we simplify this to use status and correct the code comment and PR description? If distinguishing stored from observed state is intended, that needs a separate backend field first. This is nonblocking; the extra preference doesn't change behavior with the current response contract.

Base automatically changed from ac/admin-ui-sandbox-list-org to main November 2, 2026 06:47
@ac-pplx
ac-pplx force-pushed the ac/admin-ui-sandbox-row-actions branch from 48d2847 to d676430 Compare November 2, 2026 06:47
@ac-pplx

ac-pplx commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, you were right: the admin list fills both fields from the observed status. d349688 keys the bring-up action on status alone, drops the unused lifecycle prop and the test that assumed the two could differ, and the description now says so. Telling stored from observed state in the list would need a separate backend field.

Sandbox list rows offered a single Wake for stopped, paused and
suspended sandboxes, while the detail page offers Start, Resume and
Restore. Wake accepts any of those paths, so the list never said
whether a click resumes a sandbox in memory or restores it from a
snapshot. Rows now offer the action that matches the state: Start for
created and stopped, Resume for paused, Restore for suspended.

The status filter offered "running", which the admin API never reports
(it reports "started"), and lacked "checkpointing" and "quarantined".
It now offers the lifecycle states the admin API returns.
The list shows a node's reported state when a node reports one, so it
can show the two node-only states, cleanup_required and cleanup_failed,
which the filter did not offer. It also reports deleted: a delete
through the API removes the row, but a sandbox a Direct node's complete
census no longer includes is tombstoned with lifecycle "deleted" and
stays listed for a retention period. The status filter now offers the
control-plane lifecycle states, the two node-only states, and deleted.
The bring-up action keyed off status, which is the node's reported
state when a node reports one, so a sandbox stored as suspended whose
node reports it stopped offered Start. The server restores it either
way, since start on a suspended sandbox goes through the restore
machinery, but the label was wrong. The action now keys on the row's
lifecycle when it has one and falls back to status, with a test.
The status filter left out unspecified, which nodes can report, and any
other status a loaded row carried, so such a row was reachable only
with the filter cleared. It now offers unspecified, and after the known
states every other status present in the loaded rows; a row without a
status is offered, and filters, as unknown, the way its tag reads.
A one-line comment on SandboxLifecycle says ui/src/lib/sandbox-lifecycle.ts
mirrors its states, so a change to one reaches the other.
The admin list fills both a row's status and lifecycle from the node's
reported state when a node reports one, falling back to the stored
lifecycle for both (sandbox_to_cached_response), so preferring lifecycle
never changed the action and the stored state is not available in a
list row. Key on status, drop the unused lifecycle prop and the test
that assumed the two could differ, and say why in the comment.
@ac-pplx
ac-pplx force-pushed the ac/admin-ui-sandbox-row-actions branch from d349688 to ffe0813 Compare November 2, 2026 16:17
@ac-pplx
ac-pplx added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit fa4d33d Nov 2, 2026
23 checks passed
@ac-pplx
ac-pplx deleted the ac/admin-ui-sandbox-row-actions branch November 2, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage:pending Waiting for a maintainer to triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants