pi-claude-supervisor 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented here.
4
4
 
5
+ ## [0.5.2](https://github.com/btnalit/pi-claude-supervisor/compare/v0.5.1...v0.5.2) (2026-09-14)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * harden automatic review lifecycle ([fefa5c5](https://github.com/btnalit/pi-claude-supervisor/commit/fefa5c5b39fd2411b5e82df384074983252263ca))
11
+
12
+ ## [0.5.1](https://github.com/btnalit/pi-claude-supervisor/compare/v0.5.0...v0.5.1) (2026-09-14)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * harden single-worker recovery lifecycle ([14c01ec](https://github.com/btnalit/pi-claude-supervisor/commit/14c01ec610b1210eda1ea2d60269fad25b8b3575))
18
+
5
19
  ## [0.5.0](https://github.com/btnalit/pi-claude-supervisor/compare/v0.4.1...v0.5.0) (2026-09-14)
6
20
 
7
21
 
@@ -11,12 +25,27 @@ All notable changes to this project will be documented here.
11
25
 
12
26
  ## [Unreleased]
13
27
 
28
+ ### Hardening implemented in working tree (not yet released)
29
+
30
+ - Split JSONL in-process `repairableSession` from cross-restart `persistentSession` and eliminate duplicate terminal transitions.
31
+ - Make `verifying` stop/shutdown cleanup authoritative, with abortable acceptance and Reviewer operations.
32
+ - Pause and rebase the no-output watchdog clock across pause/resume while retaining the cumulative deadline.
33
+ - Include staged, unstaged and bounded untracked evidence in independent Review, with symlink/path safety and fail-closed completeness.
34
+ - Harden assistant-message output framing, startup/runtime preflight, permission gates, bounded acceptance buffers and lifecycle progress reporting.
35
+
36
+ ### Remaining hardening gate
37
+
38
+ - Complete the exact-head independent read-only Review; the real editable Claude `2.1.270` repair/reacceptance drill and cleanup/lease evidence are recorded in `docs/automation-hardening-plan.md`.
39
+
14
40
  ### Added
15
41
 
16
42
  - Structured task specifications with Goal, scope, constraints, forbidden actions and multiple argv-based acceptance checks.
17
43
  - Independent read-only Reviewer results with bounded findings and automatic repair rounds.
18
44
  - JSONL malformed-record handling and duplicate result/permission suppression fixtures.
19
45
  - Active JSONL request shutdown coverage and deterministic acceptance/review tests.
46
+ - Durable Decision Worker recovery claims with stale-owner reconciliation and explicit fail-closed takeover.
47
+ - Identity-bound tmux handoff cleanup and recovery/lease lifecycle coverage.
48
+ - Claude Code 2.1.270 bounded stability matrix evidence in `docs/stability-matrix-2.1.270.md`.
20
49
 
21
50
  ## [0.4.1](https://github.com/btnalit/pi-claude-supervisor/compare/v0.4.0...v0.4.1) (2026-09-14)
22
51
 
@@ -85,7 +114,7 @@ All notable changes to this project will be documented here.
85
114
  - Historical Claude CLI 2.1.268 permission allow/deny and SIGTERM/SIGINT transport spike evidence; current release validation uses Claude CLI 2.1.270.
86
115
  - Event-driven JSONL `control_request`/`result`/exit events, permission responses, persistent Pi Decision Worker automation, bounded duplicate/turn handling, and outbound human-intervention webhooks.
87
116
  - Long-task defaults are now 100 automatic turns, 4 hours wall time and 20 minutes without output; Decision Worker API failures alert human operators directly instead of attempting an LLM fallback.
88
- - Automatic Decision Worker sessions now persist as Pi JSONL with a 0600 task registry. Unclean Pi restarts expose explicit `/supervise recover <task-id>` recovery; Claude work is not silently duplicated.
117
+ - Automatic Decision Worker sessions now persist as Pi JSONL with a 0600 task registry. Unclean Pi restarts expose explicit `/supervise recover [--takeover] <task-id>` recovery; Claude work is not silently duplicated.
89
118
  - Local tests and package-content checks.
90
119
 
91
120
  ### Limitations
package/README.cn.md CHANGED
@@ -8,7 +8,12 @@
8
8
 
9
9
  用于 Pi 的 Claude Code Worker 监督扩展。MVP 中 Pi 负责生命周期、状态机、策略门和独立验收;Worker 只是被显式启动的子进程。
10
10
 
11
- > 当前默认 transport 是无额外依赖的 process pipe,不是 PTY。已新增可选 Claude JSONL framing,并通过基础 prompt、多轮和 resume Spike。当前优先保证生命周期、进程组清理、恢复和独立验收;低权限用户、OS sandbox 与网络隔离不作为当前主线,按明确授权和宿主机策略运行,后续再做安全加固。
11
+ > `v0.5.1` 已发布为单 Worker recovery 基线。默认手动 transport 是无额外依赖的
12
+ > process pipe,不是 PTY;自动模式只使用 Claude JSONL 或 tmux。当前工作树已实现
13
+ > repairable/persistent 能力拆分、可取消验收/Reviewer、证据完整性门禁、启动前
14
+ > preflight 和阶段进度通知;真实 Claude Code `2.1.270` 允许编辑的
15
+ > repair/reacceptance 演练已在隔离临时 worktree 通过,剩余发布门禁是 exact-head
16
+ > 独立只读 Review。低权限用户、OS sandbox 与网络隔离继续延期。
12
17
 
