@medicine-wheel/infra 0.5.7 → 0.5.8

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/README.md CHANGED
@@ -14,7 +14,7 @@ does not have to change to make that true.
14
14
  ## Install
15
15
 
16
16
  ```bash
17
- npm install @medicine-wheel/infra
17
+ npm install @medicine-wheel/infra@^0.5.8
18
18
  ```
19
19
 
20
20
  Depends only on `@medicine-wheel/ontology-core` and `zod`.
@@ -110,26 +110,103 @@ if (!parsed.success) { /* the emitter misread systemd — do not reconcile on th
110
110
  Port numbers are integers in 1–65535. A misparsed `0` is rejected here rather
111
111
  than silently never colliding with anything downstream.
112
112
 
113
- ## Scope of `0.1.0`
113
+ ## `Precondition` — where the machine fact and the human do not merge
114
114
 
115
- Ships S1, S3, S5 only — facets, `detectPortConflicts`, `MetisHold`, schemas.
116
- Deliberately **not** here:
115
+ ```ts
116
+ import { preconditionGuard, readyService, lingerFact } from '@medicine-wheel/infra';
117
+
118
+ const p = {
119
+ id: 'pre:gmusic-linger',
120
+ gates: 'node:knowledge:assembly-mux',
121
+ fact: lingerFact('node:human:gmusic', 'enabled', '2026-08-05T12:00:00Z'),
122
+ consent: { consentId: 'consent:root-step:gmusic', state: 'withdrawn', readAt: '2026-08-05T12:00:00Z' },
123
+ };
124
+
125
+ preconditionGuard(p).verdict; // 'unauthorized' — NOT 'satisfied', NOT 'unsatisfied'
126
+ ```
127
+
128
+ `linger` is a machine fact: a checkbox with no authority and no ability to
129
+ withdraw. The human authorization to run the root step that set it is a
130
+ `ConsentRecord` in `@medicine-wheel/consent-lifecycle`. A `Precondition`
131
+ references both **by id**, and `infra` never imports that package — a package
132
+ that can construct a consent record can fabricate one.
133
+
134
+ Four verdicts, and the distinctions are the whole point:
117
135
 
118
- - **`0.2.0`** (S4) — `Precondition`, `preconditionGuard`, `readyService`. Where
119
- the machine fact `linger` and the human, withdrawable `ConsentRecord` that
120
- authorized the root step reference each other by id without either collapsing
121
- into the other.
122
- - **`0.3.0`** (S6) — level-triggered `reconcile()` + `ObservedState` and the four
123
- drift states, which keep `satisfied` from becoming a stale snapshot.
124
- - **`ontology-core@0.6.0`** (S2) — `part-of` / `ordered-after` / `binds-port` in
125
- the governed `KINSHIP_EDGE_TYPES` registry. Until then a dependency edge is an
126
- ordinary `Relation`; `infra` does not fork the vocabulary.
136
+ | verdict | means |
137
+ |---|---|
138
+ | `satisfied` | every declared half was read and holds |
139
+ | `unsatisfied` | a machine fact was read and does not match |
140
+ | `unauthorized` | the machine half holds and consent does not authorize |
141
+ | `unknown` | a declared half has **not been read**, which is not the same as false |
142
+
143
+ `unauthorized` is separate from `unsatisfied` on purpose. Collapse them and a
144
+ withdrawn consent looks like a technical failure an operator can fix by
145
+ restarting something. `unknown` is separate from both because a guard that
146
+ reports "blocked" when nobody has looked yet is a guard operators learn to
147
+ ignore.
148
+
149
+ `readyService` reports `declared: 0` out loud rather than letting an
150
+ unconfigured service look checked, and carries every `MetisHold` from the
151
+ blocking preconditions forward — the reason a gate is stuck is exactly where
152
+ "you have to restart it twice" lives.
153
+
154
+ ## `reconcile` — level-triggered, so `satisfied` cannot age
155
+
156
+ ```ts
157
+ const result = reconcile(declaredServices, {
158
+ observedBy: 'node:land:gaia',
159
+ observedAt: '2026-08-05T12:00:00Z',
160
+ services: readLiveFromSystemd(),
161
+ }, { now: '2026-08-05T12:05:00Z' });
162
+
163
+ result.summary; // { converged: 4, drifted: 1, unrealized: 0, undeclared: 2 }
164
+ result.observationAgeMs; // 300000 — refuse to act on a reading that is too old
165
+ ```
127
166
 
128
- Each step makes the next honest. None is a backlog item for this one.
167
+ The outage this answers (jgwill/gaia#74) was not caused by a missing fact. It was
168
+ caused by a fact that *had been* true. So `reconcile` remembers no transitions:
169
+ it compares the current declared level against the current observed level, every
170
+ time. There is no "converged a minute ago" to go stale.
171
+
172
+ | drift state | means |
173
+ |---|---|
174
+ | `converged` | both sides agree on every compared field |
175
+ | `drifted` | both exist, they disagree — the differences are named |
176
+ | `unrealized` | declared, not observed — written down, not running |
177
+ | `undeclared` | **observed, not declared** — running and nobody wrote it down |
178
+
179
+ `undeclared` carries the most information: it finds the service somebody started
180
+ by hand at 2am, which is the service holding the port the next tenant is about to
181
+ be given. The §S3 port traversal is folded in here, because this is the one place
182
+ declared and observed are both in hand.
183
+
184
+ **Métis is never a difference.** Two sides holding different tacit notes is not
185
+ drift — it is two people knowing different things. A reconciler that reported it
186
+ would be asking an operator to delete what they know to make a table go green.
187
+
188
+ ## Scope
189
+
190
+ Every specification ships: S1 facets, S3 `detectPortConflicts`, S4
191
+ `Precondition`, S5 `MetisHold`, S6 `reconcile`. S2 — the governed `part-of`,
192
+ `ordered-after` and `binds-port` edges — landed in
193
+ `ontology-core`'s `KINSHIP_EDGE_TYPES`, which is where edge vocabulary belongs.
194
+ `infra` never forked it.
195
+
196
+ S4 and S6 first ship in **0.5.8**. `0.5.7` and earlier contain S1, S3 and S5
197
+ only — a package that installs `^0.5.7` may resolve a dist with no
198
+ `preconditionGuard` and no `reconcile`.
199
+
200
+ The package stays types plus pure functions: zero I/O, zero persistence, zero
201
+ clock. `reconcile` takes `now` as an argument for exactly that reason.
202
+ Persistence belongs to `@medicine-wheel/mcp`, which is where these are reachable
203
+ from — `register_host`, `register_tenant`, `register_service`,
204
+ `detect_port_conflicts`, `hold_metis`, `list_infra_topology`,
205
+ `reconcile_infra_state`, and `mw_enforce_gate` for the precondition gate.
129
206
 
130
207
  ## Grounding
131
208
 
132
- - Specification: `/opt/eury/rispecs/foundations/02-specifications.md` §S1, §S3, §S5
209
+ - Specification: `/opt/eury/rispecs/foundations/02-specifications.md` §S1–§S6
133
210
  - Argument: `/opt/eury/foundations/relational-infrastructure/synthesis.md`
134
211
  - The collision this exists to catch: `jgwill/gaia#75`
135
212
  - The outage that motivated observed state: `jgwill/gaia#74`
package/dist/index.d.ts CHANGED
@@ -10,15 +10,25 @@
10
10
  * package: the closed `NodeType` union is reused, never widened, and facets are
11
11
  * keyed by node id rather than re-declaring nodes.
12
12
  *
13
- * Scope of `0.1.0` — S1, S3, S5 only. `Precondition` / `preconditionGuard` /
14
- * `readyService` are `0.2.0` (S4); `reconcile()` / `ObservedState` are `0.3.0`
15
- * (S6). Neither is shipped here.
13
+ * Every specification is now shipped: S1 (facets), S3 (`detectPortConflicts`),
14
+ * S4 (`Precondition` / `preconditionGuard` / `readyService`), S5 (`MetisHold`),
15
+ * S6 (`ObservedState` / `reconcile`). S2 — the governed `part-of`,
16
+ * `ordered-after` and `binds-port` edges — landed in `ontology-core`, which is
17
+ * where edge vocabulary belongs; `infra` never forked it.
18
+ *
19
+ * The package remains types plus pure functions. Zero I/O, zero persistence,
20
+ * zero clock: `reconcile` takes `now` as an argument for exactly that reason.
16
21
  *
17
22
  * @packageDocumentation
18
23
  */
19
24
  export type { NodeId, MetisHold, LingerState, Reachability, PortProto, ServiceScope, HostFacet, TenantFacet, ServiceFacet, PortBinding, PortConflict, FacetKind, } from './types';
20
25
  export { DEFAULT_PORT_PROTO, FACET_NODE_TYPES } from './types';
21
26
  export { MetisHoldSchema, LingerStateSchema, ReachabilitySchema, PortProtoSchema, ServiceScopeSchema, PortNumberSchema, PortBindingSchema, PortConflictSchema, HostFacetSchema, TenantFacetSchema, ServiceFacetSchema, } from './schemas';
22
- export type { ValidatedMetisHold, ValidatedPortBinding, ValidatedPortConflict, ValidatedHostFacet, ValidatedTenantFacet, ValidatedServiceFacet, } from './schemas';
27
+ export { PreconditionKindSchema, MachineFactSchema, ConsentReferenceSchema, PreconditionSchema, PreconditionVerdictSchema, DriftStateSchema, ObservedStateSchema, } from './schemas';
28
+ export type { ValidatedMetisHold, ValidatedPortBinding, ValidatedPortConflict, ValidatedHostFacet, ValidatedTenantFacet, ValidatedServiceFacet, ValidatedMachineFact, ValidatedConsentReference, ValidatedPrecondition, ValidatedObservedState, } from './schemas';
23
29
  export { detectPortConflicts } from './ports';
30
+ export { preconditionGuard, readyService, lingerFact, AUTHORIZING_CONSENT_STATES, CONSENT_REQUIRING_KINDS, } from './preconditions';
31
+ export type { PreconditionKind, MachineFact, ConsentReference, Precondition, PreconditionVerdict, PreconditionResult, ServiceReadiness, } from './preconditions';
32
+ export { reconcile, DRIFT_STATES } from './reconcile';
33
+ export type { DriftState, ObservedState, FieldDifference, DriftRow, ReconcileResult, } from './reconcile';
24
34
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,YAAY,EACV,MAAM,EACN,SAAS,EACT,WAAW,EACX,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAEnB,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,YAAY,EACV,MAAM,EACN,SAAS,EACT,WAAW,EACX,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,EACzB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAEnB,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,EACL,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,0BAA0B,EAC1B,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEtD,YAAY,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,QAAQ,EACR,eAAe,GAChB,MAAM,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -11,14 +11,19 @@
11
11
  * package: the closed `NodeType` union is reused, never widened, and facets are
12
12
  * keyed by node id rather than re-declaring nodes.
13
13
  *
14
- * Scope of `0.1.0` — S1, S3, S5 only. `Precondition` / `preconditionGuard` /
15
- * `readyService` are `0.2.0` (S4); `reconcile()` / `ObservedState` are `0.3.0`
16
- * (S6). Neither is shipped here.
14
+ * Every specification is now shipped: S1 (facets), S3 (`detectPortConflicts`),
15
+ * S4 (`Precondition` / `preconditionGuard` / `readyService`), S5 (`MetisHold`),
16
+ * S6 (`ObservedState` / `reconcile`). S2 — the governed `part-of`,
17
+ * `ordered-after` and `binds-port` edges — landed in `ontology-core`, which is
18
+ * where edge vocabulary belongs; `infra` never forked it.
19
+ *
20
+ * The package remains types plus pure functions. Zero I/O, zero persistence,
21
+ * zero clock: `reconcile` takes `now` as an argument for exactly that reason.
17
22
  *
18
23
  * @packageDocumentation
19
24
  */
20
25
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.detectPortConflicts = exports.ServiceFacetSchema = exports.TenantFacetSchema = exports.HostFacetSchema = exports.PortConflictSchema = exports.PortBindingSchema = exports.PortNumberSchema = exports.ServiceScopeSchema = exports.PortProtoSchema = exports.ReachabilitySchema = exports.LingerStateSchema = exports.MetisHoldSchema = exports.FACET_NODE_TYPES = exports.DEFAULT_PORT_PROTO = void 0;
26
+ exports.DRIFT_STATES = exports.reconcile = exports.CONSENT_REQUIRING_KINDS = exports.AUTHORIZING_CONSENT_STATES = exports.lingerFact = exports.readyService = exports.preconditionGuard = exports.detectPortConflicts = exports.ObservedStateSchema = exports.DriftStateSchema = exports.PreconditionVerdictSchema = exports.PreconditionSchema = exports.ConsentReferenceSchema = exports.MachineFactSchema = exports.PreconditionKindSchema = exports.ServiceFacetSchema = exports.TenantFacetSchema = exports.HostFacetSchema = exports.PortConflictSchema = exports.PortBindingSchema = exports.PortNumberSchema = exports.ServiceScopeSchema = exports.PortProtoSchema = exports.ReachabilitySchema = exports.LingerStateSchema = exports.MetisHoldSchema = exports.FACET_NODE_TYPES = exports.DEFAULT_PORT_PROTO = void 0;
22
27
  var types_1 = require("./types");
23
28
  Object.defineProperty(exports, "DEFAULT_PORT_PROTO", { enumerable: true, get: function () { return types_1.DEFAULT_PORT_PROTO; } });
24
29
  Object.defineProperty(exports, "FACET_NODE_TYPES", { enumerable: true, get: function () { return types_1.FACET_NODE_TYPES; } });
@@ -35,7 +40,26 @@ Object.defineProperty(exports, "PortConflictSchema", { enumerable: true, get: fu
35
40
  Object.defineProperty(exports, "HostFacetSchema", { enumerable: true, get: function () { return schemas_1.HostFacetSchema; } });
36
41
  Object.defineProperty(exports, "TenantFacetSchema", { enumerable: true, get: function () { return schemas_1.TenantFacetSchema; } });
37
42
  Object.defineProperty(exports, "ServiceFacetSchema", { enumerable: true, get: function () { return schemas_1.ServiceFacetSchema; } });
38
- // ── Port-conflict detection ─────────────────────────────────────────────────
43
+ var schemas_2 = require("./schemas");
44
+ Object.defineProperty(exports, "PreconditionKindSchema", { enumerable: true, get: function () { return schemas_2.PreconditionKindSchema; } });
45
+ Object.defineProperty(exports, "MachineFactSchema", { enumerable: true, get: function () { return schemas_2.MachineFactSchema; } });
46
+ Object.defineProperty(exports, "ConsentReferenceSchema", { enumerable: true, get: function () { return schemas_2.ConsentReferenceSchema; } });
47
+ Object.defineProperty(exports, "PreconditionSchema", { enumerable: true, get: function () { return schemas_2.PreconditionSchema; } });
48
+ Object.defineProperty(exports, "PreconditionVerdictSchema", { enumerable: true, get: function () { return schemas_2.PreconditionVerdictSchema; } });
49
+ Object.defineProperty(exports, "DriftStateSchema", { enumerable: true, get: function () { return schemas_2.DriftStateSchema; } });
50
+ Object.defineProperty(exports, "ObservedStateSchema", { enumerable: true, get: function () { return schemas_2.ObservedStateSchema; } });
51
+ // ── Port-conflict detection (S3) ────────────────────────────────────────────
39
52
  var ports_1 = require("./ports");
40
53
  Object.defineProperty(exports, "detectPortConflicts", { enumerable: true, get: function () { return ports_1.detectPortConflicts; } });
54
+ // ── Preconditions (S4) ──────────────────────────────────────────────────────
55
+ var preconditions_1 = require("./preconditions");
56
+ Object.defineProperty(exports, "preconditionGuard", { enumerable: true, get: function () { return preconditions_1.preconditionGuard; } });
57
+ Object.defineProperty(exports, "readyService", { enumerable: true, get: function () { return preconditions_1.readyService; } });
58
+ Object.defineProperty(exports, "lingerFact", { enumerable: true, get: function () { return preconditions_1.lingerFact; } });
59
+ Object.defineProperty(exports, "AUTHORIZING_CONSENT_STATES", { enumerable: true, get: function () { return preconditions_1.AUTHORIZING_CONSENT_STATES; } });
60
+ Object.defineProperty(exports, "CONSENT_REQUIRING_KINDS", { enumerable: true, get: function () { return preconditions_1.CONSENT_REQUIRING_KINDS; } });
61
+ // ── Reconciliation (S6) ─────────────────────────────────────────────────────
62
+ var reconcile_1 = require("./reconcile");
63
+ Object.defineProperty(exports, "reconcile", { enumerable: true, get: function () { return reconcile_1.reconcile; } });
64
+ Object.defineProperty(exports, "DRIFT_STATES", { enumerable: true, get: function () { return reconcile_1.DRIFT_STATES; } });
41
65
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAkBH,iCAA+D;AAAtD,2GAAA,kBAAkB,OAAA;AAAE,yGAAA,gBAAgB,OAAA;AAE7C,+EAA+E;AAC/E,qCAYmB;AAXjB,0GAAA,eAAe,OAAA;AACf,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,6GAAA,kBAAkB,OAAA;AAClB,2GAAA,gBAAgB,OAAA;AAChB,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAYpB,+EAA+E;AAC/E,iCAA8C;AAArC,4GAAA,mBAAmB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;;;AAkBH,iCAA+D;AAAtD,2GAAA,kBAAkB,OAAA;AAAE,yGAAA,gBAAgB,OAAA;AAE7C,+EAA+E;AAC/E,qCAYmB;AAXjB,0GAAA,eAAe,OAAA;AACf,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,6GAAA,kBAAkB,OAAA;AAClB,2GAAA,gBAAgB,OAAA;AAChB,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,4GAAA,iBAAiB,OAAA;AACjB,6GAAA,kBAAkB,OAAA;AAGpB,qCAQmB;AAPjB,iHAAA,sBAAsB,OAAA;AACtB,4GAAA,iBAAiB,OAAA;AACjB,iHAAA,sBAAsB,OAAA;AACtB,6GAAA,kBAAkB,OAAA;AAClB,oHAAA,yBAAyB,OAAA;AACzB,2GAAA,gBAAgB,OAAA;AAChB,8GAAA,mBAAmB,OAAA;AAgBrB,+EAA+E;AAC/E,iCAA8C;AAArC,4GAAA,mBAAmB,OAAA;AAE5B,+EAA+E;AAC/E,iDAMyB;AALvB,kHAAA,iBAAiB,OAAA;AACjB,6GAAA,YAAY,OAAA;AACZ,2GAAA,UAAU,OAAA;AACV,2HAAA,0BAA0B,OAAA;AAC1B,wHAAA,uBAAuB,OAAA;AAazB,+EAA+E;AAC/E,yCAAsD;AAA7C,sGAAA,SAAS,OAAA;AAAE,yGAAA,YAAY,OAAA"}
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Preconditions — what must hold before West runs.
3
+ *
4
+ * A service is West: the thing that executes. A port claim, a linger flag, a
5
+ * unit file on disk are South: claims that must hold *first*. This module makes
6
+ * that ordering checkable without letting either half stand in for the other.
7
+ *
8
+ * The load-bearing separation, and the reason this file exists at all:
9
+ *
10
+ * > `linger` is a machine fact — a checkbox with no authority and no ability to
11
+ * > withdraw. The human authorization to run the root step that sets it is a
12
+ * > `ConsentRecord` in `@medicine-wheel/consent-lifecycle`. A `Precondition`
13
+ * > references both **by id**. Neither collapses into the other.
14
+ *
15
+ * Collapsing them is the failure this guards against: once a granted consent is
16
+ * inferred from a machine fact, withdrawing consent stops meaning anything,
17
+ * because the flag is still set. So `unauthorized` is a verdict of its own — the
18
+ * machine is ready and the human has not said yes — and it never reads as
19
+ * `unsatisfied`.
20
+ *
21
+ * A pure function over plain data. No I/O, no ontology traversal, no clock.
22
+ *
23
+ * Spec: /opt/eury/rispecs/foundations/02-specifications.md §S4.
24
+ */
25
+ import type { LingerState, MetisHold, NodeId, ServiceFacet } from './types';
26
+ /**
27
+ * The kinds of machine fact a precondition can read.
28
+ *
29
+ * Every one of these is something a host can be asked and will answer without a
30
+ * person present. That is precisely what disqualifies them from carrying
31
+ * consent.
32
+ */
33
+ export type PreconditionKind =
34
+ /** the tenant's user units survive logout — `loginctl show-user <account>` */
35
+ 'linger'
36
+ /** the slot this service claims is not held by another service */
37
+ | 'port-free'
38
+ /** the unit file exists in the scope the facet declares */
39
+ | 'unit-present'
40
+ /** the directory the unit reads its environment from exists (jgwill/gaia#74) */
41
+ | 'working-directory';
42
+ /**
43
+ * A fact read off a machine, with its provenance attached.
44
+ *
45
+ * `observed` being absent means **never read**, which is not the same as read
46
+ * and false. A guard that treats the two alike will report a service blocked
47
+ * when nobody has looked yet, and the operator learns to ignore it.
48
+ */
49
+ export interface MachineFact {
50
+ kind: PreconditionKind;
51
+ /** the facet whose field carries this fact — host, tenant or service */
52
+ facetNodeId: NodeId;
53
+ /** what the fact must read for the precondition to hold */
54
+ expected: string;
55
+ /** what it actually read. Absent means unread. */
56
+ observed?: string;
57
+ /** ISO 8601 — when `observed` was read. Staleness is a fact, not an inference. */
58
+ observedAt?: string;
59
+ }
60
+ /**
61
+ * The consent states that authorize an action.
62
+ *
63
+ * Mirrors `ConsentState` in `@medicine-wheel/consent-lifecycle` and is exported
64
+ * as data so a caller can pass its own set rather than fork this file. `infra`
65
+ * deliberately does **not** import that package: a package that can construct a
66
+ * consent record can fabricate one, and this one must not be able to.
67
+ */
68
+ export declare const AUTHORIZING_CONSENT_STATES: readonly string[];
69
+ /**
70
+ * Fact kinds that cannot stand alone.
71
+ *
72
+ * `linger` is here because enabling it required running a root step, and that
73
+ * root step required a human to say yes. A precondition that reads the flag and
74
+ * references no `ConsentRecord` therefore opens the gate on the machine fact
75
+ * alone — which is the exact collapse this module's docstring forbids, arriving
76
+ * not through a wrong verdict but through an incomplete declaration.
77
+ *
78
+ * Such a precondition returns `unknown`, never `satisfied`: nothing authorized
79
+ * it, and nothing was checked. `unauthorized` would be wrong too — that verdict
80
+ * means consent was read and did not authorize, and here it was never named.
81
+ */
82
+ export declare const CONSENT_REQUIRING_KINDS: readonly PreconditionKind[];
83
+ /**
84
+ * A pointer at a `ConsentRecord`, never a copy of one.
85
+ *
86
+ * `state` is an opaque string on purpose. `infra` does not own consent semantics;
87
+ * it records what the lifecycle said and when it was asked.
88
+ */
89
+ export interface ConsentReference {
90
+ /** → `ConsentRecord.id` in `@medicine-wheel/consent-lifecycle` */
91
+ consentId: string;
92
+ /** the state as read at `readAt`. Absent means unread. */
93
+ state?: string;
94
+ /** ISO 8601 */
95
+ readAt?: string;
96
+ }
97
+ /**
98
+ * One condition that must hold before a service may be considered ready.
99
+ *
100
+ * A precondition may carry a machine fact, a consent reference, or both. Both is
101
+ * the interesting case and the one the whole module is shaped around: running
102
+ * the root step that enables linger needed a human to say yes, and that yes can
103
+ * be withdrawn later while the flag stays enabled.
104
+ */
105
+ export interface Precondition {
106
+ /** stable id for the precondition itself */
107
+ id: string;
108
+ /** the service whose readiness this gates → `ServiceFacet.nodeId` */
109
+ gates: NodeId;
110
+ /** one line an operator can read — 'gmusic linger must survive logout' */
111
+ description?: string;
112
+ /** the machine half */
113
+ fact?: MachineFact;
114
+ /** the human half, referenced by id and never embedded */
115
+ consent?: ConsentReference;
116
+ /** what a person knows about this precondition that the fields cannot hold */
117
+ metis?: MetisHold;
118
+ }
119
+ /**
120
+ * How a precondition reads right now.
121
+ *
122
+ * Four values, and the distinctions between them are the point:
123
+ *
124
+ * - `satisfied` — every half that was declared was read, and holds.
125
+ * - `unsatisfied` — a machine fact was read and does not match what is expected.
126
+ * - `unauthorized` — the machine half holds (or was not required) and consent
127
+ * does not authorize. Kept separate from `unsatisfied` so a
128
+ * withdrawn consent can never be mistaken for a technical
129
+ * failure, and so an operator cannot "fix" it by restarting
130
+ * something.
131
+ * - `unknown` — a declared half has not been read. Never read as either
132
+ * success or failure.
133
+ */
134
+ export type PreconditionVerdict = 'satisfied' | 'unsatisfied' | 'unauthorized' | 'unknown';
135
+ export interface PreconditionResult {
136
+ preconditionId: string;
137
+ gates: NodeId;
138
+ verdict: PreconditionVerdict;
139
+ /** one sentence naming what decided it */
140
+ reason: string;
141
+ /** echoed so a caller rendering a table does not have to re-join */
142
+ fact?: MachineFact;
143
+ consent?: ConsentReference;
144
+ metis?: MetisHold;
145
+ }
146
+ /**
147
+ * Read one precondition.
148
+ *
149
+ * Order of decision, and it matters:
150
+ *
151
+ * 1. A declared-but-unread half yields `unknown`. Nothing else is decided from
152
+ * an unread fact.
153
+ * 2. A read machine fact that does not match yields `unsatisfied`. The human
154
+ * half is not consulted — there is nothing yet to authorize.
155
+ * 3. A consent that does not authorize yields `unauthorized`, whatever the
156
+ * machine says.
157
+ * 4. A fact whose kind is in {@link CONSENT_REQUIRING_KINDS} and that names no
158
+ * consent yields `unknown`. The machine half holding is not the question.
159
+ * 5. A precondition declaring neither half yields `unknown`, not `satisfied`.
160
+ * An empty gate that reports success is how a gate stops being one.
161
+ */
162
+ export declare function preconditionGuard(precondition: Precondition, authorizingStates?: readonly string[]): PreconditionResult;
163
+ export interface ServiceReadiness {
164
+ /** → `ServiceFacet.nodeId` */
165
+ service: NodeId;
166
+ /** true only when every declared precondition is `satisfied` */
167
+ ready: boolean;
168
+ /** how many preconditions were declared for this service. Zero is a finding. */
169
+ declared: number;
170
+ /** every result, in the order the preconditions were given */
171
+ results: PreconditionResult[];
172
+ /** the subset that is not `satisfied` — what an operator has to act on */
173
+ blocking: PreconditionResult[];
174
+ /**
175
+ * Every `MetisHold` carried by a blocking precondition or by the service.
176
+ *
177
+ * Surfaced rather than summarised: the reason a gate is stuck is exactly where
178
+ * "you have to restart it twice" tends to live, and a readiness report that
179
+ * drops it has flattened the one thing the operator needed.
180
+ */
181
+ metis: MetisHold[];
182
+ /** one sentence for a caller that renders a single line */
183
+ reason: string;
184
+ }
185
+ /**
186
+ * Whether a service may be considered ready, given the preconditions that gate it.
187
+ *
188
+ * Preconditions gating other services are ignored rather than silently counted —
189
+ * the filter is on `gates`, so passing the whole set for a host is fine.
190
+ *
191
+ * A service with **no** declared preconditions is reported `ready: true` with
192
+ * `declared: 0`, and the reason says so. That is a vacuous truth and the caller
193
+ * is told it is one; hiding it would let an unconfigured service look checked.
194
+ */
195
+ export declare function readyService(service: ServiceFacet, preconditions: Precondition[], authorizingStates?: readonly string[]): ServiceReadiness;
196
+ /** Convenience: the linger fact for a tenant, shaped so the expected value is not retyped at every call site. */
197
+ export declare function lingerFact(tenantNodeId: NodeId, observed?: LingerState, observedAt?: string): MachineFact;
198
+ //# sourceMappingURL=preconditions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preconditions.d.ts","sourceRoot":"","sources":["../src/preconditions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ /**
3
+ * Preconditions — what must hold before West runs.
4
+ *
5
+ * A service is West: the thing that executes. A port claim, a linger flag, a
6
+ * unit file on disk are South: claims that must hold *first*. This module makes
7
+ * that ordering checkable without letting either half stand in for the other.
8
+ *
9
+ * The load-bearing separation, and the reason this file exists at all:
10
+ *
11
+ * > `linger` is a machine fact — a checkbox with no authority and no ability to
12
+ * > withdraw. The human authorization to run the root step that sets it is a
13
+ * > `ConsentRecord` in `@medicine-wheel/consent-lifecycle`. A `Precondition`
14
+ * > references both **by id**. Neither collapses into the other.
15
+ *
16
+ * Collapsing them is the failure this guards against: once a granted consent is
17
+ * inferred from a machine fact, withdrawing consent stops meaning anything,
18
+ * because the flag is still set. So `unauthorized` is a verdict of its own — the
19
+ * machine is ready and the human has not said yes — and it never reads as
20
+ * `unsatisfied`.
21
+ *
22
+ * A pure function over plain data. No I/O, no ontology traversal, no clock.
23
+ *
24
+ * Spec: /opt/eury/rispecs/foundations/02-specifications.md §S4.
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.CONSENT_REQUIRING_KINDS = exports.AUTHORIZING_CONSENT_STATES = void 0;
28
+ exports.preconditionGuard = preconditionGuard;
29
+ exports.readyService = readyService;
30
+ exports.lingerFact = lingerFact;
31
+ /**
32
+ * The consent states that authorize an action.
33
+ *
34
+ * Mirrors `ConsentState` in `@medicine-wheel/consent-lifecycle` and is exported
35
+ * as data so a caller can pass its own set rather than fork this file. `infra`
36
+ * deliberately does **not** import that package: a package that can construct a
37
+ * consent record can fabricate one, and this one must not be able to.
38
+ */
39
+ exports.AUTHORIZING_CONSENT_STATES = ['granted', 'active'];
40
+ /**
41
+ * Fact kinds that cannot stand alone.
42
+ *
43
+ * `linger` is here because enabling it required running a root step, and that
44
+ * root step required a human to say yes. A precondition that reads the flag and
45
+ * references no `ConsentRecord` therefore opens the gate on the machine fact
46
+ * alone — which is the exact collapse this module's docstring forbids, arriving
47
+ * not through a wrong verdict but through an incomplete declaration.
48
+ *
49
+ * Such a precondition returns `unknown`, never `satisfied`: nothing authorized
50
+ * it, and nothing was checked. `unauthorized` would be wrong too — that verdict
51
+ * means consent was read and did not authorize, and here it was never named.
52
+ */
53
+ exports.CONSENT_REQUIRING_KINDS = ['linger'];
54
+ /**
55
+ * Read one precondition.
56
+ *
57
+ * Order of decision, and it matters:
58
+ *
59
+ * 1. A declared-but-unread half yields `unknown`. Nothing else is decided from
60
+ * an unread fact.
61
+ * 2. A read machine fact that does not match yields `unsatisfied`. The human
62
+ * half is not consulted — there is nothing yet to authorize.
63
+ * 3. A consent that does not authorize yields `unauthorized`, whatever the
64
+ * machine says.
65
+ * 4. A fact whose kind is in {@link CONSENT_REQUIRING_KINDS} and that names no
66
+ * consent yields `unknown`. The machine half holding is not the question.
67
+ * 5. A precondition declaring neither half yields `unknown`, not `satisfied`.
68
+ * An empty gate that reports success is how a gate stops being one.
69
+ */
70
+ function preconditionGuard(precondition, authorizingStates = exports.AUTHORIZING_CONSENT_STATES) {
71
+ const base = {
72
+ preconditionId: precondition.id,
73
+ gates: precondition.gates,
74
+ fact: precondition.fact,
75
+ consent: precondition.consent,
76
+ metis: precondition.metis,
77
+ };
78
+ const { fact, consent } = precondition;
79
+ if (!fact && !consent) {
80
+ return {
81
+ ...base,
82
+ verdict: 'unknown',
83
+ reason: 'precondition declares neither a machine fact nor a consent reference — nothing was checked',
84
+ };
85
+ }
86
+ if (fact && fact.observed === undefined) {
87
+ return {
88
+ ...base,
89
+ verdict: 'unknown',
90
+ reason: `machine fact '${fact.kind}' on ${fact.facetNodeId} has not been read`,
91
+ };
92
+ }
93
+ if (fact && fact.observed !== fact.expected) {
94
+ return {
95
+ ...base,
96
+ verdict: 'unsatisfied',
97
+ reason: `machine fact '${fact.kind}' on ${fact.facetNodeId} read '${fact.observed}', expected '${fact.expected}'`,
98
+ };
99
+ }
100
+ if (fact && !consent && exports.CONSENT_REQUIRING_KINDS.includes(fact.kind)) {
101
+ return {
102
+ ...base,
103
+ verdict: 'unknown',
104
+ reason: `machine fact '${fact.kind}' holds, but this precondition names no ConsentRecord — ` +
105
+ `the root step that set it required a human to say yes, and no yes was referenced`,
106
+ };
107
+ }
108
+ if (consent && consent.state === undefined) {
109
+ return {
110
+ ...base,
111
+ verdict: 'unknown',
112
+ reason: `consent ${consent.consentId} has not been read from the lifecycle`,
113
+ };
114
+ }
115
+ if (consent && !authorizingStates.includes(consent.state)) {
116
+ return {
117
+ ...base,
118
+ verdict: 'unauthorized',
119
+ reason: `consent ${consent.consentId} is '${consent.state}' — the machine half may hold, but this is not authorization`,
120
+ };
121
+ }
122
+ return {
123
+ ...base,
124
+ verdict: 'satisfied',
125
+ reason: fact
126
+ ? `machine fact '${fact.kind}' reads '${fact.observed}'${consent ? ` and consent ${consent.consentId} is '${consent.state}'` : ''}`
127
+ : `consent ${consent?.consentId} is '${consent?.state}'`,
128
+ };
129
+ }
130
+ /**
131
+ * Whether a service may be considered ready, given the preconditions that gate it.
132
+ *
133
+ * Preconditions gating other services are ignored rather than silently counted —
134
+ * the filter is on `gates`, so passing the whole set for a host is fine.
135
+ *
136
+ * A service with **no** declared preconditions is reported `ready: true` with
137
+ * `declared: 0`, and the reason says so. That is a vacuous truth and the caller
138
+ * is told it is one; hiding it would let an unconfigured service look checked.
139
+ */
140
+ function readyService(service, preconditions, authorizingStates = exports.AUTHORIZING_CONSENT_STATES) {
141
+ const mine = preconditions.filter((p) => p.gates === service.nodeId);
142
+ const results = mine.map((p) => preconditionGuard(p, authorizingStates));
143
+ const blocking = results.filter((r) => r.verdict !== 'satisfied');
144
+ const metis = [];
145
+ if (service.metis)
146
+ metis.push(service.metis);
147
+ for (const r of blocking)
148
+ if (r.metis)
149
+ metis.push(r.metis);
150
+ const ready = blocking.length === 0;
151
+ let reason;
152
+ if (mine.length === 0) {
153
+ reason = `no preconditions are declared for ${service.unit} — ready is vacuously true, not verified`;
154
+ }
155
+ else if (ready) {
156
+ reason = `all ${mine.length} precondition(s) satisfied for ${service.unit}`;
157
+ }
158
+ else {
159
+ const counts = blocking.reduce((acc, r) => {
160
+ acc[r.verdict] = (acc[r.verdict] ?? 0) + 1;
161
+ return acc;
162
+ }, {});
163
+ const shape = Object.entries(counts)
164
+ .sort(([a], [b]) => a.localeCompare(b))
165
+ .map(([verdict, n]) => `${n} ${verdict}`)
166
+ .join(', ');
167
+ reason = `${service.unit} is blocked: ${shape}`;
168
+ }
169
+ return { service: service.nodeId, ready, declared: mine.length, results, blocking, metis, reason };
170
+ }
171
+ /** Convenience: the linger fact for a tenant, shaped so the expected value is not retyped at every call site. */
172
+ function lingerFact(tenantNodeId, observed, observedAt) {
173
+ return {
174
+ kind: 'linger',
175
+ facetNodeId: tenantNodeId,
176
+ expected: 'enabled',
177
+ observed,
178
+ observedAt,
179
+ };
180
+ }
181
+ //# sourceMappingURL=preconditions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preconditions.js","sourceRoot":"","sources":["../src/preconditions.ts"],"names":[],"mappings":"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"}