Skip to content

fix(host): keep channel names unique when an agent or person renames a channel - #518

Merged
iamnbutler merged 2 commits into
mainfrom
unique-names
Oct 19, 2026
Merged

iamnbutler merged 2 commits into
mainfrom
unique-names

Conversation

@iamnbutler

Copy link
Copy Markdown
Contributor

Fixes #503

Channel names were only checked at creation. Now the host injects a claim into the channel (packages/channel stays runtime-neutral), and both rename paths go through it:

  • Agent's channel tool: a taken name is numbered with fork's scheme (name-2, name-3, …), and the tool result says Another channel is named X, so this channel is named X-2.
  • Person's rename (#rename, ace rename, the app): a taken name is refused with "A channel named X already exists", same as creation.

Decision on the first open question — scope: uniqueness is per host and covers the same set as taken(): every channel record on this host, including hidden and archived ones and other tailnets' records, plus their lane prefixes. A channel's own name and prefix are excluded, so renaming back to its original name works. Other hosts' channels in the directory and hosted channels in the team's cell are out of scope.

Second open question — races: the check and the record write happen inside one BEGIN EXCLUSIVE transaction on a host-wide $ACE_HOME/channels/names.lock SQLite file, shared by the host process and every channel worker. The claimed name goes into channel.json before pi commits it, so a concurrent claim in another worker already sees it as taken. catalog.create takes the same lock, so creating a channel can't race a rename either.

Notes:

  • No stored format changes: channel.json and pi's metadata doc are unchanged; names.lock holds no data.
  • Hosted channels (services/channel) don't pass claim, because the cell can't see the host's channels, so their renames are still unchecked. Fork and hosted creation still check, then save after awaits, as before this change.
  • fork now uses the shared unused() numbering helper.

Smoke-tested with the lane's CLI against an isolated ACE_HOME (separate port and keychain service, so the running app and host weren't touched):

  • ace rename beta alpha → refused; ace rename beta gamma then gamma beta (its own prefix) → allowed.
  • After alpha was renamed to omega, ace rename beta alpha → refused, because of omega's lane prefix.
  • Two concurrent ace rename calls to delta from different channels → one succeeded, one was refused.
  • A real agent (claude-opus-5-5) was asked to rename its channel to the taken delta → the tool returned Another channel is named delta, so this channel is named delta-2, and ace ls showed delta-2.

Built in Ace

No — Claude Code subagent outside Ace, run by the lead during a batch of fixes.

…a channel

The host injects a name claim into the channel. An owner's rename to a taken name is refused;
an agent's chosen name is numbered (name-2, ...) and the tool result says so. The check covers
taken(): every channel record on the host, including hidden and archived ones, and their lane
prefixes. Checks and writes run under a host-wide SQLite lock shared by the host and workers.

Fixes #503

Why: apps/host/src/catalog.ts#function claim | the name is written to the record inside the names.lock transaction, before pi commits it, so a concurrent claim in another worker sees it as taken
@iamnbutler
iamnbutler merged commit 585f597 into main Oct 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keep channel names unique when an agent or person renames a channel

1 participant