13
18
  ## 关键安全边界
14
19
 
@@ -16,12 +21,13 @@
16
21
  - 不经过 shell 启动子进程。
17
22
  - Worker 只继承最小环境;凭据必须由调用方显式传入。
18
23
  - 破坏性命令和绕过权限的 Worker 参数默认拒绝;需要复核的启动命令会请求用户批准,不会一律拒绝。
19
- - Linux 上优先使用可写的 cgroup v2 清理后代进程,包括 `setsid()` 后代;不可用时回退到进程组清理。需要强制失败闭环时,embedding 集成可使用 `cgroupMode: "required"`。
24
+ - Linux 上优先使用可写的 cgroup v2 清理后代进程,包括 `setsid()` 后代;不可用时回退到进程组清理。需要强制失败闭环时,embedding 集成可使用 `cgroupMode: "required"`,并会在 Claude 启动前执行 preflight。
20
25
  - 普通联网查询不因联网本身被拒绝;下载后直接交给 shell 等高风险模式仍需人工复核。
21
26
  - Worker 声称完成只会进入 `verifying`,不能作为成功证据。
22
27
  - 默认独立验收命令为 `git diff --check`。
23
28
  - 扩展运行时不执行 merge、deploy、release 或 publish;仓库 Release 只会在维护者合并 Release Please PR 且 CI 门禁全部通过后自动发布。
24
- - 默认 4 小时总时限、20 分钟无输出 watchdog 超时即停止 Worker,适合长程开发任务;嵌入调用方可将对应选项设为 `0` 关闭。
29
+ - 默认 4 小时总时限、20 分钟无输出 watchdog 超时即停止 Worker;paused 期间不消耗无输出预算,resume 会重建基准但不会重置总时限。嵌入调用方可将对应选项设为 `0` 关闭。
30
+ - 验收命令、仓库证据收集和独立 Reviewer 共用 abort signal,人工 stop/shutdown 不必等待完整超时。
25
31
 
26
32
  ## 安装和使用
27
33
 
@@ -54,7 +60,7 @@ Claude CLI `2.1.270` 运行,跨版本兼容性不在本轮范围内。
54
60
  /supervise start --spec ./task.json
55
61
  /supervise poll
56
62
  /supervise sessions
57
- /supervise recover <task-id>
63
+ /supervise recover [--takeover] <task-id>
58
64
  /supervise stop human requested stop
59
65
  /supervise verify
60
66
  /supervise approve <task-id> allow|deny [request-id]
@@ -94,17 +100,23 @@ Claude CLI `2.1.270` 运行,跨版本兼容性不在本轮范围内。
94
100
 
95
101
  当前 webhook 是出站通知,不直接接受批准命令;批准或接管仍通过 Pi。
96
102
  自动模式会将 Decision Worker 会话持久化到状态目录。Pi 非正常重启后,`/supervise sessions`
97
- 会显示 `recoverable` 任务;显式执行 `/supervise recover <task-id>` 会恢复 Decision Worker 上下文并
98
- 重新启动 Claude Worker,不会静默恢复或重复执行任务。
103
+ 会显示 `recoverable` 任务;显式执行 `/supervise recover [--takeover] <task-id>` 会恢复 Decision Worker 上下文并
104
+ 重新启动 Claude Worker,不会静默恢复或重复执行任务。旧 Pi 进程已退出且租约确认旧 Worker
105
+ 进程组已消失且 cgroup 仍是真实、可读取的空边界时,才可显式添加 `--takeover`;缺失、仍存活或无法确认的 Worker 会被拒绝。持久 tmux
106
+ Worker 应使用 `adopt-tmux`,而不是 takeover。
99
107
  自动模式下,Decision Worker 可以安全拒绝 `AskUserQuestion`,让 Claude 将问题转成普通文本,
100
108
  再根据任务和仓库证据自动回答;无法确定时才升级人工。如需微信内闭环,需要另建带签名验证、
101
109
  一次性 action token 和重放保护的入站 callback 服务。
102
110
 
103
- 近期自动化目标是先稳定完成“多命令验收—独立只读 Reviewer—结构化修复轮次—再次验收”闭环。
111
+ `v0.5.0` 已完成并发布“多命令验收—独立只读 Reviewer—结构化修复轮次—再次验收”闭环。
104
112
  任务可通过 API 或 JSON spec 提供 `goal`、`scope`、`constraints`、`forbidden` 和多个
105
113
  `acceptance` 命令;旧的纯文本任务继续使用默认 `git diff --check`。Reviewer 只能使用
106
- `read`、`grep`、`find`、`ls`,不会修改工作树或批准权限。当前稳定性验证固定针对 Claude Code
107
- `2.1.270`,暂不把多版本兼容、sandbox、低权限和网络隔离作为本阶段门禁。
114
+ `read`、`grep`、`find`、`ls`,不会修改工作树或批准权限。当前加固要求 HEAD-relative
115
+ tracked diff 和受限 untracked evidence 完整,P0/P1 或重复 finding 必须人工处理;自动模式
116
+ 拒绝显式 process-pipe,并在模型执行前检查目录、可执行文件、依赖和 cgroup。剩余门禁是
117
+ 固定 Claude Code `2.1.270` 隔离 worktree 的真实 repair/reacceptance 已通过,当前只剩
118
+ exact-head 独立只读 Review;协同多 Worker 属于后续独立开发阶段,暂不把 sandbox、低权限
119
+ 和网络隔离作为本阶段门禁。
108
120
 
