Skip to content

Plannotator Inbox: the reply follows the person when two Pi or OpenCode 2 processes share a session (the lease) - #1766

Merged
backnotprop merged 2 commits into
mainfrom
fix/inbox-pi-lease-and-readmes
Oct 7, 2026
Merged

backnotprop merged 2 commits into
mainfrom
fix/inbox-pi-lease-and-readmes

Conversation

@backnotprop

@backnotprop backnotprop commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

The story

When two Pi processes had one session open, the idle one took the person's reply while they worked in the other. The PR 1763 review found this (item 1, P5). OpenCode 2 has the same gap with two servers on one database: the idle server queued the wake into a session that the other server was running. This PR ports the Claude Code mod's lease to the shared wake, so the process the person used last delivers. The claim still keeps every delivery to exactly one.

What changed

  • packages/shared/inbox/agent-link.ts: InboxWake holds a per-session lease at inbox/leases/<host>-<session>.json. It uses the mod's watcher.json rule unchanged: { owner, at, touchedAt }, a random owner id per wake, renewed or re-read every 5 s, stale after 20 s, released at dispose. The most recent touch wins, and a live holder keeps the lease against an older or equal touch. Only the holder polls and delivers. The wake re-reads the lease right before the mkdir claim. start() touches the lease, and touch() is public.
  • apps/pi-extension/inbox.ts: Pi touches the lease on an input whose source is not extension (the person typed in this process) and on a plannotator_inbox call. start covers a session start.
  • apps/opencode-plugin/inbox.ts: OpenCode 2 touches the lease on every watch (a tool call, or a run seen in this process) and on a user item entering the session.
  • AGENTS.md (= CLAUDE.md), the "Pi and OpenCode (step 7)" paragraph: the lease and the two new proofs.

Proofs (each fails on main and passes here)

  • apps/pi-extension/inbox.test.ts, "two Pi processes on one session: the reply goes to the one the person works in, once, and follows them back to the other". This runs the real Pi 0.84.4 in RPC mode against the compiled binary. A asks a question. B opens the same session with --session. The person works in B with the turn held at the model. 8 s after Send, A and B have 0 wakes. After B's turn ends, B gets the reply once (A 0, B 1, one claim). The person then asks again in A, and the second reply lands in A once. On main the same test fails at the 8 s check: A, the idle process, took the reply.
  • apps/opencode-plugin/inbox-v2.test.ts, "two OpenCode 2 servers on one session: the reply goes to the one the person works in, once". This runs two opencode2 serve 2.0.19 processes on one OpenCode home and database, each with the packed plugin installed. A asks. In B the agent calls plannotator_inbox and then its turn is held. 8 s after Send there are 0 wake rows in A and 0 wake requests at the model. After B's turn ends, B has 1 wake row and A has 0. On main, packed from main's code, the test fails at the 8 s check: A queued the wake while B was running the session.
  • Re-run here: all of apps/pi-extension/inbox.test.ts with PI_TEST_OLD_CLI set to Pi 0.79.1, plus tests/parity/vendor-parity.test.ts: 8 pass. All of apps/opencode-plugin/inbox-v2.test.ts: 3 pass. apps/opencode-plugin/inbox.test.ts (OpenCode 1): 1 pass.

Commands:

PLANNOTATOR_INBOX_TEST_BINARY=<compiled> PI_TEST_OLD_CLI=<pi 0.79.1 cli.js> PLANNOTATOR_DATA_DIR=$(mktemp -d) PLANNOTATOR_BROWSER=none bun test apps/pi-extension/inbox.test.ts tests/parity/vendor-parity.test.ts
OPENCODE2_BIN=<opencode2 2.0.19> OPENCODE_PLUGIN_TARBALL=<npm pack of apps/opencode-plugin> PLANNOTATOR_INBOX_TEST_BINARY=<compiled> PLANNOTATOR_DATA_DIR=$(mktemp -d) bun test apps/opencode-plugin/inbox-v2.test.ts apps/opencode-plugin/inbox.test.ts

Both already run in CI: inbox-pi.yml (path-gated on packages/shared/inbox/** and apps/pi-extension/**) and the "OpenCode 2 installed package" job in test.yml.

Gates

  • bun run typecheck: exit 0.
  • bun test apps/pi-extension apps/opencode-plugin: 820 pass, 1 fail. bun test packages apps/hook tests scripts under a temp data dir and HOME: 5807 pass, 1 fail. The two failures are plannotator-tool.test.ts "the CLI's startup error is the tool result" and annotate-cli.test.ts "occupied fixed port". Both come from this shell's FORCE_COLOR=3 (ANSI codes in the expected text). Both files pass with FORCE_COLOR unset. They are unrelated to this change.
  • bun pm pack --dry-run: apps/pi-extension ships inbox.ts, generated/inbox/agent-link.ts and generated/inbox/connection.ts, with no inbox.html. apps/opencode-plugin has 13 files, with no inbox.html.
  • bun run check:release-version: consistent (v0.28.7).
  • gitleaks over origin/main..HEAD: no leaks.

Deviations and what it leaves out

  1. OpenCode was in scope only if it had the same gap. It does, so the shared fix applies there too, with its own proof.
  2. A second OpenCode 2 process joins a session only when the session calls the tool there or a run event reaches the plugin. In both the 2.0.19 runs here, the plugin's v2.event.subscribe yielded no events, so the B process in the proof joins through its agent's tool call. If B only receives a typed prompt and never calls the tool, B has no wake and A still delivers. That is the existing step 7 rule for when a wake is made (deviation 8 of PR 1763), and this PR does not change it.
  3. Lease files: inbox/leases/ gets one small file per session, the same as inbox/claims/. Nothing prunes them, per the no-hardening rule. uninstall --purge removes inbox/.
  4. The package README paragraphs on plannotator_inbox moved to their own PR (docs/inbox-package-readmes), which merges on launch day with the un-hide PR. This PR's second commit removes them; the lease code is unchanged.
  5. Claude Code is untouched. The mod already has this lease.

…de 2 processes share a session (the lease), and the Inbox tool in the package READMEs
@backnotprop backnotprop changed the title Plannotator Inbox: the reply follows the person when two Pi or OpenCode 2 processes share a session (the lease); the Inbox tool in the package READMEs Plannotator Inbox: the reply follows the person when two Pi or OpenCode 2 processes share a session (the lease) Oct 7, 2026
@backnotprop
backnotprop merged commit a47fa27 into main Oct 7, 2026
31 checks 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.

1 participant