@remnic/core 9.3.692 → 9.3.694

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 (268) hide show
  1. package/dist/access-boundary.d.ts +6 -6
  2. package/dist/access-boundary.js +11 -9
  3. package/dist/access-cli.js +76 -23
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +5 -5
  6. package/dist/access-http.js +14 -12
  7. package/dist/access-mcp.d.ts +14 -5
  8. package/dist/access-mcp.js +13 -11
  9. package/dist/access-operations.d.ts +54 -7
  10. package/dist/access-operations.js +42 -10
  11. package/dist/{access-service-DvApf9LZ.d.ts → access-service-Ceq4TdPO.d.ts} +180 -3
  12. package/dist/access-service.d.ts +5 -5
  13. package/dist/access-service.js +10 -8
  14. package/dist/access-surface-catalog.d.ts +5 -5
  15. package/dist/access-surface-catalog.js +17 -0
  16. package/dist/access-surface-catalog.js.map +1 -1
  17. package/dist/action-confidence.d.ts +1 -1
  18. package/dist/active-memory-bridge.d.ts +1 -1
  19. package/dist/active-recall.d.ts +1 -1
  20. package/dist/active-recall.js +1 -1
  21. package/dist/behavior-learner.d.ts +1 -1
  22. package/dist/behavior-signals.d.ts +1 -1
  23. package/dist/bootstrap.d.ts +4 -4
  24. package/dist/briefing.d.ts +1 -1
  25. package/dist/briefing.js +3 -3
  26. package/dist/buffer-surprise-report.d.ts +1 -1
  27. package/dist/buffer.d.ts +1 -1
  28. package/dist/calibration.d.ts +1 -1
  29. package/dist/capabilities.d.ts +1 -1
  30. package/dist/{catalog-BEWLjdOr.d.ts → catalog-Tkb991rg.d.ts} +1 -1
  31. package/dist/causal-behavior.d.ts +1 -1
  32. package/dist/causal-consolidation.d.ts +1 -1
  33. package/dist/causal-consolidation.js +4 -4
  34. package/dist/{chunk-ADUAF5EV.js → chunk-24FCDBJV.js} +2 -2
  35. package/dist/{chunk-V56S3A23.js → chunk-5A35EGHM.js} +1092 -29
  36. package/dist/chunk-5A35EGHM.js.map +1 -0
  37. package/dist/{chunk-3VVH6ZQW.js → chunk-5S53ZBPH.js} +2 -2
  38. package/dist/{chunk-DO7MDZMU.js → chunk-6C4WHMKV.js} +123 -7
  39. package/dist/chunk-6C4WHMKV.js.map +1 -0
  40. package/dist/{chunk-2D27ODNX.js → chunk-B55EAZNF.js} +2 -2
  41. package/dist/{chunk-W4MICQIZ.js → chunk-C5AOVGVC.js} +15 -15
  42. package/dist/chunk-CDOIDOTQ.js +381 -0
  43. package/dist/chunk-CDOIDOTQ.js.map +1 -0
  44. package/dist/{chunk-UOCLRM74.js → chunk-D7PQ7HZL.js} +2 -2
  45. package/dist/{chunk-HXNMDPHN.js → chunk-DT2ZGQT7.js} +2 -2
  46. package/dist/{chunk-QFG23CRE.js → chunk-EPVIBPVE.js} +2 -2
  47. package/dist/{chunk-O4ZIV62T.js → chunk-FCH766FX.js} +21 -19
  48. package/dist/chunk-FCH766FX.js.map +1 -0
  49. package/dist/{chunk-7MOTEVAA.js → chunk-HRUULBBV.js} +3 -1
  50. package/dist/chunk-HRUULBBV.js.map +1 -0
  51. package/dist/{chunk-U2OBZSGG.js → chunk-IZLNJYLX.js} +2 -2
  52. package/dist/{chunk-KYH53EFD.js → chunk-JIKBEDSB.js} +6 -6
  53. package/dist/{chunk-3BRA3N5A.js → chunk-KCRV3HVN.js} +2 -2
  54. package/dist/{chunk-AFWEUTMS.js → chunk-MOGFK2DU.js} +2 -2
  55. package/dist/{chunk-SAC5V6JT.js → chunk-MSUA4KAK.js} +2 -2
  56. package/dist/{chunk-VFVG4NS2.js → chunk-NXNUG7IA.js} +2 -2
  57. package/dist/{chunk-66HROLW5.js → chunk-P7IDNF7H.js} +2 -2
  58. package/dist/{chunk-FM7KLRKN.js → chunk-PILLH262.js} +2 -2
  59. package/dist/{chunk-UTUYAPLY.js → chunk-PNW4KKJX.js} +12 -3
  60. package/dist/{chunk-UTUYAPLY.js.map → chunk-PNW4KKJX.js.map} +1 -1
  61. package/dist/{chunk-2EN5WZQB.js → chunk-PQP6MFPL.js} +2 -2
  62. package/dist/chunk-PQP6MFPL.js.map +1 -0
  63. package/dist/{chunk-6C2BMUZA.js → chunk-QXWQ5V24.js} +2 -2
  64. package/dist/{chunk-SEK4XIDN.js → chunk-SJGTY2TW.js} +35 -9
  65. package/dist/chunk-SJGTY2TW.js.map +1 -0
  66. package/dist/{chunk-4IQNAOP3.js → chunk-SN5DFE34.js} +2 -2
  67. package/dist/{chunk-DQHZA7Y3.js → chunk-U62ZGWU7.js} +1 -1
  68. package/dist/chunk-U62ZGWU7.js.map +1 -0
  69. package/dist/{chunk-WIKNVFBS.js → chunk-UX5QTAFE.js} +2 -2
  70. package/dist/chunk-VQ7B2Q6M.js +112 -0
  71. package/dist/chunk-VQ7B2Q6M.js.map +1 -0
  72. package/dist/{chunk-MCV2DD6F.js → chunk-WVTNZUER.js} +2 -2
  73. package/dist/{chunk-ZLXGFAML.js → chunk-YI3KC2B5.js} +2 -2
  74. package/dist/{chunk-W4ELOUNX.js → chunk-YWYZPKK3.js} +2 -2
  75. package/dist/{chunk-NUZ3N445.js → chunk-YXZPKBFW.js} +3 -3
  76. package/dist/{chunk-GCOSQBQ2.js → chunk-ZKD3UVEO.js} +3 -3
  77. package/dist/{cli-BjsY9Le7.d.ts → cli-CxtZyBLl.d.ts} +3 -3
  78. package/dist/cli.d.ts +6 -6
  79. package/dist/cli.js +28 -26
  80. package/dist/coding/optional-coding-graph.js +7 -104
  81. package/dist/coding/optional-coding-graph.js.map +1 -1
  82. package/dist/compounding/engine.d.ts +1 -1
  83. package/dist/compounding/engine.js +3 -3
  84. package/dist/compounding/preference-consolidator.d.ts +1 -1
  85. package/dist/compression-optimizer.d.ts +1 -1
  86. package/dist/config.d.ts +1 -1
  87. package/dist/config.js +1 -1
  88. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  89. package/dist/connectors/codex-materialize-runner.js +3 -3
  90. package/dist/connectors/codex-materialize.d.ts +1 -1
  91. package/dist/connectors/index.d.ts +1 -1
  92. package/dist/connectors/index.js +3 -3
  93. package/dist/consolidation-provenance-check.d.ts +1 -1
  94. package/dist/consolidation-undo.d.ts +1 -1
  95. package/dist/contradiction/index.d.ts +1 -1
  96. package/dist/conversation-index/backend.d.ts +1 -1
  97. package/dist/conversation-index/chunker.d.ts +1 -1
  98. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  99. package/dist/conversation-index/indexer.d.ts +1 -1
  100. package/dist/conversation-index/search.d.ts +1 -1
  101. package/dist/day-summary.d.ts +1 -1
  102. package/dist/delinearize.d.ts +1 -1
  103. package/dist/direct-answer-wiring.d.ts +1 -1
  104. package/dist/direct-answer.d.ts +1 -1
  105. package/dist/embedding-fallback.d.ts +1 -1
  106. package/dist/enrichment/index.d.ts +1 -1
  107. package/dist/entity-retrieval.d.ts +1 -1
  108. package/dist/entity-retrieval.js +3 -3
  109. package/dist/entity-schema.d.ts +1 -1
  110. package/dist/explicit-capture.d.ts +4 -4
  111. package/dist/extraction-judge-telemetry.d.ts +1 -1
  112. package/dist/extraction-judge-training.d.ts +1 -1
  113. package/dist/extraction-judge.d.ts +1 -1
  114. package/dist/extraction.d.ts +1 -1
  115. package/dist/fallback-llm.d.ts +1 -1
  116. package/dist/identity-continuity.d.ts +1 -1
  117. package/dist/importance.d.ts +1 -1
  118. package/dist/index.d.ts +9 -9
  119. package/dist/index.js +37 -36
  120. package/dist/index.js.map +1 -1
  121. package/dist/intent.d.ts +1 -1
  122. package/dist/lcm/engine.d.ts +1 -1
  123. package/dist/lcm/index.d.ts +1 -1
  124. package/dist/lcm/tools.d.ts +1 -1
  125. package/dist/lifecycle.d.ts +1 -1
  126. package/dist/live-connectors-runner.d.ts +1 -1
  127. package/dist/local-llm.d.ts +1 -1
  128. package/dist/maintenance/memory-governance.d.ts +1 -1
  129. package/dist/maintenance/memory-governance.js +3 -3
  130. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  131. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  132. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  133. package/dist/memory-action-policy.d.ts +1 -1
  134. package/dist/memory-cache.d.ts +1 -1
  135. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  136. package/dist/memory-projection-store.d.ts +1 -1
  137. package/dist/memory-provenance.d.ts +1 -1
  138. package/dist/memory-worth-outcomes.d.ts +1 -1
  139. package/dist/models-json.d.ts +1 -1
  140. package/dist/namespaces/migrate.d.ts +2 -2
  141. package/dist/namespaces/migrate.js +4 -4
  142. package/dist/namespaces/principal.d.ts +1 -1
  143. package/dist/namespaces/search.d.ts +1 -1
  144. package/dist/namespaces/storage.d.ts +2 -2
  145. package/dist/namespaces/storage.js +3 -3
  146. package/dist/native-knowledge.d.ts +1 -1
  147. package/dist/operator-toolkit.d.ts +1 -1
  148. package/dist/operator-toolkit.js +9 -9
  149. package/dist/orchestration/maintenance.d.ts +2 -2
  150. package/dist/orchestration/maintenance.js +5 -5
  151. package/dist/{orchestrator-CNJCYjZ1.d.ts → orchestrator-CnbxRQs8.d.ts} +3 -3
  152. package/dist/orchestrator.d.ts +4 -4
  153. package/dist/orchestrator.js +16 -16
  154. package/dist/patterns-cli.d.ts +1 -1
  155. package/dist/policy-runtime.d.ts +1 -1
  156. package/dist/provenance.d.ts +1 -1
  157. package/dist/qmd-recall-cache.d.ts +1 -1
  158. package/dist/qmd.d.ts +1 -1
  159. package/dist/recall-disclosure-escalation.d.ts +1 -1
  160. package/dist/recall-explain-renderer.d.ts +1 -1
  161. package/dist/recall-explain-renderer.js +3 -3
  162. package/dist/recall-planner-llm.d.ts +1 -1
  163. package/dist/recall-state.d.ts +1 -1
  164. package/dist/recall-tag-filter.d.ts +1 -1
  165. package/dist/recall-xray-cli.d.ts +1 -1
  166. package/dist/recall-xray-cli.js +4 -4
  167. package/dist/recall-xray-renderer.d.ts +1 -1
  168. package/dist/recall-xray-renderer.js +3 -3
  169. package/dist/recall-xray.d.ts +1 -1
  170. package/dist/recall-xray.js +2 -2
  171. package/dist/resolve-auth-token.d.ts +1 -1
  172. package/dist/resume-bundles.js +2 -2
  173. package/dist/retrieval-agents.d.ts +1 -1
  174. package/dist/retrieval-tiers.d.ts +1 -1
  175. package/dist/routing/engine.d.ts +1 -1
  176. package/dist/routing/store.d.ts +1 -1
  177. package/dist/schemas.d.ts +28 -28
  178. package/dist/search/embed-helper.d.ts +1 -1
  179. package/dist/search/factory.d.ts +1 -1
  180. package/dist/search/index.d.ts +1 -1
  181. package/dist/search/lancedb-backend.d.ts +1 -1
  182. package/dist/search/meilisearch-backend.d.ts +1 -1
  183. package/dist/search/noop-backend.d.ts +1 -1
  184. package/dist/search/orama-backend.d.ts +1 -1
  185. package/dist/search/port.d.ts +1 -1
  186. package/dist/search/remote-backend.d.ts +1 -1
  187. package/dist/{semantic-consolidation-D5U0Mrdb.d.ts → semantic-consolidation-DXmtQ_4B.d.ts} +1 -1
  188. package/dist/semantic-consolidation.d.ts +2 -2
  189. package/dist/semantic-consolidation.js +4 -4
  190. package/dist/semantic-rule-promotion.js +3 -3
  191. package/dist/semantic-rule-verifier.d.ts +1 -1
  192. package/dist/semantic-rule-verifier.js +3 -3
  193. package/dist/session-observer-bands.d.ts +1 -1
  194. package/dist/session-observer-state.d.ts +1 -1
  195. package/dist/shared-context/manager.d.ts +1 -1
  196. package/dist/signal.d.ts +1 -1
  197. package/dist/storage.d.ts +1 -1
  198. package/dist/storage.js +2 -2
  199. package/dist/summarizer.d.ts +1 -1
  200. package/dist/summary-snapshot.d.ts +1 -1
  201. package/dist/temporal-supersession.d.ts +1 -1
  202. package/dist/temporal-validity.d.ts +1 -1
  203. package/dist/threading.d.ts +1 -1
  204. package/dist/tier-migration.d.ts +1 -1
  205. package/dist/tier-routing.d.ts +1 -1
  206. package/dist/topics.d.ts +1 -1
  207. package/dist/transcript.d.ts +1 -1
  208. package/dist/transfer/types.d.ts +12 -12
  209. package/dist/{types-2NoBoRBM.d.ts → types-CEWBmjCH.d.ts} +14 -0
  210. package/dist/types.d.ts +1 -1
  211. package/dist/types.js +1 -1
  212. package/dist/utility-runtime.d.ts +1 -1
  213. package/dist/verified-recall.js +3 -3
  214. package/package.json +2 -2
  215. package/src/access-boundary.ts +19 -1
  216. package/src/access-cli.ts +65 -2
  217. package/src/access-http.ts +47 -3
  218. package/src/access-mcp.ts +128 -2
  219. package/src/access-operations.ts +305 -0
  220. package/src/access-service.ts +137 -0
  221. package/src/access-surface-catalog.test.ts +1 -1
  222. package/src/access-surface-catalog.ts +17 -0
  223. package/src/cli.ts +2 -0
  224. package/src/coding/architecture-surfaces.test.ts +2 -0
  225. package/src/coding/codegraph-runtime.ts +773 -0
  226. package/src/coding/codegraph-surfaces.test.ts +880 -0
  227. package/src/coding/codegraph-surfaces.ts +771 -0
  228. package/src/coding/coding-knowledge-config.ts +7 -0
  229. package/src/coding/coding-namespace.ts +1 -1
  230. package/src/coding/decision-surfaces.test.ts +2 -0
  231. package/src/coding/session-delta-surfaces.test.ts +385 -0
  232. package/src/coding/session-delta-surfaces.ts +325 -0
  233. package/src/coding/session-delta.test.ts +411 -0
  234. package/src/coding/session-delta.ts +442 -0
  235. package/src/config.test.ts +2 -0
  236. package/src/types.ts +14 -0
  237. package/dist/chunk-2EN5WZQB.js.map +0 -1
  238. package/dist/chunk-7MOTEVAA.js.map +0 -1
  239. package/dist/chunk-DO7MDZMU.js.map +0 -1
  240. package/dist/chunk-DQHZA7Y3.js.map +0 -1
  241. package/dist/chunk-NEILAC3H.js +0 -153
  242. package/dist/chunk-NEILAC3H.js.map +0 -1
  243. package/dist/chunk-O4ZIV62T.js.map +0 -1
  244. package/dist/chunk-SEK4XIDN.js.map +0 -1
  245. package/dist/chunk-V56S3A23.js.map +0 -1
  246. /package/dist/{chunk-ADUAF5EV.js.map → chunk-24FCDBJV.js.map} +0 -0
  247. /package/dist/{chunk-3VVH6ZQW.js.map → chunk-5S53ZBPH.js.map} +0 -0
  248. /package/dist/{chunk-2D27ODNX.js.map → chunk-B55EAZNF.js.map} +0 -0
  249. /package/dist/{chunk-W4MICQIZ.js.map → chunk-C5AOVGVC.js.map} +0 -0
  250. /package/dist/{chunk-UOCLRM74.js.map → chunk-D7PQ7HZL.js.map} +0 -0
  251. /package/dist/{chunk-HXNMDPHN.js.map → chunk-DT2ZGQT7.js.map} +0 -0
  252. /package/dist/{chunk-QFG23CRE.js.map → chunk-EPVIBPVE.js.map} +0 -0
  253. /package/dist/{chunk-U2OBZSGG.js.map → chunk-IZLNJYLX.js.map} +0 -0
  254. /package/dist/{chunk-KYH53EFD.js.map → chunk-JIKBEDSB.js.map} +0 -0
  255. /package/dist/{chunk-3BRA3N5A.js.map → chunk-KCRV3HVN.js.map} +0 -0
  256. /package/dist/{chunk-AFWEUTMS.js.map → chunk-MOGFK2DU.js.map} +0 -0
  257. /package/dist/{chunk-SAC5V6JT.js.map → chunk-MSUA4KAK.js.map} +0 -0
  258. /package/dist/{chunk-VFVG4NS2.js.map → chunk-NXNUG7IA.js.map} +0 -0
  259. /package/dist/{chunk-66HROLW5.js.map → chunk-P7IDNF7H.js.map} +0 -0
  260. /package/dist/{chunk-FM7KLRKN.js.map → chunk-PILLH262.js.map} +0 -0
  261. /package/dist/{chunk-6C2BMUZA.js.map → chunk-QXWQ5V24.js.map} +0 -0
  262. /package/dist/{chunk-4IQNAOP3.js.map → chunk-SN5DFE34.js.map} +0 -0
  263. /package/dist/{chunk-WIKNVFBS.js.map → chunk-UX5QTAFE.js.map} +0 -0
  264. /package/dist/{chunk-MCV2DD6F.js.map → chunk-WVTNZUER.js.map} +0 -0
  265. /package/dist/{chunk-ZLXGFAML.js.map → chunk-YI3KC2B5.js.map} +0 -0
  266. /package/dist/{chunk-W4ELOUNX.js.map → chunk-YWYZPKK3.js.map} +0 -0
  267. /package/dist/{chunk-NUZ3N445.js.map → chunk-YXZPKBFW.js.map} +0 -0
  268. /package/dist/{chunk-GCOSQBQ2.js.map → chunk-ZKD3UVEO.js.map} +0 -0