109
121
  ### tmux/PTY 交互模式
110
122
 
@@ -150,6 +162,11 @@ PTY 屏幕文字不是 Claude JSONL。权限/信任对话框和无法确定的 T
150
162
  /supervise send <task-id> continue after checking the test failure
151
163
  ```
152
164
 
165
+ 当前支持的是**独立任务会话并行**,不是共享工作树的协同多 Worker。后续多 Worker
166
+ 开发任务会引入 parent/child 任务图、依赖、并发上限、结构化 handoff、汇总验收和
167
+ 跨进程恢复,但不会放宽“一个 worktree 一个写入者”的边界,也不会自动 merge 或 publish。
168
+ 该阶段应安排在固定 Claude `2.1.270` 稳定性统计和单 Worker recovery 语义完成之后。
169
+
153
170
  Pull Request 必须通过聚合的 `CI / Quality gate`。Release Please 根据 Conventional Commits 创建版本 PR;维护者合并后,Release workflow 会针对精确 tag commit 重新验证,并通过受保护的 `npm` environment 使用 npm provenance 发布。
154
171
 
155
172
  详细内容见 [engineering-plan.md](docs/engineering-plan.md)、[independent-review.md](docs/independent-review.md)、[architecture.md](docs/architecture.md)、[testing.md](docs/testing.md) 和 [releasing.md](docs/releasing.md)。
package/README.md CHANGED
@@ -10,11 +10,15 @@ A policy-gated [Pi](https://pi.dev) extension for supervising a Claude Code work
10
10
  The MVP keeps Pi in control of lifecycle, state, policy and verification while the
11
11
  worker remains an explicitly started child process.
12
12
 
13
- > **MVP status:** the default transport is dependency-free process pipes, not PTY.
14
- > An opt-in Claude JSONL framing mode has passed basic prompt, multi-turn and
15
- > resume fixtures. The current priority is signal, shutdown, process-group and
16
- > recovery validation; OS sandbox, low-privilege execution and network isolation
17
- > are deferred hardening items and are not required by the current MVP plan.
13
+ > **Release status:** `v0.5.1` is the released single-Worker recovery baseline. The
14
+ > default manual transport is dependency-free process pipes, not PTY. Automatic
15
+ > supervision uses Claude JSONL or tmux, and the current working-tree hardening
16
+ > adds repairable-vs-persistent capabilities, cancellable verification, evidence
17
+ > completeness gates, startup preflight and phase progress reporting. A real
18
+ > edit-capable Claude Code `2.1.270` repair/reacceptance drill has passed in an
19
+ > isolated temporary worktree; the exact-head independent review is the remaining
20
+ > release gate. OS sandbox, low-privilege execution and network isolation remain
21
+ > deferred.
18
22
 
19
23
  ## Safety boundary
20
24
 
@@ -27,7 +31,8 @@ worker remains an explicitly started child process.
27
31
  - Verification is an independent host command (default: `git diff --check`).
28
32
  - The extension never performs merge, deploy, release, or publish at runtime. Repository releases are automated only after a maintainer merges a Release Please PR and the full CI gate passes.
29
33
  - A 4-hour wall-clock and 20-minute no-output watchdog stop a worker by default for long development tasks; embedding callers can set either to `0` to disable.
30
- - On Linux, the adapter automatically uses a writable cgroup v2 for descendant cleanup, including `setsid()` descendants; it falls back to process-group cleanup when unavailable. Use the adapter's `cgroupMode: "required"` for a fail-closed integration.
34
+ - On Linux, the adapter automatically uses a writable cgroup v2 for descendant cleanup, including `setsid()` descendants; it falls back to process-group cleanup when unavailable. Use `cgroupMode: "required"` for a fail-closed integration; required mode is preflighted before Claude starts.
35
+ - Acceptance commands, repository evidence collection and independent Review share an abort signal, so operator stop/shutdown does not wait for a full command or model timeout.
31
36
  - Events are append-only JSONL records in `~/.pi/agent/claude-supervisor/events.jsonl`.
32
37
 
33
38
  ## Install
@@ -59,7 +64,7 @@ export PI_CLAUDE_SUPERVISOR_WORKER=claude
59
64
  /supervise start inspect the current repository and report what should be changed
60
65
  /supervise start --spec ./task.json
61
66
  /supervise sessions
62
- /supervise recover <task-id>
67
+ /supervise recover [--takeover] <task-id>
63
68
  /supervise poll
64
69
  /supervise poll all
65
70
  /supervise send continue with read-only inspection
@@ -100,24 +105,39 @@ The supervisor allows only one active JSONL request per session: poll until its
100
105
  yet exposed by the adapter. Multiple independent task sessions can run
101
106
  concurrently when they use different canonical working directories or
102
107
  worktrees; same-directory starts are rejected even when concurrent, and
103
- `/supervise sessions` lists the sessions. Unattended use still requires the
104
- remaining lifecycle, signal and recovery checks. Host permissions and network
105
- access follow explicit caller authorization and host policy; there is no
106
- automatic merge, deploy, release or publish.
107
-
108
- The near-term automation milestone adds a structured acceptance pipeline:
108
+ `/supervise sessions` lists the sessions. This is independent-session
109
+ parallelism, not coordinated multi-worker collaboration. A future multi-worker
110
+ milestone will add explicit parent/child task graphs, dependencies, bounded
111
+ scheduling, structured handoffs, aggregate acceptance and graph-aware recovery;
112
+ it will not relax the one-writer-per-worktree rule or enable automatic
113
+ merge/publish. Unattended use still requires the remaining lifecycle, signal and
114
+ recovery checks. Host permissions and network access follow explicit caller
115
+ authorization and host policy; there is no automatic merge, deploy, release or
116
+ publish.
117
+
118
+ The `v0.5.0` automation milestone adds a structured acceptance pipeline:
109
119
  multiple argv-based checks, an independent read-only Reviewer, bounded structured
110
120
  findings and repair rounds. Legacy text tasks keep the default `git diff --check`.
111
121
  The Reviewer only has `read`, `grep`, `find` and `ls`; it cannot edit files or grant
112
- permissions. Stability evidence is pinned to Claude Code `2.1.270`; CLI
113
- multi-version compatibility, sandboxing, low-privilege execution and network
114
- isolation are not part of this milestone.
122
+ permissions. The current hardening also requires complete HEAD-relative tracked and
123
+ bounded untracked evidence, rejects P0/P1 or repeated findings, and only repairs a
124
+ live `repairableSession` Worker. Automatic mode rejects explicit `process-pipe` and
125
+ preflights runtime prerequisites. The real pinned Claude Code `2.1.270` disposable repair/reacceptance run has passed;
126
+ the remaining gate is the exact-head independent review.
127
+ Coordinated multi-worker scheduling is a later milestone; CI uses deterministic fake
128
+ Workers/replay fixtures, and real multi-worker Claude tests remain authenticated
129
+ manual Spikes. CLI multi-version compatibility, sandboxing, low-privilege execution
130
+ and network isolation are not part of this milestone.
115
131
 
116
132
  Automatic mode persists the Pi Decision Worker session under the configured state
117
133
  directory. After an unclean Pi restart, `/supervise sessions` lists recoverable
118
- tasks; `/supervise recover <task-id>` explicitly restores the Decision Worker
134
+ tasks; `/supervise recover [--takeover] <task-id>` explicitly restores the Decision Worker
119
135
  context and starts a new Claude Worker. It never silently resumes or duplicates
120
- work. The adapter intentionally does not inherit arbitrary host environment variables.
136
+ work. If the old Pi owner is dead, add `--takeover` only after the lease proves
137
+ the old Worker's process group is gone and its cgroup is a real, readable empty
138
+ boundary; missing or unverifiable Worker evidence is refused.
139
+ For a persistent tmux Worker, use explicit `adopt-tmux` instead of takeover.
140
+ The adapter intentionally does not inherit arbitrary host environment variables.
121
141
  Pass credentials through an explicit `WorkerStartInput.env` in an embedding
122
142
  integration. For the built-in command, opt in to named variables, for example
123
143
  `PI_CLAUDE_SUPERVISOR_WORKER_ENV=ANTHROPIC_API_KEY`.
@@ -27,6 +27,28 @@ directories/worktrees; same-cwd and parent/child cwd starts are rejected before
27
27
  spawn, including concurrent starts, to prevent uncoordinated edits. Pending starts
28
28
  are also awaited during Pi shutdown.
29
29
 
30
+ ### Multi-worker boundary and roadmap
31
+
32
+ The current implementation supports multiple **independent** task sessions, not
33
+ coordinated shared-worktree editing. Every active session must own a
34
+ non-overlapping canonical cwd/worktree and has an isolated Supervisor,
35
+ watchdog, Worker handle and acceptance/review loop. The shared EventLog is only
36
+ an audit stream; it is not a collaboration or authorization channel.
37
+
38
+ A future multi-worker scheduler must introduce an explicit parent/child task
39
+ graph, roles, dependencies, bounded concurrency and structured handoff
40
+ artifacts. Child Workers must communicate through validated evidence and event
41
+ references rather than another Worker's control channel. Each child is accepted
42
+ independently; the parent can complete only after aggregate acceptance and
43
+ independent Review. Integration, conflict resolution, merge and publication
44
+ remain explicit human-controlled operations in a separate integration worktree.
45
+
46
+ Recovery and shutdown must be graph-aware: a parent with an unknown child state
47
+ cannot complete, cancellation must propagate within a bounded budget, and Pi
48
+ shutdown must leave every child either cleanup-verified or explicitly
49
+ recoverable. This work is scheduled after single-worker stability and session
50
+ recovery, not by relaxing the current cwd lease rule.
51
+
30
52
  ## MVP transport
31
53
 
32
54
  `ProcessWorkerAdapter` uses `node:child_process.spawn` with:
@@ -37,6 +59,13 @@ are also awaited during Pi shutdown.
37
59
  - idempotency keys for messages;
38
60
  - no session-resume claim.
39
61
 
62
+ Worker capabilities distinguish two different properties. `persistentSession` means that a
63
+ Worker can remain usable across a Pi disconnect/restart and can be explicitly re-adopted;
64
+ `repairableSession` means that the current Supervisor can send another bounded turn after a
65
+ verification/review result. Claude JSONL is repairable while attached but does not claim
66
+ cross-restart resume. Tmux is both repairable and persistent. The Supervisor must never infer
67
+ one capability from the other.
68
+
40
69
  This is a control-boundary fixture and headless transport. It does not emulate a
41
70
  terminal. Manual compatibility mode remains `process-pipe`; automatic mode
42
71
  (`PI_CLAUDE_SUPERVISOR_MODE=auto`) defaults to Claude JSONL and uses the CLI
@@ -46,7 +75,8 @@ screen-based.
46
75
  A worker exit automatically triggers cleanup, and terminal status waits for
47
76
  that cleanup to be confirmed (or reports a cleanup error). On Linux the adapter
48
77
  uses cgroup v2 automatically when the current user cgroup is writable; the
49
- `required` mode fails startup if cgroup attachment is unavailable. Cgroup
78
+ `required` mode performs a preflight and fails before Claude starts if cgroup
79
+ attachment or cleanup is unavailable. Cgroup
50
80
  cleanup kills descendants even when they call `setsid()` or create another
51
81
  process group. Attachment occurs immediately after spawn, so a worker that
52
82
  forks before attachment remains a documented startup-window limitation.
@@ -56,9 +86,11 @@ process-group cleanup. That fallback is not recursive: `setsid()` descendants
56
86
  can escape, and PID reuse between leader exit and cleanup is a host-level
57
87
  limitation. Production deployments that require an atomic boundary should use a
58
88
  service-manager scope, Job Object, pidfd-aware reaper, or equivalent supervisor.
59
- The Pi host installs graceful `SIGTERM`/`SIGINT` handlers, but `SIGSTOP` and
60
- `SIGKILL` cannot be handled; no orphan guarantee is claimed for those host-fatal
61
- signals.
89
+ Pi owns graceful `SIGTERM`/`SIGINT` handling and invokes the extension's
90
+ `session_shutdown` hook. The extension does not install a second `process.exit()`
91
+ handler, avoiding races with Pi terminal restoration and other extensions. `SIGSTOP`
92
+ and `SIGKILL` cannot be handled; no orphan guarantee is claimed for those
93
+ host-fatal signals.
62
94
 
63
95
  ## tmux/PTY transport
64
96
 
@@ -108,10 +140,12 @@ idle -> starting -> running -> waiting -> running -> verifying -> completed
108
140
  | | | |
109
141
  v v v v
110
142
  paused failed stopped idle
143
+ verifying -> stopped
111
144
  ```
