Skip to content

afx send <workspace>:<agent> has no disambiguator when basenames collide — support path-tail matching #1565

Description

@amrmelsayed

Problem

The cross-workspace half of the afx send <workspace>:<agent> address is resolved by basename only: findWorkspaceByBasename (packages/codev/src/agent-farm/servers/tower-messages.ts:249-273) compares path.basename(wsPath) (lowercased) against the live workspace map and nothing else. When two active workspaces share a basename, resolution fails closed with AMBIGUOUS — correct per the never-guess doctrine — but the grammar offers no way to disambiguate, so the sender has no in-address recourse at all.

Live instance (2026-08-31): ~/repos/cluesmith/codev and ~/repos/amrmelsayed/codev were both active; afx send codev:architect from the second workspace returned AMBIGUOUS, and the sender's only workaround was to drop the project prefix and run the send from a directory inside the target workspace (a bare architect address skips basename lookup and uses the CWD-derived workspace context). That workaround is undiscoverable and defeats the point of cross-workspace addressing.

Proposed fix

Extend the workspace match to a two-stage resolution, mirroring the builder tail-match precedent already in the same resolver (resolveAgentInRegistry's "exact then tail match; ambiguous tail is still AMBIGUOUS"):

  1. Exact basename match (current behavior). Unique → resolve.
  2. Otherwise treat the project part as a path tail: cluesmith/codev:architect matches any active workspace whose path ends with /cluesmith/codev. Unique → resolve; still ambiguous or zero → the existing errors.

The AMBIGUOUS error message should teach the syntax: list the matching paths (it already does) AND say "disambiguate with a path tail, e.g. cluesmith/codev:architect".

Notes

  • Fail-closed stays: no guessing at any stage; only a unique match resolves.
  • Slash in the project part is currently impossible to confuse with the architect:<name> intercept (that form is matched on the literal string architect before workspace resolution) — but the implementer should add a test proving a/b:agent parsing survives parseAddress's overloaded grammar.
  • Same resolution should apply everywhere findWorkspaceByBasename is consumed (live resolver and registry/mailbox-hold path), so the mailbox-first hold works for tail addresses too.
  • Tests: unique basename, colliding basenames + unique tail, colliding tails, zero matches, case-insensitivity, and the error-message hint.

Metadata

Metadata

Assignees

No one assigned

    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