Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,17 @@ error, `2` CLI usage error or an unresolved target reported as no-target, and
is an operational error (`1`); a missing saved thread during dry-run returns `2`.
Exit `0` on send does not confirm consumption or a reply.

### Bounded handoff watch (v1.3 candidate)

```sh
session-peer handoff status --correlation-id PREPARED_UUID --json
session-peer handoff watch --correlation-id PREPARED_UUID --watch-timeout 30 --interval-ms 1000 --json
```

Use an existing prepared UUID in the local owner's POSIX ledger. Status remains one snapshot; watch polls for 1..60 seconds (default 30) at 1000..5000 millisecond intervals (default 1000), accepting ASCII integers only and taking at most 61 samples. Watch emits an initial snapshot only if the budget permits a valid public projection, then changed public metadata/historyPhase only; it may emit no snapshots. JSON watch output uses the common schemaVersion 1 NDJSON envelope with operation watch and event snapshot or end, nesting the unchanged handoff object in snapshots; status JSON is unchanged. Timeout completion is exit 0 for watch completion only, not ACK or wait success; SIGINT is 130, history/output failure 1, invalid arguments 2. Missing/corrupt history or ledgerEpoch change stops watching. historyPhase is prepared, attempted, terminal, tombstone or quarantined; attempted means a durable attempt fence, not active execution.

Watch is read-only: no intents, waits, receipts, ledger writes, submission or wake. It adds no host/device/Windows route or Codex ACK. Limits are 16 KiB per frame and 256 KiB total with a 2 KiB end reserve. Real-fd stdout uses one owned isolated output-only Python helper that sees only bounded public serialized bytes, never changes shared descriptor flags and reads no ledger/native data. The parent enforces the original total budget including IPC, checks it before new ledger read/validation stages, and reserves 100 ms within it for final output and cleanup, so sampling may end earlier. It terminates and reaps its helper on timeout, SIGINT or error; uninterruptible OS operations prevent a hard-real-time guarantee. Partial or unacknowledged writes prohibit subsequent/end frames; consumers must reject truncated final NDJSON lines. Helper completion means OS write completion, not recipient consumption. Snapshots are not a lossless event stream, and local UTC observations do not prove ACK arrival order. See the [handoff runtime guide](handoff-runtime.md) for privacy, retained submission/ACK/wait meanings, read-only collector queries and the existing quarantine gate. Watch launches no native message process or collector. This candidate is not a published release. No payload/capability digests are exposed; public targetGeneration keeps its existing contract. The read-only ledger releases its shared lock after reading immutable snapshot bytes, before parsing and validation. Interrupted final output and cleanup are capped at 100 ms within the remaining total budget. Overall success requires a complete successful end frame and exit 0: cleanup may fail after that frame. A nonzero exit means overall failure, without promoting ACK, discarding independently valid submission facts or permitting retry.

### Updating

For package-managed installs, use the same manager that installed the command:
Expand Down
28 changes: 27 additions & 1 deletion docs/handoff-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,38 @@ For Codex correlation-only mode there is no pending explicit wait: SIGINT after

Verified structured SSH-self addresses keep the local message limit after normalization, including prepared-ID reuse and dry-run. Actual SSH destinations still have the 90,000-character command-argument limit. The protected metadata child additionally bounds each encoded JSON input/output frame to 1 MiB, including UTF-8 encoding and JSON escapes; this can reject otherwise valid local-sized input before submission.

The sender uses one original per-destination budget for metadata, harmless target probing, durable intent, one native effect and cleanup. Its protected metadata child preserves From/Reply and structured SSH-self normalization without renewing detection timeouts. A consumed intent refuses before any new probe. Required remote delivered/acknowledged waits refuse before SSH submission; best-effort requests report unsupported and mint no capability, remote ledger or collector. The effect rechecks the original Claude generation or canonical Codex home/writer context. An explicit Codex generation request is unsupported, not silently removed. Read-only status/wait never invokes SSH submission.
The sender uses one original per-destination budget for metadata, harmless target probing, durable intent, one native effect and cleanup. Its protected metadata child preserves From/Reply and structured SSH-self normalization without renewing detection timeouts. A consumed intent refuses before any new probe. Required remote delivered/acknowledged waits refuse before SSH submission; best-effort requests report unsupported and mint no capability, remote ledger or collector. The effect rechecks the original Claude generation or canonical Codex home/writer context. An explicit Codex generation request is unsupported, not silently removed. Status/wait never invokes SSH submission.