@@ -32,6 +32,8 @@ export const CODING_KNOWLEDGE_DEFAULTS = Object.freeze({
32
32
  architectureCardLlmSummary: false,
33
33
  structuralProvider: "none",
34
34
  structuralProviderCommand: "",
35
+ codegraphTools: false,
36
+ codegraphDbDir: "",
35
37
  } satisfies Record<keyof CodingKnowledgeConfig, boolean | string>);
36
38
 
37
39
  const VALID_STRUCTURAL_PROVIDERS = ["none", "subprocess", "native"] as const;
@@ -63,6 +65,11 @@ export function parseCodingKnowledgeConfig(raw: unknown): CodingKnowledgeConfig
63
65
  typeof record.structuralProviderCommand === "string"
64
66
  ? record.structuralProviderCommand.trim()
65
67
  : "",
68
+ codegraphTools: readStrictBool(record.codegraphTools, "codegraphTools", /* default */ false),
69
+ codegraphDbDir:
70
+ typeof record.codegraphDbDir === "string"
71
+ ? record.codegraphDbDir.trim()
72
+ : "",
66
73
  };
67
74
  }
68
75
 
@@ -66,7 +66,7 @@ export interface CodingNamespaceOverlay {
66
66
  * greedy quantifiers) closes the polynomial-backtracking surface that
