@remnic/core 9.3.760 → 9.3.761

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.
Files changed (128) hide show
  1. package/dist/access-admin-ops-surface.d.ts +3 -3
  2. package/dist/access-admin-ops-surface.js +7 -7
  3. package/dist/access-boundary.d.ts +3 -3
  4. package/dist/access-boundary.js +8 -8
  5. package/dist/access-cli.js +28 -28
  6. package/dist/access-http.d.ts +3 -3
  7. package/dist/access-http.js +12 -12
  8. package/dist/access-identity-continuity-surface.d.ts +2 -2
  9. package/dist/access-identity-continuity-surface.js +7 -7
  10. package/dist/access-lcm-surface.d.ts +3 -3
  11. package/dist/access-lcm-surface.js +7 -7
  12. package/dist/access-mcp.d.ts +3 -3
  13. package/dist/access-mcp.js +11 -11
  14. package/dist/access-observe-write-surface.d.ts +3 -3
  15. package/dist/access-observe-write-surface.js +7 -7
  16. package/dist/access-operations-batch.js +9 -9
  17. package/dist/access-operations.d.ts +7 -7
  18. package/dist/access-operations.js +10 -10
  19. package/dist/access-recall-surface.d.ts +3 -3
  20. package/dist/access-recall-surface.js +7 -7
  21. package/dist/access-schema.d.ts +68 -68
  22. package/dist/{access-service-BW-gb3DL.d.ts → access-service-CaWJP_f6.d.ts} +2 -2
  23. package/dist/access-service.d.ts +3 -3
  24. package/dist/access-service.js +7 -7
  25. package/dist/access-surface-catalog.d.ts +3 -3
  26. package/dist/bootstrap.d.ts +2 -2
  27. package/dist/briefing.js +2 -2
  28. package/dist/{catalog-DaKAFbSJ.d.ts → catalog-bflYNWsM.d.ts} +8 -4
  29. package/dist/causal-consolidation.js +3 -3
  30. package/dist/{chunk-FYJWEGYP.js → chunk-2B3BTQN4.js} +2 -2
  31. package/dist/{chunk-IZLGHKWA.js → chunk-3CBSKDRL.js} +7 -7
  32. package/dist/{chunk-YP23FQPT.js → chunk-3DAIQSVS.js} +80 -36
  33. package/dist/chunk-3DAIQSVS.js.map +1 -0
  34. package/dist/{chunk-OLEBPUZ7.js → chunk-4WOXD56B.js} +5 -5
  35. package/dist/{chunk-QUGPXPFJ.js → chunk-5AZQZFJG.js} +5 -5
  36. package/dist/{chunk-DTSOB2YF.js → chunk-6S2C5FQ4.js} +2 -2
  37. package/dist/{chunk-WIXQ3F5P.js → chunk-742MWWVL.js} +2 -2
  38. package/dist/{chunk-W3M34XT4.js → chunk-BAVVI5GP.js} +2 -2
  39. package/dist/{chunk-QOM3F6YW.js → chunk-CFHDI5CV.js} +5 -5
  40. package/dist/{chunk-IOZZG75J.js → chunk-DUU4VLOW.js} +2 -2
  41. package/dist/{chunk-ZFXCQPNO.js → chunk-EGGE52UU.js} +2 -2
  42. package/dist/chunk-EGGE52UU.js.map +1 -0
  43. package/dist/{chunk-LLVKDFNU.js → chunk-GYJESQS5.js} +2 -2
  44. package/dist/{chunk-EGQ3AVZV.js → chunk-HHCMZEUC.js} +3 -3
  45. package/dist/{chunk-6CUUFFU7.js → chunk-HP23UD6W.js} +131 -39
  46. package/dist/chunk-HP23UD6W.js.map +1 -0
  47. package/dist/{chunk-XQT5GSWU.js → chunk-ICVNVSKN.js} +2 -2
  48. package/dist/{chunk-2XE4F3VZ.js → chunk-IRFHFRHP.js} +12 -12
  49. package/dist/{chunk-ILVF6TIY.js → chunk-IV4INTAU.js} +2 -2
  50. package/dist/{chunk-AZSSKOG6.js → chunk-JYH3XYWM.js} +3 -3
  51. package/dist/{chunk-3JBW3GHN.js → chunk-LG2MCUBC.js} +2 -2
  52. package/dist/{chunk-SLAZZXBU.js → chunk-NPZTEHR7.js} +2 -2
  53. package/dist/{chunk-PKRRBRY2.js → chunk-OCPVA4TH.js} +2 -2
  54. package/dist/{chunk-M2AAYJVI.js → chunk-P7N2DQLC.js} +4 -4
  55. package/dist/{chunk-SN2EWIAI.js → chunk-P7UA66OG.js} +2 -2
  56. package/dist/{chunk-SMLMH7S5.js → chunk-W4ADKEIY.js} +16 -16
  57. package/dist/{chunk-P26O6R3Q.js → chunk-XDFWMY4X.js} +3 -3
  58. package/dist/{chunk-QQJMVF6P.js → chunk-Y3XEN5TL.js} +3 -3
  59. package/dist/{chunk-ZEQVS6AZ.js → chunk-YU4CLTZY.js} +2 -2
  60. package/dist/{chunk-7HWOEYHW.js → chunk-ZAIU7ZEA.js} +2 -2
  61. package/dist/{cli-D551dnva.d.ts → cli-CvWpIbMD.d.ts} +2 -2
  62. package/dist/cli.d.ts +4 -4
  63. package/dist/cli.js +22 -22
  64. package/dist/compounding/engine.js +2 -2
  65. package/dist/connectors/codex-materialize-runner.js +2 -2
  66. package/dist/connectors/index.js +2 -2
  67. package/dist/entity-retrieval.js +2 -2
  68. package/dist/explicit-capture.d.ts +2 -2
  69. package/dist/index.d.ts +5 -5
  70. package/dist/index.js +28 -28
  71. package/dist/maintenance/memory-governance.js +2 -2
  72. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +2 -2
  73. package/dist/maintenance/rebuild-memory-projection.js +3 -3
  74. package/dist/mcp-memory-inspector-app.d.ts +3 -3
  75. package/dist/namespaces/migrate.d.ts +1 -1
  76. package/dist/namespaces/migrate.js +5 -5
  77. package/dist/namespaces/search.js +2 -2
  78. package/dist/namespaces/storage.d.ts +1 -1
  79. package/dist/namespaces/storage.js +3 -3
  80. package/dist/operator-toolkit.js +9 -9
  81. package/dist/orchestration/maintenance.d.ts +1 -1
  82. package/dist/orchestration/maintenance.js +6 -6
  83. package/dist/{orchestrator-BnM187cb.d.ts → orchestrator-C04Q4Zao.d.ts} +1 -1
  84. package/dist/orchestrator.d.ts +2 -2
  85. package/dist/orchestrator.js +28 -28
  86. package/dist/schemas.d.ts +62 -62
  87. package/dist/semantic-consolidation.js +3 -3
  88. package/dist/semantic-rule-promotion.js +2 -2
  89. package/dist/semantic-rule-verifier.js +2 -2
  90. package/dist/shared-context/manager.d.ts +8 -8
  91. package/dist/storage.js +1 -1
  92. package/dist/transfer/types.d.ts +66 -66
  93. package/dist/verified-recall.js +2 -2
  94. package/package.json +2 -2
  95. package/src/namespaces/catalog.test.ts +133 -1
  96. package/src/namespaces/catalog.ts +163 -48
  97. package/src/namespaces/identity.ts +2 -2
  98. package/src/storage/secure-line-reader.ts +79 -0
  99. package/src/storage-memory-lifecycle-ledger.test.ts +130 -0
  100. package/src/storage.ts +16 -33
  101. package/dist/chunk-6CUUFFU7.js.map +0 -1
  102. package/dist/chunk-YP23FQPT.js.map +0 -1
  103. package/dist/chunk-ZFXCQPNO.js.map +0 -1
  104. /package/dist/{chunk-FYJWEGYP.js.map → chunk-2B3BTQN4.js.map} +0 -0
  105. /package/dist/{chunk-IZLGHKWA.js.map → chunk-3CBSKDRL.js.map} +0 -0
  106. /package/dist/{chunk-OLEBPUZ7.js.map → chunk-4WOXD56B.js.map} +0 -0
  107. /package/dist/{chunk-QUGPXPFJ.js.map → chunk-5AZQZFJG.js.map} +0 -0
  108. /package/dist/{chunk-DTSOB2YF.js.map → chunk-6S2C5FQ4.js.map} +0 -0
  109. /package/dist/{chunk-WIXQ3F5P.js.map → chunk-742MWWVL.js.map} +0 -0
  110. /package/dist/{chunk-W3M34XT4.js.map → chunk-BAVVI5GP.js.map} +0 -0
  111. /package/dist/{chunk-QOM3F6YW.js.map → chunk-CFHDI5CV.js.map} +0 -0
  112. /package/dist/{chunk-IOZZG75J.js.map → chunk-DUU4VLOW.js.map} +0 -0
  113. /package/dist/{chunk-LLVKDFNU.js.map → chunk-GYJESQS5.js.map} +0 -0
  114. /package/dist/{chunk-EGQ3AVZV.js.map → chunk-HHCMZEUC.js.map} +0 -0
  115. /package/dist/{chunk-XQT5GSWU.js.map → chunk-ICVNVSKN.js.map} +0 -0
  116. /package/dist/{chunk-2XE4F3VZ.js.map → chunk-IRFHFRHP.js.map} +0 -0
  117. /package/dist/{chunk-ILVF6TIY.js.map → chunk-IV4INTAU.js.map} +0 -0
  118. /package/dist/{chunk-AZSSKOG6.js.map → chunk-JYH3XYWM.js.map} +0 -0
  119. /package/dist/{chunk-3JBW3GHN.js.map → chunk-LG2MCUBC.js.map} +0 -0
  120. /package/dist/{chunk-SLAZZXBU.js.map → chunk-NPZTEHR7.js.map} +0 -0
  121. /package/dist/{chunk-PKRRBRY2.js.map → chunk-OCPVA4TH.js.map} +0 -0
  122. /package/dist/{chunk-M2AAYJVI.js.map → chunk-P7N2DQLC.js.map} +0 -0
  123. /package/dist/{chunk-SN2EWIAI.js.map → chunk-P7UA66OG.js.map} +0 -0
  124. /package/dist/{chunk-SMLMH7S5.js.map → chunk-W4ADKEIY.js.map} +0 -0
  125. /package/dist/{chunk-P26O6R3Q.js.map → chunk-XDFWMY4X.js.map} +0 -0
  126. /package/dist/{chunk-QQJMVF6P.js.map → chunk-Y3XEN5TL.js.map} +0 -0
  127. /package/dist/{chunk-ZEQVS6AZ.js.map → chunk-YU4CLTZY.js.map} +0 -0
  128. /package/dist/{chunk-7HWOEYHW.js.map → chunk-ZAIU7ZEA.js.map} +0 -0
