delegation-layer 0.1.29 → 0.1.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delegation-layer",
3
- "version": "0.1.29",
3
+ "version": "0.1.32",
4
4
  "description": "Multi-provider AI agent delegation CLI and Agent Skill for running bounded coding tasks across provider CLIs.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "delegation-layer",
3
3
  "displayName": "Delegation Layer",
4
- "version": "0.1.29",
4
+ "version": "0.1.32",
5
5
  "description": "Consult external AI harnesses and CCR-configured models for reviews, research, implementation, and debate through the resumable Delegate CLI.",
6
6
  "author": {
7
7
  "name": "Hesham Karam",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delegation-layer",
3
- "version": "0.1.29",
3
+ "version": "0.1.32",
4
4
  "description": "Consult external AI harnesses and CCR-configured models for reviews, research, implementation, and debate through the resumable Delegate CLI.",
5
5
  "author": {
6
6
  "name": "Hesham Karam",
@@ -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,
@@ -321,8 +327,8 @@ delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" status TASK_ID --json
321
327
  delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" collect TASK_ID --watch 5s --json
322
328
  ```
323
329
 
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
330
+ Check `status` first: if it is `timed_out` or `interrupted`, follow step 6 even
331
+ when publication is pending or unknown and no outcome is present. If `interaction.pending_requests` is
326
332
  nonempty and the interaction is not closed, answer the questions using
327
333
  [active-task interaction](references/interaction.md) before collecting again.
328
334
  Otherwise, if `collect` exits with code `3` because publication is still
@@ -336,12 +342,15 @@ is committed. A rejected outcome is terminal evidence and must be reported as
336
342
  rejected. Payload and raw output are returned as validated descriptors; do not
337
343
  copy unrestricted provider output into the control response.
338
344
 
339
- ### 6. Continue a timed out turn
345
+ ### 6. Continue a timed out or interrupted turn
340
346
 
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
347
+ When `status` or `collect` reports `timed_out` or `interrupted`, read
348
+ [continuation and timeout recovery](references/continuation.md). Unless a
349
+ cancellation or `stop_and_report` applies, a `timed_out` task with
350
+ `continuation.resumable: true` may use `continue --task TASK_ID`. An
351
+ `interrupted` task may use it only under all three conditions in rule 7;
352
+ otherwise report it. When continuing, use the original state root and a finite
353
+ budget, then observe the returned successor ID. Do not
345
354
  wait indefinitely for pending publication or restart the task as an unrelated
346
355
  dispatch. If continuation is unavailable, report its reason. Apply the same
347
356
  `failure` and `task_record` decisions as for dispatch; never repeat an uncertain
@@ -365,8 +374,8 @@ recruit more agents unless that is explicitly part of the task.
365
374
  model/effort options or another public command.
366
375
  - Read [result handling](references/result-handling.md) when interpreting
367
376
  admission, publication, authentication, errors, or exit codes.
368
- - Read [continuation](references/continuation.md) for timeout recovery or an
369
- exact-session follow-up.
377
+ - Read [continuation](references/continuation.md) for timeout or interruption
378
+ recovery or an exact-session follow-up.
370
379
  - Read [managed workspaces](references/workspaces.md) for optional Git
371
380
  worktrees, capture, review scope, release, and forgetting.
372
381
  - Use [read-only example](examples/read-only-task.md),
@@ -52,8 +52,8 @@ Dispatch flags:
52
52
  - `--model MODEL` and `--effort VALUE`: provider-advertised choices (CCR requires a model alias);
53
53
  Codex maps effort to TOML `model_reasoning_effort` for ordinary turns or a native
54
54
  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;
55
+ pass native effort flags; Pi maps effort to `--thinking`; OpenCode sends model
56
+ and effort as acknowledged ACP session options; CCR forwards effort to Claude and uses `--model` as a CCR alias; `default` is omitted;
57
57
  - `--interactive`: enable advertised native clarification and steering; see [interaction](interaction.md);
58
58
  - `--native-timeout DURATION`: optional provider-native timeout where advertised.
59
59
 
@@ -135,7 +135,9 @@ enumeration, not effort metadata. `null` means effort metadata is unknown;
135
135
  `[]` means the provider explicitly reported no choices. Discovery exits `0`
136
136
  for `available` or `partial`, `2` for `blocked` or `unavailable`, and `1` for
137
137
  `failed`. Preserve a blocked or unavailable discovery result as unresolved;
138
- defaults remain allowed.
138
+ defaults remain allowed. An operational discovery failure also includes an
139
+ optional `failure` object and a fixed `error` message; keep both fields and
140
+ follow `failure.next_action`.
139
141
 
140
142
  ## Checking readiness
141
143
 
@@ -61,11 +61,14 @@ with a fresh dispatch. A created successor is only observed.
61
61
  - `unsupported` cannot be continued safely.
62
62
 
63
63
  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.
64
+ independent dispatch to recover a timeout. The CLI accepts a validated
65
+ terminal outcome, a durable Pueue-ended budget stop backed by a guardian
66
+ lifetime-termination record with independently proven empty scope, or canonical
67
+ closed lifetime evidence of any closure kind together with a free runner lease
68
+ for a turn with no seal or outcome. A sealed predecessor must be collected
69
+ first. A queue-row end or a timestamp alone never qualifies; without one of those
70
+ proofs it refuses continuation. Inspect the predecessor again rather than
71
+ bypassing that boundary.
69
72
 
70
73
  For a managed workspace timeout, capture is a new finite auxiliary operation
71
74
  after canonical closure. The normal path proves the provider scope empty and
@@ -76,6 +79,56 @@ unavailable capture can still preserve continuation evidence; it does not
76
79
  change the provider outcome.
77
80
 
78
81
 
82
+ ## Interrupted turn
83
+
84
+ A host restart can end a provider turn before it publishes anything. Delegate
85
+ reports an interruption only when the task's stored publication state is still
86
+ unknown, with no provider exit or outcome, its canonical lifetime closure is
87
+ proven, and its runner lease is released. A terminated budget stop is checked
88
+ first and reports `timed_out` instead, as does a closure whose recorded
89
+ termination cause is the budget. Closure proves only that the provider
90
+ scope is gone; it is not provider success, and no answer or output from that
91
+ turn is accepted. Do not check these facts yourself or infer eligibility from
92
+ missing files: obey the returned `status`, `failure`, `continuation`, and
93
+ `pending` fields. For an eligible task, after real outcomes and concrete errors
94
+ are checked, `status` and `collect` report `status: "interrupted"` with
95
+ `publication: "unknown"`, no `outcome`, and
96
+ `failure.code: "task_interrupted"`. `status` exits `0`; `collect` exits `1`.
97
+ An interruption may also carry `pending.kind: "supervisor-command"`; that
98
+ independent observation means only that a supervisor command has not
99
+ finished, so follow the interruption's `failure` and `continuation` as usual.
100
+ Do not infer an interruption from `publication: "unknown"` alone: a response
101
+ with `pending.kind: "publication"` is still pending publication and carries no
102
+ interrupted resume offer, so handle it as pending under
103
+ [result handling](result-handling.md). Outcomes, `timed_out`, interaction,
104
+ `lifetime-finalization`, and ordinary pending exit `3` keep their meaning.
105
+
106
+ | Response | Action |
107
+ |---|---|
108
+ | `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. |
109
+ | `interrupted` with any other combination, including `failure.next_action: "stop_and_report"` or `continuation.resumable: false` | Report the reason and stop. |
110
+ | `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. |
111
+ | Any other operational error | It is not an interruption; apply [result handling](result-handling.md). |
112
+
113
+ `continuation.resumable: true` alone never revives an attempt that returned
114
+ `stop_and_report`, and a failed admission keeps its `task_record` rules.
115
+ Never continue a cancelled task automatically or start a fresh provider
116
+ fallback. A user stop keeps `interrupted` / `task_interrupted` for a closed
117
+ interrupted task, now with `stop_and_report` and `resumable: false`. A
118
+ committed or rejected outcome stays authoritative and is never replaced by
119
+ `task_cancelled`. A later, explicit user request to `continue` is a separate
120
+ action and still validated against real closure and the recorded session.
121
+
122
+ The successor uses the predecessor's observed provider session from
123
+ `provider.ref`. Only when `provider.ref` is genuinely absent, for any eligible
124
+ predecessor and not only an interrupted one, does `continue` fall back to the
125
+ validated immutable session that turn was asked to resume. The response reports
126
+ the source in `continuation.session_source`: `observed` means `provider.ref`
127
+ supplied the identity, and `recorded_input` means the requested session supplied
128
+ it. Neither value is evidence the provider saved any message or resumed
129
+ successfully. A missing task reference, or an unreadable, malformed, or mismatched
130
+ record, refuses. Never search ancestors or rerun the original task.
131
+
79
132
  ## Follow up after normal completion
80
133
 
81
134
  A validated terminal outcome, committed or rejected, can also support a new
@@ -24,7 +24,13 @@ the same as a successful provider result.
24
24
  evidence for an existing task.
25
25
  3. If `status` is `timed_out`, follow the continuation procedure before treating
26
26
  pending publication as a reason to wait. `continuation.resumable: true`
27
- permits a linked successor even when no outcome has been published.
27
+ permits a linked successor even when no outcome has been published. An
28
+ `interrupted` task (`failure.code: "task_interrupted"`) follows the table in
29
+ [interrupted turn](continuation.md#interrupted-turn): continue only when
30
+ `failure.next_action` is `continue_task` and `continuation.resumable` is
31
+ `true`, otherwise report it. A `timed_out` task whose `failure.code` is
32
+ `task_cancelled` is `stop_and_report`: report it and stop, even though
33
+ `collect` exits `3`; that exit is not success.
28
34
  4. If `publication` is committed and `outcome.verdict` is committed, return
29
35
  the result and its payload/evidence descriptors.
30
36
  5. If `outcome.verdict` is rejected and `failure.next_action` is `recheck`,
@@ -51,7 +57,13 @@ from an inspection-worker or supervisor-observation failure.
51
57
  For `delegate models`, validate the complete JSON envelope before using any
52
58
  fact. Treat `available` and `partial` with exit `0` as observations, preserve
53
59
  `blocked` and `unavailable` with exit `2` as unresolved prerequisites, and
54
- preserve `failed` with exit `1` as a discovery failure. `complete` refers to
60
+ preserve `failed` as a discovery failure. Exit `1` with `failed` is an
61
+ operational failure; exit `2` with `failed` is an invalid or unsupported
62
+ request, not an authentication block. Status and exit are separate
63
+ observations. When the response
64
+ includes the optional `failure` object and `error` message, keep both in the
65
+ report and follow `failure.next_action`; report and stop on
66
+ `stop_and_report`. `complete` refers to
55
67
  model enumeration. Keep `efforts: null` distinct from `efforts: []`, and do not
56
68
  apply harness-wide effort choices to every model. Discovery is advisory and
57
69
  never an admission allowlist; a provider default remains valid and a listed
@@ -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
@@ -146,9 +179,10 @@ identity check retains the checkout and evidence for retry. `--discard` accepts
146
179
  loss of uncaptured checkout content; it does not waive native closure,
147
180
  ownership, or identity proof.
148
181
 
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
182
+ Release leaves the pinned refs/blobs, turn-baseline refs, and capture receipts
183
+ as evidence. After a proven release, `workspace forget ID` writes an exact
184
+ inventory intent before removing only matching owned refs and optional
185
+ artifacts by identity/CAS. It
152
186
  retains capture receipts, tombstones, all provider outcomes, raw evidence, and
153
187
  payload descriptors. Readers should label an intentionally forgotten item as
154
188
  `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,
@@ -321,8 +327,8 @@ delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" status TASK_ID --json
321
327
  delegate --root "${DELEGATE_ROOT:-$HOME/delegation-state}" collect TASK_ID --watch 5s --json
322
328
  ```
323
329
 
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
330
+ Check `status` first: if it is `timed_out` or `interrupted`, follow step 6 even
331
+ when publication is pending or unknown and no outcome is present. If `interaction.pending_requests` is
326
332
  nonempty and the interaction is not closed, answer the questions using
327
333
  [active-task interaction](references/interaction.md) before collecting again.
328
334
  Otherwise, if `collect` exits with code `3` because publication is still
@@ -336,12 +342,15 @@ is committed. A rejected outcome is terminal evidence and must be reported as
336
342
  rejected. Payload and raw output are returned as validated descriptors; do not
337
343
  copy unrestricted provider output into the control response.
338
344
 
339
- ### 6. Continue a timed out turn
345
+ ### 6. Continue a timed out or interrupted turn
340
346
 
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
347
+ When `status` or `collect` reports `timed_out` or `interrupted`, read
348
+ [continuation and timeout recovery](references/continuation.md). Unless a
349
+ cancellation or `stop_and_report` applies, a `timed_out` task with
350
+ `continuation.resumable: true` may use `continue --task TASK_ID`. An
351
+ `interrupted` task may use it only under all three conditions in rule 7;
352
+ otherwise report it. When continuing, use the original state root and a finite
353
+ budget, then observe the returned successor ID. Do not
345
354
  wait indefinitely for pending publication or restart the task as an unrelated
346
355
  dispatch. If continuation is unavailable, report its reason. Apply the same
347
356
  `failure` and `task_record` decisions as for dispatch; never repeat an uncertain
@@ -365,8 +374,8 @@ recruit more agents unless that is explicitly part of the task.
365
374
  model/effort options or another public command.
366
375
  - Read [result handling](references/result-handling.md) when interpreting
367
376
  admission, publication, authentication, errors, or exit codes.
368
- - Read [continuation](references/continuation.md) for timeout recovery or an
369
- exact-session follow-up.
377
+ - Read [continuation](references/continuation.md) for timeout or interruption
378
+ recovery or an exact-session follow-up.
370
379
  - Read [managed workspaces](references/workspaces.md) for optional Git
371
380
  worktrees, capture, review scope, release, and forgetting.
372
381
  - Use [read-only example](examples/read-only-task.md),
@@ -52,8 +52,8 @@ Dispatch flags:
52
52
  - `--model MODEL` and `--effort VALUE`: provider-advertised choices (CCR requires a model alias);
53
53
  Codex maps effort to TOML `model_reasoning_effort` for ordinary turns or a native
54
54
  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;
55
+ pass native effort flags; Pi maps effort to `--thinking`; OpenCode sends model
56
+ and effort as acknowledged ACP session options; CCR forwards effort to Claude and uses `--model` as a CCR alias; `default` is omitted;
57
57
  - `--interactive`: enable advertised native clarification and steering; see [interaction](interaction.md);
58
58
  - `--native-timeout DURATION`: optional provider-native timeout where advertised.
59
59
 
@@ -135,7 +135,9 @@ enumeration, not effort metadata. `null` means effort metadata is unknown;
135
135
  `[]` means the provider explicitly reported no choices. Discovery exits `0`
136
136
  for `available` or `partial`, `2` for `blocked` or `unavailable`, and `1` for
137
137
  `failed`. Preserve a blocked or unavailable discovery result as unresolved;
138
- defaults remain allowed.
138
+ defaults remain allowed. An operational discovery failure also includes an
139
+ optional `failure` object and a fixed `error` message; keep both fields and
140
+ follow `failure.next_action`.
139
141
 
140
142
  ## Checking readiness
141
143
 
@@ -61,11 +61,14 @@ with a fresh dispatch. A created successor is only observed.
61
61
  - `unsupported` cannot be continued safely.
62
62
 
63
63
  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.
64
+ independent dispatch to recover a timeout. The CLI accepts a validated
65
+ terminal outcome, a durable Pueue-ended budget stop backed by a guardian
66
+ lifetime-termination record with independently proven empty scope, or canonical
67
+ closed lifetime evidence of any closure kind together with a free runner lease
68
+ for a turn with no seal or outcome. A sealed predecessor must be collected
69
+ first. A queue-row end or a timestamp alone never qualifies; without one of those
70
+ proofs it refuses continuation. Inspect the predecessor again rather than
71
+ bypassing that boundary.
69
72
 
70
73
  For a managed workspace timeout, capture is a new finite auxiliary operation
71
74
  after canonical closure. The normal path proves the provider scope empty and
@@ -76,6 +79,56 @@ unavailable capture can still preserve continuation evidence; it does not
76
79
  change the provider outcome.
77
80
 
78
81
 
82
+ ## Interrupted turn
83
+
84
+ A host restart can end a provider turn before it publishes anything. Delegate
85
+ reports an interruption only when the task's stored publication state is still
86
+ unknown, with no provider exit or outcome, its canonical lifetime closure is
87
+ proven, and its runner lease is released. A terminated budget stop is checked
88
+ first and reports `timed_out` instead, as does a closure whose recorded
89
+ termination cause is the budget. Closure proves only that the provider
90
+ scope is gone; it is not provider success, and no answer or output from that
91
+ turn is accepted. Do not check these facts yourself or infer eligibility from
92
+ missing files: obey the returned `status`, `failure`, `continuation`, and
93
+ `pending` fields. For an eligible task, after real outcomes and concrete errors
94
+ are checked, `status` and `collect` report `status: "interrupted"` with
95
+ `publication: "unknown"`, no `outcome`, and
96
+ `failure.code: "task_interrupted"`. `status` exits `0`; `collect` exits `1`.
97
+ An interruption may also carry `pending.kind: "supervisor-command"`; that
98
+ independent observation means only that a supervisor command has not
99
+ finished, so follow the interruption's `failure` and `continuation` as usual.
100
+ Do not infer an interruption from `publication: "unknown"` alone: a response
101
+ with `pending.kind: "publication"` is still pending publication and carries no
102
+ interrupted resume offer, so handle it as pending under
103
+ [result handling](result-handling.md). Outcomes, `timed_out`, interaction,
104
+ `lifetime-finalization`, and ordinary pending exit `3` keep their meaning.
105
+
106
+ | Response | Action |
107
+ |---|---|
108
+ | `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. |
109
+ | `interrupted` with any other combination, including `failure.next_action: "stop_and_report"` or `continuation.resumable: false` | Report the reason and stop. |
110
+ | `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. |
111
+ | Any other operational error | It is not an interruption; apply [result handling](result-handling.md). |
112
+
113
+ `continuation.resumable: true` alone never revives an attempt that returned
114
+ `stop_and_report`, and a failed admission keeps its `task_record` rules.
115
+ Never continue a cancelled task automatically or start a fresh provider
116
+ fallback. A user stop keeps `interrupted` / `task_interrupted` for a closed
117
+ interrupted task, now with `stop_and_report` and `resumable: false`. A
118
+ committed or rejected outcome stays authoritative and is never replaced by
119
+ `task_cancelled`. A later, explicit user request to `continue` is a separate
120
+ action and still validated against real closure and the recorded session.
121
+
122
+ The successor uses the predecessor's observed provider session from
123
+ `provider.ref`. Only when `provider.ref` is genuinely absent, for any eligible
124
+ predecessor and not only an interrupted one, does `continue` fall back to the
125
+ validated immutable session that turn was asked to resume. The response reports
126
+ the source in `continuation.session_source`: `observed` means `provider.ref`
127
+ supplied the identity, and `recorded_input` means the requested session supplied
128
+ it. Neither value is evidence the provider saved any message or resumed
129
+ successfully. A missing task reference, or an unreadable, malformed, or mismatched
130
+ record, refuses. Never search ancestors or rerun the original task.
131
+
79
132
  ## Follow up after normal completion
80
133
 
81
134
  A validated terminal outcome, committed or rejected, can also support a new
@@ -24,7 +24,13 @@ the same as a successful provider result.
24
24
  evidence for an existing task.
25
25
  3. If `status` is `timed_out`, follow the continuation procedure before treating
26
26
  pending publication as a reason to wait. `continuation.resumable: true`
27
- permits a linked successor even when no outcome has been published.
27
+ permits a linked successor even when no outcome has been published. An
28
+ `interrupted` task (`failure.code: "task_interrupted"`) follows the table in
29
+ [interrupted turn](continuation.md#interrupted-turn): continue only when
30
+ `failure.next_action` is `continue_task` and `continuation.resumable` is
31
+ `true`, otherwise report it. A `timed_out` task whose `failure.code` is
32
+ `task_cancelled` is `stop_and_report`: report it and stop, even though
33
+ `collect` exits `3`; that exit is not success.
28
34
  4. If `publication` is committed and `outcome.verdict` is committed, return
29
35
  the result and its payload/evidence descriptors.
30
36
  5. If `outcome.verdict` is rejected and `failure.next_action` is `recheck`,
@@ -51,7 +57,13 @@ from an inspection-worker or supervisor-observation failure.
51
57
  For `delegate models`, validate the complete JSON envelope before using any
52
58
  fact. Treat `available` and `partial` with exit `0` as observations, preserve
53
59
  `blocked` and `unavailable` with exit `2` as unresolved prerequisites, and
54
- preserve `failed` with exit `1` as a discovery failure. `complete` refers to
60
+ preserve `failed` as a discovery failure. Exit `1` with `failed` is an
61
+ operational failure; exit `2` with `failed` is an invalid or unsupported
62
+ request, not an authentication block. Status and exit are separate
63
+ observations. When the response
64
+ includes the optional `failure` object and `error` message, keep both in the
65
+ report and follow `failure.next_action`; report and stop on
66
+ `stop_and_report`. `complete` refers to
55
67
  model enumeration. Keep `efforts: null` distinct from `efforts: []`, and do not
56
68
  apply harness-wide effort choices to every model. Discovery is advisory and
57
69
  never an admission allowlist; a provider default remains valid and a listed
@@ -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
@@ -146,9 +179,10 @@ identity check retains the checkout and evidence for retry. `--discard` accepts
146
179
  loss of uncaptured checkout content; it does not waive native closure,
147
180
  ownership, or identity proof.
148
181
 
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
182
+ Release leaves the pinned refs/blobs, turn-baseline refs, and capture receipts
183
+ as evidence. After a proven release, `workspace forget ID` writes an exact
184
+ inventory intent before removing only matching owned refs and optional
185
+ artifacts by identity/CAS. It
152
186
  retains capture receipts, tombstones, all provider outcomes, raw evidence, and
153
187
  payload descriptors. Readers should label an intentionally forgotten item as
154
188
  `forgotten`, not as corruption.