The probe records an old remote boot-bound monotonic anchor before replying. Only after strict response validation does the sender compute both remaining durations from one original clock sample. The remote effect uses that old anchor plus the remaining durations, including exactly five seconds within the total for cleanup; changed boot, future/expired anchor or malformed context refuses before native effect. No UTC or cross-host clock origins are compared. SSH shutdown does not prove cancellation of remote submission or consumption. Home mapping is the trusted remote handler's actual expanduser/resolve result, including owned symlink aliases; it is not a sender-verified inode, atomic incarnation or universal native storage guarantee.

The actual command must enter the closed private handler before config/source/SSH children start. Hidden options are not authentication: the receiver quotes every external body line once. Strict native profiles and request/target/home/context proofs preserve complete submission evidence after disconnect, timeout, diagnostic overflow or interruption, but never salvage an overflowing protocol stdout prefix. Connection-verified SSH identity and safe reply metadata remain separate from native facts. Private proofs, body, clock anchors and native context are not reflected into public outputs. Owned fake-SSH/source-stream/SQLite/queue fixtures verify this shipped slice, not actual remote hosts, native model consumption or installed reverse-SSH receipt bootstrap.

## Read one request's status

```sh
session-peer handoff status --correlation-id PREPARED_UUID
```

Use an existing prepared ID. The human-readable view shows one snapshot from the local owner's ledger: known submission facts, injection observation, ACK assurance and whether it was late, the latest stored wait, retry prohibition and supported next actions. The ACK and wait are shown separately: a late ACK does not rewrite an expired wait. The latest stored wait is not a complete wait history. ACK lateness is relative to the first wait, which may differ from the latest stored wait. Expired or quarantined history shows receipt and injection details as unavailable; a pending ACK without a supported keep-waiting action does not establish that further waiting is available. The JSON field schema and exit-code meanings are retained: a successful query returns 0 and a failed query returns 1; query success is not proof of receipt.

Status does not submit or wake, initialize or repair a ledger, mint receipt authority, or change waits. Its read-only ledger path does not save changes; in-memory retention may still show a tombstone with details unavailable. Explicit quarantine after restore sets submission to unknown and retains terminal waits on disk; the view shows the latest wait only when a valid public snapshot can be formed. Known lifecycle limitation: quarantine clears ACK detail, so a retained satisfied wait cannot satisfy the existing public validator; status refuses with invalid_handoff (exit 1). It does not repair or rewrite that history. This remains a #181 lifecycle gate. Existing status logic may query an already-running collector for clock evidence to determine next actions, but does not start one. The view does not read transcripts or message bodies, or expose raw capabilities.

The collector clock query is also read-only. An older collector may reject that query, so `keep_waiting` may be absent from the available next actions; status does not fall back to a writing query or restart the collector. The ledger snapshot and optional collector clock observation are separate, not an atomic combined observation. After a wait fails with history_unavailable and the collector clock becomes available again, status now preserves known submission and that failed wait without offering keep_waiting. Other wait-creation semantics are unchanged. A tombstone's latest-wait detail is shown as unavailable due to expired history, not as evidence that no wait was requested.

