Escalation
cmd/crewship/cmd_escalation.go covers the escalation verbs: per-crew listing, resolution, withdrawal, the deadline sweep, and the workspace-wide pending-count aggregator used by dashboards and alerting.
The lifecycle
An escalation is a question an agent asked a human and blocked on. It has one non-terminal state and three terminal ones:EXPIRED escalation no longer reports itself as “already resolved”, which used to tell an operator somebody had decided it when nobody had.
Two deadlines, and only one of them is yours
Every escalation carries two:
When
deadline_at passes, the agent stops waiting and continues without the answer, having been explicitly told so — see Harbormaster → What happens when nobody answers. The row stays PENDING and you can still answer it. Only answer_deadline_at makes it EXPIRED.
agent_gave_up_at (in --format json) tells you the agent already moved on. Answering is still worthwhile — an approved credential proposal lands in the vault for the next run — but the run that asked will not receive it. escalation resolve prints that as a note.
crewship escalation list
Lists escalations under one crew via GET /api/v1/crews/{crewId}/escalations. --crew is required — the server enforces crew-level auth, and listing across all crews would require N fan-out calls anyway.
Table columns:
ID, TYPE, FROM, REASON (50-char truncated), STATUS, DEADLINE, CREATED. DEADLINE prints — for rows raised before deadlines existed; those never expire.
Listing also settles any past-answer-deadline question in the workspace before it answers, so the list can never show a row as PENDING past the point where you could still act on it. A row past its agent deadline is untouched — it is still yours to answer.
crewship escalation resolve <id>
PATCH /api/v1/escalations/{id}/resolve. Optional notes land in the audit row.
Escalation <id> resolved. on success.
Resolving works even after the agent stopped waiting — that is the point of the two deadlines. When it does, the response carries agent_still_waiting: false and a note naming what the answer did and did not accomplish: the decision is recorded and an approved credential proposal is activated in the vault for the next run, but the run that asked has already finished without it and is not resumed.
Resolving a CREDENTIAL escalation is refused with 403 when the caller is
recorded as the owner of the agent that raised it — including a workspace OWNER,
which is not exempt. Two independent things put that
segregation of duties
rule in force, and either is sufficient:
- the workspace toggle (
crewship keeper second-approver enable), which covers every credential; or - the credential’s own L4 · critical tier, which forces the rule whether or not the workspace opted in — a tier can only tighten it, never loosen it.
crewship keeper status prints which of the two applies on its In force:
line, so you can check before you are refused rather than after. See
Keeper → The switch is a floor, not a master switch.
crewship escalation cancel <id>
POST /api/v1/escalations/{id}/cancel. Withdraws a question instead of deciding it — the deploy was rolled back, the task was reassigned, the agent was restarted. MANAGER+.
crewship escalation sweep-expired
POST /api/v1/escalations/sweep-expired. Moves every PENDING escalation in this workspace past its answer_deadline_at to EXPIRED, and prints how many moved. ADMIN+ — it writes terminal states.
The agent’s deadline_at is not swept and never was grounds to expire a row: an agent giving up on a poll is not a human declining to answer. A CREDENTIAL escalation that does expire here disposes of its staged credential the way a rejection does, so no unreachable secret is left in the vault.
0 the second time: the transition is compare-and-swap guarded, so each escalation expires exactly once no matter how many observers notice.
crewship escalation pending-count
Workspace-wide aggregate — backs dashboard tiles and alerting that just needs the total without per-crew fan-out. GET /api/v1/escalations/pending-count returns {"count": N}.
See also
crewship approvals— the related human-in-the-loop surface (Keeper denials, MCP gates).crewship notify— desktop notifications when escalations land.crewship journal— live event tail. Every transition emitspeer.escalationwithpayload.stateofpending|resolved|expired|cancelled; expiries are severitywarnso they surface in the default attention filter.- Harbormaster — the state machine, where the deadline is decided, and what an agent does with an unanswered question.