@@ -11,23 +11,23 @@ import {
11
11
  processChatMessage,
12
12
  sessionBelongsToPrincipal,
13
13
  subscribeChatTranscript
14
- } from "./chunk-AZSSKOG6.js";
14
+ } from "./chunk-JYH3XYWM.js";
15
15
  import {
16
16
  validateRequest
17
17
  } from "./chunk-2MR3MFQB.js";
18
18
  import {
19
19
  getOperation
20
- } from "./chunk-3JBW3GHN.js";
20
+ } from "./chunk-LG2MCUBC.js";
21
21
  import {
22
22
  CorrectionContractError,
23
23
  EngramAccessInputError
24
- } from "./chunk-IZLGHKWA.js";
24
+ } from "./chunk-3CBSKDRL.js";
25
25
  import {
26
26
  isTrustZoneName
27
27
  } from "./chunk-JGSKJHF7.js";
28
28
  import {
29
29
  projectTagProjectId
30
- } from "./chunk-W3M34XT4.js";
30
+ } from "./chunk-BAVVI5GP.js";
31
31
  import {
32
32
  subscribeGraphEvents
33
33
  } from "./chunk-DEUNUKTD.js";
@@ -2627,4 +2627,4 @@ function positiveIntQueryParam(value, label) {
2627
2627
  export {
2628
2628
  EngramAccessHttpServer
2629
2629
  };
2630
- //# sourceMappingURL=chunk-OLEBPUZ7.js.map
2630
+ //# sourceMappingURL=chunk-4WOXD56B.js.map
@@ -3,10 +3,10 @@ import {
3
3
  readNamespaceMaintenanceLastRanStatuses,
4
4
  readNamespaceMaintenanceStatuses,
5
5
  runNamespaceMaintenancePlan
6
- } from "./chunk-6CUUFFU7.js";
6
+ } from "./chunk-HP23UD6W.js";
7
7
  import {
8
8
  listNamespaces
9
- } from "./chunk-M2AAYJVI.js";
9
+ } from "./chunk-P7N2DQLC.js";
10
10
  import {
11
11
  analyzeSessionIntegrity,
12
12
  applySessionRepair,
@@ -42,13 +42,13 @@ import {
42
42
  import {
43
43
  listMemoryGovernanceRuns,
44
44
  readMemoryGovernanceRunArtifact
45
- } from "./chunk-SN2EWIAI.js";
45
+ } from "./chunk-P7UA66OG.js";
46
46
  import {
47
47
  analyzeGraphHealth
48
48
  } from "./chunk-EHQ5HO5T.js";
49
49
  import {
50
50
  StorageManager
51
- } from "./chunk-YP23FQPT.js";
51
+ } from "./chunk-3DAIQSVS.js";
52
52
  import {
53
53
  lintWorkspaceFiles
54
54
  } from "./chunk-DM2T26WE.js";
