Console Agent stop and deletion
Overview
Trace Stop Agent and Delete Agent on Agent detail and Agents list rows, from confirmation to readback. See the parent flow and Agent editing; steps 7 and 8 continue that trace's numbering.
Entry Points
apps/controller/src/console/agents/stop.mts:createAgentStopsends the stop request.apps/controller/src/console/agents/deletion.mts:createAgentDeletionsends the deletion request.apps/controller/src/console/agents/detail.mts:renderAgentDetailshows the Danger zone on Configuration. When deletion starts, the deletion row replaces the tabbed view. Agents list rows also offer Stop and Delete.- A signed-in caller needs Agent
operateto stop and Agentdeleteto delete.
Flow
graph TD
subgraph Browser["Browser"]
E["Confirm Agent deletion"]
S["Confirm Agent stop"]
L["Agents list row icon"] --> E
L --> S
end
subgraph API["Controller API"]
E --> G["DELETE exact Agent"]
S --> T["POST exact Agent stop"]
T -->|Console accepted or uncertain| U["Console refreshes desired state and selected version"]
T -->|Console denied| I
G -->|accepted or uncertain| H["Refresh exact Agent"]
G -->|denied| I["Show access denied"]
H -->|exists| J["Show current Agent state"]
J -->|known deleting and manual request| E
H -->|not found| K["Return to Agents list"]
endThe CLI uses a separate request and output path:
graph TD
CLI["CLI agent stop"] --> READ["Try advisory Agent GET"]
READ -->|read succeeds or fails| POST["Submit exact Agent stop POST"]
POST -->|accepted| OUT["Validate and print Agent and advice"]
OUT -->|write failure| ERR["Exit nonzero without readback or retry"]
ERR -->|manual operator action| GET["occ --namespace NAMESPACE agent get ID"]Execution Trace
7. Request a stop and read back the Agent
A negative permission answer hides the Stop or Delete row, and the card when neither remains. If the dialog is open, it disables confirmation, focuses Cancel and keeps the row. A missing answer keeps the rows; late advice cannot recall a request.
apps/controller/src/console/agents/stop.mts:createAgentStop sends a bodyless POST to the
Agent’s /stop route. OCC’s packages/occ/src/index.ts:stopAgent checks Agent operate,
persists the requested stopped state, and queues reconciliation. Success means admission,
not that Compute has shut down. Cancel restores focus to the invoking button; submission focuses
Refresh stop status. A 403 about grants shows the Console's refusal with the Agent's name
and the Request ID; any other 403 shows the API's text. Either way Stop stays unavailable,
described by it, until a permission answer read after the refusal allows operate, such as the one
a window refocus rereads. An answer read before the refusal, even one that arrives after it,
does not lift it.
For a stopped Agent, Request Stop again confirms a new stop intent, even with no selected
version; the Console cannot inspect history or candidate cleanup. Its confirmation says the
Agent is already requested to stop and that this sends the request again. With no selected version
and no readable versions yet (setNoReadableVersions), the Stop row is hidden unless the viewer
just used it. A running Agent keeps Stop regardless of the latest readable deployment outcome
(setLatestDeployment), as on the Agents list: a failed readable deployment can hide newer queued
work. Agent operate still controls access. The latest readable outcome supplies confirmation
text, not visibility. A queued or in-progress one says Stop cancels that deployment. An unreadable
deployment or a failed read keeps the confirmation that names both outcomes; a succeeded one read
before the Agent reread keeps the running-gateway text. The overview's Runtime row reports
a requested stop. After an accepted Stop during a first deployment, the Stop row reports
Deployment cancellation requested, not deployment or runtime completion. applyAgent clears
that local result when either Stop readback or a detail-page update observes a running request
or a selected version. An unchanged stopped read with no selected version keeps it.
Refresh reads desired state and selected version, not live health. Denial stays inline; changed state updates panels in place. If the read finds deletion in progress, Agent detail switches to the deletion recovery controls without sending a write. Focus moves to Refresh deletion status only when the reader stayed on the disappearing Stop control. An uncertain write blocks another Stop until readback succeeds. Deploy to resume; the lifecycle reference covers worker shutdown and preservation of versions, credentials, and state.
8. Confirm deletion and read back the Agent
apps/controller/src/console/agents/deletion.mts:createAgentDeletion
Delete Agent requests confirmation before sending a bodyless DELETE to the
exact Agent URL. The controller requires Agent delete. A 403 shows the Console's
refusal with the Agent's name and the Request ID, or the API's text when the refusal is not
about grants. Delete Agent stays unavailable, described by it, until a permission answer read
after the refusal allows delete. Acceptance starts asynchronous cleanup and marks the Agent deleting. The page
rereads the exact Agent every few seconds; Refresh deletion status reads it
on demand. A read error stops polling. Only a not-found read after
an accepted or uncertain request, or when an already-deleting Agent is opened,
returns to the Agents list in the selected Namespace. A 403 after the Agent was marked
deleting means the caller can no longer follow cleanup. The Console stops polling and closes
the repeat confirmation if it is open. If the reader confirmed a repeat while that status read was
pending, the Console drops the unsent request. Neither action cancels a DELETE already sent or
establishes whether cleanup finished.
An already deleting Agent keeps Request deletion again beside Refresh. It opens a cancel-first confirmation and sends the same bodyless DELETE only when the reader confirms. Queued or claimed work stays unchanged; terminal work can restart under the controller's existing current-permission and retry-ownership checks. The view does not infer a worker outcome from the Agent's deleting state or add a separate status contract.
An uncertain initial or repeated deletion blocks writes until a successful exact Agent read. A deleting read clears that guard even when the view was already deleting; a failed read does not. Cancel returns focus to the repeat-request button, and successful writes retain status refresh. The browser never retries DELETE automatically.
packages/occ/src/index.ts:deleteAgent owns deletion admission. The
Agent deletion reference covers
worker cleanup and the Namespace-owned resources it preserves.
Stop or delete from an Agents list row
apps/controller/src/console/agents/collection.mts:renderAgentList rows use the
same Stop and Delete confirmations. Each row reads
permission advice
like its deployment, and reads it again when a retained list resumes.
Refusal disables the action and explains why; in an open confirmation, it also
disables confirmation and focuses Cancel. A stopped Agent without a selected
version has no Stop action. A response that confirms the action for the requested Agent
updates its row without polling. If the response names a different Agent, the list reports an
unknown outcome and keeps the last observed state of both Agents. After 404, 409, or an
uncertain outcome, the requested row’s actions stay disabled until Refresh rereads it.
After a 403 about grants, the refused icon stays unavailable and the row's note names the grant
under it, until a permission answer read later allows that action. Any other 403, such as
retry ownership on a stale row, shows the API's text, the row's note points to it, and the row's
actions stay unavailable, described by it, until Refresh. A window refocus that rebuilds the
list shows that refusal again while the Agent is listed, and keeps focus on the same row control,
scrolling only as far as needed to keep its cell and note in view.
If that control is gone or disabled, focus goes to the row's name, or to the page heading when
the row is gone.
CLI stop output
internal/occcli/cli.go:agentCommand also submits a stop through
internal/occclient/client.go:StopAgent. It first tries to read the Agent to
choose advice for an idle Agent, an earlier stop request, or a new stop request.
After the POST, it validates the returned Agent identity, Namespace, and stopped
intent, then prints that Agent to stdout and the advice to stderr.
Each advice branch returns its write error. cmd/occ/main.go:main exits nonzero
when stderr cannot receive the notice. The stop has already been admitted and
stdout may contain the Agent; this output failure does not undo or retry the
stop. The CLI does not automatically read the Agent again after this failure.
Run occ --namespace NAMESPACE agent get ID manually to inspect the Agent's
state before deciding whether to submit another request. A successful notice
still means requested intent, not completed shutdown.
Debugging and Verification
- On repeat
403, follow the API’s retry-ownership explanation. For ordinary denials, checkdeleteon the exact Agent; Agentreadandoperatedo not authorize deletion. Any failed repeat pauses polling until a manual action. Use the displayed request ID when present. - An accepted deletion remains in progress until the exact Agent read reports not found. A failed refresh does not establish whether cleanup finished.
The Go regression TestAgentStopReturnsNoticeDeliveryErrors in
internal/occcli/agent_stop_output_test.go covers all three CLI advice branches
with an unavailable writer, retained JSON output, and one stop request. Compiled
CLI proof can use a synthetic loopback HTTP peer and a read-only stderr descriptor
to exercise the OS write failure. This proves output and exit handling, not
controller persistence or Compute shutdown.
