Skip to content

feat(scripts): --peer runs two peered live-test servers - #555

Merged
Jacksondr5 merged 7 commits into
j5/mainfrom
j5code/live-test-peer
Oct 12, 2026
Merged

Jacksondr5 merged 7 commits into
j5/mainfrom
j5code/live-test-peer

Conversation

@Jacksondr5

@Jacksondr5 Jacksondr5 commented Oct 12, 2026 •

Copy link
Copy Markdown
Owner

Problem

Peering and cross-server messaging had almost no live coverage, because standing up two servers and peering them took a hand-built rig each time. Part of #546 (stage 3, second of five stacked PRs; based on #554).

What changed

A start or rebuild takes --peer, or --peer --poll:

node scripts/j5/setup-agent-live-test.ts --peer
node scripts/j5/setup-agent-live-test.ts rebuild --peer --poll
  • Two homes. The generator runs twice: home/ and test-repo/ for Local, remote/home/ and remote/test-repo/ for Remote, all under .j5code/live-test/. Remote's paths get the same containment checks as Local's on every command.
  • Two servers. Local runs the full dev stack on the worktree's derived ports. Remote runs the server only (dev:server), on its own port: it gets a dev instance named after the worktree, so it does not derive Local's ports and two worktrees do not collide. They are named with J5CODE_SERVER_NAME. Remote allows Local's web origin (T3CODE_DEV_ALLOWED_ORIGINS) so the browser can reach it.
  • Peering. The script runs the server's own a2a peer credential and peer add against each home, the steps scripts/j5/pr-env.sh --peer uses, with its retry (five attempts, three seconds apart) because a server that is seconds old can miss the first hello. With --poll, only Remote holds a credential: Local stores its messages and Remote polls for them. Peering is repeated on every start; it rotates the credential and the record, so it leaves one clean pair.
  • Every command handles both. Each server has its own process record and is proved by its leader's pid, start time and cwd, exactly as before. stop classifies both before signalling either. status prints the full block only when both are running.
  • Separate homes. Before anything is started, Local's and Remote's homes, state directories and repositories must resolve to different places; two servers on one database would corrupt it.
  • One time budget. The builds, both server waits and the peering attempts share one 110-second budget from the moment a start is asked for, so the command returns inside a two-minute shell call. A server that is merely slow is left running and reported.
  • Peering is claimed only from evidence. The output says the two are peered when each server's own peer list names the other with no error against it. Two listening ports say nothing about it. Otherwise it says "NOT peered yet" and gives the start command that finishes the job; peering runs on every start, so running that start again is the repair. A failed attempt reports what the peer command said.
  • A note for the browser. A rebuild gives both servers new ids and the browser keeps the old Remote entry, so the block says to remove an earlier Remote first. Found by the tester.
  • Output. Both servers with port, home and log; Local's pairing URL; and a link that adds Remote to a browser already paired with Local, in the form pr-env --peer: add Remote to the browser with a ready link, and mint scoped pairing tokens without a restart #257 describes (<local web>/pair?host=<remote origin>&label=Remote#token=<a token minted for Remote>).

A loopback server offers no peer addresses in the UI. That does not matter here, because peering is done from the command line.

Live testing

On 2026-10-11, from this branch's worktree, through the Browser panel.

Exercised:

  • rebuild --peer: 78 s for two builds, two starts and peering. Both servers report their names; each lists the other as "sends directly both ways".

  • Paired a clean incognito tab with Local, then opened the printed add-Remote link: Settings > Connections shows Remote "Connected" under Environments and under Peer servers.

  • An agent on Local reached an agent on Remote. A Claude Haiku 4.5 thread on Local called j5_list_participants and reported 40 participants across two servers, with Remote's marked "Remote". It then sent "hello from Local, reply with the word PONG" to Remote's "Replay fixture: multi_turn (Codex)". Remote's event log shows the message arriving at 03:35:39 with the envelope "... on Local", a run on gpt-6-luna, and the reply "PONG" at 03:35:46.

  • status, restart (28 s, re-peers), stop for both servers; both ports free afterwards.

  • --peer --poll on a --peer build is refused with the two commands. rebuild --peer --poll: Local lists Remote as "polls this server for its messages", online; Remote lists Local as "this server polls it".

  • The default mode still builds, starts and stops after the restructure.

  • A message in poll mode. On a --peer --poll build, a GPT-6-Luna thread on Local sent "poll mode hello from Local, reply with the word POLLPONG" to Remote's "multi_turn (Codex)". Remote's event log shows it arriving at 04:20:17 with the "on Local" envelope and the reply "POLLPONG" at 04:20:22 on gpt-6-luna.

  • I removed Remote's peer record by hand while both servers ran: status said "NOT peered yet" and named the start command; running it re-peered them and status said peered again.

Not exercised live:

  • A message from Remote to Local started by a person; the Remote-to-Local reply path was not separately checked.
  • One server crashing while the other runs.

