@uchibeke/gallivant 0.0.0-stage → 0.2.0

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.
Files changed (168) hide show
  1. package/CONTRIBUTING.md +26 -0
  2. package/LICENSE +204 -0
  3. package/README.md +201 -2
  4. package/SECURITY.md +19 -0
  5. package/bin/gallivant.mjs +257 -0
  6. package/bin/package-plugin.mjs +16 -0
  7. package/bin/release-live.mjs +29 -0
  8. package/bin/release-package.mjs +23 -0
  9. package/bin/session-runtime.mjs +13 -0
  10. package/bin/task-delivery-state.mjs +12 -0
  11. package/docs/README.md +70 -0
  12. package/docs/agent-setup.md +214 -0
  13. package/docs/app.md +111 -0
  14. package/docs/architecture.md +265 -0
  15. package/docs/chains.md +102 -0
  16. package/docs/codex-cloud.md +284 -0
  17. package/docs/commands.md +620 -0
  18. package/docs/config.md +430 -0
  19. package/docs/execution.md +796 -0
  20. package/docs/guardrails.md +311 -0
  21. package/docs/install.md +338 -0
  22. package/docs/launch.md +42 -0
  23. package/docs/learning.md +369 -0
  24. package/docs/network-recovery.md +101 -0
  25. package/docs/openai-agents.md +457 -0
  26. package/docs/prd.md +246 -0
  27. package/docs/preparation.md +110 -0
  28. package/docs/release-qualification.md +196 -0
  29. package/docs/releasing.md +140 -0
  30. package/docs/runtime-environment.md +113 -0
  31. package/docs/task-delivery.md +476 -0
  32. package/docs/tasks.md +739 -0
  33. package/lib/aport.mjs +95 -0
  34. package/lib/attribution.mjs +95 -0
  35. package/lib/block.mjs +89 -0
  36. package/lib/capabilities.mjs +3 -0
  37. package/lib/chains.mjs +222 -0
  38. package/lib/codex_cloud.mjs +160 -0
  39. package/lib/codex_cloud_config.mjs +20 -0
  40. package/lib/codex_cloud_input.mjs +49 -0
  41. package/lib/codex_cloud_result.mjs +74 -0
  42. package/lib/commands/doctor.mjs +457 -0
  43. package/lib/commands/init.mjs +34 -0
  44. package/lib/commands/learn.mjs +54 -0
  45. package/lib/commands/new.mjs +47 -0
  46. package/lib/commands/ready.mjs +45 -0
  47. package/lib/commands/register.mjs +195 -0
  48. package/lib/commands/resume.mjs +224 -0
  49. package/lib/commands/serve.mjs +349 -0
  50. package/lib/commands/start.mjs +247 -0
  51. package/lib/commands/start_services.mjs +22 -0
  52. package/lib/commands/status.mjs +150 -0
  53. package/lib/commands/task.mjs +73 -0
  54. package/lib/commands/up.mjs +228 -0
  55. package/lib/commands/why.mjs +143 -0
  56. package/lib/config.mjs +461 -0
  57. package/lib/context.mjs +100 -0
  58. package/lib/control.mjs +202 -0
  59. package/lib/control_contract.mjs +16 -0
  60. package/lib/coordinator.mjs +19 -0
  61. package/lib/credential.mjs +161 -0
  62. package/lib/daemon.mjs +156 -0
  63. package/lib/deliverable.mjs +357 -0
  64. package/lib/events.mjs +261 -0
  65. package/lib/executable.mjs +31 -0
  66. package/lib/execution.mjs +631 -0
  67. package/lib/execution_contract.mjs +15 -0
  68. package/lib/execution_input.mjs +4 -0
  69. package/lib/execution_io.mjs +94 -0
  70. package/lib/execution_journal.mjs +69 -0
  71. package/lib/execution_preparation.mjs +29 -0
  72. package/lib/execution_result.mjs +266 -0
  73. package/lib/fs.mjs +133 -0
  74. package/lib/gh.mjs +289 -0
  75. package/lib/git.mjs +434 -0
  76. package/lib/harness.mjs +521 -0
  77. package/lib/harness_log.mjs +432 -0
  78. package/lib/hash.mjs +16 -0
  79. package/lib/identity.mjs +98 -0
  80. package/lib/learning.mjs +654 -0
  81. package/lib/learning_contract.mjs +2 -0
  82. package/lib/learning_data.mjs +135 -0
  83. package/lib/learning_input.mjs +111 -0
  84. package/lib/learning_qualification.mjs +67 -0
  85. package/lib/learning_signer.mjs +151 -0
  86. package/lib/learning_store.mjs +88 -0
  87. package/lib/log.mjs +53 -0
  88. package/lib/network_diagnostics.mjs +64 -0
  89. package/lib/network_recovery.mjs +42 -0
  90. package/lib/notify.mjs +58 -0
  91. package/lib/openai_agents.mjs +373 -0
  92. package/lib/openai_agents_config.mjs +14 -0
  93. package/lib/openai_agents_http.mjs +87 -0
  94. package/lib/openai_agents_input.mjs +15 -0
  95. package/lib/openai_agents_sources.mjs +112 -0
  96. package/lib/owner_delivery.mjs +443 -0
  97. package/lib/plugin_package.mjs +340 -0
  98. package/lib/prd.mjs +635 -0
  99. package/lib/preparation_agents.mjs +33 -0
  100. package/lib/preparation_codex.mjs +212 -0
  101. package/lib/probe.mjs +126 -0
  102. package/lib/prompt.mjs +238 -0
  103. package/lib/provider_health.mjs +55 -0
  104. package/lib/public-format.mjs +31 -0
  105. package/lib/public_output.mjs +49 -0
  106. package/lib/release_package.mjs +154 -0
  107. package/lib/repository_files.mjs +86 -0
  108. package/lib/run.mjs +385 -0
  109. package/lib/run_log.mjs +94 -0
  110. package/lib/runtime-environment.mjs +87 -0
  111. package/lib/runtime_dependencies.mjs +15 -0
  112. package/lib/runtime_node.mjs +107 -0
  113. package/lib/runtime_state.mjs +174 -0
  114. package/lib/sources/index.mjs +27 -0
  115. package/lib/sources/prd.mjs +50 -0
  116. package/lib/start_contract.mjs +37 -0
  117. package/lib/state.mjs +246 -0
  118. package/lib/task_allowance.mjs +31 -0
  119. package/lib/task_completion.mjs +61 -0
  120. package/lib/task_contract.mjs +450 -0
  121. package/lib/task_delivery.mjs +219 -0
  122. package/lib/task_delivery_cancel.mjs +34 -0
  123. package/lib/task_delivery_contract.mjs +61 -0
  124. package/lib/task_delivery_mcp.mjs +40 -0
  125. package/lib/task_delivery_store.mjs +43 -0
  126. package/lib/task_delivery_view.mjs +88 -0
  127. package/lib/task_environment.mjs +281 -0
  128. package/lib/task_prepare.mjs +224 -0
  129. package/lib/task_store.mjs +38 -0
  130. package/lib/tasks.mjs +513 -0
  131. package/lib/tick.mjs +1635 -0
  132. package/lib/time.mjs +36 -0
  133. package/lib/tracker/assets.mjs +43 -0
  134. package/lib/tracker/pages.mjs +320 -0
  135. package/lib/tracker/runs.mjs +102 -0
  136. package/lib/tracker/stream.mjs +74 -0
  137. package/lib/tracker_process.mjs +107 -0
  138. package/lib/verification_sandbox.mjs +181 -0
  139. package/node_modules/smol-toml/LICENSE +24 -0
  140. package/node_modules/smol-toml/README.md +605 -0
  141. package/node_modules/smol-toml/dist/date.d.ts +41 -0
  142. package/node_modules/smol-toml/dist/date.js +156 -0
  143. package/node_modules/smol-toml/dist/error.d.ts +37 -0
  144. package/node_modules/smol-toml/dist/error.js +67 -0
  145. package/node_modules/smol-toml/dist/extract.js +289 -0
  146. package/node_modules/smol-toml/dist/index.cjs +770 -0
  147. package/node_modules/smol-toml/dist/index.d.ts +44 -0
  148. package/node_modules/smol-toml/dist/index.js +34 -0
  149. package/node_modules/smol-toml/dist/parse.d.ts +73 -0
  150. package/node_modules/smol-toml/dist/parse.js +164 -0
  151. package/node_modules/smol-toml/dist/primitive.js +151 -0
  152. package/node_modules/smol-toml/dist/stringify.d.ts +32 -0
  153. package/node_modules/smol-toml/dist/stringify.js +219 -0
  154. package/node_modules/smol-toml/dist/struct.js +156 -0
  155. package/node_modules/smol-toml/dist/util.d.ts +43 -0
  156. package/node_modules/smol-toml/dist/util.js +57 -0
  157. package/node_modules/smol-toml/package.json +68 -0
  158. package/npm-shrinkwrap.json +1823 -0
  159. package/package.json +105 -4
  160. package/templates/claude-settings.json +18 -0
  161. package/templates/passport.gallivant.json +83 -0
  162. package/templates/plugin/bundled-runtime.json +26 -0
  163. package/templates/plugin/schemas/mcp.schema.json +120 -0
  164. package/templates/plugin/schemas/plugin.schema.json +65 -0
  165. package/templates/plugin/task-delivery.SKILL.md +60 -0
  166. package/templates/prd.md +32 -0
  167. package/templates/tracker/tracker.css +301 -0
  168. package/templates/tracker/tracker.js +721 -0
@@ -0,0 +1,796 @@
1
+ # Execution transport contract
2
+
3
+ Gallivant runs local CLI harnesses and a hosted Agents API through one execution
4
+ boundary, task service, registry, isolated checkout and PR lifecycle. The
5
+ coordinator still runs on your machine or a Node host you operate; choosing a
6
+ hosted harness does not host Gallivant's daemon for you. Repository outputs can
7
+ be code, documentation, research or other verifiable artifacts. Start with a
8
+ [natural-language task](tasks.md) or an approved [PRD](prd.md).
9
+
10
+ The execution layer reuses registry claims and the canonical PR lifecycle.
11
+ `assertExecutionDependency` refuses a runtime assembled without the shared intake
12
+ service (`EXECUTION_DEPENDENCY`); normal installations include it. Consumers must
13
+ use a compatible complete package instead of recreating intake or a second review
14
+ engine. The original approved implementation brief is
15
+ [the execution PRD](https://github.com/uchibeke/gallivant/blob/main/docs/prds/hosted-codex-harnesses.md).
16
+
17
+ ## Availability and capabilities
18
+
19
+ `executionRegistry` in `lib/harness.mjs` is the capability source for configuration,
20
+ probes, the task service's `capabilities()` and `GET /api/capabilities`. Existing
21
+ `claude`, `codex`, `opencode`, `gemini` and `goose` public names and CLI adapters are
22
+ preserved. Each entry declares authentication, environments, preparation policy,
23
+ continuation, observability, cancellation/interrupt, and cost/hard-cap support.
24
+ Mandatory policy, model and environment choices are checked before dispatch;
25
+ unsupported choices refuse execution without falling back or changing defaults.
26
+ Preparation is an optional versioned capability of that same selected provider.
27
+ Claude's tools-off policy, Codex's isolated preparation profile and Agents API's
28
+ text-only `environment:none` policy are registered by their adapters; task
29
+ lifecycle code contains no Claude-only name gate. Gemini, OpenCode and Goose
30
+ currently support implementation from a ready PRD but have no enforced preparation
31
+ capability. A private HOME or a version/sign-in probe does not qualify one. See
32
+ [preparation](preparation.md) for boundaries and measured qualification.
33
+
34
+ Availability means an implemented adapter can be selected; it is not a claim
35
+ that every provider/model has passed a live task. The recorded qualification is:
36
+
37
+ | Harness | Continuation and controls | Qualification and limits |
38
+ | --- | --- | --- |
39
+ | Claude Code, local Codex | Native session; cancel and interrupt | Production/local Git evidence exists. Claude alone supplies a native dollar cap; Codex reports unknown dollar cost. Their permission settings differ; see [guardrails](guardrails.md). |
40
+ | Gemini CLI, OpenCode, Goose | Native session; cancel and interrupt | Adapters and fixtures exist, but the recorded coding campaign did not qualify a complete start/resume or GitHub review lifecycle for these combinations. Native permissions can block headless work; Gallivant adds no OS sandbox to these three. |
41
+ | OpenAI Agents API | Native session; cancel, no interrupt | Hosted preparation and implementation are implemented. Preparation-only live evidence does not qualify hosted implementation/publication/review correction; the separate campaign retains those gates. |
42
+ | Codex Cloud | Successor task; no native cancel or interrupt | New dispatch is unavailable. Offline transport tests and owned-task recovery do not qualify environment/repository binding or live execution. |
43
+
44
+ Local adapters use the provider/model selected in the native harness settings;
45
+ Gallivant does not supply a generic per-task local model router. A model that is
46
+ listed or a stored credential is not proof of model access, credits or permission
47
+ for unattended work. Agents requires an explicit authorized model; Cloud refuses
48
+ model overrides. Unknown dollar cost stays unknown, and remote reservations limit
49
+ future dispatch rather than provider billing. See the
50
+ [dated local campaign](https://github.com/uchibeke/gallivant/blob/main/docs/audit/harness-live-validation.md)
51
+ and each hosted guide for the exact evidence and remaining prerequisites.
52
+
53
+ `openai-agents` has an implemented managed REST transport; see
54
+ [account/environment setup and verification](openai-agents.md). `codex-cloud`
55
+ has an implemented CLI transport but remains unavailable for new dispatch until
56
+ a supported interface verifies the selected environment's connected repository;
57
+ see [Cloud setup and recovery](codex-cloud.md). Offline fixtures
58
+ exercise shared lifecycle behavior; they do not certify live account/model access
59
+ or installed-plugin behavior. The separately opt-in Agents campaign retains unmet
60
+ live prerequisites explicitly. Existing intake qualification blockers remain in
61
+ [the intake evidence](https://github.com/uchibeke/gallivant/blob/main/docs/audit/plugin-ready/README.md).
62
+
63
+ ## Exported boundary
64
+
65
+ Package subpaths `./execution` and `./execution-result` export `makeExecution`,
66
+ `EXECUTION_VERSION`, `PROVIDER_STATES`, `executionLive`, `executionRunner`,
67
+ `assertExecutionDependency` and `reconcileExecutionResult`. `makeExecution` takes
68
+ an owned root, task ID, provider/account, capabilities and either the existing
69
+ local runner or a transport. Its local facade preserves `start`, `current`,
70
+ `read`, `lines`, `settle`, `kill`, `waitForKill` and `dirOf`.
71
+
72
+ A transport implements the following asynchronous methods. Every method must
73
+ honor its `AbortSignal`, bound responses, release resources on abort, and avoid
74
+ creating its own scheduler or review loop. The coordinator owns progress/review
75
+ scheduling. Explicit task preparation mutations can reconcile one owned turn at
76
+ its persisted cadence; read-only clients never call a transport.
77
+
78
+ | Method | Input and acknowledgement |
79
+ | --- | --- |
80
+ | `prepare` | Preparation kind, prompt, authorized limited environment and allowance; confirmed boundary and bounded environment grant, without paid submit |
81
+ | `submit` | Task/provider/account, canonical run ID, operation ID, idempotency key, prompt/input head, mandatory policy, allowance; exact provider job, session and root turn IDs |
82
+ | `inspect` | Exact owned job/session/turn, event cursor; authoritative state, observation time, optional nullable `completedAt`, bounded event page, next cursor, explicit gap and nullable cost |
83
+ | `continue` | Same dispatch contract plus this provider's recorded native session; exact new owned job/root turn acknowledgement |
84
+ | `collect` | Exact job/session/turn/input head; finished boolean outcome, nullable cost, optional nullable `completedAt`, saved result and optional artifact manifest or verified pushed revision |
85
+ | `cancel` | Task/provider/account/run, exact owned job/session/root turn, durable cancellation idempotency key and AbortSignal; boolean `confirmed`, true only after authoritative remote cancellation |
86
+ | optional `lookup` | Persisted intent/idempotency key after lost acknowledgement; authoritative exact job observation, or unresolved null |
87
+ | optional `recoverResponse` | Explicit retry of the identical authorized answer and stable key; reconcile the owned pending call or saved output before replay/acceptance |
88
+
89
+ The public boundary's `inspect()` is deliberately a cached read. Its coordinator
90
+ `reconcile()` invokes transport inspection/lookup. Reading CLI status, task
91
+ inspection, UI status/history/results or SSE never probes providers, submits work,
92
+ or starts an independent provider loop.
93
+
94
+ Inspection and acceptance lookup cursors are bounded JSON scalars: strings,
95
+ finite numbers or booleans, at most 4096 bytes of serialized UTF-8 JSON, including
96
+ quotes and escapes. Omitted/null cursors retain the recorded token. The fixed
97
+ `EXECUTION_CURSOR_BYTES` limit applies before journaling, ownership/progress
98
+ adoption and persistence, independently of the event-page limit. Invalid tokens
99
+ leave prior progress/cursor and journals intact and enter scoped protocol backoff;
100
+ the next inspection sends only the last validated token. Long valid cursors use
101
+ a stable hashed gap ID when the legacy `gap-<cursor>` ID would exceed 512 characters;
102
+ short gap IDs and replay identity remain compatible.
103
+ Hashed IDs use the distinct `gap:` prefix, so a short token resembling a digest
104
+ cannot collide with a long token's gap ID.
105
+
106
+ Restart checks the current cursor and saved acknowledgement cursors before any
107
+ transport call or state rewrite. An invalid pre-upgrade checkpoint raises
108
+ `EXECUTION_PROTOCOL` with an explicit remedy: preserve the run files, restore the
109
+ exact bounded cursor from the owned provider checkpoint, then restart. Do not
110
+ delete the intent, reset the cursor to skip history or redispatch the task.
111
+
112
+ For intake, `makeExecution.prepare()` returns an issued `receipt` and either the
113
+ native `{cmd, args, options}` spec or a remote environment grant. The receipt
114
+ contains version 1, provider/account, authority/boundary, task ID and an input
115
+ fingerprint. `submit({kind: "intake", preparation: receipt, ...})` requires an
116
+ issued receipt matching the exact prepared input. Changing argv, prompt or grant
117
+ cannot reuse it. Environment credentials remain private; only their fingerprint
118
+ enters execution state. Native preparation uses the selected CLI/config defaults,
119
+ the existing small environment allowlist and enforced adapter argv. Optional
120
+ adapter configuration receives an AbortSignal and the transport deadline;
121
+ deadline or concurrent cancellation never issues late authority.
122
+
123
+ Drafting, clarification and repair all call this operation and the shared submit,
124
+ reconcile, collect and cancel boundary. Remote preparation returns `preparing`
125
+ while acceptance or results are unknown, retaining exact ownership for explicit
126
+ resume. It never waits in an independent polling loop. Unknown cost stays null;
127
+ each remote turn reserves up to one sixteenth of the original task budget, and
128
+ accounting retains the greater of reservation and reported usage. The existing
129
+ 16-turn, three-repair and runtime limits, immutable owner requirements,
130
+ deterministic validation/readiness and explicit implementation consent remain
131
+ canonical. Preparation grants no implementation or GitHub-writing authority.
132
+
133
+ Example normalized observation and result (provider identities are illustrative):
134
+
135
+ ```json
136
+ {
137
+ "providerJobId": "job-123", "sessionId": "session-123", "turnId": "root-2",
138
+ "state": "succeeded", "observedAt": 1780000000000, "cursor": "page-2",
139
+ "events": [{"eventId": "delta-7", "sequence": 7, "type": "output",
140
+ "timestamp": 1780000000000, "data": "verification finished"}],
141
+ "costUsd": null
142
+ }
143
+ ```
144
+
145
+ ```json
146
+ {
147
+ "providerJobId": "job-123", "sessionId": "session-123", "turnId": "root-2",
148
+ "inputHead": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
149
+ "outcome": {"finished": true, "ok": true, "cost": null, "denials": []},
150
+ "repo": "owner/repository", "branch": "work/example",
151
+ "files": [{"path": "src/example.txt", "beforeHash": null,
152
+ "afterHash": "SHA256-of-text", "text": "new content"}],
153
+ "pr": {"title": "Implement example", "body": "Reviewable receipt body"}
154
+ }
155
+ ```
156
+
157
+ Hashes must be real SHA-256 values, and input heads real exact revisions. Result
158
+ collection supplies canonical version/task/run identities even if a transport
159
+ returns conflicting fields. Missing, oversized, mismatched or incomplete results
160
+ remain unresolved; provider success alone does not mean workflow readiness.
161
+ `observedAt` is a finite UTC epoch millisecond value between the owned run's
162
+ start/last accepted observation and the coordinator clock at reconciliation.
163
+ There is no allowance for future clock skew. Inspection and lost-reply lookup
164
+ reject future observations before adopting ownership, cost, events or terminal
165
+ state, retain the last valid checkpoint, and use scoped protocol backoff.
166
+ Correct the transport's clock/units and reconcile the same owned job/key; no
167
+ replacement dispatch is needed. Older checkpoints already containing future
168
+ timestamps require restoring the last authoritative observation evidence while
169
+ preserving task/run ownership, intents and allowance records.
170
+
171
+ Private result ownership (`taskId`, `runId`, `providerJobId`, `sessionId`, `turnId`,
172
+ `inputHead`, `repo`, `branch`, `pushedHead`) retains exact protocol values through
173
+ redaction, collection and replay, even when an opaque ID resembles a credential
174
+ or equals a supplied secret. Collection validates provider identities before
175
+ saving them; publication independently validates repository, branch and revisions.
176
+ Diagnostic fields remain redacted. A previously rewritten identity is not a valid
177
+ substitute: preserve the cache and restore exact ownership from the recorded
178
+ acknowledgement/provider root before continuing; never redispatch for recovery.
179
+
180
+ `completedAt` is the exact owned root's completion time in UTC epoch milliseconds,
181
+ not submission-client exit or collection time. An inspection may supply it only
182
+ for a terminal state, between the recorded start and its authoritative observation.
183
+ A collected result may supply it between start and collection, no later than the
184
+ first terminal observation; it must agree with any recorded exact completion.
185
+ Invalid, conflicting or foreign timestamps cannot qualify output. Missing/null
186
+ timestamps remain compatible: the first terminal observation provides an inferred
187
+ upper bound, or collection time is the conservative bound for an older result.
188
+ Remote `job.json` retains compatible `endedAt` plus `completionInferred`; exact
189
+ completion and `terminalObservedAt` survive restart in execution metadata. Local
190
+ process completion and identities retain their existing semantics.
191
+
192
+ Preparation retrieves terminal results before applying runtime allowance, even
193
+ when task resume occurs after the deadline. It compares this owned completion
194
+ time (or conservative bound) with the saved inclusive deadline. Timely output is
195
+ eligible for deterministic validation; late/unknown-late output preserves result,
196
+ usage and cost evidence but cannot authorize readiness or another repair. Missing
197
+ or invalid results stay pending without cancelling an already completed root.
198
+ A timeout/unsafe-boundary stop of active or unresolved work persists its intent
199
+ in the task attempt before control I/O. Unconfirmed, failed or unsupported stops
200
+ keep the intake `preparing`, with an explicit cancellation next action and no new
201
+ dispatch. Call `task resume` with the current revision to reconcile that owned
202
+ stop through the boundary's persisted cadence until cancellation or terminal
203
+ completion is confirmed, then the task becomes `blocked` and output is quarantined.
204
+ Older blocked timeouts with a pending owned stop permit this same control-only
205
+ resume. Other blocked refusals remain inert; inspect/list never poll providers.
206
+
207
+ ## Durable state and reconciliation
208
+
209
+ `execution.json` version 1 records task/provider/account ownership, current run,
210
+ operations, stop fence and cumulative allowance. Each operation records the input
211
+ fingerprint, canonical run ID, deterministic idempotency key, intent timestamp,
212
+ input head and acceptance (`unknown` or `accepted`). Intent is atomically persisted
213
+ before submit/continue. Issued preparation receipts live in `preparations`, keyed
214
+ by their exact input fingerprints; intake execution metadata records its receipt
215
+ and `kind`. Task attempts retain receipts, reservations and cached execution
216
+ observations. Legacy scalar receipts remain valid only for the selected adapter's
217
+ declared boundary; another provider's receipt cannot authorize readiness,
218
+ answers, dispatch or recovery. The process-identity lock serializes mutations on the
219
+ operator's local filesystem; this is not a distributed multi-host lease.
220
+
221
+ Accepted execution metadata in both the registry and compatible `job.json`
222
+ contains transport kind, exact job/session/root-turn IDs, environment/model/policy, provider state,
223
+ nullable `observedAt`, `reconciledAt`, `nextAttemptAt`, failures, cursor, nullable
224
+ cost, collected/quarantined flags, controls and saved workflow ownership. Saved
225
+ workflow cursors recover a correction accepted before registry acknowledgement.
226
+ A repeated operation with identical input reuses its acknowledgement; changed
227
+ input conflicts. A trusted transport can mark a local refusal with
228
+ `dispatchNotSentError` only before persisting its transport operation and before
229
+ every provider request. The shared boundary then restores the prior root and
230
+ allowance, removes the unsubmitted projection, and retains the latest twenty
231
+ private `dispatchRefusals` (operation/run IDs, timestamp and code). Initial start
232
+ and review dispatch stop with `dispatch-refused`; repair the prerequisites and
233
+ explicitly resume. The same authorized operation can also be retried through the
234
+ shared service across restart. This release does not apply to provider setup
235
+ requests, lost replies, unmarked errors or an unproven deadline.
236
+ Remote start consumes its clone claim into a registered `launch-preparation` entry
237
+ before source delivery and read-only provider preparation. `initial_dispatch`
238
+ version 1 retains the original start-token operation and `unsubmitted` status.
239
+ `unsubmittedInitialDispatch` verifies that token, the entry's sessionless ownership
240
+ and an empty, unstopped execution ledger before CLI/control/tracker resume can
241
+ authorize the first submission. Existing private refusal proof also recovers older
242
+ initial refusal entries. The shared coordinator rebuilds verified current inputs
243
+ from the saved approved brief, changes the marker to `dispatching` before submit,
244
+ and clears it after acceptance or exact startup reconciliation. Proven pre-send
245
+ refusals restore `unsubmitted`; unknown acceptance keeps its recorded intent and
246
+ cannot start another session. Partial/unknown clone failures still retain their
247
+ original claim for inspection rather than silently deleting an owned checkout.
248
+ Unknown acceptance never authorizes another dispatch. Startup
249
+ and periodic lookup reconcile it; without authoritative lookup an operator must
250
+ inspect the recorded key and restore exact ownership. Never manually delete an
251
+ acceptance-unknown intent to force a retry. Remote submission/continuation acknowledgements and every lookup
252
+ or observation require nonempty string job and root-turn IDs. A job-only reply
253
+ keeps its reservation and unresolved intent. Incomplete older remote records
254
+ must recover the root through the intent lookup; job-level inspection or cached
255
+ results cannot fill that identity. Collection validates saved results against the
256
+ exact task/run/job/root turn as well as newly retrieved results. Raw job projections
257
+ use the recorded transport kind (or canonical registry for older records) and
258
+ cannot treat an unresolved remote root as terminal; CLI runs need no root-turn ID.
259
+ Reported remote sessions must be nonempty strings and match an already recorded
260
+ session. Omitted/null sessions retain that identity; the first reported session
261
+ may establish it when none is known. The same check protects acknowledgement,
262
+ lookup, inspection, fresh/cached collection and shared publication, before events
263
+ or usage are adopted. A foreign reply cannot authorize a native continuation.
264
+
265
+ For local adapters that do not echo a session ID, the boundary retains the session
266
+ minted by the existing start path. Legacy continuation uses the recorded registry
267
+ session for that same provider/root, or its native output identity. An active
268
+ legacy writer still fences dispatch; a foreign session cannot be substituted.
269
+
270
+ Provider states are `queued`, `provisioning`, `running`, `waiting-for-input`,
271
+ `retrieving-results`, `succeeded`, `failed`, `cancellation-requested`, `cancelled`
272
+ and `status-unknown`. They are separate from existing workflow/review states.
273
+ Stale reads, missing events, launcher exit, a cached response or silence cannot
274
+ complete a remote turn. Both success and failure enter retrieval until an exact
275
+ finished result is saved; `terminalState` retains the authoritative root outcome
276
+ while workflow settlement waits. Fresh and cached results must agree with that
277
+ outcome before persistence or settlement; the shared PR writer enforces the same
278
+ check before artifact or GitHub operations. A contradictory result stays
279
+ unresolved with `EXECUTION_RESULT`, retaining its observation, ownership and
280
+ allowance without authorizing another run. A later inspection cannot replace
281
+ the retained terminal state. Older collected records whose state contradicts
282
+ their retained observation re-enter retrieval; their private cache remains for
283
+ inspection and cannot authorize publication. Records without exact job/root
284
+ ownership cannot retain a qualified terminal claim; lookup must establish that
285
+ identity and its authoritative completion before a cache can settle it.
286
+ Correct the provider's exact owned
287
+ result before retrying collection; do not erase identities or redispatch.
288
+ Failure diagnostics, denials, usage and recovered
289
+ session identity survive collection and cached-result replay. `collectedAt` records
290
+ durable retrieval separately from owned completion/observation timestamps, so
291
+ restart does not lose cost, session or completion time. Older failure records claiming collection without a
292
+ result re-enter retrieval without redispatch. Failed artifacts remain private;
293
+ only a successful collected outcome can enter the shared publication path. A
294
+ stop during terminal result retrieval or after collection quarantines the result
295
+ without issuing a cancellation call for a root already known to have finished.
296
+ Foreign job/session/root-turn observations refuse reconciliation. Unavailable
297
+ status carries timestamps, retry time and recovery action rather than fabricated
298
+ liveness. Terminal collected results survive log removal.
299
+
300
+ Local settlement, collection, cached replay and session reads apply the same
301
+ session ownership rule before adopting an identity. A reported ID must match
302
+ the preassigned or previously discovered session; omitted IDs retain it. An
303
+ anonymous first turn may establish a nonempty string ID. Legacy runs use their
304
+ recorded registry session, and submission acknowledgements retain preassigned
305
+ identities across restart. A mismatch raises `EXECUTION_RESULT`, rejects result
306
+ adoption and prevents continuation into the foreign session. Restore
307
+ the exact owned stream/cache evidence before retrying; never transplant sessions.
308
+
309
+ ## Scheduling and limits
310
+
311
+ Configuration may override the integer `execution` limits validated by
312
+ `lib/config.mjs`; `EXECUTION_LIMITS` is the default. Per-operation transport and
313
+ health deadlines are 20 seconds, progress cadence 10 seconds, maximum exponential
314
+ backoff 5 minutes with jitter/Retry-After, concurrency 4, event pages 100 records
315
+ and 64 KiB. Native standalone CLI version/sign-in probes keep their compatible
316
+ 20-second subprocess bound; coordinator probes also honor the configured
317
+ abort deadline. The shared default includes native verification startup, which
318
+ can exceed two seconds on a healthy host. Explicit shorter deadlines still apply,
319
+ and timed-out submissions retain their unknown-acceptance fences. Remote standalone
320
+ probes use the configured `execution.deadlineMs`
321
+ for their entire serial access check, or 10 seconds when none is configured; an
322
+ earlier caller signal is retained. The configurable transport ceiling is 120
323
+ seconds. Snapshotting hosts may explicitly use 60 seconds when each outbound
324
+ request carries several seconds of authorization and persistence latency. This
325
+ does not increase task runtime/spend allowances or replace durable lost-reply
326
+ reconciliation. Health is persisted per provider **and account**. A success stops
327
+ authorizing dispatch at its saved `nextAttemptAt`, before the due refresh starts
328
+ and while it waits for a probe worker or reply. Read-only health checks enforce
329
+ that deadline across restart without probing or changing the saved observation.
330
+ Fresh scopes remain independent; failed checks retain their backoff and remedy.
331
+ The shared fix/resume path rechecks health after checkout, credentials, input-head
332
+ and remote preparation, immediately before submit/continue or local launch. A
333
+ failure at that boundary leaves feedback and run ownership available for retry
334
+ on the existing coordinator schedule, without a submission intent or paid call.
335
+
336
+ One coordinator tick has separate bounded progress and GitHub review worker
337
+ lanes. Provider checks run alongside them and register due scopes before the
338
+ tick. Dispatches waiting on health leave the review workers free. A bounded
339
+ dispatch lane joins only those already scheduled scoped probes, then retries
340
+ once after the observation is persisted. It uses the same dispatch function,
341
+ rechecks registry ownership and stop fences, and never starts probes itself.
342
+ A successful refresh can release work in that tick; failures preserve the
343
+ feedback cursor for the next review interval. Progress polling is independent of
344
+ `poll_seconds`, which still schedules entry review and chain merge/ancestry reads.
345
+ Each tick instance checks chains immediately on boot, then at most once per
346
+ review interval; failed chain checks consume retries only on that schedule.
347
+ GitHub authentication health has a separate bounded lane and `github-health.json`
348
+ scope keyed by the startup login. It refreshes on `poll_seconds` with the same
349
+ backoff/deadline rules, publishes `probes.gh` in daemon status and retains the
350
+ existing `gh not ready` notification and authentication remedy. It cannot consume
351
+ provider probe workers or fence unrelated execution progress. The native GitHub
352
+ probe receives the abort signal, including a credential lookup that finishes
353
+ after its deadline. Status/UI reads only persisted observations and never probe.
354
+ Startup forces reconciliation
355
+ once, then saved retry times govern subsequent inspection. Provider failures do
356
+ not globally suppress ticks. UI polling reads these persisted results.
357
+
358
+ ## Journal, projections and retention
359
+
360
+ Each new run retains compatible `job.json`/`job.log` plus:
361
+
362
+ | File | Contract |
363
+ | --- | --- |
364
+ | `events.jsonl` | Version 1 redacted normalized records: task/run/provider, event ID, ingestion sequence, provider sequence, provenance and timestamp |
365
+ | `journal.json`, `event-keys/` | Durable append checkpoint and hashed exact provider/job/turn/event replay keys; interrupted append becomes an explicit gap |
366
+ | `job.idx`, `log-meta.json` | Fixed-width 8-byte offsets, version 2 offset-plus-one encoding, stable sequence numbers and retained first sequence; zero entries denote gaps |
367
+ | `local-output.json` | Raw local ingestion byte cursor, bounded page, pending marker and persistent `discarding` state |
368
+ | `local-output-queue.json` (task run root) | Version 1 ordered local run IDs awaiting projection or private-file cleanup; persisted before replacing `latest-run` |
369
+ | `result.json` | Mode 0600 private collected result, exact ownership fields and artifact manifest/payloads, redacted diagnostics and `artifacts` summary; independent of retained logs |
370
+ | `result-application.json`, `verification.json` | Artifact application/publication checkpoint and actual approved verification observations |
371
+
372
+ Provider-order numbers survive out-of-order arrival; ingestion order remains
373
+ monotonic. Replay keys survive restart and projection retention. Rotation,
374
+ truncation, missing indexes, oversized records and unrecoverable interrupted
375
+ history produce explicit gaps rather than renumbering replay IDs or inventing
376
+ output. Redaction occurs before journals, public projections and saved evidence,
377
+ using recognized credentials and supplied resolved secret values.
378
+ Overlapping supplied values are replaced longest first without changing the
379
+ caller's secret list, so environment order cannot expose a credential suffix.
380
+ Verification merges coordinator environment and execution/run secrets in one
381
+ redaction pass via `sanitize(value, runId, extraSecrets = [])`; existing callers
382
+ can omit the extra list.
383
+ Artifact paths, hashes and text retain the provider's exact bytes in the private
384
+ result. Diagnostic redaction never rewrites publication input. The coordinator still checks original
385
+ hashes and ownership before application. Modified/truncated content cannot publish.
386
+ The additive version 1 `artifacts` field is a redacted path/hash summary generated
387
+ by collection, overriding any provider-supplied summary. Tracker result responses
388
+ use this summary for artifacts and application paths, and never include file text.
389
+ The result route redacts its complete response through the execution's supplied
390
+ and retained run secrets, including ownership fields and publication checkpoints;
391
+ standalone run readers use recognized-credential redaction. These read-only views
392
+ never replace private routing identities or call a provider.
393
+ Legacy results without the summary retain their already-redacted projection.
394
+ Verification output also uses the execution boundary's supplied-secret redaction.
395
+ Previously modified cached artifacts remain invalid; restore exact artifacts for
396
+ the recorded provider job/root turn before publication, without redispatching.
397
+
398
+ Journal append recovery reads at most 65,537 bytes at the saved pending offset.
399
+ An invalid or partial pending record is removed by truncating to that offset
400
+ before a complete gap record replaces it. Previously acknowledged bytes remain
401
+ intact. A complete pending record with a missing newline receives its delimiter
402
+ before another event can append. Recovery and subsequent replay preserve the
403
+ original sequence/event ID and do not append a second replacement gap. If an
404
+ existing journal is shorter than the acknowledged offset, recovery refuses and
405
+ preserves it: restore its matching backup before replay instead of extending
406
+ the file with zero bytes or claiming a damaged prefix was repaired.
407
+
408
+ Completed local output may outlive its current-run pointer. Before a successor
409
+ can save an execution intent or start, the runner preserves the old run in
410
+ `local-output-queue.json` while output is pending or either private diagnostic
411
+ file remains, including drained and empty logs. Coordinator reconciliation drains
412
+ one queued run page and one current-run page, each at most 100 records and 262,144
413
+ raw bytes (200 records and 524,288 bytes per observation; maximum single buffer
414
+ 262,144 bytes). Runs awaiting projection or cleanup rotate through the queue;
415
+ drained runs check expiry without reading diagnostic bytes or rewriting journals.
416
+ Journal byte-position identities and atomic checkpoints make restart/replay
417
+ duplicate-free. The queue is serviced even when legacy execution metadata has
418
+ no current record. Public status, tail and history reads do not ingest diagnostics
419
+ or mutate this queue.
420
+ Results settle independently of this backlog, so a large old log cannot postpone
421
+ unrelated execution or GitHub review. Unsupported queue versions, invalid cursors
422
+ and queue persistence errors preserve diagnostics and refuse a new paid intent;
423
+ restore the matching compatible package/checkpoint before dispatch or replay.
424
+
425
+ An oversized raw local record emits one stable gap at its starting byte ID.
426
+ `discarding: true` then consumes bounded pages through that record's newline;
427
+ no suffix can enter normalized history or the public projection. At an active
428
+ EOF the flag remains pending until a delimiter or confirmed local exit. A final
429
+ unterminated oversized record is discarded through EOF. Restart, queue rotation
430
+ and cursor acknowledgement replay retain this state. Older mid-record cursors
431
+ are recognized with a one-byte lookbehind inside the same per-page read budget
432
+ and discard the remaining continuation. Invalid discard state refuses dispatch
433
+ or replay with the checkpoint/backup remedy.
434
+
435
+ Private local diagnostics use `provider.log` and `sensitive-redactions.json`
436
+ (mode 0600 in the private run tree). They are excluded from routes/projections.
437
+ The redaction metadata records a 24-hour expiry; coordinator observation removes
438
+ expired diagnostics once a durable result exists, retaining the queue item until
439
+ both private files have been removed. Removal failures preserve the item for a
440
+ later coordinator retry. If redaction metadata is missing, the owned job's start
441
+ time supplies the 24-hour expiry; missing metadata never authorizes raw projection.
442
+ If queued bytes expire before projection, or diagnostics/redaction metadata are
443
+ missing or truncated, the coordinator appends a stable explicit gap and retains
444
+ any remaining private files for expiry cleanup. Results, job outcomes, session
445
+ identity and public history
446
+ survive this cleanup, and read-only routes cannot trigger it. The version 1 queue
447
+ format remains compatible. Runs already omitted from the queue by an older
448
+ package require explicit retention maintenance: after checking expiry and a
449
+ durable `result.json`, remove only `provider.log` and `sensitive-redactions.json`.
450
+ Never remove result/application evidence or change `latest-run` for cleanup.
451
+ Legacy raw local files keep their existing runner semantics.
452
+
453
+ Public tail/history reads are capped at 400 lines, 256 KiB data plus at most
454
+ 3,208 index bytes, 64 KiB per encoded line. JSON escaping is included in response
455
+ budgets. Large legacy logs without indexes use a bounded suffix and explicit
456
+ byte-position replay IDs; small legacy logs preserve numeric line IDs. New logs
457
+ always have stable numeric journal sequence IDs. An absent index with existing
458
+ metadata is an explicit recovery gap, not a legacy renumbering fallback.
459
+
460
+ Existing `/api/run/output`, `/api/run/log`, `/api/run/history` and
461
+ `/api/run/status` routes remain compatible, including mount prefixes and foreign
462
+ configured run trees. `Last-Event-ID` resumes replay; history's `before` cursor
463
+ paginates older windows. SSE has a 20-second heartbeat, a 320 KiB pending-buffer
464
+ cap, pauses file reads on backpressure, and removes watchers/timers/listeners on
465
+ disconnect. The browser caps live and history DOM at 2,000 lines each and pending
466
+ lines at 400. Buffered events retain their SSE replay IDs. Display drops coalesce
467
+ into a bounded pair of recovery cursors, separate from the oldest history cursor;
468
+ the existing history button retrieves the missing windows without a `skipped`
469
+ frame. Each rendered row keeps its event replay ID and stable position within
470
+ that event. History restores missing row positions even when a sibling remains,
471
+ merges by event ID and row position, avoids duplicates with painted/pending rows
472
+ and preserves older pagination through bursts and reconnects, including legacy
473
+ byte IDs. DOM eviction also exposes recoverable history. A failed history read
474
+ retains its cursor; page exit aborts the read and discards pending/late frames.
475
+ `/api/run/result` exposes saved outcomes, artifact hashes and
476
+ verification evidence after log retention, without exposing raw diagnostics.
477
+
478
+ ## Controls, costs and publication
479
+
480
+ `cancel` queues a protected operator control and immediately fences dispatch.
481
+
482
+ The final publication step and cancellation queueing share an entry-scoped
483
+ `publication.lock` using the existing process-identity lock helper. Publication
484
+ takes this lock before `execution.lock`, rechecks registry and private stop/root
485
+ ownership, and holds both through branch push, PR write and the published
486
+ checkpoint. An accepted cancellation precedes and prevents these writes. Once
487
+ publication has started, cancellation returns `EXECUTION_PUBLICATION_BUSY` with
488
+ a retry message and saves no request/fence; retry after publication completes.
489
+ Direct cancellation/continuation also cannot enter its execution lock. Provider
490
+ inspection holds only `execution.lock`, so operator cancellation can still queue
491
+ while progress I/O runs. Other entries remain independent. Both locks release
492
+ after success, failed I/O/checkpoint writes or contention; dead process owners
493
+ use existing identity-checked recovery, never elapsed-time expiry of a live lock.
494
+
495
+ The coordinator records cancellation requested before calling a supported transport,
496
+ then distinguishes authoritative confirmation from unsupported/unconfirmed
497
+ cancellation. Native local cancellation retains verified process-tree cleanup;
498
+ active runs carried across an upgrade are adopted with their exact local run and
499
+ session identity before cancellation. This stop-only record has `legacy: true`,
500
+ `operationId: null`, unknown cost and no invented submission intent. Cancellation
501
+ remains requested while owned descendants survive or inspection is unavailable;
502
+ restart reconciliation confirms only the same observed run and quarantines its
503
+ late output. A kill request alone never confirms cessation.
504
+ Coordinator reconciliation also resumes the runner's persisted `killAfter`
505
+ fallback when its original timer owner exited. The existing process-identity
506
+ checks still refuse reused PIDs or unavailable inspection, and the same owned
507
+ run remains cancellation-requested until its descendants are confirmed stopped.
508
+ If a launch reply was lost, reconciliation first records exact local acceptance
509
+ and then initiates any already-requested supported stop, even when no kill was
510
+ previously started. Unsupported stops and mismatched jobs remain fenced; failed
511
+ process inspection remains pending for later coordinator reconciliation.
512
+ Read-only status/history access never signals a process.
513
+ Cancelling after a local run has been collected preserves its success/failure,
514
+ exit, end time, result, usage and session; only future dispatch is fenced. No
515
+ process stop is requested for that completed run, including when cancellation
516
+ capability is unavailable. Restart reconciliation preserves the same historical
517
+ outcome. Completed pre-boundary local jobs also retain their original files and
518
+ identity without an invented stop/adoption record. Active local stops still
519
+ require exact process-tree confirmation and quarantine their late results.
520
+ If an owned local job has already exited before collection, cancellation saves
521
+ its exact exit/end-time snapshot and the future-dispatch fence, then uses the
522
+ same local settlement helper to retain its outcome, usage and session. A failed
523
+ cache write remains in `retrieving-results`; restart reconciliation or a repeated
524
+ cancel completes that saved snapshot. This requires the matching job, an actual
525
+ recorded exit and no prior active stop. Uncertain exits and output arriving after
526
+ an active stop retain the existing cancellation/quarantine path.
527
+ Remote cancellation never equates stopping a submission client with stopping its
528
+ job. The additive `execution.cancellation` record stores status (`pending`,
529
+ `acknowledged`, `confirmed`, `unsupported` or `terminal`), request reason/time,
530
+ idempotency key, attempt/failure counts and retry times. A stop requested during
531
+ unknown submission acceptance remains pending. Authoritative lookup first saves
532
+ exact owned job/root identity, then the existing coordinator sends the stop.
533
+ Lost stop replies retry the same key under bounded backoff; transports must make
534
+ that cancellation key idempotent. An acknowledged unconfirmed stop is inspected
535
+ until cancellation is authoritative, without resending every tick. Unsupported
536
+ stops never call the transport. Natural completion still quarantines the output
537
+ and records `terminal`, without claiming cancellation. Applied control receipts
538
+ are updated when the matching run's later reconciliation confirms cancellation
539
+ or observes natural completion. `cancel()` returns `{confirmed: false,
540
+ terminal: true}` for an already ended remote root, distinguishing it from an
541
+ unconfirmed live stop. The operator task is stopped with reason `cancelled` and
542
+ a receipt stating that provider execution had already completed; its historical
543
+ provider success/failure, exit, completion time and collected result remain
544
+ unchanged. Existing pending receipts are repaired only for their matching run.
545
+ No read-only client drives this recovery. Interrupted continuity requires native
546
+ capability and exact session ownership.
547
+ Cancelled/timed-out work cannot overlap a replacement; late results are
548
+ quarantined. A requested deadline abort leaves unknown acceptance unresolved.
549
+
550
+ A local implementation `timeout` fences dispatch while its owned process tree
551
+ is stopping. Cancellation or restart reconciliation releases only this timeout
552
+ fence after the exact matching local run is confirmed non-live; missing/different
553
+ runs, surviving descendants and unreadable process state keep it fenced. The
554
+ timed-out run remains collected and quarantined, so late success or a saved
555
+ result cannot rewrite settlement or qualify publication. The canonical timeout
556
+ stop and stranded-work diagnostics remain on the entry; explicit `resume` can
557
+ then continue the same session in its existing branch/worktree. This also repairs
558
+ older local timeout fences on reconciliation. Operator cancellation, preparation
559
+ allowance stops and remote timeout fences remain in force. A later timeout retry
560
+ cannot replace an operator or preparation stop with a recoverable timeout.
561
+
562
+ Remote dispatch requires a positive reservation and positive finite remaining
563
+ allowance. Reservations accumulate durably, including unknown usage; they are not
564
+ released on missing cost. Accounting charges the greater of each reservation and
565
+ its reported usage, preserves higher prior usage and deduplicates repeated reads.
566
+ Execution metadata and settlement outcomes retain the maximum finite nonnegative
567
+ cost reported for the exact run. Missing, invalid or lower observations and
568
+ collected estimates cannot erase known spend. Private results preserve the
569
+ provider's reported cost; `outcome(runId)` and the tracker result projection
570
+ combine it with that run's durable operation cost, including after restart or
571
+ when a subsequent turn is active. These reads never poll a provider.
572
+ Older regressed projections recover known cost from their owned operation.
573
+ Unknown cost remains null and is never reported as zero.
574
+ An exhausted allowance refuses further dispatch. Future providers must enforce
575
+ any mandatory hard cap in their declared capability; this boundary does not
576
+ advertise billing enforcement a transport cannot provide.
577
+
578
+ Shared result publication validates task/run/root-turn/input-head ownership,
579
+ repository origin, task branch/base, paths/symlinks and artifact content/base
580
+ hashes before mutation. Every artifact target is canonicalized with the shared
581
+ repository containment/symlink validator before any content is read. Equivalent
582
+ spellings (`a.txt`, `./a.txt`, repeated separators) cannot name duplicate targets.
583
+ The manifest also rejects paths that collide after NFC normalization and Unicode
584
+ upper/lower folding, including file or parent-directory case aliases. This is a
585
+ conservative portable manifest rule on every filesystem, including case-sensitive
586
+ checkouts: distinct files with ambiguous case/Unicode spellings require separate
587
+ results. A single accepted path keeps its casing and original artifact bytes;
588
+ application, commit paths and lost-acknowledgement reconciliation use the same
589
+ canonical Git-relative paths. The private provider manifest remains unchanged.
590
+ Each target component is also compared with tracked HEAD and index paths before
591
+ content reads or mutation. A single artifact cannot introduce a case/Unicode
592
+ alias of an existing file or parent directory, including a directory with a
593
+ different child filename or a file/directory collision. Exact tracked spelling
594
+ remains valid for edits and deletions. The same check runs before publication
595
+ recovery; pushed results validate their complete tracked tree. Published receipt
596
+ replay remains read-only. Missing or incomplete metadata blocks publication.
597
+ Git path reads use NUL separators, preserving spaces and newlines, with a
598
+ 2-second deadline and 1 MiB output cap per HEAD/index query. Ownership checks on
599
+ staged and changed paths reuse that bounded reader without trimming filenames.
600
+ Text-only manifests preserve each target's input Git mode (`100644` or `100755`);
601
+ new files use `100644`. Before reading payloads or applying changes, the shared
602
+ Git helper compares input, current HEAD and index modes with the actual regular
603
+ file's owner executable bit. Uncommitted, staged and already-committed mode
604
+ changes cannot be adopted into a text result, even when its content hash matches.
605
+ Missing, malformed or incomplete mode metadata refuses publication; tree/index
606
+ records reuse the NUL reader's 2-second and 1 MiB limits. The same guard covers
607
+ lost commit replies and verified-result replay. Exact-mode edits/deletions and
608
+ regular creations remain recoverable; pushed revisions describe their own modes.
609
+ Applied content and input ancestry reconcile lost commit
610
+ acknowledgements. Only existing Git/GitHub helpers commit, push and reconcile one
611
+ regular PR. Approved immutable PRD Verify commands run in the owned checkout
612
+ with real exit/output/timestamp/revision evidence (120-second, 256 KiB command
613
+ bounds). They use a fixed non-login POSIX shell (`/bin/sh -c`), preserving the
614
+ approved command text without selecting the operator's `SHELL` or sourcing login
615
+ startup files. Verify commands must use POSIX shell syntax and explicitly invoke
616
+ any other interpreter they need. Provider assertions are not verification
617
+ evidence. Verification receives an explicit allowlist of PATH, locale, timezone,
618
+ terminal/color and CI settings. Coordinator tokens, provider credentials,
619
+ proxies, SSH agents, shell startup and Node injection variables are excluded.
620
+ An empty private HOME/XDG/temp profile exists for the command list and is removed
621
+ on success, failure, dirty-tree rejection or cancellation. Existing Git/tooling
622
+ environment helpers disable system/global credential stores, interactive auth
623
+ and user npm configuration; repository credential helpers are reset too.
624
+ Verification also requires an OS filesystem/network sandbox. macOS uses
625
+ `/usr/bin/sandbox-exec` with the installed `dyld-support.sb` loader rules and a
626
+ deny-default profile. Linux uses `/usr/bin/bwrap` with isolated user, PID, IPC and
627
+ network namespaces and a private mount tree. Only the checkout and private
628
+ verification profile are writable; fixed system tools/libraries, the running
629
+ Node binary and its npm package are readable. Git metadata is hidden or denied.
630
+ PATH is limited to that Node installation, `/usr/bin` and `/bin`; arbitrary
631
+ operator PATH entries and configuration directories are not granted. Commands
632
+ needing other tools must use an isolated supported runtime installation rather
633
+ than broadening access to an operator home. Network-dependent Verify commands
634
+ fail: fetch dependencies in the approved execution environment or use installed
635
+ checkout dependencies for offline verification.
636
+ Before repository code runs, a bounded trusted probe checks private writes,
637
+ denied outside reads/writes and host TCP isolation. No missing executable,
638
+ namespace restriction or failed probe can fall back to an ordinary host shell.
639
+ It records `verification-sandbox-unavailable`, preserves the applied revision
640
+ and saved results, and requires explicit operator recovery after host repair.
641
+ Success/failure evidence records the sandbox driver and enforcement probe.
642
+ Recovered `verified` checkpoints lacking this provenance block before push;
643
+ completed commands are never silently replayed. Empty Verify lists still record
644
+ no test claim. The profile and probe files are removed on every exit.
645
+ Git checks use an immutable temporary `GIT_DIR` containing only the exact current
646
+ commit/tree/blob objects and a reconstructed index; the real Git directory or
647
+ linked-worktree pointer stays hidden. Config, hooks, remotes, reflogs, alternate
648
+ stores and ancestor history are not copied. The shared process helper preserves
649
+ binary Git object bytes, and the same native read-only policy protects the view
650
+ on macOS and Linux. macOS Git additionally requires its fixed installed Command
651
+ Line Tools runtime. The view has bounded object count, bytes and preparation time
652
+ and is removed on success, refusal, command failure and stop.
653
+ Remote readiness also exercises this enforcement in disposable directories with
654
+ the same empty verification environment and a trusted empty Git commit; actual
655
+ Git reads and denied index writes prove that the runtime and read-only metadata
656
+ boundary work before cloning. Doctor and initial start check before
657
+ API access or cloning; manual resume and review dispatch check before checkout
658
+ recreation or base merging, and the managed transport checks before new provider
659
+ dispatch. Failed/unproven/aborted probes refuse with
660
+ `EXECUTION_VERIFICATION_SANDBOX` and the existing OS/runtime remedy. No repository
661
+ code runs, and temporary profiles are removed on every path. Preparation and
662
+ provider-health records retain the successful probe evidence. Read-only progress,
663
+ artifact collection and cancellation remain available after host refusal. The
664
+ publication-time probe still verifies the actual applied checkout and descendants.
665
+ Manual resume uses full native/API readiness before recreation, merging,
666
+ resnapshot acceptance or reopening state. API refusal preserves the existing
667
+ brief/source snapshots and session/PR along with the checkout.
668
+ Hosted resume additionally refuses ordinary unfinished repository files before
669
+ merge or reopening; commit those intended edits on the owned branch and resume
670
+ the same session. Source preparation repeats this check for all dispatch paths,
671
+ allowing only approved brief/carried-source overlays with verified saved bytes.
672
+ An unanswered operator request uses the same durable signature/key on explicit retry: the
673
+ shared boundary calls optional `recoverResponse`, leaving transports without a
674
+ safe recovery method ambiguous. The managed adapter verifies the current owned
675
+ pending `ask_operator` call before replay, or confirms matching successful saved
676
+ output across bounded pages; an absent action alone never proves acceptance.
677
+ The canonical tick continues to own readiness, trusted feedback,
678
+ freshness, receipts, conflicts, rounds and human merge boundaries. No automatic
679
+ merge is added.
680
+
681
+ Permanent publication validation failures also persist a `blocked` checkpoint
682
+ with reason `result-validation`, retaining any previous fingerprint, applied head
683
+ and evidence. Invalid repository/branch ownership, paths, modes, hashes or public
684
+ text cannot be applied or retried on each poll. The publication boundary rejects
685
+ NUL bytes in raw PR titles and bodies before normalization or artifact application,
686
+ including pushed and previously verified results. Shared GitHub writers and
687
+ recovery-only metadata migration use the same NUL validation without rewriting
688
+ payloads or exposing rejected text in errors. The canonical needs-human lane
689
+ settles the provider's actual exit, cost and session, and an explicit same-session
690
+ resume starts correction without clearing the rejected result. Missing collection
691
+ results, unavailable Git metadata and ordinary I/O failures remain unresolved and
692
+ retryable. A failed checkpoint write cannot claim durable blocking or settlement.
693
+
694
+ Transports distinguish deterministic validation of a downloaded completed-turn
695
+ artifact with `collectionValidationError` from unavailable delivery or I/O.
696
+ The shared collection boundary persists an owned `blocked` checkpoint with reason
697
+ `result-collection-validation` before settling liveness. `resultRejected: true`
698
+ retains the authoritative root status and actual exit while reporting unsuccessful
699
+ delivery; no valid `result.json` is invented. The checkpoint restores settlement
700
+ after a crash between its write and execution/job projection writes, using exact
701
+ task/run/job/session/turn/input-head ownership. The coordinator refreshes that
702
+ run's state before entering the same `needs-human` corrective lane. Repeated polls
703
+ and rechecks cannot recollect rejected input; explicit same-session continuation
704
+ keeps the old rejection evidence and starts a new owned turn.
705
+
706
+ Verification requires the recorded applied commit and a clean Git index/worktree,
707
+ including untracked files. The existing strict Git status helper checks both
708
+ before commands, after each successful command and before PR publication; a
709
+ status failure blocks publication. Ignored build outputs remain subject to the
710
+ repository's own ignore rules. No verification-created edits are silently
711
+ committed, discarded or represented as part of the verified revision.
712
+
713
+ The application checkpoint advances through `applying`, `verifying`, `verified`
714
+ and `published`. A failed command, changed revision, dirty checkout or unavailable
715
+ status/evidence becomes `blocked`, with its reason and `blockedAt` timestamp.
716
+ Each completed command persists real evidence, including a failing exit or an
717
+ unknown exit on a transport error. A restart that finds `verifying` records
718
+ `verification-interrupted`: commands may have run and cannot be replayed safely.
719
+ The coordinator settles the provider run separately and enters `needs-human`
720
+ with reason `result-verification`; provider success and unknown cost stay truthful.
721
+ Polling, read-only projections and PR recheck cannot retry blocked verification.
722
+ Inspect the private checkpoint, evidence and retained checkout, then explicitly
723
+ resume the same session (or send a correction) to authorize a new corrective run.
724
+ The old run's blocked evidence remains intact. A push/PR acknowledgement failure
725
+ after `verified` retries only publication, requiring the same clean revision and
726
+ complete successful evidence for the approved command list. A published replay
727
+ returns the saved PR without executing commands again.
728
+
729
+ ## Upgrade and rollback
730
+
731
+ 1. Stop the coordinator and preserve its private home, registry, queued chain
732
+ snapshots, run trees and immutable PRD/source snapshots. Do not interrupt local
733
+ children merely to upgrade; their recorded identity survives launcher exit.
734
+ 2. Install the compatible package and restart one coordinator. Legacy version 1
735
+ registry entries/local runs stay readable without changing task/PR/session IDs.
736
+ Startup reconciles recorded runs before paid dispatch. Review the status and
737
+ recovery action for every unresolved acceptance. A pre-upgrade `verifying`
738
+ checkpoint stops as interrupted; inspect its preserved evidence and explicitly
739
+ resume for correction instead of deleting it to rerun verification.
740
+ 3. Back up the entire matching state before rollback. Older packages can retain
741
+ legacy local registry/job files, but must not operate active remote metadata or
742
+ new private-output runs they do not understand. Restore the matching package
743
+ and state backup together; do not clear run pointers, transplant sessions or
744
+ erase intents to make an old coordinator proceed.
745
+ 4. An unsupported registry, execution, health, local output queue or journal version fails with an
746
+ actionable compatible-package/backup remedy. Preserve the original files and
747
+ resolve that version error before restarting; redispatch is not a migration.
748
+
749
+ Run `npm run test:execution-backends` for focused G3/G3a/G6 fixture coverage and the
750
+ full Verify set for regressions. [The foundation evidence](https://github.com/uchibeke/gallivant/blob/main/docs/audit/plugin-ready/execution-foundation.json)
751
+ is an offline G8 receipt, with live-provider status explicitly unqualified.
752
+ The [shared preparation follow-up](https://github.com/uchibeke/gallivant/blob/main/docs/audit/plugin-ready/execution-preparation.json)
753
+ records the owner correction, final verification and source/output review.
754
+
755
+ ## Integration boundary and recorded milestone handoff
756
+
757
+ M3/M4 and APort must consume these shared preparation/submit/inspection/result/
758
+ control operations and the existing task service, without copying a runner or
759
+ adding a review engine. An implemented provider registers preparation version 1,
760
+ its concrete `boundary`, limited `authority` and remote `environment`; a CLI
761
+ adapter additionally supplies its enforced `render`, optional abortable
762
+ `configure` and required native minimum version. The context's `capabilitiesFor`
763
+ and `executionFor` must use that same registry. Mark provider availability only
764
+ after its implemented transport and required live evidence exist.
765
+
766
+ The restricted Codex preparation adapter introduced in
767
+ [`uchibeke/gallivant#10`](https://github.com/uchibeke/gallivant/pull/10) is registered
768
+ alongside the shared execution foundation and Agents API transport. Consume its
769
+ registry capability rather than copying its adapter or restoring a provider-name
770
+ gate. Drafting, questions and repairs already use the selected
771
+ provider through this shared execution contract. Validation/readiness remains
772
+ deterministic, and implementation still requires explicit authorization.
773
+
774
+ The [preparation qualification](preparation.md) records native Codex 0.160.0 checks
775
+ against a loopback provider, including inherited notifier and effective-policy
776
+ regressions. That evidence tests installed CLI/configuration
777
+ enforcement; it does not establish paid model access, managed preparation or
778
+ installed-plugin behavior. Preserve the operator's model/provider defaults,
779
+ limited preparation authority, bounded attempts and execution consent. Carry
780
+ unmet live qualification evidence forward rather than substituting fixtures.
781
+
782
+ The original M5/M6 handoff identified the following connected execution successor.
783
+ This is a recorded integration plan, not evidence that the external deployment
784
+ is live or its gates have passed. Check the linked PR and the consumer's current
785
+ release evidence before an installation:
786
+
787
+ The connected execution successor is
788
+ [`aporthq/agent-passport#417`](https://github.com/aporthq/agent-passport/pull/417),
789
+ with canonical PRD `docs/prds/aport-task-delivery-workflow-integration.md` and job
790
+ identity `aporthq/agent-passport/aport-task-delivery-workflow-integration`. It
791
+ preserves the original T0–T9 requirements; the recorded plan requires the actual
792
+ M5 portable artifact before its integration gate can pass. M6 must reuse it, pin the artifact's version, commit,
793
+ integrity and actual exports, connect existing APort authentication, regional
794
+ storage and shared UI to this task service and single coordinator, and qualify the
795
+ full workflow. No second runner, duplicate foundation job or other-repository
796
+ implementation is part of this API milestone.