@tiphys/kernel 0.1.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 (94) hide show
  1. package/AGENTS.md +56 -4
  2. package/assurance-modes.yaml +23 -2
  3. package/dist/bin/tiphys.js +86 -8
  4. package/dist/src/adapters/load.d.ts +202 -0
  5. package/dist/src/adapters/load.js +440 -0
  6. package/dist/src/brief.js +27 -20
  7. package/dist/src/checks.d.ts +720 -9
  8. package/dist/src/checks.js +1874 -163
  9. package/dist/src/cli.js +11 -0
  10. package/dist/src/commands/brief.js +27 -4
  11. package/dist/src/commands/cutover.d.ts +35 -0
  12. package/dist/src/commands/cutover.js +448 -0
  13. package/dist/src/commands/doctor.d.ts +229 -0
  14. package/dist/src/commands/doctor.js +968 -27
  15. package/dist/src/commands/init.d.ts +3 -3
  16. package/dist/src/commands/init.js +57 -8
  17. package/dist/src/commands/lock.d.ts +33 -0
  18. package/dist/src/commands/lock.js +117 -6
  19. package/dist/src/commands/next.d.ts +130 -0
  20. package/dist/src/commands/next.js +597 -0
  21. package/dist/src/commands/pool.js +12 -1
  22. package/dist/src/commands/resume.d.ts +1 -0
  23. package/dist/src/commands/resume.js +88 -0
  24. package/dist/src/commands/spawn.js +51 -2
  25. package/dist/src/commands/status.d.ts +6 -4
  26. package/dist/src/commands/status.js +6 -4
  27. package/dist/src/commands/sync.d.ts +47 -0
  28. package/dist/src/commands/sync.js +341 -0
  29. package/dist/src/commands/teardown.js +10 -2
  30. package/dist/src/commands/validate.js +70 -0
  31. package/dist/src/cutover.d.ts +584 -0
  32. package/dist/src/cutover.js +1444 -0
  33. package/dist/src/exclusion.d.ts +389 -0
  34. package/dist/src/exclusion.js +843 -0
  35. package/dist/src/exec/env.d.ts +152 -2
  36. package/dist/src/exec/env.js +146 -2
  37. package/dist/src/fleet.d.ts +172 -0
  38. package/dist/src/fleet.js +219 -1
  39. package/dist/src/gates/citations.js +7 -1
  40. package/dist/src/gates/coverage.d.ts +113 -22
  41. package/dist/src/gates/coverage.js +166 -31
  42. package/dist/src/gates/credentials.d.ts +159 -0
  43. package/dist/src/gates/credentials.js +221 -2
  44. package/dist/src/gates/gate-classes.d.ts +56 -0
  45. package/dist/src/gates/gate-classes.js +633 -0
  46. package/dist/src/gates/merge-preconditions.d.ts +319 -0
  47. package/dist/src/gates/merge-preconditions.js +932 -0
  48. package/dist/src/gates/red-witness.js +105 -13
  49. package/dist/src/gates/run.d.ts +49 -1
  50. package/dist/src/gates/run.js +83 -5
  51. package/dist/src/gates/schemas/phase-declaration.schema.json +45 -0
  52. package/dist/src/gates/suite.js +48 -7
  53. package/dist/src/hooks.d.ts +55 -3
  54. package/dist/src/hooks.js +69 -6
  55. package/dist/src/index.d.ts +31 -0
  56. package/dist/src/index.js +30 -0
  57. package/dist/src/lock.d.ts +82 -4
  58. package/dist/src/lock.js +314 -22
  59. package/dist/src/model-resolution.d.ts +159 -0
  60. package/dist/src/model-resolution.js +307 -0
  61. package/dist/src/path-identity.d.ts +32 -0
  62. package/dist/src/path-identity.js +38 -0
  63. package/dist/src/pool.d.ts +197 -1
  64. package/dist/src/pool.js +289 -22
  65. package/dist/src/roles.d.ts +31 -0
  66. package/dist/src/roles.js +42 -0
  67. package/dist/src/spawn.d.ts +307 -2
  68. package/dist/src/spawn.js +690 -19
  69. package/dist/src/status.d.ts +27 -2
  70. package/dist/src/status.js +34 -5
  71. package/dist/src/task.d.ts +295 -55
  72. package/dist/src/task.js +125 -123
  73. package/dist/src/teardown.d.ts +7 -0
  74. package/dist/src/teardown.js +120 -12
  75. package/dist/src/validate.d.ts +44 -11
  76. package/dist/src/validate.js +44 -34
  77. package/dist/src/watcher.js +1 -11
  78. package/dist/src/witness/run.d.ts +32 -7
  79. package/dist/src/witness/run.js +76 -30
  80. package/dist/src/witness/spec.d.ts +168 -0
  81. package/dist/src/witness/spec.js +240 -18
  82. package/dist/tsconfig.src.tsbuildinfo +1 -1
  83. package/gate-registry.yaml +136 -0
  84. package/gates.manifest.json +63 -1
  85. package/package.json +18 -3
  86. package/roles/implementer.md +3 -0
  87. package/schemas/README.md +1 -0
  88. package/schemas/assurance-modes.schema.json +1 -1
  89. package/schemas/charter.schema.json +19 -0
  90. package/schemas/cutover-state.schema.json +64 -0
  91. package/schemas/executor-record.schema.json +36 -0
  92. package/schemas/model-resolution.schema.json +362 -0
  93. package/schemas/verdict.schema.json +9 -3
  94. package/schemas/write-bypass.schema.json +69 -0
package/AGENTS.md CHANGED
@@ -245,11 +245,30 @@ conversation memory is a cache and the files are the truth, and a cache that is
245
245
  never written back is lost at the next restart, which is a routine event and not
246
246
  an incident.
247
247
 
248
+ HOW: `tiphys sync`, which is the mechanism this clause is discharged by. It is
249
+ not a reminder to run git carefully. It enumerates what changed, asks git which
250
+ of those paths the fleet `.gitignore` covers, commits exactly the rest, and
251
+ pushes. The partition above is therefore DERIVED from the fleet's own ignore
252
+ rules rather than from a reader's judgment about which of two files is durable,
253
+ and a prefix added to that ignore set is honoured by the same derivation with
254
+ no further edit anywhere.
255
+
256
+ TWO THINGS IT REFUSES RATHER THAN GUESSES, because both are states where being
257
+ helpful would be destructive. An ephemeral path that is ALREADY STAGED stops the
258
+ run: git commits the index, not the pathspec it was handed, so committing at all
259
+ would commit that path, and unstaging on your behalf is a change to work the
260
+ command did not create. A fleet home with no remote stops the run BEFORE the
261
+ commit, because the discipline is commit AND push and a command that did the
262
+ first half here would have done the half that gets dropped and reported the half
263
+ that does not.
264
+
248
265
  AND PUSHED, WHICH IS THE HALF THAT GETS DROPPED. Evidence that lives only on a
249
266
  long-lived side branch dies with that branch. Process paperwork reaches the
250
267
  default branch through a pull request like everything else, batched rather than
251
268
  one request per file, and it is not allowed to accumulate unpushed while the
252
- code it proves lands ahead of it.
269
+ code it proves lands ahead of it. A failed push is a NONZERO exit carrying git's
270
+ own first stderr line, never a warning printed beside a success: the work is
271
+ committed locally and still owed to the remote, and you are the one who owes it.
253
272
 
254
273
  ## clause merge-authority: the declared mode says who signs, and for a delegated grant the signature is dual clean review
255
274
 
@@ -292,12 +311,45 @@ content of this clause: a ref update is a recorded, reversible act over reviewed
292
311
  commits, and a file edit is unreviewed work by the one role whose independence
293
312
  every later check assumes.
294
313
 
314
+ WHICH MECHANISM ENFORCES IT, replacing nothing above. The clause was a sentence
315
+ you were trusted to remember until M4-P9 shipped a `PreToolUse` hook for it. The
316
+ hook draws exactly the line the two paragraphs above draw, and it draws it over
317
+ the TARGET rather than over a tool name, because "designated merge tooling" has
318
+ no referent in this kernel: a write resolving inside a project working tree and
319
+ outside that clone's `.git/` is REFUSED, and a write under `.git/` is the ref
320
+ update and is PERMITTED. Its refusal is an exit code, not a warning.
321
+
322
+ THE HOTFIX CARVE-OUT IS A DECLARED ACT AND IT EXPIRES. When the pipeline is
323
+ wedged and the fix has to be made in the tree, you write one declaration naming
324
+ the project, the paths, the reason and an ABSOLUTE EXPIRY INSTANT, validated by
325
+ `schemas/write-bypass.schema.json#properties.expiresAt`. Nothing about it is
326
+ optional: a bypass with no expiry is a block switched off permanently by the
327
+ first hotfix, which is why the expiry is required rather than encouraged. Every
328
+ write it permits is appended to a separate evidence log, and NO DECISION EVER
329
+ READS THAT LOG, which is constraint C-1 and is the reason the declaration and
330
+ the evidence are two files rather than one.
331
+
332
+ WHAT IT DOES NOT COVER, stated here because a partial guard read as a total one
333
+ is worse than none. The hook adjudicates the file-writing tools. A shell command
334
+ is NOT adjudicated, because the payload a shell tool call delivers carries the
335
+ command string and no resolvable write target, which was measured rather than
336
+ assumed. So the hook makes an accidental edit impossible and a deliberate one
337
+ through a shell still yours to not do.
338
+
295
339
  ## clause fleet-resume-specification: what survives reclamation, what is rebuilt, and what doctor reports
296
340
 
297
341
  Assigned to this document by plan v1 PR-201. THIS CLAUSE IS A SPECIFICATION AND
298
- NOT A MECHANISM, and it says so at the top rather than reading as a description
299
- of something that runs. The machinery is deferred to a later milestone; what is
300
- settled here is what that machinery will have to do.
342
+ NOT A MECHANISM: what is settled here is what the machinery has to do, and the
343
+ commands that do it are named below rather than described here.
344
+
345
+ WHICH PHASES DELIVERED IT, replacing the sentence that deferred the machinery to
346
+ "a later milestone" and gave a reader no way to tell whether that milestone had
347
+ arrived. `tiphys resume` (M4-P16) rebuilds the ephemeral half a clone does not
348
+ carry, creating only what is absent and never removing anything. `tiphys sync`
349
+ (M4-P18) commits and pushes the durable half, which is what makes "everything
350
+ committed and pushed" below a reachable state rather than a hope. What remains
351
+ deferred is the doctor reporting in the third paragraph, which is owned by its
352
+ own phase; nothing else in this clause is waiting on a milestone.
301
353
 
302
354
  WHAT MUST SURVIVE a cloud fleet being reclaimed: everything committed and pushed
303
355
  under `fleet-state-commit-discipline` above. Nothing else is promised, and
@@ -108,7 +108,8 @@ modes:
108
108
  blueprint section 8 (the mode and its pipeline); process doc section 9
109
109
  item 3 enumerated (R-096); T-003 for the fix-round-verification stage;
110
110
  DR-0012 and DR-0015 for the merge authority actually in force; DR-0016
111
- for the escalation response; T-007 and D-M3-32 for the review contracts.
111
+ for the escalation response; DR-0035 for the fix-round ceiling; T-007 and
112
+ D-M3-32 for the review contracts.
112
113
  # ORDER IS LOAD-BEARING. R-024: an adversarial plan review happens before
113
114
  # anyone builds, which is a property of this sequence and not of its set,
114
115
  # and `mode-stage-order` is the check that says so.
@@ -193,8 +194,28 @@ modes:
193
194
  verdict.
194
195
  # DR-0012's two limits with DR-0016's response. DATA THE ORCHESTRATOR BRIEF
195
196
  # CITES, NOT AN ENFORCEMENT ENGINE: nothing in M3 counts fix rounds.
197
+ #
198
+ # THE FIX-ROUND NUMBER IS DR-0035'S CEILING OF 3, NOT DR-0012'S 2 (M4-P30).
199
+ # DR-0035 replaced the single bound with a table: the dial is the number of
200
+ # rounds between the clean-room reviewer and the implementer, and it runs
201
+ # from one round (a zero or small subject) to three (a large subject at
202
+ # high impact). A single integer cannot carry a table, so what this field
203
+ # records is the CEILING, which is the only reading under which the value
204
+ # is true of every phase rather than wrong for some of them. Writing 2 here
205
+ # made this file state a bound DR-0035 had already lifted, and the kernel's
206
+ # own charter had to go out of its way to avoid repeating it: charter.yaml
207
+ # names the BUDGET rather than a number precisely because the two documents
208
+ # disagreed. They no longer do.
209
+ #
210
+ # NOTHING READS THIS VALUE, and the derivation is published rather than
211
+ # asserted: `grep -rn 'max-fix-rounds-after-review' --include='*.ts'
212
+ # --include='*.mjs' src/ bin/ scripts/` returns zero hits, so no code path
213
+ # branches on it. The readers are this file's schema, the test that pins
214
+ # the shipped values, and `tiphys mode show`, which prints whatever is
215
+ # here. See delivery/work-history/m4-p30.md:1 for the full output and for
216
+ # the regions that derivation did NOT cover.
196
217
  escalation-bounds:
197
- max-fix-rounds-after-review: 2
218
+ max-fix-rounds-after-review: 3
198
219
  recurrence-of-high-in-one-component: 1
199
220
  on-exceeded: fresh-implementer-and-third-contract
200
221
 
@@ -1,14 +1,92 @@
1
1
  #!/usr/bin/env node
2
- import { EX_USAGE, run } from "../src/cli.js";
2
+ import { run, usageLine } from "../src/cli.js";
3
+ /**
4
+ * THE TOP-LEVEL ERROR PRESENTATION HANDLER (kernel plan M3, M3-P1 step 8b;
5
+ * D-M3-21).
6
+ *
7
+ * `delivery/STATE.md` carried this as an unowned seam: "clean presentation of
8
+ * a load-time configuration error ... a seam no M1 phase owns". M3-P1 owns it
9
+ * because M3-P1 is the first phase to add commands whose ordinary input is a
10
+ * HAND-AUTHORED FILE that will routinely be malformed, and a validator that
11
+ * answers malformed YAML with a stack trace is a validator nobody trusts
12
+ * (DR-0013 YAML clause 4).
13
+ *
14
+ * The rule: a thrown error from any subcommand is ONE diagnostic line on
15
+ * stderr and a nonzero exit. Never a stack trace, on either stream.
16
+ *
17
+ * THE DOCUMENTED USAGE-MARKING MECHANISM IS GONE, and its removal is the
18
+ * fix rather than the loss. This docblock used to promise that "an `Error`
19
+ * carrying `usage: true` exits 64", with an `UsageMarkedError` interface
20
+ * beside it, and a grep over `src/` and `bin/` found ZERO code that ever set
21
+ * the field: every command reaches EX_USAGE by returning it from its own
22
+ * handler. A comment describing a contract nothing implements is how the
23
+ * next implementer reaches for a mechanism that silently does nothing, so
24
+ * the branch and the sentence are removed together and `EX_USAGE` stays the
25
+ * value handlers return.
26
+ *
27
+ * SCOPE IS EXACTLY THIS HANDLER, THE HELP AFFORDANCE, AND EPIPE (D-M3-21
28
+ * plus the DR-0047 sweep's two CLI findings). Nothing else in this file
29
+ * changes, because a dispatcher is the last place to accumulate incidental
30
+ * edits.
31
+ */
3
32
  function singleLine(text) {
4
33
  return text.replace(/\s+/g, " ").trim();
5
34
  }
6
- try {
7
- process.exitCode = await run(process.argv.slice(2));
35
+ /**
36
+ * AN EXIT CODE MUST NOT CONTRADICT THE STATE THE COMMAND LEFT BEHIND.
37
+ *
38
+ * Measured before this handler existed: `tiphys lock acquire --duration 900 |
39
+ * head -1` printed the acquired line, exited 1 on an unhandled EPIPE, and
40
+ * left BOTH layers mutated, the local lease on disk and the shared register
41
+ * advanced to the next counter. A wrapper of the shape
42
+ * `if ! tiphys lock acquire | grep -q acquired` therefore took its failure
43
+ * branch while holding the fleet, which is the one wrong conclusion the
44
+ * exclusion commands must never produce.
45
+ *
46
+ * EPIPE ONLY, and that word is load-bearing. A consumer closing the pipe is
47
+ * not a failure of the command; any OTHER write error still surfaces, so
48
+ * this does not become a blanket swallow of output failures. Both streams
49
+ * are covered because a short consumer of stderr is the same hazard as a
50
+ * short consumer of stdout.
51
+ */
52
+ function ignoreEpipe(stream) {
53
+ stream.on("error", (error) => {
54
+ if (error.code === "EPIPE") {
55
+ return;
56
+ }
57
+ throw error;
58
+ });
8
59
  }
9
- catch (error) {
10
- const usage = error?.usage === true;
11
- const message = error instanceof Error ? error.message : String(error);
12
- process.stderr.write(`tiphys: ${singleLine(message)}\n`);
13
- process.exitCode = usage ? EX_USAGE : 1;
60
+ ignoreEpipe(process.stdout);
61
+ ignoreEpipe(process.stderr);
62
+ /**
63
+ * THE HELP AFFORDANCE (DR-0047 sweep, group cli, CR-F04).
64
+ *
65
+ * `tiphys --help` exited 64 with ZERO bytes on stdout and the usage line on
66
+ * stderr, so `tiphys --help | less` showed nothing, while this project's own
67
+ * evidence uses `tiphys --help` as a probe. That satisfied M1-P1 criterion 4
68
+ * to the letter, because `--help` is an unknown subcommand, and it is still
69
+ * the first thing a consumer types.
70
+ *
71
+ * The usage text is `usageLine()`, which src/cli.ts DERIVES from the dispatch
72
+ * table, so this cannot drift from the commands that exist. M1-P1 criterion
73
+ * 4's contract is untouched: an unknown subcommand still prints usage to
74
+ * STDERR with an EMPTY stdout and exit 64, and the help words alone print to
75
+ * STDOUT with exit 0.
76
+ */
77
+ const HELP_WORDS = new Set(["--help", "-h", "help"]);
78
+ const argv = process.argv.slice(2);
79
+ if (argv.length === 1 && HELP_WORDS.has(argv[0])) {
80
+ process.stdout.write(`${usageLine()}\n`);
81
+ process.exitCode = 0;
82
+ }
83
+ else {
84
+ try {
85
+ process.exitCode = await run(argv);
86
+ }
87
+ catch (error) {
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ process.stderr.write(`tiphys: ${singleLine(message)}\n`);
90
+ process.exitCode = 1;
91
+ }
14
92
  }
@@ -0,0 +1,202 @@
1
+ import type { Fleet } from "../fleet.ts";
2
+ import type { ExecutorAdapter } from "../spawn.ts";
3
+ /**
4
+ * ADAPTER SELECTION AND LOADING (kernel plan M4, M4-P4; M4-D-03 at
5
+ * delivery/plan/m4-intake.md:729).
6
+ *
7
+ * THE RESOLUTION ROOT IS THE WHOLE SECURITY PROPERTY, and it is the reason
8
+ * this module exists rather than a one-line `await import(specifier)` at the
9
+ * spawn call site. Loading an adapter executes third-party code INSIDE the
10
+ * orchestrator process, the same process that holds delegated merge
11
+ * authority under DR-0012. DR-0029 Part 2c's untrusted-project-content
12
+ * boundary does not exist and M4-D-23 recommends it is not built in M4, so
13
+ * the only mitigation available is WHERE the code is resolved from:
14
+ *
15
+ * - the FLEET HOME is owner-controlled and already pins `@tiphys/kernel`
16
+ * exactly, which is what makes it a boundary at all;
17
+ * - the PROJECT CLONE is the thing under review and may contain anything
18
+ * a contributor pushed, including a `node_modules/` directory.
19
+ *
20
+ * So resolution is rooted at `<fleet home>/package.json` rather than at this
21
+ * module or at `process.cwd()`. A plain `await import(specifier)` resolves
22
+ * relative to THIS MODULE's URL, which is the kernel checkout, and
23
+ * `import.meta.resolve(specifier, parent)` silently ignores its second
24
+ * argument unless Node is started with `--experimental-import-meta-resolve`
25
+ * (measured on v26.6.0, 2026-09-17: the parent was ignored and the specifier
26
+ * resolved from the CALLER's file). Both of those are the shape this module
27
+ * refuses to be.
28
+ *
29
+ * THE ROOTING IS NOT BY ITSELF THE PROPERTY, AND THE SENTENCE THAT STOOD HERE
30
+ * SAID IT WAS (CR-B-004, re-raised as CR-F-CRED-002 and CH-002).
31
+ *
32
+ * Until this round this paragraph asserted, without qualification, that
33
+ * "NOTHING here ever consults the project clone". That is true of BARE
34
+ * specifiers, which is the only shape M4-P4 criterion 2's witness exercises:
35
+ * `createRequire` walks the fleet home and its PARENTS looking for
36
+ * `node_modules`, and the project clone sits BELOW the fleet home at
37
+ * `<fleet>/projects/<name>`, so no such walk reaches it. It was FALSE of the
38
+ * module. Measured at `ad2428b`, with a module in the project clone that drops
39
+ * a sentinel on import: a bare specifier did not evaluate it, an ABSOLUTE
40
+ * specifier did, and the fleet-relative `./projects/demo/evil.mjs` did,
41
+ * because a relative path does not walk up looking for `node_modules`, it
42
+ * walks DOWN the tree the rooting chose.
43
+ *
44
+ * The mechanism is a constraint that holds for one INPUT SHAPE, documented as
45
+ * holding for the module. Both halves are now checked against the RESOLVED
46
+ * REAL PATH, which is the only thing that describes where the code actually
47
+ * comes from, rather than against the specifier's spelling:
48
+ *
49
+ * - a resolved path inside `<fleet>/projects/` is refused WHATEVER the
50
+ * specifier's shape, because the project tree is the thing under review;
51
+ * - a PATH-SHAPED specifier (absolute, or beginning `./` or `../`) whose
52
+ * resolved path is outside the fleet home is refused, because an absolute
53
+ * specifier can point anywhere and a relative one can climb out with
54
+ * `../`. A BARE specifier is exempt from the second check only, because
55
+ * `createRequire`'s parent walk legitimately finds a hoisted
56
+ * `node_modules` above the fleet home, and refusing that would break an
57
+ * ordinary install rather than a hazard.
58
+ *
59
+ * `realpathSync` is used for both, so a symlink planted inside the fleet home
60
+ * pointing into the project clone is refused by the same check rather than by
61
+ * a second one.
62
+ *
63
+ * WHAT IS STILL NOT DEFENDED, said here so the next reader does not re-derive
64
+ * it: an operator who types an absolute path INTO the fleet home gets what
65
+ * they typed, and the fleet home is owner-controlled by assumption. This moves
66
+ * the boundary to the fleet home for every specifier shape; it does not defend
67
+ * inside it.
68
+ *
69
+ * WHAT THIS MODULE DOES NOT DEFEND. Once a specifier resolves inside the
70
+ * fleet home, its code runs. A legitimate fleet-home adapter that is later
71
+ * compromised is not covered by anything here, and no criterion of this
72
+ * phase claims otherwise: this phase MOVES the boundary to the fleet home,
73
+ * it does not defend inside it. That residue is M4-D-23's, stated rather
74
+ * than implied.
75
+ */
76
+ /**
77
+ * The built-in adapter's name, and the one name a loaded adapter may not
78
+ * claim (M4-P4 criterion 5).
79
+ *
80
+ * IT LIVES HERE RATHER THAN IN `src/spawn.ts` so that the runtime import
81
+ * edge runs one way only: `src/spawn.ts` imports this value, and this module
82
+ * imports nothing but a TYPE back. A value-level cycle between the launch
83
+ * module and the loader would evaluate one of them inside the other's
84
+ * temporal dead zone the first time an import order changed, which is a
85
+ * failure nothing in the suite would predict.
86
+ *
87
+ * The launch record's `adapter` field (src/spawn.ts:113) is the only thing
88
+ * that ever says what ran. A loaded adapter naming itself after the built-in
89
+ * one makes every later record ambiguous, and the ambiguity is unresolvable
90
+ * after the fact because the record is the only witness. This is the
91
+ * misattribution guard src/gates/schemas/release-record.schema.json:26
92
+ * already instantiates for release records, applied one layer down.
93
+ */
94
+ export declare const BUILT_IN_ADAPTER_NAME = "subprocess";
95
+ /**
96
+ * The fleet-home `package.json` field that names a default adapter, as a
97
+ * JSON pointer written out in the one place that reads it.
98
+ *
99
+ * `tiphys.adapter` rather than a top-level key: the kernel's own
100
+ * `package.json` already namespaces its configuration under `tiphys`
101
+ * (package.json:52), so a fleet home carrying kernel configuration uses the
102
+ * same namespace rather than inventing a second convention.
103
+ */
104
+ export declare const FLEET_ADAPTER_FIELD = "tiphys.adapter";
105
+ export type AdapterSelection = {
106
+ ok: true;
107
+ adapter: ExecutorAdapter | undefined;
108
+ origin: string;
109
+ } | {
110
+ ok: false;
111
+ reason: string;
112
+ };
113
+ export type AdapterLoad = {
114
+ ok: true;
115
+ adapter: ExecutorAdapter;
116
+ } | {
117
+ ok: false;
118
+ reason: string;
119
+ };
120
+ /**
121
+ * The specifier the fleet home declares as its default, if any.
122
+ *
123
+ * READ THROUGH `readRegularFileIfPresent`, never `readFileSync`: the fleet
124
+ * home is a directory this process does not own the contents of, and a FIFO
125
+ * at `package.json` would hang the spawn rather than refuse it. That is the
126
+ * hazard CR-520 records for `meta.json`, one path along.
127
+ *
128
+ * A fleet home with no `package.json`, no `tiphys` object, or no `adapter`
129
+ * key declares NOTHING, which is a different fact from declaring something
130
+ * unusable: the first yields `undefined` and the built-in default, the
131
+ * second is a refusal naming the field.
132
+ */
133
+ export declare function fleetAdapterSpecifier(fleet: Fleet): {
134
+ ok: true;
135
+ specifier: string | undefined;
136
+ } | {
137
+ ok: false;
138
+ reason: string;
139
+ };
140
+ /**
141
+ * THE CONTAINMENT RULES, applied to the RESOLVED REAL PATH (CR-F-CRED-002,
142
+ * CH-002). Exported so a test can drive both rules directly without staging a
143
+ * module on disk for every member; `loadAdapter` is the only caller in `src/`.
144
+ *
145
+ * Returns a refusal reason, or undefined to allow. Both rules name the
146
+ * resolved path, because an operator reading the refusal has to be able to see
147
+ * WHERE the specifier landed, which is precisely the thing the specifier's own
148
+ * spelling hides.
149
+ */
150
+ export declare function refuseResolvedAdapterPath(fleet: Fleet, specifier: string, origin: string, resolved: string): string | undefined;
151
+ export declare function loadAdapter(fleet: Fleet, specifier: string, origin: string): Promise<AdapterLoad>;
152
+ /**
153
+ * IS THIS AN ADAPTER (M4-P4 criterion 4)?
154
+ *
155
+ * The three shapes the criterion names are three ways a module can be
156
+ * IMPORTABLE and not be an adapter, and each of them is silent at the import
157
+ * itself: a module with no default export, a default export with no `launch`,
158
+ * and a `launch` that is not callable. Left unchecked, the first two surface
159
+ * as `TypeError: adapter.launch is not a function` thrown out of `spawnTask`
160
+ * AFTER pool create has made a worktree, a branch and a pool record, which is
161
+ * the same "refusal that arrives as a crash, after creation" shape M4-P3's
162
+ * requirement checks exist to prevent (src/spawn.ts's checkAdapterRequirements).
163
+ *
164
+ * A FOURTH CHECK IS HERE THAT THE CRITERION DOES NOT NAME, and it is declared
165
+ * rather than smuggled: the adapter's `name` must be a non-empty string.
166
+ * Criterion 5 refuses one particular name, and `undefined` written into the
167
+ * launch record's `adapter` field would defeat the whole point of that
168
+ * criterion by making the record say nothing at all. A guard on the value of
169
+ * a field that may be absent is a guard that cannot go red for the absent
170
+ * case.
171
+ *
172
+ * Every refusal names the SPECIFIER and the member, because an operator
173
+ * reading it has a module to go and fix and needs to know which one.
174
+ */
175
+ export declare function checkAdapterShape(module: unknown, specifier: string, origin: string, resolved: string): AdapterLoad;
176
+ /**
177
+ * WHICH ADAPTER THIS SPAWN USES, AND WHY (M4-P4 criteria 1 and 6).
178
+ *
179
+ * WHICH SOURCE WINS, and it is explicit in the record rather than silent:
180
+ * the `--adapter` FLAG outranks the fleet-home default, and the fleet-home
181
+ * default outranks the built-in one.
182
+ *
183
+ * THE WORDING IS DELIBERATE AND IS NOT A STYLE CHOICE. The word this sentence
184
+ * would naturally use is claimed by four rows of
185
+ * delivery/plan/cutover/retirement-inventory.json, which assert that no kernel
186
+ * artifact states an authority order among charter, plan and decision records
187
+ * and enumerate BY NAME the `src/` files allowed to carry the token. A new
188
+ * file carrying it reddens `scripts/check-retirement-inventory.mjs`, correctly,
189
+ * because the checker cannot tell a benign use from a refuting one without a
190
+ * human reading it. This use is benign and incidental, so the token is not
191
+ * used here rather than the rows being widened from a phase that does not own
192
+ * that document. A flag is a per-spawn instruction and
193
+ * a fleet-home field is a standing one, so the narrower statement wins; the
194
+ * opposite order would make the flag unusable on any fleet that declared a
195
+ * default.
196
+ *
197
+ * `undefined` for the adapter means USE THE BUILT-IN ONE, and it is returned
198
+ * as `undefined` rather than as `subprocessAdapter` so that this module
199
+ * imports nothing but a type from `src/spawn.ts`. See BUILT_IN_ADAPTER_NAME
200
+ * above for why the import edge runs one way.
201
+ */
202
+ export declare function selectAdapter(fleet: Fleet, flagSpecifier: string | undefined): Promise<AdapterSelection>;