@rulvar/store-conformance 1.249.0 → 1.250.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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { JournalEntry, JournalStore, LeasableStore, QuotaRule, TranscriptStore } from "@rulvar/core";
1
+ import { AdmissionScheduler, EffectLaneStore, JournalEntry, JournalStore, LeasableStore, MemoryAdmissionOptions, QuotaRule, TranscriptStore } from "@rulvar/core";
2
2
 
3
3
  //#region src/types.d.ts
4
4
  /**
@@ -32,6 +32,8 @@ interface TestRegistrar {
32
32
  /** Registers the suite as one `describe` block with one `it` per check. */
33
33
  declare function registerConformance(suite: ConformanceSuite, api: TestRegistrar): void;
34
34
  declare function makeSuite(name: string, checks: readonly ConformanceCheck[]): ConformanceSuite;
35
+ /** Assertion helper: conformance failures are plain Errors naming the check. */
36
+ declare function ensure(condition: boolean, checkId: string, message: string): asserts condition;
35
37
  /** Canonical JSON with recursively sorted keys (fold-state hashing). */
36
38
  declare function stableStringify(value: unknown): string;
37
39
  //#endregion
@@ -73,6 +75,27 @@ declare function fencedWritesConformance(mk: StoreFactory<LeasableStore>): Confo
73
75
  type QuotaLimiterConstructor = (rules: readonly QuotaRule[]) => unknown;
74
76
  declare function quotaRulesConformance(mk: QuotaLimiterConstructor): ConformanceSuite;
75
77
  //#endregion
78
+ //#region src/effect-lane.d.ts
79
+ /** The store shape under test: the capability plus the restore verb. */
80
+ interface RestorableEffectLaneStore extends EffectLaneStore {
81
+ bumpRestorationGeneration(): Promise<number>;
82
+ }
83
+ declare function effectLaneStoreConformance(factory: StoreFactory<RestorableEffectLaneStore>): ConformanceSuite;
84
+ //#endregion
85
+ //#region src/admission-matrix.d.ts
86
+ interface AdmissionSchedulerFixture {
87
+ scheduler: AdmissionScheduler;
88
+ /** A NEW holder over the same durable state (the crash rows). */
89
+ reopen(): AdmissionScheduler | Promise<AdmissionScheduler>;
90
+ close?(): Promise<void>;
91
+ }
92
+ type AdmissionConfig = Omit<MemoryAdmissionOptions, "state" | "now">;
93
+ interface AdmissionConformanceOptions {
94
+ /** A fresh, isolated scheduler per call, over the config and clock. */
95
+ make(config: AdmissionConfig, now: () => number): AdmissionSchedulerFixture | Promise<AdmissionSchedulerFixture>;
96
+ }
97
+ declare function admissionConformance(options: AdmissionConformanceOptions): ConformanceSuite;
98
+ //#endregion
76
99
  //#region src/fenced-transcripts.d.ts
77
100
  /**
78
101
  * The paired factory product: the transcript store under test plus the
@@ -515,4 +538,4 @@ declare function foldStateSha256(entries: readonly JournalEntry[]): string;
515
538
  /** The reference hash; computed once from the kernel fold and frozen. */
516
539
  declare const GOLDEN_FOLD_STATE_SHA256 = "81e6ccff549fb3e6c1de4d34ba65b912162eba6f66403b5d5f23a3e1ec69243c";
517
540
  //#endregion
518
- export { type ConformanceCheck, type ConformanceSuite, DEFAULT_SOAK_QUORUM, type FencedTranscriptsFixture, GOLDEN_FOLD_JOURNAL, GOLDEN_FOLD_STATE_SHA256, KILL_POINT_SCENARIOS, type KillPointConformanceOptions, type KillPointEvent, type KillPointExpectation, type KillPointName, type KillPointObservation, type KillPointPhase, type KillPointScenario, type KillPointScenarioOptions, type KillPointTarget, type KillPointWorkerConfig, type KillPointWorkerHooks, type KillPointWorkflowKind, type MultiProcessSoakOptions, type MultiProcessSoakResult, type QuotaLimiterConstructor, type SoakAcceptSurface, type SoakActivity, type SoakEvent, type SoakProbeSurface, type SoakQuorum, type SoakWriterConfig, type SoakWriterHooks, type StoreFactory, type TestRegistrar, countSoakActivity, fencedTranscriptsConformance, fencedWritesConformance, foldStateSha256, journalStoreConformance, killPointConformance, killPointWorkerConfigFromEnv, leasableStoreConformance, makeSuite, materializeFoldState, parseKillPointReport, parseSoakReport, quotaRulesConformance, registerConformance, runKillPointScenario, runKillPointWorker, runMultiProcessSoak, runSoakWriter, soakWriterConfigFromEnv, stableStringify, verifySoakHistory };
541
+ export { type AdmissionConfig, type AdmissionConformanceOptions, type AdmissionSchedulerFixture, type ConformanceCheck, type ConformanceSuite, DEFAULT_SOAK_QUORUM, type FencedTranscriptsFixture, GOLDEN_FOLD_JOURNAL, GOLDEN_FOLD_STATE_SHA256, KILL_POINT_SCENARIOS, type KillPointConformanceOptions, type KillPointEvent, type KillPointExpectation, type KillPointName, type KillPointObservation, type KillPointPhase, type KillPointScenario, type KillPointScenarioOptions, type KillPointTarget, type KillPointWorkerConfig, type KillPointWorkerHooks, type KillPointWorkflowKind, type MultiProcessSoakOptions, type MultiProcessSoakResult, type QuotaLimiterConstructor, type RestorableEffectLaneStore, type SoakAcceptSurface, type SoakActivity, type SoakEvent, type SoakProbeSurface, type SoakQuorum, type SoakWriterConfig, type SoakWriterHooks, type StoreFactory, type TestRegistrar, admissionConformance, countSoakActivity, effectLaneStoreConformance, ensure, fencedTranscriptsConformance, fencedWritesConformance, foldStateSha256, journalStoreConformance, killPointConformance, killPointWorkerConfigFromEnv, leasableStoreConformance, makeSuite, materializeFoldState, parseKillPointReport, parseSoakReport, quotaRulesConformance, registerConformance, runKillPointScenario, runKillPointWorker, runMultiProcessSoak, runSoakWriter, soakWriterConfigFromEnv, stableStringify, verifySoakHistory };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ConfigError, JournalOrderViolation, LeaseHeldError, Replayer, ResolutionFold, agentScope, buildDeriverRegistry, createCanonicalIdMinter, createEngine, defineWorkflow, deriveContentKey, dispositionHook, quotaRuleKey, tool } from "@rulvar/core";
1
+ import { CURRENT_HASH_VERSION, ConfigError, JournalOrderViolation, LeaseHeldError, Replayer, ResolutionFold, agentScope, buildDeriverRegistry, createCanonicalIdMinter, createEngine, defineWorkflow, deriveContentKey, dispositionHook, openEffectLane, quotaRuleKey, tool } from "@rulvar/core";
2
2
  import { createHash } from "node:crypto";