@@ -1599,4 +1599,4 @@ export {
1599
1599
  runBenchmarkRecall,
1600
1600
  runOperatorRepair
1601
1601
  };
1602
- //# sourceMappingURL=chunk-QUGPXPFJ.js.map
1602
+ //# sourceMappingURL=chunk-5AZQZFJG.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  StorageManager,
3
3
  normalizeEntityName
4
- } from "./chunk-YP23FQPT.js";
4
+ } from "./chunk-3DAIQSVS.js";
5
5
  import {
6
6
  serializeMutations
7
7
  } from "./chunk-LTJAMRGI.js";
@@ -956,4 +956,4 @@ export {
956
956
  resolveBriefingSaveDir,
957
957
  briefingFilename
958
958
  };
959
- //# sourceMappingURL=chunk-DTSOB2YF.js.map
959
+ //# sourceMappingURL=chunk-6S2C5FQ4.js.map
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-3UXOZBHV.js";
8
8
  import {
9
9
  StorageManager
10
- } from "./chunk-YP23FQPT.js";
10
+ } from "./chunk-3DAIQSVS.js";
11
11
  import {
12
12
  isSafeRouteNamespace
13
13
  } from "./chunk-U3PN77QT.js";
@@ -118,4 +118,4 @@ export {
118
118
  runCodexMaterialize,
119
119
  runPostConsolidationMaterialize
120
120
  };
