@tiphys/kernel 0.0.0 → 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 (207) hide show
  1. package/AGENTS.md +663 -0
  2. package/LICENSE +202 -0
  3. package/assurance-modes.yaml +299 -0
  4. package/checklists/clean-room.yaml +325 -0
  5. package/checklists/env-failure-diagnosis.yaml +68 -0
  6. package/checklists/flake-playbook.yaml +68 -0
  7. package/checklists/hazard-review.yaml +144 -0
  8. package/checklists/plan-review.yaml +103 -0
  9. package/dist/bin/tiphys.d.ts +2 -0
  10. package/dist/bin/tiphys.js +92 -0
  11. package/dist/src/adapters/load.d.ts +202 -0
  12. package/dist/src/adapters/load.js +440 -0
  13. package/dist/src/brief.d.ts +32 -0
  14. package/dist/src/brief.js +66 -0
  15. package/dist/src/checklists.d.ts +169 -0
  16. package/dist/src/checklists.js +310 -0
  17. package/dist/src/checks.d.ts +1539 -0
  18. package/dist/src/checks.js +5025 -0
  19. package/dist/src/cli.d.ts +9 -0
  20. package/dist/src/cli.js +68 -0
  21. package/dist/src/commands/brief.d.ts +92 -0
  22. package/dist/src/commands/brief.js +343 -0
  23. package/dist/src/commands/checklist.d.ts +42 -0
  24. package/dist/src/commands/checklist.js +168 -0
  25. package/dist/src/commands/cutover.d.ts +35 -0
  26. package/dist/src/commands/cutover.js +448 -0
  27. package/dist/src/commands/doctor.d.ts +264 -0
  28. package/dist/src/commands/doctor.js +1546 -0
  29. package/dist/src/commands/gates.d.ts +9 -0
  30. package/dist/src/commands/gates.js +360 -0
  31. package/dist/src/commands/init.d.ts +30 -0
  32. package/dist/src/commands/init.js +177 -0
  33. package/dist/src/commands/lock.d.ts +34 -0
  34. package/dist/src/commands/lock.js +340 -0
  35. package/dist/src/commands/mode.d.ts +40 -0
  36. package/dist/src/commands/mode.js +134 -0
  37. package/dist/src/commands/next.d.ts +130 -0
  38. package/dist/src/commands/next.js +597 -0
  39. package/dist/src/commands/plan.d.ts +20 -0
  40. package/dist/src/commands/plan.js +105 -0
  41. package/dist/src/commands/pool.d.ts +1 -0
  42. package/dist/src/commands/pool.js +139 -0
  43. package/dist/src/commands/resume.d.ts +1 -0
  44. package/dist/src/commands/resume.js +88 -0
  45. package/dist/src/commands/spawn.d.ts +1 -0
  46. package/dist/src/commands/spawn.js +195 -0
  47. package/dist/src/commands/status.d.ts +21 -0
  48. package/dist/src/commands/status.js +122 -0
  49. package/dist/src/commands/sync.d.ts +47 -0
  50. package/dist/src/commands/sync.js +341 -0
  51. package/dist/src/commands/teardown.d.ts +1 -0
  52. package/dist/src/commands/teardown.js +87 -0
  53. package/dist/src/commands/tuition.d.ts +21 -0
  54. package/dist/src/commands/tuition.js +218 -0
  55. package/dist/src/commands/validate.d.ts +78 -0
  56. package/dist/src/commands/validate.js +430 -0
  57. package/dist/src/commands/watch.d.ts +1 -0
  58. package/dist/src/commands/watch.js +172 -0
  59. package/dist/src/cutover.d.ts +584 -0
  60. package/dist/src/cutover.js +1444 -0
  61. package/dist/src/exclusion.d.ts +389 -0
  62. package/dist/src/exclusion.js +843 -0
  63. package/dist/src/exec/env.d.ts +278 -0
  64. package/dist/src/exec/env.js +334 -0
  65. package/dist/src/fleet.d.ts +223 -0
  66. package/dist/src/fleet.js +298 -0
  67. package/dist/src/gates/adapters/http-json.d.ts +5 -0
  68. package/dist/src/gates/adapters/http-json.js +283 -0
  69. package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
  70. package/dist/src/gates/adapters/migrations-command.js +373 -0
  71. package/dist/src/gates/citations.d.ts +408 -0
  72. package/dist/src/gates/citations.js +1169 -0
  73. package/dist/src/gates/coverage.d.ts +375 -0
  74. package/dist/src/gates/coverage.js +836 -0
  75. package/dist/src/gates/credentials.d.ts +233 -0
  76. package/dist/src/gates/credentials.js +752 -0
  77. package/dist/src/gates/deploy.d.ts +1 -0
  78. package/dist/src/gates/deploy.js +33 -0
  79. package/dist/src/gates/gate-classes.d.ts +56 -0
  80. package/dist/src/gates/gate-classes.js +633 -0
  81. package/dist/src/gates/manifest.d.ts +99 -0
  82. package/dist/src/gates/manifest.js +208 -0
  83. package/dist/src/gates/merge-preconditions.d.ts +319 -0
  84. package/dist/src/gates/merge-preconditions.js +932 -0
  85. package/dist/src/gates/migrations.d.ts +1 -0
  86. package/dist/src/gates/migrations.js +36 -0
  87. package/dist/src/gates/pin.d.ts +114 -0
  88. package/dist/src/gates/pin.js +154 -0
  89. package/dist/src/gates/red-witness.d.ts +22 -0
  90. package/dist/src/gates/red-witness.js +482 -0
  91. package/dist/src/gates/release.d.ts +283 -0
  92. package/dist/src/gates/release.js +820 -0
  93. package/dist/src/gates/result.d.ts +116 -0
  94. package/dist/src/gates/result.js +91 -0
  95. package/dist/src/gates/run.d.ts +614 -0
  96. package/dist/src/gates/run.js +1614 -0
  97. package/dist/src/gates/schemas/citation-config.schema.json +59 -0
  98. package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
  99. package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
  100. package/dist/src/gates/schemas/gate-result.schema.json +160 -0
  101. package/dist/src/gates/schemas/phase-declaration.schema.json +87 -0
  102. package/dist/src/gates/schemas/release-record.schema.json +119 -0
  103. package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
  104. package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
  105. package/dist/src/gates/scope.d.ts +131 -0
  106. package/dist/src/gates/scope.js +1018 -0
  107. package/dist/src/gates/suite.d.ts +217 -0
  108. package/dist/src/gates/suite.js +968 -0
  109. package/dist/src/gates/validate.d.ts +121 -0
  110. package/dist/src/gates/validate.js +414 -0
  111. package/dist/src/hooks.d.ts +84 -0
  112. package/dist/src/hooks.js +125 -0
  113. package/dist/src/index.d.ts +31 -0
  114. package/dist/src/index.js +30 -0
  115. package/dist/src/liveness.d.ts +321 -0
  116. package/dist/src/liveness.js +396 -0
  117. package/dist/src/lock.d.ts +256 -0
  118. package/dist/src/lock.js +792 -0
  119. package/dist/src/model-resolution.d.ts +159 -0
  120. package/dist/src/model-resolution.js +307 -0
  121. package/dist/src/modes.d.ts +149 -0
  122. package/dist/src/modes.js +258 -0
  123. package/dist/src/path-identity.d.ts +34 -0
  124. package/dist/src/path-identity.js +48 -0
  125. package/dist/src/plan.d.ts +73 -0
  126. package/dist/src/plan.js +153 -0
  127. package/dist/src/pool.d.ts +326 -0
  128. package/dist/src/pool.js +988 -0
  129. package/dist/src/roles.d.ts +461 -0
  130. package/dist/src/roles.js +776 -0
  131. package/dist/src/spawn.d.ts +482 -0
  132. package/dist/src/spawn.js +1003 -0
  133. package/dist/src/status.d.ts +116 -0
  134. package/dist/src/status.js +148 -0
  135. package/dist/src/task.d.ts +504 -0
  136. package/dist/src/task.js +307 -0
  137. package/dist/src/teardown.d.ts +39 -0
  138. package/dist/src/teardown.js +422 -0
  139. package/dist/src/tuition.d.ts +159 -0
  140. package/dist/src/tuition.js +311 -0
  141. package/dist/src/validate.d.ts +263 -0
  142. package/dist/src/validate.js +742 -0
  143. package/dist/src/version.d.ts +3 -0
  144. package/dist/src/version.js +38 -0
  145. package/dist/src/watcher.d.ts +275 -0
  146. package/dist/src/watcher.js +849 -0
  147. package/dist/src/witness/run.d.ts +299 -0
  148. package/dist/src/witness/run.js +1373 -0
  149. package/dist/src/witness/spec.d.ts +270 -0
  150. package/dist/src/witness/spec.js +475 -0
  151. package/dist/tsconfig.src.tsbuildinfo +1 -0
  152. package/gate-registry.yaml +526 -0
  153. package/gates.manifest.json +257 -0
  154. package/package.json +72 -3
  155. package/role-model-config.yaml +88 -0
  156. package/roles/README.md +128 -0
  157. package/roles/_shared-dispatch-contract.md +87 -0
  158. package/roles/adversarial-plan-reviewer.md +80 -0
  159. package/roles/clean-room-reviewer.md +140 -0
  160. package/roles/implementer.md +463 -0
  161. package/roles/investigator.md +138 -0
  162. package/roles/plan-writer.md +95 -0
  163. package/schemas/README.md +82 -0
  164. package/schemas/assurance-modes.schema.json +264 -0
  165. package/schemas/charter.schema.json +185 -0
  166. package/schemas/checklist.schema.json +114 -0
  167. package/schemas/cutover-state.schema.json +64 -0
  168. package/schemas/decision-record.schema.json +88 -0
  169. package/schemas/executor-record.schema.json +36 -0
  170. package/schemas/final-report.schema.json +90 -0
  171. package/schemas/finding.schema.json +106 -0
  172. package/schemas/gate-registry.schema.json +260 -0
  173. package/schemas/mechanism-index.schema.json +94 -0
  174. package/schemas/model-resolution.schema.json +362 -0
  175. package/schemas/plan.schema.json +300 -0
  176. package/schemas/report.schema.json +579 -0
  177. package/schemas/role-brief.schema.json +105 -0
  178. package/schemas/role-model-config.schema.json +90 -0
  179. package/schemas/status-line.schema.json +40 -0
  180. package/schemas/tuition.schema.json +191 -0
  181. package/schemas/verdict.schema.json +295 -0
  182. package/schemas/work-history.schema.json +183 -0
  183. package/schemas/write-bypass.schema.json +69 -0
  184. package/templates/charter.example.yaml +54 -0
  185. package/templates/decision-record.example.yaml +27 -0
  186. package/templates/final-report.example.yaml +80 -0
  187. package/templates/plan.example.yaml +87 -0
  188. package/templates/report.example.yaml +236 -0
  189. package/templates/warnings.md +74 -0
  190. package/templates/work-history.example.yaml +185 -0
  191. package/tuition/README.md +76 -0
  192. package/tuition/T-001.yaml +48 -0
  193. package/tuition/T-002.yaml +51 -0
  194. package/tuition/T-003.yaml +100 -0
  195. package/tuition/T-004.yaml +52 -0
  196. package/tuition/T-005.yaml +72 -0
  197. package/tuition/T-006.yaml +81 -0
  198. package/tuition/T-007.yaml +56 -0
  199. package/tuition/T-008.yaml +111 -0
  200. package/tuition/T-009.yaml +50 -0
  201. package/tuition/T-015.yaml +36 -0
  202. package/tuition/T-016.yaml +36 -0
  203. package/tuition/T-017.yaml +46 -0
  204. package/tuition/T-018.yaml +84 -0
  205. package/tuition/T-021.yaml +40 -0
  206. package/tuition/T-022.yaml +36 -0
  207. package/tuition/mechanism-index.yaml +256 -0
@@ -0,0 +1,482 @@
1
+ import type { ChildEnvExtension } from "./exec/env.ts";
2
+ import type { Fleet } from "./fleet.ts";
3
+ import type { CredentialHandoverRecord, GuardResult, PayloadClass, TaskMeta, TaskShape } from "./task.ts";
4
+ /**
5
+ * tiphys spawn (kernel plan v1, M1-P4 step 4): worktree, brief, turn-end
6
+ * hook, task meta and the executor launch in ONE command.
7
+ *
8
+ * Order, exactly as the plan states it: liveness-guard check (M1-P5; a
9
+ * documented no-op seam in this phase), holdership check, pool create,
10
+ * brief assembly, meta write, executor launch. The holdership check runs
11
+ * before pool create because its refusal must create nothing
12
+ * (criterion 12).
13
+ *
14
+ * Rollback contract, stated narrowly because the broad version would not
15
+ * be honest (the lesson M1-P3's destroy paid for four times over):
16
+ *
17
+ * - A failure BEFORE pool create removes nothing, because nothing was
18
+ * created. In particular a duplicate task id leaves any pre-existing
19
+ * tasks/<id>/ byte-identical: this command writes nothing under
20
+ * tasks/ until pool create has succeeded (criterion 4, PR-005).
21
+ * - A failure AFTER pool create and BEFORE the payload starts removes
22
+ * exactly what this invocation created (the files it wrote, the task
23
+ * directory when it created it, and the pool worktree) and nothing
24
+ * else (criterion 5). This holds for a step that RETURNS a failure
25
+ * and for one that THROWS: every write in that window goes through
26
+ * runStep, because an unwrapped raise walked past the rollback
27
+ * entirely and orphaned the worktree, the branch and the pool record
28
+ * (F-2). The worktree is untouched at that point, so
29
+ * the removal passes the ordinary pool destroy gates and needs no
30
+ * force flag: --discard and --delete-branch-force are deliberately
31
+ * NOT passed, so a worktree that somehow is not pristine refuses and
32
+ * survives rather than being destroyed by a cleanup path.
33
+ * - Once the PAYLOAD HAS STARTED, nothing is ever rolled back. The
34
+ * worktree may hold real work by then, and a cleanup that removed it
35
+ * would be M1-P3's V-1 data-loss defect with a different name. A
36
+ * failure after that point reports what happened and changes
37
+ * nothing.
38
+ *
39
+ * Substrate: everything here is substrate-neutral except the executor,
40
+ * which sits behind ExecutorAdapter (DR-0007). M1 ships exactly one
41
+ * adapter, the local subprocess one.
42
+ */
43
+ /** What the adapter is asked to do. Everything crosses as files and exit codes. */
44
+ export interface ExecutorRequest {
45
+ taskId: string;
46
+ /** cwd for the payload: the task worktree. */
47
+ worktree: string;
48
+ /** argv of the payload, argv[0] being the program. */
49
+ command: string[];
50
+ /** Script the adapter invokes with the payload exit code when it exits. */
51
+ hookPath: string;
52
+ /** Where the adapter writes its launch record. */
53
+ recordPath: string;
54
+ /** Optional non-completion deadline in seconds (PR-207). */
55
+ deadlineSeconds: number | undefined;
56
+ /**
57
+ * The EXACT environment for BOTH children this launch produces: the
58
+ * payload and the turn-end hook (M2-P8, M2R-004 edit 4). Built by
59
+ * `buildChildEnv` (src/exec/env.ts): allowlisted names only, with the
60
+ * five credential-store pointers redirected to harness-owned paths.
61
+ * `undefined` means the children inherit the parent's environment
62
+ * UNCHANGED, and is only ever passed under `allowPrCredentials`, the
63
+ * declared escape hatch; an adapter must never widen it on its own.
64
+ */
65
+ env: Record<string, string> | undefined;
66
+ /**
67
+ * THE ASSEMBLED BRIEF, and it is NOT optional (M4-P3 criterion 1).
68
+ *
69
+ * The brief is the agent payload's entire input, and until this phase the
70
+ * request did not carry it at all, although the call site already had it:
71
+ * `assembleBrief` returns the path and `spawnTask` pushes it onto
72
+ * `createdFiles` before the launch. So an optional `briefPath` would be an
73
+ * optionality the kernel never exercises, which is a field that cannot go
74
+ * red: every production path supplies it and no test could construct the
75
+ * absent case without inventing one.
76
+ *
77
+ * The three fields below it are `string | undefined` for the opposite
78
+ * reason: nothing in the kernel produces them, they arrive from the caller,
79
+ * and an adapter that needs one says so through `requires` rather than
80
+ * hoping.
81
+ */
82
+ briefPath: string;
83
+ /**
84
+ * The role the payload is being asked to play, verbatim as the caller named
85
+ * it. The kernel neither interprets it nor holds a vocabulary for it; the
86
+ * role briefs are a shipped artifact and the mapping from a role to a brief
87
+ * is the plugin's business.
88
+ */
89
+ role: string | undefined;
90
+ /**
91
+ * THE DECLARED TIER, VERBATIM, AND NEVER A MODEL NAME (M4-P3, the
92
+ * zero-vendor-names requirement). Whatever `role-model-config.yaml`
93
+ * declares, `strongest` or `cheaper` or anything else, crosses this seam
94
+ * unaltered. The tier-to-model mapping lives in the plugin: a mapping in
95
+ * `src/` is what would close off every harness that is not the one it names
96
+ * (delivery/plan/m4-intake.md:377).
97
+ */
98
+ declaredTier: string | undefined;
99
+ /**
100
+ * The delivery phase this task belongs to, CARRIED and never DERIVED.
101
+ *
102
+ * The scope gate derives a phase id from a branch name, and M4-D-22 leaves
103
+ * open whether that convention is the kernel's or the delivering project's.
104
+ * A kernel that derived the phase id from a branch here would settle
105
+ * M4-D-22 by accident, and a shipped constant is the hardest kind of
106
+ * decision to renumber.
107
+ */
108
+ phaseId: string | undefined;
109
+ }
110
+ /** The closed set of names an adapter may name in `requires`, in field order. */
111
+ export declare function requirableRequestFields(): readonly string[];
112
+ /**
113
+ * THE ADAPTER CONTRACT CHECK (M4-P3 criterion 3): a requirement naming a
114
+ * field that does not exist is a DEFECT IN THE ADAPTER, refused when the
115
+ * adapter is taken up and before anything is created.
116
+ *
117
+ * IT IS A DIFFERENT QUESTION FROM `checkAdapterRequirements` BELOW, AND THE
118
+ * ORDER IS LOAD-BEARING. The presence test there answers "is this field
119
+ * absent", and an unknown key is absent too, so a single check written that
120
+ * way would answer a defect in the adapter with a message about a missing
121
+ * flag: an operator would go looking for a `--modelName` that the kernel has
122
+ * no field for and could never accept. Running this one FIRST is what keeps
123
+ * the two answers distinct, and `test/spawn.test.ts` asserts that they say
124
+ * structurally different things rather than merely both refusing.
125
+ */
126
+ export declare function checkAdapterContract(adapter: ExecutorAdapter): {
127
+ ok: true;
128
+ } | {
129
+ ok: false;
130
+ reason: string;
131
+ };
132
+ /**
133
+ * THE DECLARED-REQUIREMENT CHECK (M4-P3 criterion 2): a field the adapter
134
+ * declared it needs, and the spawn was not given, is a USAGE ERROR, and a
135
+ * usage error creates nothing.
136
+ *
137
+ * THE DANGEROUS STATE THIS EXISTS FOR is not "an adapter gets undefined". It
138
+ * is an adapter DISCOVERING that it got undefined, and raising, after pool
139
+ * create has already made a worktree, a branch and a pool record. That is the
140
+ * measured `--deadline` defect at src/commands/spawn.ts:81 with a new field:
141
+ * a value the kernel could not represent used to raise inside the adapter,
142
+ * after the creation, and the repair was to refuse at parse time. This is the
143
+ * same repair one field along, and it is the reason the check runs at the top
144
+ * of `spawnTask` rather than beside the launch where the request is built.
145
+ */
146
+ export declare function checkAdapterRequirements(adapter: ExecutorAdapter, options: SpawnOptions): {
147
+ ok: true;
148
+ } | {
149
+ ok: false;
150
+ reason: string;
151
+ };
152
+ /**
153
+ * THE CREDENTIAL POLICY CHECK (M4-P8 steps 2, 3 and 5), and it runs before
154
+ * ANYTHING is resolved, loaded or created.
155
+ *
156
+ * Three refusals, in this order, and the order is the fail-closed one:
157
+ *
158
+ * 1. an absent or unrecognised `payloadClass`. Checked first so that
159
+ * refusal 2 never has to reason about an unknown value: without this,
160
+ * `payloadClass !== "project"` would read an omission as permission.
161
+ * 2. the declared escape hatch asked for on a PROJECT payload. This is
162
+ * the pairing the phase exists to refuse: `allowPrCredentials` hands
163
+ * the parent environment over UNCHANGED (see ExecutorRequest.env),
164
+ * credentials and all, and M2-P8 criterion 1 wrote it for the
165
+ * orchestrator's own spawns only. Until this phase the option was
166
+ * reachable from the library seam with nothing between it and a
167
+ * project payload.
168
+ * 3. an extension entry the child must not carry, which is the same
169
+ * vocabulary check `buildChildEnv` makes, made EARLIER. The
170
+ * duplication is deliberate and is not two implementations: both call
171
+ * `refuseExtraAllowlist`. Making it here as well is what keeps a
172
+ * rejected widening from costing a worktree, a branch and a pool
173
+ * record, because `buildChildEnv` does not run until after pool
174
+ * create.
175
+ *
176
+ * It returns a reason rather than throwing, because every refusal on this
177
+ * path must be able to say "nothing was created" in the same sentence.
178
+ */
179
+ export declare function checkCredentialPolicy(options: SpawnOptions): {
180
+ ok: true;
181
+ } | {
182
+ ok: false;
183
+ reason: string;
184
+ };
185
+ /**
186
+ * Pointer evidence: what the five credential-store redirections actually
187
+ * were where the payload ran, and where that observation came from.
188
+ */
189
+ export interface RedirectionEvidence {
190
+ source: "turn-end-record" | "adapter";
191
+ /** Observed value per name; `null` for a name that was unset. */
192
+ values: Readonly<Record<string, string | null>>;
193
+ }
194
+ /**
195
+ * COMPARE THE HANDOVER (M4-P8 criterion 6, repaired for CR-B-001).
196
+ *
197
+ * `handed` is what the kernel built and passed, `reported` is the name set
198
+ * the adapter says it launched with, `pointers` is what the five
199
+ * CREDENTIAL_STORE_REDIRECTIONS actually were where the payload ran.
200
+ *
201
+ * TWO PROPERTIES, AND THE STATUS SAYS WHICH WERE CHECKED. The name-set
202
+ * comparison alone used to be written down as `compared`, which an operator
203
+ * reads as "the handover was verified"; an adapter that keeps the name set
204
+ * byte-identical and puts `HOME` and `XDG_CONFIG_HOME` back to their real
205
+ * paths defeats the M2R-004 defense entirely and was recorded as clean. See
206
+ * `CredentialHandoverRecord` for the five status values and for why the
207
+ * VALUES are still never written into the record.
208
+ *
209
+ * The pointer comparison is by value and the values are DISCARDED: only the
210
+ * names that differ survive into `changedRedirections`. A name the kernel
211
+ * never handed over is not compared, because there is no handed value to
212
+ * compare it against, and the name-set arms are what speak to that case.
213
+ */
214
+ export declare function compareHandover(handed: Record<string, string> | undefined, reported: readonly string[] | undefined, pointers?: RedirectionEvidence): CredentialHandoverRecord;
215
+ /**
216
+ * Launch outcomes. The distinction between a payload that never started
217
+ * and one that did is load-bearing: only the first authorizes rollback.
218
+ */
219
+ export type LaunchOutcome = {
220
+ kind: "completed";
221
+ exitCode: number;
222
+ /**
223
+ * THE NAMES THE ADAPTER REPORTS IT ACTUALLY LAUNCHED WITH (M4-P8
224
+ * criterion 6). Optional, and the optionality is honest rather than
225
+ * lenient: an adapter written before this phase reports nothing, and
226
+ * a kernel that refused every silent adapter would be refusing on an
227
+ * absence of evidence. What the kernel DOES refuse is a reported set
228
+ * that DIFFERS from the one it handed over, which is the adapter
229
+ * saying, in its own record, that it widened the environment.
230
+ *
231
+ * It is NOT on `ExecutorRecord`: that document has a shipped schema
232
+ * with `additionalProperties: false`, and this value is a report to
233
+ * the kernel rather than a durable launch fact for an operator.
234
+ *
235
+ * A DISHONEST ADAPTER IS NOT CAUGHT HERE, and nothing in this field
236
+ * pretends otherwise: an adapter that widens `env` and reports the
237
+ * kernel's set is caught by the child-written probe instead, which
238
+ * is why criterion 5 asserts on a file the CHILD wrote.
239
+ */
240
+ launchedEnvNames?: readonly string[];
241
+ /**
242
+ * THE FIVE CREDENTIAL-STORE POINTERS AS THE ADAPTER LAUNCHED THEM
243
+ * (CR-B-001). Optional for the same honest reason `launchedEnvNames`
244
+ * is, and WEAKER than the kernel's own evidence: it is the adapter's
245
+ * word about its own behaviour. The kernel prefers the turn-end record
246
+ * the generated hook normally writes and falls back to this, and the
247
+ * record says which ARTIFACT it read (`redirectionSource`). That is a
248
+ * weaker claim than which PARTY observed the values, and it used to be
249
+ * spelled as the stronger one; see CredentialHandoverRecord in
250
+ * src/task.ts for the measurement that changed the word.
251
+ */
252
+ launchedRedirections?: Readonly<Record<string, string | null>>;
253
+ } | {
254
+ kind: "launch-failed";
255
+ reason: string;
256
+ } | {
257
+ kind: "incomplete";
258
+ reason: string;
259
+ /**
260
+ * WHAT AN ADAPTER THAT COULD NOT CONFIRM COMPLETION STILL LAUNCHED
261
+ * WITH (CR-B-003). The payload RAN on this arm, so the handover is a
262
+ * real question here and the kernel now asks it; before this round the
263
+ * comparison sat after the `incomplete` return and `meta.json` carried
264
+ * no `handover` key at all, so a widening on this arm was recorded
265
+ * nowhere. Optional for the same reason as on the `completed` arm.
266
+ */
267
+ launchedEnvNames?: readonly string[];
268
+ launchedRedirections?: Readonly<Record<string, string | null>>;
269
+ };
270
+ /**
271
+ * The ExecutorAdapter interface (DR-0007, M1-P4 grounding). The ENTIRE
272
+ * contract is: write the launch record tasks/<id>/executor.json at
273
+ * launch, launch the payload in the task worktree, and ensure the
274
+ * turn-end file is written with the payload exit code on completion. All
275
+ * state crosses this boundary through files and exit codes, never
276
+ * through terminal inspection (FM-055: pane scraping is a race farm;
277
+ * FM-060: every toolbelt boundary is a subprocess with an exit code).
278
+ * A multiplexer-window adapter and a cloud-session adapter are further
279
+ * adapters against this same interface, not kernel changes (M4 era).
280
+ */
281
+ export interface ExecutorAdapter {
282
+ readonly name: string;
283
+ /**
284
+ * WHAT THIS ADAPTER CANNOT LAUNCH WITHOUT (M4-P3 criteria 2 and 3).
285
+ *
286
+ * Every name is a field of `ExecutorRequest`. The kernel cannot know what
287
+ * a given adapter needs, so the adapter DECLARES it and the kernel refuses
288
+ * BEFORE it creates anything, rather than handing over an `undefined` that
289
+ * the adapter discovers once a worktree, a branch and a pool record exist.
290
+ *
291
+ * REQUIRED, never optional: an optional declaration would let an adapter
292
+ * omit it and get the old behaviour back silently, which is the thing this
293
+ * field exists to stop. An adapter that needs nothing declares `[]`, and
294
+ * that is a statement rather than a default.
295
+ */
296
+ readonly requires: readonly string[];
297
+ /**
298
+ * ASYNCHRONOUS since M4-P2. An agent turn is long and a subprocess is
299
+ * short: a window adapter or a cloud-session adapter cannot express
300
+ * "the turn ended" in a synchronous return, so the signature that only
301
+ * ever fitted the local subprocess case is the one that changes.
302
+ *
303
+ * This is NOT permission to background (constraint C-3). The kernel
304
+ * AWAITS this promise inside `spawnTask`, so the command still ends
305
+ * after the payload does; awaiting a call is not outliving it, and
306
+ * delivery/plan/kernel-plan-v1.md:311 already puts process ownership in
307
+ * the harness rather than the kernel.
308
+ *
309
+ * The three-armed outcome is unchanged and is still the whole contract:
310
+ * only `launch-failed` authorises rollback, because only `launch-failed`
311
+ * asserts that the payload never started. An adapter that cannot tell
312
+ * returns `incomplete` and the kernel touches nothing. What DID change
313
+ * is that `completed` is no longer taken on the adapter's word: see the
314
+ * completion precondition in `spawnTask`.
315
+ */
316
+ launch(request: ExecutorRequest): Promise<LaunchOutcome>;
317
+ }
318
+ /** The launch record (JSON per DR-0006, shape per PR-207). */
319
+ export interface ExecutorRecord {
320
+ adapter: string;
321
+ launchedAt: string;
322
+ /**
323
+ * Absolute ISO-8601 instant by which the task must have completed,
324
+ * present only when --deadline was passed. Recorded as an instant
325
+ * rather than as the raw seconds so the M1-P5 watcher compares it
326
+ * against the clock directly, the way it already compares lease
327
+ * expiry, instead of re-deriving it from launchedAt. The plan fixes
328
+ * the field name and its optionality, not its units; this is the
329
+ * choice, recorded rather than assumed.
330
+ */
331
+ deadline?: string;
332
+ /**
333
+ * WHAT WAS REQUESTED, NOT WHAT WAS RESOLVED (M4-P3 criterion 6).
334
+ *
335
+ * The declared tier the request carried, copied verbatim. Optional in the
336
+ * same sense `deadline` is: present exactly when the request carried one,
337
+ * absent otherwise, never the string "undefined".
338
+ *
339
+ * THERE IS NO RESOLVED MODEL HERE, and the absence is a decision rather
340
+ * than an omission. This record is written BEFORE the payload starts,
341
+ * which is the whole basis of the launch-failed-versus-incomplete
342
+ * distinction, while a harness that requests one model and is served
343
+ * another resolves mid-turn. A resolved model in a launch record would
344
+ * therefore be a value nobody could have observed at the moment it was
345
+ * written. M4-P7 carries the resolved half, at turn end, where it can be
346
+ * true.
347
+ */
348
+ requestedTier?: string;
349
+ /** The role the request carried, copied verbatim. See `requestedTier`. */
350
+ requestedRole?: string;
351
+ }
352
+ /**
353
+ * The local subprocess adapter, the one M1 ships (and the one the exit
354
+ * test's stub payload uses).
355
+ *
356
+ * The payload runs to COMPLETION before this returns: the kernel never
357
+ * auto-backgrounds anything (plan constraint C-3, FM-054), so there is
358
+ * no daemonize path here to forget to guard.
359
+ *
360
+ * `async` since M4-P2, and its BODY IS UNCHANGED: every statement below
361
+ * is still synchronous, `spawnSync` is still what runs both children, and
362
+ * the promise this now returns is already settled by the time the first
363
+ * `await` on it runs. The keyword is there because the INTERFACE is async
364
+ * for the adapters that need it, not because this adapter gained a
365
+ * concurrency path to get wrong.
366
+ *
367
+ * It runs without a shell on purpose. Under a shell a missing payload
368
+ * binary arrives as an ordinary exit code 127, indistinguishable from a
369
+ * payload that ran and failed, and spawn's rollback rule turns on
370
+ * exactly that distinction.
371
+ */
372
+ export declare const subprocessAdapter: ExecutorAdapter;
373
+ /**
374
+ * Liveness-guard seam (plan step 4). The guard itself is M1-P5 work; in
375
+ * this phase it is a documented no-op, so that P5 adds a body here
376
+ * rather than a new call site in the middle of the spawn sequence.
377
+ */
378
+ export declare function livenessGuard(fleet: Fleet): GuardResult;
379
+ /**
380
+ * Tokenise --exec. M1 supports a program plus plain arguments separated
381
+ * by whitespace; there is no shell and no quoting. Anything richer is an
382
+ * adapter's business (M4 era), not a string this kernel re-parses.
383
+ */
384
+ export declare function parseExecCommand(command: string): string[];
385
+ export interface SpawnOptions {
386
+ taskId: string;
387
+ project: string;
388
+ briefFile: string;
389
+ shape: TaskShape;
390
+ exec: string;
391
+ deadlineSeconds: number | undefined;
392
+ /** Passed straight through to pool create (EXT-F-03); see spawnTask. */
393
+ offline: boolean;
394
+ /**
395
+ * DECLARED ESCAPE HATCH from the credential scrub (M2-P8 criterion 1).
396
+ * When true, both children inherit the parent environment unchanged,
397
+ * including any pull-request-capable credential the parent holds. This
398
+ * exists for the orchestrator's own spawns, never for an implementer
399
+ * payload; default is false and the scrub is on.
400
+ */
401
+ allowPrCredentials?: boolean;
402
+ /**
403
+ * WHOSE AUTHORITY THIS PAYLOAD RUNS UNDER (M4-P8 step 2). REQUIRED, and
404
+ * there is NO DEFAULT anywhere on this path, in the type or at runtime.
405
+ *
406
+ * A default would be the whole defect: `allowPrCredentials` is reachable
407
+ * from the library seam, which is where the plugin sits, and the pairing
408
+ * this field exists to refuse is the escape hatch on a project payload.
409
+ * If omission meant "orchestrator", a caller would acquire the
410
+ * orchestrator's authority by leaving a field out, which is the quietest
411
+ * way there is to reach a credential. `spawnTask` therefore refuses an
412
+ * absent or unrecognised value before it creates or loads anything,
413
+ * rather than trusting the type: TypeScript is a compile-time promise and
414
+ * the consumer that matters here is a JavaScript plugin.
415
+ */
416
+ payloadClass: PayloadClass;
417
+ /**
418
+ * PER-INVOCATION ALLOWLIST EXTENSIONS, EACH WITH THE REASON IT WAS
419
+ * GRANTED (M4-P8 step 3). Absent means none, which is the measured
420
+ * minimum for model authentication in the probed container
421
+ * (delivery/verification/m4-prototype-probes.md:33).
422
+ *
423
+ * The reason is DATA and a blank one is refused, so a widening cannot be
424
+ * granted without leaving behind something a later reader can check; the
425
+ * granted set is copied into meta.json verbatim. The kernel refuses any
426
+ * entry naming a variable in the walked gh-token or dangerous vocabulary,
427
+ * so this field cannot be used to re-admit a credential.
428
+ */
429
+ extraAllowlist?: readonly ChildEnvExtension[];
430
+ /**
431
+ * The three caller-supplied request fields (M4-P3 criterion 1, M4-D-05).
432
+ * Each is `string | undefined` because nothing in the kernel produces one;
433
+ * `briefPath` is not here because `assembleBrief` does produce it.
434
+ */
435
+ role: string | undefined;
436
+ declaredTier: string | undefined;
437
+ phaseId: string | undefined;
438
+ /**
439
+ * AN ADAPTER OBJECT SUPPLIED DIRECTLY, which only the kernel's own tests
440
+ * do. It outranks `adapterSpecifier` because it is not a request to
441
+ * resolve anything: there is nothing to root, nothing to load, and no
442
+ * trust boundary to cross.
443
+ */
444
+ adapter?: ExecutorAdapter;
445
+ /**
446
+ * THE `--adapter` SPECIFIER, verbatim as the operator typed it (M4-P4
447
+ * criterion 1). `undefined` means the flag was absent, which is NOT the
448
+ * same as an empty one: an absent flag falls through to the fleet home's
449
+ * declared default and then to the built-in adapter, and each of those
450
+ * three outcomes is named in the launch record rather than being silent
451
+ * (criterion 6).
452
+ *
453
+ * The specifier is not resolved here. It is handed to `selectAdapter`,
454
+ * which roots Node module resolution at the FLEET HOME and never at the
455
+ * project clone; see src/adapters/load.ts for why that root is the whole
456
+ * security property of this phase.
457
+ */
458
+ adapterSpecifier?: string;
459
+ }
460
+ export interface SpawnSuccess {
461
+ meta: TaskMeta;
462
+ exitCode: number;
463
+ }
464
+ export type SpawnResult = {
465
+ ok: true;
466
+ value: SpawnSuccess;
467
+ } | {
468
+ ok: false;
469
+ reason: string;
470
+ };
471
+ /**
472
+ * One spawn. See the module docs for the rollback contract.
473
+ *
474
+ * --offline is passed straight through to pool create and is the only
475
+ * way a fleet can reach an offline base. meta.baseOffline is then COPIED
476
+ * from the pool record the create returned, never recomputed from this
477
+ * flag: pool create attempts the fetch even under --offline and records
478
+ * offline false when it succeeds, so a spawn that trusted its own flag
479
+ * would mark a perfectly fetched base as offline (PR-212, and the
480
+ * provenance inversion measured as V-2 in M1-P3).
481
+ */
482
+ export declare function spawnTask(fleet: Fleet, options: SpawnOptions): Promise<SpawnResult>;