Repository navigation
ui: name the action that brings a sandbox back up in the list - #1257
Conversation
|
Thanks for opening this pull request! A SPACE maintainer will take a look. CONTRIBUTING.md explains how we review outside contributions. |
bde9bd8 to
0dfbddf
Compare
0dfbddf to
4dd1e19
Compare
zibi-pplx
left a comment
There was a problem hiding this comment.
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:408andui/src/lib/sandbox-lifecycle.ts:27:bringUpActionkeys offstatus. As the description notes,statusis the node's reported state whenever a node reports one. #1238's render table shows that a storedsuspendedwith an observedstoppedrenders asstopped. In that case the row now sendsforce-start, where it used to sendforce-wake, which let the server pick the path from the stored lifecycle. Can you confirm whatforce-startdoes for a sandbox whose stored lifecycle issuspended: restore, cold boot, or reject? If it isn't a restore, there are two options:- key
bringUpActiononlifecyclewhen it is present; - keep Wake when
statusandlifecycledisagree.
- key
-
[low] The status filter is missing
unspecified, and has no catch-all.
ui/src/lib/sandbox-lifecycle.ts:20: nodes can reportunspecified(it is in #1238's list of node statuses), but it isn't inSANDBOX_STATUS_FILTER_STATES. Any status outside the list can only be found with the filter cleared. Suggestion: addunspecified, or build the options from the known list plus any other statuses present in the loaded rows. -
[nit]
SANDBOX_LIFECYCLE_STATESis a hand-kept copy ofSandboxLifecycle, whose ownalllist is already maintained by hand. A pointer comment on the Rust enum would help keep the copies in step.
Withdrawing this approval for now. Any feedback above still stands.
johnlouie-pplx
left a comment
There was a problem hiding this comment.
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.
48d2847 to
d676430
Compare
|
Thanks, you were right: the admin list fills both fields from the observed status. d349688 keys the bring-up action on |
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.
d349688 to
ffe0813
Compare
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:
The action keys on the row's
status. The admin list fills bothstatusandlifecyclefrom the node's reported state when a node reports one, falling back to the stored lifecycle for both (sandbox_to_cached_responseincrates/libs/api/src/controllers/sandbox/responses.rs), so a list row does not carry the stored state separately. A sandbox stored assuspendedwhose node reportsstoppedtherefore shows Start; the server handles that too, becausestarton a suspended sandbox goes through the restore machinery.Status filter. It offered
running, which the admin API never reports (it reportsstarted), so choosing it matched nothing. It also lackedcheckpointingandquarantined. It now offers the lifecycle states ofSandboxLifecycle, the three node-only statesunspecified,cleanup_requiredandcleanup_failed, anddeleted, 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, asunknown, 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 fromSandboxLifecycleState(crates/libs/sbx/mgr/protocol/src/lifecycle.rs). A delete through the API removes the sandbox's row (crates/libs/sandbox/teardown); adeletedrow 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
SandboxLifecycleincrates/libs/common/core/src/types/sandbox.rssays thatui/src/lib/sandbox-lifecycle.tsmirrors 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, includingsrc/lib/sandbox-lifecycle.test.mjs; new tests coverunspecifiedand the options taken from loaded rows),npx tsc --noEmit -p .(no errors) andnpm run buildinui/.rustfmt --edition 2024 --checkpasses oncrates/libs/common/core/src/types/sandbox.rs;cargo checkwas not run for the comment-only change.cleanup_requiredandcleanup_failed, adds a syntheticdeletedtombstone row to the list answer (Kind runs the Kubernetes substrate, which keeps none), and checks that filtering ondeletedshows it.space-uiimage). Clicking Restore on the seeded suspended sandbox's row brings it back tostarted; the loop then suspends it again.main.statusreplaced on two rows:unspecified, andhibernating, a synthetic status no list names. In the status filter,unspecnow offersunspecifiedandhibernoffershibernating, where the earlier head offered neither. Theunspecifiedrow 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":
Videos: 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:
unspecifiedandhibernating.Typing
unspecin the status filter:unspecifiedis now offered:Typing
hibern:hibernating, a status only the loaded rows carry, is now offered: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 wherewakewould 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.