121
- //# sourceMappingURL=chunk-WIXQ3F5P.js.map
121
+ //# sourceMappingURL=chunk-742MWWVL.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  namespaceIdentityFromToken,
3
3
  namespaceIdentityToken
4
- } from "./chunk-ZFXCQPNO.js";
4
+ } from "./chunk-EGGE52UU.js";
5
5
  import {
6
6
  launchProcessSync
7
7
  } from "./chunk-O75CRYGF.js";
@@ -772,4 +772,4 @@ export {
772
772
  resolveNamespaceFromStorageDir,
773
773
  resolveWritableNamespaceValue
774
774
  };
775
- //# sourceMappingURL=chunk-W3M34XT4.js.map
775
+ //# sourceMappingURL=chunk-BAVVI5GP.js.map
@@ -21,16 +21,16 @@ import {
21
21
  } from "./chunk-T4WDJPEZ.js";
22
22
  import {
23
23
  defineOperation
24
- } from "./chunk-3JBW3GHN.js";
24
+ } from "./chunk-LG2MCUBC.js";
25
25
  import {
26
26
  EngramAccessInputError
27
- } from "./chunk-IZLGHKWA.js";
27
+ } from "./chunk-3CBSKDRL.js";
28
28
  import {
29
29
  projectTagProjectId
30
- } from "./chunk-W3M34XT4.js";
30
+ } from "./chunk-BAVVI5GP.js";
31
31
  import {
32
32
  validateBriefingFormat
33
- } from "./chunk-DTSOB2YF.js";
33
+ } from "./chunk-6S2C5FQ4.js";
34
34
  import {
35
35
  resolvePrincipal
36
36
  } from "./chunk-PE6XR4GG.js";