112
145
 
113
- `stop` is available from `starting`, `running`, `waiting` and `paused`. Invalid
114
- transitions fail closed. Supervisor lifecycle operations and their state/event
146
+ `stop` is available from `starting`, `running`, `waiting`, `paused` and `verifying`.
147
+ A stop request from `verifying` is cleanup-authoritative and takes precedence over a
148
+ verification result that has not yet been finalized. Invalid transitions fail closed. Supervisor lifecycle operations and their state/event
115
149
  updates run through one serial queue, so concurrent `poll`, `send`, `stop`,
116
150
  watchdog and shutdown work cannot produce duplicate terminal transitions. If a
117
151
  lifecycle event append fails after the state transition, it remains pending and
@@ -126,9 +160,12 @@ registry before spawning Claude. The default registry is
126
160
  may point all Pi processes at an alternate shared directory. Canonical paths
127
161
  conflict with both their parents and descendants, and the registry lock
128
162
  serializes acquisition across independent Pi processes. A lease is released
129
- only after the adapter confirms the worker and its descendant cleanup; an
130
- unconfirmed lease left by a crashed Pi is intentionally retained and requires
131
- operator verification/manual cleanup rather than unsafe automatic reclamation.
163
+ only after the adapter confirms the worker and its descendant cleanup. An
164
+ unconfirmed lease left by a crashed Pi is intentionally retained. Ordinary
165
+ recovery refuses it; an operator may use `recover --takeover` only when the old
166
+ owner is dead, the Worker process group is gone, and the lease independently
167
+ reads a real empty cgroup boundary for the old Worker. Missing or unverifiable
168
+ Worker evidence still requires manual cleanup rather than unsafe reclamation.
132
169
  An explicitly adopted tmux session may hand off an existing lease only after
