feat: session enumeration confirmation + reply-alias worker pool - #41
Merged
Merged
Conversation
…ures A reply-alias worker process can exit, or crash before registering, before its parent observes a "started" acknowledgement; this gives that failure its own machine-readable error code, matching the existing CcPeerError taxonomy.
AliasPool's Node adapter and worker exchange start/stop commands and started/message events over child_process.fork()'s IPC channel; this schema is the single source of truth for that shape, independent of the UDS wire protocol schemas.
Registering more than one natively-discoverable peer name per process is not possible: the registry is one file per real OS pid (~/.claude/sessions/<pid>.json) with a single optional name field each, so a process publishing a second name overwrites its own entry rather than adding to it. Each reply alias therefore needs its own real, distinct OS process. alias-worker.ts is that process's entry point: it registers a standalone CcPeer under a given name and relays every inbound reply back over its fork()'s own IPC channel. ForkedAliasProcess is the Node adapter that forks one, satisfying the new AliasProcess port. alias-worker.ts is deliberately its own dedicated tsdown entry (not left to be pulled in transitively), so it is emitted as a real, directly forkable file at a predictable path rather than folded into a shared internal chunk whose location is a bundler implementation detail.
Lazily materialises one ForkedAliasProcess per mesh correspondent name on ensure(), dedup'd against concurrent calls for the same name, and relays each alias's own "message" and "exit" events with the correspondent name attached. retire() waits for an in-flight ensure() of the same name to settle before stopping it, so a retire issued while an alias is still starting still stops it once (and if) it becomes active.
Adds a dedicated tsdown entry and package.json subpath export for alias-pool.ts, mirroring the existing pattern for the "." entry, rather than re-exporting it through cc-peer.ts (barrel re-exports are banned by this repo's own eslint config). Also excludes alias-worker.ts from the coverage gate, matching src/bin/** and sea-entry.ts: it is process lifecycle glue exercised by a real fork in an integration test, not by in-process unit coverage.
Records the empirical finding behind AliasPool: session enumeration is already fully native (the roster builder reads every registry file on disk, not just cc-peer's own), while reply aliases genuinely are not, because the registry is one file per real pid with a single name each. README documents cc-peer/alias-pool's own usage.
Extracts the worker-path extension derivation into workerExtensionFor(), a pure function both branches can be exercised directly rather than only through this module's own import.meta.url, which in dev/test always ends in .ts and so could only ever reach the .mjs branch. Also fixes the "worker fails to start" integration test: pointing socketDir directly at a plain file (not a directory) forces mkdir() to throw on macOS and Linux, but recursive mkdir() against an already-existing path does not appear to verify it is actually a directory on Windows, so the worker started successfully there instead of failing. Nesting socketDir one level inside the broken file forces mkdir() to create a genuinely new directory entry inside a file, which has no valid resolution on any platform.
…cketDir CcPeer.start() skips its socketDir mkdir entirely on Windows (a named pipe has no filesystem directory of its own), so corrupting socketDir can never force a failure there regardless of nesting, confirmed by this test still failing on both Windows CI runners after the first nesting fix. keys.writeForSocket() and registry.write() both mkdir into sessionsDir(homeDir) unconditionally on every platform, so corrupting homeDir instead reaches a real, unconditional mkdir() everywhere.
Mearman
marked this pull request as ready for review
September 17, 2026 15:05
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #40
Implements the two capabilities the agent-comms cross-machine mesh epic's default cc-peer front needs, gated on what Claude Code's local peer protocol actually supports (checked against source, not assumed):
CcPeer.roster()/GET /sessions(backed byFsRegistryStore.list(), which reads every~/.claude/sessions/*.jsonfile, not just cc-peer's own). No new capability needed here; this PR documents the finding.~/.claude/sessions/<pid>.json) with a single optionalnamefield each, so a process can only ever publish one discoverable identity at a time (an existing integration test already demonstrates this: two peers sharing a pid leave only the last-registered name visible). NativeSendMessage(name=X)resolution requires a real, live, distinctly-pid'd registry entry per name. This PR addsAliasPool, a minimal mechanism that lazily spawns one lightweight child process per correspondent alias (each running the existingCcPeerclass under its own real pid), relays inbound replies back to the parent, and retires aliases on demand.Work in progress; pushing early per the stacked-PR convention. Will flip to ready once the full slice (pool + Node adapter + worker + docs) lands and CI is green.