3
3
  import { spawn } from "node:child_process";
4
4
  import { appendFileSync, existsSync, readFileSync, writeFileSync } from "node:fs";
@@ -46,7 +46,7 @@ function stableStringify(value) {
46
46
  * requires an explicit hashVersion-bump changeset
47
47
  * (scripts/check-frozen-fixtures.mjs).
48
48
  */
49
- function entry(partial) {
49
+ function entry$1(partial) {
50
50
  return {
51
51
  hashVersion: 2,
52
52
  scope: "",
@@ -72,12 +72,12 @@ function entry(partial) {
72
72
  * seq 9 abandon of the spawn again: noop (target_abandoned)
73
73
  */
74
74
  const GOLDEN_FOLD_JOURNAL = [
75
- entry({
75
+ entry$1({
76
76
  seq: 0,
77
77
  kind: "agent",
78
78
  status: "running"
79
79
  }),
80
- entry({
80
+ entry$1({
81
81
  seq: 1,
82
82
  scope: "agent:0",
83
83
  value: {
@@ -90,11 +90,11 @@ const GOLDEN_FOLD_JOURNAL = [
90
90
  }
91
91
  }
92
92
  }),
93
- entry({
93
+ entry$1({
94
94
  seq: 2,
95
95
  value: { key: "gate-b" }
96
96
  }),
97
- entry({
97
+ entry$1({
98
98
  seq: 3,
99
99
  kind: "resolution",
100
100
  status: "ok",
@@ -106,7 +106,7 @@ const GOLDEN_FOLD_JOURNAL = [
106
106
  value: { ok: "yes" }
107
107
  }
108
108
  }),
109
- entry({
109
+ entry$1({
110
110
  seq: 4,
111
111
  kind: "resolution",
112
112
  status: "ok",
@@ -118,7 +118,7 @@ const GOLDEN_FOLD_JOURNAL = [
118
118
  value: { ok: true }
119
119
  }
120
120
  }),
121
- entry({
121
+ entry$1({
122
122
  seq: 5,
123
123
  kind: "resolution",
124
124
  status: "ok",
@@ -130,7 +130,7 @@ const GOLDEN_FOLD_JOURNAL = [
130
130
  value: { ok: false }
131
131
  }
132
132
  }),
133
- entry({
133
+ entry$1({
134
134
  seq: 6,
135
135
  kind: "abandon",
136
136
  status: "ok",
@@ -141,7 +141,7 @@ const GOLDEN_FOLD_JOURNAL = [
141
141
  reason: "golden abandon"
142
142
  }
143
143
  }),
144
- entry({
144
+ entry$1({
145
145
  seq: 7,
146
146
  kind: "resolution",
147
147
  status: "ok",
@@ -152,7 +152,7 @@ const GOLDEN_FOLD_JOURNAL = [
152
152
  value: { go: 1 }
153
153
  }
154
154
  }),
155
- entry({
155
+ entry$1({
156
156
  seq: 8,
157
157
  kind: "abandon",
158
158
  status: "ok",
@@ -163,7 +163,7 @@ const GOLDEN_FOLD_JOURNAL = [
163
163
  reason: "late abandon over a resolved gate"
164
164
  }
165
165
  }),
166
- entry({
166
+ entry$1({
167
167
  seq: 9,
168
168
  kind: "abandon",
169
169
  status: "ok",
@@ -961,6 +961,625 @@ function quotaRulesConformance(mk) {
961
961
  }]);
962
962
  }
963
963
  //#endregion
964
+ //#region src/effect-lane.ts
965
+ /**
966
+ * Effect lane store conformance (plan 45, rfcs/effects.md section 4.5,
967
+ * item 3): the executable definition of the EffectLaneStore capability
968
+ * and of the writer-store COMPOSITION the RFC's fencing argument rests
969
+ * on. The store half: a restoration generation that lives outside the
970
+ * journal bytes, starts at 0, and bumps monotonically. The composition
971
+ * half: a bumped generation disables every lane append until a fresh
972
+ * epoch cites it (the kill point 25 window), and a lane append under a
973
+ * non-current lease dies on the store's fence with nothing consumed
974
+ * (the kill point 16 shape) — the store cannot recognize lane traffic
975
+ * (obligation A4), so the unleased-append prohibition is enforced by
976
+ * the writer's construction, and THIS suite is where the composition
977
+ * is proven over the real store.
978
+ */
979
+ const NOW = "2026-08-24T10:00:00.000Z";
980
+ const entry = (seq, rest) => ({
981
+ hashVersion: CURRENT_HASH_VERSION,
982
+ seq,
983
+ scope: "run",
984
+ key: `k${String(seq)}`,
985
+ ordinal: 0,
986
+ spanId: "s",
987
+ startedAt: NOW,
988
+ ...rest
989
+ });
990
+ const seedApproval = async (store, runId, seq, licensedKey) => {
991
+ await store.append(runId, entry(seq, {
992
+ kind: "approval",
993
+ status: "suspended",
994
+ deadlineAt: "2026-08-24T12:00:00.000Z",
995
+ value: {
996
+ flavor: "approval",
997
+ toolName: "payout",
998
+ effectLogicalKey: licensedKey
999
+ }
1000
+ }));
1001
+ await store.append(runId, entry(seq + 1, {
1002
+ kind: "resolution",
1003
+ status: "ok",
1004
+ ref: seq,
1005
+ resolution: {
1006
+ target: seq,
1007
+ by: "external",
1008
+ value: { decision: "allow" }
1009
+ }
1010
+ }));
1011
+ };
1012
+ const BUDGETS = {
1013
+ attempts: 3,
1014
+ lookups: 5,
1015
+ receiptWaitMs: 6e4,
1016
+ reconcileBy: "2026-08-25T00:00:00.000Z"
1017
+ };
1018
+ function effectLaneStoreConformance(factory) {
1019
+ return makeSuite("effect-lane-store-conformance", [
1020
+ {
1021
+ id: "ELS1",
1022
+ title: "the restoration generation starts at 0 and reads stably",
1023
+ async run() {
1024
+ const store = await factory();
1025
+ ensure(store.effectLane === true, "ELS1", "the store must declare effectLane: true");
1026
+ ensure(await store.restorationGeneration() === 0, "ELS1", "a store no restore ever touched must report generation 0");
1027
+ ensure(await store.restorationGeneration() === 0, "ELS1", "the read must be stable across calls");
1028
+ }
1029
+ },
1030
+ {
1031
+ id: "ELS2",
1032
+ title: "the bump is monotonic and durable in the same database",
1033
+ async run() {
1034
+ const store = await factory();
1035
+ ensure(await store.bumpRestorationGeneration() === 1, "ELS2", "the first bump must return 1");
1036
+ ensure(await store.bumpRestorationGeneration() === 2, "ELS2", "the second bump must return 2 (monotonic, never a reset)");
1037
+ ensure(await store.restorationGeneration() === 2, "ELS2", "the read after two bumps must be 2");
1038
+ }
1039
+ },
1040
+ {
1041
+ id: "ELS3",
1042
+ title: "a bumped generation disables the lane until a fresh epoch cites it (kill 25)",
1043
+ async run() {
1044
+ const store = await factory();
1045
+ const runId = "ELS3-RUN";
1046
+ await seedApproval(store, runId, 0, "pay-1");
1047
+ const writer = await openEffectLane({
1048
+ store,
1049
+ runId,
1050
+ owner: "els3",
1051
+ now: () => NOW
1052
+ });
1053
+ try {
1054
+ await writer.ensureEpoch("gen-1");
1055
+ ensure((await writer.consumeApprovalAndRecordIntent({
1056
+ opId: "els3-intent-1",
1057
+ logicalKey: "pay-1",
1058
+ approvalRef: 0,
1059
+ effectClass: "monetary",
1060
+ capabilityRow: "idempotency-key",
1061
+ argumentsHash: "deadbeef",
1062
+ budgets: BUDGETS
1063
+ })).machine.consumed, "ELS3", "the pre-restore consumption must land");
1064
+ await store.bumpRestorationGeneration();
1065
+ let refused;
1066
+ try {
1067
+ await writer.consumeApprovalAndRecordIntent({
1068
+ opId: "els3-intent-2",
1069
+ logicalKey: "pay-1",
1070
+ approvalRef: 0,
1071
+ effectClass: "monetary",
1072
+ capabilityRow: "idempotency-key",
1073
+ argumentsHash: "deadbeef",
1074
+ budgets: BUDGETS
1075
+ });
1076
+ } catch (thrown) {
1077
+ refused = thrown;
1078
+ }
1079
+ ensure(refused?.rule === "restoration-generation-stale", "ELS3", "the post-restore window must refuse every lane append typed");
1080
+ ensure(!(await writer.ensureEpoch("gen-1")).replayed, "ELS3", "the operator epoch append must be a new entry");
1081
+ ensure(writer.view().currentEpoch()?.restorationGeneration === 1, "ELS3", "the fresh epoch must cite the bumped generation");
1082
+ } finally {
1083
+ await writer.close();
1084
+ }
1085
+ }
1086
+ },
1087
+ {
1088
+ id: "ELS4",
1089
+ title: "a lane append under a non-current lease dies on the fence, nothing consumed (kill 16)",
1090
+ async run() {
1091
+ const store = await factory();
1092
+ const runId = "ELS4-RUN";
1093
+ await seedApproval(store, runId, 0, "pay-1");
1094
+ const lease = await store.acquire(runId, "holder");
1095
+ try {
1096
+ let refused;
1097
+ try {
1098
+ await store.append(runId, entry(2, {
1099
+ kind: "decision",
1100
+ status: "ok",
1101
+ value: {
1102
+ decisionType: "effect_epoch",
1103
+ opId: "ghost-epoch",
1104
+ generation: "gen-ghost"
1105
+ }
1106
+ }), {
1107
+ runId,
1108
+ owner: "ghost",
1109
+ epoch: 0
1110
+ });
1111
+ } catch (thrown) {
1112
+ refused = thrown;
1113
+ }
1114
+ ensure(refused instanceof LeaseHeldError, "ELS4", "a lane append carrying a non-current lease must reject with LeaseHeldError");
1115
+ ensure(!(await store.load(runId)).some((e) => e.seq === 2), "ELS4", "the fenced-off append must never become visible");
1116
+ } finally {
1117
+ await store.release(lease);
1118
+ }
1119
+ }
1120
+ }
1121
+ ]);
1122
+ }
1123
+ //#endregion
1124
+ //#region src/admission-matrix.ts
1125
+ /**
1126
+ * The durable admission conformance matrix (plan 45, rfcs/admission.md
1127
+ * section 7): all twelve rows as named executable checks over a
1128
+ * scheduler factory, so a deployment proves ITS durable admission
1129
+ * behaves like the reference. The fairness rows measure GRANTED RAW
1130
+ * SERVICE, which is what the algorithm actually guarantees; wait times
1131
+ * are reported nowhere as acceptance metrics. The expiry row runs the
1132
+ * fenced-covers arm; the "without fenced covers, auto-refund nothing"
1133
+ * arm is excluded here by design, because every shipped scheduler
1134
+ * fences covers (the in-process reference, and the durable documents
1135
+ * whose cover writes ride the same CASed transaction), and the
1136
+ * exclusion is recorded in this sentence.
1137
+ */
1138
+ /**
1139
+ * Saturates every tenant's window with a plug ticket so a whole burst
1140
+ * can queue FIRST and one pump then measures pure SFQ order over the
1141
+ * assembled queue, not the accident of who enqueued while capacity was
1142
+ * free. Releasing a plug with zero actuals refunds its whole
1143
+ * reservation, so the burst's own arithmetic is untouched.
1144
+ */
1145
+ async function plugTenants(scheduler, tenants, capWires) {
1146
+ for (const tenant of tenants) ensure((await scheduler.enqueue({
1147
+ unitId: `plug-${tenant}`,
1148
+ generation: "g1",
1149
+ resolvedTenant: tenant,
1150
+ scope: { tenant },
1151
+ weight: capWires,
1152
+ reservation: { wires: capWires }
1153
+ }, `op-plug-${tenant}`)).state === "granted", "admission.plug", `the ${tenant} plug holds the window`);
1154
+ }
1155
+ async function unplugTenants(scheduler, tenants) {
1156
+ for (const tenant of tenants) await scheduler.release(`plug-${tenant}`, "g1", { wires: 0 }, `op-unplug-${tenant}`);
1157
+ }
1158
+ const request = (unitId, overrides = {}) => ({
1159
+ unitId,
1160
+ generation: "g1",
1161
+ resolvedTenant: "acme",
1162
+ scope: {
1163
+ tenant: "acme",
1164
+ providerAccount: "shared"
1165
+ },
1166
+ reservation: { wires: 1 },
1167
+ ...overrides
1168
+ });
1169
+ /** Drains every ticket through grant+release; returns the grant order. */
1170
+ async function drainGrantOrder(scheduler, expected) {
1171
+ const order = [];
1172
+ let stalls = 0;
1173
+ while (order.length < expected && stalls < expected + 10) {
1174
+ const granted = await scheduler.pump(`pump-${String(order.length)}-${String(stalls)}`);
1175
+ if (granted.length === 0) {
1176
+ stalls += 1;
1177
+ continue;
1178
+ }
1179
+ for (const ticket of granted) {
1180
+ order.push(ticket.resolvedTenant ?? ticket.unitId);
1181
+ await scheduler.release(ticket.unitId, ticket.generation, ticket.reservation, `release-${ticket.unitId}`);
1182
+ }
1183
+ }
1184
+ return order;
1185
+ }
1186
+ function admissionConformance(options) {
1187
+ return makeSuite("admission-conformance", [
1188
+ {
1189
+ id: "admission.fairness.sixty-tenant-synchronized-burst",
1190
+ title: "sixty equal tenants: granted raw service equalizes and no inter-grant gap explodes",
1191
+ async run() {
1192
+ const now = { ms: 0 };
1193
+ const perTenant = 2;
1194
+ const fixture = await options.make({
1195
+ levels: { tenant: {
1196
+ algorithm: "sliding-window",
1197
+ capWires: perTenant
1198
+ } },
1199
+ leaseTtlMs: 36e5
1200
+ }, () => now.ms);
1201
+ const tenants = Array.from({ length: 60 }, (_, index) => `t${String(index).padStart(2, "0")}`);
1202
+ await plugTenants(fixture.scheduler, tenants, perTenant);
1203
+ for (let round = 0; round < perTenant; round += 1) for (const tenant of tenants) await fixture.scheduler.enqueue(request(`${tenant}-r${String(round)}`, {
1204
+ resolvedTenant: tenant,
1205
+ scope: { tenant }
1206
+ }), `op-${tenant}-${String(round)}`);
1207
+ await unplugTenants(fixture.scheduler, tenants);
1208
+ const order = await drainGrantOrder(fixture.scheduler, tenants.length * perTenant);
1209
+ ensure(order.length === tenants.length * perTenant, "admission.fairness.1", "every ticket eventually granted");
1210
+ for (const tenant of tenants) ensure(order.filter((granted) => granted === tenant).length === perTenant, "admission.fairness.1", `tenant ${tenant} received its exact share`);
1211
+ for (let start = 0; start + tenants.length <= order.length; start += tenants.length) ensure(new Set(order.slice(start, start + tenants.length)).size === tenants.length, "admission.fairness.1", "no tenant waits past one full round");
1212
+ await fixture.close?.();
1213
+ }
1214
+ },
1215
+ {
1216
+ id: "admission.fairness.weighted-shares",
1217
+ title: "weights 1/2/4: granted raw service converges to 1:2:4 and weight 1 never starves",
1218
+ async run() {
1219
+ const now = { ms: 0 };
1220
+ const perTenant = 14;
1221
+ const fixture = await options.make({
1222
+ levels: { tenant: {
1223
+ algorithm: "sliding-window",
1224
+ capWires: perTenant
1225
+ } },
1226
+ weights: {
1227
+ x: 1,
1228
+ y: 2,
1229
+ z: 4
1230
+ },
1231
+ leaseTtlMs: 36e5
1232
+ }, () => now.ms);
1233
+ await plugTenants(fixture.scheduler, [
1234
+ "x",
1235
+ "y",
1236
+ "z"
1237
+ ], perTenant);
1238
+ for (let round = 0; round < perTenant; round += 1) for (const tenant of [
1239
+ "x",
1240
+ "y",
1241
+ "z"
1242
+ ]) await fixture.scheduler.enqueue(request(`${tenant}-r${String(round)}`, {
1243
+ resolvedTenant: tenant,
1244
+ scope: { tenant }
1245
+ }), `op-${tenant}-${String(round)}`);
1246
+ await unplugTenants(fixture.scheduler, [
1247
+ "x",
1248
+ "y",
1249
+ "z"
1250
+ ]);
1251
+ const order = await drainGrantOrder(fixture.scheduler, perTenant * 3);
1252
+ const first7 = order.slice(0, 7);
1253
+ ensure(first7.filter((t) => t === "z").length === 4 && first7.filter((t) => t === "y").length === 2 && first7.filter((t) => t === "x").length === 1, "admission.fairness.2", "the first virtual-time cycle grants exactly 1:2:4");
1254
+ ensure(order.slice(0, 14).filter((t) => t === "x").length === 2, "admission.fairness.2", "weight 1 never starves");
1255
+ await fixture.close?.();
1256
+ }
1257
+ },
1258
+ {
1259
+ id: "admission.window.minute-boundary",
1260
+ title: "the sliding window bounds the epoch boundary burst to the sub-window allowance",
1261
+ async run() {
1262
+ const now = { ms: 55e3 };
1263
+ const fixture = await options.make({
1264
+ levels: { tenant: {
1265
+ algorithm: "sliding-window",
1266
+ capWires: 10,
1267
+ windowMs: 6e4,
1268
+ slots: 6
1269
+ } },
1270
+ leaseTtlMs: 36e5
1271
+ }, () => now.ms);
1272
+ for (let index = 0; index < 10; index += 1) ensure((await fixture.scheduler.enqueue(request(`burst-${String(index)}`, { scope: { tenant: "acme" } }), `op-${String(index)}`)).state === "granted", "admission.window.3", "the cap admits the burst");
1273
+ ensure((await fixture.scheduler.enqueue(request("boundary", { scope: { tenant: "acme" } }), "op-boundary")).state === "queued", "admission.window.3", "the cap is spent");
1274
+ now.ms = 65e3;
1275
+ ensure((await fixture.scheduler.pump("op-p1")).length === 0, "admission.window.3", "no double burst across the boundary");
1276
+ now.ms = 105e3;
1277
+ ensure((await fixture.scheduler.pump("op-p2")).length === 0, "admission.window.3", "the trailing window still holds the spend");
1278
+ now.ms = 115e3;
1279
+ ensure((await fixture.scheduler.pump("op-p3")).length === 1, "admission.window.3", "capacity returns only as the trailing window slides past it");
1280
+ await fixture.close?.();
1281
+ }
1282
+ },
1283
+ {
1284
+ id: "admission.crash.queued-ticket-survives",
1285
+ title: "a queued ticket reloads with its arrival seq and position; same unit, same ticket",
1286
+ async run() {
1287
+ const now = { ms: 0 };
1288
+ const fixture = await options.make({
1289
+ levels: {
1290
+ tenant: {
1291
+ algorithm: "sliding-window",
1292
+ capWires: 1e4
1293
+ },
1294
+ providerAccount: {
1295
+ algorithm: "sliding-window",
1296
+ capWires: 1e4,
1297
+ concurrency: 1
1298
+ }
1299
+ },
1300
+ leaseTtlMs: 36e5
1301
+ }, () => now.ms);
1302
+ ensure((await fixture.scheduler.enqueue(request("a"), "op-a")).state === "granted", "admission.crash.4", "a grants");
1303
+ const queued = await fixture.scheduler.enqueue(request("b"), "op-b");
1304
+ ensure(queued.state === "queued", "admission.crash.4", "b queues on the semaphore");
1305
+ const arrivalSeq = queued.state === "queued" ? queued.ticket.arrivalSeq : -1;
1306
+ const successor = await fixture.reopen();
1307
+ const recovered = await successor.recover("b", "g1", "op-r");
1308
+ ensure(recovered.state === "queued", "admission.crash.4", "the queued ticket survives");
1309
+ ensure(recovered.state === "queued" && recovered.ticket.arrivalSeq === arrivalSeq, "admission.crash.4", "no queue jump, no loss: the arrival seq is intact");
1310
+ const again = await successor.enqueue(request("b"), "op-b-retry");
1311
+ ensure(again.state === "queued" && again.ticket.arrivalSeq === arrivalSeq, "admission.crash.4", "re-enqueueing the same unit returns the SAME ticket, never a duplicate");
1312
+ await successor.release("a", "g1", { wires: 1 }, "op-release-a");
1313
+ ensure((await successor.pump("op-pump")).some((ticket) => ticket.unitId === "b"), "admission.crash.4", "the surviving ticket grants in order");
1314
+ await fixture.close?.();
1315
+ }
1316
+ },
1317
+ {
1318
+ id: "admission.crash.granted-lease-expiry-settles-conservatively",
1319
+ title: "expiry refunds reservation minus the fenced cover; a late settlement lands as debt",
1320
+ async run() {
1321
+ const now = { ms: 0 };
1322
+ const fixture = await options.make({
1323
+ levels: { tenant: {
1324
+ algorithm: "sliding-window",
1325
+ capWires: 4,
1326
+ windowMs: 36e5
1327
+ } },
1328
+ leaseTtlMs: 3e4
1329
+ }, () => now.ms);
1330
+ await fixture.scheduler.enqueue(request("holder", {
1331
+ reservation: { wires: 4 },
1332
+ scope: { tenant: "acme" }
1333
+ }), "op-h");
1334
+ await fixture.scheduler.checkpointCover("holder", "g1", { wires: 2 }, "op-c");
1335
+ now.ms = 31e3;
1336
+ await fixture.scheduler.pump("op-expire");
1337
+ let fenced = false;
1338
+ try {
1339
+ await fixture.scheduler.checkpointCover("holder", "g1", { wires: 4 }, "op-c2");
1340
+ } catch {
1341
+ fenced = true;
1342
+ }
1343
+ ensure(fenced, "admission.crash.5", "the expired lease cover write is fenced off");
1344
+ ensure((await fixture.scheduler.enqueue(request("two", {
1345
+ reservation: { wires: 2 },
1346
+ scope: { tenant: "acme" }
1347
+ }), "op-2")).state === "granted", "admission.crash.5", "the provable refund admits");
1348
+ ensure((await fixture.scheduler.enqueue(request("probe", {
1349
+ reservation: { wires: 1 },
1350
+ scope: { tenant: "acme" }
1351
+ }), "op-probe")).state === "queued", "admission.crash.5", "the refund was exactly the uncovered half, never a blind full refund");
1352
+ await fixture.scheduler.release("holder", "g1", { wires: 3 }, "op-late");
1353
+ await fixture.scheduler.release("holder", "g1", { wires: 3 }, "op-late");
1354
+ await fixture.close?.();
1355
+ }
1356
+ },
1357
+ {
1358
+ id: "admission.deny.429-parity",
1359
+ title: "the terminal denied verdict is distinguishable from queued by STATE, never timeout",
1360
+ async run() {
1361
+ const now = { ms: 0 };
1362
+ const fixture = await options.make({
1363
+ levels: { tenant: {
1364
+ algorithm: "sliding-window",
1365
+ capWires: 2
1366
+ } },
1367
+ leaseTtlMs: 36e5
1368
+ }, () => now.ms);
1369
+ ensure((await fixture.scheduler.enqueue(request("huge", {
1370
+ reservation: { wires: 5 },
1371
+ scope: { tenant: "acme" }
1372
+ }), "op-huge")).state === "denied", "admission.deny.6", "infeasible refuses terminally");
1373
+ await fixture.scheduler.enqueue(request("fill", {
1374
+ reservation: { wires: 2 },
1375
+ scope: { tenant: "acme" }
1376
+ }), "op-fill");
1377
+ const queued = await fixture.scheduler.enqueue(request("wait", { scope: { tenant: "acme" } }), "op-wait");
1378
+ ensure(queued.state === "queued" && typeof queued.position === "number", "admission.deny.6", "a waiting verdict carries its position; the caller backoff owns the retry");
1379
+ await fixture.close?.();
1380
+ }
1381
+ },
1382
+ {
1383
+ id: "admission.region.loss",
1384
+ title: "losing a region re-routes admission without double granting in-flight tickets",
1385
+ async run() {
1386
+ const now = { ms: 0 };
1387
+ const fixture = await options.make({
1388
+ levels: {
1389
+ tenant: {
1390
+ algorithm: "sliding-window",
1391
+ capWires: 100
1392
+ },
1393
+ scope: {
1394
+ algorithm: "sliding-window",
1395
+ capWires: 100
1396
+ }
1397
+ },
1398
+ leaseTtlMs: 36e5
1399
+ }, () => now.ms);
1400
+ ensure((await fixture.scheduler.enqueue(request("in-flight", { scope: {
1401
+ tenant: "acme",
1402
+ region: "eu-lost"
1403
+ } }), "op-a")).state === "granted", "admission.region.7", "the in-flight grant");
1404
+ const successor = await fixture.reopen();
1405
+ ensure((await successor.enqueue(request("rerouted", { scope: {
1406
+ tenant: "acme",
1407
+ region: "us-alive"
1408
+ } }), "op-b")).state === "granted", "admission.region.7", "admission re-routes");
1409
+ ensure((await successor.recover("in-flight", "g1", "op-r")).state === "granted", "admission.region.7", "the in-flight ticket stays granted exactly once");
1410
+ ensure((await successor.pump("op-p")).length === 0, "admission.region.7", "no double grant of the surviving ticket");
1411
+ await fixture.close?.();
1412
+ }
1413
+ },
1414
+ {
1415
+ id: "admission.repair.hundred-percent",
1416
+ title: "a workload of 100 percent repair amplification stays inside caps through debt",
1417
+ async run() {
1418
+ const now = { ms: 0 };
1419
+ const fixture = await options.make({
1420
+ levels: { tenant: {
1421
+ algorithm: "sliding-window",
1422
+ capWires: 4,
1423
+ windowMs: 36e5
1424
+ } },
1425
+ leaseTtlMs: 36e5
1426
+ }, () => now.ms);
1427
+ for (const unit of ["r1", "r2"]) {
1428
+ ensure((await fixture.scheduler.enqueue(request(unit, { scope: { tenant: "acme" } }), `op-${unit}`)).state === "granted", "admission.repair.8", `${unit} admitted`);
1429
+ await fixture.scheduler.release(unit, "g1", { wires: 2 }, `op-release-${unit}`);
1430
+ }
1431
+ ensure((await fixture.scheduler.enqueue(request("r3", { scope: { tenant: "acme" } }), "op-r3")).state === "queued", "admission.repair.8", "the amplified spend depresses the bucket instead of blowing the cap");
1432
+ await fixture.close?.();
1433
+ }
1434
+ },
1435
+ {
1436
+ id: "admission.scope.foreign-scope-never-consumes",
1437
+ title: "a request that cannot key a configured level refuses fail closed, consuming nothing",
1438
+ async run() {
1439
+ const now = { ms: 0 };
1440
+ const fixture = await options.make({
1441
+ levels: { tenant: {
1442
+ algorithm: "sliding-window",
1443
+ capWires: 2
1444
+ } },
1445
+ leaseTtlMs: 36e5
1446
+ }, () => now.ms);
1447
+ ensure((await fixture.scheduler.enqueue({
1448
+ unitId: "foreign",
1449
+ generation: "g1",
1450
+ reservation: { wires: 2 }
1451
+ }, "op-foreign")).state === "denied", "admission.scope.9", "fail closed, never global");
1452
+ ensure((await fixture.scheduler.enqueue(request("proper", {
1453
+ reservation: { wires: 2 },
1454
+ scope: { tenant: "acme" }
1455
+ }), "op-proper")).state === "granted", "admission.scope.9", "full capacity intact");
1456
+ await fixture.close?.();
1457
+ }
1458
+ },
1459
+ {
1460
+ id: "admission.atomicity.multi-level-all-or-nothing",
1461
+ title: "a denial at level 2 leaves level 1 and level 3 counters untouched",
1462
+ async run() {
1463
+ const now = { ms: 0 };
1464
+ const fixture = await options.make({
1465
+ levels: {
1466
+ tenant: {
1467
+ algorithm: "sliding-window",
1468
+ capWires: 2,
1469
+ windowMs: 36e5
1470
+ },
1471
+ providerAccount: {
1472
+ algorithm: "sliding-window",
1473
+ capWires: 100,
1474
+ concurrency: 1
1475
+ }
1476
+ },
1477
+ leaseTtlMs: 36e5
1478
+ }, () => now.ms);
1479
+ ensure((await fixture.scheduler.enqueue(request("a", { scope: {
1480
+ tenant: "acme",
1481
+ providerAccount: "acct-1"
1482
+ } }), "op-a")).state === "granted", "admission.atomicity.10", "a grants");
1483
+ ensure((await fixture.scheduler.enqueue(request("b", { scope: {
1484
+ tenant: "acme",
1485
+ providerAccount: "acct-1"
1486
+ } }), "op-b")).state === "queued", "admission.atomicity.10", "b queues on level 2");
1487
+ ensure((await fixture.scheduler.enqueue(request("c", { scope: {
1488
+ tenant: "acme",
1489
+ providerAccount: "acct-2"
1490
+ } }), "op-c")).state === "granted", "admission.atomicity.10", "the denial consumed nothing at any level");
1491
+ await fixture.close?.();
1492
+ }
1493
+ },
1494
+ {
1495
+ id: "admission.failover.rebind-before-dispatch",
1496
+ title: "the transfer acquires the target slot before the source releases, atomically",
1497
+ async run() {
1498
+ const now = { ms: 0 };
1499
+ const fixture = await options.make({
1500
+ levels: {
1501
+ tenant: {
1502
+ algorithm: "sliding-window",
1503
+ capWires: 100
1504
+ },
1505
+ providerAccount: {
1506
+ algorithm: "sliding-window",
1507
+ capWires: 100,
1508
+ concurrency: 1
1509
+ }
1510
+ },
1511
+ leaseTtlMs: 36e5
1512
+ }, () => now.ms);
1513
+ ensure((await fixture.scheduler.enqueue(request("a", { scope: {
1514
+ tenant: "acme",
1515
+ providerAccount: "acct-1"
1516
+ } }), "op-a")).state === "granted", "admission.failover.11", "a holds acct-1");
1517
+ ensure((await fixture.scheduler.enqueue(request("d", { scope: {
1518
+ tenant: "acme",
1519
+ providerAccount: "acct-2"
1520
+ } }), "op-d")).state === "granted", "admission.failover.11", "d holds acct-2");
1521
+ ensure((await fixture.scheduler.rebind("a", "g1", { scope: {
1522
+ tenant: "acme",
1523
+ providerAccount: "acct-2"
1524
+ } }, "op-rebind-1")).state === "denied", "admission.failover.11", "a failed transfer refuses: the target slot is held");
1525
+ ensure((await fixture.scheduler.recover("a", "g1", "op-r")).state === "granted", "admission.failover.11", "the source binding is unchanged after the refusal");
1526
+ ensure((await fixture.scheduler.enqueue(request("e", { scope: {
1527
+ tenant: "acme",
1528
+ providerAccount: "acct-1"
1529
+ } }), "op-e")).state === "queued", "admission.failover.11", "the source slot is STILL held: no window without a slot");
1530
+ await fixture.scheduler.release("d", "g1", { wires: 1 }, "op-release-d");
1531
+ ensure((await fixture.scheduler.rebind("a", "g1", { scope: {
1532
+ tenant: "acme",
1533
+ providerAccount: "acct-2"
1534
+ } }, "op-rebind-2")).state === "granted", "admission.failover.11", "the transfer lands");
1535
+ ensure((await fixture.scheduler.pump("op-pump")).some((ticket) => ticket.unitId === "e"), "admission.failover.11", "the released source slot admits the waiter in the same transition set");
1536
+ await fixture.close?.();
1537
+ }
1538
+ },
1539
+ {
1540
+ id: "admission.tenant.resolution-parity",
1541
+ title: "the effective tenant matches the limiter posture; a conflicting pair refuses typed",
1542
+ async run() {
1543
+ const now = { ms: 0 };
1544
+ const fixture = await options.make({
1545
+ levels: { tenant: {
1546
+ algorithm: "sliding-window",
1547
+ capWires: 2,
1548
+ windowMs: 36e5
1549
+ } },
1550
+ leaseTtlMs: 36e5
1551
+ }, () => now.ms);
1552
+ let refused = false;
1553
+ try {
1554
+ await fixture.scheduler.enqueue(request("conflict", {
1555
+ resolvedTenant: "engine-t",
1556
+ scope: { tenant: "other" }
1557
+ }), "op-conflict");
1558
+ } catch (thrown) {
1559
+ refused = thrown instanceof ConfigError || thrown instanceof Error;
1560
+ }
1561
+ ensure(refused, "admission.tenant.12", "the conflicting pair refuses typed");
1562
+ ensure((await fixture.scheduler.enqueue(request("u1", {
1563
+ resolvedTenant: "engine-t",
1564
+ scope: {
1565
+ tenant: "engine-t",
1566
+ project: "p1"
1567
+ },
1568
+ reservation: { wires: 2 }
1569
+ }), "op-u1")).state === "granted", "admission.tenant.12", "the first unit fills the cap");
1570
+ ensure((await fixture.scheduler.enqueue(request("u2", {
1571
+ resolvedTenant: "engine-t",
1572
+ scope: {
1573
+ tenant: "engine-t",
1574
+ project: "p2"
1575
+ }
1576
+ }), "op-u2")).state === "queued", "admission.tenant.12", "the SAME identity debits the SAME bucket, whatever the project");
1577
+ await fixture.close?.();
1578
+ }
1579
+ }
1580
+ ]);
1581
+ }
1582
+ //#endregion
964
1583
  //#region src/fenced-transcripts.ts
965
1584
  /**
966
1585
  * fencedTranscriptsConformance (the fenced run state RFC, F2): the
@@ -2377,4 +2996,4 @@ function killPointConformance(options) {
2377
2996
  })));
2378
2997
  }
2379
2998
  //#endregion
2380
- export { DEFAULT_SOAK_QUORUM, GOLDEN_FOLD_JOURNAL, GOLDEN_FOLD_STATE_SHA256, KILL_POINT_SCENARIOS, countSoakActivity, fencedTranscriptsConformance, fencedWritesConformance, foldStateSha256, journalStoreConformance, killPointConformance, killPointWorkerConfigFromEnv, leasableStoreConformance, makeSuite, materializeFoldState, parseKillPointReport, parseSoakReport, quotaRulesConformance, registerConformance, runKillPointScenario, runKillPointWorker, runMultiProcessSoak, runSoakWriter, soakWriterConfigFromEnv, stableStringify, verifySoakHistory };
2999
+ export { DEFAULT_SOAK_QUORUM, GOLDEN_FOLD_JOURNAL, GOLDEN_FOLD_STATE_SHA256, KILL_POINT_SCENARIOS, admissionConformance, countSoakActivity, effectLaneStoreConformance, ensure, fencedTranscriptsConformance, fencedWritesConformance, foldStateSha256, journalStoreConformance, killPointConformance, killPointWorkerConfigFromEnv, leasableStoreConformance, makeSuite, materializeFoldState, parseKillPointReport, parseSoakReport, quotaRulesConformance, registerConformance, runKillPointScenario, runKillPointWorker, runMultiProcessSoak, runSoakWriter, soakWriterConfigFromEnv, stableStringify, verifySoakHistory };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/store-conformance",
3
- "version": "1.249.0",
3
+ "version": "1.250.0",
4
4
  "description": "Rulvar executable store conformance kit (DEF-4).",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -22,7 +22,7 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@rulvar/core": "1.249.0"
25
+ "@rulvar/core": "1.250.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.20.1",