133
170
  its owner identity is no longer live and its canonical cwd, tmux session/socket,
134
171
  pane id, pane PID/start time, and pane command all match; ordinary starts
@@ -141,6 +178,14 @@ is alive; the extension periodically rechecks released sessions and removes the
141
178
  lease only after the pane is confirmed gone. If that check fails, the lease is
142
179
  retained rather than allowing a cwd overlap.
143
180
 
181
+ Before model or Worker execution, automatic starts preflight the validated cwd,
182
+ worker executable, transport dependencies, runtime state/lease directories and,
183
+ when requested, the real writable cgroup-v2 boundary. A failed preflight is
184
+ fail-closed and does not start Claude. Long acceptance commands and Reviewer
185
+ sessions share an abort signal with the Supervisor, so operator stop/shutdown
186
+ wins without waiting for a full check timeout. Progress hooks expose starting,
187
+ Worker heartbeat, acceptance, review, repair and human-gate phases in the Pi UI.
188
+
144
189
  Startup owns an `AbortController` and passes its signal to the adapter. A stop
145
190
  or shutdown request aborts the controller and calls the adapter's out-of-band
146
191
  startup cleanup without waiting behind the serialized start operation. Each
@@ -161,7 +206,9 @@ must be treated as sensitive because worker output may contain repository data.
161
206
  For Claude JSONL, the adapter tracks `activeRequests`, `lastInputAt` and
