Skip to content

Commit 378eb3b

Browse files
authored
Merge pull request #8 from vitry/fix/cross-session-orphan-rescue
Fix cross-session orphan Rescue settlement
2 parents c753155 + 96de4bb commit 378eb3b

24 files changed

Lines changed: 1793 additions & 92 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ All notable changes follow Semantic Versioning.
1010
- Improved `$zcode:setup` guidance when the ZCode CLI has no model provider configured, including the distinction between Desktop and CLI settings and API-key providers that do not require OAuth.
1111
- Added foreground activity output, a 20-second heartbeat, and durable status previews for long-running ZCode work.
1212
- Added bounded foreground `SIGINT` and `SIGTERM` handling: the plugin cancels before session creation or sends `session/stop` only to the exact persisted ZCode session, while background jobs continue until completion or explicit cancellation with `$zcode:cancel`.
13+
- Added safe orphan settlement through best-effort `SessionEnd` handling and a reservation-time crash fallback, while preserving owner-only access and retaining the writable guard when liveness or remote-stop acknowledgement is uncertain.
1314
- Kept the package version at `0.1.0` for these Unreleased behavior changes.
1415

1516
## 0.1.0 - 2026-08-06

CONTEXT.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ _Avoid_: Command execution, request
2424
A persisted record of a Companion Run, including its ownership, lifecycle state, progress, and stored result.
2525
_Avoid_: Process, thread
2626

27+
**Orphaned Job**:
28+
A nonterminal Tracked Job whose exact worker-lifetime lease is no longer held, proving that its local executor has disappeared.
29+
_Avoid_: Stale job, dead session
30+
31+
**Lifecycle Maintenance Principal**:
32+
Internal authority derived only from a validated Orphaned Job's original owner and used to settle that job without transferring user-visible ownership.
33+
_Avoid_: Impersonated owner, adopted owner
34+
2735
**Review**:
2836
A read-only Companion Run that evaluates repository changes and returns findings without modifying the workspace.
2937
_Avoid_: Audit

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ To verify configuration, rerun `$zcode:setup`, then run `$zcode:rescue --fresh -
6363

6464
Every run is reserved as a durable, owner-scoped job. Installed plugin state lives beneath `$CODEX_HOME/plugins/data/zcode-<marketplace>/workspaces/<workspace-hash>/` with private permissions; prompts, results, session IDs, and logs are never written into the repository or plugin cache. `$zcode:status`, `$zcode:result`, and `$zcode:cancel` work across later turns in the same Codex session, while sibling sessions cannot adopt a job.
6565

66+
`SessionEnd` performs best-effort settlement of the ending session's writable Rescue. A claimed queued reservation remains unchanged while its worker lease is held. If the process exits before settlement completes, a later Rescue uses a reservation-time crash fallback and may settle a provably orphaned writable job; settlement does not transfer ownership, and only the original owner can access its result. During this reservation-time crash fallback, a held exact worker lease keeps the writable guard in place. An unacknowledged `session/stop` also keeps the writable guard in either settlement path. Other sessions can use `$zcode:status --all` only for redacted workspace inspection.
67+
6668
Foreground runs stream ZCode activity to the current terminal. If no new activity arrives, they emit a 20-second heartbeat so a long model or tool call remains visibly alive. The same safe activity is stored on the job; `$zcode:status <job-id>` shows its phase, last activity time, and recent progress previews. For example:
6769

6870
```text

README.zh-CN.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ Setup 会把以下 schema 写入 `$CODEX_HOME/plugins/data/zcode-<marketplace>/w
6363

6464
每次运行都会先建立持久、带 owner 的 job。已安装插件的状态保存在 `$CODEX_HOME/plugins/data/zcode-<marketplace>/workspaces/<workspace-hash>/`,使用私有权限;prompt、result、session ID 和日志都不会写进仓库或插件缓存。后续 turn 仍可使用 `$zcode:status``$zcode:result``$zcode:cancel`,但 sibling Codex session 无法接管任务。
6565

66+
`SessionEnd` 会对结束会话的可写 Rescue 执行 best-effort 结算。已 claim 的 queued reservation 在其 worker lease 仍被持有时保持不变。若进程在结算完成前退出,后续 Rescue 会执行预留时的崩溃回退,并可结算可证明的孤儿可写 job;结算不会转移 ownership,仍只有原 owner 能读取其结果。在这个预留时的崩溃回退中,仍被持有的精确 worker lease 会保留 writable guard。未确认的 `session/stop` 在两条结算路径中也会保留 writable guard。其他会话只能通过 `$zcode:status --all` 查看脱敏后的 workspace 信息。
67+
6668
前台运行会把 ZCode 活动流式显示在当前终端。如果没有新活动,则每 20 秒输出一次心跳,让耗时较长的模型请求或工具调用仍然可见。同一份安全活动也会持久化到 job;`$zcode:status <job-id>` 会显示阶段、最后活动时间和近期进度预览。例如:
6769

6870
```text
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
status: accepted
3+
---
4+
5+
# Settle orphaned jobs with an internal maintenance principal
6+
7+
When a writable Rescue's exact worker lease is free, internal lifecycle maintenance may derive the original broker owner ID only from that schema-validated durable job and use it to inspect, stop, and settle the job. This does not transfer ownership: public status, result, cancel, and resume selection remain bound to the original Codex session, and the maintenance path returns no old-job content to the session that triggered it.
8+
9+
## Considered Options
10+
11+
Same-owner-only recovery was rejected because workspace-global writable exclusion lets a dead owner permanently block every later session. SessionEnd-only settlement was rejected because crashes and force exits can skip the hook. Cross-session job adoption and force release were rejected because they would expose another session's work or permit two agents to mutate one workspace while the old remote session may still be active.
12+
13+
## Consequences
14+
15+
SessionEnd uses only an already healthy broker to read, stop, reread, and settle its exact active writable job before generic owner release; generic released mappings are not treated as stop acknowledgements. Writable reservation performs the crash fallback. Both paths use the existing per-job cancellation lock; claimed-job orphan detection additionally requires the exact worker lease to be free, while an unclaimed reservation retains the existing bounded worker-claim grace period. A remote stop that cannot be acknowledged retains the writable guard and an actionable bounded error rather than claiming a false terminal state.

0 commit comments

Comments
 (0)