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"):
- Exact basename match (current behavior). Unique → resolve.
- 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.
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) comparespath.basename(wsPath)(lowercased) against the live workspace map and nothing else. When two active workspaces share a basename, resolution fails closed withAMBIGUOUS— 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/codevand~/repos/amrmelsayed/codevwere both active;afx send codev:architectfrom 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 barearchitectaddress 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"):cluesmith/codev:architectmatches 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
architect:<name>intercept (that form is matched on the literal stringarchitectbefore workspace resolution) — but the implementer should add a test provinga/b:agentparsing survivesparseAddress's overloaded grammar.findWorkspaceByBasenameis consumed (live resolver and registry/mailbox-hold path), so the mailbox-first hold works for tail addresses too.