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 +30 -1
- package/README.cn.md +26 -9
- package/README.md +38 -18
- package/docs/architecture.md +71 -15
- package/docs/automation-hardening-plan.md +211 -0
- package/docs/engineering-plan.md +76 -3
- package/docs/recovery-review.md +24 -0
- package/docs/stability-matrix-2.1.270.md +32 -0
- package/docs/testing.md +90 -21
- package/package.json +1 -1
- package/src/cwd-lease.ts +95 -6
- package/src/decision-session-store.ts +343 -37
- package/src/decision-worker.ts +97 -13
- package/src/index.ts +189 -88
- package/src/redaction.ts +2 -1
- package/src/reviewer.ts +69 -16
- package/src/supervisor.ts +276 -45
- package/src/types.ts +6 -2
- package/src/verifier.ts +180 -22
- package/src/worker/process-adapter.ts +245 -47
- package/src/worker/tmux-adapter.ts +42 -3
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
|
-
>
|
|
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
|
|
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
|
-
|
|
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
|
|
107
|
-
|
|
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
|
-
> **
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
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
|
|
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.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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.
|
|
113
|
-
|
|
114
|
-
|
|
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.
|
|
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`.
|
package/docs/architecture.md
CHANGED
|
@@ -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
|
|
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
|
-
|
|
60
|
-
`
|
|
61
|
-
|
|
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 `
|
|
114
|
-
|
|
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
|
|
130
|
-
unconfirmed lease left by a crashed Pi is intentionally retained
|
|
131
|
-
operator
|
|
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
|
|
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
|
|
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
|
|
204
|
-
|
|
205
|
-
|
|
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。
|