67
67
  * CodeQL flagged on patterns like `-+` and `^-+|-+$`.
68
68
  */
69
- function sanitizeFragment(input: string): string {
69
+ export function sanitizeFragment(input: string): string {
70
70
  if (typeof input !== "string") return "";
71
71
  const trimmed = input.trim().toLowerCase();
72
72
  let out = "";
@@ -41,6 +41,8 @@ const GATE_OFF_CONFIG: CodingKnowledgeConfig = {
41
41
  architectureCardLlmSummary: false,
42
42
  structuralProvider: "none",
43
43
  structuralProviderCommand: "",
44
+ codegraphTools: false,
45
+ codegraphDbDir: "",
44
46
  };
45
47
 
46
48
  const GATE_ON_CONFIG: CodingKnowledgeConfig = {
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Surface contract tests for the session-delta handler
3
+ * (issue #1548 Track A PR 4).
4
+ *
5
+ * Contract under test:
6
+ * - Gate predicate: enabled + sessionDelta + coding context (rule 39).
7
+ * - get with no prior state → first_run, state initialized.
8
+ * - get with prior head == current → unchanged.
9
+ * - get with prior head ancestor → changed with capped delta.
10
+ * - get with unreachable prior head → tagged failure (rule 34).
11
+ * - State write happens AFTER compute (rule 25); failures are logged not fatal.
12
+ */
13
+ import assert from "node:assert/strict";
14
+ import test from "node:test";
15
+
16
+ import {
17
+ DELTA_SUBCOMMANDS,
18
+ formatDeltaSubcommands,
19
+ handleCodingDelta,
20
+ isDeltaSubcommand,
21
+ isSessionDeltaSurfaceEnabled,
22
+ isSessionDeltaSurfaceVisible,
23
+ type DeltaSurfaceContext,
24
+ type DeltaSurfaceStorage,
25
+ } from "./session-delta-surfaces.js";
26
+ import type {
27
+ CodingKnowledgeConfig,
28
+ CodingContext,
29
+ } from "../types.js";
30
+ import type { SessionDeltaGitInvoker } from "./session-delta.js";
31
+
32
+ // ──────────────────────────────────────────────────────────────────────────
33
+ // Fixtures
34
+ // ──────────────────────────────────────────────────────────────────────────
35
+
36
+ const DEFAULT_CONFIG: CodingKnowledgeConfig = {
37
+ enabled: true,
38
+ decisionRecords: true,
39
+ architectureCard: true,
40
+ sessionDelta: true,
41
+ architectureCardLlmSummary: false,
42
+ structuralProvider: "none",
43
+ structuralProviderCommand: "",
44
+ codegraphTools: false,
45
+ codegraphDbDir: "",
46
+ };
47
+
48
+ const CODING_CONTEXT: CodingContext = {
49
+ projectId: "origin:deadbeef",
50
+ branch: "main",
51
+ rootPath: "/fake/repo",
52
+ defaultBranch: "main",
53
+ };
54
+
55
+ function makeStorage(): DeltaSurfaceStorage {
56
+ return { memoryDir: "/fake/mem", namespace: "project-test" };
57
+ }
58
+
59
+ function makeInvoker(responses: Array<{ args: string[]; stdout: string; exitCode?: number }>): SessionDeltaGitInvoker {
60
+ return (_cwd: string, args: string[]) => {
61
+ const match = responses.find((r) => r.args.join(" ") === args.join(" "));
62
+ if (!match) return { stdout: "", exitCode: 1 };
63
+ return { stdout: match.stdout, exitCode: match.exitCode ?? 0 };
64
+ };
65
+ }
66
+
67
+ function makeContext(overrides: Partial<DeltaSurfaceContext> = {}): DeltaSurfaceContext {
68
+ return {
69
+ codingKnowledge: DEFAULT_CONFIG,
70
+ getCodingContext: () => CODING_CONTEXT,
71
+ resolveStorage: async () => makeStorage(),
72
+ gitInvoker: makeInvoker([]),
73
+ throwInputError: (msg) => {
74
+ throw new Error(msg);
75
+ },
76
+ ...overrides,
77
+ };
78
+ }
79
+
80
+ // Stub the pure module's state read/write by importing the real module and
81
+ // shimming its filesystem. The handler imports readLastSeenState / writeLastSeenState
82
+ // from ./session-delta.js, which hit the real fs. We point memoryDir at a
83
+ // temp dir per-test.
84
+ import { mkdtemp, rm } from "node:fs/promises";
85
+ import { tmpdir } from "node:os";
86
+ import path from "node:path";
87
+
88
+ async function makeTempStorage(): Promise<{ storage: DeltaSurfaceStorage; cleanup: () => Promise<void> }> {
89
+ const dir = await mkdtemp(path.join(tmpdir(), "delta-surface-"));
90
+ return {
91
+ storage: { memoryDir: dir, namespace: "project-test" },
92
+ cleanup: async () => { await rm(dir, { recursive: true, force: true }); },
93
+ };
94
+ }
95
+
96
+ // ──────────────────────────────────────────────────────────────────────────
97
+ // Subcommand + gate helpers
98
+ // ──────────────────────────────────────────────────────────────────────────
99
+
100
+ test("subcommands: the surface exposes only `get`", () => {
101
+ assert.deepEqual([...DELTA_SUBCOMMANDS], ["get"]);
102
+ assert.equal(formatDeltaSubcommands(), "get");
103
+ });
104
+
105
+ test("isDeltaSubcommand: narrows valid + rejects unknown", () => {
106
+ assert.equal(isDeltaSubcommand("get"), true);
107
+ assert.equal(isDeltaSubcommand("refresh"), false);
108
+ assert.equal(isDeltaSubcommand(undefined), false);
109
+ assert.equal(isDeltaSubcommand(123), false);
110
+ });
111
+
112
+ test("gate: surface enabled when config + sessionDelta + coding context all true", () => {
113
+ assert.equal(isSessionDeltaSurfaceEnabled(DEFAULT_CONFIG, CODING_CONTEXT), true);
114
+ });
115
+
116
+ test("gate: surface disabled when master gate off", () => {
117
+ const config: CodingKnowledgeConfig = { ...DEFAULT_CONFIG, enabled: false };
118
+ assert.equal(isSessionDeltaSurfaceEnabled(config, CODING_CONTEXT), false);
119
+ });
120
+
121
+ test("gate: surface disabled when sessionDelta off", () => {
122
+ const config: CodingKnowledgeConfig = { ...DEFAULT_CONFIG, sessionDelta: false };
123
+ assert.equal(isSessionDeltaSurfaceEnabled(config, CODING_CONTEXT), false);
124
+ });
125
+
126
+ test("gate: surface disabled when no coding context", () => {
127
+ assert.equal(isSessionDeltaSurfaceEnabled(DEFAULT_CONFIG, null), false);
128
+ assert.equal(isSessionDeltaSurfaceEnabled(DEFAULT_CONFIG, undefined), false);
129
+ });
130
+
131
+ test("visibility gate: config-only check mirrors full gate minus context", () => {
132
+ assert.equal(isSessionDeltaSurfaceVisible(DEFAULT_CONFIG), true);
133
+ assert.equal(isSessionDeltaSurfaceVisible({ ...DEFAULT_CONFIG, enabled: false }), false);
134
+ assert.equal(isSessionDeltaSurfaceVisible({ ...DEFAULT_CONFIG, sessionDelta: false }), false);
135
+ });
136
+
137
+ // ──────────────────────────────────────────────────────────────────────────
138
+ // Handler — gate enforcement
139
+ // ──────────────────────────────────────────────────────────────────────────
140
+
141
+ test("handler: throws when gate fails (no coding context)", async () => {
142
+ const ctx = makeContext({ getCodingContext: () => null });
143
+ await assert.rejects(
144
+ () => handleCodingDelta({ subcommand: "get", sessionKey: "s1" }, ctx),
145
+ /coding_delta requires/,
146
+ );
147
+ });
148
+
149
+ test("handler: throws when gate fails (master gate off)", async () => {
150
+ const ctx = makeContext({
151
+ codingKnowledge: { ...DEFAULT_CONFIG, enabled: false },
152
+ });
153
+ await assert.rejects(
154
+ () => handleCodingDelta({ subcommand: "get", sessionKey: "s1" }, ctx),
155
+ /coding_delta requires/,
156
+ );
157
+ });
158
+
159
+ test("handler: throws when gate fails (sessionDelta off)", async () => {
160
+ const ctx = makeContext({
161
+ codingKnowledge: { ...DEFAULT_CONFIG, sessionDelta: false },
162
+ });
163
+ await assert.rejects(
164
+ () => handleCodingDelta({ subcommand: "get", sessionKey: "s1" }, ctx),
165
+ /coding_delta requires/,
166
+ );
167
+ });
168
+
169
+ // ──────────────────────────────────────────────────────────────────────────
170
+ // Handler — three-state matrix via the surface
171
+ // ──────────────────────────────────────────────────────────────────────────
172
+
173
+ test("handler/get: first run → first_run, nextState initialized", async () => {
174
+ const { storage, cleanup } = await makeTempStorage();
175
+ try {
176
+ const invoker = makeInvoker([
177
+ { args: ["rev-parse", "HEAD"], stdout: "newhead\n" },
178
+ ]);
179
+ const ctx = makeContext({
180
+ resolveStorage: async () => storage,
181
+ gitInvoker: invoker,
182
+ });
183
+ const result = await handleCodingDelta({ subcommand: "get", sessionKey: "s1" }, ctx);
184
+ assert.equal(result.subcommand, "get");
185
+ if (!("ok" in result) || !result.ok || result.kind !== "first_run") {
186
+ assert.fail(`expected first_run, got ${JSON.stringify(result)}`);
187
+ return;
188
+ }
189
+ assert.equal(result.nextState.head, "newhead");
190
+ assert.ok(result.nextState.at);
191
+ } finally {
192
+ await cleanup();
193
+ }
194
+ });
195
+
196
+ test("handler/get: unchanged head → suppressed", async () => {
197
+ const { storage, cleanup } = await makeTempStorage();
198
+ try {
199
+ // Pre-seed the state file by running once.
200
+ const invoker1 = makeInvoker([
201
+ { args: ["rev-parse", "HEAD"], stdout: "stable\n" },
202
+ ]);
203
+ const ctx1 = makeContext({ resolveStorage: async () => storage, gitInvoker: invoker1 });
204
+ await handleCodingDelta({ subcommand: "get", sessionKey: "s1" }, ctx1);
205
+
206
+ // Second call with the same head.
207
+ const invoker2 = makeInvoker([
208
+ { args: ["rev-parse", "HEAD"], stdout: "stable\n" },
209
+ ]);
210
+ const ctx2 = makeContext({ resolveStorage: async () => storage, gitInvoker: invoker2 });
211
+ const result = await handleCodingDelta({ subcommand: "get", sessionKey: "s1" }, ctx2);
212
+ if (!("ok" in result) || !result.ok || result.kind !== "unchanged") {
213
+ assert.fail(`expected unchanged, got ${JSON.stringify(result)}`);
214
+ return;
215
+ }
216
+ } finally {
217
+ await cleanup();
218
+ }
219
+ });
220
+
221
+ test("handler/get: changed head → real delta", async () => {
222
+ const { storage, cleanup } = await makeTempStorage();
223
+ try {
224
+ // Seed: first run on headA.
225
+ const invoker1 = makeInvoker([
226
+ { args: ["rev-parse", "HEAD"], stdout: "headA\n" },
227
+ ]);
228
+ await handleCodingDelta(
229
+ { subcommand: "get", sessionKey: "s1" },
230
+ makeContext({ resolveStorage: async () => storage, gitInvoker: invoker1 }),
231
+ );
232
+
233
+ // Now head moved to headB with two commits.
234
+ const sep = "\x1f";
235
+ const log = [
236
+ `sha1${sep}feat: a`,
237
+ "",
238
+ "src/a.ts",
239
+ "",
240
+ `sha2${sep}fix: b`,
241
+ "",
242
+ "src/b.ts",
243
+ "",
244
+ ].join("\n");
245
+ const invoker2 = makeInvoker([
246
+ { args: ["rev-parse", "HEAD"], stdout: "headB\n" },
247
+ { args: ["log", "--reverse", "--pretty=format:%H\x1f%s", "--name-only", "headA..headB"], stdout: log },
248
+ ]);
249
+ const result = await handleCodingDelta(
250
+ { subcommand: "get", sessionKey: "s1" },
251
+ makeContext({ resolveStorage: async () => storage, gitInvoker: invoker2 }),
252
+ );
253
+ if (!("ok" in result) || !result.ok || result.kind !== "changed") {
254
+ assert.fail(`expected changed, got ${JSON.stringify(result)}`);
255
+ return;
256
+ }
257
+ assert.equal(result.delta.commits.length, 2);
258
+ assert.equal(result.delta.touchedFiles.length, 2);
259
+ assert.match(result.delta.summaryLine, /2 commits, 2 files touched/);
260
+ } finally {
261
+ await cleanup();
262
+ }
263
+ });
264
+
265
+ test("handler/get: unreachable prior head → tagged failure, state advances", async () => {
266
+ const { storage, cleanup } = await makeTempStorage();
267
+ try {
268
+ // Seed headA.
269
+ const invoker1 = makeInvoker([
270
+ { args: ["rev-parse", "HEAD"], stdout: "headA\n" },
271
+ ]);
272
+ await handleCodingDelta(
273
+ { subcommand: "get", sessionKey: "s1" },
274
+ makeContext({ resolveStorage: async () => storage, gitInvoker: invoker1 }),
275
+ );
276
+
277
+ // Now head is headB but `git log headA..headB` fails (force-push).
278
+ const invoker2 = makeInvoker([
279
+ { args: ["rev-parse", "HEAD"], stdout: "headB\n" },
280
+ { args: ["log", "--reverse", "--pretty=format:%H\x1f%s", "--name-only", "headA..headB"], stdout: "", exitCode: 128 },
281
+ ]);
282
+ const result = await handleCodingDelta(
283
+ { subcommand: "get", sessionKey: "s1" },
284
+ makeContext({ resolveStorage: async () => storage, gitInvoker: invoker2 }),
285
+ );
286
+ if (!("ok" in result) || result.ok || result.code !== "unreachable_head") {
287
+ assert.fail(`expected unreachable_head, got ${JSON.stringify(result)}`);
288
+ return;
289
+ }
290
+ // nextState advanced so the NEXT call sees headB as the baseline.
291
+ assert.equal(result.nextState?.head, "headB");
292
+ } finally {
293
+ await cleanup();
294
+ }
295
+ });
296
+
297
+ test("handler/get: git rev-parse failure → git_failed, no state advance", async () => {
298
+ const { storage, cleanup } = await makeTempStorage();
299
+ try {
300
+ const invoker = makeInvoker([
301
+ { args: ["rev-parse", "HEAD"], stdout: "", exitCode: 128 },
302
+ ]);
303
+ const result = await handleCodingDelta(
304
+ { subcommand: "get", sessionKey: "s1" },
305
+ makeContext({ resolveStorage: async () => storage, gitInvoker: invoker }),
306
+ );
307
+ if (!("ok" in result) || result.ok || result.code !== "git_failed") {
308
+ assert.fail(`expected git_failed, got ${JSON.stringify(result)}`);
309
+ return;
310
+ }
311
+ // No nextState when the head itself was unreadable.
312
+ assert.equal(result.nextState, undefined);
313
+ } finally {
314
+ await cleanup();
315
+ }
316
+ });
317
+
318
+ test("handler/get: transient git log failure (exit 127) → git_failed, state NOT advanced", async () => {
319
+ const { storage, cleanup } = await makeTempStorage();
320
+ try {
321
+ // Seed headA.
322
+ const invoker1 = makeInvoker([
323
+ { args: ["rev-parse", "HEAD"], stdout: "headA\n" },
324
+ ]);
325
+ await handleCodingDelta(
326
+ { subcommand: "get", sessionKey: "s1" },
327
+ makeContext({ resolveStorage: async () => storage, gitInvoker: invoker1 }),
328
+ );
329
+
330
+ // Head is headB but git log times out (exit 127 = spawn/timeout).
331
+ const invoker2 = makeInvoker([
332
+ { args: ["rev-parse", "HEAD"], stdout: "headB\n" },
333
+ { args: ["log", "--reverse", "--pretty=format:%H\x1f%s", "--name-only", "headA..headB"], stdout: "", exitCode: 127 },
334
+ ]);
335
+ const result = await handleCodingDelta(
336
+ { subcommand: "get", sessionKey: "s1" },
337
+ makeContext({ resolveStorage: async () => storage, gitInvoker: invoker2 }),
338
+ );
339
+ if (!("ok" in result) || result.ok || result.code !== "git_failed") {
340
+ assert.fail(`expected git_failed, got ${JSON.stringify(result)}`);
341
+ return;
342
+ }
343
+ // State marker must NOT advance — headA preserved for retry on next call.
344
+ // Read the state file back to verify.
345
+ const { readFile } = await import("node:fs/promises");
346
+ const { sessionDeltaStatePath } = await import("./session-delta.js");
347
+ const raw = await readFile(
348
+ sessionDeltaStatePath(storage.memoryDir, storage.namespace),
349
+ "utf8",
350
+ );
351
+ const persisted = JSON.parse(raw);
352
+ assert.equal(persisted.head, "headA", "transient failure must not advance the state marker");
353
+ } finally {
354
+ await cleanup();
355
+ }
356
+ });
357
+
358
+ // ──────────────────────────────────────────────────────────────────────────
359
+ // Rule 25 — state write happens AFTER compute
360
+ // ──────────────────────────────────────────────────────────────────────────
361
+
362
+ test("handler/get: state file is created after a successful first_run", async () => {
363
+ const { storage, cleanup } = await makeTempStorage();
364
+ try {
365
+ const invoker = makeInvoker([
366
+ { args: ["rev-parse", "HEAD"], stdout: "head1\n" },
367
+ ]);
368
+ await handleCodingDelta(
369
+ { subcommand: "get", sessionKey: "s1" },
370
+ makeContext({ resolveStorage: async () => storage, gitInvoker: invoker }),
371
+ );
372
+ const { readFile } = await import("node:fs/promises");
373
+ const raw = await readFile(
374
+ // The handler uses sessionDeltaStatePath(memoryDir, namespace).
375
+ // We import and call the same helper for parity.
376
+ (await import("./session-delta.js")).sessionDeltaStatePath(storage.memoryDir, storage.namespace),
377
+ "utf8",
378
+ );
379
+ const parsed = JSON.parse(raw);
380
+ assert.equal(parsed.head, "head1");
381
+ assert.ok(parsed.at);
382
+ } finally {
383
+ await cleanup();
384
+ }
385
+ });