Status remains a single local query. The v1.3 candidate adds bounded local watching below; Windows handoff and Codex ACK remain unsupported. [#296](https://github.com/abruption/session-peer/issues/296) remains open pending review and merge, and this candidate is not a published release.

## Watch one request for a bounded period

```sh
session-peer handoff watch --correlation-id PREPARED_UUID --watch-timeout 30 --interval-ms 1000 --json
```

Watch polls only an existing local owner's POSIX ledger. The timeout accepts ASCII integers 1..60 seconds (default 30); the interval accepts ASCII integers 1000..5000 milliseconds (default 1000). It takes at most 61 samples and emits an initial snapshot only if the budget permits a valid public projection, then only changes to public metadata or historyPhase; it may emit no snapshots. It is not a lossless event stream. Known submission, ACK assurance, ACK lateness relative to the first wait, and the latest stored wait retain the status view's separate meanings. A local observation time in UTC is not ACK arrival time or ordering evidence. historyPhase is prepared, attempted, terminal, tombstone or quarantined; attempted means a durable attempt fence, not active execution.

JSON output is NDJSON using the common schemaVersion 1 envelope with watch-specific fields: operation is watch and event is snapshot or end. A snapshot nests the unchanged public handoff object; normal status JSON is unchanged. Deadline completion returns 0 and means only that watching ended, not that an ACK arrived or a wait succeeded. SIGINT returns 130; history or output failure returns 1. Missing/corrupt history or a changed ledgerEpoch stops the watch without inventing a handoff or epoch. The existing quarantined satisfied-wait refusal remains the #181 lifecycle gate described above.

Each output frame is limited to 16 KiB, total output to 256 KiB, with 2 KiB reserved for the end frame. For real-fd stdout, one owned isolated output-only Python helper receives only bounded public serialized bytes and never changes shared descriptor flags or reads ledger/native data. The parent enforces the original total deadline, including helper IPC, and checks it before new ledger read or validation stages. It reserves 100 ms within that total for final output and cleanup, so sampling may end earlier. The parent terminates and reaps its helper on timeout, SIGINT or error. Uninterruptible OS operations still prevent a hard-real-time guarantee. After a partial or unacknowledged write, no subsequent frame, including end, is sent; consumers must reject a truncated final NDJSON line. A helper completion reply proves OS write completion only, not recipient consumption. Watch does not read native bodies or transcripts or expose capabilities or payload/capability digests; public targetGeneration remains unchanged under its existing contract. It does not create intents, waits or receipts, submit, wake, initialize, repair or write the ledger. It may use the existing read-only clock query on an already-running local collector, never launch a collector or native message process. Host/device routing and Windows watching are unsupported; no Codex ACK support is added. The read-only ledger releases its shared lock after reading immutable snapshot bytes, before parsing and validation. Interrupted final output and cleanup are capped at 100 ms within the remaining total budget. Overall success requires a complete successful end frame and exit 0: cleanup may fail after that frame. A nonzero exit means overall failure, without promoting ACK, discarding independently valid submission facts or permitting retry.

## Evidence, privacy and timing

A complete inbox write is submission, not consumption or turn completion. Only an authenticated correlated receipt, or a separate explicit operator attestation with provable ordering, produces acknowledged. An early receipt during native write/drain is saved as bounded unclassified evidence. It is classified only after independently known submission and original-clock ordering; classification cannot invent a receipt or backdate an ACK. A committed ACK survives collector restart; unused authority expires conservatively on restart. Duplicates still require original token-hash proof, including after expiry. Manual confirmation never rewrites an old wait.
Expand Down
4 changes: 2 additions & 2 deletions docs/i18n-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
"docs/ai-assistant-guide.md": "9624ec96b92fb688fae3be3c94cb3564c77985091be451fbf1f056a1af9b9d9c",
"docs/antigravity.md": "f2b5122a0ce6e6fb5bd4d8ed50ce2c8bd2dc9c98ec8ce328405cf7c2cd24229d",
"docs/architecture/agent-transports.md": "01a4e3942e87acb476595859f2e37f9653ea1e3bc19ebc68dced1adf20a6966f",
"docs/cli-reference.md": "43e9e3232d8975d3e6b17065f40f43ff590184cac503d2c8fdae05142668334b",
"docs/cli-reference.md": "af1d3524bad9020dd4cb9ecb6b23db7207686ee956ae2215c21d01d79551ede1",
"docs/compatibility-v1.md": "7e551aad37a369faff7e370728376763efb54c2c9e9cfd8556a0c19aabc3d103",
"docs/contracts/handoff-v1.md": "46a953010c49214bd8f969412f26713384fbe09a0ba78ebf0fef75da40d0d889",
"docs/diagnostics.md": "0949e53612eeab2cd7c7bbda498e14d71e9cb1b1189aa7722030cb8702cd8481",
"docs/download-metrics.md": "ded60e7b84d14ce779f8d639516b4e390358a06ef76b677dc9c1f997fb626a9f",
"docs/handoff-runtime.md": "b2ecd28949dabd8b0c63371a1d3e8d732a2dcd07ce972e5737241732b4991dfb",
"docs/handoff-runtime.md": "80800abed85d4af1a2e2001448b39f6cf02ae12912cde0fde90c6863124e5220",
"docs/mcp.md": "a64010cdd63f2cde6a0052450bed2d05faa0f19c93c76143cb0d585d36f8ff62",
"docs/multi-home-list.md": "f8d8e3adf727e4842ea7c4d45e8f6523b11d0513bf2509d4d1204645875e6489",
"docs/paired-devices.md": "44c70f99d3f8f8ff9f6f86d128913ada69f4cc4e794c724cfc214f7f8ea78f20",
Expand Down
11 changes: 11 additions & 0 deletions docs/ja/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,17 @@ SSH `User` 設定に誘導し、再試行されることはありません。
dry-run 中の保存済みスレッドの欠落は `2` を返します。send における終了コード `0` は、
消費または返信を確認するものではありません。

### 時間制限付き handoff 監視(v1.3 候補)

```sh
session-peer handoff status --correlation-id PREPARED_UUID --json
session-peer handoff watch --correlation-id PREPARED_UUID --watch-timeout 30 --interval-ms 1000 --json
```

ローカル所有者の POSIX 記録にある準備済み UUID を使用します。状態照会は単一スナップショットのままです。監視は 1..60 秒(既定 30)、1000..5000 ミリ秒間隔(既定 1000)で最大 61 回照会し、値は ASCII 整数のみ受け付けます。予算内に有効な公開結果を構成できる場合だけ最初のスナップショットを出力し、その後は公開メタデータ/historyPhase の変更だけを出力します。スナップショットが一つも出ない場合があります。JSON 監視出力は operation watch、event snapshot または end の共通 schemaVersion 1 NDJSON エンベロープを使い、スナップショットに従来の handoff オブジェクトを内包します。status JSON は変わりません。期限完了の終了コード 0 は監視の完了だけを表し、ACK や待機成功ではありません。SIGINT は 130、履歴・出力の失敗は 1、不正な引数は 2 です。履歴の欠落・破損や ledgerEpoch の変更で監視を終了します。 historyPhase は prepared、attempted、terminal、tombstone または quarantined です。attempted は永続的な試行フェンスを意味し、実行中を意味しません。

監視は読み取り専用で、intent・待機・受領確認の作成、記録の書き込み、提出や起動を行いません。ホスト・機器・Windows 経路や Codex ACK は追加しません。フレームは 16 KiB、総量は 256 KiB に制限し、終了用に 2 KiB を予約します。実ファイル記述子の標準出力には専用の分離された Python 出力補助プロセスを一つ使用します。サイズ制限付きの直列化された公開バイトだけを受け取り、共有記述子フラグを変えず、記録やネイティブデータも読みません。親は IPC を含め元の総予算を適用し、新しい記録読み取り・検証段階の前にも期限を確認します。総時間内に最終出力と終了処理用の 100 ms を確保するため、照会は早めに終わる場合があります。タイムアウト、SIGINT、エラー時には補助プロセスを終了・回収しますが、中断不能な OS 操作の厳密リアルタイム保証ではありません。部分書き込みや完了応答がない場合は終了を含む後続フレームを禁止し、利用側は切れた最後の NDJSON 行を拒否する必要があります。補助プロセスの完了は OS 書き込み完了であり、受信者の消費ではありません。スナップショットは全イベントを欠落なく伝えるストリームではなく、ローカル UTC 観測は ACK 到着順序を証明しません。プライバシー、保持する提出・ACK・待機の意味、読み取り専用コレクター照会、従来の隔離の制約は [handoff ランタイムガイド](handoff-runtime.md)を参照してください。監視はネイティブメッセージプロセスやコレクターを起動しません。この候補は公開リリースではありません。 ペイロード・権限のダイジェストは公開せず、公開 targetGeneration の既存契約は維持します。 読み取り専用記録は不変のスナップショットバイトを読み終えた後、解析・検証の前に共有ロックを解放します。 中断時の最終出力と終了処理は、残りの総予算内で最大 100 ms に制限します。 全体の成功には完全な成功終了フレームと終了コード 0 の両方が必要です。そのフレームの後に終了処理が失敗する場合があります。非ゼロの終了は全体の失敗であり、ACK を格上げしたり、独立して有効な提出事実を捨てたり、再試行を許可したりするものではありません。

<a id="updating"></a>
### 更新

Expand Down
Loading
Loading