Skip to content

feat(node): lib-level auto-suffix name resolver (resolveAvailableName)#31

Open
sym-bot wants to merge 1 commit into
mainfrom
feat/lib-level-name-resolver
Open

feat(node): lib-level auto-suffix name resolver (resolveAvailableName)#31
sym-bot wants to merge 1 commit into
mainfrom
feat/lib-level-name-resolver

Conversation

@sym-bot

@sym-bot sym-bot commented Jun 9, 2026

Copy link
Copy Markdown
Owner

What

Moves same-host node-id collision resilience down from the sym-mesh-channel host into @sym-bot/sym, so every host shares it (the sym CLI, sym-swift, custom agents) — not just mesh-channel's server.js resolveNodeName.

Why

Today only sym-mesh-channel auto-suffixes on a name clash. Any other @sym-bot/sym host hard-fails with EIDENTITYLOCK when two co-resident processes want the same name (duplicate dev agent, or sessions sharing a fixed SYM_NODE_NAME).

Changes

  • config.jsresolveAvailableName(base, {maxSuffix=64}): base when free/stale/own, else -2/-3/… past live foreign holders. Over-64-byte candidates skipped; exhausting maxSuffix returns base so acquireIdentityLock still throws (backstop preserved). Factored pidIsAlive() (now reused by acquireIdentityLock, behavior-identical) + lockHolderPid(); all exported.
  • node.js — opt-in opts.autoSuffix (default OFF — strict throw stays default; double-launch detection unchanged). Resolves before identity/dir/lock derive from this.name; node.requestedName preserves the original.

Follow-up (not in this PR)

sym-mesh-channel server.js can delegate to resolveAvailableName and drop its local copy.

Tests

14 new (resolveAvailableName/pidIsAlive/lockHolderPid) — live foreign holder via spawned child, dead-PID, own-PID, multi-skip, 64-byte overflow, validation. config 41/41, node 17/17. The 4 ask/llm-reason failures are pre-existing/env (claude CLI present → NO_PROVIDER path), unrelated.

🤖 Generated with Claude Code

Same-host node-id collision resilience lived only in the sym-mesh-channel
host (server.js resolveNodeName). Every other @sym-bot/sym host — the sym
CLI (`sym start`), sym-swift, custom agents — still hard-failed with
EIDENTITYLOCK when two co-resident processes wanted the same name (a
duplicate dev agent, or sessions sharing a fixed SYM_NODE_NAME).

Move the resolver down into the lib so all hosts share it:

- config.js: add resolveAvailableName(base, {maxSuffix=64}) — returns the
  base when free / stale / our own, else appends -2/-3/… past live foreign
  holders. Over-64-byte candidates are skipped; exhausting maxSuffix falls
  back to base so acquireIdentityLock still hard-fails (backstop preserved).
- factor the liveness probe into pidIsAlive(); acquireIdentityLock now
  reuses it (behavior-identical), and lockHolderPid() reads a holder PID.
  Both exported for hosts/tests.
- node.js: opt-in `opts.autoSuffix` (default OFF — strict throw stays the
  default so double-launch detection is unchanged). Resolves BEFORE
  identity/dir/lock derive from this.name; node.requestedName keeps the
  original. Hosts read node.name for the resolved value.

Lets sym-mesh-channel delegate to the lib instead of its own copy.

Tests: 14 new (resolveAvailableName/pidIsAlive/lockHolderPid) — live
foreign holder via a spawned child, dead-PID, own-PID, multi-skip,
64-byte overflow, validation. config 41/41, node 17/17. The 4 ask/
llm-reason failures are pre-existing/env (claude CLI present → NO_PROVIDER
path), unrelated to this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant