pi-claude-supervisor 0.5.1 → 0.5.3

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.
@@ -1,5 +1,7 @@
1
1
  # Architecture
2
2
 
3
+ > The confirmed target is fully unattended local development with an independent remote/main boundary. Automatic mode implements the local editing, testing, repair, acceptance, Review and local-commit loop; unresolved work becomes a parked candidate. Legacy human/takeover APIs remain compatibility controls only. See [autonomy-target.md](autonomy-target.md).
4
+
3
5
  ## Control boundary
4
6
 
5
7
  Pi owns the `Supervisor`. The supervisor owns the task state machine, event log,
@@ -17,12 +19,16 @@ Supervisor -> Policy Gate -> WorkerAdapter -> child process
17
19
  ```
18
20
 
19
21
  The Worker cannot advance a task directly to `completed`. A clean worker exit
20
- moves the supervisor to `verifying`; only a successful verifier moves it to
21
- `completed`.
22
+ moves the supervisor to `verifying`; only successful acceptance, independent Review,
23
+ complete evidence and the configured local-commit boundary move it to `completed`.
24
+ An unresolvable automatic path moves it to `blocked`, never to a publishable result.
22
25
 
23
26
  The extension keeps a registry of independent task sessions. Each session has
24
27
  its own Supervisor, watchdog, state machine and Worker handle, while the event
25
- log is shared and protected by an inter-process lock. Concurrent active sessions must use non-overlapping canonical working
28
+ log is shared and protected by an inter-process lock. Once a task starts, the
29
+ local development loop is intended to run unattended: the Worker may edit, test,
30
+ repair and commit locally. Remote push and merge into `main`/an integration branch
31
+ are outside Worker authority and cross an independent boundary. Concurrent active sessions must use non-overlapping canonical working
26
32
  directories/worktrees; same-cwd and parent/child cwd starts are rejected before
27
33
  spawn, including concurrent starts, to prevent uncoordinated edits. Pending starts
28
34
  are also awaited during Pi shutdown.
@@ -40,8 +46,11 @@ graph, roles, dependencies, bounded concurrency and structured handoff
40
46
  artifacts. Child Workers must communicate through validated evidence and event
41
47
  references rather than another Worker's control channel. Each child is accepted
42
48
  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.
49
+ independent Review. Integration and conflict resolution remain separate from the local development
50
+ loop. The Worker cannot push remotely or merge into `main`/an integration branch;
51
+ the independent integration boundary may combine read-only review, CI and an
52
+ authorized integration action in a separate integration worktree. Rejection or
53
+ shutdown leaves the candidate local.
45
54
 
46
55
  Recovery and shutdown must be graph-aware: a parent with an unknown child state
47
56
  cannot complete, cancellation must propagate within a bounded budget, and Pi
@@ -59,6 +68,13 @@ recovery, not by relaxing the current cwd lease rule.
59
68
  - idempotency keys for messages;
60
69
  - no session-resume claim.
61
70
 
71
+ Worker capabilities distinguish two different properties. `persistentSession` means that a
72
+ Worker can remain usable across a Pi disconnect/restart and can be explicitly re-adopted;
73
+ `repairableSession` means that the current Supervisor can send another bounded turn after a
74
+ verification/review result. Claude JSONL is repairable while attached but does not claim
75
+ cross-restart resume. Tmux is both repairable and persistent. The Supervisor must never infer
76
+ one capability from the other.
77
+
62
78
  This is a control-boundary fixture and headless transport. It does not emulate a
63
79
  terminal. Manual compatibility mode remains `process-pipe`; automatic mode
64
80
  (`PI_CLAUDE_SUPERVISOR_MODE=auto`) defaults to Claude JSONL and uses the CLI
@@ -68,7 +84,8 @@ screen-based.
68
84
  A worker exit automatically triggers cleanup, and terminal status waits for
69
85
  that cleanup to be confirmed (or reports a cleanup error). On Linux the adapter
70
86
  uses cgroup v2 automatically when the current user cgroup is writable; the
71
- `required` mode fails startup if cgroup attachment is unavailable. Cgroup
87
+ `required` mode performs a preflight and fails before Claude starts if cgroup
88
+ attachment or cleanup is unavailable. Cgroup
72
89
  cleanup kills descendants even when they call `setsid()` or create another
73
90
  process group. Attachment occurs immediately after spawn, so a worker that
74
91
  forks before attachment remains a documented startup-window limitation.
@@ -78,9 +95,11 @@ process-group cleanup. That fallback is not recursive: `setsid()` descendants
78
95
  can escape, and PID reuse between leader exit and cleanup is a host-level
79
96
  limitation. Production deployments that require an atomic boundary should use a
80
97
  service-manager scope, Job Object, pidfd-aware reaper, or equivalent supervisor.
81
- The Pi host installs graceful `SIGTERM`/`SIGINT` handlers, but `SIGSTOP` and
82
- `SIGKILL` cannot be handled; no orphan guarantee is claimed for those host-fatal
83
- signals.
98
+ Pi owns graceful `SIGTERM`/`SIGINT` handling and invokes the extension's
99
+ `session_shutdown` hook. The extension does not install a second `process.exit()`
100
+ handler, avoiding races with Pi terminal restoration and other extensions. `SIGSTOP`
101
+ and `SIGKILL` cannot be handled; no orphan guarantee is claimed for those
102
+ host-fatal signals.
84
103
 
85
104
  ## tmux/PTY transport
86
105
 
@@ -109,9 +128,11 @@ For an owned initial turn, the adapter emits a synthetic `turn_completed` only
109
128
  after output activity and two stable input-prompt observations. Adopting an idle
110
129
  prompt remains inactive and emits no synthetic completion. This is a liveness
111
130
  signal, not proof that the task succeeded; the independent verifier remains
112
- mandatory. Interactive dialogs,
113
- trust prompts and ambiguous screens are not auto-approved. Human takeover sets a
114
- Supervisor gate that stops automatic messages until `resume-auto`.
131
+ mandatory. Interactive dialogs, trust prompts and ambiguous screens are interpreted by
132
+ the configured autonomy policy and recorded as evidence. An unresolved task is
133
+ parked or failed as a non-publishable candidate rather than requiring a human to
134
+ remain online. Human takeover remains an explicit kill/control path and stops
135
+ automatic messages until `resume-auto`.
115
136
 
116
137
  `/supervise adopt-tmux` is explicit and validates the pinned pane's cwd and
117
138
  process identity before attaching. Every later input, capture and signal uses
@@ -130,10 +151,12 @@ idle -> starting -> running -> waiting -> running -> verifying -> completed
130
151
  | | | |
131
152
  v v v v
132
153
  paused failed stopped idle
154
+ verifying -> stopped
133
155
  ```
134
156
 
135
- `stop` is available from `starting`, `running`, `waiting` and `paused`. Invalid
136
- transitions fail closed. Supervisor lifecycle operations and their state/event
157
+ `stop` is available from `starting`, `running`, `waiting`, `paused` and `verifying`.
158
+ A stop request from `verifying` is cleanup-authoritative and takes precedence over a
159
+ verification result that has not yet been finalized. Invalid transitions fail closed. Supervisor lifecycle operations and their state/event
137
160
  updates run through one serial queue, so concurrent `poll`, `send`, `stop`,
138
161
  watchdog and shutdown work cannot produce duplicate terminal transitions. If a
139
162
  lifecycle event append fails after the state transition, it remains pending and
@@ -153,7 +176,9 @@ unconfirmed lease left by a crashed Pi is intentionally retained. Ordinary
153
176
  recovery refuses it; an operator may use `recover --takeover` only when the old
154
177
  owner is dead, the Worker process group is gone, and the lease independently
155
178
  reads a real empty cgroup boundary for the old Worker. Missing or unverifiable
156
- Worker evidence still requires manual cleanup rather than unsafe reclamation.
179
+ Worker evidence retains the lease and parks the task rather than performing unsafe
180
+ reclamation; later recovery can inspect or clean it without requiring an operator to
181
+ be online.
157
182
  An explicitly adopted tmux session may hand off an existing lease only after
158
183
  its owner identity is no longer live and its canonical cwd, tmux session/socket,
159
184
  pane id, pane PID/start time, and pane command all match; ordinary starts
@@ -166,6 +191,21 @@ is alive; the extension periodically rechecks released sessions and removes the
166
191
  lease only after the pane is confirmed gone. If that check fails, the lease is
167
192
  retained rather than allowing a cwd overlap.
168
193
 
194
+ Before model or Worker execution, automatic starts validate a full existing Git
195
+ baseline, a non-bare worktree, a readable non-protected branch, the direct bare
196
+ `claude`/`claude.exe` command name, JSONL transport, runtime state/lease directories
197
+ and, when requested, the real writable cgroup-v2 boundary. The resolved Claude
198
+ executable is checked for an operator-owned, non-writable path and then pinned by
199
+ absolute path; `PI_CLAUDE_SUPERVISOR_TRUSTED_CLAUDE` can pin the expected identity. The initial repository HEAD is captured, and the repository boundary immediately
200
+ before the Worker adapter starts must report that exact same HEAD (recovery captures
201
+ and compares its current HEAD separately while retaining the persisted baseline).
202
+ The built-in process adapter invokes the same assertion through `preSpawnCheck`
203
+ after cgroup/executable setup and immediately before `spawn`; a failed preflight
204
+ is fail-closed and does not start Claude. Long acceptance commands and Reviewer
205
+ sessions share an abort signal with the Supervisor, so operator stop/shutdown
206
+ wins without waiting for a full check timeout. Progress hooks expose starting,
207
+ Worker heartbeat, acceptance, review, repair and candidate/decision phases in the Pi UI.
208
+
169
209
  Startup owns an `AbortController` and passes its signal to the adapter. A stop
170
210
  or shutdown request aborts the controller and calls the adapter's out-of-band
171
211
  startup cleanup without waiting behind the serialized start operation. Each
@@ -186,7 +226,9 @@ must be treated as sensitive because worker output may contain repository data.
186
226
  For Claude JSONL, the adapter tracks `activeRequests`, `lastInputAt` and
187
227
  `lastOutputAt`. A `result` record closes an active request; malformed output does
188
228
  not. JSONL sends are rejected while a request is active, and a valid terminal
189
- result moves the session to `waiting`; only then may the next turn be sent.
229
+ result moves the session to `waiting`; only then may the next turn be sent. A paused
230
+ Worker does not consume its no-output budget; resume establishes a fresh no-output
231
+ baseline while the cumulative wall-clock deadline remains active.
190
232
  Input writes are serialized with stop and are acknowledged through the stream
191
233
  write callback before their idempotency key is consumed. Writes have a bounded
192
234
  timeout, and `stop()` preempts a queued lifecycle operation by initiating adapter
@@ -199,20 +241,22 @@ unclean Pi restart, recovery is explicit: `/supervise recover [--takeover]
199
241
  <task-id>` restores
200
242
  the Decision Worker context and starts a new Claude Worker. It does not silently
201
243
  resume or duplicate a task. It does not poll to detect turn completion. A watchdog timer remains only as a deadlock safety
202
- fallback. Permission actions pass through `evaluatePermission` and can be
203
- approved or denied manually with `/supervise approve`; human escalation is sent
204
- to an outbound webhook when configured. If the Decision Worker API/model call
205
- fails, the system records `decision_worker_failed` and directly alerts the
206
- human operator; it does not attempt a second LLM fallback. Alert delivery is
207
- kept independent from event-log persistence so an audit write failure cannot
208
- suppress the alert.
244
+ fallback. Permission and other actions pass through the configured autonomy policy and
245
+ are recorded. The local development loop must not require synchronous human
246
+ approval for ordinary actions; a task that cannot safely produce a candidate is
247
+ parked or failed without granting remote/main authority. If the Decision Worker
248
+ API/model call fails, the system records `decision_worker_failed`, applies the
249
+ bounded retry/park policy and preserves the candidate evidence. Optional alert
250
+ delivery remains independent from event-log persistence, but notification is not
251
+ the control boundary.
209
252
 
210
253
  ## Acceptance, review and repair loop
211
254
 
212
255
  A task may provide a structured `TaskSpec` with `goal`, `scope`, `constraints`,