162
207
  `lastOutputAt`. A `result` record closes an active request; malformed output does
163
208
  not. JSONL sends are rejected while a request is active, and a valid terminal
164
- result moves the session to `waiting`; only then may the next turn be sent.
209
+ result moves the session to `waiting`; only then may the next turn be sent. A paused
210
+ Worker does not consume its no-output budget; resume establishes a fresh no-output
211
+ baseline while the cumulative wall-clock deadline remains active.
165
212
  Input writes are serialized with stop and are acknowledged through the stream
166
213
  write callback before their idempotency key is consumed. Writes have a bounded
167
214
  timeout, and `stop()` preempts a queued lifecycle operation by initiating adapter
@@ -170,7 +217,8 @@ duplicate turns. The adapter also exposes event subscriptions for `result`,
170
217
  `control_request`, permission requests and process exit. Automatic mode routes
171
218
  those events to a persistent, read-only Pi Decision Worker; its Pi session JSONL
172
219
  and task mapping are persisted under the supervisor state directory. After an
173
- unclean Pi restart, recovery is explicit: `/supervise recover <task-id>` restores
220
+ unclean Pi restart, recovery is explicit: `/supervise recover [--takeover]
221
+ <task-id>` restores
174
222
  the Decision Worker context and starts a new Claude Worker. It does not silently
175
223
  resume or duplicate a task. It does not poll to detect turn completion. A watchdog timer remains only as a deadlock safety
176
224
  fallback. Permission actions pass through `evaluatePermission` and can be
@@ -198,11 +246,19 @@ conversation or control channel. It can inspect only `read`, `grep`, `find` and
198
246
  output or a Reviewer API failure is a human-required condition.
199
247
 
200
248
  A `revise` result produces an audited repair round and sends a bounded corrective
201
- instruction to a still-live JSONL/tmux Worker. Checks and review then run again.
249
+ instruction to a still-live `repairableSession` Worker. Checks and review then run again.
202
250
  The repair budget defaults to three rounds; repeated findings and P0/P1 findings
203
- stop automation and escalate. A non-persistent Worker that has already exited
204
- cannot be silently recreated for repair; it remains failed/recoverable rather
205
- than replaying the original task.
251
+ stop automation and escalate. A Worker that has already exited cannot be silently recreated
252
+ for repair; it remains failed/recoverable rather than replaying the original task. If a repair
253
+ or human-review branch cannot continue, a single idempotent terminalizer records
254
+ `verification_failed`, closes the Decision Worker and reports cleanup evidence; it never performs
255
+ a second `failed -> failed` transition.
256
+
257
+ Repository evidence is HEAD-relative: tracked staged and unstaged changes are collected together,
258
+ and untracked regular files are included through bounded, component-safe, no-symlink reads. Incomplete or
259
+ truncated evidence is not sufficient for an independent `pass` verdict. Acceptance
260
+ process output uses a bounded execution buffer before the smaller persisted evidence
261
+ limit, so a normal large test report is not misclassified as a failed command.
206
262
 
207
263
  ## Deliberate non-goals
208
264
 