@@ -417,4 +417,4 @@ defineOperation({ name: "offline_sync_snapshot_stream", description: "SSE stream
417
417
  defineOperation({ name: "graph_events", description: "SSE graph events.", schema: strictSchema({}), handler: async (_i, _ctx) => ({ result: { stream: "sse" } }) });
418
418
  defineOperation({ name: "chat_message", description: "Chat message.", schema: strictSchema({ message: S.str, chatSessionId: S.str }), handler: async (_i, _ctx) => ({ result: { ok: true } }) });
419
419
  defineOperation({ name: "chat_events", description: "SSE chat events.", schema: strictSchema({}), handler: async (_i, _ctx) => ({ result: { stream: "sse" } }) });
420
- //# sourceMappingURL=chunk-QOM3F6YW.js.map
420
+ //# sourceMappingURL=chunk-CFHDI5CV.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  StorageManager
3
- } from "./chunk-YP23FQPT.js";
3
+ } from "./chunk-3DAIQSVS.js";
4
4
  import {
5
5
  getCachedRuleMemories,
6
6
  setCachedRuleMemories
@@ -117,4 +117,4 @@ export {
117
117
  compareVerifiedSemanticRuleResults,
118
118
  searchVerifiedSemanticRules
119
119
  };
120
- //# sourceMappingURL=chunk-IOZZG75J.js.map
120
+ //# sourceMappingURL=chunk-DUU4VLOW.js.map
@@ -1,6 +1,6 @@
1
1
  // src/namespaces/identity.ts
2
2
  function normalizeNamespaceIdentity(namespace) {
3
- return namespace.trim();
3
+ return namespace?.trim() ?? "";
4
4
  }
5
5
  function namespaceIdentityToken(namespace) {
6
6
  const normalized = normalizeNamespaceIdentity(namespace);
@@ -24,4 +24,4 @@ export {
24
24
  namespaceIdentityToken,
25
25
  namespaceIdentityFromToken
26
26
  };
27
- //# sourceMappingURL=chunk-ZFXCQPNO.js.map
27
+ //# sourceMappingURL=chunk-EGGE52UU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/namespaces/identity.ts"],"sourcesContent":["export function normalizeNamespaceIdentity(namespace: string | null | undefined): string {\n return namespace?.trim() ?? \"\";\n}\n\nexport function namespaceIdentityToken(namespace: string): string {\n const normalized = normalizeNamespaceIdentity(namespace);\n const bytes = new TextEncoder().encode(normalized);\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n return `ns-${hex || \"default\"}`;\n}\n\nexport function namespaceIdentityFromToken(token: string): string | null {\n if (!token.startsWith(\"ns-\")) return null;\n const hex = token.slice(3);\n if (hex === \"default\") return \"\";\n if (hex.length === 0 || hex.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(hex)) {\n return null;\n }\n const decoded = Buffer.from(hex, \"hex\").toString(\"utf8\");\n return namespaceIdentityToken(decoded).toLowerCase() === token.toLowerCase()\n ? decoded\n : null;\n}\n"],"mappings":";AAAO,SAAS,2BAA2B,WAA8C;AACvF,SAAO,WAAW,KAAK,KAAK;AAC9B;AAEO,SAAS,uBAAuB,WAA2B;AAChE,QAAM,aAAa,2BAA2B,SAAS;AACvD,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,UAAU;AACjD,QAAM,MAAM,MAAM,KAAK,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AACnF,SAAO,MAAM,OAAO,SAAS;AAC/B;AAEO,SAAS,2BAA2B,OAA8B;AACvE,MAAI,CAAC,MAAM,WAAW,KAAK,EAAG,QAAO;AACrC,QAAM,MAAM,MAAM,MAAM,CAAC;AACzB,MAAI,QAAQ,UAAW,QAAO;AAC9B,MAAI,IAAI,WAAW,KAAK,IAAI,SAAS,MAAM,KAAK,CAAC,eAAe,KAAK,GAAG,GAAG;AACzE,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,KAAK,KAAK,KAAK,EAAE,SAAS,MAAM;AACvD,SAAO,uBAAuB,OAAO,EAAE,YAAY,MAAM,MAAM,YAAY,IACvE,UACA;AACN;","names":[]}
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-SFQ6QNL7.js";
7
7
  import {
8
8
  StorageManager
9
- } from "./chunk-YP23FQPT.js";
9
+ } from "./chunk-3DAIQSVS.js";
10
10
  import {
11
11
  buildLifecycleEventsForMemory,
12
12
  sortMemoryLifecycleEvents
@@ -74,4 +74,4 @@ export {
74
74
  backupExistingLedger,
75
75
  rebuildMemoryLifecycleLedger
76
76
  };
77
- //# sourceMappingURL=chunk-LLVKDFNU.js.map
77
+ //# sourceMappingURL=chunk-GYJESQS5.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  namespaceIdentityToken,
3
3
  normalizeNamespaceIdentity
4
- } from "./chunk-ZFXCQPNO.js";
4
+ } from "./chunk-EGGE52UU.js";
5
5
  import {
6
6
  StorageManager
7
- } from "./chunk-YP23FQPT.js";
7
+ } from "./chunk-3DAIQSVS.js";
8
8
  import {
9
9
  MutationSerializer
10
10
  } from "./chunk-LTJAMRGI.js";
@@ -339,4 +339,4 @@ export {
339
339
  resolveNamespaceStorageRoot,
340
340
  NamespaceStorageRouter
341
341
  };
342
- //# sourceMappingURL=chunk-EGQ3AVZV.js.map
342
+ //# sourceMappingURL=chunk-HHCMZEUC.js.map
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  resolveDefaultNamespaceRoot,
3
3
  resolveNamespaceStorageRoot
