Skip to content

Latest commit

 

History

History
1208 lines (1021 loc) · 112 KB

PARITY.md

File metadata and controls

1208 lines (1021 loc) · 112 KB

Python compatibility and npm migration

Public 0.4.0 checkpoint — 2026-10-18 KST

Version 0.4.0 is public from source dde3923efe116f87f38b1639b29b889d2b832e44. The dated verification records identical reviewed/Actions/staged/public bytes, signature/provenance checks and isolated installation/removal. The release includes the approved limited macOS Codex observation and explicit operator-confirmed receipt scope below; #130–#132 remain separate platform/collector extensions. Published skill 0.2.1 remains the installation pin; runtime publication is not companion-skill publication.

Current comparison — Python 1.1.0 / TypeScript 0.4.0

Updated 2026-10-18 KST. TypeScript 0.4.0 uses published Python 1.1.0, commit 8ca77cf, as the ordinary list/discovery comparison baseline. JSON reports referenceVersion: "1.1.0"; this identifies a comparison source, not complete runtime parity. The source reports VERSION=0.4.0; publication evidence belongs to the dated validation and release records. This source does not change immutable public npm artifacts or their historical comparison markers.

  • The exact-release compatibility-v1.json and compatibility document are byte-identical to the historical Python 1.0.2 versions. The existing conformance.test.ts and discovery.test.ts selection passed 21 tests on macOS arm64 / Node 24.16.0 / Python 3.15.0 against an isolated export of the exact 1.1.0 source. This is ordinary list/discovery and synthetic refusal evidence, not full send/handoff/SSH interoperability or live ACK evidence.
  • python-reference.pin.json records the exact ordinary source, comparison scope and selected source-file hashes; the tests check those hashes before using the oracle. CI and the publishing workflow use the same immutable source. This test-only manifest is not an npm runtime dependency or an installed-package authenticity check.
  • Python 1.1.0 source adds bounded Claude registry reads and fail-closed Codex SQLite-storage checks, along with revised SSH evidence handling and opt-in target-generation/handoff behavior. TS retains its own error codes, stricter selected-storage parser and distinct native result shapes; it does not copy Python output mechanically or weaken its existing guards. These differences were inspected statically; the 21 tests are not coverage of every new path.
  • Python 1.1.0 has an experimental local Claude receipt-only ACK path. This does not establish automated ACK, SSH handoff or Windows handoff support in the scoped TS 0.4.0 implementation. Receipt bootstrap, original generation, body-free native observation, cleanup and platform qualification remain separate gates. Neither a native queue result nor turn completion establishes ACK or safe retry; no end-to-end consumption/exactly-once guarantee is introduced.

The frozen handoff design and its historical Python 1.0.3 design-review baseline remain separate from this ordinary runtime comparison. Exact frozen bytes and hashes are preserved. See the published design checkpoint and VALIDATION.md.

Historical 0.3.x comparison context

The retained tables and dated migration sections below describe their original Python 1.0.2/1.0.3 comparisons and TS 0.3.x source snapshots. Their historical pins and evidence are not rewritten as new-release conformance claims.

Updated 2026-10-14 KST. Python reference: 1.0.2, 47c2371. This matrix describes the TS 0.3.6 source/package contract: the 0.2.0 unified listing, safe home selection, read-only doctor, text/help/positional input, Unicode 14.0.0 name matching and separately managed TS skill guidance (#16/#17/#18/#19/#25), the 0.2.1 reliability and hardening changes, and the 0.3.0 remote usability and maintenance changes with their known limitations, plus the bounded ordinary SSH correction and finite skill guard. At the 2026-10-09 KST preparation checkpoint, public npm latest was 0.3.2 and preview was 0.1.0-preview.1. Historical 0.1.0 differences are called out explicitly. Public availability and tags must be checked in the registry; this document does not assert a publication outcome. SSH preflight enforces the exact same TS version banner. For builds from source between releases, operators should also align reviewed commits; the wire does not verify a source commit. referenceVersion: "1.0.2" identifies the comparison baseline, not complete parity. See VALIDATION.md for dated evidence.

0.3.6 documentation patch

This source/package carries the published skill 0.2.1 guidance adopted in PR #146, merge source 602cfc09b31ce8aad23e1dd9382a2448a61143f9. All four guides use immutable skill source 0f494cabd05901c03161dc91792dcf89f8e96631; skill metadata/full-version and Python reference remain unchanged. TS 0.3.6 adds only its explicitly reviewed finite row with the same skill 0.1.0/0.2.0/0.2.1 acceptance as public 0.3.5. Historical rows stay unchanged and unreviewed future versions remain incompatible.

The patch updates package/banner/current examples and the stable release baseline to previous public 0.3.5. SSH still requires exact matching TS versions; 0.3.5 and 0.3.6 refuse each other. No native delivery behavior, platform qualification, automatic ACK/wait, retry, installation manager, skill publication or 0.4 experiment is added. Public availability must be checked in the registry; this source description is not publication evidence. Dated preparation and publication checks belong in VALIDATION.md and release notes.

Published skill 0.2.1 adoption — 2026-10-14 KST

The four guides now pin the independently published TS skill 0.2.1 release to immutable source 0f494cabd05901c03161dc91792dcf89f8e96631. Annotated tag object 4e7df7d8f99863e86b477eacce958033d7280246 peels to that source. The complete SKILL.md is 13,996 raw UTF-8 bytes, SHA-256 8c3520f95e0d6807303be96bfa06df69887ddaf76be05f1905505d0a956820c3. The tag, source and file identities were independently rechecked after publication; the reviewed PR head dc303b513d9da862f2ce21d648e168fdd847ae95 and published source have identical SKILL bytes.

Public npm TS 0.3.5, source a2057940ba19056fcaaa5551a7820f7414bd3bfa, accepts the exact profile: skill 0.2.1, implementation typescript, minimum 0.1.0, documented feature reference 0.3.2, policy probe-help. It retains legacy skill 0.1.0 and 0.2.0 acceptance. Public 0.3.2/0.3.3/0.3.4 reject skill 0.2.1; future runtime/profile combinations are not inferred from version ranges. The published 0.3.5 tarball's validator accepted these exact complete bytes with compiled default, explicit undefined, explicit 0.3.5 and outer inspectSkill; supplied historical/future identities were rejected. These helper checks are not executions of historical CLIs.

The published bytes are retained as an offline guidance fixture, distinct from the earlier candidate fixture. compatible / skill_contract_compatible / metadata_only is a bounded metadata result, not content authentication, send permission, delivery or ACK. Runtime and skill installation/update remain separate and manager-owned; no postinstall, global installation or implicit update was performed. Python reference 1.0.2 and Python skill pins remain unchanged; unshipped 0.4 handoff/observer/ACK and experiments are excluded. Earlier candidate, pending-publication and 0.2.0 pin records below are dated history, not the current installation guidance.

0.3.5 candidate preparation — 2026-10-13 KST

Historical preparation record; the current published skill and installation pin are recorded in the 2026-10-14 checkpoint.

This preparation is based on public 0.3.4 source afed4a5a8945012faf1b23d52cffecb70470a5b3 plus unmerged Draft #140, checkpoint b917769047db41dedcb4aa1367a1b89915beb000. Candidate compiled VERSION=0.3.5 preserves exact legacy skill 0.1.0 and published skill 0.2.0 profiles and proposes exactly skill 0.2.1 / minimum 0.1.0 / full 0.3.2 / typescript / probe-help. Future versions remain incompatible; metadata-only compatibility is not instruction authenticity, CLI permission, delivery or ACK evidence.

Companion agreement on the exact profile and complete final skill bytes is pending. The initial inactive-session dry-run was refused without submission. After user activation, a stable-writer dry-run passed and one review request was queued under correlation STS-035-SKILL-PROFILE-REVIEW-20261013-A; consumption, review and agreement remain unconfirmed. #139 remains open for that agreement and separate rollout. The four guides retain published skill source 8482cf644507c682915e35c85aa3384c53864906 and SHA-256 00c6fc343fdbeb8f6dc1b1ee1b730f04cef71bbfb08205a7be3559202f1d8804; no successor pin or skill publication is approved. Python reference 1.0.2 remains unchanged. Exact-version SSH requires both endpoints at 0.3.5 for candidate tests; public 0.3.4 refuses that banner. Draft #106–#109, #82 and #142 are excluded. Historical 0.3.4 preparation below remains dated evidence, not the current candidate description.

Exact successor candidate checkpoint — 2026-10-13 KST

The earlier pending agreement statements above describe their initial preparation checkpoints. Companion Draft #35, source 9db5420b45568dd08fc43d406dbf9ad1952f7891, now provides the complete 13,930-byte skill 0.2.1 candidate, SHA-256 96d5a0d049f8a4d193703af3583b198a91079d44ae373de34e94a54e2296a4d6. Independent full-content/hash comparison and compiled guard/default/undefined/outer-inspection tests support candidate-specific design/content acceptance; see the exact review. This is a test fixture, not a published skill, approved installation pin or final joint publication freeze. Final skill wording/bytes after actual guard publication require another exact review and separate release approval. Published pin8482cf6 remains unchanged. Local scoped tests passed24/24; exact source/artifact/current-head CI and review status are recorded in PR #144. No instruction execution, live delivery/ACK, merge, staging or publication is claimed.

0.3.4 patch preparation — 2026-10-12 KST

PR #134 corrects Windows-destination PowerShell forwarding of native exit codes for version preflight and wire requests. A missing native exit fails with 1; complete validated abnormal responses retain independent submission facts. Strict response limits, exact-version SSH, host-key checks and no automatic resend remain unchanged. This is not delivery or ACK evidence.

The four guides pin independently published TS skill 0.2.0 source 8482cf644507c682915e35c85aa3384c53864906. The 0.3.4 candidate accepts only the same two exact reviewed profiles as public 0.3.3; 0.3.5 and unreviewed versions remain incompatible. Python reference 1.0.2 is unchanged. Draft 0.4 handoff/app-server/observation and Side Session are excluded.

Feature and command matrix

Implemented means the stated bounded behavior exists. Partial means a related command exists with the differences shown. Planned means no usable TS equivalent yet. Deliberately different identifies a maintained boundary, not a missing implementation. Issue links describe work, not available features.

Surface Python 1.0.2 TS source (published differences noted) Status / follow-up
Claude discovery and inbox submission Local POSIX socket / Windows named pipe; PID or name; discovery rechecked before submission Same transport families and recheck; Windows process start identity and authenticated pipe checks Implemented; native fixtures are not live ACKs
list selection Unified agent list; optional agent; bounded multiple Codex homes; partial discovery diagnostics Source: combined Claude/Codex by default, optional agent/home filters, bounded multi-home inventory and partial diagnostics. Published 0.1.0 still requires an agent and explicit Codex home Implemented in source for #16, 0.2.0; no Antigravity adapter
Codex active queue Saved UUID, unique live writer, real lock/owner checks, pre-submit revalidation Source: same protections, full UUID, implicit unique-live selection or explicit home; all known candidates checked Implemented in source for #17, 0.2.0
Implicit home / inactive queue Select unique live home; explicit inactive opt-in under separate guards Source: unique live selection; explicit saved inactive home with opt-in and verified inactive candidates. Public 0.1.0 has neither Implemented in source for #17, 0.2.0; no wake/resume
doctor Readiness diagnostics; return-route check is opt-in Source: read-only local/SSH metadata, per-agent/home results, capabilities and bounded TS skill compatibility. Published 0.1.0 has no doctor Implemented in source for #18, 0.2.0 base diagnostics; opt-in doctor --check-return-route added in 0.3.0 (#21)
CLI input, help and names Human text or JSON; positional or named/stdin message; Unicode casefold matching 0.2.0: explicit JSON/text format, positional/named/stdin body, command help, exact Unicode 14.0.0 casefold with PID for ambiguity Implemented; #19, 0.2.0. Published 0.1.0 help still says “preview”; source help now describes unified list
From / Reply-To Caller detection, automatic return-route metadata, structured URI resolution 0.2.1 and earlier: From uses Codex environment UUID only; explicit --reply-address; local/SSH Reply-To URI parsing with conflict checks. 0.3.0: Claude/Codex sender detection, generated routes, --reply-to, same-user self normalization, Tailscale routing hint and doctor --check-return-route (details) Implemented for #21; no Antigravity identity, no executable Reply: line, 0.3.0
SSH Python source streamed to POSIX remote Python; multiple hosts/options/host metadata Same-version installed TS CLI required; strict preflight; JSON stdin; explicit Windows PowerShell path. 0.3.0: ordered repeated --host, allowlisted --ssh-opt, typed single-hop --ssh-jump (POSIX clients), IPv6 literals and sshUser/sshUserSource (details) Partial; #20 (no arbitrary options or multi-hop; --ssh-jump refused on Windows clients; Tailscale routing hint in #21), #23 deployment/version design (proposed ADR), 0.3.0
Updates Cached advisory client/skill notices (on by default); GitHub release check; standalone self-update and remote push; package-manager guidance 0.3.0: update --check reads npm dist-tags (latest/preview) with installation-manager guidance; no self-update or remote update; cached client notices are opt-in (SESSION_PEER_UPDATE_NOTICE=1), honor --no-update-notice/SESSION_PEER_NO_UPDATE_NOTICE; no skill notices. 0.2.1 only accepts --no-update-notice as a no-op Partial for #22, 0.3.0; update boundaries
Reusable execution API Agent/transport capability adapters Package root exposes pure protocol helpers only; no general typed execution API Planned #24, Future — Agent integrations
Skill guidance/setup Companion session-peer skill targets Python Separate session-peer-ts companion PR and exact-commit setup; baseline/version/help gates Legacy metadata profile from #25; guidance refresh/guard coordination in #118
MCP Optional extra, restricted destinations; separate wake permission Unsupported Planned #26, Future — Agent integrations
Codex wake Explicit opt-in; platform/version gates; may run a model and alter history Unsupported; queued does not wake or prove consumption Planned #27, Future — Agent integrations
Antigravity Experimental registered bridge generation/inbox contract Unsupported Planned #28, Future — Agent integrations
Paired device client Optional crypto/device transport and receiver policy Unsupported Planned #29 design, #30–#32, Future — Paired devices and Relay
Relay server / hosted control service Separate infrastructure and deployment Not part of the TS CLI deliverable Deliberately different scope; #29 must define client boundaries before implementation
Wait / general ACK / exactly-once consumption No general CLI wait/ACK or exactly-once consumption guarantee No general CLI wait/ACK or exactly-once consumption guarantee Shared limit, not a TS parity gap; paired-device deduplication is not end-to-end consumption

Historical 0.2.0 source anchors and the then-pinned Python oracle: TS CLI, writer, send, protocol; Python parser, commands, Codex.

Environment and installation ownership

Setting / surface TS source behavior / migration consequence
CLAUDE_CONFIG_DIR, ANTHROPIC_CONFIG_DIR Claude config precedence: first nonempty value in that order, otherwise ~/.claude. No Codex home is required for Claude listing.
CODEX_HOME, default ~/.codex, macOS Orca account homes Source: bounded automatic Codex listing and writer safety inventory. Codex send selects a unique stable live home, or validates the explicit home against all competitors. Published 0.1.0 requires it for Codex list too. No recursive disk scan.
SESSION_PEER_CODEX_HOMES Source: JSON array of absolute or ~/ paths adds required listing homes and writer candidates. Invalid list configuration is rejected as a whole with other sources preserved; send keeps fail-closed inventory. Published 0.1.0 uses it only for writer checks.
CODEX_THREAD_ID, CODEX_SESSION_ID Valid UUID enables default From metadata; --no-from disables it. 0.3.0: two different values, or Claude evidence at the same time, give no identity.
CLAUDE_CODE_MESSAGING_SOCKET 0.3.0: must match exactly one live registered Claude session to give From: claude:NAME (PID when the name is not a unique printable target). Antigravity caller detection is not implemented.
SESSION_PEER_REPLY_HOST, CC_PEER_REPLY_HOST 0.3.0: return host for generated Reply-To and doctor --check-return-route, after --reply-to and before the tailnet address. Invalid values are refused (invalid_reply_host).
SESSION_PEER_TAILSCALE TS only, 0.3.0: off skips tailscale status --json, so every destination is ordinary SSH.
SESSION_PEER_UPDATE_NOTICE, SESSION_PEER_NO_UPDATE_NOTICE, SESSION_PEER_CACHE_DIR, SESSION_PEER_UPDATE_REGISTRY 0.3.0 (#22): opt-in cached notices, opt-out (wins), absolute cache directory override, and npm registry mirror for update checks (HTTPS, or loopback HTTP; no credentials). Python's XDG_CACHE_HOME update.json is not read; TS uses its own npm-update.json.
Reply/update/Relay settings from Python No blanket compatibility. Only the flags and environment reads above are implemented. Do not infer a route or permission from an environment variable or From header.
Runtime and native dependency TS needs Node 22.13+ within 22.x or Node 24.x, plus a matching native lock binary. Python is not a TS runtime fallback.
CLI ownership Both distributions install session-peer; npm does not remove Python/uv/pipx/venv installations. Select one path deliberately and update it through its owning manager.
Skills Install separately and explicitly; no package postinstall downloads a skill or invokes another package manager.

Source unified listing contract — #16 / 0.2.0

node dist/cli.js list --json combines Claude and saved Codex rows. Use --agent claude to skip all Codex inventory, or --agent codex to skip Claude. --codex-home PATH pins only Codex discovery (also in a combined list) and bypasses unrelated environment/Orca/configuration errors. It is inapplicable to --agent claude. A configured empty string is invalid JSON: unset SESSION_PEER_CODEX_HOMES or use [] to disable extra homes.

Automatic sources are visited in order: default ~/.codex, nonempty CODEX_HOME, immediate macOS Orca account homes, then SESSION_PEER_CODEX_HOMES. Canonical aliases merge source labels; UUIDs in distinct homes stay distinct. Every Codex row includes codexHome and stateDb; identity is host + canonical home + UUID. Rows sort by descending updatedAt, home and UUID using Unicode code-point ordering. --all independently includes archived records in every home and unreachable Claude records. Discovery success does not imply an active writer.

Per-home status is ok, absent or error; missing optional default/Orca DBs are absent. Missing argument/environment/configured homes are errors, even when they alias an optional source. Empty readable DBs are ok with zero rows. Aggregate Codex status is not_installed only when no DB is readable and there are no errors; that is exit 0. Any inventory/home/agent failure yields exit 1 while preserving other rows. Error codes are state_db_missing, state_db_not_regular, permission_denied, state_db_read_failed, home_resolution_failed, candidate_enumeration_failed, or invalid_home_configuration. Errors use fixed text, never raw exception bodies. Inventory errors appear in discovery.codex.errors; per-home errors in homes. Top-level codexHome appears only for exactly one candidate and no inventory errors; consumers should use each row's home.

The same listing and filters execute in the SSH destination's environment; nonzero partial responses retain rows and diagnostics. No recursive scan, inbox connection, writer selection or queue submission is part of listing. SQLite is read-only but its existing WAL bookkeeping caveat still applies. Listing availability never relaxes send's lock/owner/revalidation checks. The shared enumerators reject a set empty extra-home variable and require named homes for both list and send. Listing may return partial results; send refuses any unreadable inventory and never uses listing results as authorization. Pinned listing bypasses unrelated configuration, but pinned send checks all sources.

Source: discovery.ts, CLI, writer. Contracts: discovery.test.ts ports Python unified/multi-home cases; native Windows SSH/partial-envelope checks are in windows-contract.test.ts. The Python oracle runs in POSIX CI; deterministic fixtures and injected filesystem failures run on Windows too. Error injection is not a real Windows ACL test.

Bounded Claude registry reads — source patch #99

0.3.1 patch for #99: Claude list/send/sender discovery skips non-regular, symlinked or larger-than-1-MiB registry records while retaining healthy records. Descriptor checks, POSIX nonblocking open and bounded reads cover FIFO replacement races. This is not a deadline for network-filesystem I/O; doctor retains its separately bounded metadata contract. The patch is not in the immutable npm 0.3.0 archive.

JSON result compatibility

Do not parse human messages or assume an optional key exists. Python's schemaVersion: 1 and TS's schemaVersion: 1 are not identical field contracts. TS normal envelopes have schemaVersion, host, command, ok, version, referenceVersion; caught failures omit the two version fields. Exit codes are 0 (success), 1 (operation error), 2 (usage/refusal as classified by the command).

Field / path Python 1.0.2 TS (source differences noted)
list.sessions, discovery Unified/per-agent and per-home diagnostics; partial results can remain with ok:false Source: Claude rows then Codex rows; readable homes survive failed homes/agents with ok:false, exit 1. Published 0.1.0 has only selected-agent/single-home results
send.status, submitted Submission/wake/transport-specific details; do not assume a uniform error shape validated:false, posted:true (Claude), queued:true (Codex); caught refusal is refused:false, unknown is unknown:null
consumptionConfirmed Optional and transport-specific; never infer a missing value as false or confirmation Always false on send results, including refusal/unknown
target, chars, dryRun, codexHome Target and home details depend on agent; Codex target includes id/name, with separate agent Success/dry-run target has agent plus id or pid/name; chars counts enveloped code points; codexHome only on Codex success/dry-run
queueId Present if native queue stdout supplies an ID Source #17: optional bounded native confirmation ID on successful queue only; absent in public 0.1.0
codexHomeResolution Resolution diagnostics on Codex success and some refusals Source #17: sanitized resolution on success and failures after resolution begins; private owner fingerprints remain internal. Absent in public 0.1.0
remoteVersion Optional remote list metadata after version discovery (also update diagnostics); not universal on send Absent; exact TS version banner must pass preflight; mismatch is a refusal
replyRoute, addressResolution Optional metadata when route advertised / Reply-To destination resolved 0.2.1 and earlier: absent. 0.3.0: same names and values as Python for generated routes and URI destinations; an explicit --reply-address has no replyRoute
wake Optional, opt-in; failed activation may still have submitted:true and ok:false Absent; unsupported option
SSH metadata Requested/resolved host and SSH metadata; multi-host list may be an array Verified remote response adds host/sshHost; caught local SSH failure for one host reports the local host. 0.3.0 adds sshUser/sshUserSource and returns an ordered array for repeated --host (see #20 section); with a resolved Tailscale peer, host is its MagicDNS name and sshHost the requested alias (#21)
error, retryAllowed Error/detail fields vary by path; submission may already have happened Fixed error codes, caught failure has retryAllowed:false; native stderr/message body are not copied into errors
clientUpdate, skillUpdates Optional advisory notice metadata 0.3.0 (#22): client-local. Added by the invoking client to a flat (single-result) list/send/doctor JSON result, including a single --host result that failed or is unknown, when notices are opted in and a fresh npm cache shows a newer stable version. Never inside a repeated --host array, never on top-level parse/local caught failures, never produced by a --stdio-request receiver; a remote-supplied clientUpdate is dropped. It never changes status, exit code, consumption or retry decisions. skillUpdates absent

An absent submission field is not false; null is not false either. Check ok, command, status, exit code and presence separately. TS unknown means submission may have happened; never automatically resend. A Python wake error with submitted:true must not be retried as a fresh send. A missing reply or target exit alone does not establish consumption or failure. Observe ACK in the receiver TUI separately. Further #20 changes must test success, refused, partial and unknown output, including absent/null/false distinctions.

Output sources: TS CLI/send above; Python commands, Codex queue/wake, SSH errors.

Ordinary CLI migration notes — #117

This dated 0.3.x comparison excludes Relay/MCP and paired-device transports. It retained the Python 1.0.2 oracle and referenceVersion at that checkpoint; the current 0.4.0 candidate baseline is described above. A fixed-source review on 2026-10-09 used published Python 1.0.3 0d252550 and TS 0.3.2 f335f073 for migration guidance, not byte/shape parity or a runtime-reference repin.

  • Python's Claude adapter returns ok, target, chars and dryRun; ordinary Claude results can omit status, submitted and consumptionConfirmed. TS emits validated with submitted:false for dry-run and posted with submitted:true after inbox submission, always with consumptionConfirmed:false. Claude target.name may be null. Codex queue IDs are optional; none of these facts confirms ACK.
  • Missing Claude session directories yield an empty list and discovery status:ok; optional missing Codex homes yield absent, and no readable DB with no errors yields not_installed. These are discovery states, not proof that an agent executable or inbox is ready. Doctor completion is separate from ready, and neither grants send authorization.
  • Repeated SSH hosts produce ordered per-host results; an operational failure during per-host dispatch makes exit 1. Command-wide refusals before dispatch (for example duplicate hosts or an empty message) retain exit 2, including when rendered as an array. A single host stays flat. Presence, ok, status and exit must be interpreted separately. The shipped 0.3.2 SSH path can classify even complete stdout as unknown after exit 255 or timeout: #115 and #116 track the correction and strict response parsing. This document does not claim those fixes have shipped. Unknown never permits automatic resend or fallback.
  • TS supports manager-owned npm update checks, not Python's standalone self-update or remote maintenance. Client notices are off by default; opt-out wins over opt-in, but explicit update --check still requests the registry. Runtime, remote installation and skill maintenance remain separate.
  • TS normalizes a self SSH Reply-To only for an explicit matching OS user and a recognized exact local name, with no explicit SSH option. Conflicting Claude/Codex sender evidence omits identity instead of choosing Claude first; unusable Claude names fall back to PID. An explicit return-route check runs a reverse SSH no-op only for a usable non-local destination. A self route instead returns verified, transport:local, self_route_normalized without SSH; a missing host fails before a probe. verified therefore means local normalization or one successful SSH no-op, not receipt authentication, general reverse access or agent consumption.
  • Stronger TS storage, registry and output guards are intentional; migration does not relax them to reproduce Python behavior. General native ACK/wait, end-to-end deduplication and Side Session support are not public ordinary-CLI features in either compared release. Historical wake tests for native Codex 0.154.0 do not qualify a current native CLI.
  • The separately pinned TS companion uses published skill metadata 0.2.1. Its repository tag and the Python skill version are different version trains. Public TS 0.3.5 accepts this exact profile; public 0.3.2/0.3.3/0.3.4 reject it. The runtime-first, separately approved skill rollout in #118/#139 is recorded in the published checkpoint. A Draft content candidate is not an approved installation source; use the reviewed immutable release pin in the guides, not a moving branch.

Transport, size and failure boundaries

Boundary Python 1.0.2 TS (source differences noted)
Remote prerequisite POSIX-compatible remote shell and usable Python; source delivered over stdin. Native Windows destination is not this SSH path Installed same-version TS CLI + supported Node/native dependency. POSIX shell by default; explicit --remote-platform win32 uses PowerShell and a .cmd launcher. Provisioning and a separate compatibility contract are proposed, not implemented, in the remote deployment ADR (#23)
Message on SSH Base64 body in quoted remote argv; source on stdin Message in UTF-8 stdin JSON; PowerShell's encoded command contains launcher/flag, not the message
Remote body/command 90,000-character remote message cap; fully quoted UTF-8 SSH command capped at 131,071 bytes after base64/argument expansion Stdin reader capped at 4,100,000 bytes, including JSON framing/escaping. No unlimited stream; OS argv limits still apply when invoking native queue
General message At most 1,000,000 characters; Codex at most 32 KiB UTF-8 At most 1,000,000 Unicode code points; Codex at most 32,768 UTF-8 bytes; rejects blank/NUL
Envelope overhead From/Reply-To and encoding may reduce usable body budget Limits apply after From/Reply-To envelope creation; JSON escaping can exhaust wire budget sooner
Preflight and refusal Source transport and path-specific diagnostics, not exact installed-TS preflight Before message dispatch, version mismatch/auth/host trust/unreachable/timeout produce fixed refusal codes and submitted:false
After dispatch Respect submission metadata; failed wake/transport does not prove nothing was submitted Lost/malformed/unverified response or nonzero/timed-out native queue after spawn is unknown with submitted:null, no automatic retry

Sources: Python limits, SSH construction; TS CLI/send above. UTF-8 byte limits are not character counts. Local native Windows support, WSL/POSIX SSH and TS native Windows SSH are separate paths.

Migrating a workflow to npm

  1. Inspect command ownership. Run type -a session-peer (POSIX) or Get-Command session-peer -All (PowerShell), then session-peer --version. Record the Python interpreter/venv or owning uv/pipx/package manager before changing PATH. Do not delete another manager's files or overwrite its shim.
  2. Install the chosen version explicitly after checking registry availability. The public 0.3.2 record pins its source and artifact verified on 2026-10-07 KST. If 0.3.3 is unavailable in the registry, use a reviewed source build. Check npm view session-peer@0.3.3 version dist.integrity, then use npm install --global --ignore-scripts session-peer@0.3.3. Verify the resolved command/version again. An isolated npm prefix and explicit launcher path allow side-by-side evaluation. Install any desired skill separately using the exact reviewed commit, explicit agent and scope in the guide. The original companion session-peer skill targets Python; use distinct session-peer-ts guidance. Check implementation and supported help; 0.1.0 development builds historically shared their version string with the public baseline.
  3. Make selection and output explicit. Replace bare list with session-peer list --agent claude --json, or session-peer list --agent codex --codex-home /absolute/home --json. Use a full Codex UUID; preserve the discovered home when selecting a row. Unicode names use exact Unicode 14.0.0 casefold; use PID for collisions. Supply one positional body, --message, or UTF-8 stdin. Keep --json for machine consumers. 0.2.0 also supports combined listing and text output.
  4. Validate before submission. Use send ... --json --dry-run. Review the target/home, then remove --dry-run only for an intended submission. Check posted/queued separately from receiver ACK; do not retry unknown outcomes. Do not test migration against unrelated live sessions.
  5. Migrate each SSH endpoint deliberately. Install the exact same TS version remotely; Python's same-named CLI does not pass TS preflight. Set an absolute --remote-bin where PATH is ambiguous; for Windows add --remote-platform win32 and the .cmd path. Retain existing SSH host trust and authentication; a control socket is not new authorization. Account for the size limits above. In TS 0.3.0, repeat --host for ordered per-host results; the single-host result stays flat. TS 0.2.1 and earlier lacked multi-host aggregation.
  6. Keep unsupported workflows on an explicit Python path. Remote updates, MCP, wake, Antigravity and paired devices need their existing implementation until their issues are delivered. Doctor is available in 0.2.0; 0.1.0 lacked this command. Updating npm does not update Python, skills, external Relay infrastructure or a remote host automatically. To return to Python, select its recorded path or uninstall only the npm-owned installation.

Handoff v1 design candidate — #69

Design-freeze checkpoint: jointly approved, frozen DESIGN v0.2; runtime was unimplemented and unshipped at that checkpoint. The current source implementation and its limited 0.4.0 scope are described below. All four issues #69, #70, #71 and #72 remain in the single 0.4.0 milestone. Milestone assignment is not delivery or publication evidence.

The upstream candidate is Python Draft PR #261, commit 99d8ec3855d57da34a6fa789f3a375820a8fbe85: document and synthetic fixture. Exact copies are retained in test/fixtures/handoff-v1.md and test/fixtures/handoff-v1.json; handoff-v1.pin.json records their SHA-256 hashes, review status and separate runtime/design references. These test-only files are excluded from the npm package. The design-only checkpoint retained its then-current 51-file allowlist; the implementation adds its runtime modules explicitly.

The design review uses published Python 1.0.3 at 0d252550ab40c26d1ac4a19193df6ca2f84d830b as its compatibility baseline. The original review retained the then-current Python 1.0.2 ordinary comparison pin. The current candidate separately checks ordinary list/discovery against published Python 1.1.0; that does not change these frozen design bytes or claim identical Python/TS legacy outputs or implemented cross-runtime handoff parity. An approved design commit pins the shared contract fixture independently of runtime publication and the bounded ordinary comparison.

Proposed separation and TS implementation gates

Axis Candidate meaning / TS boundary
Submission Preserve native status, target, queueId, submitted and optional-field absence; an observer failure must not pass through a catch that erases known submission evidence.
Observation Opt-in metadata-only output for an exact native message ID/home/original generation. SQLite may evaluate body-bearing cells internally; this is not a blanket no-body-access promise. Queue deletion or turn completion is not receipt ACK.
Receipt ACK Receipt-only capability or explicitly recorded operator attestation; never generic reply parsing, a new model prompt, wake or permission to act.
Wait Separate immutable wait operation/results; a late receipt can add current evidence without changing the original timeout result.
Retry Native local/SSH replay remains forbidden. Sender fences do not implement receiver dedup or a universal exactly-once guarantee.
SSH A fully validated, requested explicit-wait result may preserve queued/true with ok:false/exit1. Unrequested handoff data cannot enable this exception; target validation is still required.

The candidate adds a nested handoff object only in opted-in mode. Its proposed init/prepare/status/wait/confirm/ack commands, epoch IDs, private collector, 30-second per-destination budget and 5-second cleanup reserve were not CLI capabilities at the design-freeze checkpoint. The source subset is described in the scoped implementation sections below. Budgets 1–5 seconds are syntactically valid in the proposal but refuse before effect. Source-streamed SSH without a compatible receipt handler remains ACK-unsupported under this candidate. No transcript/body-returning observer or fallback after a possible submission is authorized.

Amended candidate review and remaining gates

The amended exact candidate supersedes the first 605061 review snapshot. Its normative document and synthetic vectors now address the seven prior findings:

  1. Duplicate receipt queries require original capability-hash authentication, including the same-committed-receipt expiry exception; wrong tokens are denied.
  2. Receipt classification and wait-order facts commit atomically. Proven committed ACKs survive clock restart; unprovable manual confirmation refuses without rewriting history.
  3. Receipt evidence binds the original generation. Null generation cannot mint authority; copied original capability data is explicitly only token-possession assurance.
  4. Sender/collector outputs and own persistence exclude secrets, while intended recipient native storage may retain effect-input authority outside CLI control.
  5. Missing/corrupt ledger or unknown ID returns a closed handoffQuery error, without a fabricated epoch or native result. Known tombstones retain original context.
  6. Raw private input, compact public subtree and outer/fanout limits are defined separately; lexical constraints stay inside the new schema and native facts are preserved.
  7. Explicit-wait wire exceptions require the original request and validated native target/IDs/goal; returned metadata cannot opt a legacy request in or promote forged ACK.

The final candidate now aligns the three remaining fixture/profile rules:

  • Four original-request profiles preserve Python Claude's absent native fields, TS Claude's nullable name/optional agent and Codex's queued/true/false result, optional queue ID, native UUID normalization and canonical home/context.
  • A post-submission unsupported channel is a request-gated explicit-wait failure at exit 1, preserving known submission and any injection evidence.
  • Interrupting a pending explicit wait records stopped/stopped_by_operator and returns complete structured output with ok:false/exit130. A successful status query of that record returns exit 0; terminal waits remain immutable.

The shared candidate has 39 synthetic request-aware wire vectors for positive, forged, wrong-target/home/agent/profile, unsupported and interruption cases. The exact candidate has TS design-only approval after independent review; no additional normative change is requested for these reviewed findings. Joint design freeze is recorded for the exact source/hash tuple above. Python acceptance record confirms both sides' design-only approval; TS acceptance was recorded at 63473fb80936b0085e08039997ac39f7158a58dd. handoff-v1.pin.json now has jointFreezeApproved:true, runtimeImplemented:false and no open design-review gates. The byte-identical source document and synthetic fixture retain their historical proposal/draft labels; the external approval freezes DESIGN, not a shipped or implemented runtime protocol. No regeneration or runtime/reference repin occurred. Normative/schema/vector changes require a new exact joint review. Design-only approval does not establish receipt/bootstrap/runtime acceptance or authorize merge, implementation, live effects, operational changes or publication.

Python's exact-candidate documentation tests and CodeQL passed; its optional Control dependency audit and required release gate failed. That CI status is separate from this static design review. No bypass, dependency repair or claim of a fully passing upstream PR is made by this historical review. At that checkpoint, runtime producer/collector/bootstrap, platform cleanup, metadata-only observation and live acceptance were future gates; current implementation evidence appears below.

handoff-contract.test.ts checks exact pins, synthetic status and selected golden safety invariants. It does not implement or test a collector, native receipt, crash recovery, monotonic clock continuity, app-server or live SSH. Upstream's separate test-only validator likewise supplies design/schema checks, not runtime acceptance evidence. No issue is closed and no implementation is enabled here.

Evidence and ongoing maintenance

Evidence class What it establishes What it does not establish
Baseline source review above Implemented branches, flags, bounds and optional fields at pinned SHAs Behavior of a different release or a live destination
POSIX CI fixtures Read-only SQLite/reference discovery, real local fixture locks/sockets, CLI/SSH failure contracts, packaging on Node 22/24 Live model consumption or all OS/architecture combinations
Windows CI native fixtures x64 Node 22/24: real kernel locks, Restart Manager PID/SID/start evidence, ownership/revalidation races, queue fixture, actual PowerShell/.cmd with fake SSH endpoint, installed native binary/package checks Real network SSH authentication, real Codex queue acceptance, Windows arm64, receiver ACK
Dedicated live observations Only the recorded target/runtime/OS/arch and observed submission/ACK in VALIDATION.md General qualification of all builds/agents; fixture pass is never ACK
Public registry checks Exact package hashes, provenance/signature metadata and isolated installation for the recorded release Live-agent delivery or consumption

The implemented rows map to these repeatable checks:

  • conformance.test.ts: Claude/Codex discovery against the pinned Python oracle, unsupported schema/row/command refusal, empty-PATH runtime.
  • transport.test.ts: real POSIX inbox, guarded queue, wrong/competing writer, unknown native outcome, inert Reply-To, host boundaries, SSH preflight/framing/loss.
  • windows.test.ts and windows-contract.test.ts: the bounded Windows native contracts described above.
  • input-boundaries.test.ts: exact 1,000,000-code-point / 32,768-byte message bounds, envelope overhead, 4,100,000-byte escaped JSON wire bound and invalid UTF-8 refusal. Python SSH limits are source-inspected here; these TS tests do not exercise the Python transport.
  • types.test.ts, package-smoke.ts, release.test.ts: pure typed helpers, package/native loading, artifact and publication gates. package-smoke also checks installed-path update guidance without network or file changes.
  • updates.test.ts: #22 update checks and notices against a local fixture registry (see below).

Windows expansion belongs to #34; this matrix belongs to #33. Each feature PR must update its rows, sources, evidence date and affected README languages. Changes to JSON, env, defaults, runtime/remote requirements or packaging must include the corresponding acceptance tests. Keep planned features distinct from supported behavior and deliberate exclusions; milestone assignment is not a release commitment. Recheck the exact packaged copy during release review.

Source Codex home selection — #17 / 0.2.0

This is 0.2.0 behavior; it does not change the immutable npm 0.1.0 archive. send --to codex:UUID selects only a unique stable live writer across bounded known homes. --codex-home pins the destination but still inventories competitors. A unique live writer without a saved row yields thread_not_yet_persisted; an unknown owner or multiple live writers refuses. An unsaved live competitor is checked too. Listing does not authorize sending.

--allow-inactive-codex-home requires an explicit home (including a home in a Reply-To URI), a saved thread there, and all candidates verified inactive. Ordinary inactive sends retain error: inactive_writer with resolution reason inactive_queue_requires_opt_in. No wake, resume, native DB write, wait or ACK is added. Dry-run validates once and submits zero messages. Actual submission re-enumerates and compares all candidates, saved-row presence, DB file identity, lock-file identity and live owner evidence immediately before one native queue call. Evidence changes refuse; changes after the final check remain an OS/native queue race, not an exactly-once guarantee. Post-spawn failures remain unknown, submitted: null, retryAllowed: false; a queue ID is not an ACK.

Home/configuration Source send behavior
Unset SESSION_PEER_CODEX_HOMES, or [] No extra candidates
Empty/whitespace, malformed JSON, relative/non-string/NUL array entry Reject entire configuration, even with explicit destination
Absolute or ~/ array entries Canonicalized, deduplicated, required homes
Explicit argument, nonempty CODEX_HOME, configured home missing DB home_inventory_unreadable; candidate state_db_missing
Optional default/Orca home missing DB Allowed as absent, but requested UUID lock still checked
Unreadable DB, unresolved path, unknown writer Refuse; never skip a competitor using partial results

Migration from 0.1.0: unset an empty SESSION_PEER_CODEX_HOMES or set it to []; remove stale CODEX_HOME instead of relying on its missing DB being ignored. These guards are stricter than Python 1.0.2's selected-missing-DB exemption and saved-match writer inspection: this source checks even unsaved competitors.

codexHomeResolution has schemaVersion: 1, status (selected, explicit, ambiguous, unknown), canonical selected or null, fixed reason, and candidates. Each candidate has codexHome, sources, savedThread (boolean/null), writerLock (not_checked, held, free, absent, unknown), optional activity, and fixed reason. Uninspected candidates remain explicit. No raw subprocess stdout/stderr, PID start fingerprint, UID or command is exported. Errors before home resolution (invalid options/message or missing executable) need not include this field. SSH resolves on the destination and preserves these fields on verified responses; response loss cannot supply trusted diagnostics.

queueId is included only when successful native stdout contains exactly one line Queued message ID for thread UUID. for the requested UUID. ID must be 1–128 ASCII alphanumeric/./_/:/- characters, starting alphanumeric. Missing, malformed or multiple confirmations omit the field without retrying or changing successful queue status. Failed native processes never expose an ID.

Contracts: test/codex-homes.test.ts, test/transport.test.ts and test/windows-contract.test.ts. These are fixtures, not real recipient ACKs.

UID-zero writer identity — source patch #100

0.3.1 patch for #100: numeric UID 0 is a valid caller identity for writer verification. Unknown or mismatched owners still refuse, and all lock stability/start-time checks and Windows SID validation are retained. This patch is not in the immutable npm 0.3.0 archive.

Source read-only doctor — #18 / 0.2.0

Historical base-doctor evidence date: 2026-09-28. Introduced in 0.2.0, doctor --json [--agent claude|codex] supports local inspection and the existing version-checked SSH/stdin JSON transport. Published npm 0.1.0 does not contain this command. Both SSH endpoints must use the same TS version; reviewed commit alignment for source builds remains the operator's responsibility, not a wire-level commit check.

ok:true, exit 0 and diagnosticCompleted:true mean the diagnostic command completed, including when agents are unavailable. ready is a metadata precondition summary for the selected agents, not authorization to send. Per-agent results remain in agents; per-home Codex results retain source labels, required/optional status and missing, permission, unsupported-schema or unknown codes. Explicit doctor home inspection bypasses unrelated inventory, like list; it does not replace send's stricter competitor checks. Codex executable paths are inspected for accessibility and never executed, even with --codex-bin. An accessible DB and executable do not verify a live writer (writerVerified:false).

Claude checks bounded numeric session records (regular files, maximum 64 KiB), process identity and inbox metadata. POSIX checks the socket file; Windows checks process identity and a named-pipe advertisement, not pipe existence or availability for connection. verification states this distinction. Invalid records and unknown/permission inspections are counted. There is no inbox connection, queue submission, lock acquisition, chmod, configuration mutation, wake, login, or credential read. Native process arguments, stderr and session names/bodies are not emitted. Host/home and executable paths are intentionally reported.

capabilities describes implementation support: list/send/doctor:true, wake/wait/ack/consumptionConfirmation:false, agents:[claude,codex], transports:[local,ssh]; implementation:typescript distinguishes runtime identity. These are implementation capabilities, not evidence of agent readiness or ACK.

skills inspects only ~/.agents/skills/session-peer-ts/SKILL.md and <explicit home, CODEX_HOME, or ~/.codex>/skills/session-peer-ts/SKILL.md. No skill installation or reference traversal occurs. The legacy #25 TS skill contract requires metadata version/min/full 0.1.0, implementation typescript, and capability policy probe-help; missing/malformed/incompatible metadata is reported separately and does not change agent readiness. This conservative metadata check does not certify instruction content. Generic Python session-peer skills are not inferred compatible. Since 0.3.0, the separate opt-in doctor --check-return-route can execute a bounded reverse SSH no-op (current behavior); base doctor does not run that probe. --check-reply-to is not an alias and is unsupported. Neither mode observes a reply or authenticates an ACK.

Acceptance evidence: test/diagnostics.test.ts ports Python diagnostic cases for missing homes/tools, unsupported schema, per-home results, permission/unknown boundaries, invalid/stale session records, read-only inspection and unavailable inbox; adds source capabilities, bounded skill metadata, stdio and POSIX SSH fixtures. test/windows-contract.test.ts checks doctor through real encoded PowerShell/.cmd fixture SSH, without queue dispatch. Fixture evidence is not a live-agent or delivery/consumption test.

Source CLI usability — issue #19 (2026-09-28)

Version 0.2.0 adds full list/send/doctor help, text rendering and one positional message. The matrix distinguishes this from historical 0.1.0. The explicit output requirement is retained: --json or --output-format json|text; no default format changes. No selector retains json_output_required for compatibility. Conflicting selectors and all parse errors produce one JSON refusal before stdin reads/discovery/send. Valid text requests render execution errors as text. Remote wire requests require JSON even when the caller selects local text output. Partial discovery preserves rows, diagnostics and nonzero exit status. Terminal control characters in text labels are escaped; JSON is unchanged.

One positional body, named body or stdin is accepted; combining sources refuses before reading stdin. -- terminates option parsing. Empty or whitespace input is rejected before envelope construction. Existing input/transport budgets and unknown outcome/no retry boundaries remain.

Claude exact name matching uses Unicode 14.0.0 CaseFolding.txt C/F mappings (default non-Turkic full casefold), pinned independently of Node ICU and Python's runtime Unicode version. No normalization or fuzzy comparison occurs. ß/ss, Greek sigma and ligature collisions require PID selection. Unicode data source and SHA-256 are recorded in src/casefold.ts; regenerate offline with node scripts/generate-casefold.mjs /path/to/CaseFolding.txt (hash enforced). The package includes UNICODE-LICENSE.txt. Python builds with newer Unicode databases can differ for characters added after 14.0.0; this is not a blanket future-Unicode parity claim. test/cli-usability.test.ts covers format/help/input/error boundaries, real local name/PID selection with collisions, and text escaping on all CI platforms.

Source TS skill guidance — #25 / 0.2.0

Evidence date: 2026-09-28. TS CLI baseline for this work is 8289da7; companion candidate is 081cc3c1d16a394bd92824333f4bc61c36951799. This paragraph records the 2026-09-28 source review, not current repository-tag availability. Companion PR #14 was later merged; its separately pinned TS metadata remains 0.1.0, and #118 owns any successor contract/pin. Published npm 0.1.0 remains unchanged. There is no runtime setup subcommand: user-invoked Skills CLI 1.7.0 is the reviewed setup path, with an exact commit, explicit agent, and explicit project/user scope chosen before running the documented command.

  • Ownership: resolve the PATH launcher, require session-peer X.Y.Z (typescript), then capture that launcher's help. Python's referenceVersion is not a feature guarantee. Baseline examples retain explicit agent, Codex home and JSON flags.
  • Development capabilities: doctor, implicit home selection and inactive opt-in require affirmative help support. When top-level help advertises per-command help, inspect send --help for send flags and doctor --help for the supported doctor command. Mere mention in an unsupported list does not enable a feature. Skill minimum/full runtime metadata mean baseline support, not support for every conditional option. No assumption of wake, Antigravity, Relay, MCP or general wait support is imported from Python.
  • Workflow: fresh list → preserve exact home/PID → dry-run → one authorized send. Reply URIs remain data, queued is not ACK, and unknown never permits retry.
  • Metadata contract: separate session-peer-ts/SKILL.md, skill version 0.1.0, runtime-implementation: "typescript", runtime-min-version: "0.1.0", runtime-full-version: "0.1.0", runtime-capability-policy: "probe-help". Missing or incompatible skills can be diagnosed independently of transport readiness; the runtime never executes instructions merely to inspect metadata.
  • Lifecycle: no npm hooks, downloads, automatic update or overwrite of the Python skill. Pinned update repeats add for another reviewed commit after inspecting local edits. skills update follows a moving source and is not this workflow.
  • Acceptance evidence: test/diagnostics.test.ts already tests the exact companion metadata contract as compatible without executing or exposing the skill body; compatibility does not authorize sending. test/skill-guidance.test.ts exercises baseline JSON list, selected-home dry-run refusal and gated explicit inactive dry-run in disposable SQLite homes; no submission occurs. Companion scripts/test-ts-skill.mjs exercises real Skills CLI project/global install, replacement-update, list and removal for Codex/Claude Code while preserving a Python skill fixture. Those are installation/contract fixtures, not live-agent delivery or ACK evidence.

0.2.1 reliability and hardening

0.2.1 (#49–#57, #59, #64, #65) tightens existing 0.2.0 behavior and adds no new command, transport or JSON field. Callers may notice the stricter refusals below.

Area Source behavior
Codex body argv (#49) Native queue receives --message=<text> as one argument, so bodies such as - item, --help or -x are queued as text. The SSH request forwards the body the same way.
Codex SQLite location (#50) An inherited CODEX_SQLITE_HOME is removed from the native queue environment. A home whose config.toml sets sqlite_home is refused as unsupported_codex_sqlite_home (unreadable config: codex_config_unreadable) in dry-run and send, and doctor reports it as unsupported, because validation reads <home>/state_5.sqlite. A live writer started with its own CODEX_SQLITE_HOME cannot be observed.
Option values (#51) A separate value that is exactly an option name (for example --message --dry-run) is invalid_option (exit 2), as in Python argparse. Use --message=<text>, stdin or -- <text> for such text.
SSH exit code (#52) A verified remote response keeps its 0/1/2 exit code locally.
Reply-To codexHome (#53) Must be a POSIX or Windows absolute path, matching Python; relative and ~ paths are invalid_reply_uri.
Text output (#54) Also escapes U+061C, U+200B–U+200F, U+202A–U+202E, U+2060–U+2069 and U+FEFF. JSON is unchanged.
SSH preflight (#55) Exit 0 with the exact version line passes even when login scripts write to stderr; stderr classification applies only to failed preflights.
Windows --remote-bin (#56) Every PowerShell single-quote variant (', U+2018–U+201B) is doubled inside the encoded command.
Windows batch shims (#57) A .cmd/.bat executable found first on PATH (or given explicitly) is reported as executable_unsupported by send and doctor, because Node cannot spawn them without a shell and a shell would interpret the message. Use the native codex.exe.
Envelope trimming (#59, #64) Leading/trailing \n is trimmed by index scanning instead of /^\n+|\n+$/, which was quadratic on long interior newline runs (CodeQL js/polynomial-redos). Trimming semantics are unchanged.
Windows owner inspection (#65) The compiling Add-Type owner inspection waits up to 20000 ms; the compile-free creation-time probe keeps 8000 ms. Timeouts still refuse.

0.3.1 reliability fixes

0.3.1 source includes the following patch updates; publication is a separate approved release operation. The dated public 0.3.0 evidence remains historical.

Area Patch contract
SSH results (#93, #97) Malformed successful send responses affect only that host; other outcomes are preserved. Unnamed Claude PID targets accept name:null. Unknown outcomes are never retried automatically.
Codex storage (#94) Parse TOML root keys, including escaped keys, with TOML semantics. Nested sqlite_home is not a root override; malformed configuration is refused. An inactive opt-in does not bypass the root storage guard.
Reply-To (#98) Reject decoded Claude session values with reserved codex: or session-peer: prefixes; wire receivers reject unresolved nested URI transports before SSH or queue execution.
Claude discovery (#99) Read bounded regular registry files, skip unsafe/oversized entries and preserve healthy sessions. POSIX FIFO/nonblocking and descriptor race tests are platform-specific evidence.
Writer identity (#100) UID 0 is a valid verified identity; missing or malformed identity information remains refused.
Documentation (#90) Includes the migration wording correction absent from the immutable public 0.3.0 archive.

SSH still requires identical TS versions on both ends: 0.3.0 and 0.3.1 refuse each other at preflight. Coordinate endpoint upgrades. The 0.3.0 known limitations below remain: Windows-client jump routing, the Windows deployment cell and newer receipt/recovery rules, and update notice validation gaps are not resolved by this patch.

0.3.0 remote usability and maintenance

0.3.0 (#20, #21, #22, #74, #78/#79, #87; design #23) adds remote and maintenance features to 0.2.1. Existing commands, exit codes and the delivery boundaries (posted/queued are submission, never consumption or ACK) are unchanged. SSH still requires the same version on both ends, so 0.2.1 and 0.3.0 endpoints refuse each other at preflight; upgrade them together.

Area 0.3.0 behavior
Multi-host SSH (#20) Ordered repeated --host for list/send/doctor (array output for two or more), all hosts validated before any ssh starts, duplicate_ssh_host, IPv6 literals and sshUser/sshUserSource. See details
Connection options (#20) --ssh-opt is an allowlist (-p, -l, -i, -o Port/User/IdentityFile/IdentitiesOnly, -4, -6); everything else is refused. Typed single-hop --ssh-jump USER@HOST[:PORT] with a fixed hardened ProxyCommand, POSIX clients only
Sender context and return routes (#21) Verified Claude/Codex sender identity (From:), generated structured Reply-To URIs, --reply-to, a Tailscale routing hint and opt-in doctor --check-return-route. No executable Reply: line. See details
Update checks (#22) update --check reads npm dist-tags once (3 s, no retries) and reports manager-specific guidance; nothing is installed. Cached client notices are opt-in (SESSION_PEER_UPDATE_NOTICE=1) and opt-out wins. See details
sp shorthand (#74) Opt-in, shell-scoped Bash/zsh and PowerShell activation files; npm registers only session-peer. See details
Bounded timeouts (#87) On POSIX, external commands run in their own process group, and a timeout, output overflow or termination signal kills the whole group (including a jump ProxyCommand). On Windows only the direct child is killed; the call still returns at its deadline
Windows process probe (#78, #79) The compile-free creation-time probe waits up to 15000 ms (was 8000 ms) after a CI cold-start timeout; the compiling owner inspection keeps 20000 ms. Timeouts still exclude the process
Remote deployment (#23) Design only: a proposed ADR for Python-free deployment and version compatibility. No deployment code ships; the exact-version preflight is unchanged

Known limitations in 0.3.0

  • #20 — Partial. --ssh-jump is refused on Windows clients (ssh_jump_unsupported_platform) until Win32-OpenSSH ProxyCommand behavior is verified. No multi-hop and no arbitrary ssh options.
  • #23 — Partial. The remote deployment ADR is a proposal. Its POSIX cell was exercised by hand (record); that dated Linux run did not exercise the later receipt/recovery rules. The 2026-10-07 Windows record adds bounded published-0.3.1 qualification on Windows 10 x64, an administrator account, PowerShell 5.1 and Node 24.16.0: offline provisioning, wrapper/native locking, operator doctor and nonexistent-target dry-run, plus 31 local storage fixtures. Node 22 has banner/native-lock smoke only, not the full matrix. Non-admin, other architectures, F6 runtime-identity enforcement and the Linux current-rule gaps remain open. No 0.3.2 live ACK or deploy helper is established.
  • Update notices (#22). The Windows refresh, refresh-failure and forged-clientUpdate (injection) cells are unverified; Windows CI covers only the array/flat shape and one text stderr line. Those cases have POSIX fixture evidence only.
  • Fixture and CI evidence is not a live-agent ACK.

Sender context and return routes — #21 / 0.3.0

0.3.1 patch for #98: a Claude Reply-To URI whose decoded session starts with reserved lowercase codex: or session-peer: is invalid_reply_uri before local selection or SSH dispatch. Ordinary Claude names and Codex UUID routes keep their existing contract; this patch is not in the immutable npm 0.3.0 archive. Wire receivers require an already resolved target; an unresolved Reply-To URI in --stdio-request is nested_transport_forbidden before queue or SSH calls.

#21 behavior added in 0.3.0; 0.2.1 and earlier do not have it. Compared with Python 1.0.2 replies.py/diagnostics.py:

Area Python 1.0.2 TS source
Claude sender PID parsed from CLAUDE_CODE_MESSAGING_SOCKET, matched in the registry; name or PID Same evidence. The match must be exactly one live row (exact socket match when the path has no PID). A name that is not unique by casefold, is all digits, contains : or a control character falls back to the PID
Codex sender CODEX_THREAD_ID, else CODEX_SESSION_ID; codexHome from CODEX_HOME Same, plus codexHome only for an absolute CODEX_HOME. Two different IDs give no identity
Nested agents Claude wins over Codex variables Both kinds of evidence give no identity (ambiguous), per #21
From line From: agent:id @ user@host with a local-hostname fallback From: agent:id only. The return host appears only in the structured Reply-To
Reply-To URI plus executable Reply: python3 … send … line URI only; no executable line. Generated only with a sender; local sends without a configured host get transport=local
Host precedence --reply-to, SESSION_PEER_REPLY_HOST, CC_PEER_REPLY_HOST, detected tailnet address; user added when missing Same order. Detection reads only tailscale status --json (MagicDNS name, else 100.64.0.0/10 IPv4); no tailscale ip/ip addr/ifconfig scraping. --reply-to conflicts with --reply-address/--no-reply-to
Self normalization User (if any) equals this user and host is local The user is required and must equal this user, and the host must be an exact local form (localhost, canonical 127.x.y.z, ::1, ::ffff:127.x.y.z, the host name or Tailscale self); non-canonical numerics and IPv4-compatible IPv6 (RFC 4291 section 2.5.5.1) never normalize to local. This strict predicate is separate from the fail-closed return-host refusal. Applies only when the route came from the URI alone (no --host/SSH options, including --ssh-jump)
Tailscale routing Online peer: alias kept, HostName/HostKeyAlias added; offline/ambiguous refused; unknown unchanged; MagicDNS off means no lookup Alias kept and HostName added only for Online === true with MagicDNS on. HostKeyAlias is added only when the same ssh -G used for sshUser read the config and found none; a configured alias is kept and an unread config is never overridden. Online === false is refused even with MagicDNS off; any other Online value is ordinary SSH. Status is queried at most once per command with a 3 s bound; host is the MagicDNS name and sshHost the requested alias. SESSION_PEER_TAILSCALE=off disables it. The macOS app CLI is used only when no tailscale is on PATH
Return-route probe doctor --check-return-route [--reply-to]; fixed true command; detail may contain stderr Same options plus -T and ControlPath=none. Fixed exit 0 (a no-op in POSIX shells, cmd.exe and PowerShell; only POSIX return hosts have fixture coverage). The 8 s deadline covers the final ssh only; the Tailscale (3 s) and ssh -G (5 s) lookups are bounded separately. Same returnRoute statuses and reasons plus return_host_is_receiver; never includes stderr. With --host, a return host that might name a machine (fail closed, no DNS: localhost names, canonical 127.0.0.0/8 and 0.0.0.0/8, ::1, ::, ::ffff:-mapped forms, IPv4-compatible IPv6 and every non-canonical numeric such as 127.1, 0177.0.0.1, 2130706433 or 4294967296, whose meaning differs between resolvers) is invalid_return_route before SSH, on the receiver a known self name (exact OS host name or Tailscale self name/address, for any user; other DNS/LAN aliases are not detected) or an unprovable numeric form fails as return_host_is_receiver instead of normalizing to local, and the receiver gets only the return host through the JSON request. A remote send refuses a loopback or non-canonical numeric reply host (invalid_reply_host). Like every ssh this CLI starts, the probe and its ssh -G lookup use the probing machine's trusted ssh_config; see the #20 trust boundary

Evidence: POSIX fake ssh/tailscale fixtures in test/replies.test.ts (sender detection and ambiguity, name fallback, precedence and conflicts, same-user/wrong-user self routes, offline/unknown/ambiguous peers, generated and malformed URIs, probe argv and auth/host-key/timeout/other outcomes, remote probe forwarding). A test preload sets SESSION_PEER_TAILSCALE=off and clears CLAUDE_CODE_MESSAGING_SOCKET, CODEX_THREAD_ID, CODEX_SESSION_ID and the reply-host variables for every test; fixtures that need an identity set it explicitly. No real tailnet, SSH destination or agent session was used.

Multi-host SSH and connection options — #20 / 0.3.0

#20 behavior added in 0.3.0; 0.2.1 and earlier do not have it. Compared with Python 1.0.2 --host/--ssh-opt:

Area Python 1.0.2 TS source
Repeated --host Ordered per-host loop; one host stays flat, several return a JSON array; any failure exits 1 Same shape and order for list, send and doctor. Every element keeps schemaVersion, ok, host (as requested) and command. Any failed host makes the overall exit 1; a single host keeps its own 0/1/2 exit code
Malformed successful send response No delivery may be inferred from an invalid response A spawned send response with a missing or command-invalid target is unknown for that host (submitted:null, no retry); other host results remain intact. Target validation follows the requested agent and accepts the Python Claude shape without target.agent. Text output falls back to JSON for values that cannot be string-coerced, so formatting cannot turn an accepted send into a false command-wide refusal
Validation before dispatch Host/option checks run inside the loop, so an earlier host may already be contacted. argparse syntax errors print usage to stderr (exit 2) without JSON; other pre-dispatch errors are attributed to each --host Every host, --ssh-opt, message and remote option is validated before any ssh process starts (including ssh -G). TS always answers in JSON. Every --host VALUE/--host=VALUE before -- is collected before any other check, so with two or more of them every pre-dispatch refusal, including syntax errors such as an unknown option or a missing value, is an array with one submitted:false element per host; with fewer it is one flat object
Duplicate destinations Not checked duplicate_ssh_host for a repeated destination string (user, case-insensitive host name, canonical IPv6). Syntactic only: aliases or addresses of the same physical host are not detected
Attempts One attempt per host One preflight and at most one request per destination. Refused/unknown hosts are reported in place; later hosts still get their single attempt. No failover, resend or retry after unknown
Timeouts Python subprocess timeouts on the direct child POSIX: each ssh (and every other external command) starts detached, in a new session and process group without a controlling terminal, registered as soon as its PID exists; cleanup covers that group only (a descendant that calls setsid() leaves it and is out of scope; no PID scanning); a timeout, an output overflow or SIGINT/SIGTERM/SIGHUP to the CLI kills the whole group, including a jump ProxyCommand that holds the pipes, and the call completes after the exit plus a 100 ms grace instead of waiting for the pipes. Windows: only the direct child is killed, descendants may linger, and the call still returns at the deadline. Preflight timeout stays a refusal and request timeout stays unknown without retry (#87)
--ssh-opt Arbitrary ssh arguments minus a substring blocklist (proxycommand, localcommand, permitlocalcommand) Allowlist only: -p PORT, -l USER, -i IDENTITY_FILE, -o Port=/User=/IdentityFile=/IdentitiesOnly=yes|no, -4, -6, as separate or attached tokens. Anything else is unsupported_ssh_option; a bad, duplicate or dash-leading value is invalid_ssh_option. Identity paths refuse % and $ (OpenSSH token/environment expansion). Options are re-emitted canonically after the fixed BatchMode=yes, StrictHostKeyChecking=yes and ConnectTimeout=10, which OpenSSH therefore keeps
Jump hosts ProxyJump/-J allowed through --ssh-opt; the hop gets no command-line BatchMode/StrictHostKeyChecking Done for POSIX clients: typed --ssh-jump USER@HOST[:PORT] (one hop, explicit user, shell- and %-inert characters only). The CLI builds a fixed ProxyCommand: the shell-quoted, %-escaped ssh path with BatchMode=yes, StrictHostKeyChecking=yes, UpdateHostKeys=no, ConnectTimeout=10, ConnectionAttempts=1, ProxyCommand=none, ProxyJump=none, ControlPath=none, ForwardAgent=no, ClearAllForwardings=yes, PermitLocalCommand=no, -W '[%h]:%p'. It is placed with the fixed options, together with outer ControlMaster=no, ControlPath=none and ProxyUseFdpass=no, so it overrides a ProxyJump/ProxyCommand and any control master in ssh_config for the target (checked with ssh -G -F <fixture>). -J/ProxyJump/ProxyCommand through --ssh-opt stay refused. Refused with --ssh-control-path and on Windows clients (ssh_jump_unsupported_platform) until Win32-OpenSSH ProxyCommand behavior is verified. No multi-hop, and no Tailscale lookup for the hop (v1). Result field sshJump. --ssh-opt values (including -4/-6) apply to the target only, not the hop. Shell scope: the generated ProxyCommand was verified with sh, bash and zsh; other login shells are unverified. Unverified boundaries: Win32-OpenSSH clients. Python parity: Python accepts arbitrary -J hops; TS offers a stricter, typed single hop
User sshUser/sshUserSource: explicit from USER@HOST, else ssh -G (ssh_config_or_local_default), else unknown Same fields and sources; -l USER also counts as explicit. -l together with USER@HOST is conflicting_ssh_user, because -l silently wins. Without an explicit user, one local ssh -G runs per host (5 s limit). It does not connect, but per ssh(1)/ssh_config(5) it evaluates the user's ssh configuration, including Match exec commands, as Python's lookup does. A failed or unexpected lookup gives unknown for that host only
Trust boundary Blocklisted command-line options; user ssh config trusted The allowlist governs only options this CLI puts on the ssh command line. The user's and system ssh_config are trusted user configuration: ProxyCommand, ProxyJump, Match exec, Include and similar settings there apply to every ssh this CLI starts, as for the user's own ssh
IPv6 Passed through to ssh 2001:db8::1, [2001:db8::1] and user@[2001:db8::1] are accepted; brackets are removed for ssh, sshHost shows the destination used. Zone IDs (%) are refused
Control socket / Windows n/a --ssh-control-path requires exactly one --host. --remote-platform win32 and --remote-bin apply to every host with the same encoded PowerShell command
Text output Per-host human blocks Per-host blocks prefixed by Host: <host>

Evidence: POSIX fake-ssh fixtures in test/ssh-hosts.test.ts (ordering, argv, zero invocations on invalid input including syntax errors, flat versus array refusals, partial results, per-host ssh -G failure, IPv6, POSIX/Windows remote commands, body absent from argv) and a two-host case in the Windows native fixture. --ssh-jump fixtures use a fake ssh (installed under a path with spaces, a quote and %) that expands %h/%p/%% and runs the ProxyCommand through /bin/sh -c exec, recording the exact hop argv; injection attempts start no ssh. Before implementation, local ssh -G -F <fixture config> (OpenSSH 10.3p1, no connection) confirmed that a command-line ProxyCommand overrides the target's configured ProxyJump/ProxyCommand without error, and that the hop's command-line ProxyCommand=none/ProxyJump=none override the jump host's configured proxies; ssh_config(5) does not list ProxyCommand among the keywords with ${ENV} expansion. The unknown case is a classification fixture: the fake exits 255 on the request without running the remote CLI, so it shows that unknown is never retried, not that a delivered message's response was lost. No real SSH destination was contacted.

Update checks — #22 / 0.3.0

Evidence date: 2026-10-02. Added in 0.3.0; 0.2.1 and earlier have no update command. session-peer update --check --json [--channel latest|preview] makes one GET to <registry>/-/package/session-peer/dist-tags (default https://registry.npmjs.org/, 3-second timeout covering the body, 64 KiB body limit, redirects refused, no retries). latest must be a stable npm semver; preview may be a prerelease. Versions follow SemVer 2.0 precedence without a v prefix or build metadata. Python 1.0.2's GitHub tags are a different stream and are never compared; referenceVersion is unrelated to this check.

Field Meaning
current, latest, channel, distTag Running TS version, the dist-tag's version, and the tag name
source npm_registry
status, outdated update_available (outdated:true), up_to_date or ahead
updated Always false; nothing is installed
managedBy, updateCommand, guidance Owner from the resolved CLI path, positively identified only: npm (global prefix whose bin/session-peer symlink or session-peer.cmd launcher targets this package: default, Homebrew, nvm, nvm-windows, fnm), pnpm/yarn/bun (global store whose package.json declares session-peer), volta, npx. Command only for those owners and only when outdated. npm_project/pnpm_project (project package.json declares the dependency), source (checkout) and unknown (anything else, such as an unconfirmed nvm-windows prefix) always get null plus a guidance sentence
cache written, skipped_locked, skipped_stale_lock, skipped_newer, failed, or not_applicable (preview channel)
skills, skillsManagedBy Same local TS skill metadata check as doctor; separate

Failures use the standard caught-failure envelope with registry_timeout, registry_unreachable, registry_http_error, registry_response_invalid, dist_tag_missing (exit 1), or invalid_update_registry, unsupported_update_channel (exit 2). Response bodies are never emitted. update without --check is self_update_unsupported (exit 2) with updated:false, managedBy, updateCommand (dist-tag spec, same owner rules), guidance and checkCommand; it makes no request. --host, other list/send options and --stdio-request (error remote_update_unsupported) are refused, so a remote destination never performs update checks. doctor capabilities add updateCheck:true, selfUpdate:false.

Notices are deliberately opt-in (Python's are on by default): agents and scripts call this CLI frequently, and an unrequested registry call or extra stderr line would change its no-network default. With SESSION_PEER_UPDATE_NOTICE=1 (1/true/yes/on), completed local list/send/doctor results read only the local cache. A fresh (24 h) cache whose stable latest is newer adds clientUpdate (schemaVersion, status, current, latest, channel, checkedAt, source: "npm_registry_cache", managedBy, command, guidance) to JSON, or one stderr line for text; stdout text is unchanged. A missing, invalid, future-dated or expired cache spawns one detached refresh and returns immediately. A failed refresh stores latest:null, which suppresses attempts for 1 hour. Opt-out (--no-update-notice, SESSION_PEER_NO_UPDATE_NOTICE) suppresses these background notices and refreshes and wins over opt-in. An explicit update --check is an intended request: it always contacts the registry and, on latest, writes the cache under the same lock rules (reported as cache). Cache and network failures never change results or exit codes.

Scope: notices belong to the invoking client. With --host, the client adds clientUpdate (or its stderr line) to its own top-level output, including the verified result obtained over SSH; the --stdio-request receiver never reads, refreshes or produces a notice. The notice is computed once per invocation. Multi-host and failures (shipped 0.3.0 integration with #20/#21):

  • Repeated --host JSON array: request order and every element's exact shape are kept; no clientUpdate in elements and no wrapper object.
  • Flat result: one destination (local or a single --host) gets the additive field, also when that per-host result failed (preflight refusal, remote ok:false) or is unknown. Top-level parse errors and local caught failures (the top-level catch path in cli.ts) never get it.
  • Text output: one stderr line per invocation, for flat or array output and with failed, mixed or successful hosts; stdout is unchanged.
  • The advisory never changes status, exit code, submitted, consumptionConfirmed or retryAllowed.
  • clientUpdate is reserved for the client: remote() deletes any clientUpdate in a verified receiver response before it is shown.
  • Refresh count: the notice is computed at a single call site after dispatch, once per invocation. The fixture evidence is indirect: registry request counts with the single-flight cache lock (one request for three hosts, none from receivers). It does not instrument the call itself.

Evidence: POSIX test/integration-multihost-updates.test.ts (fake ssh). Windows: test/windows-contract.test.ts checks one opted-in repeated --host array (no element field), one text stderr line and a flat single-host field through the encoded PowerShell fixture; it is not evidence for the refresh count or the failure/forged-field cases, which are POSIX-only.

Single flight, fail closed (changed after review). Earlier revisions of this PR reclaimed stale locks and used a takeover mutex; review found legal filesystem interleavings that let a newer cache record be overwritten or another generation's lock or mutex be deleted. That code is removed:

  • One lock, npm-update.lock, created with O_EXCL and holding a random token. The notice path creates it before spawning the detached refresh, which inherits the token. update --check creates it around its own cache write.
  • Every cache write (background or explicit) happens only while holding the lock, and only when the existing record is older than the writer's start (checkedAt); an explicit check that finds the lock held still reports its result with cache: "skipped_locked". A refresh without the current token does nothing.
  • No code path takes over, renames or deletes a lock, temporary file or other resource it did not create, and no mutex exists. The shared cache record npm-update.json is the one file that is replaced by design, by atomic rename and only under the lock rules above. An existing lock of any age makes the background refresh skip. The holder deletes its own lock after re-reading its token.

Guaranteed, with no automatic recovery: at most one lock holder at a time (O_EXCL), so at most one background refresh and at most one cache writer at once; no writer replaces a record newer than its own start; no invocation deletes or moves a foreign lock, temporary file or other resource. Cost: a lock can be left behind when its holder crashes (the detached child runs for at most about 3 seconds) or when releasing it fails, for example because its token cannot be read back. A failed token write right after creation is undone only for that same inode. A left-behind lock blocks background refreshes until a user deletes npm-update.lock by hand while no session-peer process runs. update --check reports cache: "skipped_stale_lock" once the lock is older than 60 s; it never removes it. Manual deletion during a running refresh is the only way to break the guarantee (the holder could then remove a lock created after the deletion).

Cache: npm-update.json (0600, atomic temp-file rename) in a 0700 directory owned by the user: SESSION_PEER_CACHE_DIR (absolute), else $XDG_CACHE_HOME/session-peer on macOS/Linux, ~/Library/Caches/session-peer, ~/.cache/session-peer, or %LOCALAPPDATA%\session-peer\Cache. It holds only package name, channel, version and timestamp. Windows relies on the per-user profile ACL instead of POSIX modes. No checks or notices modify npm-owned files, Python installations, remote hosts or skills.

Acceptance evidence: test/updates.test.ts (POSIX and Windows CI) covers SemVer ordering, stable/prerelease tags, invalid/oversized/HTTP/redirect/timeout/offline registry responses without body leakage, manager guidance per install layout, including nvm, nvm-windows, fnm, Volta and ambiguous prefixes (unknown, no command), cache paths and modes, opt-in default off, flag/env opt-out, JSON additivity, text stderr, offline/invalid/expired/future caches, concurrent single-flight refresh, fail-closed live and stale locks (no refresh until manual removal), review regressions: an explicit check while a background holder runs reports skipped_locked and writes nothing, then the holder publishes; a holder whose start predates a newer record publishes nothing; a stale lock is never taken over, so its holder's release cannot remove a successor; a stale lock and a leftover mutex keep the same inode and content after notices and an explicit check; a non-holder token neither releases nor writes; multi-process acquisition yields one owner (none for a stale lock), failure backoff, Python cache isolation, TS skill metadata, wire isolation, and a POSIX fake-SSH list --host where only the client adds the notice. test/package-smoke.ts checks the installed package's guidance and unchanged files. All use a local fixture registry; no test contacts npm.

Optional shorthand — #74 / 0.3.0

0.3.0 ships opt-in Bash/zsh and PowerShell aliases for sp. 0.2.1 and earlier have no activation assets. npm still registers only canonical session-peer. Sourcing in the caller's shell/scope refuses existing sp executables, aliases and functions; no install hook, profile/PATH edits or runtime dispatcher is added. The alias resolves the same canonical command with the same arguments, stdin, output, exit codes and delivery boundaries. Automation/SSH/help/version retain canonical identity. See activation and lifecycle.

Python/TS co-installation is excluded; replacing an implementation respects its original manager. This TS change does not implement additional Python features. Package fixtures cover name equivalence, collisions, an in-place version update and removal on every supported shell. They do not establish live delivery or ACK.

0.3.2 release scope

The public 0.3.2 record pins publication source f335f07352c842f2f6ceb12bd2ca6274b52f69f7 and the artifact. Runtime behavior differs from 0.3.1 only in the version banner and exact-version SSH gate. Its dated preparation record describes the pre-publication checkpoint, not current availability. Windows destination qualification used published 0.3.1, with the limits above. No 0.4 handoff/app-server/observation runtime is included; Drafts and the joint design freeze are not shipped capabilities.

Ordinary SSH response correction — 0.3.3

The 0.3.3 correction for #116 and #115 validates bounded raw stdout with fatal UTF-8, one complete JSON object, decoded-key uniqueness, depth 64 and 65,536 nodes. Stdout has a 1 MiB byte cap; diagnostic stderr has an independent 4,096-byte retained budget. Overflow cannot turn a retained prefix into success.

Complete request-matched native facts survive a later SSH 255 or collection timeout. Native ok/status/submitted/target/queueId stay intact, while local exit 1 and client-owned sshTransport report transport failure. A peer-supplied sshTransport is discarded. Missing, partial, polluted, malformed or mismatched send responses remain outcome_unknown/submitted:null/retryAllowed:false. No retry, fallback or failover occurs, and submission is not ACK. Normal verified exit 0/1/2 behavior, nullable Claude names, optional queue IDs, per-host ordering and text/JSON output remain supported. Explicit Codex home/inactive selection context is checked; canonical remote aliases remain a destination trust boundary.

See the response contract and fixture limits. This 0.3.3 correction is not part of the immutable public 0.3.2 artifact, does not change the Python 1.0.2 oracle, and adds no 0.4.0 handoff/observer/ACK feature.

Published TypeScript companion skill — #118 follow-up

Historical 0.2.0 evidence; current 0.2.1 guidance is recorded in the 2026-10-14 checkpoint.

Evidence recheck: 2026-10-12 KST. Public TS runtime 0.3.3 at c0ed30273b9e9c0049df86ae3c38486a94289527 accepts the legacy skill 0.1.0 profile and the exact published skill 0.2.0 profile (typescript, minimum 0.1.0, full 0.3.2, probe-help). Public 0.3.2 remains legacy-only. The companion release session-peer-ts-v0.2.0 was pinned by the four guides at that checkpoint to source 8482cf644507c682915e35c85aa3384c53864906. The annotated tag object de7d619aebdfb4a8fe86893dc70728f646032e8d peels to that source; its 12,835-byte SKILL.md has SHA-256 00c6fc343fdbeb8f6dc1b1ee1b730f04cef71bbfb08205a7be3559202f1d8804. Those tag/file identities were independently rechecked for this follow-up.

compatible / skill_contract_compatible / metadata_only is a bounded metadata verdict, not a content signature, send permission or delivery/ACK result. Minimum is the baseline CLI compatibility floor; full is the documented published feature coverage reference. Optional operations still need actual version/help and platform/native prerequisites. Runtime and skill management remain separate, without postinstall. The Python 1.0.2 reference and Python skill are unchanged.

The immutable public 0.3.4 source/package uses compiled VERSION=0.3.4 and accepts these same two exact profiles through its default inspector; its explicit helper rejects unreviewed runtimes such as 0.3.5. TS 0.3.5 source/package adds the exact skill 0.2.1 tuple reviewed in the dated candidate-content record above. This source support is not proof of registry publication or an approved successor skill installation pin; final skill publication and pin adoption remain separate. At the earlier PR #134 checkpoint, compiled VERSION was 0.3.3 and the 0.3.4 row was tested through explicit validator arguments. That checkpoint is historical. Source support neither broadens compatibility to arbitrary future versions nor establishes runtime or skill publication. See the finite matrix and retained dated review. The dated candidate checkpoint below remains historical.

TypeScript skill guard — 0.3.3

Version 0.3.3 uses compiled VERSION=0.3.3 on the production doctor inspection path. Its proposed finite matrix accepts the legacy profile (skill/minimum/full 0.1.0) and the exact proposed successor (skill 0.2.0, minimum 0.1.0, full 0.3.2, typescript, probe-help, root name session-peer-ts). Unsupported well-formed tuples are incompatible; malformed or conflicting metadata is unknown. Supported and incompatible tuple verdicts report verification:metadata_only; missing, permission and unknown results omit it. Whole-file reading is bounded at 65,536 bytes with a single overflow-detection byte, fatal UTF-8 and regular-file/change checks. This does not authenticate instruction content or authorize delivery.

See the bounded grammar, matrix and reader evidence. Companion Draft #20 design acceptance at 699a325 records the 2026-10-09 static design review of exact #126 ba18bb806a6ba503381259b4255211af3adc0336 and release Draft #129 bfb3d6f1256ab076a76660e08a7bf1ad071a6c6c; their reviewed design SHA-256 values are recorded in the linked guard design. No tests were rerun in that review. At this dated checkpoint, runtime and successor skill were unpublished. The earlier queued coordination request remains historical, without independently established consumption or approval. Design/fixture acceptance does not authorize runtime merge/release, final skill metadata/publication/installation/pin changes or claim public compatibility; the immutable npm 0.3.2 checker stays legacy-only. The guard runtime must actually publish before separate successor skill finalization/tag/publication/pin changes. At that checkpoint the install pin remained 081cc3c1d16a394bd92824333f4bc61c36951799; managers own upgrades, and no postinstall is introduced.

At the 2026-10-09 KST preparation checkpoint, #128 integrated open PRs #125/#127 and Draft #126 for review; that preparation did not bypass their human merge gate or approve staging/publication. 0.3.2 and 0.3.3 refuse each other's SSH preflight. Windows jump-host #20, provisioning #23, the Python 1.0.2 oracle, and unshipped 0.4/native ACK boundaries remain unchanged.

Published Python handoff checkpoint — 2026-10-11

Python 1.1.0 is now public at 8ca77cf31254c8287a2593fe484bb30a7dba2727. Its handoff contract SHA-256 is 46a953010c49214bd8f969412f26713384fbe09a0ba78ebf0fef75da40d0d889, byte-identical to the jointly reviewed design. The published fixture SHA-256 is 963b94e78f510c2535e405813558275d512a5bfe6bbd99ffdf0994b4d2e6e441, also byte-identical to the frozen shared fixture. Both identities were rechecked against the exact release source through local Git without replace objects and the GitHub contents API. The exact reviewed TS mirror remains unchanged; publication is not permission to regenerate shared vectors or declare runtime conformance.

The handoff pin manifest records this source-only comparison separately from the historical Python 1.0.3 design baseline and the historical Python 1.0.2 ordinary oracle. The current candidate's ordinary source pin is recorded separately in python-reference.pin.json. The Python release's experimental local Claude receipt path does not establish automated ACK support in the TS implementation. The pure-validator comparisons below do not establish native execution parity. Receipt bootstrap and additional native platforms are deferred to #130–#132; original binding/generation guards remain mandatory in the included scope. This design-identity record alone does not repin a runtime reference or establish runtime conformance.

0.4.0 private handoff implementation — #70

The frozen #69 design pin is unchanged. The private POSIX ledger and strict protocol helpers now implement prepare, at-most-once sender fencing, status, bounded wait records and explicit receipt/operator-confirmation validation. CLI details describe the opt-in 0.4.0 scope; earlier 0.3.x packages lack these commands. Automated ACK bootstrap, Windows private ACL support and original-sender SSH collector coordination are deferred extensions (#131/#132), not prerequisites of the user-approved limited 0.4.0 scope. Fixture passes are not live receipt/injection qualification. Default ordinary output stays unchanged. The ordinary conformance oracle is published Python 1.1.0; experimental receipt implementation parity is not claimed. handoff-cross-runtime.test.ts also invokes the pinned published Python pure validators/projection helpers and the TS validators on 45 data cases: all seven shared states, schema/privacy/ACK/retry negatives, unsupported observation and agent-specific native field absence. Standalone and canonical module hashes are pinned. No native CLI, ledger/collector, model or network is invoked by that comparison; equal validator decisions are not end-to-end handoff parity.

The journal encrypts its private message-binding digest with a separate mode-0600 ledger.key; missing/changed/untrusted keys and plaintext journals are refused without automatic key recovery or history migration. This is an at-rest journal boundary, not protection against a reader of both private files. Duplicate committed operator confirmation returns the original receipt without rewriting its wait classification or journal.

0.4.0 bounded app-server foundation — #71

The opt-in internal client is qualified only for queue mechanics on Codex 0.160.1 and 0.162.1 / macOS arm64, using separate isolated network-denied synthetic homes. One native queue/add, original client message ID persistence and no loaded/resumed thread were observed. This is not live delivery, general owner-route or receipt ACK qualification. Legacy Codex queue remains the default CLI path.

The client bounds frames, RPCs, traffic, owner verification, monotonic deadlines and owned process-group cleanup. An authorized caller supplies a durable before-effect fence; at most one queue RPC follows it. After a possible write, disconnect, timeout, schema error or remote refusal never enables fallback or resend. initialize opts out the exact profile notification set (83 for 0.160.1, 84 for 0.162.1); queue/add's own input echo is validated but only IDs are returned. queue/list, history, full item/turn notifications, resume/start and model turns are excluded. The separate synthetic metadata protocol is explicitly fixture only. Other platforms/versions and native app-server observation remain unsupported.

Generated schema hashes and the fixed official source commit are recorded in test/fixtures/app-server-schema-evidence.json; tests use hermetic streams and owned Node children. These mechanics do not establish Python runtime delivery parity or implement automatic ACK.

0.4.0 local observation integration — #72

Scoped runtime details cover bounded one-shot/explicit-wait observation, native CID queueing, original writer generation checks, per-operation terminal waits and positive native fact preservation. Generated schema/native queue mechanics are distinct from synthetic metadata/wait integration and live consumption evidence. The combined CLI path is restricted to Codex 0.160.1 or 0.162.1/macOS arm64; POSIX SQL fixtures alone do not qualify other native platforms. No rollout/transcript API is queried, no ACK is inferred and no post-attempt fallback/resend occurs.

Dedicated 0.162.1 Codex and Claude Code 2.1.296 test sessions supplied additional local evidence; native client ID injection is separate from plain model response or ACK. Retained qualified intents support independent waits with no new native submission. Observation is read-only against native state; each wait persists its private operation and terminal record in the sender ledger. Each process owns its wait cutoff; foreign pending and old terminal waits are preserved. A separate voluntary matching reply was explicitly confirmed before any wait with operator_confirmed; ACK status and a later acknowledged wait succeeded. Confirmation after an earlier independent wait correctly refused receipt_order_unprovable, without replay. This is manual operator attestation, not an automated token-possession collector.

POSIX SSH opt-in now prepares an unattempted intent at an explicitly initialized destination ledger, pins resolved target/home/generation/epoch/CID, then sends once. Strict request-aware native facts survive complete failed-transport responses; unsolicited/malformed handoff cannot create ACK. Source deadlines are not renewed; destination remaining-duration watchdogs reap owned groups. Transit delay cannot be synchronized across independent monotonic clocks. Destination-handler/recording-socket fixtures use fake SSH. Separate real KR/US Linux checks established ordinary native queue acceptance and a US manual operator receipt; they did not qualify native client-ID observation on Linux. See SSH details.

Open capabilities include original-sender SSH collector coordination, Windows ACL/native observation and automated receipt producer/collector bootstrap. Other platform/version tuples and arbitrary blocking-filesystem deadlines are not qualified by these tests. #69/#70/#71/#72 remain together in the scoped 0.4.0 milestone. The ordinary comparison is pinned to Python 1.1.0; version 0.4.0 does not claim broader runtime parity.

Storage binding is checked on the owned queue transport before its effect: env/CLI pins plus exact opened state/queue paths. Managed-policy redirection or missing direct-native PID proof refuses with zero queue RPC. An additional network-denied initialization-only fixture verified config/CLI/env precedence with zero queued rows and no model/thread effect. Bootstrap policy files are not promised unchanged by the native tool; no managed requirement is bypassed.

Exact 0.162.1 queue profile qualification

The internal queue client also pins Codex 0.162.1/source 092d3acd6bec3e3a14bdc7e7a2810ab628ab759d on macOS arm64. The source/schema fixture records the exact notification opt-out set (84 methods, compared with 83 in 0.160.1). An unloaded, isolated thread received one queue request with its exact supplied client ID and native queue receipt; a separate owned-descriptor fixture refused the durable effect guard and left zero queue rows. No version range, Linux/Windows qualification, live delivery or ACK follows from these queue-only fixtures. Initial harness failures are recorded separately and were never retried with an already-attempted intent.

0.4.0 candidate release scope

On 2026-10-11 the user approved keeping #69/#70/#71/#72 together in 0.4.0, with the following explicit qualification boundaries. This is source-candidate scope, not a publication claim or blanket Python runtime parity.

Capability Included candidate scope Deferred extension
Native client-ID injection observation Exact Codex 0.160.1/0.162.1 on macOS arm64; warm original metadata store and unchanged writer generation Linux #130, Windows #131; no inherited profile support
Explicit receipt ACK Operator attestation on supported POSIX ledgers, with original binding/order validated; operator_confirmed is not model authentication Voluntary receipt producer, authenticated collector/bootstrap and original-sender SSH collector #132
Windows handoff storage Required capability reports unsupported; ordinary published Windows send/SSH are separate Private DACL/reparse storage and owned cleanup qualification #131
SSH handoff Explicit same-version destination preparation/ledger, strict binding and one send; unsupported goals refuse before effect Real remote native client-ID observation remains unqualified; actual Linux queues/manual ACK do not establish it

An automatic receipt collector, if later implemented, still records only a receiver's explicit receipt. Visible replies, arbitrary message text, queue deletions and turn completion never authorize ACK promotion. The frozen design bytes and historical approval metadata remain unchanged. SQLite projections may evaluate body-bearing cells internally; bodies/errors do not cross into JS or ordinary output. Required unsupported goals refuse before effect; after any possible submission no fallback/resend is permitted.

The four development PRs were merged in the authorized order #106→#107→#108→#109. Scoped evidence and separate release acceptance are tracked in their issues and #150. Platform/collector follow-ups are not completion conditions for this limited release. Companion skill candidate guidance and version/profile rollout is tracked in companion #31 and Draft #37. The 0.4.0 finite row preserves the three reviewed published profiles and accepts the exact proposed skill 0.3.0/min0.1.0/full0.4.0/probe-help tuple after design/content review of the complete Draft #37 fixture. That fixture is not a published skill or installation pin; final skill bytes require renewed exact review. A new runtime version is never automatically compatible by numerical ordering.