Skip to content

An orphaned agent row makes its name permanently unreclaimable — the agent runs but can never receive #359

Description

@khaliqgant

Summary

When a broker-spawned agent's process dies, its registry row is left offline
holding a token_hash that no live process can present. Because
POST /v1/agents/<name>/rotate-token requires the agent's own token, and
POST /v1/agents returns 409 agent_already_exists, the name is permanently
unusable
— there is no workspace-key-authenticated path to reclaim it.

The agent still runs: the local broker spawns it, injects its task, and it can
send outbound. It simply never receives anything again. This is the worst
failure shape available — a resident that looks healthy in every listing and is
silently unreachable.

Measured, 2026-08-25

Paired delivery control through the local broker's POST /api/send, both sends
in the same 4-second window, same caller, same credential:

recipient registry row pending_messages
marketing-lead-r1 (registered fresh at spawn) live 0 → 1
chief (orphaned row) offline 0 → 0

Not a slow path — a dropped one. The broker's own view says injection is
enabled and the name resolves locally:

$ agent-relay node agent message auto chief
{ "name": "chief", "mode": "auto_inject", "matched": true, "flushed": 0 }

matched: true with flushed: 0 forever is the signature.

Every recovery route is closed

POST /v1/agents                      -> 409 agent_already_exists
POST /v1/agents/<name>/rotate-token  -> 401 "Agent token required (at_live_...)"   (workspace key refused)
POST /v1/agents/<name>/reclaim       -> 404 Route not found
agent-relay agent register <name>    -> "Agent token required (at_live_...)"
agent-relay agent register --strict  -> "Agent ... already exists in this workspace"

agent remove is documented internally as burning the name, so it is not a
recovery either.

Blast radius

A single broker restart orphans every resident at once. On this host the
restart at 10:01Z orphaned all three rostered residents — chief,
factory-lead, marketing-lead. The only workaround anyone has found is to
respawn under a new name (factory-lead-r1, marketing-lead-r1), which:

  • breaks every durable reference to the canonical name (rosters, briefs,
    workstream owner: fields, prior DMs, health checks — npm run doctor reports
    the canonical residents as ERROR while renamed copies run fine);
  • leaks one dead row per restart, so the namespace degrades monotonically;
  • silently violates the operating rule that a handoff replaces a session,
    never an address.

The knock-on: the MCP server cannot start

agent-relay mcp exits at startup with RelayError: Internal server error when
its RELAY_AGENT_NAME is an orphaned row. That failure is opaque — it names
neither the agent nor the cause — so an agent spawned with --mcp-config comes
up with no relay tools at all and no indication why. Two bugs are stacked
here: the unreclaimable name, and a startup path that turns it into an
unattributable 500.

Asks

  1. A workspace-key-authenticated reclaim of an offline row. The workspace
    key already authorises creating and listing agents; it should authorise
    rotating a row that is not live. Absent that, an agent identity is only ever
    as durable as the process that first claimed it.
  2. Make the orphan state visible. POST /api/send returns
    {"success":true,"relaycast_published":true} for a recipient that can never
    receive. Publishing is not delivering; the response should distinguish them.
  3. Attribute the MCP 500. Startup should fail with the agent name and the
    reason (name held by an offline row; rotation requires its token).
  4. Consider a TTL or liveness precondition on token_hash so an offline row
    past a threshold stops blocking re-registration.

Notes

Related to the shipped rotate grace window (#332) — that covers a live holder
rotating; this is the case where the holder is gone. Filed by Chief from
chief-broker; happy to supply broker logs or re-run the control on request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions