@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.
- package/AGENTS.md +56 -4
- package/assurance-modes.yaml +23 -2
- package/dist/bin/tiphys.js +86 -8
- package/dist/src/adapters/load.d.ts +202 -0
- package/dist/src/adapters/load.js +440 -0
- package/dist/src/brief.js +27 -20
- package/dist/src/checks.d.ts +720 -9
- package/dist/src/checks.js +1874 -163
- package/dist/src/cli.js +11 -0
- package/dist/src/commands/brief.js +27 -4
- package/dist/src/commands/cutover.d.ts +35 -0
- package/dist/src/commands/cutover.js +448 -0
- package/dist/src/commands/doctor.d.ts +229 -0
- package/dist/src/commands/doctor.js +968 -27
- package/dist/src/commands/init.d.ts +3 -3
- package/dist/src/commands/init.js +57 -8
- package/dist/src/commands/lock.d.ts +33 -0
- package/dist/src/commands/lock.js +117 -6
- package/dist/src/commands/next.d.ts +130 -0
- package/dist/src/commands/next.js +597 -0
- package/dist/src/commands/pool.js +12 -1
- package/dist/src/commands/resume.d.ts +1 -0
- package/dist/src/commands/resume.js +88 -0
- package/dist/src/commands/spawn.js +51 -2
- package/dist/src/commands/status.d.ts +6 -4
- package/dist/src/commands/status.js +6 -4
- package/dist/src/commands/sync.d.ts +47 -0
- package/dist/src/commands/sync.js +341 -0
- package/dist/src/commands/teardown.js +10 -2
- package/dist/src/commands/validate.js +70 -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 +152 -2
- package/dist/src/exec/env.js +146 -2
- package/dist/src/fleet.d.ts +172 -0
- package/dist/src/fleet.js +219 -1
- package/dist/src/gates/citations.js +7 -1
- package/dist/src/gates/coverage.d.ts +113 -22
- package/dist/src/gates/coverage.js +166 -31
- package/dist/src/gates/credentials.d.ts +159 -0
- package/dist/src/gates/credentials.js +221 -2
- package/dist/src/gates/gate-classes.d.ts +56 -0
- package/dist/src/gates/gate-classes.js +633 -0
- package/dist/src/gates/merge-preconditions.d.ts +319 -0
- package/dist/src/gates/merge-preconditions.js +932 -0
- package/dist/src/gates/red-witness.js +105 -13
- package/dist/src/gates/run.d.ts +49 -1
- package/dist/src/gates/run.js +83 -5
- package/dist/src/gates/schemas/phase-declaration.schema.json +45 -0
- package/dist/src/gates/suite.js +48 -7
- package/dist/src/hooks.d.ts +55 -3
- package/dist/src/hooks.js +69 -6
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.js +30 -0
- package/dist/src/lock.d.ts +82 -4
- package/dist/src/lock.js +314 -22
- package/dist/src/model-resolution.d.ts +159 -0
- package/dist/src/model-resolution.js +307 -0
- package/dist/src/path-identity.d.ts +32 -0
- package/dist/src/path-identity.js +38 -0
- package/dist/src/pool.d.ts +197 -1
- package/dist/src/pool.js +289 -22
- package/dist/src/roles.d.ts +31 -0
- package/dist/src/roles.js +42 -0
- package/dist/src/spawn.d.ts +307 -2
- package/dist/src/spawn.js +690 -19
- package/dist/src/status.d.ts +27 -2
- package/dist/src/status.js +34 -5
- package/dist/src/task.d.ts +295 -55
- package/dist/src/task.js +125 -123
- package/dist/src/teardown.d.ts +7 -0
- package/dist/src/teardown.js +120 -12
- package/dist/src/validate.d.ts +44 -11
- package/dist/src/validate.js +44 -34
- package/dist/src/watcher.js +1 -11
- package/dist/src/witness/run.d.ts +32 -7
- package/dist/src/witness/run.js +76 -30
- package/dist/src/witness/spec.d.ts +168 -0
- package/dist/src/witness/spec.js +240 -18
- package/dist/tsconfig.src.tsbuildinfo +1 -1
- package/gate-registry.yaml +136 -0
- package/gates.manifest.json +63 -1
- package/package.json +18 -3
- package/roles/implementer.md +3 -0
- package/schemas/README.md +1 -0
- package/schemas/assurance-modes.schema.json +1 -1
- package/schemas/charter.schema.json +19 -0
- package/schemas/cutover-state.schema.json +64 -0
- package/schemas/executor-record.schema.json +36 -0
- package/schemas/model-resolution.schema.json +362 -0
- package/schemas/verdict.schema.json +9 -3
- package/schemas/write-bypass.schema.json +69 -0
|
@@ -96,6 +96,76 @@ export const TYPE_TABLE = new Map([
|
|
|
96
96
|
`mechanism-rule-evidence-resolves` are registered for type `tuition` and
|
|
97
97
|
nothing runs them until the type resolves. */
|
|
98
98
|
["tuition", "tuition.schema.json"],
|
|
99
|
+
/* M4-P25 criterion 4. The fleet's `cutover.json`. It carries NO `kind`
|
|
100
|
+
field, so this row extends `--type` and NOT `resolveAutoType`; `role-brief`
|
|
101
|
+
above is the standing precedent for that half of M3R-001, and the half the
|
|
102
|
+
rule is actually about, a schema that ships without a `--type` row and
|
|
103
|
+
forces a later implementer to edit an undeclared file, is satisfied here.
|
|
104
|
+
The document is validated programmatically on every write as well
|
|
105
|
+
(src/cutover.ts:253), which is what criterion 4 means by validated by the
|
|
106
|
+
schema rather than by the command. */
|
|
107
|
+
["cutover-state", "cutover-state.schema.json"],
|
|
108
|
+
/* M4-P3 criterion 4. tasks/<id>/executor.json, the launch record an
|
|
109
|
+
ExecutorAdapter writes.
|
|
110
|
+
|
|
111
|
+
THIS ROW IS THE WHOLE REGISTRATION, AND `resolveAutoType` IS NOT A SECOND
|
|
112
|
+
PLACE TO EDIT: that function answers `--type auto` by reading the
|
|
113
|
+
instance's `kind` and looking it up in THIS MAP, so a row added here is
|
|
114
|
+
the auto resolver's source of truth as well. M3R-001's requirement that a
|
|
115
|
+
shipped schema not arrive without its `--type` row is satisfied by the
|
|
116
|
+
row; nothing else in this module holds a type list.
|
|
117
|
+
|
|
118
|
+
WHAT THE ROW CANNOT DO, stated here rather than left to be discovered.
|
|
119
|
+
An executor record carries no `kind` field, because M4-P3's criterion 6
|
|
120
|
+
fixes the record's fields and `kind` is not among them, so `--type auto`
|
|
121
|
+
on a real executor.json is a USAGE error naming the missing kind. That is
|
|
122
|
+
the `role-brief` situation above reached by a different route: there the
|
|
123
|
+
document cannot be decoded before the type is known, here the decoded
|
|
124
|
+
document does not say what it is. Both are types a caller must NAME. The
|
|
125
|
+
property is asserted by a registered test rather than trusted, so a later
|
|
126
|
+
phase that gives the record a `kind` reddens this comment instead of
|
|
127
|
+
leaving it stale. */
|
|
128
|
+
["executor-record", "executor-record.schema.json"],
|
|
129
|
+
/* M4-P7 criterion 1. tasks/<id>/model-resolution.json, the record a harness
|
|
130
|
+
adapter writes at TURN END saying which tier was requested, which the
|
|
131
|
+
charter resolved, and which model family served the turn.
|
|
132
|
+
|
|
133
|
+
ONE ROW SERVES BOTH REGISTRATIONS, which is the property the brief for
|
|
134
|
+
this phase asks to be got right and the row above states the mechanism
|
|
135
|
+
for: `resolveAutoType` answers `--type auto` by reading the instance's
|
|
136
|
+
`kind` and looking it up in THIS MAP. The difference from the executor
|
|
137
|
+
record is that a model-resolution record DOES carry a `kind`, required and
|
|
138
|
+
`const`, so `--type auto` resolves a real record rather than raising the
|
|
139
|
+
usage error the row above documents. `test/model-resolution.test.ts`
|
|
140
|
+
asserts both halves against the shipped CLI rather than against this
|
|
141
|
+
comment.
|
|
142
|
+
|
|
143
|
+
THE DERIVED CHECKS COME WITH THE ROW AND ARE THE REASON IT MATTERS. The
|
|
144
|
+
schema is Kind A and reaches presence; `model-resolution-subject-echo` in
|
|
145
|
+
src/checks.ts is Kind B and reaches agreement between sibling fields, and
|
|
146
|
+
nothing runs it until the type resolves here. */
|
|
147
|
+
["model-resolution", "model-resolution.schema.json"],
|
|
148
|
+
/* M4-P9 step 4. <fleet>/write-bypass.json, the infrastructure-hotfix bypass
|
|
149
|
+
the project-write block reads.
|
|
150
|
+
|
|
151
|
+
THE ROW IS WHAT MAKES THE DECLARATION CHECKABLE BEFORE IT IS TRUSTED. The
|
|
152
|
+
hook reads the document defensively and refuses on anything it cannot use,
|
|
153
|
+
which is the right behaviour at decision time and a terrible way to find
|
|
154
|
+
out you mistyped an expiry: the orchestrator learns at the moment its own
|
|
155
|
+
hotfix is refused. With this row an author runs `tiphys validate --type
|
|
156
|
+
write-bypass <file>` and is told which pointer is wrong, before the
|
|
157
|
+
document is in front of a hook.
|
|
158
|
+
|
|
159
|
+
A bypass declaration DOES carry a `kind`, required and `const`, so one row
|
|
160
|
+
serves `--type` and `resolveAutoType` together (M3R-001), unlike the
|
|
161
|
+
executor record above.
|
|
162
|
+
|
|
163
|
+
WHAT THE ROW CANNOT DO, stated rather than left to be discovered: the
|
|
164
|
+
schema reaches the SHAPE and never the WINDOW. Whether `expiresAt` is in
|
|
165
|
+
the future is a comparison against a clock, and `decideWrite` takes `now`
|
|
166
|
+
as a parameter precisely so that comparison is testable at the boundary
|
|
167
|
+
rather than hidden inside a validator. */
|
|
168
|
+
["write-bypass", "write-bypass.schema.json"],
|
|
99
169
|
]);
|
|
100
170
|
/**
|
|
101
171
|
* COMPANION SCHEMAS, declared beside the type table (M3-P4 step 3).
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cutover state and rollback (kernel plan M4, M4-P26; D-19's second limb at
|
|
3
|
+
* delivery/plan/kernel-plan-v1.md:394).
|
|
4
|
+
*
|
|
5
|
+
* WHAT THIS MODULE IS. The freeze point is not one event. It is FIVE
|
|
6
|
+
* independently flippable switches, taken from the list the current delivery
|
|
7
|
+
* process retains at
|
|
8
|
+
* delivery/decisions/DR-0025-controlled-pre-m4-local-pilot.md:45 and read
|
|
9
|
+
* against DR-0036's authority condition. Five switches rather than one event
|
|
10
|
+
* is what makes rollback PARTIAL rather than all-or-nothing, and it is why
|
|
11
|
+
* `credentials-and-refs`, the one switch with owner latency, is separable
|
|
12
|
+
* from the four that are not.
|
|
13
|
+
*
|
|
14
|
+
* THE SPLIT THAT KEEPS THIS HONEST, and it runs through every function here.
|
|
15
|
+
* Rollback of the FILES is cheap: everything under the retirement roots is
|
|
16
|
+
* git-tracked and revertible from history. Rollback of AUTHORITY is the
|
|
17
|
+
* expensive half: the branch-protection ruleset is owner-configured, the
|
|
18
|
+
* orchestrator cannot change it, and
|
|
19
|
+
* delivery/decisions/DR-0036-the-harness-adapter-leads-m4-and-the-kernel-is-the-second-subject.md:15
|
|
20
|
+
* prices the revert at "whatever phases ran under it". Nothing in this module
|
|
21
|
+
* flips authority. It PREPARES the request an owner acts on, and it refuses
|
|
22
|
+
* loudly rather than pretending the preparation is the act.
|
|
23
|
+
*
|
|
24
|
+
* WHAT THIS MODULE DELIBERATELY DOES NOT DO. It does not print
|
|
25
|
+
* `tiphys cutover status`, does not write switches to `kernel`, and does not
|
|
26
|
+
* ship a state schema. Those are M4-P25's acceptance criteria and M4-P25's
|
|
27
|
+
* files. This phase was dispatched ahead of its stated dependency because the
|
|
28
|
+
* conflict pre-pass holds `src/commands/cutover.ts` for exactly one unit at a
|
|
29
|
+
* time, so the state MODEL is created here and the status SURFACE is left
|
|
30
|
+
* where the plan put it. The boundary is recorded in
|
|
31
|
+
* delivery/work-history/m4-p26.md:1 rather than left to be discovered.
|
|
32
|
+
*
|
|
33
|
+
* PLAN CONSTRAINTS. C-1: current state comes from `cutover.json` and from
|
|
34
|
+
* meta.json plus the turn-end file, never from the tail of a log. C-2: no pid,
|
|
35
|
+
* no signal, no /proc and no process probing appears here; a live worktree is
|
|
36
|
+
* a DIRECTORY and an open task is a FILE STATE. C-3: nothing is backgrounded.
|
|
37
|
+
*/
|
|
38
|
+
import type { Fleet } from "./fleet.ts";
|
|
39
|
+
import { type GuardResult } from "./task.ts";
|
|
40
|
+
import type { SchemaDocument } from "./validate.ts";
|
|
41
|
+
/**
|
|
42
|
+
* The five retained items of DR-0025, in the order section 4.3 of the M4
|
|
43
|
+
* plan tabulates them. This list is CLOSED. A sixth name is a plan revision,
|
|
44
|
+
* not a code edit, because every rollback trigger enumerates it.
|
|
45
|
+
*/
|
|
46
|
+
export declare const CUTOVER_SWITCHES: readonly ["planning-and-scope", "review-and-arbitration", "credentials-and-refs", "salvage-and-recovery", "closeout"];
|
|
47
|
+
export type CutoverSwitchName = (typeof CUTOVER_SWITCHES)[number];
|
|
48
|
+
/** Which process holds the authority a switch governs. */
|
|
49
|
+
export type SwitchState = "current" | "kernel";
|
|
50
|
+
export declare const SWITCH_STATES: readonly SwitchState[];
|
|
51
|
+
/**
|
|
52
|
+
* The switch with owner latency. Named as a constant because three separate
|
|
53
|
+
* places have to treat it differently and a repeated string literal is how
|
|
54
|
+
* one of them ends up not doing so.
|
|
55
|
+
*/
|
|
56
|
+
export declare const OWNER_LATENCY_SWITCH: CutoverSwitchName;
|
|
57
|
+
/**
|
|
58
|
+
* One switch's record. `restoreTo` is the value the switch held BEFORE the
|
|
59
|
+
* flip. It is what makes freeze-point restore a matter of reading a recorded
|
|
60
|
+
* value rather than reconstructing an intent, and a record without it cannot
|
|
61
|
+
* be rolled back at all, which is why every read here refuses one.
|
|
62
|
+
*/
|
|
63
|
+
export interface SwitchRecord {
|
|
64
|
+
state: SwitchState;
|
|
65
|
+
flippedAt: string;
|
|
66
|
+
flippedBy: string;
|
|
67
|
+
reason: string;
|
|
68
|
+
restoreTo: SwitchState;
|
|
69
|
+
}
|
|
70
|
+
export interface CutoverState {
|
|
71
|
+
switches: Record<CutoverSwitchName, SwitchRecord>;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* `cutover.json` sits at the FLEET ROOT and not under `state/`, because
|
|
75
|
+
* `state/` is gitignored (src/fleet.ts:29) and trigger 2 step 1 identifies a
|
|
76
|
+
* flip with `git log` over this exact path. A switch history that is not
|
|
77
|
+
* committed is not a history.
|
|
78
|
+
*/
|
|
79
|
+
export declare function cutoverStatePath(fleet: Fleet): string;
|
|
80
|
+
/**
|
|
81
|
+
* The document as it was parsed, keys and all. It is carried beside the typed
|
|
82
|
+
* state because the typed view is NARROWER than the file: `CutoverState` names
|
|
83
|
+
* `switches` and nothing else, so a writer that serialises the typed view
|
|
84
|
+
* DELETES every other key the file held. A rollback is a change to the
|
|
85
|
+
* switches, not a rewrite of the document, and this is what lets the two be
|
|
86
|
+
* different sizes.
|
|
87
|
+
*/
|
|
88
|
+
export type CutoverDocument = Record<string, unknown>;
|
|
89
|
+
export type CutoverRead = {
|
|
90
|
+
kind: "read";
|
|
91
|
+
state: CutoverState;
|
|
92
|
+
document: CutoverDocument;
|
|
93
|
+
} | {
|
|
94
|
+
kind: "absent";
|
|
95
|
+
} | {
|
|
96
|
+
kind: "refused";
|
|
97
|
+
reason: string;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Validate a parsed document into a CutoverState, returning every reason at
|
|
101
|
+
* once rather than the first.
|
|
102
|
+
*
|
|
103
|
+
* FAIL CLOSED ON `restoreTo`. A record missing it, or carrying it as a word
|
|
104
|
+
* outside the two-value vocabulary, is REFUSED rather than defaulted to
|
|
105
|
+
* `current`. Defaulting would make rollback silently guess, and a guess that
|
|
106
|
+
* happens to be right most of the time is the guard that cannot go red.
|
|
107
|
+
*/
|
|
108
|
+
export declare function validateCutoverDocument(document: unknown): string[];
|
|
109
|
+
/** Read and validate the fleet's cutover state. */
|
|
110
|
+
export declare function readCutoverState(fleet: Fleet): CutoverRead;
|
|
111
|
+
export declare function renderCutoverState(state: CutoverState | CutoverDocument): string;
|
|
112
|
+
/**
|
|
113
|
+
* Put a new switch table into the document the file actually held, leaving
|
|
114
|
+
* every other key exactly as it was read.
|
|
115
|
+
*
|
|
116
|
+
* WHY THIS EXISTS AS A NAMED FUNCTION. `validateCutoverDocument` refuses an
|
|
117
|
+
* unknown SWITCH NAME because the five are a closed list, and that made it
|
|
118
|
+
* look as though the whole document were closed. It is not: M4-P25 owns the
|
|
119
|
+
* schema and may add top-level keys, so a rollback that serialises only what
|
|
120
|
+
* this module's interface names would silently delete them. Refusing them
|
|
121
|
+
* instead would be this phase deciding M4-P25's schema, which is not its to
|
|
122
|
+
* decide, so the document is CARRIED rather than narrowed or refused.
|
|
123
|
+
*/
|
|
124
|
+
export declare function withSwitches(document: CutoverDocument, switches: Record<CutoverSwitchName, SwitchRecord>): CutoverDocument;
|
|
125
|
+
/**
|
|
126
|
+
* Publish a whole cutover state ATOMICALLY: serialise everything, write it to
|
|
127
|
+
* a sibling temporary file, fsync that file, then rename over the
|
|
128
|
+
* destination. Nothing partial is ever visible at the destination path,
|
|
129
|
+
* because rename(2) within one directory is atomic.
|
|
130
|
+
*
|
|
131
|
+
* THE MECHANISM THIS EXISTS AGAINST is not "a crash". It is WRITING STATE
|
|
132
|
+
* PER ITEM WHILE ITERATING, which leaves a file that is internally valid and
|
|
133
|
+
* factually wrong: three switches saying `kernel` and two saying `current`
|
|
134
|
+
* describes a process that has no owner for three of its five authorities.
|
|
135
|
+
* Every caller in this module builds the complete next state first and calls
|
|
136
|
+
* this once.
|
|
137
|
+
*/
|
|
138
|
+
export declare function publishCutoverState(path: string, state: CutoverState | CutoverDocument): void;
|
|
139
|
+
export interface SwitchChange {
|
|
140
|
+
name: CutoverSwitchName;
|
|
141
|
+
from: SwitchState;
|
|
142
|
+
to: SwitchState;
|
|
143
|
+
}
|
|
144
|
+
export interface RollbackPlan {
|
|
145
|
+
changes: SwitchChange[];
|
|
146
|
+
next: CutoverState;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Observer called once per switch as the next state is assembled IN MEMORY,
|
|
150
|
+
* before anything reaches disk. It exists so a caller can report progress,
|
|
151
|
+
* and it is also the seam criterion 2's red witness uses: an observer that
|
|
152
|
+
* throws on the third call aborts the assembly, and the destination file must
|
|
153
|
+
* still hold the five values it held before.
|
|
154
|
+
*/
|
|
155
|
+
export type SwitchObserver = (change: SwitchChange, index: number) => void;
|
|
156
|
+
export interface RollbackOptions {
|
|
157
|
+
now: string;
|
|
158
|
+
by: string;
|
|
159
|
+
reason: string;
|
|
160
|
+
onSwitch?: SwitchObserver;
|
|
161
|
+
}
|
|
162
|
+
export type RollbackTrigger = "drain-reversal" | "freeze-point-restore" | "retirement-unmet";
|
|
163
|
+
export declare const ROLLBACK_TRIGGERS: readonly RollbackTrigger[];
|
|
164
|
+
/**
|
|
165
|
+
* The target value a trigger moves a switch to.
|
|
166
|
+
*
|
|
167
|
+
* Trigger 1 (drain reversal) hands every authority back to the current
|
|
168
|
+
* process, so the target is `current` for all five: in-flight work has to be
|
|
169
|
+
* handled by the process that started it.
|
|
170
|
+
*
|
|
171
|
+
* Trigger 2 (freeze-point restore) reads each switch's own `restoreTo`. That
|
|
172
|
+
* is the difference between the two, and it is why M4-P25 criterion 4 refuses
|
|
173
|
+
* a write that omits the field.
|
|
174
|
+
*
|
|
175
|
+
* ROLLBACK IS MONOTONE AND THE CLAMP BELOW IS WHAT MAKES THE MODULE HEADER
|
|
176
|
+
* TRUE. `restoreTo` is the value a switch held BEFORE its last flip, and
|
|
177
|
+
* `planRollback` sets it to the value the switch is leaving. So a switch that
|
|
178
|
+
* has already been rolled back to `current` carries `restoreTo: "kernel"`, and
|
|
179
|
+
* returning that value would move the switch FORWARD on the second run of the
|
|
180
|
+
* same command. The failure arm of trigger 1 step 1 exits nonzero after the
|
|
181
|
+
* local write has already happened, and the natural response to that is to run
|
|
182
|
+
* the command again, so the second run is not a hypothetical. A rollback never
|
|
183
|
+
* hands authority to the kernel: `current` is a floor, and a switch already at
|
|
184
|
+
* it is a no-op rather than a flip.
|
|
185
|
+
*/
|
|
186
|
+
export declare function targetFor(trigger: RollbackTrigger, record: SwitchRecord): SwitchState;
|
|
187
|
+
/**
|
|
188
|
+
* Assemble the complete next state. Throws rather than returning a partial
|
|
189
|
+
* plan, and touches no file at all: publishing is a separate call.
|
|
190
|
+
*/
|
|
191
|
+
export declare function planRollback(state: CutoverState, trigger: RollbackTrigger, options: RollbackOptions): RollbackPlan;
|
|
192
|
+
export type RollbackOutcome = {
|
|
193
|
+
ok: true;
|
|
194
|
+
changes: SwitchChange[];
|
|
195
|
+
next: CutoverState;
|
|
196
|
+
} | {
|
|
197
|
+
ok: false;
|
|
198
|
+
reason: string;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Plan and publish in one call: validate the whole input, assemble the whole
|
|
202
|
+
* next state, then write once.
|
|
203
|
+
*
|
|
204
|
+
* ORDER IS THE PROPERTY. Every refusal reachable from a bad input happens
|
|
205
|
+
* before `publishCutoverState` is called, so a refusal leaves the file
|
|
206
|
+
* byte-identical. That is asserted directly rather than trusted: see
|
|
207
|
+
* test/cutover.test.ts.
|
|
208
|
+
*/
|
|
209
|
+
export declare function applyRollback(fleet: Fleet, trigger: RollbackTrigger, options: RollbackOptions): RollbackOutcome;
|
|
210
|
+
/**
|
|
211
|
+
* `unexaminable` is a THIRD kind and not an error channel. A drain predicate
|
|
212
|
+
* that cannot decide an entry has not found it clean, and folding the
|
|
213
|
+
* undecidable into "nothing here" is how a guard reads quiet at full speed.
|
|
214
|
+
*/
|
|
215
|
+
export type InFlightKind = "worktree" | "task" | "unexaminable";
|
|
216
|
+
export interface InFlightItem {
|
|
217
|
+
kind: InFlightKind;
|
|
218
|
+
id: string;
|
|
219
|
+
detail: string;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* WHAT DRAIN COUNTS, and the omission is the decision.
|
|
223
|
+
*
|
|
224
|
+
* In-flight work only: live worktrees and open tasks with no turn-end. Pushed
|
|
225
|
+
* unmerged BRANCHES are deliberately NOT counted (M4-D-15, decided at
|
|
226
|
+
* delivery/plan/kernel-plan-m4.md:3279). This container cannot delete a remote
|
|
227
|
+
* ref and the delete dry run exits 0 either way, and the probes generalised
|
|
228
|
+
* that further: a dry run does not probe push authorization at all
|
|
229
|
+
* (delivery/verification/m4-prototype-probes.md:165). A drain predicate over
|
|
230
|
+
* branches therefore blocks cutover on an owner action with no local
|
|
231
|
+
* pre-check, which is a predicate that can never read clean.
|
|
232
|
+
*
|
|
233
|
+
* C-2 is load-bearing here. A live worktree is a DIRECTORY on disk and an
|
|
234
|
+
* open task is meta.json's status plus the absence of the turn-end file. No
|
|
235
|
+
* process is probed, no pid is read, and nothing asks whether an agent is
|
|
236
|
+
* still breathing.
|
|
237
|
+
*/
|
|
238
|
+
export declare function inFlightItems(fleet: Fleet): InFlightItem[];
|
|
239
|
+
/**
|
|
240
|
+
* The named list of things no local command can see. It is printed with every
|
|
241
|
+
* drain report and is NOT abbreviated when the network is unreachable: a
|
|
242
|
+
* command that silently degrades to a shorter answer is indistinguishable
|
|
243
|
+
* from one reporting a quiet system, which is the shape standing warning 6
|
|
244
|
+
* records for watchers.
|
|
245
|
+
*/
|
|
246
|
+
export declare const CANNOT_SEE: readonly string[];
|
|
247
|
+
export interface CommandResult {
|
|
248
|
+
status: number;
|
|
249
|
+
stdout: string;
|
|
250
|
+
stderr: string;
|
|
251
|
+
}
|
|
252
|
+
export declare function runGit(cwd: string, args: string[]): CommandResult;
|
|
253
|
+
/**
|
|
254
|
+
* Commit and push the fleet's rolled-back state.
|
|
255
|
+
*
|
|
256
|
+
* THE FAILURE ARM IS WRITTEN FIRST, and that ordering is the point rather
|
|
257
|
+
* than a style. A rollback that changes `cutover.json` locally and does not
|
|
258
|
+
* land it leaves every other environment believing the switches are still
|
|
259
|
+
* `kernel`, and a push whose failure is swallowed is indistinguishable from a
|
|
260
|
+
* push that worked. Every arm below returns a reason; none returns silence.
|
|
261
|
+
*
|
|
262
|
+
* A fleet with no remote is a REFUSAL, not a skip. The caller may pass
|
|
263
|
+
* `allowNoRemote` to turn it into a reported, non-fatal condition, and when it
|
|
264
|
+
* does the reason is still printed, so a reader can never mistake the local
|
|
265
|
+
* write for a published one.
|
|
266
|
+
*
|
|
267
|
+
* THE COMMIT CARRIES THE NAMED PATHS AND NOTHING ELSE. Staging everything at
|
|
268
|
+
* the fleet root stages whatever is dirty anywhere under it, and the
|
|
269
|
+
* precondition of trigger 1 is that IN-FLIGHT WORK EXISTS: a half-written
|
|
270
|
+
* note, a torn meta.json, an operator scratch file. Committing all of it under
|
|
271
|
+
* the message "cutover rollback" publishes somebody else's unfinished work
|
|
272
|
+
* under this command's name, which is a write whose scope is wider than the
|
|
273
|
+
* sentence describing it. Three checks make the scope exactly the named set:
|
|
274
|
+
* the index must be empty before, the staging names paths, and the staged set
|
|
275
|
+
* is compared with the requested one afterwards.
|
|
276
|
+
*/
|
|
277
|
+
export type SyncOutcome = {
|
|
278
|
+
ok: true;
|
|
279
|
+
pushed: true;
|
|
280
|
+
head: string;
|
|
281
|
+
staged: string[];
|
|
282
|
+
} | {
|
|
283
|
+
ok: true;
|
|
284
|
+
pushed: false;
|
|
285
|
+
reason: string;
|
|
286
|
+
staged: string[];
|
|
287
|
+
} | {
|
|
288
|
+
ok: false;
|
|
289
|
+
reason: string;
|
|
290
|
+
};
|
|
291
|
+
export declare function syncFleetState(fleetRoot: string, options: {
|
|
292
|
+
allowNoRemote?: boolean;
|
|
293
|
+
message: string;
|
|
294
|
+
paths: string[];
|
|
295
|
+
}): SyncOutcome;
|
|
296
|
+
/**
|
|
297
|
+
* Refuse to touch a tree that holds uncommitted work.
|
|
298
|
+
*
|
|
299
|
+
* ANY `git checkout --` in such a tree is destructive, INCLUDING when it names
|
|
300
|
+
* a single path, and especially the path being edited. This repository has
|
|
301
|
+
* paid for that twice; the rule is standing warning 8 in the agent-rules file.
|
|
302
|
+
* The guard is a precondition of the restore and not a warning printed beside
|
|
303
|
+
* it, because a warning is advice and this is the only thing standing between
|
|
304
|
+
* a rollback and four rounds of somebody's uncommitted work.
|
|
305
|
+
*/
|
|
306
|
+
export declare function refuseIfTreeDirty(repoRoot: string): GuardResult;
|
|
307
|
+
export type RestoreFilesOutcome = {
|
|
308
|
+
ok: true;
|
|
309
|
+
roots: string[];
|
|
310
|
+
removed: string[];
|
|
311
|
+
} | {
|
|
312
|
+
ok: false;
|
|
313
|
+
reason: string;
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* Restore the named retirement roots from a pre-freeze sha.
|
|
317
|
+
*
|
|
318
|
+
* The dirty check runs FIRST and returns before any version-control
|
|
319
|
+
* invocation that can write. That order is the whole guarantee, and the test
|
|
320
|
+
* asserts the dirty file is byte-identical afterwards rather than asserting
|
|
321
|
+
* the exit code alone.
|
|
322
|
+
*
|
|
323
|
+
* "RESTORE" IS A CLAIM ABOUT THE WHOLE ROOT, AND CHECKING A TREE OUT OVER A
|
|
324
|
+
* PATH IS NOT ONE. `checkout <sha> -- <root>` writes what the sha held and
|
|
325
|
+
* removes NOTHING, so every file added under the root after the freeze
|
|
326
|
+
* survives the restore untouched. The result is a hybrid tree that the caller
|
|
327
|
+
* prints `RESTORED` over: a verdict wider than the operation that produced it.
|
|
328
|
+
* The post-freeze additions are therefore enumerated and removed, and then the
|
|
329
|
+
* root is COMPARED with the sha. The comparison is the verdict; without it the
|
|
330
|
+
* success arm is an assumption, and a success arm that cannot fail is the
|
|
331
|
+
* guard that cannot go red.
|
|
332
|
+
*/
|
|
333
|
+
export declare function restoreRetirementRoots(repoRoot: string, sha: string, roots: string[]): RestoreFilesOutcome;
|
|
334
|
+
/**
|
|
335
|
+
* The property that makes trigger 2 step 3 unrehearsable, in one sentence,
|
|
336
|
+
* exported so the document, the command and the rehearsal script all print
|
|
337
|
+
* the SAME sentence and cannot drift apart.
|
|
338
|
+
*/
|
|
339
|
+
export declare const UNREHEARSABLE_REASON: string;
|
|
340
|
+
export type RestoreRequestOutcome = {
|
|
341
|
+
ok: true;
|
|
342
|
+
text: string;
|
|
343
|
+
fields: number;
|
|
344
|
+
} | {
|
|
345
|
+
ok: false;
|
|
346
|
+
reasons: string[];
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* Generate the owner request for trigger 2 step 3.
|
|
350
|
+
*
|
|
351
|
+
* TWO STRUCTURALLY DIFFERENT REFUSALS, and the second is the one a field
|
|
352
|
+
* check is green on. An ABSENT key is caught by asking whether the key is
|
|
353
|
+
* there. A key that is PRESENT and EMPTY passes that question and produces a
|
|
354
|
+
* request with a blank where a pre-flip value belongs, which the owner would
|
|
355
|
+
* have to fill in from memory. Both are refused, and the refusal names the
|
|
356
|
+
* field, because an owner request with a hole in it is worse than no request:
|
|
357
|
+
* it looks complete.
|
|
358
|
+
*/
|
|
359
|
+
export declare function generateRestoreRequest(document: unknown): RestoreRequestOutcome;
|
|
360
|
+
export type Disposition = "PORT" | "DELETE" | "KEEP";
|
|
361
|
+
export interface RetirementRow {
|
|
362
|
+
id: string;
|
|
363
|
+
disposition: Disposition;
|
|
364
|
+
/** For PORT: the kernel artifact the rule moves to, relative to the repo. */
|
|
365
|
+
destination?: string;
|
|
366
|
+
/**
|
|
367
|
+
* For PORT: a command that was RED under the OLD rule. Retirement is
|
|
368
|
+
* refused until it is red under the NEW artifact too. This is what turns
|
|
369
|
+
* "PORT, verify not weaker" from a judgment into a command.
|
|
370
|
+
*/
|
|
371
|
+
negativeWitness?: string[];
|
|
372
|
+
/**
|
|
373
|
+
* The exit status the row RECORDED for its negative witness, when it
|
|
374
|
+
* records one. Present, it is required to match: see `evaluatePortRow`.
|
|
375
|
+
*/
|
|
376
|
+
expectedWitnessExit?: number;
|
|
377
|
+
}
|
|
378
|
+
export type PortVerdict = "ported" | "unported";
|
|
379
|
+
export interface PortResult {
|
|
380
|
+
id: string;
|
|
381
|
+
verdict: PortVerdict;
|
|
382
|
+
reason: string;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Decide one PORT row.
|
|
386
|
+
*
|
|
387
|
+
* `ported` needs BOTH halves and the second is the one that matters. A verdict
|
|
388
|
+
* derived only from the destination file existing is the vacuous version: a
|
|
389
|
+
* file can exist and say nothing. The negative witness was RED under the old
|
|
390
|
+
* rule, so a witness that exits 0 under the new artifact means the new
|
|
391
|
+
* artifact does not catch what the old one caught, which is precisely
|
|
392
|
+
* "WEAKER". Exit 0 from the witness is therefore `unported`, not a pass.
|
|
393
|
+
*
|
|
394
|
+
* `ported` IS REACHED FROM A POSITIVE TEST AND NEVER FROM A FALLTHROUGH, and
|
|
395
|
+
* that is what the first three checks below are. `readRetirementInventory`
|
|
396
|
+
* casts whatever the fixture's `rows` array holds, so a row may be a string, a
|
|
397
|
+
* number, or an object whose `disposition` is misspelt; the earlier form asked
|
|
398
|
+
* only whether the disposition was NOT the word `PORT`, so every one of those
|
|
399
|
+
* returned `ported`, which is a verdict of "this retirement is complete" over
|
|
400
|
+
* a row nobody could read. The vocabulary is closed, so a value outside it is
|
|
401
|
+
* `unported` and names itself.
|
|
402
|
+
*/
|
|
403
|
+
export declare const DISPOSITIONS: readonly Disposition[];
|
|
404
|
+
export declare function evaluatePortRow(row: RetirementRow, repoRoot: string): PortResult;
|
|
405
|
+
export type InventoryRead = {
|
|
406
|
+
kind: "read";
|
|
407
|
+
rows: RetirementRow[];
|
|
408
|
+
} | {
|
|
409
|
+
kind: "absent";
|
|
410
|
+
} | {
|
|
411
|
+
kind: "refused";
|
|
412
|
+
reason: string;
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Read an M4-P23 retirement inventory. The path is a parameter because that
|
|
416
|
+
* phase runs concurrently with this one and its file does not exist on this
|
|
417
|
+
* branch; the rehearsal supplies a fixture.
|
|
418
|
+
*/
|
|
419
|
+
export declare function readRetirementInventory(path: string): InventoryRead;
|
|
420
|
+
/** The shipped schema document's basename, in the package's `schemas/`. */
|
|
421
|
+
export declare const CUTOVER_STATE_SCHEMA_FILENAME = "cutover-state.schema.json";
|
|
422
|
+
/**
|
|
423
|
+
* Absolute path of the shipped cutover-state schema.
|
|
424
|
+
*
|
|
425
|
+
* `packageRoot()` walks UP and TESTS rather than counting `..`, because the
|
|
426
|
+
* depth differs between running from `src/` and running from `dist/src/`
|
|
427
|
+
* (src/modes.ts:39 states the same reason for the same walk).
|
|
428
|
+
*/
|
|
429
|
+
export declare function cutoverStateSchemaPath(): string;
|
|
430
|
+
/** The shipped schema document, read once. */
|
|
431
|
+
export declare function cutoverStateSchema(): SchemaDocument;
|
|
432
|
+
/**
|
|
433
|
+
* Validate a whole cutover document against the SHIPPED SCHEMA.
|
|
434
|
+
*
|
|
435
|
+
* WHY THIS IS NOT `validateCutoverDocument`, and the two are kept apart on
|
|
436
|
+
* purpose. That function is this module's READ guard and answers "can this
|
|
437
|
+
* file be interpreted". This one is the WRITE guard and answers "is this
|
|
438
|
+
* document one the package is prepared to ship", and criterion 4 says the
|
|
439
|
+
* required-field rule is validated BY THE SCHEMA rather than by a command.
|
|
440
|
+
* A check implemented only in a command is one the schema cannot enforce for
|
|
441
|
+
* a later writer, and a later writer is precisely who rollback depends on:
|
|
442
|
+
* M4-P26's `targetFor` READS the recorded `restoreTo` (src/cutover.ts:352)
|
|
443
|
+
* rather than remembering an intent, so a record written without one is a
|
|
444
|
+
* switch that can never be rolled back.
|
|
445
|
+
*/
|
|
446
|
+
export declare function cutoverSchemaDiagnostics(document: unknown): string[];
|
|
447
|
+
/**
|
|
448
|
+
* The captured pre-freeze state, relative to the REPOSITORY root. It carries
|
|
449
|
+
* the branch-protection ruleset as it was before the first flip, so the
|
|
450
|
+
* restore request M4-P26 generates has an input rather than a memory. T-025
|
|
451
|
+
* is why: the one step that cannot be rehearsed is the one that failed, and
|
|
452
|
+
* its INPUT can be captured in advance even when its EXECUTION cannot.
|
|
453
|
+
*/
|
|
454
|
+
export declare const PRE_FREEZE_RULESET_PATH = "delivery/plan/cutover/pre-freeze-ruleset.json";
|
|
455
|
+
export type PreFreezeVerdict =
|
|
456
|
+
/** No switch reads `kernel`, so nothing has been frozen and nothing is owed. */
|
|
457
|
+
{
|
|
458
|
+
kind: "not-required";
|
|
459
|
+
} | {
|
|
460
|
+
kind: "satisfied";
|
|
461
|
+
capturedAt: string;
|
|
462
|
+
} | {
|
|
463
|
+
kind: "refused";
|
|
464
|
+
arm: "absent" | "stale" | "unreadable";
|
|
465
|
+
reason: string;
|
|
466
|
+
};
|
|
467
|
+
/**
|
|
468
|
+
* THE CAPTURE TIME IS READ FROM THE DOCUMENT, NOT FROM ITS MTIME, and that is
|
|
469
|
+
* a correction rather than a preference.
|
|
470
|
+
*
|
|
471
|
+
* The criterion says the capture must not be OLDER than the most recent switch
|
|
472
|
+
* write. An mtime does not survive the journey: `git clone` and `git checkout`
|
|
473
|
+
* set every working-tree mtime to the moment of the checkout, so a capture
|
|
474
|
+
* taken weeks before a flip reads as newer than the flip on any fresh clone,
|
|
475
|
+
* and the guard reports satisfied on exactly the machine a reviewer uses.
|
|
476
|
+
* Measured, and the measurement is in the work history. So the document
|
|
477
|
+
* records `captured-at` and the comparison is content to content.
|
|
478
|
+
*
|
|
479
|
+
* FAIL CLOSED ON AN UNREADABLE CAPTURE. A document with no `captured-at`, or
|
|
480
|
+
* one that does not parse as an instant, is `unreadable` and refuses. Treating
|
|
481
|
+
* it as satisfied would make a malformed capture indistinguishable from a good
|
|
482
|
+
* one, which is the guard that cannot go red.
|
|
483
|
+
*/
|
|
484
|
+
export declare function preFreezeGuard(repoRoot: string, state: CutoverState): PreFreezeVerdict;
|
|
485
|
+
export type BranchCount = {
|
|
486
|
+
kind: "counted";
|
|
487
|
+
count: number;
|
|
488
|
+
branches: string[];
|
|
489
|
+
} | {
|
|
490
|
+
kind: "unexaminable";
|
|
491
|
+
reason: string;
|
|
492
|
+
};
|
|
493
|
+
/**
|
|
494
|
+
* Count the PUSHED, UNMERGED branches, and print them nowhere near the drain
|
|
495
|
+
* verdict.
|
|
496
|
+
*
|
|
497
|
+
* THIS NUMBER DOES NOT FEED THE PREDICATE, AND THAT IS THE DECISION (M4-D-15,
|
|
498
|
+
* delivery/plan/kernel-plan-m4.md:3279). Remote ref deletion is refused in
|
|
499
|
+
* this container, and `git push --dry-run` does not probe push authorization
|
|
500
|
+
* at all, in either direction, for any ref namespace
|
|
501
|
+
* (delivery/verification/m4-prototype-probes.md:165). A drain defined as "no
|
|
502
|
+
* unmerged branches" therefore waits forever on an owner action that has no
|
|
503
|
+
* local pre-check, which is a predicate that can never read clean. It is
|
|
504
|
+
* reported because an operator wants to see it, and it is reported on its own
|
|
505
|
+
* line so that no reader can mistake it for a reason the cutover is blocked.
|
|
506
|
+
*
|
|
507
|
+
* `unexaminable` is a THIRD answer for the same reason it is one in
|
|
508
|
+
* `inFlightItems`: a repository with no `origin/<default>` cannot be asked
|
|
509
|
+
* this question, and printing `0` there would be an answer nobody measured.
|
|
510
|
+
*/
|
|
511
|
+
export declare function unmergedBranchCount(repoRoot: string, upstream?: string): BranchCount;
|
|
512
|
+
/** The M4-P23 inventory, relative to the REPOSITORY root. */
|
|
513
|
+
export declare const RETIREMENT_INVENTORY_PATH = "delivery/plan/cutover/retirement-inventory.json";
|
|
514
|
+
/**
|
|
515
|
+
* The first tokens a retirement row's command may start a segment with.
|
|
516
|
+
*
|
|
517
|
+
* MIRRORED FROM `scripts/check-retirement-inventory.mjs`, DELIBERATELY, AND
|
|
518
|
+
* THE DRIFT IS ASSERTED BY A TEST rather than by this comment. The script is
|
|
519
|
+
* this project's own predicate and is KEPT rather than shipped (DR-0029), so
|
|
520
|
+
* the kernel cannot import it; but the two lists screening the same rows must
|
|
521
|
+
* not diverge, so `test/cutover.test.ts` reads the script's
|
|
522
|
+
* `ALLOWED_FIRST_TOKENS` and requires this set to be no wider.
|
|
523
|
+
*
|
|
524
|
+
* WHAT THE LIST BUYS AND WHAT IT DOES NOT. Every tool on it is one with no
|
|
525
|
+
* option for writing a file, so a row cannot modify the tree this command is
|
|
526
|
+
* auditing. It is a TOOL allowlist, not a sandbox: the child still runs with
|
|
527
|
+
* this process's privileges and can read anything this process can read.
|
|
528
|
+
*/
|
|
529
|
+
export declare const RETIREMENT_COMMAND_TOKENS: ReadonlySet<string>;
|
|
530
|
+
/**
|
|
531
|
+
* Screen one inventory command. The rows are DATA FROM A FILE, so the command
|
|
532
|
+
* is screened before anything spawns it, and the check is on the EXECUTABLE
|
|
533
|
+
* POSITION of every segment rather than on the whole string: a whole-string
|
|
534
|
+
* denylist refuses `grep -c 'npm ci' gate-registry.yaml`, which runs no npm at
|
|
535
|
+
* all and merely searches for those characters.
|
|
536
|
+
*/
|
|
537
|
+
export declare function screenRetirementCommand(command: unknown): string[];
|
|
538
|
+
export type RowAdaptation = {
|
|
539
|
+
kind: "row";
|
|
540
|
+
row: RetirementRow;
|
|
541
|
+
} | {
|
|
542
|
+
kind: "refused";
|
|
543
|
+
result: PortResult;
|
|
544
|
+
};
|
|
545
|
+
/**
|
|
546
|
+
* Turn one row of the SHIPPED inventory into the shape `evaluatePortRow`
|
|
547
|
+
* takes.
|
|
548
|
+
*
|
|
549
|
+
* THIS ADAPTER EXISTS BECAUSE THE TWO SHAPES REALLY ARE DIFFERENT, and the
|
|
550
|
+
* difference is silent in the dangerous direction. `RetirementRow` declares
|
|
551
|
+
* `negativeWitness` as an argv ARRAY (src/cutover.ts:1128), and the M4-P23
|
|
552
|
+
* inventory writes `negative-witness` as an OBJECT carrying a SHELL STRING
|
|
553
|
+
* (delivery/plan/cutover/retirement-inventory.json:1). Handing the shipped
|
|
554
|
+
* document straight to `evaluatePortRow` therefore returns `unported` with
|
|
555
|
+
* "PORT row carries no negative-witness command" for every row in it: a
|
|
556
|
+
* verdict that looks like a finding about the kernel and is a finding about a
|
|
557
|
+
* key spelling. The adapter is named, tested and refuses rather than
|
|
558
|
+
* defaulting, so the mismatch cannot come back as a silent all-red.
|
|
559
|
+
*/
|
|
560
|
+
export declare function retirementRowFromDocument(raw: unknown): RowAdaptation;
|
|
561
|
+
export interface RetirementReport {
|
|
562
|
+
results: PortResult[];
|
|
563
|
+
unported: number;
|
|
564
|
+
}
|
|
565
|
+
export type RetirementRead = {
|
|
566
|
+
kind: "read";
|
|
567
|
+
report: RetirementReport;
|
|
568
|
+
} | {
|
|
569
|
+
kind: "refused";
|
|
570
|
+
reason: string;
|
|
571
|
+
};
|
|
572
|
+
/**
|
|
573
|
+
* Evaluate every PORT row of the inventory.
|
|
574
|
+
*
|
|
575
|
+
* THE VACUOUS VERDICT IS THE ONE THIS GUARDS AGAINST. `ported` is not "the
|
|
576
|
+
* named kernel artifact exists"; a file can exist and say nothing. Both halves
|
|
577
|
+
* are required and the second is the one that matters: the row's negative
|
|
578
|
+
* witness was RED against a subject that does not carry the rule, so a witness
|
|
579
|
+
* exiting 0 means the probe discriminates nothing and the row is `unported`.
|
|
580
|
+
* That derivation is M4-P26's `evaluatePortRow` and is REUSED here rather than
|
|
581
|
+
* reimplemented; this function supplies the reading, the adaptation and the
|
|
582
|
+
* screen.
|
|
583
|
+
*/
|
|
584
|
+
export declare function evaluateRetirementInventory(inventoryPath: string, repoRoot: string): RetirementRead;
|