delegation-layer 0.1.30 → 0.1.37

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.
@@ -4,7 +4,7 @@ Managed workspaces are an optional Delegate feature for tasks that need a
4
4
  retained Git worktree. Ordinary delegation, including ordinary work in a
5
5
  non-Git directory, is unchanged. Git is required only when a request creates
6
6
  or attaches to a managed workspace (`--isolation git-worktree` or
7
- `--workspace ID`).
7
+ `--workspace ID`), or creates or removes a [Git checkpoint](#git-checkpoints).
8
8
 
9
9
  This reference is self-contained guidance for using managed workspaces.
10
10
 
@@ -48,7 +48,10 @@ delegate --root ROOT dispatch --isolation git-worktree --cwd ABS \
48
48
  `--base` resolves to a commit; the default is `HEAD`. Dirty and untracked
49
49
  source changes are observed but excluded from the managed checkout. The
50
50
  provider runs in the retained detached worktree, with the source-relative
51
- working subdirectory preserved. A request that attaches to an existing
51
+ working subdirectory preserved. The source may itself be a managed checkout,
52
+ such as a sibling in the same workspace container. After provisioning, the new
53
+ workspace depends on the shared repository, not on the source checkout, so
54
+ removing that source does not block capture, release, or forget. A request that attaches to an existing
52
55
  workspace uses its recorded checkout and source identity:
53
56
 
54
57
  ```text
@@ -117,6 +120,36 @@ facts. Normal capture covers the initial committed base through the captured
117
120
  tree, including worker commits and added untracked files that belong to the
118
121
  Git-content view. It uses a private temporary Git index and records an owned
119
122
  base pin plus tree/snapshot reference.
123
+
124
+ Each provider turn also records its actual starting tree, `HEAD`, and index
125
+ identity just before launch, so Lead edits made between turns belong to the
126
+ next turn's start. The index identity covers every entry's mode, object, stage
127
+ (including conflicts), path, assume-unchanged or skip-worktree flag, and
128
+ intent-to-add (`git add -N`) state, but not stat caches or `HEAD`. A task capture's `candidate.turn` compares that start with the
129
+ final capture: `content`, `head`, and `index` are `changed` or `unchanged`, and
130
+ `baseline_ref` keeps the start tree. This is observed evidence, not
131
+ enforcement: a read-only turn that commits or edits is reported, not rejected,
132
+ and writes reverted before capture or ignored files are not observed.
133
+ `status: "unavailable"` with `reason_code: "baseline_unavailable"` means no
134
+ baseline exists, for example for a turn started before this evidence existed;
135
+ treat it as unknown, never as unchanged. A capture without `candidate.turn`,
136
+ such as one recorded before per-turn evidence existed or a release or exec
137
+ recapture, is also unknown, never unchanged. A failed baseline refuses the
138
+ provider launch for that task.
139
+
140
+ An observed turn adds `turn-diff.patch` and `turn-name-status.z`, which diff
141
+ that turn's start tree to the final tree with the same bounds as the
142
+ cumulative artifacts. Use them to see which files the current turn touched;
143
+ they exclude earlier turns and Lead edits made before its launch. Use the
144
+ cumulative `diff.patch` and `name-status.z`, from the workspace base, to land
145
+ or review the whole delegated result. Report a read-only turn whose `content`,
146
+ `head`, or `index` is `changed` as a mutation, even when its turn file list is
147
+ empty, and report an unavailable turn as unknown.
148
+
149
+ `status` and `collect` for a task report that task's own capture, even after a
150
+ later turn has used the workspace. The workspace `state` still reflects the
151
+ resource now, so a sealed earlier capture does not make a busy, released, or
152
+ forgotten workspace usable. `workspace status` reports the latest use.
120
153
  Optional binary patch and name-status artifacts are bounded to 8 MiB each; an
121
154
  overflow is omitted, never silently truncated. Verification diagnostics retain
122
155
  an 8 MiB prefix per stream, mark truncation explicitly, and continue draining
@@ -136,6 +169,98 @@ expired provider scope is never renewed or reused. If capture
136
169
  is unavailable after closure, the closure evidence can still preserve a valid
137
170
  continuation decision; it does not rewrite the provider outcome.
138
171
 
172
+ ## Git checkpoints
173
+
174
+ A checkpoint records the current Git content of one checkout as a synthetic
175
+ commit pinned by the owned ref `refs/delegate/ROOT_ID/checkpoints/CHECKPOINT_ID`.
176
+ It does not move `HEAD`, create or switch a branch, or change the real index.
177
+ The commit's tree is the full current Git-content view, built through a private
178
+ index with the capture algorithm above, so the same exclusions apply. Its single
179
+ parent is the `HEAD` the helper observed. Author, committer, and timestamp are
180
+ fixed Delegate values; trailers carry the checkpoint ID and request digest.
181
+ This is unrelated to a provider continuation `mode: "checkpoint"`.
182
+
183
+ ```text
184
+ delegate --root ROOT checkpoint create --cwd ABS [--budget DURATION] --json
185
+ delegate --root ROOT checkpoint create --workspace ID [--budget DURATION] --json
186
+ delegate --root ROOT checkpoint status CHECKPOINT_ID [--operation OP] [--watch DURATION] --json
187
+ delegate --root ROOT checkpoint list [--after CHECKPOINT_ID] [--limit 1..50] --json
188
+ delegate --root ROOT checkpoint remove CHECKPOINT_ID [--budget DURATION] --json
189
+ ```
190
+
191
+ - `create` names exactly one target: `--cwd` for an absolute ordinary Git
192
+ checkout directory, or `--workspace` for a managed workspace ID. Nothing is
193
+ inferred from the current directory. Select a managed checkout only by
194
+ `--workspace ID`; never pass its path as `--cwd`.
195
+ - `status` and `remove` take the checkpoint ID `C` from the create response.
196
+ `list` takes no target and runs no Git command.
197
+ - Only `create --cwd` may initialize a new state root. Every other checkpoint
198
+ command needs the existing root and fails with exit `1` without creating it.
199
+ Use one state root for every command about the same checkpoint.
200
+ - `--budget` is the finite helper budget (default 5m). It is persisted with the
201
+ operation and does not renew any provider task budget. `--watch` exists only
202
+ on `status`.
203
+
204
+ A managed checkpoint is a workspace use. It needs the workspace's use owner and
205
+ holds that workspace's usage reservation while its helper runs, so a concurrent
206
+ turn or `workspace exec` on that workspace gets the normal busy response. It
207
+ closes without content capture. Create it while the workspace is retained.
208
+
209
+ Delegate does not restore, apply, merge, or land a checkpoint into any checkout.
210
+ It does not control nested delegation budgets or lifetimes either. A fork from
211
+ a checkpoint is an ordinary new managed dispatch with its own task budget.
212
+
213
+ ### Checkpoint before fork
214
+
215
+ Use this sequence to start an isolated worker from current uncommitted work.
216
+ Save every response and exit code once; follow
217
+ [checkpoint results](result-handling.md#checkpoint-results) for each one.
218
+
219
+ 1. Run `checkpoint create` with one target. Exit `3` with `checkpoint.id` and
220
+ `next_action: "watch_checkpoint"` means the request was accepted. It does
221
+ not mean the content was captured. Save that exact ID as `C`.
222
+ 2. Run `checkpoint status C --watch 30s --json` until the exit is not `3`.
223
+ Never repeat `create` to observe it.
224
+ 3. Fork only when `status` exits `0` and `checkpoint.current.state` is
225
+ `checkpointed` and `checkpoint.current.usable` is `true`. A `commit_oid` or
226
+ `snapshot_ref` alone is not proof: an unavailable creation can still name a
227
+ retained candidate.
228
+ 4. Dispatch the worker with the qualified `checkpoint.current.commit_oid` as
229
+ the existing `--base` of a new managed workspace. Use an ordinary checkout of
230
+ the same repository as `--cwd`, normally the source the Lead already uses:
231
+
232
+ ```text
233
+ delegate --root ROOT dispatch --auto --brief ABS_BRIEF --cwd ABS_SOURCE \
234
+ --isolation git-worktree --base COMMIT_OID \
235
+ --permission workspace-write --budget 30m --json
236
+ ```
237
+
238
+ 5. Remove the checkpoint explicitly when the Lead no longer needs it.
239
+
240
+ `status` exit `0` means a successful metadata observation, not an available
241
+ checkpoint. `usable` is `true` only for a `checkpointed` creation with no
242
+ registered removal attempt, including a failed one. It is derived from
243
+ Delegate's records; it is not proof that an external Git command left the ref
244
+ in place.
245
+
246
+ If `checkpoint create --workspace ID` returns exit `3` with
247
+ `reason_code: "requested_action_not_authorized"` and `retry_create`, no
248
+ checkpoint was accepted and no ID was assigned. When that workspace is in use
249
+ by the turn you are running in, do not wait for yourself, retry in a loop, or
250
+ retry through `--cwd`. Finish and report; the Lead creates the checkpoint after
251
+ your turn closes. When another use holds it, the Lead observes that workspace
252
+ until it is retained again, then retries the same create once.
253
+
254
+ Checkpoint refs outlive `workspace release` and `workspace forget`: the forget
255
+ inventory covers only the workspace's own base, capture, and baseline refs.
256
+ `checkpoint remove C` is the only command that removes a checkpoint ref.
257
+ Removal needs only the shared Git common directory, so it works after the
258
+ source checkout or managed checkout is gone. A missing or replaced common
259
+ directory makes that removal attempt `unavailable`, never `removed`. Creation
260
+ history stays observable after removal: `current.state` keeps the creation
261
+ disposition, while `current.usable` becomes `false` and `current.removal`
262
+ shows the latest attempt.
263
+
139
264
  ## Retention and forgetting
140
265
 
141
266
  A created managed checkout is retained for every outcome, including no-op and
@@ -146,9 +271,10 @@ identity check retains the checkout and evidence for retry. `--discard` accepts
146
271
  loss of uncaptured checkout content; it does not waive native closure,
147
272
  ownership, or identity proof.
148
273
 
149
- Release leaves the pinned refs/blobs and capture receipts as evidence. After a
150
- proven release, `workspace forget ID` writes an exact inventory intent before
151
- removing only matching owned refs and optional artifacts by identity/CAS. It
274
+ Release leaves the pinned refs/blobs, turn-baseline refs, and capture receipts
275
+ as evidence. After a proven release, `workspace forget ID` writes an exact
276
+ inventory intent before removing only matching owned refs and optional
277
+ artifacts by identity/CAS. It
152
278
  retains capture receipts, tombstones, all provider outcomes, raw evidence, and
153
279
  payload descriptors. Readers should label an intentionally forgotten item as
154
280
  `forgotten`, not as corruption.
@@ -112,7 +112,13 @@ tasks may proceed; the failed attempt must remain reported as failed or blocked.
112
112
  output as an answer.
113
113
  7. If a task is timed out and its JSON says `continuation.resumable: true`, use
114
114
  `continue --task TASK_ID`, optionally with a new `--brief`. Do not create a
115
- fresh unrelated dispatch to replace a resumable task.
115
+ fresh unrelated dispatch to replace a resumable task. The one other automatic
116
+ route is an `interrupted` task from `status` or `collect`, and only when
117
+ `failure.code` is `"task_interrupted"`, `failure.next_action` is
118
+ `"continue_task"`, and `continuation.resumable` is `true`; see
119
+ [interrupted turn](references/continuation.md#interrupted-turn). A
120
+ cancellation, `stop_and_report`, or failed admission always takes precedence
121
+ over `resumable: true`.
116
122
  8. Treat `blocked` authentication as unresolved. Do not call it compatible,
117
123
  successful, or failed provider behavior, and do not invent login commands.
118
124
  9. Never put credentials, tokens, or unrestricted provider output in a brief,
@@ -127,7 +133,12 @@ tasks may proceed; the failed attempt must remain reported as failed or blocked.
127
133
  or provider comparison. For independent parallel writes, choose
128
134
  `--isolation git-worktree` automatically unless the user explicitly chooses
129
135
  shared coordination; the model chooses fan-out and task scope. A read-only
130
- review may attach a retained workspace with `--workspace ID`.
136
+ review may attach a retained workspace with `--workspace ID`. To fork a
137
+ worker from current uncommitted content, follow
138
+ [checkpoint before fork](references/workspaces.md#checkpoint-before-fork):
139
+ fork only from a `checkpointed`, `usable` checkpoint commit. If your own
140
+ turn holds the workspace, do not wait or retry through `--cwd`; report so
141
+ the Lead checkpoints after your turn closes.
131
142
 
132
143
  For an explicit Codex code review, use the discoverable `codex:review` profile
133
144
  with one persisted target: `--review-target uncommitted`,
@@ -321,8 +332,8 @@ delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" status TASK_ID --json
321
332
  delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" collect TASK_ID --watch 5s --json
322
333
  ```
323
334
 
324
- Check `status` first: if it is `timed_out`, follow step 6 even when publication
325
- is pending and no outcome is present. If `interaction.pending_requests` is
335
+ Check `status` first: if it is `timed_out` or `interrupted`, follow step 6 even
336
+ when publication is pending or unknown and no outcome is present. If `interaction.pending_requests` is
326
337
  nonempty and the interaction is not closed, answer the questions using
327
338
  [active-task interaction](references/interaction.md) before collecting again.
328
339
  Otherwise, if `collect` exits with code `3` because publication is still
@@ -336,14 +347,19 @@ is committed. A rejected outcome is terminal evidence and must be reported as
336
347
  rejected. Payload and raw output are returned as validated descriptors; do not
337
348
  copy unrestricted provider output into the control response.
338
349
 
339
- ### 6. Continue a timed out turn
350
+ ### 6. Continue a timed out or interrupted turn
340
351
 
341
- When `status` or `collect` reports `timed_out`, read
342
- [continuation and timeout recovery](references/continuation.md). If
343
- `continuation.resumable` is true, use `continue --task TASK_ID` with the original
344
- state root and a finite budget, then observe the returned successor ID. Do not
352
+ When `status` or `collect` reports `timed_out` or `interrupted`, read
353
+ [continuation and timeout recovery](references/continuation.md). Unless a
354
+ cancellation or `stop_and_report` applies, a `timed_out` task with
355
+ `continuation.resumable: true` may use `continue --task TASK_ID`. An
356
+ `interrupted` task may use it only under all three conditions in rule 7;
357
+ otherwise report it. When continuing, use the original state root and a finite
358
+ budget, then observe the returned successor ID. Do not
345
359
  wait indefinitely for pending publication or restart the task as an unrelated
346
- dispatch. If continuation is unavailable, report its reason. Apply the same
360
+ dispatch. If continuation is unavailable, report its reason, except the busy
361
+ managed-workspace case in [continuation and timeout
362
+ recovery](references/continuation.md). Apply the same
347
363
  `failure` and `task_record` decisions as for dispatch; never repeat an uncertain
348
364
  successor creation.
349
365
 
@@ -365,10 +381,12 @@ recruit more agents unless that is explicitly part of the task.
365
381
  model/effort options or another public command.
366
382
  - Read [result handling](references/result-handling.md) when interpreting
367
383
  admission, publication, authentication, errors, or exit codes.
368
- - Read [continuation](references/continuation.md) for timeout recovery or an
369
- exact-session follow-up.
384
+ - Read [continuation](references/continuation.md) for timeout or interruption
385
+ recovery or an exact-session follow-up.
370
386
  - Read [managed workspaces](references/workspaces.md) for optional Git
371
- worktrees, capture, review scope, release, and forgetting.
387
+ worktrees, capture, review scope, release, and forgetting, and
388
+ [Git checkpoints](references/workspaces.md#git-checkpoints) before creating,
389
+ observing, forking from, or removing a checkpoint.
372
390
  - Use [read-only example](examples/read-only-task.md),
373
391
  [write example](examples/workspace-write-task.md),
374
392
  [JSON examples](examples/json-results.md), and
@@ -11,3 +11,45 @@ own the decision order for task creation, failures, timeout continuation,
11
11
  terminal outcomes, pending interaction, and bounded collection. Read them
12
12
  before interpreting these commands; pending publication alone is not a reason
13
13
  to wait when the task needs an answer or timeout recovery.
14
+
15
+ ## Checkpoint responses
16
+
17
+ These abbreviated shapes use the delivered field names; `…` marks omitted
18
+ values. Apply [checkpoint results](../references/result-handling.md#checkpoint-results)
19
+ to the whole response and its exit code.
20
+
21
+ Accepted creation, exit `3`:
22
+
23
+ ```json
24
+ {"schema_version":1,"command":"checkpoint","root_id":"…","admission":"unknown","liveness":"undetermined","publication":"unknown",
25
+ "checkpoint":{"id":"CHECKPOINT_ID","next_action":"watch_checkpoint"}}
26
+ ```
27
+
28
+ Busy refusal, exit `3`, with no accepted ID:
29
+
30
+ ```json
31
+ {"checkpoint":{"reason_code":"requested_action_not_authorized","next_action":"retry_create"}}
32
+ ```
33
+
34
+ Qualified checkpoint from `status`, exit `0`; only this state may be forked:
35
+
36
+ ```json
37
+ {"checkpoint":{"id":"CHECKPOINT_ID","current":{"id":"CHECKPOINT_ID","selection":"ordinary","cwd":"/abs/source",
38
+ "state":"checkpointed","snapshot_ref":"refs/delegate/ROOT_ID/checkpoints/CHECKPOINT_ID","commit_oid":"…","usable":true}}}
39
+ ```
40
+
41
+ Exact removal attempt from `status CHECKPOINT_ID --operation OPERATION_ID`,
42
+ exit `0`. Creation history stays `checkpointed`, but `usable` is now `false`:
43
+
44
+ ```json
45
+ {"checkpoint":{"id":"CHECKPOINT_ID","operation_id":"OPERATION_ID",
46
+ "current":{"id":"CHECKPOINT_ID","selection":"ordinary","state":"checkpointed","usable":false,
47
+ "removal":{"operation_id":"OPERATION_ID","attempts":1,"state":"removed"}},
48
+ "operation":{"operation_id":"OPERATION_ID","attempts":1,"state":"removed"}}}
49
+ ```
50
+
51
+ An empty `list` page, exit `0`, still has the array:
52
+
53
+ ```json
54
+ {"schema_version":1,"command":"checkpoint","root_id":"…","admission":"unknown","liveness":"undetermined","publication":"unknown","checkpoints":[]}
55
+ ```
@@ -66,3 +66,64 @@ Save each response and exit code once. A created task is observed with
66
66
  success; continue observing the same task. The Lead reviews the capture and
67
67
  owns landing any changes. `workspace release` is explicit; no automatic
68
68
  apply, commit, branch merge, TTL eviction, or provider-CLI fallback occurs.
69
+
70
+ ## Fork from a checkpoint
71
+
72
+ Use this sequence when the worker must start from current uncommitted content
73
+ instead of a committed base. Run it from the Lead, not from inside a worker
74
+ that uses the same workspace. The [checkpoint guidance](../references/workspaces.md#checkpoint-before-fork)
75
+ owns the rules; [checkpoint results](../references/result-handling.md#checkpoint-results)
76
+ owns every response decision.
77
+
78
+ ```sh
79
+ SOURCE="$PWD"
80
+ RESPONSE="$(mktemp)"
81
+ if delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" checkpoint create \
82
+ --cwd "$SOURCE" --budget 5m --json >"$RESPONSE.checkpoint"; then
83
+ checkpoint_exit=0
84
+ else
85
+ checkpoint_exit=$?
86
+ fi
87
+ printf '%s\n' "$checkpoint_exit" >"$RESPONSE.checkpoint.exit"
88
+ cat "$RESPONSE.checkpoint"
89
+ ```
90
+
91
+ Exit `3` with `checkpoint.id` and `next_action: "watch_checkpoint"` means the
92
+ request was accepted, not captured. Use that `checkpoint.id` as
93
+ `CHECKPOINT_ID`. Do not run `create` again.
94
+
95
+ ```sh
96
+ if delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" checkpoint status CHECKPOINT_ID \
97
+ --watch 30s --json >"$RESPONSE.checkpoint.status"; then
98
+ checkpoint_status_exit=0
99
+ else
100
+ checkpoint_status_exit=$?
101
+ fi
102
+ printf '%s\n' "$checkpoint_status_exit" >"$RESPONSE.checkpoint.status.exit"
103
+ cat "$RESPONSE.checkpoint.status"
104
+ ```
105
+
106
+ Repeat that status command while it exits `3`. Fork only when it exits `0`,
107
+ `checkpoint.current.state` is `checkpointed`, and `checkpoint.current.usable`
108
+ is `true`. Use `checkpoint.current.commit_oid` as `COMMIT_OID` in the first
109
+ dispatch example, replacing `--base HEAD` with `--base COMMIT_OID`. Report a
110
+ `stop_and_report` result instead of forking.
111
+
112
+ When the Lead no longer needs the checkpoint, remove it explicitly:
113
+
114
+ ```sh
115
+ if delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" checkpoint remove CHECKPOINT_ID \
116
+ --budget 5m --json >"$RESPONSE.checkpoint.remove"; then
117
+ checkpoint_remove_exit=0
118
+ else
119
+ checkpoint_remove_exit=$?
120
+ fi
121
+ printf '%s\n' "$checkpoint_remove_exit" >"$RESPONSE.checkpoint.remove.exit"
122
+ cat "$RESPONSE.checkpoint.remove"
123
+ ```
124
+
125
+ Exit `3` with `checkpoint.operation_id` and `watch_checkpoint` means removal
126
+ attempt `OPERATION_ID` was accepted. Observe that exact attempt with
127
+ `checkpoint status CHECKPOINT_ID --operation OPERATION_ID --watch 30s --json`,
128
+ saving its response and exit the same way. A `retry_remove` response has no
129
+ `operation_id` and accepted nothing.
@@ -8,6 +8,22 @@ cap or TTL eviction. A read-only review may attach a retained workspace with
8
8
  release rules live in [managed workspace guidance](workspaces.md); do not
9
9
  restate them in each collaboration brief.
10
10
 
11
+ Give each participant a bounded deliverable, explicit write ownership, and the
12
+ inputs it depends on. Run independent assignments in parallel; dependent work
13
+ begins only after the required results are collected and any prerequisite
14
+ changes are applied and verified in the consuming worker's workspace, without
15
+ requiring a merge. Combine the relevant completed results in the verification
16
+ workspace before running checks that depend on their interaction. A worker's
17
+ terminal outcome establishes that assignment's result, not completion of the
18
+ user's full objective; keep remaining integration, verification and unresolved
19
+ work explicit.
20
+
21
+ When a participant must start from the Lead's current uncommitted work, the
22
+ Lead checkpoints that content first and forks each worker from the qualified
23
+ checkpoint commit with `--base`. Follow
24
+ [checkpoint before fork](workspaces.md#checkpoint-before-fork); a worker never
25
+ checkpoints a workspace its own turn is using.
26
+
11
27
  ## Keep each request identifiable
12
28
 
13
29
  Choose the fan-out needed for the user's task. Each participant needs its own
@@ -6,7 +6,8 @@ All paths passed to `--root`, `--cwd`, and `--brief` must be absolute. Add
6
6
  Pass the intended workspace directly. Delegate handles provider launch options;
7
7
  do not initialize Git, change trust settings, or call a provider CLI as a
8
8
  workspace workaround. Ordinary tasks support non-Git directories. Git is
9
- required for managed worktree creation/attachment only; native review retains
9
+ required for managed worktree creation/attachment and checkpoint creation or
10
+ removal only; native review retains
10
11
  its separate explicit review-target contract. See [managed workspace guidance](workspaces.md)
11
12
  for the policy owner.
12
13
 
@@ -52,8 +53,8 @@ Dispatch flags:
52
53
  - `--model MODEL` and `--effort VALUE`: provider-advertised choices (CCR requires a model alias);
53
54
  Codex maps effort to TOML `model_reasoning_effort` for ordinary turns or a native
54
55
  turn parameter for interactive turns, while Claude and AGY
55
- pass native effort flags; Pi maps effort to `--thinking`; OpenCode maps it to
56
- `--variant`; CCR forwards effort to Claude and uses `--model` as a CCR alias; `default` is omitted;
56
+ pass native effort flags; Pi maps effort to `--thinking`; OpenCode sends model
57
+ and effort as acknowledged ACP session options; CCR forwards effort to Claude and uses `--model` as a CCR alias; `default` is omitted;
57
58
  - `--interactive`: enable advertised native clarification and steering; see [interaction](interaction.md);
58
59
  - `--native-timeout DURATION`: optional provider-native timeout where advertised.
59
60
 
@@ -101,6 +102,27 @@ Continuation flags:
101
102
  - `--budget DURATION`, `--model MODEL`, and `--effort VALUE`: optional overrides;
102
103
  - `--interactive`: enable interaction when supported; already-interactive predecessors keep it.
103
104
 
105
+ ## Checkpoint commands
106
+
107
+ Git checkpoints are optional and launch no provider work. Use them to fork a
108
+ worker from current uncommitted content; the canonical
109
+ [checkpoint guidance](workspaces.md#git-checkpoints) owns targets, limits, and
110
+ the checkpoint-before-fork sequence, and
111
+ [checkpoint results](result-handling.md#checkpoint-results) owns the decisions.
112
+
113
+ ```sh
114
+ delegate --root ROOT checkpoint create --cwd ABS [--budget DURATION] --json
115
+ delegate --root ROOT checkpoint create --workspace ID [--budget DURATION] --json
116
+ delegate --root ROOT checkpoint status CHECKPOINT_ID [--operation OP] [--watch DURATION] --json
117
+ delegate --root ROOT checkpoint list [--after CHECKPOINT_ID] [--limit 1..50] --json
118
+ delegate --root ROOT checkpoint remove CHECKPOINT_ID [--budget DURATION] --json
119
+ ```
120
+
121
+ `create` takes exactly one of `--cwd` or `--workspace`. `--operation` selects a
122
+ removal attempt ID returned by `remove`; without it, `status` observes the
123
+ creation and its latest removal. `list` pages with the returned
124
+ `checkpoint_cursor` as `--after`.
125
+
104
126
  ## Choosing models and effort
105
127
 
106
128
  Pass an explicitly supplied model ID and effort unchanged, even if discovery
@@ -135,7 +157,9 @@ enumeration, not effort metadata. `null` means effort metadata is unknown;
135
157
  `[]` means the provider explicitly reported no choices. Discovery exits `0`
136
158
  for `available` or `partial`, `2` for `blocked` or `unavailable`, and `1` for
137
159
  `failed`. Preserve a blocked or unavailable discovery result as unresolved;
138
- defaults remain allowed.
160
+ defaults remain allowed. An operational discovery failure also includes an
161
+ optional `failure` object and a fixed `error` message; keep both fields and
162
+ follow `failure.next_action`.
139
163
 
140
164
  ## Checking readiness
141
165
 
@@ -53,6 +53,42 @@ Then retry the exact `continue --task PREDECESSOR_ID` after it becomes retained.
53
53
  Keep the original predecessor, brief, and budget; do not replace continuation
54
54
  with a fresh dispatch. A created successor is only observed.
55
55
 
56
+ **Busy managed workspace after a timeout.** This is the only case where an
57
+ unavailable continuation may be waited for instead of reported. It applies
58
+ only when a `timed_out` task reports all of `continuation.resumable: false`,
59
+ `continuation.reason` exactly `"managed workspace is busy"`,
60
+ `workspace.state: "busy"`, and `workspace.next_action: "watch_workspace"`.
61
+ Another reservation may still hold the workspace; do not infer which use. Wait
62
+ only when further work needs to continue this task, and return a committed
63
+ result normally. Do not wait when a cancellation (`failure.code:
64
+ "task_cancelled"`), a concrete fault, or `stop_and_report` applies to the task
65
+ or the workspace. If a successor from this predecessor was already created,
66
+ observe that successor instead; if its creation is uncertain, follow the
67
+ `task_record` stop rule in [result handling](result-handling.md).
68
+
69
+ 1. Watch the workspace with the same root and a finite duration, using
70
+ `workspace.id` from the task response; never guess an ID or path:
71
+
72
+ ```sh
73
+ delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" workspace status \
74
+ WORKSPACE_ID --watch 30s --json
75
+ ```
76
+
77
+ Repeat the watch only while it reports `state: "busy"`.
78
+ 2. When it reports `state: "retained"`, run a fresh `status` for the same
79
+ predecessor task ID.
80
+ 3. Only if that status now reports `continuation.resumable: true` and the task
81
+ rules above still permit it, run the exact `continue --task PREDECESSOR_ID`
82
+ once. Otherwise report its reason and stop.
83
+
84
+ Never run `continue` while `resumable` is `false`. Report any other reason,
85
+ workspace state, or fault under the existing guidance; do not reactivate a
86
+ `released` or `forgotten` workspace. An `undetermined` workspace view is
87
+ unobserved and follows [managed workspace results](result-handling.md#managed-workspace-results);
88
+ it is neither a terminal failure nor permission to continue. Do not dispatch,
89
+ fall back to another provider, repair the environment, or mutate the workspace
90
+ to free it. Availability is not guaranteed; do not wait indefinitely.
91
+
56
92
  `mode` describes the provider contract:
57
93
 
58
94
  - `native` uses the provider’s exact persisted conversation/session ID;
@@ -61,11 +97,14 @@ with a fresh dispatch. A created successor is only observed.
61
97
  - `unsupported` cannot be continued safely.
62
98
 
63
99
  Do not use a guessed session ID, a provider-specific resume command, or a new
64
- independent dispatch to recover a timeout. The CLI accepts either a validated
65
- terminal outcome or a durable Pueue-ended observation backed by a guardian
66
- lifetime-termination record with independently proven empty scope for the
67
- session handoff; without one of those proofs it refuses continuation. Inspect
68
- the predecessor again rather than bypassing that boundary.
100
+ independent dispatch to recover a timeout. The CLI accepts a validated
101
+ terminal outcome, a durable Pueue-ended budget stop backed by a guardian
102
+ lifetime-termination record with independently proven empty scope, or canonical
103
+ closed lifetime evidence of any closure kind together with a free runner lease
104
+ for a turn with no seal or outcome. A sealed predecessor must be collected
105
+ first. A queue-row end or a timestamp alone never qualifies; without one of those
106
+ proofs it refuses continuation. Inspect the predecessor again rather than
107
+ bypassing that boundary.
69
108
 
70
109
  For a managed workspace timeout, capture is a new finite auxiliary operation
71
110
  after canonical closure. The normal path proves the provider scope empty and
@@ -76,6 +115,56 @@ unavailable capture can still preserve continuation evidence; it does not
76
115
  change the provider outcome.
77
116
 
78
117
 
118
+ ## Interrupted turn
119
+
120
+ A host restart can end a provider turn before it publishes anything. Delegate
121
+ reports an interruption only when the task's stored publication state is still
122
+ unknown, with no provider exit or outcome, its canonical lifetime closure is
123
+ proven, and its runner lease is released. A terminated budget stop is checked
124
+ first and reports `timed_out` instead, as does a closure whose recorded
125
+ termination cause is the budget. Closure proves only that the provider
126
+ scope is gone; it is not provider success, and no answer or output from that
127
+ turn is accepted. Do not check these facts yourself or infer eligibility from
128
+ missing files: obey the returned `status`, `failure`, `continuation`, and
129
+ `pending` fields. For an eligible task, after real outcomes and concrete errors
130
+ are checked, `status` and `collect` report `status: "interrupted"` with
131
+ `publication: "unknown"`, no `outcome`, and
132
+ `failure.code: "task_interrupted"`. `status` exits `0`; `collect` exits `1`.
133
+ An interruption may also carry `pending.kind: "supervisor-command"`; that
134
+ independent observation means only that a supervisor command has not
135
+ finished, so follow the interruption's `failure` and `continuation` as usual.
136
+ Do not infer an interruption from `publication: "unknown"` alone: a response
137
+ with `pending.kind: "publication"` is still pending publication and carries no
138
+ interrupted resume offer, so handle it as pending under
139
+ [result handling](result-handling.md). Outcomes, `timed_out`, interaction,
140
+ `lifetime-finalization`, and ordinary pending exit `3` keep their meaning.
141
+
142
+ | Response | Action |
143
+ |---|---|
144
+ | `failure.code: "task_interrupted"`, `failure.next_action: "continue_task"`, and `continuation.resumable: true` (all three) | Run the `continue` above for that predecessor with the original root and a finite budget, then observe the successor. |
145
+ | `interrupted` with any other combination, including `failure.next_action: "stop_and_report"` or `continuation.resumable: false` | Report the reason and stop. |
146
+ | `timed_out` with `failure.code: "task_cancelled"` (user stop and budget stop, no outcome) | `stop_and_report`; `continuation.resumable` is `false` and no `continue_command` is returned. Report it. `collect` exit `3` here is a finite watch, not success and not a reason to keep waiting. |
147
+ | Any other operational error | It is not an interruption; apply [result handling](result-handling.md). |
148
+
149
+ `continuation.resumable: true` alone never revives an attempt that returned
150
+ `stop_and_report`, and a failed admission keeps its `task_record` rules.
151
+ Never continue a cancelled task automatically or start a fresh provider
152
+ fallback. A user stop keeps `interrupted` / `task_interrupted` for a closed
153
+ interrupted task, now with `stop_and_report` and `resumable: false`. A
154
+ committed or rejected outcome stays authoritative and is never replaced by
155
+ `task_cancelled`. A later, explicit user request to `continue` is a separate
156
+ action and still validated against real closure and the recorded session.
157
+
158
+ The successor uses the predecessor's observed provider session from
159
+ `provider.ref`. Only when `provider.ref` is genuinely absent, for any eligible
160
+ predecessor and not only an interrupted one, does `continue` fall back to the
161
+ validated immutable session that turn was asked to resume. The response reports
162
+ the source in `continuation.session_source`: `observed` means `provider.ref`
163
+ supplied the identity, and `recorded_input` means the requested session supplied
164
+ it. Neither value is evidence the provider saved any message or resumed
165
+ successfully. A missing task reference, or an unreadable, malformed, or mismatched
166
+ record, refuses. Never search ancestors or rerun the original task.
167
+
79
168
  ## Follow up after normal completion
80
169
 
81
170
  A validated terminal outcome, committed or rejected, can also support a new