@lmzhen/dsh-evolution-state-storage 0.17.1 → 0.18.1

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
@@ -20,7 +20,9 @@ import { expect } from 'vitest' // or any runner wrapped into `ConformanceAssert
20
20
  await runStateProviderConsistency(myProvider, expect)
21
21
  ```
22
22
 
23
- The assertion surface is INJECTED rather than imported, which is what lets this module ship: it carries no test-runner dependency, so loading the package never loads vitest. Both shipped providers run the same suite (`evolution-state-json` / `evolution-state-domain` `tests/provider-consistency.spec.ts`), and `consistency-forge.spec.ts` proves the suite fails when a single field is forged.
23
+ Every value the vectors use is PARAMETERIZABLE (U-2): `runStateProviderConsistency(provider, assert, options)` takes `idPrefix` / `claimPrefix` / `createdAt` / `sessionId` / `attributionSessionId` / `reviewSessionPrefix`, so a medium that already holds records in the default `c-…` namespace — or whose schema insists on a parseable stamp — runs the very same vectors under its own values. Every field defaults to the in-tree literals, so a two-argument call is unchanged. Each vector carries a `since <version>` note saying which release it became binding in.
24
+
25
+ The assertion surface is INJECTED rather than imported, which is what lets this module ship: it carries no test-runner dependency, so loading the package never loads vitest. Both shipped providers run the same suite (`evolution-state-json` / `evolution-state-domain` `tests/provider-consistency.spec.ts`) — the json one also runs it under a caller-supplied namespace — and `consistency-forge.spec.ts` proves the suite fails when a single field is forged.
24
26
 
25
27
 
26
28
  ## Known limitations
package/lib/index.js CHANGED
@@ -82,17 +82,38 @@ const REVIEW_STATE_SESSION_CAP = 500;
82
82
  const UNKNOWN_FIELD_POLICY = "preserve";
83
83
  const isNonNegInt = (value) => typeof value === "number" && Number.isInteger(value) && value >= 0;
84
84
  const optionalString = (value) => value === void 0 || typeof value === "string";
85
- const PENDING_KINDS = new Set([
85
+ /** 0.3.17 (S3.5, D-4): 'skill_batch' is gone — nothing ever created one (a dead enum member). A
86
+ * historic value, if it ever reached disk, is read as an unknown kind by consumers rather than
87
+ * minted here.
88
+ *
89
+ * 0.3.66: 'capability' stays with NO producer — the evolution-capability adapter was removed. It is
90
+ * a read-compatibility member: state written by an install that used that adapter (≤0.3.65) still
91
+ * holds such records, and they must keep loading, listing in `/evolution pending`, and answering
92
+ * approve or reject. Dropping it would strand them two ways: json quarantines the row to
93
+ * `<file>.corrupt` and refuses the resolving write, while the domain provider validates every
94
+ * stored record at mount, so one such row fails the whole domain with `invalid-record`.
95
+ *
96
+ * S2.9/1-10: this array is the ONE source of the vocabulary — the types, the write gate below and
97
+ * the domain provider's schema all derive from it. A second literal copy is the E-10 drift class
98
+ * this module exists to end. */
99
+ const PENDING_KINDS = [
86
100
  "memory",
87
101
  "skill",
88
102
  "capability"
89
- ]);
90
- const PENDING_STATUSES = new Set([
103
+ ];
104
+ /** 0.3.17 (S3.3, E-24): 'executing' = claimed, runner in flight — a fresh claim only takes
105
+ * 'pending', and resolve accepts 'pending'/'executing', so a crash mid-approve can never
106
+ * double-execute the runner. Same one-source rule as PENDING_KINDS. */
107
+ const PENDING_STATUSES = [
91
108
  "pending",
92
109
  "executing",
93
110
  "approved",
94
111
  "rejected"
95
- ]);
112
+ ];
113
+ /** Membership test for the write gate (per record per write, so the Set is worth it). */
114
+ const PENDING_KIND_SET = new Set(PENDING_KINDS);
115
+ /** Membership test for the write gate. */
116
+ const PENDING_STATUS_SET = new Set(PENDING_STATUSES);
96
117
  /**
97
118
  * The write gate for one record. Both providers call this before persisting,
98
119
  * so a record the other provider would refuse can never land.
@@ -118,12 +139,12 @@ function recordIssue(table, record) {
118
139
  return null;
119
140
  }
120
141
  if (typeof value.id !== "string") return "id must be a string";
121
- if (typeof value.kind !== "string" || !PENDING_KINDS.has(value.kind)) return "kind must be memory|skill|capability";
142
+ if (typeof value.kind !== "string" || !PENDING_KIND_SET.has(value.kind)) return `kind must be ${PENDING_KINDS.join("|")}`;
122
143
  if (typeof value.summary !== "string") return "summary must be a string";
123
144
  if (!Object.prototype.hasOwnProperty.call(value, "args")) return "args key is required (may be any cloneable value)";
124
145
  if (value.args === void 0) return "args must be a cloneable value — `undefined` is dropped by the json medium and the record would be unreadable after a restart (pass {} instead)";
125
146
  if (typeof value.createdAt !== "string") return "createdAt must be a string";
126
- if (typeof value.status !== "string" || !PENDING_STATUSES.has(value.status)) return "status must be pending|executing|approved|rejected";
147
+ if (typeof value.status !== "string" || !PENDING_STATUS_SET.has(value.status)) return `status must be ${PENDING_STATUSES.join("|")}`;
127
148
  for (const field of [
128
149
  "resolvedAt",
129
150
  "claimedBy",
@@ -185,8 +206,9 @@ function cloneRecord(record) {
185
206
  * the same public operations on both providers, so neither medium can keep the
186
207
  * bound to itself.
187
208
  */
188
- /** Build a fixture pending record, optionally carrying origin/sessionId. */
189
- const pendingOf = (id, kind = "memory", attribution = {}) => ({
209
+ /** Build a fixture pending record, optionally carrying origin/sessionId. The suite's own stamp and
210
+ * namespace come from {@link ConformanceOptions} — see `basePendingOf`'s call sites. */
211
+ const basePendingOf = (id, kind = "memory", attribution = {}) => ({
190
212
  id,
191
213
  kind,
192
214
  summary: `${kind}:${id}`,
@@ -213,13 +235,24 @@ const findRecord = (records, id) => {
213
235
  if (found === void 0) throw new Error(`conformance: pending record "${id}" missing from the listing`);
214
236
  return found;
215
237
  };
216
- async function runStateProviderConsistency(provider, assert) {
217
- await provider.saveReviewState("s-consistent", {
238
+ async function runStateProviderConsistency(provider, assert, options = {}) {
239
+ const idPrefix = options.idPrefix ?? "c-";
240
+ const claimPrefix = options.claimPrefix ?? "claim-";
241
+ const stamp = options.createdAt ?? "now";
242
+ const sessionId = options.sessionId ?? "s-consistent";
243
+ const attributionSessionId = options.attributionSessionId ?? "sess-attrib";
244
+ const reviewSessionPrefix = options.reviewSessionPrefix ?? "s-cap-";
245
+ /** This run's fixture record: the module helper's fields under THIS run's namespace and stamp. */
246
+ const pending = (id, kind = "memory", attribution = {}) => ({
247
+ ...basePendingOf(id, kind, attribution),
248
+ createdAt: stamp
249
+ });
250
+ await provider.saveReviewState(sessionId, {
218
251
  turnsSinceMemory: 1,
219
252
  turnsSinceSkill: 2,
220
253
  lastTurn: 3
221
254
  });
222
- assert(await provider.loadReviewState("s-consistent")).toEqual({
255
+ assert(await provider.loadReviewState(sessionId)).toEqual({
223
256
  turnsSinceMemory: 1,
224
257
  turnsSinceSkill: 2,
225
258
  lastTurn: 3
@@ -267,92 +300,92 @@ async function runStateProviderConsistency(provider, assert) {
267
300
  lastSummary: "updated",
268
301
  paused: false
269
302
  });
270
- const live = pendingOf("c-live");
303
+ const live = pending(`${idPrefix}live`);
271
304
  await provider.savePending(live);
272
- assert((await provider.listPending("pending")).map((record) => record.id)).toContain("c-live");
273
- const claimed = await provider.claimPending("c-live", "claim-a");
305
+ assert((await provider.listPending("pending")).map((record) => record.id)).toContain(`${idPrefix}live`);
306
+ const claimed = await provider.claimPending(`${idPrefix}live`, `${claimPrefix}a`);
274
307
  assert(claimed?.status).toBe("executing");
275
- assert(claimed?.claimedBy).toBe("claim-a");
308
+ assert(claimed?.claimedBy).toBe(`${claimPrefix}a`);
276
309
  expectPendingCarried(assert, claimed, live);
277
310
  assert(typeof claimed?.claimedAt).toBe("string");
278
- const resolved = await provider.tryResolvePending("c-live", "approved");
311
+ const resolved = await provider.tryResolvePending(`${idPrefix}live`, "approved");
279
312
  assert(resolved.applied).toBe(true);
280
- assert(resolved.record?.id).toBe("c-live");
313
+ assert(resolved.record?.id).toBe(`${idPrefix}live`);
281
314
  assert(resolved.record?.status).toBe("approved");
282
315
  expectPendingCarried(assert, resolved.record, live);
283
- assert(resolved.record?.claimedBy).toBe("claim-a");
316
+ assert(resolved.record?.claimedBy).toBe(`${claimPrefix}a`);
284
317
  assert(typeof resolved.record?.claimedAt).toBe("string");
285
318
  assert(typeof resolved.record?.resolvedAt).toBe("string");
286
- assert((await provider.listPending("approved")).map((record) => record.id)).toContain("c-live");
287
- assert((await provider.listPending("pending")).map((record) => record.id)).not.toContain("c-live");
288
- const attrib = pendingOf("c-attrib", "memory", {
319
+ assert((await provider.listPending("approved")).map((record) => record.id)).toContain(`${idPrefix}live`);
320
+ assert((await provider.listPending("pending")).map((record) => record.id)).not.toContain(`${idPrefix}live`);
321
+ const attrib = pending(`${idPrefix}attrib`, "memory", {
289
322
  origin: "background_review",
290
- sessionId: "sess-attrib"
323
+ sessionId: attributionSessionId
291
324
  });
292
325
  await provider.savePending(attrib);
293
- expectPendingCarried(assert, await provider.claimPending("c-attrib", "claim-a"), attrib);
294
- const attribResolved = await provider.tryResolvePending("c-attrib", "approved");
326
+ expectPendingCarried(assert, await provider.claimPending(`${idPrefix}attrib`, `${claimPrefix}a`), attrib);
327
+ const attribResolved = await provider.tryResolvePending(`${idPrefix}attrib`, "approved");
295
328
  expectPendingCarried(assert, attribResolved.record, attrib);
296
329
  assert(attribResolved.record?.origin).toBe("background_review");
297
- assert(attribResolved.record?.sessionId).toBe("sess-attrib");
330
+ assert(attribResolved.record?.sessionId).toBe(attributionSessionId);
298
331
  assert(typeof attribResolved.record?.resolvedAt).toBe("string");
299
- const rel = pendingOf("c-release", "skill");
332
+ const rel = pending(`${idPrefix}release`, "skill");
300
333
  await provider.savePending(rel);
301
- assert((await provider.claimPending("c-release", "claim-b"))?.status).toBe("executing");
302
- await provider.releasePendingClaim("c-release", "claim-b");
303
- const released = (await provider.listPending("pending")).find((record) => record.id === "c-release");
334
+ assert((await provider.claimPending(`${idPrefix}release`, `${claimPrefix}b`))?.status).toBe("executing");
335
+ await provider.releasePendingClaim(`${idPrefix}release`, `${claimPrefix}b`);
336
+ const released = (await provider.listPending("pending")).find((record) => record.id === `${idPrefix}release`);
304
337
  assert(released?.status).toBe("pending");
305
338
  expectPendingCarried(assert, released, rel);
306
339
  assert(released?.claimedBy).toBeUndefined();
307
340
  assert(released?.claimedAt).toBeUndefined();
308
- const scoped = pendingOf("c-scoped");
341
+ const scoped = pending(`${idPrefix}scoped`);
309
342
  await provider.savePending(scoped);
310
- await provider.claimPending("c-scoped", "claim-owner");
311
- assert((await provider.tryResolvePending("c-scoped", "approved", "claim-foreign")).applied).toBe(false);
312
- assert((await provider.listPending("executing")).map((record) => record.id)).toContain("c-scoped");
313
- const ownerScope = await provider.tryResolvePending("c-scoped", "approved", "claim-owner");
343
+ await provider.claimPending(`${idPrefix}scoped`, `${claimPrefix}owner`);
344
+ assert((await provider.tryResolvePending(`${idPrefix}scoped`, "approved", `${claimPrefix}foreign`)).applied).toBe(false);
345
+ assert((await provider.listPending("executing")).map((record) => record.id)).toContain(`${idPrefix}scoped`);
346
+ const ownerScope = await provider.tryResolvePending(`${idPrefix}scoped`, "approved", `${claimPrefix}owner`);
314
347
  assert(ownerScope.applied).toBe(true);
315
348
  assert(ownerScope.record?.status).toBe("approved");
316
- const filt = pendingOf("c-filter");
349
+ const filt = pending(`${idPrefix}filter`);
317
350
  await provider.savePending(filt);
318
- assert((await provider.listPending("pending")).map((record) => record.id)).toContain("c-filter");
319
- assert((await provider.listPending("approved")).map((record) => record.id)).not.toContain("c-filter");
320
- const post = pendingOf("c-post-resolve", "memory");
351
+ assert((await provider.listPending("pending")).map((record) => record.id)).toContain(`${idPrefix}filter`);
352
+ assert((await provider.listPending("approved")).map((record) => record.id)).not.toContain(`${idPrefix}filter`);
353
+ const post = pending(`${idPrefix}post-resolve`, "memory");
321
354
  await provider.savePending(post);
322
- await provider.claimPending("c-post-resolve", "claim-c");
323
- await provider.tryResolvePending("c-post-resolve", "rejected");
324
- await provider.releasePendingClaim("c-post-resolve", "claim-c");
325
- const postResolved = (await provider.listPending("rejected")).find((record) => record.id === "c-post-resolve");
355
+ await provider.claimPending(`${idPrefix}post-resolve`, `${claimPrefix}c`);
356
+ await provider.tryResolvePending(`${idPrefix}post-resolve`, "rejected");
357
+ await provider.releasePendingClaim(`${idPrefix}post-resolve`, `${claimPrefix}c`);
358
+ const postResolved = (await provider.listPending("rejected")).find((record) => record.id === `${idPrefix}post-resolve`);
326
359
  assert(postResolved?.status).toBe("rejected");
327
- assert(postResolved?.claimedBy).toBe("claim-c");
360
+ assert(postResolved?.claimedBy).toBe(`${claimPrefix}c`);
328
361
  assert(typeof postResolved?.claimedAt).toBe("string");
329
362
  assert(typeof postResolved?.resolvedAt).toBe("string");
330
363
  await assert(provider.savePending({
331
- id: "c-bad",
364
+ id: `${idPrefix}bad`,
332
365
  kind: "memory",
333
366
  summary: "bad",
334
- createdAt: "now",
367
+ createdAt: stamp,
335
368
  status: "pending"
336
369
  })).rejects.toThrow();
337
- const poison = pendingOf("c-poison");
370
+ const poison = pending(`${idPrefix}poison`);
338
371
  poison.args = { nested: { value: 1 } };
339
372
  await provider.savePending(poison);
340
- const returned = findRecord(await provider.listPending("pending"), "c-poison");
373
+ const returned = findRecord(await provider.listPending("pending"), `${idPrefix}poison`);
341
374
  returned.summary = "poisoned";
342
375
  returned.args.nested.value = 99;
343
- const reread = findRecord(await provider.listPending("pending"), "c-poison");
344
- assert(reread.summary).toBe("memory:c-poison");
376
+ const reread = findRecord(await provider.listPending("pending"), `${idPrefix}poison`);
377
+ assert(reread.summary).toBe(`memory:${idPrefix}poison`);
345
378
  assert(reread.args.nested.value).toBe(1);
346
379
  const uncloneable = {
347
- ...pendingOf("c-uncloneable"),
380
+ ...pending(`${idPrefix}uncloneable`),
348
381
  args: { fn: () => {} }
349
382
  };
350
383
  await assert(provider.savePending(uncloneable)).rejects.toThrow();
351
- const aliased = pendingOf("c-alias");
384
+ const aliased = pending(`${idPrefix}alias`);
352
385
  aliased.args = { nested: { value: 1 } };
353
386
  await provider.savePending(aliased);
354
387
  aliased.args.nested.value = 99;
355
- assert(findRecord(await provider.listPending("pending"), "c-alias").args.nested.value).toBe(1);
388
+ assert(findRecord(await provider.listPending("pending"), `${idPrefix}alias`).args.nested.value).toBe(1);
356
389
  await assert(provider.saveCuratorState({
357
390
  lastRunAt: 1,
358
391
  runCount: 0,
@@ -361,37 +394,37 @@ async function runStateProviderConsistency(provider, assert) {
361
394
  schemaVersion: -1
362
395
  })).rejects.toThrow();
363
396
  await provider.savePending({
364
- ...pendingOf("c-extra"),
397
+ ...pending(`${idPrefix}extra`),
365
398
  extraField: "kept"
366
399
  });
367
- assert((await provider.listPending("pending")).find((record) => record.id === "c-extra").extraField).toBe("kept");
368
- const capPrefix = "c-cap-";
369
- await provider.savePending(pendingOf("c-cap-live", "skill"));
400
+ assert((await provider.listPending("pending")).find((record) => record.id === `${idPrefix}extra`).extraField).toBe("kept");
401
+ const capPrefix = `${idPrefix}cap-`;
402
+ await provider.savePending(pending(`${idPrefix}cap-live`, "skill"));
370
403
  for (let index = 0; index <= 201; index += 1) {
371
404
  const id = capPrefix + String(index).padStart(3, "0");
372
- await provider.savePending(pendingOf(id));
373
- await provider.claimPending(id, "claim-cap");
405
+ await provider.savePending(pending(id));
406
+ await provider.claimPending(id, `${claimPrefix}cap`);
374
407
  await provider.tryResolvePending(id, "approved");
375
408
  }
376
409
  const resolvedNow = (await provider.listPending("approved")).concat(await provider.listPending("rejected")).filter((record) => record.kind !== "capability");
377
410
  assert(resolvedNow).toHaveLength(200);
378
411
  const resolvedIds = resolvedNow.map((record) => record.id);
379
- assert(resolvedIds).not.toContain("c-cap-000");
380
- assert(resolvedIds).not.toContain("c-cap-001");
412
+ assert(resolvedIds).not.toContain(capPrefix + "000");
413
+ assert(resolvedIds).not.toContain(capPrefix + "001");
381
414
  assert(resolvedIds).toContain(capPrefix + String(201).padStart(3, "0"));
382
- assert((await provider.listPending("pending")).map((record) => record.id)).toContain("c-cap-live");
383
- const sessionPrefix = "s-cap-";
415
+ assert((await provider.listPending("pending")).map((record) => record.id)).toContain(`${idPrefix}cap-live`);
416
+ const sessionPrefix = reviewSessionPrefix;
384
417
  const reviewStateOf = (turns) => ({
385
418
  turnsSinceMemory: turns,
386
419
  turnsSinceSkill: 0,
387
420
  lastTurn: turns
388
421
  });
389
422
  for (let index = 0; index <= 500; index += 1) await provider.saveReviewState(sessionPrefix + String(index), reviewStateOf(index));
390
- assert(await provider.loadReviewState("s-cap-0")).toBeNull();
391
- assert(await provider.loadReviewState("s-cap-1")).toEqual(reviewStateOf(1));
423
+ assert(await provider.loadReviewState(sessionPrefix + "0")).toBeNull();
424
+ assert(await provider.loadReviewState(sessionPrefix + "1")).toEqual(reviewStateOf(1));
392
425
  assert(await provider.loadReviewState(sessionPrefix + String(500))).toEqual(reviewStateOf(500));
393
- await provider.saveReviewState("s-cap-0", reviewStateOf(999));
394
- assert(await provider.loadReviewState("s-cap-0")).toEqual(reviewStateOf(999));
426
+ await provider.saveReviewState(sessionPrefix + "0", reviewStateOf(999));
427
+ assert(await provider.loadReviewState(sessionPrefix + "0")).toEqual(reviewStateOf(999));
395
428
  }
396
429
  //#endregion
397
430
  //#region lib/types/index.js
@@ -520,4 +553,4 @@ var EvolutionStateStorageRegistry = class extends Service {
520
553
  }
521
554
  };
522
555
  //#endregion
523
- export { CURATOR_STATE_FILE, CURATOR_STATE_KEY, CURATOR_STATE_TABLE, EvolutionStateStorageRegistry, EvolutionStateStorageRegistry as default, PENDING_ARCHIVE_BAK_FILE, PENDING_ARCHIVE_FILE, PENDING_LEGACY_FILE, PENDING_RESOLVED_CAP, PENDING_STATE_FILE, PENDING_TABLE, PROVIDER_DOMAIN, PROVIDER_JSON, REVIEW_STATE_FILE, REVIEW_STATE_SESSION_CAP, REVIEW_STATE_TABLE, UNKNOWN_FIELD_POLICY, assertCloneable, canClaimPending, canResolvePending, cloneRecord, recordIssue, releasedStatus, runStateProviderConsistency, selectPendingOverflow, selectSessionOverflow };
556
+ export { CURATOR_STATE_FILE, CURATOR_STATE_KEY, CURATOR_STATE_TABLE, EvolutionStateStorageRegistry, EvolutionStateStorageRegistry as default, PENDING_ARCHIVE_BAK_FILE, PENDING_ARCHIVE_FILE, PENDING_KINDS, PENDING_LEGACY_FILE, PENDING_RESOLVED_CAP, PENDING_STATE_FILE, PENDING_STATUSES, PENDING_TABLE, PROVIDER_DOMAIN, PROVIDER_JSON, REVIEW_STATE_FILE, REVIEW_STATE_SESSION_CAP, REVIEW_STATE_TABLE, UNKNOWN_FIELD_POLICY, assertCloneable, canClaimPending, canResolvePending, cloneRecord, recordIssue, releasedStatus, runStateProviderConsistency, selectPendingOverflow, selectSessionOverflow };
@@ -23,5 +23,25 @@ export interface ConformanceAssert {
23
23
  };
24
24
  };
25
25
  }
26
- export declare function runStateProviderConsistency(provider: EvolutionStateStorage, assert: ConformanceAssert): Promise<void>;
26
+ /**
27
+ * The values a PROVIDER UNDER TEST may need to vary (U-2): a third-party medium can already hold
28
+ * records in the `c-…` namespace, and a strict schema may insist on a parseable `createdAt`. Every
29
+ * field defaults to the in-tree literals, so an existing caller's two-argument call is unchanged.
30
+ */
31
+ export interface ConformanceOptions {
32
+ /** Namespace for every pending record this run creates (default `'c-'`). */
33
+ idPrefix?: string;
34
+ /** Namespace for every claim id (default `'claim-'`). */
35
+ claimPrefix?: string;
36
+ /** The `createdAt` stamp every fixture record carries (default `'now'`). A provider whose
37
+ * schema requires a parseable time passes a real stamp here. */
38
+ createdAt?: string;
39
+ /** The review-state session the round-trip vector writes (default `'s-consistent'`). */
40
+ sessionId?: string;
41
+ /** The review-state session the ATTRIBUTION vector pins (default `attributionSessionId`). */
42
+ attributionSessionId?: string;
43
+ /** Namespace for the per-session cap vector's sessions (default `'s-cap-'`). */
44
+ reviewSessionPrefix?: string;
45
+ }
46
+ export declare function runStateProviderConsistency(provider: EvolutionStateStorage, assert: ConformanceAssert, options?: ConformanceOptions): Promise<void>;
27
47
  //# sourceMappingURL=conformance.d.ts.map
@@ -7,26 +7,10 @@
7
7
  * @module @lmzhen/dsh-evolution-state-storage
8
8
  */
9
9
  import { Context, Service } from '@deepseek-ai/cordis';
10
+ import type { PendingKind, PendingStatus } from './record-contract.ts';
10
11
  export * from './constants.ts';
11
12
  export * from './record-contract.ts';
12
13
  export * from './conformance.ts';
13
- /** 0.3.17 (S3.5, D-4): 'skill_batch' removed — nothing ever created one
14
- * (dead enum member); the historic value, if it ever reached disk, is read as
15
- * an unknown kind by consumers rather than minted here.
16
- *
17
- * 0.3.66: 'capability' is retained with NO producer — the evolution-capability
18
- * adapter was removed. It is a read-compatibility member: state written by an
19
- * install that used that adapter (≤0.3.65) still holds such records, and they
20
- * must keep loading, listing in `/evolution pending`, and answering approve or
21
- * reject. Dropping it would strand them two ways: json quarantines the row to
22
- * `<file>.corrupt` and refuses the resolving write, while the domain provider
23
- * validates every stored record at mount, so one such row fails the whole domain
24
- * with `invalid-record`. */
25
- export type PendingKind = 'memory' | 'skill' | 'capability';
26
- /** 0.3.17 (S3.3, E-24): 'executing' = claimed, runner in flight — a fresh
27
- * claim only takes 'pending', and resolve accepts 'pending'/'executing', so a
28
- * crash mid-approve can never double-execute the runner. */
29
- export type PendingStatus = 'pending' | 'executing' | 'approved' | 'rejected';
30
14
  /** 0.3.17 (S3.3): the claim lifecycle as ONE transition table — BOTH
31
15
  * providers (json/domain) must use these, never a hand-written copy (a second
32
16
  * copy is exactly the E-10 drift class). */
@@ -21,6 +21,29 @@ export type SeamRecordTable = typeof REVIEW_STATE_TABLE | typeof CURATOR_STATE_T
21
21
  /** Unknown fields survive a provider round-trip (json preserves by
22
22
  * construction; the domain schemas are `.loose()`). */
23
23
  export declare const UNKNOWN_FIELD_POLICY: "preserve";
24
+ /** 0.3.17 (S3.5, D-4): 'skill_batch' is gone — nothing ever created one (a dead enum member). A
25
+ * historic value, if it ever reached disk, is read as an unknown kind by consumers rather than
26
+ * minted here.
27
+ *
28
+ * 0.3.66: 'capability' stays with NO producer — the evolution-capability adapter was removed. It is
29
+ * a read-compatibility member: state written by an install that used that adapter (≤0.3.65) still
30
+ * holds such records, and they must keep loading, listing in `/evolution pending`, and answering
31
+ * approve or reject. Dropping it would strand them two ways: json quarantines the row to
32
+ * `<file>.corrupt` and refuses the resolving write, while the domain provider validates every
33
+ * stored record at mount, so one such row fails the whole domain with `invalid-record`.
34
+ *
35
+ * S2.9/1-10: this array is the ONE source of the vocabulary — the types, the write gate below and
36
+ * the domain provider's schema all derive from it. A second literal copy is the E-10 drift class
37
+ * this module exists to end. */
38
+ export declare const PENDING_KINDS: readonly ["memory", "skill", "capability"];
39
+ /** 0.3.17 (S3.3, E-24): 'executing' = claimed, runner in flight — a fresh claim only takes
40
+ * 'pending', and resolve accepts 'pending'/'executing', so a crash mid-approve can never
41
+ * double-execute the runner. Same one-source rule as PENDING_KINDS. */
42
+ export declare const PENDING_STATUSES: readonly ["pending", "executing", "approved", "rejected"];
43
+ /** The legal kind of one pending record. */
44
+ export type PendingKind = (typeof PENDING_KINDS)[number];
45
+ /** The legal status of one pending record. */
46
+ export type PendingStatus = (typeof PENDING_STATUSES)[number];
24
47
  /**
25
48
  * The write gate for one record. Both providers call this before persisting,
26
49
  * so a record the other provider would refuse can never land.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-state-storage",
3
3
  "description": "Provider registry seam for durable evolution state (community build)",
4
- "version": "0.17.1",
4
+ "version": "0.18.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },