Skip to content

feat(enter): switch existing worktrees in process - #25

Merged
neilwashere merged 5 commits into
mainfrom
feat/in-process-enter
Aug 23, 2026
Merged

feat(enter): switch existing worktrees in process#25
neilwashere merged 5 commits into
mainfrom
feat/in-process-enter

Conversation

@neilwashere

Copy link
Copy Markdown
Contributor

Summary

  • replace /worktree enter's terminal-multiplexer relaunch with Pi's in-process switchSession API
  • persist a visible pi-worktree-transition orientation in the target session before teardown, so the replacement cannot arrive rudderless
  • carry unflushed and zero-entry sessions from their in-memory active branch; retain the full tree with forkFrom when the session file is authoritative
  • consume relaunch PI_WT_HANDOFF payloads exactly once and use ctx.cwd, never process.cwd(), as the session location
  • keep all live replacement tests inside a disposable Docker container and gate releases on that CI job

Closes#19.
Fixes#24.
Part of #17.

Why the container boundary exists

The mechanism probe proved that Pi rebuilds cwd-bound tools and resources correctly, but two early host integration attempts reached an unoriented temp-worktree session and had to be manually abandoned. The missing protocol was the handoff: relaunches deliver it through a new process environment; an in-process replacement has no such boundary.

The implementation now writes orientation into the target session before switching. The normal host suite contains no switchSession call. test/container/enter-switch.e2e.ts refuses to run unless both /.dockerenv and PI_WORKTREE_CONTAINER_TEST=1 are present.

Behaviour

  1. Resolve the existing linked worktree and provisioning status as before.
  2. Snapshot the active session branch.
  3. Build a target session at the worktree cwd:
    • flushed, matching source file: SessionManager.forkFrom carries the full tree;
    • unflushed or stale persisted leaf: write the active in-memory branch;
    • no entries: write a valid empty target.
  4. Append one visible custom orientation as the target leaf.
  5. Verify target cwd, source leaf and orientation persistence.
  6. Call ctx.switchSession and return without touching the stale source context.

Preparation failure or cancellation stays put and leaves the worktree available. A replacement error is allowed to propagate to Pi; relaunching after teardown would use a stale command context.

Verification

npm run check
typecheck: pass
biome: pass
normal suite: 11 files, all pass
switch-plan: 12 cases
npm run test:container-enter
container enter-switch e2e: OK

The disposable proof loads the real extension into a real runtime, invokes the registered /worktree enter feat/x command with no multiplexer present, and verifies:

  • runtime, services and session-manager cwd all change to the linked worktree;
  • exactly one transition orientation is visible and available to the next model context;
  • idle enter adds no synthetic user or assistant turn;
  • a pending predecessor handoff does not reach the replacement;
  • a relaunch handoff is consumed once and remains gone after an actual extension reload.

Residual scope

This changes only the human slash-command enter path. Model-callable create/enter remains on the existing transition machinery and is tracked by #20. Create and dispose still relaunch; their migration is tracked separately in #21 and #22.

pi can rebuild a live session around a new working directory without the
process restarting: `switchSession` recreates the runtime with the cwd
recorded in the target session file, which rebuilds the built-in tools from
that cwd and re-resolves settings, extensions and context files against it.
This extension relaunches pi through a terminal multiplexer to do the same
thing.
Records the mechanism, the evidence, and the two constraints that shape any
use of it. A session file is not written until the session holds an assistant
message, so `SessionManager.forkFrom` refuses a session whose turn is still in
flight and the in-memory entries have to carry it. And `process.cwd()` never
moves, so nothing may treat it as the session's location once a switch is
possible.
Verification that drives a real runtime is described in the findings rather
than run by the suite: standing a runtime up and switching it is the one
operation here that can relocate a session, which does not belong in a test
that runs on a developer's machine.
Refs #18
Replace the slash-command enter relaunch with Pi's session-replacement API.
The target session is complete before teardown: it preserves the active
conversation branch and appends a visible custom orientation that participates
in the next model context without triggering a model turn.
Build unflushed and zero-entry sessions from in-memory entries because Pi does
not write a session file until it has an assistant message. A flushed session
still uses forkFrom so the full tree carries across. Verify target cwd, active
leaf and orientation before attempting replacement.
Consume relaunch handoffs exactly once and use the replacement context's cwd in
the caveat. A successful in-process replacement also retires an unconsumed
predecessor handoff; cancellation leaves it available to the source session.
Do not relaunch after a replacement error because Pi tears the old runtime down
before constructing the new one, making its captured context unsafe.
Live replacement is excluded from the normal host test suite. A dedicated CI
job builds a disposable Docker image, loads the real extension into a real
runtime, invokes /worktree enter and proves cwd/resource rebinding, persisted
orientation, next-turn context, no synthetic turn and one-shot handoff use.
Closes#19Fixes#24
Importing SessionManager at runtime exposes the actual compatibility floor of
the pi peer dependency. pi-coding-agent 0.83.0 and its undici 8.5.0 dependency
both require Node >=22.19.0; the previous >=18 package claim and Node 20 CI
seat passed only while every pi import was erased as a type.
Declare the real floor and replace the false Node 20 matrix seat with Node 22.
Refs #19
Comment threadextensions/worktree-switch.ts Outdated
Comment threadextensions/worktree-switch.ts
Comment threadextensions/worktree.ts
Comment threadextensions/worktree.ts
Comment threadextensions/worktree-switch.ts Outdated
Carry every in-memory session entry when the persisted leaf is stale, then
attach orientation to the selected leaf or root. This preserves sibling
branches and prevents a reset leaf from being mistaken for an empty session.
Let Pi derive the worktree's cwd-specific default session directory while
retaining explicitly configured stores. Delete prepared target files when a
switch is cancelled or preparation fails, and surface cleanup failures.
Persist the complete transition handoff in the target orientation. The fresh
replacement verifies registration, provisioning and receipt identity during
session_start, records successful verification, and emits a durable caveat for
partial or mismatched arrivals. Reloads restore that result without repeating
the check.
Treat --worktree as startup-only so command-line flag values reapplied by Pi do
not bounce a resumed replacement back to its original checkout.
Expand the disposable runtime proof to use Pi's real rebind lifecycle and cover
default session relocation, cancellation cleanup, retained CLI flags and a
receipt mutation between planning and successor verification.
Refs #19Fixes#24
@neilwashere
neilwashere merged commit 6f1f3ab into mainAug 23, 2026
4 checks passed
@neilwashere
neilwashere deleted the feat/in-process-enter branch August 23, 2026 17:02
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 1.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(handoff): consume each transition handoff exactly once feat(enter): switch the session in-process instead of relaunching

1 participant

@neilwashere