Found while testing, not caused by this PR:

  • Asked in plain language to "list the other agents you can message", the Haiku agent used Claude Code's own ListAgents tool, which returned real Claude sessions on this machine, not the test servers' participants. It messaged none of them. A test agent can see, and could message, real sessions that way. It needed the J5 tool named before it used j5_list_participants.
  • Two thread ids are the same on both servers: the two Codex subagent child threads, whose ids come from the recorded transcript. Every other id is random per build.

Checklist

  • One concern: the description has no "also"
  • Tests cover the changed behavior (backend changes ship with focused tests)
  • UI changes: before/after screenshots above, and a video for motion or interaction (no UI change)
  • Upstream-owned files: each one is recorded in FORK.md (case text and file-table row) in this PR (none edited)
  • Upstream product: any change to what upstream's product does has a human decision linked above and a register entry in docs/j5/product/upstream.md (none)
  • Surfaces: entry points, clients, providers, contracts, reverse states, connection modes (see AGENTS.md). Local and loopback only; stop and rebuild are the way out. Claude sent and Codex received.
  • Docs: definitions under docs/j5/product/ and user docs rewritten where this changes them (the skill text is in docs(j5): agents live-test with one setup script, the Browser panel and a testing skill #549)

Tests: vp test run scripts/j5/setup-agent-live-test.test.ts (29 pass): flag parsing for both peer modes and the combinations that are refused, the peer credential and peer add arguments for both directions and for poll mode, the add-Remote link, and the containment check on Remote's home. Typecheck and lint are clean on the touched files.

Built with Claude Opus 5.5 in Claude Code.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
j5-code Ready Ready Preview Oct 12, 2026 4:59am UTC

Request Review

@Jacksondr5
Jacksondr5 force-pushed the j5code/live-test-peer branch from 4a6b2e7 to 32130b1 Compare October 12, 2026 04:05
@Jacksondr5
Jacksondr5 force-pushed the j5code/live-test-peer branch from 32130b1 to 0055f90 Compare October 12, 2026 04:06
@Jacksondr5
Jacksondr5 force-pushed the j5code/live-test-peer branch from 0055f90 to d82f7db Compare October 12, 2026 04:09
Jacksondr5 added a commit that referenced this pull request Oct 12, 2026
…nd reports peering honestly

Review fixes for #555:
- Local's and Remote's homes, and their state directories, must resolve to
  different places before anything is started.
- One time budget covers the builds, both server waits and the peering
  attempts, so a start returns inside a two-minute shell call.
- The output says the two are peered only when peering succeeded on the start
  they are running from, and says how to redo it when it did not.
- A failed peering attempt reports what the peer command said.

Part of #546.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from j5code/live-test-fresh to j5/main October 12, 2026 04:20
Jacksondr5 and others added 4 commits October 12, 2026 00:45
A start or rebuild with --peer builds two homes, each with the test project,
and runs two dev servers named Local and Remote. Remote is server-only on its
own port. The script peers them through the server's own peer commands, both
ways, or with --poll so that Remote polls Local. It prints both servers and a
link that adds Remote to a browser already paired with Local. Every command
acts on both servers, each proved by its leader's identity as before.

Part of #546.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd reports peering honestly

Review fixes for #555:
- Local's and Remote's homes, and their state directories, must resolve to
  different places before anything is started.
- One time budget covers the builds, both server waits and the peering
  attempts, so a start returns inside a two-minute shell call.
- The output says the two are peered only when peering succeeded on the start
  they are running from, and says how to redo it when it did not.
- A failed peering attempt reports what the peer command said.

Part of #546.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The output says the two are peered when each server's own peer list names the
other, and otherwise says they are not peered yet and gives the start command
that finishes the job. Remote's repository must also be a different directory
from Local's.

Part of #546.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the browser

A rebuild gives both servers new ids, and the browser keeps the old Remote
entry, so adding the new one made a duplicate with a mismatch error.

Part of #546.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The environment-id requests and the peer commands are given the time that is
left, and the deadline is checked before each one, so a server that accepts
and never answers cannot hold the command past its budget. The budget is 100
seconds, leaving room to mint the pairing URLs afterwards, and a start that
has just peered the servers does not ask them again before printing.

Part of #546.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The refusal said both servers were running, which is wrong when peering
failed because one of them had just gone.

Part of #546.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A server that accepts and never answers could hang status in peer mode. A
list that does not arrive within fifteen seconds counts as no evidence of
peering.

Part of #546.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Jacksondr5
Jacksondr5 marked this pull request as ready for review October 12, 2026 05:10
@Jacksondr5
Jacksondr5 merged commit a5e2e7d into j5/main Oct 12, 2026
33 checks passed
@Jacksondr5
Jacksondr5 deleted the j5code/live-test-peer branch October 12, 2026 05:10

This branch was successfully deployed

1 active deployment
Preview — 6b9454f9 Deployed Oct 12, 2026 by vercel[bot]
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