4
- } from "./chunk-EGQ3AVZV.js";
4
+ } from "./chunk-HHCMZEUC.js";
5
5
  import {
6
6
  getConfiguredNamespaces
7
- } from "./chunk-W3M34XT4.js";
7
+ } from "./chunk-BAVVI5GP.js";
8
8
  import {
9
9
  namespaceIdentityFromToken,
10
10
  namespaceIdentityToken,
11
11
  normalizeNamespaceIdentity
12
- } from "./chunk-ZFXCQPNO.js";
12
+ } from "./chunk-EGGE52UU.js";
13
13
  import {
14
14
  MutationSerializer,
15
15
  withHeldFileLock
@@ -34,10 +34,11 @@ import {
34
34
  appendFile,
35
35
  lstat,
36
36
  mkdir,
37
+ open,
37
38
  readdir,
38
- readFile,
39
39
  realpath,
40
40
  rename,
41
+ stat,
41
42
  writeFile
42
43
  } from "fs/promises";
43
44
  var NAMESPACE_KINDS = [
@@ -256,12 +257,19 @@ var NamespaceCatalog = class {
256
257
  // neither mistakes the other's lock for self-held (round 6, codex P2 — NBsGP
257
258
  // invariant preserved and tightened).
258
259
  criticalSection = new MutationSerializer();
260
+ compactedCache;
259
261
  // Test-only seam (round 7 — NEZkA): fires inside a touch's HELD-lock critical
260
262
  // section, after the lock is acquired but BEFORE the read→merge→append. A
261
263
  // deterministic concurrency test installs a hook here to widen the (otherwise
262
264
  // microscopic) window and prove that a cross-process rebuild CANNOT run its
263
265
  // load→rename while a touch holds the lock. Never set in production code.
264
266
  onTouchCriticalSectionForTest;
267
+ // Test-only observation seam: fires only when the JSONL file is fully read.
268
+ // Cached loads do not invoke it.
269
+ onCatalogReadForTest;
270
+ // Test-only seam: fires after this process appends but before it stats the
271
+ // file to refresh the cache identity.
272
+ onAfterCatalogAppendForTest;
265
273
  // Test-only seam (round 7 — NEZkA): fires inside a mutating rebuild's HELD-lock
266
274
  // critical section, after the final cross-process re-merge `loadCompacted()` and
267
275
  // BEFORE the atomic `rename()`. This is the EXACT window in which a check-then-
@@ -376,14 +384,16 @@ var NamespaceCatalog = class {
376
384
  }
377
385
  return [...byStorageDir.values()];
378
386
  }
379
- async loadSanitizedRecords() {
380
- const records = await this.loadCompacted();
381
- const sanitized = await Promise.all(
382
- [...records.values()].map((r) => this.sanitizeRecordForRead(r))
383
- );
384
- return this.dropDuplicateStorageRootAliases(
385
- sanitized.filter((r) => r !== null)
386
- );
387
+ loadSanitizedRecords() {
388
+ return this.queueCritical(async () => {
389
+ const records = await this.loadCompacted();
390
+ const sanitized = await Promise.all(
391
+ [...records.values()].map((r) => this.sanitizeRecordForRead(r))
392
+ );
393
+ return this.dropDuplicateStorageRootAliases(
394
+ sanitized.filter((r) => r !== null)
395
+ );
396
+ });
387
397
  }
388
398
  async listNamespaces(filter) {
389
399
  if (!this.enabled) return [];
@@ -1040,7 +1050,7 @@ var NamespaceCatalog = class {
1040
1050
  */
1041
1051
  async finishRebuild(rebuilt, skipped, dryRun, canMutate, memoryReal, nowIso) {
1042
1052
  if (canMutate) {
1043
- const latest = await this.loadCompacted();
1053
+ const latest = await this.loadCompacted(true);
1044
1054
  for (const [ns, fresh] of latest) {
1045
1055
  const current = rebuilt.get(ns);
1046
1056
  if (!current) {
@@ -1168,36 +1178,71 @@ var NamespaceCatalog = class {
1168
1178
  }
1169
1179
  // ── Persistence ──────────────────────────────────────────────────────────
1170
1180
  /** Load the JSONL log and fold it into current state (last-record-wins). */
1171
- async loadCompacted() {
1181
+ async loadCompacted(forceFresh = false) {
1172
1182
  const records = /* @__PURE__ */ new Map();
1173
- let raw;
1183
+ let handle;
1174
1184
  try {
1175
- raw = await readFile(this.catalogPath, "utf8");
1185
+ handle = await open(this.catalogPath, "r");
1176
1186
  } catch {
1187
+ this.compactedCache = void 0;
1177
1188
  return records;
1178
1189
  }
1179
- for (const line of raw.split("\n")) {
1180
- const trimmed = line.trim();
1181
- if (trimmed.length === 0) continue;
1182
- let parsed;
1183
- try {
1184
- parsed = JSON.parse(trimmed);
1185
- } catch {
1186
- continue;
1190
+ let identity;
1191
+ try {
1192
+ const fileStat = await handle.stat();
1193
+ identity = {
1194
+ size: fileStat.size,
1195
+ mtimeMs: fileStat.mtimeMs,
1196
+ ctimeMs: fileStat.ctimeMs,
1197
+ ino: fileStat.ino
1198
+ };
1199
+ if (!forceFresh && this.compactedCache && this.compactedCache.identity.size === identity.size && this.compactedCache.identity.mtimeMs === identity.mtimeMs && this.compactedCache.identity.ctimeMs === identity.ctimeMs && this.compactedCache.identity.ino === identity.ino) {
1200
+ await handle.close().catch(() => void 0);
1201
+ return this.cloneCompactedRecords(this.compactedCache.records);
1187
1202
  }
1188
- const record = coerceRecord(parsed);
1189
- if (!record) continue;
1190
- const prior = records.get(record.namespace);
1191
- records.set(record.namespace, prior ? mergeNewerTouchFields(record, prior) : record);
1203
+ } catch {
1192
1204
  }
1193
- return records;
1205
+ try {
1206
+ this.onCatalogReadForTest?.();
1207
+ const raw = await handle.readFile("utf8");
1208
+ for (const line of raw.split("\n")) {
1209
+ const trimmed = line.trim();
1210
+ if (trimmed.length === 0) continue;
1211
+ let parsed;
1212
+ try {
1213
+ parsed = JSON.parse(trimmed);
1214
+ } catch {
1215
+ continue;
1216
+ }
1217
+ const record = coerceRecord(parsed);
1218
+ if (!record) continue;
1219
+ const prior = records.get(record.namespace);
1220
+ records.set(record.namespace, prior ? mergeNewerTouchFields(record, prior) : record);
1221
+ }
1222
+ } catch {
1223
+ this.compactedCache = void 0;
1224
+ return /* @__PURE__ */ new Map();
1225
+ } finally {
1226
+ await handle.close().catch(() => void 0);
1227
+ }
1228
+ this.compactedCache = identity ? { identity, records } : void 0;
1229
+ return this.cloneCompactedRecords(records);
1230
+ }
1231
+ cloneCompactedRecords(records) {
1232
+ return new Map(
1233
+ [...records].map(([namespace, record]) => [
1234
+ namespace,
1235
+ {
1236
+ ...record,
1237
+ lastMaintenanceAt: record.lastMaintenanceAt ? { ...record.lastMaintenanceAt } : void 0
1238
+ }
1239
+ ])
1240
+ );
1194
1241
  }
1195
1242
  /**
1196
1243
  * Serialize an arbitrary read-modify-write critical section through the single
1197
- * per-instance chain. Every catalog mutation (touch read+merge+append, full
1198
- * rewrite) runs through this so they are mutually exclusive: a touch always
1199
- * reads the latest persisted state before appending, and a rebuild rewrite
1200
- * cannot interleave with a touch's append.
1244
+ * per-instance chain. Every catalog mutation and cached catalog read runs
1245
+ * through this queue, so cache validation and updates cannot race in-process.
1201
1246
  *
1202
1247
  * Issue #1524 adoption: delegates to the shared `MutationSerializer` (stored
1203
1248
  * as `criticalSection`). The util owns the rejection-recovery invariant
@@ -1217,8 +1262,13 @@ var NamespaceCatalog = class {
1217
1262
  */
1218
1263
  async appendUnchained(record) {
1219
1264
  const line = serializeRecord(record) + "\n";
1265
+ const lineByteLength = Buffer.byteLength(line, "utf8");
1220
1266
  await mkdir(this.stateDir, { recursive: true });
1221
1267
  await appendFile(this.catalogPath, line, "utf8");
1268
+ if (this.onAfterCatalogAppendForTest) {
1269
+ await this.onAfterCatalogAppendForTest();
1270
+ }
1271
+ await this.refreshCacheAfterAppend(record, lineByteLength);
1222
1272
  }
1223
1273
  /**
1224
1274
  * Atomic temp-file + rename rewrite (CLAUDE.md rule #54: write temp, then
@@ -1234,6 +1284,48 @@ var NamespaceCatalog = class {
1234
1284
  const tmp = `${this.catalogPath}.${process.pid}.${Date.now()}.tmp`;
1235
1285
  await writeFile(tmp, body, "utf8");
1236
1286
  await rename(tmp, this.catalogPath);
1287
+ await this.refreshCacheIdentity(new Map(records.map((record) => [record.namespace, record])));
1288
+ }
1289
+ async refreshCacheAfterAppend(record, lineByteLength) {
1290
+ const cached = this.compactedCache;
1291
+ if (!cached) return;
1292
+ const expectedSize = cached.identity.size + lineByteLength;
1293
+ try {
1294
+ const fileStat = await stat(this.catalogPath);
1295
+ if (fileStat.size !== expectedSize || fileStat.ino !== cached.identity.ino) {
1296
+ this.compactedCache = void 0;
1297
+ return;
1298
+ }
1299
+ const prior = cached.records.get(record.namespace);
1300
+ cached.records.set(record.namespace, prior ? mergeNewerTouchFields(record, prior) : record);
1301
+ this.compactedCache = {
1302
+ identity: {
1303
+ size: fileStat.size,
1304
+ mtimeMs: fileStat.mtimeMs,
1305
+ ctimeMs: fileStat.ctimeMs,
1306
+ ino: fileStat.ino
1307
+ },
1308
+ records: cached.records
1309
+ };
1310
+ } catch {
1311
+ this.compactedCache = void 0;
1312
+ }
1313
+ }
1314
+ async refreshCacheIdentity(records) {
1315
+ try {
1316
+ const fileStat = await stat(this.catalogPath);
1317
+ this.compactedCache = {
1318
+ identity: {
1319
+ size: fileStat.size,
1320
+ mtimeMs: fileStat.mtimeMs,
1321
+ ctimeMs: fileStat.ctimeMs,
1322
+ ino: fileStat.ino
1323
+ },
1324
+ records
1325
+ };
1326
+ } catch {
1327
+ this.compactedCache = void 0;
1328
+ }
1237
1329
  }
1238
1330
  };
1239
1331
  function isPathInside(root, child) {
@@ -1243,13 +1335,13 @@ function isPathInside(root, child) {
1243
1335
 
1244
1336
  // src/maintenance/namespace-planner.ts
1245
1337
  import { createHash, randomUUID } from "crypto";
1246
- import { lstat as lstat2, mkdir as mkdir2, open, readFile as readFile2, readdir as readdir2, rename as rename2, rm, rmdir, utimes, writeFile as writeFile2 } from "fs/promises";
1338
+ import { lstat as lstat2, mkdir as mkdir2, open as open2, readFile, readdir as readdir2, rename as rename2, rm, rmdir, utimes, writeFile as writeFile2 } from "fs/promises";
1247
1339
  import path2 from "path";
1248
1340
  var DEFAULT_MAX_NAMESPACES_PER_CYCLE = 20;
1249
1341
  var DEFAULT_LOCK_STALE_MS = 10 * 6e4;
1250
1342
  var LOCK_BASE = "maintenance-locks";
1251
1343
  var STATUS_BASE = "namespace-maintenance-status";
1252
- var namespaceMaintenanceFs = { open, rm };
1344
+ var namespaceMaintenanceFs = { open: open2, rm };
1253
1345
  function inferConfiguredKind(config, namespace) {
1254
1346
  if (namespace === config.defaultNamespace.trim()) return "default";
1255
1347
  if (namespace === config.sharedNamespace.trim()) return "shared";
@@ -1516,7 +1608,7 @@ async function tryAcquireNamespaceMaintenanceLock(config, jobName, namespace) {
1516
1608
  path: filePath,
1517
1609
  async touch() {
1518
1610
  try {
1519
- const parsed = JSON.parse(await readFile2(ownerPath, "utf8"));
1611
+ const parsed = JSON.parse(await readFile(ownerPath, "utf8"));
1520
1612
  if (parsed.lockId === lockId) {
1521
1613
  const now = /* @__PURE__ */ new Date();
1522
1614
  await utimes(ownerPath, now, now);
@@ -1527,7 +1619,7 @@ async function tryAcquireNamespaceMaintenanceLock(config, jobName, namespace) {
1527
1619
  },
1528
1620
  async release() {
1529
1621
  try {
1530
- const parsed = JSON.parse(await readFile2(ownerPath, "utf8"));
1622
+ const parsed = JSON.parse(await readFile(ownerPath, "utf8"));
1531
1623
  if (parsed.lockId === lockId) {
1532
1624
  await namespaceMaintenanceFs.rm(ownerPath, { force: true });
1533
1625
  await rmdir(filePath).catch(() => void 0);
@@ -1607,7 +1699,7 @@ async function readLatestStatusAtByNamespace(config, jobName) {
1607
1699
  }
1608
1700
  async function readStatusFile(filePath) {
1609
1701
  try {
1610
- const parsed = JSON.parse(await readFile2(filePath, "utf8"));
1702
+ const parsed = JSON.parse(await readFile(filePath, "utf8"));
1611
1703
  return parseStatus(parsed);
1612
1704
  } catch {
1613
1705
  return null;
@@ -1950,4 +2042,4 @@ export {
1950
2042
  runNamespaceMaintenancePlan,
1951
2043
  runNamespaceMaintenanceBatchPlan
1952
2044
  };
1953
- //# sourceMappingURL=chunk-6CUUFFU7.js.map
2045
+ //# sourceMappingURL=chunk-HP23UD6W.js.map