@h-rig/kernel-seed 0.0.6-alpha.135 → 0.0.6-alpha.137

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.
@@ -1,3 +1,4 @@
1
+ export * from "./journalConformance";
1
2
  export * from "./pluginAbi";
2
3
  export * from "./resolveCapability";
3
4
  export * from "./seed";
package/dist/src/index.js CHANGED
@@ -1,4 +1,33 @@
1
1
  // @bun
2
+ // packages/kernel-seed/src/journalConformance.ts
3
+ class JournalConformanceError extends Error {
4
+ name = "JournalConformanceError";
5
+ }
6
+ var CONFORMANCE_RUN_ID = "rig.run.__journal-conformance__";
7
+ function sampleResolvedPipeline() {
8
+ return {
9
+ order: ["validate", "merge-gate"],
10
+ record: [
11
+ { stageId: "validate", contributedBy: "default", isProtected: false },
12
+ { stageId: "merge-gate", contributedBy: "default", isProtected: false }
13
+ ],
14
+ cycles: [],
15
+ grantUses: [],
16
+ resolvedAt: "1970-01-01T00:00:00.000Z"
17
+ };
18
+ }
19
+ async function assertJournalConformance(journal, runId = CONFORMANCE_RUN_ID) {
20
+ const pipeline = sampleResolvedPipeline();
21
+ await journal.recordPipeline(runId, pipeline);
22
+ const events = await journal.read(runId);
23
+ if (!Array.isArray(events) || events.length === 0) {
24
+ throw new JournalConformanceError("journal.read returned no records after recordPipeline \u2014 provider does not durably record (append-and-record contract violated)");
25
+ }
26
+ const serialized = JSON.stringify(events);
27
+ if (!serialized.includes("merge-gate") || !serialized.includes("validate")) {
28
+ throw new JournalConformanceError("journal.read did not surface the recorded resolved-pipeline \u2014 provider does not expose the append-and-record contract");
29
+ }
30
+ }
2
31
  // packages/kernel-seed/src/pluginAbi.ts
3
32
  function loadedPluginId(plugin) {
4
33
  return plugin.meta.id;
@@ -114,6 +143,42 @@ function objectAt(value, key) {
114
143
  function hasFunction(value, key) {
115
144
  return value !== null && typeof value[key] === "function";
116
145
  }
146
+ function assertSatisfiesStageRunnerInterface(value) {
147
+ if (!isRecord(value) || typeof value.resolve !== "function" || typeof value.runPipeline !== "function") {
148
+ throw new BootIncoherent("stage-runner provider must expose resolve and runPipeline");
149
+ }
150
+ }
151
+ function assertSatisfiesJournalInterface(value) {
152
+ if (!isRecord(value) || typeof value.append !== "function" || typeof value.recordPipeline !== "function" || typeof value.read !== "function") {
153
+ throw new BootIncoherent("journal provider must expose append, recordPipeline, and read");
154
+ }
155
+ }
156
+ function assertSatisfiesLoaderPolicyInterface(value) {
157
+ if (!isRecord(value) || typeof value.resolveCapability !== "function") {
158
+ throw new BootIncoherent("loader-policy provider must expose resolveCapability");
159
+ }
160
+ }
161
+ function assertSatisfiesTransportInterface(value) {
162
+ if (!isRecord(value) || typeof value.dispatch !== "function") {
163
+ throw new BootIncoherent("transport provider must expose dispatch");
164
+ }
165
+ }
166
+ function resolveRequiredCapability(plugins, capability, config, assertInterface) {
167
+ const resolution = resolveCapability(plugins, capability, config);
168
+ if (!resolution) {
169
+ throw new BootIncoherent(`no ${capability} provider resolved`);
170
+ }
171
+ assertInterface(resolution.value);
172
+ return resolution;
173
+ }
174
+ function assertSatisfiesKernelStartProvider(value) {
175
+ if (!isRecord(value)) {
176
+ throw new BootIncoherent("kernel provider did not return an object");
177
+ }
178
+ if (typeof value.start !== "function") {
179
+ throw new BootIncoherent("kernel provider must expose start");
180
+ }
181
+ }
117
182
  function assertSatisfiesKernelInterface(value) {
118
183
  if (!isRecord(value)) {
119
184
  throw new BootIncoherent("kernel provider did not return an object");
@@ -146,12 +211,41 @@ async function loadPlugins(config) {
146
211
  }
147
212
  async function boot(config) {
148
213
  const plugins = await loadPlugins(config);
149
- const resolution = resolveCapability(plugins, "kernel", config);
150
- if (!resolution) {
151
- throw new BootIncoherent("no kernel provider resolved");
214
+ const kernelResolution = resolveRequiredCapability(plugins, "kernel", config, assertSatisfiesKernelStartProvider);
215
+ const stageRunnerResolution = resolveRequiredCapability(plugins, "stage-runner", config, assertSatisfiesStageRunnerInterface);
216
+ const journalResolution = resolveRequiredCapability(plugins, "journal", config, assertSatisfiesJournalInterface);
217
+ const loaderPolicyResolution = resolveRequiredCapability(plugins, "loader-policy", config, assertSatisfiesLoaderPolicyInterface);
218
+ const transportResolution = resolveRequiredCapability(plugins, "transport", config, assertSatisfiesTransportInterface);
219
+ const kernel = kernelResolution.value;
220
+ const stageRunner = stageRunnerResolution.value;
221
+ const journal = journalResolution.value;
222
+ const loaderPolicy = loaderPolicyResolution.value;
223
+ const transport = transportResolution.value;
224
+ const composed = {
225
+ ...kernel,
226
+ stageRunner,
227
+ journal,
228
+ loaderPolicy,
229
+ transport,
230
+ start(loadedPlugins) {
231
+ return kernel.start.call(composed, loadedPlugins);
232
+ }
233
+ };
234
+ assertSatisfiesKernelInterface(composed);
235
+ if (config.verifyJournalConformance === true) {
236
+ await assertJournalConformance(composed.journal);
152
237
  }
153
- assertSatisfiesKernelInterface(resolution.value);
154
- return { kernel: resolution.value, plugins };
238
+ return {
239
+ kernel: composed,
240
+ plugins,
241
+ capabilityProviderIds: {
242
+ kernel: kernelResolution.plugin.meta.id,
243
+ "stage-runner": stageRunnerResolution.plugin.meta.id,
244
+ journal: journalResolution.plugin.meta.id,
245
+ "loader-policy": loaderPolicyResolution.plugin.meta.id,
246
+ transport: transportResolution.plugin.meta.id
247
+ }
248
+ };
155
249
  }
156
250
  export {
157
251
  resolveCapability,
@@ -159,6 +253,8 @@ export {
159
253
  declaresReplacement,
160
254
  boot,
161
255
  assertSatisfiesKernelInterface,
256
+ assertJournalConformance,
257
+ JournalConformanceError,
162
258
  CapabilityProviderMissingError,
163
259
  BootIncoherent,
164
260
  AmbiguousCapabilityError
@@ -0,0 +1,22 @@
1
+ import type { JournalCapability } from "@rig/contracts";
2
+ /**
3
+ * Raised when a `journal` capability provider passes the *structural* check
4
+ * (append/recordPipeline/read present) but does not actually durably record —
5
+ * e.g. a no-op stub that would silently erase the audit trail / safety backstop.
6
+ */
7
+ export declare class JournalConformanceError extends Error {
8
+ readonly name = "JournalConformanceError";
9
+ }
10
+ /**
11
+ * Behavioral journal-append conformance — the fixed contract every `journal`
12
+ * provider must satisfy (see the Journal-Append Invariant). The storage backend
13
+ * is swappable; the guarantee that the resolved-pipeline record is durably
14
+ * appended and exposed for projection is NOT. A structurally-complete no-op
15
+ * stub (all three methods present, recording nothing) fails this, which is the
16
+ * whole point: observability is the safety backstop, so a journal that does not
17
+ * record must be rejected — not merely accepted because it has the right shape.
18
+ *
19
+ * Uses a clearly-namespaced probe runId so the record is identifiable and
20
+ * harmless to real-run projections (which filter by the actual runId).
21
+ */
22
+ export declare function assertJournalConformance(journal: JournalCapability, runId?: string): Promise<void>;
@@ -0,0 +1,34 @@
1
+ // @bun
2
+ // packages/kernel-seed/src/journalConformance.ts
3
+ class JournalConformanceError extends Error {
4
+ name = "JournalConformanceError";
5
+ }
6
+ var CONFORMANCE_RUN_ID = "rig.run.__journal-conformance__";
7
+ function sampleResolvedPipeline() {
8
+ return {
9
+ order: ["validate", "merge-gate"],
10
+ record: [
11
+ { stageId: "validate", contributedBy: "default", isProtected: false },
12
+ { stageId: "merge-gate", contributedBy: "default", isProtected: false }
13
+ ],
14
+ cycles: [],
15
+ grantUses: [],
16
+ resolvedAt: "1970-01-01T00:00:00.000Z"
17
+ };
18
+ }
19
+ async function assertJournalConformance(journal, runId = CONFORMANCE_RUN_ID) {
20
+ const pipeline = sampleResolvedPipeline();
21
+ await journal.recordPipeline(runId, pipeline);
22
+ const events = await journal.read(runId);
23
+ if (!Array.isArray(events) || events.length === 0) {
24
+ throw new JournalConformanceError("journal.read returned no records after recordPipeline \u2014 provider does not durably record (append-and-record contract violated)");
25
+ }
26
+ const serialized = JSON.stringify(events);
27
+ if (!serialized.includes("merge-gate") || !serialized.includes("validate")) {
28
+ throw new JournalConformanceError("journal.read did not surface the recorded resolved-pipeline \u2014 provider does not expose the append-and-record contract");
29
+ }
30
+ }
31
+ export {
32
+ assertJournalConformance,
33
+ JournalConformanceError
34
+ };
@@ -9,6 +9,7 @@ export type PluginContributions = Readonly<Record<string, unknown>>;
9
9
  export type PluginRuntimeChannel = {
10
10
  readonly capabilities?: Readonly<Record<string, unknown>>;
11
11
  readonly kernel?: KernelCapability;
12
+ readonly [key: string]: unknown;
12
13
  };
13
14
  export type LoadedPlugin = {
14
15
  readonly meta: PluginMeta;
@@ -19,13 +20,23 @@ export type LoadedPlugin = {
19
20
  readonly runtime: PluginRuntimeChannel;
20
21
  };
21
22
  export type CapabilityPrecedence = Partial<Record<CapabilityTag, readonly string[]>>;
23
+ export type CapabilityProviderIds = Readonly<Record<CapabilityTag, string>>;
22
24
  export type ResolvedBootConfig = {
23
25
  readonly plugins?: readonly LoadedPlugin[];
24
26
  readonly loadPlugins?: () => Promise<readonly LoadedPlugin[]> | readonly LoadedPlugin[];
25
27
  readonly capabilityPrecedence?: CapabilityPrecedence;
28
+ /**
29
+ * When true, boot runs the behavioral journal-append conformance probe
30
+ * against the resolved kernel's journal (rejecting a structurally-complete
31
+ * no-op provider). Off by default to avoid writing a probe record into a live
32
+ * run journal on every boot; opt-in for managed/multi-tenant postures and
33
+ * exercised directly by the conformance test.
34
+ */
35
+ readonly verifyJournalConformance?: boolean;
26
36
  };
27
37
  export type BootResult = {
28
38
  readonly kernel: KernelCapability;
29
39
  readonly plugins: readonly LoadedPlugin[];
40
+ readonly capabilityProviderIds: CapabilityProviderIds;
30
41
  };
31
42
  export declare function loadedPluginId(plugin: LoadedPlugin): string;
package/dist/src/seed.js CHANGED
@@ -1,4 +1,34 @@
1
1
  // @bun
2
+ // packages/kernel-seed/src/journalConformance.ts
3
+ class JournalConformanceError extends Error {
4
+ name = "JournalConformanceError";
5
+ }
6
+ var CONFORMANCE_RUN_ID = "rig.run.__journal-conformance__";
7
+ function sampleResolvedPipeline() {
8
+ return {
9
+ order: ["validate", "merge-gate"],
10
+ record: [
11
+ { stageId: "validate", contributedBy: "default", isProtected: false },
12
+ { stageId: "merge-gate", contributedBy: "default", isProtected: false }
13
+ ],
14
+ cycles: [],
15
+ grantUses: [],
16
+ resolvedAt: "1970-01-01T00:00:00.000Z"
17
+ };
18
+ }
19
+ async function assertJournalConformance(journal, runId = CONFORMANCE_RUN_ID) {
20
+ const pipeline = sampleResolvedPipeline();
21
+ await journal.recordPipeline(runId, pipeline);
22
+ const events = await journal.read(runId);
23
+ if (!Array.isArray(events) || events.length === 0) {
24
+ throw new JournalConformanceError("journal.read returned no records after recordPipeline \u2014 provider does not durably record (append-and-record contract violated)");
25
+ }
26
+ const serialized = JSON.stringify(events);
27
+ if (!serialized.includes("merge-gate") || !serialized.includes("validate")) {
28
+ throw new JournalConformanceError("journal.read did not surface the recorded resolved-pipeline \u2014 provider does not expose the append-and-record contract");
29
+ }
30
+ }
31
+
2
32
  // packages/kernel-seed/src/pluginAbi.ts
3
33
  function loadedPluginId(plugin) {
4
34
  return plugin.meta.id;
@@ -110,6 +140,42 @@ function objectAt(value, key) {
110
140
  function hasFunction(value, key) {
111
141
  return value !== null && typeof value[key] === "function";
112
142
  }
143
+ function assertSatisfiesStageRunnerInterface(value) {
144
+ if (!isRecord(value) || typeof value.resolve !== "function" || typeof value.runPipeline !== "function") {
145
+ throw new BootIncoherent("stage-runner provider must expose resolve and runPipeline");
146
+ }
147
+ }
148
+ function assertSatisfiesJournalInterface(value) {
149
+ if (!isRecord(value) || typeof value.append !== "function" || typeof value.recordPipeline !== "function" || typeof value.read !== "function") {
150
+ throw new BootIncoherent("journal provider must expose append, recordPipeline, and read");
151
+ }
152
+ }
153
+ function assertSatisfiesLoaderPolicyInterface(value) {
154
+ if (!isRecord(value) || typeof value.resolveCapability !== "function") {
155
+ throw new BootIncoherent("loader-policy provider must expose resolveCapability");
156
+ }
157
+ }
158
+ function assertSatisfiesTransportInterface(value) {
159
+ if (!isRecord(value) || typeof value.dispatch !== "function") {
160
+ throw new BootIncoherent("transport provider must expose dispatch");
161
+ }
162
+ }
163
+ function resolveRequiredCapability(plugins, capability, config, assertInterface) {
164
+ const resolution = resolveCapability(plugins, capability, config);
165
+ if (!resolution) {
166
+ throw new BootIncoherent(`no ${capability} provider resolved`);
167
+ }
168
+ assertInterface(resolution.value);
169
+ return resolution;
170
+ }
171
+ function assertSatisfiesKernelStartProvider(value) {
172
+ if (!isRecord(value)) {
173
+ throw new BootIncoherent("kernel provider did not return an object");
174
+ }
175
+ if (typeof value.start !== "function") {
176
+ throw new BootIncoherent("kernel provider must expose start");
177
+ }
178
+ }
113
179
  function assertSatisfiesKernelInterface(value) {
114
180
  if (!isRecord(value)) {
115
181
  throw new BootIncoherent("kernel provider did not return an object");
@@ -142,12 +208,41 @@ async function loadPlugins(config) {
142
208
  }
143
209
  async function boot(config) {
144
210
  const plugins = await loadPlugins(config);
145
- const resolution = resolveCapability(plugins, "kernel", config);
146
- if (!resolution) {
147
- throw new BootIncoherent("no kernel provider resolved");
211
+ const kernelResolution = resolveRequiredCapability(plugins, "kernel", config, assertSatisfiesKernelStartProvider);
212
+ const stageRunnerResolution = resolveRequiredCapability(plugins, "stage-runner", config, assertSatisfiesStageRunnerInterface);
213
+ const journalResolution = resolveRequiredCapability(plugins, "journal", config, assertSatisfiesJournalInterface);
214
+ const loaderPolicyResolution = resolveRequiredCapability(plugins, "loader-policy", config, assertSatisfiesLoaderPolicyInterface);
215
+ const transportResolution = resolveRequiredCapability(plugins, "transport", config, assertSatisfiesTransportInterface);
216
+ const kernel = kernelResolution.value;
217
+ const stageRunner = stageRunnerResolution.value;
218
+ const journal = journalResolution.value;
219
+ const loaderPolicy = loaderPolicyResolution.value;
220
+ const transport = transportResolution.value;
221
+ const composed = {
222
+ ...kernel,
223
+ stageRunner,
224
+ journal,
225
+ loaderPolicy,
226
+ transport,
227
+ start(loadedPlugins) {
228
+ return kernel.start.call(composed, loadedPlugins);
229
+ }
230
+ };
231
+ assertSatisfiesKernelInterface(composed);
232
+ if (config.verifyJournalConformance === true) {
233
+ await assertJournalConformance(composed.journal);
148
234
  }
149
- assertSatisfiesKernelInterface(resolution.value);
150
- return { kernel: resolution.value, plugins };
235
+ return {
236
+ kernel: composed,
237
+ plugins,
238
+ capabilityProviderIds: {
239
+ kernel: kernelResolution.plugin.meta.id,
240
+ "stage-runner": stageRunnerResolution.plugin.meta.id,
241
+ journal: journalResolution.plugin.meta.id,
242
+ "loader-policy": loaderPolicyResolution.plugin.meta.id,
243
+ transport: transportResolution.plugin.meta.id
244
+ }
245
+ };
151
246
  }
152
247
  export {
153
248
  boot,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h-rig/kernel-seed",
3
- "version": "0.0.6-alpha.135",
3
+ "version": "0.0.6-alpha.137",
4
4
  "type": "module",
5
5
  "description": "Irreducible Rig bootstrap seed, plugin ABI, and capability resolver.",
6
6
  "license": "UNLICENSED",
@@ -33,6 +33,6 @@
33
33
  "module": "./dist/src/index.js",
34
34
  "types": "./dist/src/index.d.ts",
35
35
  "dependencies": {
36
- "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.135"
36
+ "@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.137"
37
37
  }
38
38
  }