@@ -0,0 +1,211 @@
1
+ # 自动化稳定性与生命周期加固计划
2
+
3
+ > 计划状态:Phase A–D 已在当前工作树实现;真实可编辑 repair/reacceptance 已通过,待 exact-head 独立只读 Review 作为发布前最后门禁
4
+ > 基线:`v0.5.1` / `26443f0`
5
+ > 真实验证:Claude Code `2.1.270`
6
+ > 记录日期:2026-09-14
7
+
8
+ ## 1. 实际演练结论
9
+
10
+ 本轮真实演练完成了以下链路:
11
+
12
+ ```text
13
+ 真实 Claude Code Worker
14
+ -> Decision Worker 判断完成
15
+ -> argv/execFile 验收
16
+ -> 独立只读 Reviewer
17
+ -> P1/P2 阻塞发现
18
+ -> fail-closed / human intervention
19
+ -> Worker、Pi、lease 清理
20
+ ```
21
+
22
+ 六项验收全部通过:
23
+
24
+ - `git diff --check`
25
+ - `npm run check`
26
+ - `npm run check:workflows`
27
+ - `npm run test:pi`
28
+ - `npm run test:install`
29
+ - `npm run build`
30
+
31
+ 本次任务明确使用只读 Claude 权限并设置 `maxRepairRounds=0`,因此没有进入真实的
32
+ repair/reacceptance。现有 replay 测试覆盖了模拟 repair,但没有覆盖真实
33
+ `ProcessWorkerAdapter` 能力矩阵。
34
+
35
+ 完整事件证据位于本机临时目录:
36
+
37
+ ```text
38
+ /tmp/pi-claude-supervisor-real-state-3/events.jsonl
39
+ ```
40
+
41
+ ### 1.1 Phase D:真实可编辑 repair/reacceptance
42
+
43
+ 在不触碰主仓库的临时 Git 工作树中,使用真实 Claude Code `2.1.270` 和
44
+ `ProcessWorkerAdapter(mode=claude-jsonl, cgroup=off)` 完成了一个 bounded repair:
45
+
46
+ - task:`cd39ed51-6003-4aad-8518-ebd6ab5de7e7`;`maxRepairRounds=1`。
47
+ - Worker 首轮只创建 `add.mjs`;第一次 `node check.mjs` 按 drill 设计失败并给出
48
+ `repair required`。
49
+ - Supervisor 发送 repair round 1;真实 Worker 创建 `add.test.mjs`。
50
+ - 第二轮验收通过,独立只读 Reviewer 返回 `pass`,没有 human intervention。
51
+ - 最终状态为 `completed`;Worker 状态为 `running=false`、`exitReason=stopped`、
52
+ `processGroupCleaned=true`;Decision Worker closure 为 `cleanupConfirmed=true`。
53
+ - UI progress 覆盖 `starting -> worker -> acceptance(failed) -> repair -> worker ->
54
+ acceptance(passed) -> review -> completed`,并包含 Worker heartbeat。
55
+ - Cwd lease 在 Worker 启动后记录了 PID/start time;结束后 `leaseAfterRelease=[]`,
56
+ 未留下 lease。
57
+
58
+ 事件序列为:
59
+
60
+ ```text
61
+ task_started -> worker_started -> worker_output -> worker_waiting -> decision_made
62
+ -> acceptance_started -> acceptance_check_finished -> acceptance_result
63
+ -> repair_requested -> worker_message_sent -> worker_output -> worker_waiting
64
+ -> decision_made -> acceptance_started -> acceptance_check_finished -> acceptance_result
65
+ -> review_started -> review_result -> review_finished -> verification_passed
66
+ ```
67
+
68
+ 证据归档位置(均在主仓库之外):
69
+
70
+ ```text
71
+ /tmp/pi-cs-repair4-runtime-X6MLob/events.jsonl
72
+ /tmp/pi-cs-repair4-runtime-X6MLob/decision/
73
+ /tmp/pi-cs-repair4-runtime-X6MLob/leases/ # 结束时为空
74
+ /tmp/pi-cs-repair4.log
75
+ ```
76
+
77
+ ## 2. 正式发现与复现结果
78
+
79
+ ### P1-1:非持久 JSONL repair 会触发非法状态转换
80
+
81
+ `ProcessWorkerAdapter` 的 JSONL Worker 没有 `persistentSession`,Supervisor 在验收前停止
82
+ Worker;验收失败或 Reviewer `revise` 后,`requestRepair()` 先执行
83
+ `verifying -> failed`,调用方又执行 `failed -> failed`。
84
+
85
+ 已用非持久 JSONL fake adapter 复现:
86
+
87
+ ```text
88
+ InvalidTransitionError: Invalid supervisor transition: failed -> failed
89
+ ```
90
+
91
+ 根本修复不是把 JSONL 伪装成可跨重启恢复,而是把 Worker 能力拆成:
92
+
93
+ - `persistentSession`:能否在 Pi 重启/断开后继续存在;
94
+ - `repairableSession`:当前 Supervisor 生命周期内能否继续发送 repair turn。
95
+
96
+ JSONL 可以是 `repairableSession=true`、`persistentSession=false`;tmux 两者都可以为
97
+ `true`。终结和人工介入必须通过单一幂等路径完成,不能由 `requestRepair()` 和
98
+ `finalizeVerification()` 重复转换终态。
99
+
100
+ ### P1-2:`verifying` 状态下 stop 无效
101
+
102
+ 当前 `stop()` 和 `#stopInternal()` 没有处理 `verifying`。已复现:
103
+
104
+ ```text
105
+ before stop: verifying
106
+ adapter.stop calls: 0
107
+ after stop: verifying
108
+ ```
109
+
110
+ 结果可能是 Decision Worker 未关闭、Pi shutdown 不释放 cwd lease、后续任务被错误地
111
+ 判定为 cwd 冲突。
112
+
113
+ 修复要求:`verifying` 支持 stop、cleanup、`worker_stopped` 事件、Decision Worker
114
+ 关闭、closure callback 和 lease 回收;并确保人工 stop 优先于正在完成的 verification。
115
+
116
+ ### P2-1:paused Worker 仍触发 no-output watchdog
117
+
118
+ `SIGSTOP` 后 Worker 本来就不会产生输出,但 watchdog 仍把 `paused` 纳入 no-output
119
+ 计算。使用 50ms timeout 暂停 1.25s 已复现 Worker 被停止。
120
+
121
+ 修复要求:暂停期间暂停 no-output 时钟;resume 时重建基准;wall-clock deadline 仍然
122
+ 保持累计,不允许通过 pause 绕过总时限。
123
+
124
+ ### P2-2:Reviewer diff evidence 遗漏 staged 和 untracked 内容
125
+
126
+ 当前使用 unstaged-only 的 `git diff`。已复现:
127
+
128
+ ```text
129
+ status: M tracked.txt / ?? new.txt
130
+ diff: (none)
131
+ ```
132
+
133
+ 修复要求:tracked 文件使用 `git diff HEAD`,额外安全读取 untracked regular files,
134
+ 拒绝 symlink,限制单文件和总证据大小;证据不完整或被截断时 Reviewer 不得返回
135
+ `pass`。
136
+
137
+ ## 3.1 当前实现状态
138
+
139
+ 已完成并有确定性回归覆盖:
140
+
141
+ - `repairableSession` 与 `persistentSession` 已分离;JSONL 仅支持当前进程内 repair,tmux 才声明持久恢复。
142
+ - `verifying` 的 stop/shutdown 使用统一 cleanup/finalize 路径,人工 stop 优先于验收结果,cleanup 不确定时保留恢复记录。
143
+ - paused Worker 不消耗 no-output watchdog;resume 重建 no-output 基准,但不重置累计 deadline。
144
+ - Reviewer evidence 使用 `git diff HEAD`、受限 untracked regular-file 内容、路径组件/symlink 门禁,并对 incomplete/truncated fail-closed。
145
+ - Acceptance 子进程、证据收集和 Reviewer 共享 abort signal;Pi UI 可看到 startup、Worker heartbeat、acceptance、review、repair 和 human phases。
146
+ - 自动模式启动前检查运行目录、cwd、Worker/tmux 可执行文件、transport 依赖和 required cgroup;显式 `process-pipe` 不再进入自动模式。
147
+ - Reviewer/Decision Worker 只解析 assistant message 边界的最终文本;permission pending 会在自动和人工响应后清除,独立 human gate 不会被错误解除;扩大的 exec buffer 避免普通大测试报告被误判为命令失败。
148
+
149
+ 仍待完成:
150
+
151
+ - 对当前 exact head 执行最后一次独立只读 Review;该 Review 必须不编辑、不运行命令、不发送 Worker 输入、不提交或发布。
152
+ - Review 通过且完整质量门禁再次通过后,才允许人工决定是否提交、合并或发布;本轮仍不自动 release/publish。
153
+
154
+ ## 4. 实施顺序
155
+
156
+ ### Phase A:生命周期和能力模型(最高优先级)
157
+
158
+ 1. 增加 `repairableSession` capability。
159
+ 2. JSONL 在当前 Supervisor 生命周期内支持 repair,仍不声明跨重启恢复。
160
+ 3. 重构 repair/finalize/human 分支,保证终态转换和 Decision Worker closure 幂等。
161
+ 4. `verifying` 支持 stop/shutdown,保留 cleanup 不确定时的 lease。
162
+ 5. 增加真实非持久 adapter 和 stop-from-verifying 测试。
163
+
164
+ ### Phase B:watchdog 与证据完整性
165
+
166
+ 1. paused no-output 时钟暂停,resume 重建基准。
167
+ 2. HEAD-relative diff 覆盖 staged/unstaged tracked 修改。
168
+ 3. 安全收集 untracked regular-file evidence,防 symlink 和路径逃逸。
169
+ 4. evidence truncation/incomplete 强制 human。
170
+
171
+ ### Phase C:自动化协议和运行前保护
172
+
173
+ 1. Reviewer/Decision Worker 按 assistant message 边界解析最终响应,不拼接所有工具回合文字。
174
+ 2. 自动模式启动前执行 transport、Claude、cgroup、state/lease 目录 preflight。
175
+ 3. 区分 Worker heartbeat、验收、Reviewer、repair 阶段,并提高实时可观测性。
176
+ 4. 修复自动 permission 响应后的 stale pending request 和错误清除 human gate。
177
+ 5. 清理 Pi extension 自己安装的 signal handler,避免与 Pi `session_shutdown` 竞争。
178
+
179
+ ### Phase D:回归、真实演练和发布门禁
180
+
181
+ 1. 扩展 acceptance/replay/capability 矩阵。
182
+ 2. 在临时 worktree 中使用真实 Claude 做一次受控 repair/reacceptance;禁止触碰主仓库。
183
+ 3. 运行 `npm run check`、Pi/npm smoke、build 和真实只读 review。
184
+ 4. 只有独立 Reviewer `pass`、所有检查通过、cleanup/lease 证据完整后,才允许人工
185
+ 决定是否提交、合并或发布。
186
+
187
+ ## 5. 验收矩阵
188
+
189
+ | 场景 | 预期 |
190
+ |---|---|
191
+ | JSONL Worker alive + P2 revise | 发送一次 bounded repair,重新验收 |
192
+ | JSONL Worker 已退出 + 验收失败 | 不抛非法 transition,记录 `verification_failed`,升级人工 |
193
+ | Reviewer `human` + Worker alive | 保持 Worker 可人工接管,不自动继续 |
194
+ | Reviewer `human` + Worker 已退出 | 完成 cleanup、关闭 Decision Worker、保留 recoverable record |
195
+ | `stop()` from `verifying` | `stopped`、cleanup confirmed 后释放 lease |
196
+ | shutdown from `verifying` | 不泄漏 Worker、Decision Worker 或 cwd lease |
197
+ | pause 超过 no-output timeout | 仍保持 paused |
198
+ | resume 后无输出 | 从 resume 时刻重新计算 timeout |
199
+ | staged + untracked 修改 | Reviewer evidence 包含两者 |
200
+ | evidence 截断/不可验证 | Reviewer 只能返回 human |
201
+ | malformed/multi-message model output | 不误判为 pass/action |
202
+ | preflight 失败 | Claude 尚未启动前 fail-closed |
203
+
204
+ ## 6. 明确不改变的安全边界
205
+
206
+ - Reviewer 继续只允许 `read`、`grep`、`find`、`ls`。
207
+ - 验收命令继续使用 argv 和 `execFile`,不经过 shell 拼接。
208
+ - 不伪造 Claude `--resume`;`resumeSession` 仍然是明确能力声明。
209
+ - 不自动 merge、deploy、release 或 publish。
210
+ - 不允许多 Worker 共享同一可写 worktree。
211
+ - P0/P1、重复 finding、超时、API 错误、无效输出和不完整证据继续 fail-closed。