213
- `forbidden` and an ordered list of required or optional acceptance checks. A
256
+ `forbidden`, an ordered list of required or optional acceptance checks, and
257
+ `autonomy` (`unattended`, `requireLocalCommit`, `maxDecisionRetries`). A
214
258
  legacy plain-text task is normalized to a goal with the default `git diff
215
- --check` acceptance check. The verifier runs every configured check with argv,
259
+ --check` acceptance check and unattended defaults. The verifier runs every configured check with argv,
216
260
  bounded output and the same deterministic command policy; a Worker completion
217
261
  claim never substitutes for these results.
218
262
 
@@ -221,27 +265,40 @@ fresh read-only Reviewer session. The Reviewer receives the task specification,
221
265
  check results and bounded Worker completion evidence, but not the Decision Worker
222
266
  conversation or control channel. It can inspect only `read`, `grep`, `find` and `ls`, and must return
223
267
  `pass`, `revise` or `human` with bounded structured findings. Invalid Reviewer
224
- output or a Reviewer API failure is a human-required condition.
268
+ output, incomplete evidence or a Reviewer API failure must prevent a candidate
269
+ from crossing the remote/main boundary; the local system may retry, repair or
270
+ park it without requiring a human to be online.
225
271
 
226
272
  A `revise` result produces an audited repair round and sends a bounded corrective
227
- instruction to a still-live JSONL/tmux Worker. Checks and review then run again.
273
+ instruction to a still-live `repairableSession` Worker. Checks and review then run again.
228
274
  The repair budget defaults to three rounds; repeated findings and P0/P1 findings
229
- stop automation and escalate. A non-persistent Worker that has already exited
230
- cannot be silently recreated for repair; it remains failed/recoverable rather
231
- than replaying the original task.
275
+ stop automation and park a non-publishable candidate. A Worker that has already exited cannot be silently recreated
276
+ for repair; it remains failed/recoverable rather than replaying the original task. If a repair
277
+ or candidate branch cannot continue, a single idempotent terminalizer records
278
+ `verification_failed`, closes the Decision Worker and reports cleanup evidence; it never performs
279
+ a second `failed -> failed` transition.
280
+
281
+ Repository evidence is baseline-relative: the Supervisor records the initial HEAD,
282
+ then collects tracked committed/staged/unstaged changes, commit summaries after that
283
+ baseline and untracked regular files through bounded, component-safe, no-symlink reads.
284
+ Incomplete or truncated evidence is not sufficient for an independent `pass` verdict;
285
+ automatic mode parks a task when the required git baseline or local commit is unavailable. Acceptance
286
+ process output uses a bounded execution buffer before the smaller persisted evidence
287
+ limit, so a normal large test report is not misclassified as a failed command.
232
288
 
233
289
  ## Deliberate non-goals
234
290
 
235
- - automatic merge/deploy/release;
236
- - unauthenticated inbound webhook commands; outbound notifications do not grant
237
- permission and do not replace Pi human takeover;
238
- - treating an unknown Claude interactive question as safe to answer automatically;
239
- - bypassing Claude Code permissions;
291
+ - giving the Worker remote push or main/integration merge authority;
292
+ - unauthenticated inbound webhook commands; outbound notifications are optional,
293
+ do not grant permission and do not replace the remote/main independent boundary;
294
+ - treating an unknown Claude interactive question as safe without task evidence or configured authorization;
295
+ - bypassing the configured Claude Code/task permissions;
240
296
  - accepting model text as verification;
241
297
  - shell command interpolation;
242
- - automatic network denial or a fake domain allowlist. Network access follows
243
- Claude's own permission model and the command policy; suspicious download-to-
244
- shell patterns require human review rather than blanket network rejection;
298
+ - a host-level network sandbox for manual integrations. Automatic mode does not admit
299
+ arbitrary custom executables: its supported Worker is direct Claude, which requests a
300
+ fail-closed Claude Code Bash sandbox with no outbound domains; command policy and
301
+ credential filtering remain defense in depth;
245
302
  - Claude CLI multi-version compatibility in the current stability milestone;
246
- - OS sandbox, low-privilege execution and network isolation in the current
303
+ - full OS sandbox and low-privilege execution for custom Worker integrations in the current
247
304
  lifecycle milestone.
@@ -0,0 +1,212 @@
1
+ # 自动化稳定性与生命周期加固计划
2
+
3
+ > 计划状态:Phase A–D、真实可编辑 repair/reacceptance、exact-head 独立只读 Review、受保护发布和本地无人值守闭环已完成;远程 push/main merge 仍是独立边界。
4
+ > 基线:`v0.5.2` / `eefc7bc`
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 / parked non-publishable candidate
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 使用任务 baseline-relative diff、baseline 后 commit summaries、受限 untracked regular-file 内容、路径组件/symlink 门禁,并对 incomplete/truncated fail-closed。
145
+ - Acceptance 子进程、证据收集和 Reviewer 共享 abort signal;Pi UI 可看到 startup、Worker heartbeat、acceptance、review、repair 和 candidate/decision phases。
146
+ - 自动模式启动前检查运行目录、cwd、Worker 可执行文件、transport 依赖和 required cgroup;显式
147
+ `process-pipe` 和 `tmux` 不再进入自动模式,后者保留为手动 PTY transport。
148
+ - Reviewer/Decision Worker 只解析 assistant message 边界的最终文本;permission pending 会在自动响应后清除,候选状态不会被错误解除;扩大的 exec buffer 避免普通大测试报告被误判为命令失败。
149
+
150
+ 本轮已完成:
151
+
152
+ - 普通本地命令和权限不再进入同步人工审批;Decision Worker 的无效输出、API 错误、Reviewer 失败、P0/P1、重复 finding、证据不完整和预算耗尽会进入 `blocked` 候选。
153
+ - TaskSpec 和环境变量提供 unattended/local-commit/Decision retry 控制;Worker 可在本地修改、测试、修复并提交,push、merge、发布和破坏性边界仍硬拒绝并写入审计。
154
+ - replay、候选通知和 baseline-relative commit evidence 覆盖正常完成、修复、歧义和挂起;保持独立 Review、保护 CI、Release Please 和 provenance 发布边界。
155
+
156
+ ## 4. 实施顺序
157
+
158
+ ### Phase A:生命周期和能力模型(最高优先级)
159
+
160
+ 1. 增加 `repairableSession` capability。
161
+ 2. JSONL 在当前 Supervisor 生命周期内支持 repair,仍不声明跨重启恢复。
162
+ 3. 重构 repair/finalize/candidate 分支,保证终态转换和 Decision Worker closure 幂等。
163
+ 4. `verifying` 支持 stop/shutdown,保留 cleanup 不确定时的 lease。
164
+ 5. 增加真实非持久 adapter 和 stop-from-verifying 测试。
165
+
166
+ ### Phase B:watchdog 与证据完整性
167
+
168
+ 1. paused no-output 时钟暂停,resume 重建基准。
169
+ 2. 记录任务开始时的 HEAD;baseline-relative diff 覆盖 committed/staged/unstaged tracked 修改,并记录 baseline 后 commits。
170
+ 3. 安全收集 untracked regular-file evidence,防 symlink 和路径逃逸。
171
+ 4. evidence truncation/incomplete 生成不可发布候选并自动挂起,不能自动 `pass`。
172
+
173
+ ### Phase C:自动化协议和运行前保护
174
+
175
+ 1. Reviewer/Decision Worker 按 assistant message 边界解析最终响应,不拼接所有工具回合文字。
176
+ 2. 自动模式启动前执行 transport、Claude、cgroup、state/lease 目录 preflight。
177
+ 3. 区分 Worker heartbeat、验收、Reviewer、repair 阶段,并提高实时可观测性。
178
+ 4. 修复自动 permission 响应后的 stale pending request,并使本地决策不会依赖同步 human gate。
179
+ 5. 清理 Pi extension 自己安装的 signal handler,避免与 Pi `session_shutdown` 竞争。
180
+
181
+ ### Phase D:回归、真实演练和发布门禁
182
+
183
+ 1. 扩展 acceptance/replay/capability 矩阵。
184
+ 2. 在临时 worktree 中使用真实 Claude 做一次受控 repair/reacceptance;禁止触碰主仓库。
185
+ 3. 运行 `npm run check`、Pi/npm smoke、build 和真实只读 review。
186
+ 4. 只有独立 Reviewer `pass`、所有检查通过、cleanup/lease 证据完整后,才生成可交付本地候选;远程 push 和 main/integration merge 仍必须经过独立边界。
187
+
188
+ ## 5. 验收矩阵
189
+
190
+ | 场景 | 预期 |
191
+ |---|---|
192
+ | JSONL Worker alive + P2 revise | 发送一次 bounded repair,重新验收 |
193
+ | JSONL Worker 已退出 + 验收失败 | 不抛非法 transition,记录 `verification_failed`,生成不可发布候选 |
194
+ | Reviewer `human` + Worker alive | 不自动完成;按预算修复或挂起 `blocked` 候选,不要求人工在线 |
195
+ | Reviewer `human` + Worker 已退出 | 完成 cleanup、关闭 Decision Worker、保留 recoverable/parked record |
196
+ | `stop()` from `verifying` | `stopped`、cleanup confirmed 后释放 lease |
197
+ | shutdown from `verifying` | 不泄漏 Worker、Decision Worker 或 cwd lease |
198
+ | pause 超过 no-output timeout | 仍保持 paused |
199
+ | resume 后无输出 | 从 resume 时刻重新计算 timeout |
200
+ | staged + untracked 修改 | Reviewer evidence 包含两者 |
201
+ | evidence 截断/不可验证 | Reviewer 只能阻止 `pass`,候选自动挂起 |
202
+ | malformed/multi-message model output | 不误判为 pass/action |
203
+ | preflight 失败 | Claude 尚未启动前 fail-closed |
204
+
205
+ ## 6. 明确不改变的安全边界
206
+
207
+ - Reviewer 继续只允许 `read`、`grep`、`find`、`ls`。
208
+ - 验收命令继续使用 argv 和 `execFile`,不经过 shell 拼接。
209
+ - 不伪造 Claude `--resume`;`resumeSession` 仍然是明确能力声明。
210
+ - Worker 不得远程 push 或 merge 到 main/integration;发布仍走独立受保护 workflow。
211
+ - 不允许多 Worker 共享同一可写 worktree。
212
+ - P0/P1、重复 finding、超时、API 错误、无效输出和不完整证据继续 fail-closed,并自动生成不可发布/挂起候选,而不是要求同步人工响应。
@@ -0,0 +1,127 @@
1
+ # Confirmed autonomy target
2
+
3
+ > Owner-confirmed product requirement: local development is fully unattended; code entering a remote repository or the main/integration branch must cross an independent boundary.
4
+
5
+ This document is authoritative for the autonomy direction. Earlier planning text that treats a
6
+ human as a synchronous approval step for ordinary local development is historical conservative
7
+ baseline text and must not be used to add a new gate to the local development loop.
8
+
9
+ ## 1. Target operating model
10
+
11
+ Once a task has been started with its task specification, the local development loop may run
12
+ without a human watching it:
13
+
14
+ ```text
15
+ Worker edits and runs local commands
16
+ -> acceptance checks
17
+ -> independent Reviewer
18
+ -> bounded repair/reacceptance
19
+ -> local commit/candidate artifact
20
+ -> independent remote/main integration boundary
21
+ ```
22
+
23
+ The Supervisor may continue, answer, repair, test, review and commit locally. A human is not a
24
+ synchronous dependency for ordinary progress, routine ambiguity, or a normal failed test.
25
+
26
+ The system must still provide a kill switch, bounded execution, cleanup verification and a
27
+ complete audit trail. These are reliability and containment mechanisms, not requests for a human
28
+ to approve every development action. Automatic mode also records and revalidates a full existing
29
+ repository baseline, captures the startup HEAD and requires that exact HEAD again at
30
+ final pre-spawn, requires a non-protected branch and a pinned operator-owned direct Claude
31
+ JSONL Worker, and by default requires a local commit before a candidate is deliverable.
32
+
33
+ ## 2. Hard authority boundary
34
+
35
+ Automatic Worker supervision uses the structured JSONL transport; the interactive tmux transport
36
+ remains manual-only because it has no equivalent permission-response boundary. The Worker and local
37
+ automation do **not** receive authority or credentials for:
38
+
39
+ - pushing code to a remote repository;
40
+ - merging into `main` or another protected integration branch;
41
+ - starting automatic candidate work directly on a protected integration branch; repositories with a
42
+ branch use a non-protected local branch for unattended work;
43
+ - inheriting Git/GitHub/package credential helpers or explicitly selected remote credentials in
44
+ automatic mode.
45
+
46
+ A completed local task is a candidate until it passes the independent boundary. That boundary may
47
+ be a later read-only review, CI policy, a maintainer action, or an explicit shutdown/rejection.
48
+ The Worker must not be able to bypass it through a prompt, a local decision, or a model response.
49
+
50
+ This is the required authority boundary. Automatic mode admits only the bare direct Claude
51
+ command name and pins its operator-owned resolved executable because its fail-closed Claude Code
52
+ sandbox is part of the supported boundary; explicit paths and arbitrary custom executables must
53
+ use manual mode or an independently hardened integration. No additional
54
+ synchronous human-approval boundary should be invented for local editing, local tests, local
55
+ commits, or local repair unless the task owner explicitly configures one.
56
+
57
+ ## 3. Unattended decision behavior
58
+
59
+ The Decision Worker should resolve ordinary development decisions from the task specification,
60
+ repository evidence and configured task policy, and record its assumptions and actions. It should
61
+ not turn every uncertainty into an interactive human prompt.
62
+
63
+ If the system cannot safely reach a candidate, it may automatically retry within the configured
64
+ budget, mark the task blocked/failed, preserve the worktree and evidence, or park it for later
65
+ inspection. “Parked for later inspection” is not the same as requiring a human to be online before
66
+ other tasks can proceed.
67
+
68
+ A task that reaches `blocked`, `review_pending` or `candidate_failed` must not be pushed or merged.
69
+ The same rule applies to a ready local candidate until the independent remote/main boundary accepts it.
70
+ It may be resumed, repaired or discarded later without weakening the remote/main boundary.
71
+
72
+ ## 4. Acceptance and independent review
73
+
74
+ Acceptance and Reviewer remain automatic parts of the local loop:
75
+
76
+ - run all required checks;
77
+ - collect complete baseline-relative status, commit and untracked evidence;
78
+ - run the independent read-only Reviewer;
79
+ - apply bounded repair rounds;
80
+ - re-run acceptance and Review;
81
+ - produce a candidate with its evidence and assumptions.
82
+
83
+ Reviewer findings are first an automatic repair input. Exhausted budgets, incomplete evidence,
84
+ invalid output, duplicate findings or an unresolved finding produce a non-publishable
85
+ candidate/parked task; they do not by themselves require a synchronous takeover notification.
86
+
87
+ ## 5. Notifications and shutdown
88
+
89
+ Progress, assumptions, failures and candidate readiness must be recorded in the event log. Live
90
+ notifications are optional delivery policy, not the local development control protocol.
91
+
92
+ Immediate shutdown remains appropriate for technical containment failures such as an unverified
93
+ Worker cleanup boundary, corrupted control state or an explicit operator kill. The system should
94
+ stop or park safely and retain evidence; it must not silently grant remote or main-branch access.
95
+
96
+ ## 6. Current implementation
97
+
98
+ Automatic mode implements the local loop: policy decisions allow ordinary local development,
99
+ `AskUserQuestion` is converted to a denied interactive permission, the Decision Worker can
100
+ continue/redirect/answer/repair, acceptance and independent Review run without a human callback,
101
+ and unresolved situations become `blocked` candidates. The default task autonomy is unattended, requires a local commit, and permits two bounded
102
+ Decision Worker request retries. Automatic startup rejects non-Git/detached/bare/protected
103
+ repository states, malformed baselines, startup-HEAD races, non-JSONL transports and
104
+ non-Claude or untrusted executable identities before Worker startup. The resolved
105
+ executable identity is persisted with the Decision Worker recovery record and must match
106
+ again during recovery.
107
+
108
+ Legacy `humanRequired`, takeover and approval fields remain for compatibility and explicit operator
109
+ control. They are not entered by ordinary uncertainty, and a legacy approval object cannot override
110
+ the deterministic remote push/main merge denial. The existing independent Review and protected
111
+ CI/release paths remain the final external checks. Built-in automatic Claude workers request a fail-closed Claude Code Bash sandbox with no
112
+ outbound domains; automatic command policy and credential filtering remain defense in depth.
113
+ Full host-level sandboxing for custom Worker integrations is separate hardening work.
114
+
115
+ ## 7. Explicit non-goals of this target
116
+
117
+ This target does not authorize:
118
+
119
+ - remote push from the Worker;
120
+ - merge into `main` from the Worker;
121
+ - bypassing the independent integration boundary;
122
+ - silently treating incomplete evidence as success;
123
+ - claiming that a failed or parked task completed.
124
+
125
+ Coordinated multi-Worker scheduling, OS sandboxing and broader CLI compatibility remain separate
126
+ engineering milestones. They must not be used to add synchronous human approval to the local
127
+ development loop.