Skip to content

afx send: builder→non-spawning-architect send surfaces bare [error] NOT_FOUND, dropping the descriptive reason #1333

Description

@mohidmakhdoomi

Problem

When a builder addresses an architect it isn't allowed to (afx send architect:<name> where <name> isn't the builder's spawning architect), the CLI prints only the opaque code:

Sending Instruction
───────────────────
[error] NOT_FOUND

The agent/human can't tell why it failed — "no such architect" vs. "not authorized to address that architect" look identical. During Spec 1313 manual testing a builder literally had to guess: "no architect named codex-architect is registered/active (or, since I'm a builder, architect: only works for my own spawning architect)."

Tower does generate a descriptive message for exactly this case — it just never reaches the CLI.

Root cause (verified in code)

Two layers collaborate to drop the detail:

  1. resolveArchitectByName (packages/codev/src/agent-farm/servers/tower-messages.ts) returns a helpful message on the spoofing rejection:

    return { code: 'NOT_FOUND', message: addressSpoofingErrorMessage(sender) };
    // "builder <id> may only address its own spawning architect"

    (and a distinct "Architect '<name>' not found in workspace '<ws>'." for a genuinely-missing architect.)

  2. handleSend (packages/codev/src/agent-farm/servers/tower-routes.ts) serializes both fields:

    sendJson(res, statusCode, { error: errorCode, message: result.message });
  3. But the client's generic error extraction (packages/core/src/tower-client.ts, request()) prefers the code over the detail:

    error = json.error || json.message || text;   // → "NOT_FOUND", message discarded

    send.ts then throws new Error(result.error)fatal("NOT_FOUND").

So the descriptive message is produced server-side and thrown away client-side.

Impact

Diagnosability only — no misrouting, no corruption. But it makes a common, legitimate rejection (the builder spoofing guard) hard to understand, especially in multi-architect workspaces.

Suggested fix (one to decide during planning)

  • Preferred: when the response carries both, surface the human-readable message (optionally as "<message> (<code>)") instead of the bare code — either in tower-client.request()'s extraction (json.message || json.error, or combine) or in send.ts's error rendering. Keep the machine code available for programmatic callers.
  • Confirm the two error strings remain distinguishable: spoofing rejection vs. genuinely-absent architect.

Notes

  • Pre-existing, not introduced by Spec 1313 — the generic json.error || json.message extraction predates it. Surfaced while testing afx send: mailbox-first delivery — persist, gate on rendered-empty, never force-inject #1313 (see SPIR-1313-testing.md, steps 7/9/14/15).
  • Scope is the afx send / Tower error-surfacing path; addressing/spoofing semantics are correct and should not change (a builder may only address its own spawning architect).
  • Small, isolated (~error-rendering seam); good BUGFIX candidate.

Metadata

Metadata

Labels

area/towerArea: Tower server / agent farm CLI

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions