@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.
- package/AGENTS.md +663 -0
- package/LICENSE +202 -0
- package/assurance-modes.yaml +299 -0
- package/checklists/clean-room.yaml +325 -0
- package/checklists/env-failure-diagnosis.yaml +68 -0
- package/checklists/flake-playbook.yaml +68 -0
- package/checklists/hazard-review.yaml +144 -0
- package/checklists/plan-review.yaml +103 -0
- package/dist/bin/tiphys.d.ts +2 -0
- package/dist/bin/tiphys.js +92 -0
- package/dist/src/adapters/load.d.ts +202 -0
- package/dist/src/adapters/load.js +440 -0
- package/dist/src/brief.d.ts +32 -0
- package/dist/src/brief.js +66 -0
- package/dist/src/checklists.d.ts +169 -0
- package/dist/src/checklists.js +310 -0
- package/dist/src/checks.d.ts +1539 -0
- package/dist/src/checks.js +5025 -0
- package/dist/src/cli.d.ts +9 -0
- package/dist/src/cli.js +68 -0
- package/dist/src/commands/brief.d.ts +92 -0
- package/dist/src/commands/brief.js +343 -0
- package/dist/src/commands/checklist.d.ts +42 -0
- package/dist/src/commands/checklist.js +168 -0
- package/dist/src/commands/cutover.d.ts +35 -0
- package/dist/src/commands/cutover.js +448 -0
- package/dist/src/commands/doctor.d.ts +264 -0
- package/dist/src/commands/doctor.js +1546 -0
- package/dist/src/commands/gates.d.ts +9 -0
- package/dist/src/commands/gates.js +360 -0
- package/dist/src/commands/init.d.ts +30 -0
- package/dist/src/commands/init.js +177 -0
- package/dist/src/commands/lock.d.ts +34 -0
- package/dist/src/commands/lock.js +340 -0
- package/dist/src/commands/mode.d.ts +40 -0
- package/dist/src/commands/mode.js +134 -0
- package/dist/src/commands/next.d.ts +130 -0
- package/dist/src/commands/next.js +597 -0
- package/dist/src/commands/plan.d.ts +20 -0
- package/dist/src/commands/plan.js +105 -0
- package/dist/src/commands/pool.d.ts +1 -0
- package/dist/src/commands/pool.js +139 -0
- package/dist/src/commands/resume.d.ts +1 -0
- package/dist/src/commands/resume.js +88 -0
- package/dist/src/commands/spawn.d.ts +1 -0
- package/dist/src/commands/spawn.js +195 -0
- package/dist/src/commands/status.d.ts +21 -0
- package/dist/src/commands/status.js +122 -0
- package/dist/src/commands/sync.d.ts +47 -0
- package/dist/src/commands/sync.js +341 -0
- package/dist/src/commands/teardown.d.ts +1 -0
- package/dist/src/commands/teardown.js +87 -0
- package/dist/src/commands/tuition.d.ts +21 -0
- package/dist/src/commands/tuition.js +218 -0
- package/dist/src/commands/validate.d.ts +78 -0
- package/dist/src/commands/validate.js +430 -0
- package/dist/src/commands/watch.d.ts +1 -0
- package/dist/src/commands/watch.js +172 -0
- package/dist/src/cutover.d.ts +584 -0
- package/dist/src/cutover.js +1444 -0
- package/dist/src/exclusion.d.ts +389 -0
- package/dist/src/exclusion.js +843 -0
- package/dist/src/exec/env.d.ts +278 -0
- package/dist/src/exec/env.js +334 -0
- package/dist/src/fleet.d.ts +223 -0
- package/dist/src/fleet.js +298 -0
- package/dist/src/gates/adapters/http-json.d.ts +5 -0
- package/dist/src/gates/adapters/http-json.js +283 -0
- package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
- package/dist/src/gates/adapters/migrations-command.js +373 -0
- package/dist/src/gates/citations.d.ts +408 -0
- package/dist/src/gates/citations.js +1169 -0
- package/dist/src/gates/coverage.d.ts +375 -0
- package/dist/src/gates/coverage.js +836 -0
- package/dist/src/gates/credentials.d.ts +233 -0
- package/dist/src/gates/credentials.js +752 -0
- package/dist/src/gates/deploy.d.ts +1 -0
- package/dist/src/gates/deploy.js +33 -0
- package/dist/src/gates/gate-classes.d.ts +56 -0
- package/dist/src/gates/gate-classes.js +633 -0
- package/dist/src/gates/manifest.d.ts +99 -0
- package/dist/src/gates/manifest.js +208 -0
- package/dist/src/gates/merge-preconditions.d.ts +319 -0
- package/dist/src/gates/merge-preconditions.js +932 -0
- package/dist/src/gates/migrations.d.ts +1 -0
- package/dist/src/gates/migrations.js +36 -0
- package/dist/src/gates/pin.d.ts +114 -0
- package/dist/src/gates/pin.js +154 -0
- package/dist/src/gates/red-witness.d.ts +22 -0
- package/dist/src/gates/red-witness.js +482 -0
- package/dist/src/gates/release.d.ts +283 -0
- package/dist/src/gates/release.js +820 -0
- package/dist/src/gates/result.d.ts +116 -0
- package/dist/src/gates/result.js +91 -0
- package/dist/src/gates/run.d.ts +614 -0
- package/dist/src/gates/run.js +1614 -0
- package/dist/src/gates/schemas/citation-config.schema.json +59 -0
- package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
- package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
- package/dist/src/gates/schemas/gate-result.schema.json +160 -0
- package/dist/src/gates/schemas/phase-declaration.schema.json +87 -0
- package/dist/src/gates/schemas/release-record.schema.json +119 -0
- package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
- package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
- package/dist/src/gates/scope.d.ts +131 -0
- package/dist/src/gates/scope.js +1018 -0
- package/dist/src/gates/suite.d.ts +217 -0
- package/dist/src/gates/suite.js +968 -0
- package/dist/src/gates/validate.d.ts +121 -0
- package/dist/src/gates/validate.js +414 -0
- package/dist/src/hooks.d.ts +84 -0
- package/dist/src/hooks.js +125 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.js +30 -0
- package/dist/src/liveness.d.ts +321 -0
- package/dist/src/liveness.js +396 -0
- package/dist/src/lock.d.ts +256 -0
- package/dist/src/lock.js +792 -0
- package/dist/src/model-resolution.d.ts +159 -0
- package/dist/src/model-resolution.js +307 -0
- package/dist/src/modes.d.ts +149 -0
- package/dist/src/modes.js +258 -0
- package/dist/src/path-identity.d.ts +34 -0
- package/dist/src/path-identity.js +48 -0
- package/dist/src/plan.d.ts +73 -0
- package/dist/src/plan.js +153 -0
- package/dist/src/pool.d.ts +326 -0
- package/dist/src/pool.js +988 -0
- package/dist/src/roles.d.ts +461 -0
- package/dist/src/roles.js +776 -0
- package/dist/src/spawn.d.ts +482 -0
- package/dist/src/spawn.js +1003 -0
- package/dist/src/status.d.ts +116 -0
- package/dist/src/status.js +148 -0
- package/dist/src/task.d.ts +504 -0
- package/dist/src/task.js +307 -0
- package/dist/src/teardown.d.ts +39 -0
- package/dist/src/teardown.js +422 -0
- package/dist/src/tuition.d.ts +159 -0
- package/dist/src/tuition.js +311 -0
- package/dist/src/validate.d.ts +263 -0
- package/dist/src/validate.js +742 -0
- package/dist/src/version.d.ts +3 -0
- package/dist/src/version.js +38 -0
- package/dist/src/watcher.d.ts +275 -0
- package/dist/src/watcher.js +849 -0
- package/dist/src/witness/run.d.ts +299 -0
- package/dist/src/witness/run.js +1373 -0
- package/dist/src/witness/spec.d.ts +270 -0
- package/dist/src/witness/spec.js +475 -0
- package/dist/tsconfig.src.tsbuildinfo +1 -0
- package/gate-registry.yaml +526 -0
- package/gates.manifest.json +257 -0
- package/package.json +72 -3
- package/role-model-config.yaml +88 -0
- package/roles/README.md +128 -0
- package/roles/_shared-dispatch-contract.md +87 -0
- package/roles/adversarial-plan-reviewer.md +80 -0
- package/roles/clean-room-reviewer.md +140 -0
- package/roles/implementer.md +463 -0
- package/roles/investigator.md +138 -0
- package/roles/plan-writer.md +95 -0
- package/schemas/README.md +82 -0
- package/schemas/assurance-modes.schema.json +264 -0
- package/schemas/charter.schema.json +185 -0
- package/schemas/checklist.schema.json +114 -0
- package/schemas/cutover-state.schema.json +64 -0
- package/schemas/decision-record.schema.json +88 -0
- package/schemas/executor-record.schema.json +36 -0
- package/schemas/final-report.schema.json +90 -0
- package/schemas/finding.schema.json +106 -0
- package/schemas/gate-registry.schema.json +260 -0
- package/schemas/mechanism-index.schema.json +94 -0
- package/schemas/model-resolution.schema.json +362 -0
- package/schemas/plan.schema.json +300 -0
- package/schemas/report.schema.json +579 -0
- package/schemas/role-brief.schema.json +105 -0
- package/schemas/role-model-config.schema.json +90 -0
- package/schemas/status-line.schema.json +40 -0
- package/schemas/tuition.schema.json +191 -0
- package/schemas/verdict.schema.json +295 -0
- package/schemas/work-history.schema.json +183 -0
- package/schemas/write-bypass.schema.json +69 -0
- package/templates/charter.example.yaml +54 -0
- package/templates/decision-record.example.yaml +27 -0
- package/templates/final-report.example.yaml +80 -0
- package/templates/plan.example.yaml +87 -0
- package/templates/report.example.yaml +236 -0
- package/templates/warnings.md +74 -0
- package/templates/work-history.example.yaml +185 -0
- package/tuition/README.md +76 -0
- package/tuition/T-001.yaml +48 -0
- package/tuition/T-002.yaml +51 -0
- package/tuition/T-003.yaml +100 -0
- package/tuition/T-004.yaml +52 -0
- package/tuition/T-005.yaml +72 -0
- package/tuition/T-006.yaml +81 -0
- package/tuition/T-007.yaml +56 -0
- package/tuition/T-008.yaml +111 -0
- package/tuition/T-009.yaml +50 -0
- package/tuition/T-015.yaml +36 -0
- package/tuition/T-016.yaml +36 -0
- package/tuition/T-017.yaml +46 -0
- package/tuition/T-018.yaml +84 -0
- package/tuition/T-021.yaml +40 -0
- package/tuition/T-022.yaml +36 -0
- package/tuition/mechanism-index.yaml +256 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# THE ADVERSARIAL PLAN-REVIEW CHECKLIST (kernel plan M3, M3-P7 step 2).
|
|
2
|
+
#
|
|
3
|
+
# Run against a PLAN, before any of it is dispatched. The three canonical
|
|
4
|
+
# probes are R-026b, R-027 and R-028a, and each one is written to be
|
|
5
|
+
# unanswerable without opening something: R-027 carries the process document's
|
|
6
|
+
# own zero-amount-rows illustration, and R-028a names the artifact that counts
|
|
7
|
+
# as proof so a testability claim cannot be admired instead of tested.
|
|
8
|
+
kind: checklist
|
|
9
|
+
id: plan-review
|
|
10
|
+
applies-to: >-
|
|
11
|
+
a plan document before dispatch, read adversarially: the reviewer is looking
|
|
12
|
+
for the phase that cannot be built as written, not for reasons to approve.
|
|
13
|
+
|
|
14
|
+
probes:
|
|
15
|
+
# R-026b. Hidden dependencies and semantic coupling ABOVE the file-overlap
|
|
16
|
+
# floor. File overlap is what the M5 pre-pass computes mechanically; this
|
|
17
|
+
# probe is the part no pre-pass reaches.
|
|
18
|
+
- id: hidden-dependency-above-file-overlap
|
|
19
|
+
probe: >-
|
|
20
|
+
Which phases share MEANING rather than files? Two phases with disjoint
|
|
21
|
+
file lists still collide when one renames a key the other reads, changes
|
|
22
|
+
a value's vocabulary, moves an ordering another relies on, or changes
|
|
23
|
+
what a shared registry row means. The file-overlap pre-pass computes the
|
|
24
|
+
floor and cannot see any of those. Name each pair you found, the shared
|
|
25
|
+
concept, and the phase whose assumption breaks; then name the pairs you
|
|
26
|
+
examined and cleared, because a list of collisions with no stated scope
|
|
27
|
+
is indistinguishable from a search that looked in the wrong place.
|
|
28
|
+
applies-to: plan-structure
|
|
29
|
+
evidence-required: true
|
|
30
|
+
|
|
31
|
+
- id: grounding-names-a-real-artifact
|
|
32
|
+
probe: >-
|
|
33
|
+
Does each phase's `grounding` name artifacts that will EXIST when it
|
|
34
|
+
starts, and does its `blocked-by` follow from that rather than from
|
|
35
|
+
habit? A `blocked-by` naming a phase whose artifacts the grounding never
|
|
36
|
+
mentions is an ordering habit serialising work that could run in
|
|
37
|
+
parallel; the reverse, a grounding consuming a phase the `blocked-by`
|
|
38
|
+
does not name, is a phase that will be dispatched before its inputs
|
|
39
|
+
exist. Check both directions per phase and say which you found.
|
|
40
|
+
applies-to: plan-structure
|
|
41
|
+
evidence-required: true
|
|
42
|
+
|
|
43
|
+
# R-027. Probe every fix shape for THE STATE THAT CAN NO LONGER EXIT, with
|
|
44
|
+
# the process document's own illustration carried in the probe text so the
|
|
45
|
+
# question cannot be answered from the phrase alone.
|
|
46
|
+
- id: fix-shape-state-that-cannot-exit
|
|
47
|
+
probe: >-
|
|
48
|
+
For every fix this plan proposes, what state does it create that can no
|
|
49
|
+
longer be exited? The process document's own illustration: a repair that
|
|
50
|
+
writes rows with a ZERO amount leaves records that are structurally
|
|
51
|
+
valid, that every later query counts, and that no reconciliation can
|
|
52
|
+
distinguish from real ones, so the system can enter that state and never
|
|
53
|
+
leave it. Walk each fix and name the state, the transition INTO it, and
|
|
54
|
+
the transition OUT of it. A fix with no stated exit is the finding.
|
|
55
|
+
applies-to: fix-shapes
|
|
56
|
+
evidence-required: true
|
|
57
|
+
|
|
58
|
+
- id: fix-shape-partial-application
|
|
59
|
+
probe: >-
|
|
60
|
+
What does each fix leave behind if it is interrupted halfway? Name the
|
|
61
|
+
point of no return in each one, and whether re-running it from the start
|
|
62
|
+
is safe. A fix that is only correct when it completes is a fix that has
|
|
63
|
+
not been designed for the case it exists to handle.
|
|
64
|
+
applies-to: fix-shapes
|
|
65
|
+
evidence-required: true
|
|
66
|
+
|
|
67
|
+
# R-028a. TEST THE TESTABILITY CLAIMS. The accepted evidence is the M2-P2
|
|
68
|
+
# red-witness harness's own evidence file, not the plan's assertion that
|
|
69
|
+
# something is testable.
|
|
70
|
+
- id: testability-claim-tested
|
|
71
|
+
probe: >-
|
|
72
|
+
For every claim in this plan that something is testable, guarded or
|
|
73
|
+
witnessed, what is the accepted proof? The harness's captured evidence
|
|
74
|
+
file (`witness-records.json`, whose per-member `runs[].exitCode` and
|
|
75
|
+
`failedNamedTests` record what actually ran) is the proof; the plan's
|
|
76
|
+
own sentence is not. Where no such file can exist yet because the
|
|
77
|
+
behaviour is not built, say so in those words and mark the claim as
|
|
78
|
+
owed, rather than letting an untested assertion pass as a tested one.
|
|
79
|
+
applies-to: testability
|
|
80
|
+
evidence-required: true
|
|
81
|
+
|
|
82
|
+
- id: acceptance-criterion-is-falsifiable
|
|
83
|
+
probe: >-
|
|
84
|
+
Is each acceptance criterion FALSIFIABLE? "Works correctly" is banned;
|
|
85
|
+
the register is a command, its exit code and a count ("node --test exits
|
|
86
|
+
0 and reports N tests, N greater than zero"). Quote every criterion that
|
|
87
|
+
names no command, no observable output and no count, and propose the
|
|
88
|
+
falsifiable form. A criterion nobody can fail is a criterion nobody has
|
|
89
|
+
to meet.
|
|
90
|
+
applies-to: testability
|
|
91
|
+
evidence-required: true
|
|
92
|
+
|
|
93
|
+
- id: hazard-class-is-not-the-criteria-reworded
|
|
94
|
+
probe: >-
|
|
95
|
+
Does each phase's declared hazard class name something its own
|
|
96
|
+
acceptance criteria CANNOT reach? T-007 records a phase meeting fifteen
|
|
97
|
+
of fifteen executed criteria while live-locking every supervision
|
|
98
|
+
command. A hazard class that is the criteria list reworded reproduces
|
|
99
|
+
that failure while appearing to fix it. For each class, name the
|
|
100
|
+
criterion that reaches it or state that none does and name the
|
|
101
|
+
instrument that does.
|
|
102
|
+
applies-to: plan-structure
|
|
103
|
+
evidence-required: true
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
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
|
+
*/
|
|
32
|
+
function singleLine(text) {
|
|
33
|
+
return text.replace(/\s+/g, " ").trim();
|
|
34
|
+
}
|
|
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
|
+
});
|
|
59
|
+
}
|
|
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
|
+
}
|
|
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>;
|