@modusensus/dsh-mneme 0.13.0 → 0.14.0-beta.2

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 (543) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +830 -828
  3. package/assets/icon.svg +3 -3
  4. package/bin/cli.mjs +964 -964
  5. package/bin/dsh-mneme-choro.mjs +262 -254
  6. package/bin/dsh-mneme-mcp.mjs +510 -510
  7. package/bin/dsh-mneme-serve.mjs +151 -151
  8. package/cordis.patch.yml +21 -21
  9. package/docs/CONFIGURATION.md +407 -402
  10. package/docs/CONTINUITY.md +139 -139
  11. package/docs/DAEMON.md +57 -57
  12. package/docs/DIRECTORIES.md +114 -114
  13. package/docs/DISTILL.md +397 -371
  14. package/docs/DSH-GO.md +97 -97
  15. package/docs/ENTITIES.md +247 -247
  16. package/docs/FORGET.md +77 -77
  17. package/docs/KB.md +110 -110
  18. package/docs/LOCAL_MODEL.md +222 -222
  19. package/docs/SEMANTIC.md +256 -256
  20. package/docs/SHARE.md +552 -552
  21. package/docs/SLEEP.md +202 -202
  22. package/docs/STORAGE.md +122 -122
  23. package/docs/WORKFLOWS.md +366 -366
  24. package/lib/api-standalone.js +1007 -1007
  25. package/lib/api.js +3600 -3394
  26. package/lib/attachment-text.js +122 -122
  27. package/lib/bootstrap-http.js +105 -105
  28. package/lib/bootstrap.js +215 -215
  29. package/lib/candidates.js +454 -454
  30. package/lib/capabilities.js +819 -819
  31. package/lib/choro-command.js +111 -111
  32. package/lib/client.js +14420 -13436
  33. package/lib/commands.js +95 -95
  34. package/lib/config-keys.js +144 -144
  35. package/lib/config.js +1361 -1317
  36. package/lib/content-hash.js +54 -54
  37. package/lib/continuity.js +258 -258
  38. package/lib/date-hint.js +32 -0
  39. package/lib/diary/auto.js +190 -190
  40. package/lib/diary/compose.js +440 -440
  41. package/lib/diary/dir.js +59 -59
  42. package/lib/diary/export.js +97 -97
  43. package/lib/diary/index.js +26 -26
  44. package/lib/diary/library.js +189 -189
  45. package/lib/diary/qq-inbox.js +142 -142
  46. package/lib/diary/rollover.js +61 -61
  47. package/lib/diary/session-provider.js +122 -122
  48. package/lib/document-index.js +94 -94
  49. package/lib/document.js +322 -322
  50. package/lib/dream/clustering.js +123 -123
  51. package/lib/dream/decisions.js +945 -907
  52. package/lib/dream/narratives.js +44 -44
  53. package/lib/dream/sleep.js +2027 -1887
  54. package/lib/dream/triage.js +139 -0
  55. package/lib/dream.js +1879 -1764
  56. package/lib/dsh-go/host.js +162 -162
  57. package/lib/dsh-go/panel.js +2091 -2091
  58. package/lib/dsh-go/text.js +342 -342
  59. package/lib/dsh-go/views/common.js +68 -68
  60. package/lib/dsh-go/views/entity.js +48 -48
  61. package/lib/dsh-go/views/memory.js +198 -198
  62. package/lib/dsh-go/views/root.js +30 -30
  63. package/lib/dsh-go/views/settings.js +398 -398
  64. package/lib/dsh-go/views/status.js +51 -51
  65. package/lib/embedding.js +342 -342
  66. package/lib/entities/extractor.js +285 -285
  67. package/lib/fence.js +24 -24
  68. package/lib/graph/anchoring.js +184 -184
  69. package/lib/guide.js +74 -74
  70. package/lib/heat.js +144 -144
  71. package/lib/hot-memory.js +119 -107
  72. package/lib/image-describe.js +158 -0
  73. package/lib/index.js +852 -822
  74. package/lib/inject.js +656 -639
  75. package/lib/kb/index.js +309 -309
  76. package/lib/kb/routes.js +219 -219
  77. package/lib/kb/tools.js +148 -148
  78. package/lib/lang.js +607 -584
  79. package/lib/local-embedder.js +358 -358
  80. package/lib/maintenance.js +162 -162
  81. package/lib/memory-source.js +9 -9
  82. package/lib/mirror.js +330 -330
  83. package/lib/organize.js +456 -456
  84. package/lib/peak-hours.js +172 -172
  85. package/lib/pipeline-activities.js +178 -178
  86. package/lib/pipeline-attribution.js +165 -165
  87. package/lib/pipeline-command.js +279 -279
  88. package/lib/pipeline-confirm.js +142 -142
  89. package/lib/pipeline-exec.js +174 -174
  90. package/lib/pipeline-nudge-auto.js +129 -129
  91. package/lib/pipeline-nudge.js +1623 -1623
  92. package/lib/pipeline-observations.js +181 -181
  93. package/lib/pipeline-observe.js +133 -133
  94. package/lib/pipeline-runner.js +361 -361
  95. package/lib/pipeline-runs.js +442 -442
  96. package/lib/pipeline.js +716 -716
  97. package/lib/placement.js +13 -13
  98. package/lib/pre-inject-gate.js +354 -354
  99. package/lib/profile-extract.js +164 -164
  100. package/lib/profile-facts.js +356 -356
  101. package/lib/provenance-http.js +251 -251
  102. package/lib/quality-filter.js +162 -162
  103. package/lib/recall-stats.js +190 -190
  104. package/lib/reclaim-http.js +150 -150
  105. package/lib/reranker.js +269 -269
  106. package/lib/runtime/adopt.js +176 -176
  107. package/lib/runtime/closure.js +172 -172
  108. package/lib/runtime/download.js +247 -247
  109. package/lib/runtime/layout.js +265 -265
  110. package/lib/runtime/loader.js +252 -252
  111. package/lib/runtime/model-fetch.js +361 -361
  112. package/lib/runtime/provision.js +237 -237
  113. package/lib/runtime/tarball.js +130 -130
  114. package/lib/runtime/verify.js +223 -223
  115. package/lib/safe-slice.js +18 -18
  116. package/lib/scope-state.js +277 -277
  117. package/lib/scope.js +187 -187
  118. package/lib/search/adaptive.js +60 -60
  119. package/lib/search/bm25.js +96 -96
  120. package/lib/semantic.js +572 -572
  121. package/lib/sensitive-scan.js +209 -209
  122. package/lib/serve.js +228 -228
  123. package/lib/service.js +3268 -3196
  124. package/lib/session-gate.js +91 -91
  125. package/lib/session-usage.js +350 -350
  126. package/lib/settings.js +827 -802
  127. package/lib/share-http.js +281 -281
  128. package/lib/share-import.js +234 -234
  129. package/lib/share.js +335 -335
  130. package/lib/store.js +5895 -5662
  131. package/lib/summarize.js +1388 -1343
  132. package/lib/tools.js +1370 -1356
  133. package/lib/user-settings.js +230 -230
  134. package/lib/vector-index.js +180 -180
  135. package/lib/version-check.js +122 -122
  136. package/lib/wiki/claims.js +267 -0
  137. package/lib/wiki/cli-hint.js +90 -90
  138. package/lib/wiki/inject.js +164 -164
  139. package/lib/wiki/partition.js +200 -200
  140. package/lib/wiki/plan.js +214 -214
  141. package/lib/wiki/queries.js +640 -474
  142. package/lib/wiki/render.js +261 -187
  143. package/lib/wiki/scan.js +371 -371
  144. package/lib/wiki/t0.js +500 -463
  145. package/lib/wiki/t1-jobs.js +232 -232
  146. package/lib/wiki/t1.js +609 -502
  147. package/lib/write-admission.js +404 -404
  148. package/locale/en.json +6 -6
  149. package/locale/zh.json +6 -6
  150. package/package.json +15 -1
  151. package/runtime-manifest.json +526 -526
  152. package/scripts/benchmark-embed.js +201 -201
  153. package/scripts/benchmark-recall.js +249 -249
  154. package/scripts/benchmark-rerank.js +166 -166
  155. package/scripts/build-runtime-manifest.mjs +224 -224
  156. package/scripts/changelog-prep.mjs +91 -91
  157. package/scripts/check-sync.js +44 -44
  158. package/scripts/e2e-dsh.js +220 -220
  159. package/scripts/mneme-runtime.mjs +277 -277
  160. package/scripts/publish-gate.mjs +197 -197
  161. package/scripts/release-docs.mjs +210 -204
  162. package/scripts/release-notes.mjs +164 -161
  163. package/scripts/stress-dsh.js +255 -255
  164. package/scripts/sync-lib.js +47 -47
  165. package/scripts/sync-test-badge.mjs +69 -69
  166. package/scripts/test-count-sync.mjs +48 -48
  167. package/src/api-standalone.js +1007 -1007
  168. package/src/api.js +3600 -3394
  169. package/src/attachment-text.js +122 -122
  170. package/src/bootstrap-http.js +105 -105
  171. package/src/bootstrap.js +215 -215
  172. package/src/candidates.js +454 -454
  173. package/src/capabilities.js +819 -819
  174. package/src/choro-command.js +111 -111
  175. package/src/commands.js +95 -95
  176. package/src/config-keys.js +144 -144
  177. package/src/config.js +1361 -1317
  178. package/src/content-hash.js +54 -54
  179. package/src/continuity.js +258 -258
  180. package/src/date-hint.js +32 -0
  181. package/src/diary/auto.js +190 -190
  182. package/src/diary/compose.js +440 -440
  183. package/src/diary/dir.js +59 -59
  184. package/src/diary/export.js +97 -97
  185. package/src/diary/index.js +26 -26
  186. package/src/diary/library.js +189 -189
  187. package/src/diary/qq-inbox.js +142 -142
  188. package/src/diary/rollover.js +61 -61
  189. package/src/diary/session-provider.js +122 -122
  190. package/src/document-index.js +94 -94
  191. package/src/document.js +322 -322
  192. package/src/dream/clustering.js +123 -123
  193. package/src/dream/decisions.js +945 -907
  194. package/src/dream/narratives.js +44 -44
  195. package/src/dream/sleep.js +2027 -1887
  196. package/src/dream/triage.js +139 -0
  197. package/src/dream.js +1879 -1764
  198. package/src/dsh-go/host.js +162 -162
  199. package/src/dsh-go/panel.js +2091 -2091
  200. package/src/dsh-go/text.js +342 -342
  201. package/src/dsh-go/views/common.js +68 -68
  202. package/src/dsh-go/views/entity.js +48 -48
  203. package/src/dsh-go/views/memory.js +198 -198
  204. package/src/dsh-go/views/root.js +30 -30
  205. package/src/dsh-go/views/settings.js +398 -398
  206. package/src/dsh-go/views/status.js +51 -51
  207. package/src/embedding.js +342 -342
  208. package/src/entities/extractor.js +285 -285
  209. package/src/fence.js +24 -24
  210. package/src/graph/anchoring.js +184 -184
  211. package/src/guide.js +74 -74
  212. package/src/heat.js +144 -144
  213. package/src/hot-memory.js +119 -107
  214. package/src/image-describe.js +158 -0
  215. package/src/index.js +852 -822
  216. package/src/inject.js +656 -639
  217. package/src/kb/index.js +309 -309
  218. package/src/kb/routes.js +219 -219
  219. package/src/kb/tools.js +148 -148
  220. package/src/lang.js +607 -584
  221. package/src/local-embedder.js +358 -358
  222. package/src/maintenance.js +162 -162
  223. package/src/memory-source.js +9 -9
  224. package/src/mirror.js +330 -330
  225. package/src/organize.js +456 -456
  226. package/src/peak-hours.js +172 -172
  227. package/src/pipeline-activities.js +178 -178
  228. package/src/pipeline-attribution.js +165 -165
  229. package/src/pipeline-command.js +279 -279
  230. package/src/pipeline-confirm.js +142 -142
  231. package/src/pipeline-exec.js +174 -174
  232. package/src/pipeline-nudge-auto.js +129 -129
  233. package/src/pipeline-nudge.js +1623 -1623
  234. package/src/pipeline-observations.js +181 -181
  235. package/src/pipeline-observe.js +133 -133
  236. package/src/pipeline-runner.js +361 -361
  237. package/src/pipeline-runs.js +442 -442
  238. package/src/pipeline.js +716 -716
  239. package/src/placement.js +13 -13
  240. package/src/pre-inject-gate.js +354 -354
  241. package/src/profile-extract.js +164 -164
  242. package/src/profile-facts.js +356 -356
  243. package/src/provenance-http.js +251 -251
  244. package/src/quality-filter.js +162 -162
  245. package/src/recall-stats.js +190 -190
  246. package/src/reclaim-http.js +150 -150
  247. package/src/reranker.js +269 -269
  248. package/src/runtime/adopt.js +176 -176
  249. package/src/runtime/closure.js +172 -172
  250. package/src/runtime/download.js +247 -247
  251. package/src/runtime/layout.js +265 -265
  252. package/src/runtime/loader.js +252 -252
  253. package/src/runtime/model-fetch.js +361 -361
  254. package/src/runtime/provision.js +237 -237
  255. package/src/runtime/tarball.js +130 -130
  256. package/src/runtime/verify.js +223 -223
  257. package/src/safe-slice.js +18 -18
  258. package/src/scope-state.js +277 -277
  259. package/src/scope.js +187 -187
  260. package/src/search/adaptive.js +60 -60
  261. package/src/search/bm25.js +96 -96
  262. package/src/semantic.js +572 -572
  263. package/src/sensitive-scan.js +209 -209
  264. package/src/serve.js +228 -228
  265. package/src/service.js +3268 -3196
  266. package/src/session-gate.js +91 -91
  267. package/src/session-usage.js +350 -350
  268. package/src/settings.js +827 -802
  269. package/src/share-http.js +281 -281
  270. package/src/share-import.js +234 -234
  271. package/src/share.js +335 -335
  272. package/src/store.js +5895 -5662
  273. package/src/summarize.js +1388 -1343
  274. package/src/tools.js +1370 -1356
  275. package/src/user-settings.js +230 -230
  276. package/src/vector-index.js +180 -180
  277. package/src/version-check.js +122 -122
  278. package/src/wiki/claims.js +267 -0
  279. package/src/wiki/cli-hint.js +90 -90
  280. package/src/wiki/inject.js +164 -164
  281. package/src/wiki/partition.js +200 -200
  282. package/src/wiki/plan.js +214 -214
  283. package/src/wiki/queries.js +640 -474
  284. package/src/wiki/render.js +261 -187
  285. package/src/wiki/scan.js +371 -371
  286. package/src/wiki/t0.js +500 -463
  287. package/src/wiki/t1-jobs.js +232 -232
  288. package/src/wiki/t1.js +609 -502
  289. package/src/write-admission.js +404 -404
  290. package/test/adaptive-inject-budget.test.js +85 -85
  291. package/test/api-hardening.test.js +173 -173
  292. package/test/api-nudge-route.test.js +259 -259
  293. package/test/api-routes-gaps.test.js +1092 -1092
  294. package/test/api.test.js +3007 -2988
  295. package/test/attachment-text.test.js +139 -139
  296. package/test/audit.test.js +448 -448
  297. package/test/baseline-persist.test.js +248 -248
  298. package/test/benchmark.test.js +94 -94
  299. package/test/bm25-corpus-cache.test.js +145 -145
  300. package/test/bootstrap.test.js +228 -228
  301. package/test/candidates.test.js +345 -345
  302. package/test/capabilities.test.js +577 -577
  303. package/test/capacity-warning.test.js +262 -262
  304. package/test/changelog-prep.test.js +125 -125
  305. package/test/choro-command.test.js +218 -218
  306. package/test/cli-enums.test.js +37 -37
  307. package/test/cli-flag-value.test.js +86 -86
  308. package/test/cli-receive.test.js +390 -390
  309. package/test/cli-share.test.js +437 -437
  310. package/test/client-share.test.js +225 -225
  311. package/test/client.test.js +4595 -4184
  312. package/test/clustering.test.js +100 -100
  313. package/test/commands.test.js +106 -106
  314. package/test/config-keys-routes.test.js +233 -233
  315. package/test/config.test.js +153 -153
  316. package/test/conflict-freeze.test.js +344 -344
  317. package/test/conflict-ledger-enforce.test.js +452 -0
  318. package/test/conflict-ledger.test.js +242 -0
  319. package/test/conflict-reviewed.test.js +79 -79
  320. package/test/content-hash.test.js +204 -204
  321. package/test/continuity.test.js +452 -452
  322. package/test/contrast.test.js +432 -320
  323. package/test/date-hint.test.js +156 -0
  324. package/test/dedupe-candidate-window.test.js +180 -180
  325. package/test/dependency-overrides.test.js +78 -78
  326. package/test/diary-library.test.js +187 -187
  327. package/test/diary.test.js +576 -576
  328. package/test/differentiate-feedback.test.js +248 -248
  329. package/test/distill-tx-embed-gap.test.js +110 -110
  330. package/test/docs-config-keys.test.js +65 -65
  331. package/test/document-dir.test.js +233 -233
  332. package/test/document.test.js +703 -703
  333. package/test/dream-archive-guardrail.test.js +116 -116
  334. package/test/dream-baseline-clamp.test.js +174 -0
  335. package/test/dream-candidate-hybrid.test.js +281 -281
  336. package/test/dream-coverage-degrade.test.js +86 -86
  337. package/test/dream-decision-evidence.test.js +314 -314
  338. package/test/dream-failure-backoff.test.js +173 -173
  339. package/test/dream-fence.test.js +207 -207
  340. package/test/dream-maxtokens-fallback.test.js +132 -132
  341. package/test/dream-merge-guardrail.test.js +219 -219
  342. package/test/dream-peak-hours.test.js +194 -194
  343. package/test/dream.test.js +1528 -1528
  344. package/test/dsh-go-host.test.js +134 -134
  345. package/test/dsh-go-panel-actions.test.js +854 -854
  346. package/test/dsh-go-panel.test.js +251 -251
  347. package/test/embed-ready-poll.test.js +94 -94
  348. package/test/embed-text-reasons.test.js +112 -112
  349. package/test/embedding-cache-f32.test.js +276 -276
  350. package/test/embedding-ready.test.js +226 -226
  351. package/test/entities.test.js +711 -711
  352. package/test/entity-recall.test.js +179 -179
  353. package/test/epistemic.test.js +325 -325
  354. package/test/failure-query.test.js +147 -147
  355. package/test/failure-retention.test.js +105 -105
  356. package/test/fnew-0112.test.js +311 -311
  357. package/test/fnew-03.test.js +422 -422
  358. package/test/forget-corrections.test.js +70 -70
  359. package/test/graph-anchoring.test.js +184 -184
  360. package/test/graph-api.test.js +333 -209
  361. package/test/graph-boosted-relations.test.js +189 -189
  362. package/test/graph-inject-budget.test.js +103 -103
  363. package/test/graph-multipath.test.js +269 -269
  364. package/test/graph-passive-confirm.test.js +91 -91
  365. package/test/graph-weight-evolution.test.js +104 -104
  366. package/test/heat.test.js +175 -175
  367. package/test/helpers/dream-mock.js +86 -86
  368. package/test/helpers/peer-worker.mjs +25 -25
  369. package/test/helpers/runtime-source.js +60 -60
  370. package/test/helpers/tar-builder.js +80 -80
  371. package/test/helpers/write-admission-samples.js +312 -312
  372. package/test/hot-memory.test.js +259 -211
  373. package/test/image-description.test.js +271 -0
  374. package/test/image-memory.test.js +170 -170
  375. package/test/inject-content-cap.test.js +91 -91
  376. package/test/inject-first-round-bm25.test.js +89 -89
  377. package/test/inject-heat.test.js +77 -77
  378. package/test/inject-parent-gate.test.js +134 -106
  379. package/test/inject-pin-explicit.test.js +300 -300
  380. package/test/inject-pin-vector-crowd.test.js +225 -225
  381. package/test/inject-pools.test.js +195 -195
  382. package/test/inject-recall.test.js +172 -172
  383. package/test/inject-rotation.test.js +161 -161
  384. package/test/inject.test.js +553 -494
  385. package/test/issue-617-step1-query-and-fusion.test.js +361 -361
  386. package/test/kb-api-wiring.test.js +128 -128
  387. package/test/kb-collections.test.js +272 -272
  388. package/test/kb-routes.test.js +379 -379
  389. package/test/lang.test.js +98 -98
  390. package/test/lastrunat-persist.test.js +101 -101
  391. package/test/lib-smoke.test.js +116 -116
  392. package/test/llm-audit.test.js +815 -815
  393. package/test/llm-message-source.test.js +207 -207
  394. package/test/local-embedder.test.js +292 -292
  395. package/test/maintenance.test.js +501 -501
  396. package/test/mcp-stdio.test.js +385 -385
  397. package/test/mint-source.test.js +311 -311
  398. package/test/mirror-dirty.test.js +424 -424
  399. package/test/mirror-edit-digest.test.js +187 -187
  400. package/test/mirror-generation.test.js +499 -499
  401. package/test/mirror-types.test.js +208 -208
  402. package/test/mirror.test.js +249 -249
  403. package/test/narrative-bars.test.js +162 -162
  404. package/test/organize-tool.test.js +232 -232
  405. package/test/organize.test.js +418 -418
  406. package/test/panel-chart-scale.test.js +90 -0
  407. package/test/panel-tokens.test.js +245 -0
  408. package/test/peak-hours-observability.test.js +70 -70
  409. package/test/peer-blockers.test.js +193 -193
  410. package/test/persona-addressing.test.js +83 -83
  411. package/test/persona-api.test.js +172 -172
  412. package/test/persona-by-session.test.js +568 -568
  413. package/test/persona-flag.test.js +36 -36
  414. package/test/persona-inject.test.js +145 -145
  415. package/test/persona-settings.test.js +198 -198
  416. package/test/pipeline-activities.test.js +155 -155
  417. package/test/pipeline-attribution.test.js +175 -175
  418. package/test/pipeline-command.test.js +243 -243
  419. package/test/pipeline-confirm.test.js +148 -148
  420. package/test/pipeline-exec.test.js +172 -172
  421. package/test/pipeline-nudge-auto.test.js +248 -248
  422. package/test/pipeline-nudge.test.js +1278 -1278
  423. package/test/pipeline-observations.test.js +157 -157
  424. package/test/pipeline-observe.test.js +127 -127
  425. package/test/pipeline-r1r2.test.js +278 -278
  426. package/test/pipeline-runner.test.js +468 -468
  427. package/test/pipeline-runs.test.js +767 -767
  428. package/test/pipeline.test.js +636 -636
  429. package/test/placement.test.js +36 -36
  430. package/test/policy-epoch.test.js +259 -259
  431. package/test/pre-inject-gate.test.js +444 -444
  432. package/test/profile-extract.test.js +269 -269
  433. package/test/profile-facts.test.js +492 -492
  434. package/test/prompt-stance.test.js +128 -128
  435. package/test/provenance-http.test.js +367 -367
  436. package/test/publish-gate.test.js +272 -272
  437. package/test/quality-backfill.test.js +290 -290
  438. package/test/quality-filter.test.js +295 -295
  439. package/test/readme-claims.test.js +113 -113
  440. package/test/reasoning-effort.test.js +752 -752
  441. package/test/recall-anti-update.test.js +82 -82
  442. package/test/recall-evals.test.js +253 -253
  443. package/test/recall-layer.test.js +326 -326
  444. package/test/recall-retention.test.js +128 -128
  445. package/test/recall-stats.test.js +274 -274
  446. package/test/receipt-chain.test.js +451 -451
  447. package/test/reclaim-http.test.js +209 -209
  448. package/test/reflection-update-gate.test.js +129 -129
  449. package/test/reflection.test.js +226 -226
  450. package/test/reindex-backfill.test.js +475 -475
  451. package/test/reindex-scheduler.test.js +369 -369
  452. package/test/release-docs.test.js +156 -136
  453. package/test/release-notes.test.js +265 -240
  454. package/test/release-workflow.test.js +93 -93
  455. package/test/reranker-single-logit.test.js +73 -73
  456. package/test/reranker.test.js +197 -197
  457. package/test/resident-status.test.js +83 -83
  458. package/test/route-policy.test.js +113 -113
  459. package/test/runtime-adopt.test.js +206 -206
  460. package/test/runtime-cli.test.js +89 -89
  461. package/test/runtime-closure.test.js +193 -193
  462. package/test/runtime-download.test.js +302 -302
  463. package/test/runtime-layout.test.js +163 -163
  464. package/test/runtime-loader.test.js +255 -255
  465. package/test/runtime-manifest.test.js +159 -159
  466. package/test/runtime-model-fetch.test.js +348 -348
  467. package/test/runtime-provision.test.js +101 -101
  468. package/test/runtime-tarball.test.js +81 -81
  469. package/test/runtime-verify.test.js +319 -319
  470. package/test/scope-candidates.test.js +491 -491
  471. package/test/scope-distill-stamp.test.js +134 -134
  472. package/test/scope-explicit.test.js +452 -452
  473. package/test/scope-inject-soft.test.js +106 -106
  474. package/test/scope-optional-service.test.js +110 -110
  475. package/test/scope-retrieval.test.js +197 -197
  476. package/test/scope-state.test.js +610 -610
  477. package/test/scope-storage.test.js +255 -255
  478. package/test/scope-strict.test.js +367 -367
  479. package/test/search-fusion.test.js +90 -90
  480. package/test/security-doc.test.js +188 -178
  481. package/test/semantic.test.js +124 -124
  482. package/test/sensitive-scan.test.js +251 -251
  483. package/test/serve-bin.test.js +121 -121
  484. package/test/serve.test.js +258 -258
  485. package/test/service-search.test.js +174 -174
  486. package/test/service.test.js +410 -410
  487. package/test/session-gate.test.js +264 -264
  488. package/test/session-usage.test.js +317 -317
  489. package/test/settings.test.js +273 -273
  490. package/test/share-foreign-rows.test.js +144 -144
  491. package/test/share-http.test.js +144 -144
  492. package/test/share-import.test.js +577 -577
  493. package/test/share-imported-guard.test.js +311 -311
  494. package/test/share-imported-identity.test.js +111 -111
  495. package/test/share-pack.test.js +511 -511
  496. package/test/share-receive-http.test.js +756 -756
  497. package/test/share-routes.test.js +337 -337
  498. package/test/share-store.test.js +133 -133
  499. package/test/share-validate.test.js +101 -101
  500. package/test/skip-invalid-restore.test.js +262 -262
  501. package/test/sleep-action-set.test.js +441 -441
  502. package/test/sleep-catchup.test.js +315 -315
  503. package/test/sleep-failure-backoff.test.js +156 -156
  504. package/test/sleep-heat.test.js +174 -174
  505. package/test/sleep-scheduler-rearm.test.js +89 -89
  506. package/test/sleep.test.js +1455 -1418
  507. package/test/standalone-api.test.js +1344 -1344
  508. package/test/store-vector-cache.test.js +98 -98
  509. package/test/store-write-path-columns.test.js +191 -191
  510. package/test/store.test.js +744 -744
  511. package/test/stress.test.js +209 -209
  512. package/test/summarize-occurred-at.test.js +127 -127
  513. package/test/summarize-parse-diagnostics.test.js +75 -75
  514. package/test/summarize-reasoning-effort.test.js +189 -189
  515. package/test/summarize.test.js +2251 -2251
  516. package/test/supersede-state-tracking.test.js +394 -394
  517. package/test/test-count-sync.test.js +71 -71
  518. package/test/tool-state-ignore.test.js +65 -65
  519. package/test/tools-dto-schema-matrix.test.js +398 -398
  520. package/test/tools-memory-get-schema.test.js +65 -65
  521. package/test/tools.test.js +585 -585
  522. package/test/updated-at-semantics.test.js +113 -113
  523. package/test/user-settings-preview.test.js +127 -127
  524. package/test/user-settings-segments.test.js +527 -527
  525. package/test/vector-index.test.js +334 -334
  526. package/test/version-check.test.js +152 -152
  527. package/test/webServer-optional.test.js +82 -82
  528. package/test/wiki-api.test.js +734 -627
  529. package/test/wiki-claims.test.js +194 -0
  530. package/test/wiki-cli-hint.test.js +118 -118
  531. package/test/wiki-inject.test.js +265 -265
  532. package/test/wiki-partition.test.js +112 -112
  533. package/test/wiki-plan.test.js +80 -80
  534. package/test/wiki-provenance.test.js +202 -0
  535. package/test/wiki-queries.test.js +398 -277
  536. package/test/wiki-scan.test.js +208 -208
  537. package/test/wiki-store.test.js +190 -190
  538. package/test/wiki-t0.test.js +456 -380
  539. package/test/wiki-t1-jobs.test.js +125 -125
  540. package/test/wiki-t1.test.js +460 -235
  541. package/test/wiki-tools.test.js +176 -176
  542. package/test/workflow-guards.test.js +155 -155
  543. package/test/write-admission.test.js +668 -668
@@ -1,112 +1,112 @@
1
- // partition.js 的回归锁:划分确定性(同输入两次逐项相同——spec §9 可重建不变量的
2
- // 前半段)、兄弟组内聚合并、超限逐级下切、slug 稳定、父子链按目录包含。
3
- import test from "node:test";
4
- import assert from "node:assert/strict";
5
- import { partitionModules } from "../src/wiki/partition.js";
6
-
7
- function graph(edges) {
8
- const files = [...new Set(edges.flatMap((e) => [e.from, e.to]))].sort();
9
- return { files, graph: { edges, external: new Map() } };
10
- }
11
-
12
- test("划分是确定性的:同输入两次结果逐项相同", () => {
13
- const input = graph([
14
- { from: "src/a.js", to: "src/b.js" },
15
- { from: "lib/c.js", to: "src/a.js" },
16
- ]);
17
- const one = partitionModules({ ...input, maxFilesPerPage: 40, repoName: "demo" });
18
- const two = partitionModules({ ...input, maxFilesPerPage: 40, repoName: "demo" });
19
- assert.deepEqual(one, two);
20
- });
21
-
22
- test("根直属文件成 root 模块;目录组各自成页;互不相干的组不合并", () => {
23
- // 显式给根直属文件(graph() 只从边推文件,不含根组)。
24
- const files = ["README.md", "package.json", "src/a.js", "src/b.js", "tools/x.js", "tools/y.js"];
25
- const g = {
26
- edges: [
27
- { from: "src/a.js", to: "src/b.js" },
28
- { from: "tools/x.js", to: "tools/y.js" },
29
- ],
30
- external: new Map(),
31
- };
32
- const mods = partitionModules({ files, graph: g, maxFilesPerPage: 40, repoName: "demo" });
33
- const slugs = mods.map((m) => m.slug).sort();
34
- assert.deepEqual(slugs, ["root", "src", "tools"]);
35
- const root = mods.find((m) => m.slug === "root");
36
- assert.deepEqual(root.files.sort(), ["README.md", "package.json"]);
37
- assert.equal(root.title, "demo(仓库根)");
38
- const src = mods.find((m) => m.slug === "src");
39
- assert.deepEqual(src.files.sort(), ["src/a.js", "src/b.js"]);
40
- });
41
-
42
- test("兄弟组交叉边 ≥3 才合并(内聚判据要能口述,V2 人判用)", () => {
43
- // utils 与 src 之间 3 条边 → 合并成一个模块
44
- const strong = graph([
45
- { from: "src/a.js", to: "utils/u1.js" },
46
- { from: "src/b.js", to: "utils/u1.js" },
47
- { from: "src/a.js", to: "utils/u2.js" },
48
- ]);
49
- const merged = partitionModules({ ...strong, maxFilesPerPage: 40, repoName: "demo" });
50
- assert.equal(merged.length, 1, "3 条交叉边触发合并");
51
- // 只有 2 条边 → 各自独立
52
- const weak = graph([
53
- { from: "src/a.js", to: "utils/u1.js" },
54
- { from: "src/b.js", to: "utils/u1.js" },
55
- ]);
56
- const separate = partitionModules({ ...weak, maxFilesPerPage: 40, repoName: "demo" });
57
- assert.equal(separate.length, 2, "2 条边不合并");
58
- });
59
-
60
- test("超 max_files_per_page 的组逐级下切,叶子组仍超限按字典序切块", () => {
61
- const files = [];
62
- const edges = [];
63
- for (let i = 0; i < 7; i++) {
64
- files.push(`big/f${i}.js`);
65
- if (i > 0) edges.push({ from: "big/f0.js", to: `big/f${i}.js` });
66
- }
67
- const mods = partitionModules({ files, graph: { edges, external: new Map() }, maxFilesPerPage: 3, repoName: "demo" });
68
- const total = mods.reduce((sum, m) => sum + m.fileCount, 0);
69
- assert.equal(total, 7, "切分不丢文件");
70
- assert.ok(mods.every((m) => m.fileCount <= 3), "每块都不超上限");
71
- assert.ok(mods.length >= 3, "7 个文件、上限 3 → 至少 3 块");
72
- // slug 唯一(切块组的 #2 序号后缀避免撞名)
73
- const slugs = mods.map((m) => m.slug);
74
- assert.equal(new Set(slugs).size, slugs.length, "slug 无重复");
75
- });
76
-
77
- test("父链按目录包含取最近祖先(spec frontmatter.parents / DB parent_id 同源)", () => {
78
- const { files, graph: g } = graph([
79
- { from: "src/store/index.js", to: "src/store/util.js" },
80
- { from: "src/wiki/t0.js", to: "src/store/index.js" },
81
- ]);
82
- const mods = partitionModules({ files, graph: g, maxFilesPerPage: 40, repoName: "demo" });
83
- // 初稿是两级目录段:src/store、src/wiki;「src」本身不是组 → 无父链也合法,
84
- // 但 src/store 与 src/wiki 之间没有包含关系(同层),互不为父。
85
- const store = mods.find((m) => m.dir === "src/store");
86
- const wiki = mods.find((m) => m.dir === "src/wiki");
87
- assert.ok(store && wiki);
88
- assert.equal(store.parentSlug, null, "同级目录不是父子");
89
- });
90
-
91
- test("模块指纹随文件集/边集变化(DB 记账与过期检测的输入)", () => {
92
- const a = partitionModules({ ...graph([{ from: "src/a.js", to: "src/b.js" }]), maxFilesPerPage: 40, repoName: "d" });
93
- const b = partitionModules({
94
- files: ["src/a.js", "src/b.js", "src/c.js"],
95
- graph: { edges: [{ from: "src/a.js", to: "src/b.js" }], external: new Map() },
96
- maxFilesPerPage: 40,
97
- repoName: "d",
98
- });
99
- const fa = a.find((m) => m.slug === "src").fingerprint;
100
- const fb = b.find((m) => m.slug === "src").fingerprint;
101
- assert.notEqual(fa, fb, "文件集变了指纹必须变");
102
- });
103
-
104
- test("外部依赖按模块聚合(骨架页「外部依赖」一节的数据源)", () => {
105
- const files = ["src/a.js", "src/b.js", "lib/c.js"];
106
- const external = new Map([["left-pad", new Set(["src/a.js", "src/b.js"])]]);
107
- const mods = partitionModules({ files, graph: { edges: [], external }, maxFilesPerPage: 40, repoName: "d" });
108
- const src = mods.find((m) => m.dir === "src");
109
- assert.deepEqual(src.externalDeps, [{ pkg: "left-pad", count: 2 }]);
110
- const lib = mods.find((m) => m.dir === "lib");
111
- assert.deepEqual(lib.externalDeps, []);
112
- });
1
+ // partition.js 的回归锁:划分确定性(同输入两次逐项相同——spec §9 可重建不变量的
2
+ // 前半段)、兄弟组内聚合并、超限逐级下切、slug 稳定、父子链按目录包含。
3
+ import test from "node:test";
4
+ import assert from "node:assert/strict";
5
+ import { partitionModules } from "../src/wiki/partition.js";
6
+
7
+ function graph(edges) {
8
+ const files = [...new Set(edges.flatMap((e) => [e.from, e.to]))].sort();
9
+ return { files, graph: { edges, external: new Map() } };
10
+ }
11
+
12
+ test("划分是确定性的:同输入两次结果逐项相同", () => {
13
+ const input = graph([
14
+ { from: "src/a.js", to: "src/b.js" },
15
+ { from: "lib/c.js", to: "src/a.js" },
16
+ ]);
17
+ const one = partitionModules({ ...input, maxFilesPerPage: 40, repoName: "demo" });
18
+ const two = partitionModules({ ...input, maxFilesPerPage: 40, repoName: "demo" });
19
+ assert.deepEqual(one, two);
20
+ });
21
+
22
+ test("根直属文件成 root 模块;目录组各自成页;互不相干的组不合并", () => {
23
+ // 显式给根直属文件(graph() 只从边推文件,不含根组)。
24
+ const files = ["README.md", "package.json", "src/a.js", "src/b.js", "tools/x.js", "tools/y.js"];
25
+ const g = {
26
+ edges: [
27
+ { from: "src/a.js", to: "src/b.js" },
28
+ { from: "tools/x.js", to: "tools/y.js" },
29
+ ],
30
+ external: new Map(),
31
+ };
32
+ const mods = partitionModules({ files, graph: g, maxFilesPerPage: 40, repoName: "demo" });
33
+ const slugs = mods.map((m) => m.slug).sort();
34
+ assert.deepEqual(slugs, ["root", "src", "tools"]);
35
+ const root = mods.find((m) => m.slug === "root");
36
+ assert.deepEqual(root.files.sort(), ["README.md", "package.json"]);
37
+ assert.equal(root.title, "demo(仓库根)");
38
+ const src = mods.find((m) => m.slug === "src");
39
+ assert.deepEqual(src.files.sort(), ["src/a.js", "src/b.js"]);
40
+ });
41
+
42
+ test("兄弟组交叉边 ≥3 才合并(内聚判据要能口述,V2 人判用)", () => {
43
+ // utils 与 src 之间 3 条边 → 合并成一个模块
44
+ const strong = graph([
45
+ { from: "src/a.js", to: "utils/u1.js" },
46
+ { from: "src/b.js", to: "utils/u1.js" },
47
+ { from: "src/a.js", to: "utils/u2.js" },
48
+ ]);
49
+ const merged = partitionModules({ ...strong, maxFilesPerPage: 40, repoName: "demo" });
50
+ assert.equal(merged.length, 1, "3 条交叉边触发合并");
51
+ // 只有 2 条边 → 各自独立
52
+ const weak = graph([
53
+ { from: "src/a.js", to: "utils/u1.js" },
54
+ { from: "src/b.js", to: "utils/u1.js" },
55
+ ]);
56
+ const separate = partitionModules({ ...weak, maxFilesPerPage: 40, repoName: "demo" });
57
+ assert.equal(separate.length, 2, "2 条边不合并");
58
+ });
59
+
60
+ test("超 max_files_per_page 的组逐级下切,叶子组仍超限按字典序切块", () => {
61
+ const files = [];
62
+ const edges = [];
63
+ for (let i = 0; i < 7; i++) {
64
+ files.push(`big/f${i}.js`);
65
+ if (i > 0) edges.push({ from: "big/f0.js", to: `big/f${i}.js` });
66
+ }
67
+ const mods = partitionModules({ files, graph: { edges, external: new Map() }, maxFilesPerPage: 3, repoName: "demo" });
68
+ const total = mods.reduce((sum, m) => sum + m.fileCount, 0);
69
+ assert.equal(total, 7, "切分不丢文件");
70
+ assert.ok(mods.every((m) => m.fileCount <= 3), "每块都不超上限");
71
+ assert.ok(mods.length >= 3, "7 个文件、上限 3 → 至少 3 块");
72
+ // slug 唯一(切块组的 #2 序号后缀避免撞名)
73
+ const slugs = mods.map((m) => m.slug);
74
+ assert.equal(new Set(slugs).size, slugs.length, "slug 无重复");
75
+ });
76
+
77
+ test("父链按目录包含取最近祖先(spec frontmatter.parents / DB parent_id 同源)", () => {
78
+ const { files, graph: g } = graph([
79
+ { from: "src/store/index.js", to: "src/store/util.js" },
80
+ { from: "src/wiki/t0.js", to: "src/store/index.js" },
81
+ ]);
82
+ const mods = partitionModules({ files, graph: g, maxFilesPerPage: 40, repoName: "demo" });
83
+ // 初稿是两级目录段:src/store、src/wiki;「src」本身不是组 → 无父链也合法,
84
+ // 但 src/store 与 src/wiki 之间没有包含关系(同层),互不为父。
85
+ const store = mods.find((m) => m.dir === "src/store");
86
+ const wiki = mods.find((m) => m.dir === "src/wiki");
87
+ assert.ok(store && wiki);
88
+ assert.equal(store.parentSlug, null, "同级目录不是父子");
89
+ });
90
+
91
+ test("模块指纹随文件集/边集变化(DB 记账与过期检测的输入)", () => {
92
+ const a = partitionModules({ ...graph([{ from: "src/a.js", to: "src/b.js" }]), maxFilesPerPage: 40, repoName: "d" });
93
+ const b = partitionModules({
94
+ files: ["src/a.js", "src/b.js", "src/c.js"],
95
+ graph: { edges: [{ from: "src/a.js", to: "src/b.js" }], external: new Map() },
96
+ maxFilesPerPage: 40,
97
+ repoName: "d",
98
+ });
99
+ const fa = a.find((m) => m.slug === "src").fingerprint;
100
+ const fb = b.find((m) => m.slug === "src").fingerprint;
101
+ assert.notEqual(fa, fb, "文件集变了指纹必须变");
102
+ });
103
+
104
+ test("外部依赖按模块聚合(骨架页「外部依赖」一节的数据源)", () => {
105
+ const files = ["src/a.js", "src/b.js", "lib/c.js"];
106
+ const external = new Map([["left-pad", new Set(["src/a.js", "src/b.js"])]]);
107
+ const mods = partitionModules({ files, graph: { edges: [], external }, maxFilesPerPage: 40, repoName: "d" });
108
+ const src = mods.find((m) => m.dir === "src");
109
+ assert.deepEqual(src.externalDeps, [{ pkg: "left-pad", count: 2 }]);
110
+ const lib = mods.find((m) => m.dir === "lib");
111
+ assert.deepEqual(lib.externalDeps, []);
112
+ });
@@ -1,80 +1,80 @@
1
- // plan.yaml 校验的回归锁(spec §4 / §9;实现计划 P1)。
2
- // 锁三类回归:未知键必须整轮拒绝(拼错的键等于失效的控制,不能静默忽略)、
3
- // 类型错误逐条收集一次报完(人一次改完,不逐个试错)、无 plan.yaml 走默认启发式。
4
- import test from "node:test";
5
- import assert from "node:assert/strict";
6
- import { loadPlan, planFingerprint, PLAN_ALLOWED_KEYS, PlanInvalidError } from "../src/wiki/plan.js";
7
-
8
- test("无 plan.yaml / 空文件走默认启发式", () => {
9
- const a = loadPlan(null);
10
- assert.equal(a.plan.maxFilesPerPage, 40);
11
- assert.deepEqual(a.plan.exclude, []);
12
- assert.equal(a.plan.pages, null);
13
- assert.deepEqual(a.plan.templates, {});
14
- // 「没带 plan」也是可记账状态:指纹 = 空串 sha256
15
- assert.equal(a.fingerprint, planFingerprint(""));
16
- const b = loadPlan(" \n");
17
- assert.equal(b.plan.maxFilesPerPage, 40);
18
- });
19
-
20
- test("未知键整轮拒绝,报出键名与允许列表(spec 降级表第一行)", () => {
21
- assert.throws(
22
- () => loadPlan("includes:\n - src\n"),
23
- (err) => {
24
- assert.ok(err instanceof PlanInvalidError);
25
- assert.ok(err.message.includes("includes"), "报错要点名拼错的键");
26
- assert.ok(err.message.includes("include"), "有相近键时给改正提示");
27
- assert.ok(err.message.includes("允许的顶层键"));
28
- for (const k of PLAN_ALLOWED_KEYS) assert.ok(err.message.includes(k));
29
- return true;
30
- }
31
- );
32
- });
33
-
34
- test("完全不相干的未知键不给误导性提示", () => {
35
- assert.throws(
36
- () => loadPlan("zzzzqqqq: 1\n"),
37
- (err) => err.message.includes("zzzzqqqq")
38
- );
39
- });
40
-
41
- test("类型错误逐条收集、一次报完(不 fail-fast)", () => {
42
- try {
43
- loadPlan("exclude: not-an-array\nmax_files_per_page: zero\npages:\n - path: \"\"\n");
44
- assert.fail("应该抛 PlanInvalidError");
45
- } catch (err) {
46
- assert.ok(err instanceof PlanInvalidError);
47
- assert.ok(err.issues.length >= 3, `至少三条 issue,实际 ${err.issues.length}`);
48
- assert.ok(err.issues.some((i) => i.includes("exclude")));
49
- assert.ok(err.issues.some((i) => i.includes("max_files_per_page")));
50
- assert.ok(err.issues.some((i) => i.includes("pages[0]")));
51
- }
52
- });
53
-
54
- test("YAML 语法错误也走同一条拒绝路径", () => {
55
- assert.throws(() => loadPlan("a: [1, 2\nb: }\n"), PlanInvalidError);
56
- });
57
-
58
- test("pages 白名单形状合法时被接受,模板/笔记被保留", () => {
59
- const { plan } = loadPlan(
60
- "pages:\n - path: src\n title: 源码主区\n template: arch\n notes: 关注导出面\n" +
61
- "templates:\n arch: |\n 自定义正文行\n"
62
- );
63
- assert.deepEqual(plan.pages, [{ path: "src", title: "源码主区", template: "arch", notes: "关注导出面" }]);
64
- assert.equal(plan.templates.arch, "自定义正文行\n");
65
- });
66
-
67
- test("pages 重复 path / 未知页级键 / 非 mapping 都被点名", () => {
68
- try {
69
- loadPlan("pages:\n - path: src\n - path: src\n - bogus: 1\n - 42\n");
70
- assert.fail("应该抛");
71
- } catch (err) {
72
- assert.ok(err.issues.some((i) => i.includes("重复")));
73
- assert.ok(err.issues.some((i) => i.includes("bogus")));
74
- assert.ok(err.issues.some((i) => i.includes("pages[3]")));
75
- }
76
- });
77
-
78
- test("指纹随内容变化(plan.yaml 改了就要重划边界)", () => {
79
- assert.notEqual(planFingerprint("a: 1\n"), planFingerprint("a: 2\n"));
80
- });
1
+ // plan.yaml 校验的回归锁(spec §4 / §9;实现计划 P1)。
2
+ // 锁三类回归:未知键必须整轮拒绝(拼错的键等于失效的控制,不能静默忽略)、
3
+ // 类型错误逐条收集一次报完(人一次改完,不逐个试错)、无 plan.yaml 走默认启发式。
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import { loadPlan, planFingerprint, PLAN_ALLOWED_KEYS, PlanInvalidError } from "../src/wiki/plan.js";
7
+
8
+ test("无 plan.yaml / 空文件走默认启发式", () => {
9
+ const a = loadPlan(null);
10
+ assert.equal(a.plan.maxFilesPerPage, 40);
11
+ assert.deepEqual(a.plan.exclude, []);
12
+ assert.equal(a.plan.pages, null);
13
+ assert.deepEqual(a.plan.templates, {});
14
+ // 「没带 plan」也是可记账状态:指纹 = 空串 sha256
15
+ assert.equal(a.fingerprint, planFingerprint(""));
16
+ const b = loadPlan(" \n");
17
+ assert.equal(b.plan.maxFilesPerPage, 40);
18
+ });
19
+
20
+ test("未知键整轮拒绝,报出键名与允许列表(spec 降级表第一行)", () => {
21
+ assert.throws(
22
+ () => loadPlan("includes:\n - src\n"),
23
+ (err) => {
24
+ assert.ok(err instanceof PlanInvalidError);
25
+ assert.ok(err.message.includes("includes"), "报错要点名拼错的键");
26
+ assert.ok(err.message.includes("include"), "有相近键时给改正提示");
27
+ assert.ok(err.message.includes("允许的顶层键"));
28
+ for (const k of PLAN_ALLOWED_KEYS) assert.ok(err.message.includes(k));
29
+ return true;
30
+ }
31
+ );
32
+ });
33
+
34
+ test("完全不相干的未知键不给误导性提示", () => {
35
+ assert.throws(
36
+ () => loadPlan("zzzzqqqq: 1\n"),
37
+ (err) => err.message.includes("zzzzqqqq")
38
+ );
39
+ });
40
+
41
+ test("类型错误逐条收集、一次报完(不 fail-fast)", () => {
42
+ try {
43
+ loadPlan("exclude: not-an-array\nmax_files_per_page: zero\npages:\n - path: \"\"\n");
44
+ assert.fail("应该抛 PlanInvalidError");
45
+ } catch (err) {
46
+ assert.ok(err instanceof PlanInvalidError);
47
+ assert.ok(err.issues.length >= 3, `至少三条 issue,实际 ${err.issues.length}`);
48
+ assert.ok(err.issues.some((i) => i.includes("exclude")));
49
+ assert.ok(err.issues.some((i) => i.includes("max_files_per_page")));
50
+ assert.ok(err.issues.some((i) => i.includes("pages[0]")));
51
+ }
52
+ });
53
+
54
+ test("YAML 语法错误也走同一条拒绝路径", () => {
55
+ assert.throws(() => loadPlan("a: [1, 2\nb: }\n"), PlanInvalidError);
56
+ });
57
+
58
+ test("pages 白名单形状合法时被接受,模板/笔记被保留", () => {
59
+ const { plan } = loadPlan(
60
+ "pages:\n - path: src\n title: 源码主区\n template: arch\n notes: 关注导出面\n" +
61
+ "templates:\n arch: |\n 自定义正文行\n"
62
+ );
63
+ assert.deepEqual(plan.pages, [{ path: "src", title: "源码主区", template: "arch", notes: "关注导出面" }]);
64
+ assert.equal(plan.templates.arch, "自定义正文行\n");
65
+ });
66
+
67
+ test("pages 重复 path / 未知页级键 / 非 mapping 都被点名", () => {
68
+ try {
69
+ loadPlan("pages:\n - path: src\n - path: src\n - bogus: 1\n - 42\n");
70
+ assert.fail("应该抛");
71
+ } catch (err) {
72
+ assert.ok(err.issues.some((i) => i.includes("重复")));
73
+ assert.ok(err.issues.some((i) => i.includes("bogus")));
74
+ assert.ok(err.issues.some((i) => i.includes("pages[3]")));
75
+ }
76
+ });
77
+
78
+ test("指纹随内容变化(plan.yaml 改了就要重划边界)", () => {
79
+ assert.notEqual(planFingerprint("a: 1\n"), planFingerprint("a: 2\n"));
80
+ });
@@ -0,0 +1,202 @@
1
+ // ① 语义段溯源(verifiedBaseline / verifiedAt)的回归锁。
2
+ //
3
+ // 这一层要防的不是「少写一个字段」,而是两个具体的假象:
4
+ // 1. **T1 晚于 T0 的误报**:claims 的行号是 T1 那一刻的代码树,全仓 git_baseline 是 T0 的。
5
+ // 若拿全仓基线起算 diff,T1 之前就发生、claim 早已算进去的改动会被报成 touched
6
+ // ——面板上就是「明明没动过,却催你复核」。所以起点必须是每页自己的 verified_baseline。
7
+ // 2. **T0 的确定性被污染**:verified 字段只由 T1 写。T0 一旦也写时间戳,
8
+ // 「同状态两次 apply 逐字节相同」这条不变量就破了(测试 wiki-t0.test.js 里那条)。
9
+ import test from "node:test";
10
+ import assert from "node:assert/strict";
11
+ import { mkdtempSync, rmSync, writeFileSync, mkdirSync, readFileSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { execFileSync } from "node:child_process";
15
+ import { runChoroT0, gitHead } from "../src/wiki/t0.js";
16
+ import { createStore } from "../src/store.js";
17
+ import { createChoroQueries } from "../src/wiki/queries.js";
18
+ import { createT1 } from "../src/wiki/t1.js";
19
+ import {
20
+ FM_VERIFIED_AT,
21
+ FM_VERIFIED_BASELINE,
22
+ readFrontmatterKey,
23
+ setFrontmatterKeys,
24
+ T1_SECTION_START,
25
+ } from "../src/wiki/render.js";
26
+
27
+ const FIXED_NOW = () => "2026-11-05T12:00:00.000Z";
28
+
29
+ function makeFixture() {
30
+ const root = mkdtempSync(join(tmpdir(), "choro-prov-"));
31
+ writeFileSync(join(root, "package.json"), JSON.stringify({ name: "provfix", scripts: { test: "node --test" } }, null, 2) + "\n", "utf8");
32
+ mkdirSync(join(root, "src"), { recursive: true });
33
+ writeFileSync(join(root, "src", "a.js"), 'import { b } from "./b.js";\nexport const a = () => b();\n', "utf8");
34
+ writeFileSync(join(root, "src", "b.js"), "export const b = () => 1;\n", "utf8");
35
+ execFileSync("git", ["init", "-b", "main"], { cwd: root, stdio: "pipe" });
36
+ commit(root, "init");
37
+ return root;
38
+ }
39
+
40
+ function commit(root, message) {
41
+ execFileSync("git", ["-c", "user.email=t@t", "-c", "user.name=t", "add", "-A"], { cwd: root, stdio: "pipe" });
42
+ execFileSync("git", ["-c", "user.email=t@t", "-c", "user.name=t", "commit", "-m", message, "--no-gpg-sign"], { cwd: root, stdio: "pipe" });
43
+ return gitHead(root);
44
+ }
45
+
46
+ function rmTempBestEffort(dir) {
47
+ try {
48
+ rmSync(dir, { recursive: true, force: true });
49
+ } catch {
50
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 200);
51
+ try { rmSync(dir, { recursive: true, force: true }); } catch { /* 留给系统清理 */ }
52
+ }
53
+ }
54
+
55
+ const GOOD_OUTPUT = JSON.stringify({
56
+ architecture: "src 模块承载核心导出面,a.js 依赖 b.js。",
57
+ convention: "导出全部用 export const 函数式风格。",
58
+ techStack: "纯 ESM,无框架。",
59
+ setup: "npm test。",
60
+ claims: [{ statement: "a.js 从 b.js 导入 b", kind: "architecture", sourcePath: "src/a.js", startLine: 1, endLine: 1 }],
61
+ });
62
+
63
+ // --- frontmatter 读写(页面格式层,与 T0/T1 无关的纯函数)---------------------
64
+
65
+ test("setFrontmatterKeys:无则追加、有则覆盖、空值删键、无 frontmatter 原样返回", () => {
66
+ const page = "---\nchoro: v1\nslug: src\n---\n\n# 标题\n";
67
+ const added = setFrontmatterKeys(page, [[FM_VERIFIED_BASELINE, "abc1234"], [FM_VERIFIED_AT, FIXED_NOW()]]);
68
+ assert.equal(readFrontmatterKey(added, FM_VERIFIED_BASELINE), "abc1234");
69
+ assert.equal(readFrontmatterKey(added, FM_VERIFIED_AT), FIXED_NOW());
70
+ assert.ok(added.indexOf("---") < added.indexOf("verifiedBaseline"), "落在 frontmatter 里,不是正文");
71
+
72
+ const overwritten = setFrontmatterKeys(added, [[FM_VERIFIED_BASELINE, "def5678"]]);
73
+ assert.equal(readFrontmatterKey(overwritten, FM_VERIFIED_BASELINE), "def5678");
74
+ assert.equal(overwritten.match(/^verifiedBaseline:/gm).length, 1, "覆盖而不是重复追加");
75
+
76
+ const removed = setFrontmatterKeys(added, [[FM_VERIFIED_BASELINE, null]]);
77
+ assert.equal(readFrontmatterKey(removed, FM_VERIFIED_BASELINE), null);
78
+ assert.equal(/^verifiedBaseline:/m.test(removed), false, "删键不留空行——「没记下」要查得出来");
79
+
80
+ // 人写的坏文件(没有 frontmatter)不许被凭空造一个块出来
81
+ assert.equal(setFrontmatterKeys("# 纯正文\n", [[FM_VERIFIED_BASELINE, "x"]]), "# 纯正文\n");
82
+ assert.equal(readFrontmatterKey("# 纯正文\n", FM_VERIFIED_BASELINE), null);
83
+ });
84
+
85
+ test("readFrontmatterKey:缩进列表项不算顶层键,带引号的值剥引号", () => {
86
+ const page = '---\nslug: src\nsourceFiles:\n - verifiedBaseline: 假的\nverifiedBaseline: "a:b"\n---\n';
87
+ assert.equal(readFrontmatterKey(page, FM_VERIFIED_BASELINE), "a:b", "只认顶层键,列表里同名的行不误命中");
88
+ });
89
+
90
+ // --- T0 一个字节都不产出这两个键 ---------------------------------------------
91
+
92
+ test("T0 骨架页不含 verified 键:确定性不变量不许被溯源污染", () => {
93
+ const root = makeFixture();
94
+ const store = createStore(":memory:");
95
+ try {
96
+ runChoroT0({ repoRoot: root, store, apply: true, now: FIXED_NOW });
97
+ const page = readFileSync(join(root, ".chorography", "modules", "src.md"), "utf8");
98
+ assert.equal(page.includes(FM_VERIFIED_BASELINE), false, "骨架页没有「哪次验证的」这回事");
99
+ assert.equal(page.includes(FM_VERIFIED_AT), false);
100
+ } finally {
101
+ store.close();
102
+ rmTempBestEffort(root);
103
+ }
104
+ });
105
+
106
+ // --- T1 写下那一刻的 HEAD,且 claims 起点跟着它走 -----------------------------
107
+
108
+ test("T1 富化写下 verifiedBaseline/verifiedAt,且等于那一刻的 HEAD", async () => {
109
+ const root = makeFixture();
110
+ const store = createStore(":memory:");
111
+ try {
112
+ runChoroT0({ repoRoot: root, store, apply: true, now: FIXED_NOW });
113
+ const head = commit(root, "t1 之前的一次提交");
114
+ const t1 = createT1({ store, runSession: async () => ({ ok: true, text: GOOD_OUTPUT }), now: FIXED_NOW });
115
+ const receipt = await t1.runT1({ repoRoot: root, moduleSlug: "src" });
116
+ assert.equal(receipt.enriched.length, 1);
117
+ assert.equal(receipt.enriched[0].verifiedBaseline, head, "回执里带得出来,人不用去翻文件");
118
+
119
+ const page = readFileSync(join(root, ".chorography", "modules", "src.md"), "utf8");
120
+ assert.equal(readFrontmatterKey(page, FM_VERIFIED_BASELINE), head);
121
+ assert.equal(readFrontmatterKey(page, FM_VERIFIED_AT), FIXED_NOW());
122
+ assert.ok(page.includes(T1_SECTION_START), "语义段照旧");
123
+
124
+ const row = store.db.prepare(
125
+ "SELECT p.verified_baseline, p.verified_at FROM choro_pages p JOIN choro_modules m ON p.module_id = m.id WHERE m.slug = 'src'"
126
+ ).get();
127
+ assert.equal(row.verified_baseline, head, "文件与库同源同写");
128
+ assert.equal(row.verified_at, FIXED_NOW());
129
+
130
+ const evidence = JSON.parse(readFileSync(join(root, ".chorography", "evidence", "src.json"), "utf8"));
131
+ assert.equal(evidence.verifiedBaseline, head, "evidence 旁证也带起点");
132
+ } finally {
133
+ store.close();
134
+ rmTempBestEffort(root);
135
+ }
136
+ });
137
+
138
+ test("claimDrift:T1 晚于 T0 时,T1 之前的改动不误报 touched(这就是 per-page 基线的理由)", async () => {
139
+ const root = makeFixture();
140
+ const store = createStore(":memory:");
141
+ try {
142
+ runChoroT0({ repoRoot: root, store, apply: true, now: FIXED_NOW });
143
+ // T0 之后、T1 之前有人改了 a.js 第 1 行——claim 读的就是改完之后的这棵树。
144
+ writeFileSync(join(root, "src", "a.js"), 'export const a = () => 1;\n', "utf8");
145
+ commit(root, "T0 之后、T1 之前");
146
+ const t1 = createT1({ store, runSession: async () => ({ ok: true, text: GOOD_OUTPUT }), now: FIXED_NOW });
147
+ await t1.runT1({ repoRoot: root, moduleSlug: "src" });
148
+
149
+ const q = createChoroQueries({ store });
150
+ const repo = q.resolveRepo(undefined);
151
+ const drift = q.claimDrift(repo.id);
152
+ assert.equal(drift.totals.claims, 1);
153
+ assert.equal(drift.totals.atRiskClaims, 0, "拿全仓基线起算会在这里误报 touched(旧的 bug)");
154
+ assert.equal(drift.clean, true);
155
+ const pageDrift = drift.pages.find((p) => p.slug === "src");
156
+ assert.equal(pageDrift.verifiedBaseline, drift.head, "起点就是 HEAD");
157
+ assert.equal(pageDrift.baselineApprox, false);
158
+
159
+ // 真改了 claim 锚点所在行:这时必须报出来,别因为「起点是 HEAD」就永远说干净。
160
+ writeFileSync(join(root, "src", "a.js"), 'export const a = () => 2;\n', "utf8");
161
+ commit(root, "真的动了锚点");
162
+ const drift2 = q.claimDrift(repo.id);
163
+ assert.equal(drift2.clean, false);
164
+ assert.equal(drift2.totals.atRiskClaims, 1);
165
+ assert.equal(drift2.pages.find((p) => p.slug === "src").atRisk[0].reason, "touched");
166
+ } finally {
167
+ store.close();
168
+ rmTempBestEffort(root);
169
+ }
170
+ });
171
+
172
+ test("claimDrift:老数据(没有逐页基线)退到全仓基线,并标明这是近似起点", () => {
173
+ const root = makeFixture();
174
+ const store = createStore(":memory:");
175
+ try {
176
+ runChoroT0({ repoRoot: root, store, apply: true, now: FIXED_NOW });
177
+ const q = createChoroQueries({ store });
178
+ const repo = q.resolveRepo(undefined);
179
+ // 本功能之前富化的页:库里有 claims、没有 verified_baseline(迁移加列后的老行)。
180
+ const page = store.choroPageBySlug(repo.id, "src");
181
+ store.saveChoroPageClaims(page.id, [
182
+ { statement: "a 依赖 b", kind: "architecture", sourcePath: "src/a.js", startLine: 2, endLine: 2, createdAt: FIXED_NOW() },
183
+ ]);
184
+ store.db.prepare("UPDATE choro_pages SET verified_baseline = NULL, verified_at = NULL WHERE id = ?").run(page.id);
185
+
186
+ const drift = q.claimDrift(repo.id);
187
+ const pageDrift = drift.pages.find((p) => p.slug === "src");
188
+ assert.equal(pageDrift.verifiedBaseline, null, "真话:这页没记过逐页基线");
189
+ assert.equal(pageDrift.baselineApprox, true, "退而求其次的起点要标出来");
190
+ assert.equal(pageDrift.baselineUnknown, false, "有了保底起点就不再是「判不了」");
191
+ assert.equal(pageDrift.diffBaseline, drift.baseline, "用的就是全仓基线");
192
+
193
+ // 全仓基线也缺(极老的行)→ 才轮到 baselineUnknown,一条都不判。
194
+ store.db.prepare("UPDATE choro_repos SET git_baseline = '' WHERE id = ?").run(repo.id);
195
+ const drift2 = q.claimDrift(repo.id);
196
+ assert.equal(drift2.pages.find((p) => p.slug === "src").baselineUnknown, true);
197
+ assert.equal(drift2.totals.atRiskClaims, 0, "判不了就是判不了,不冒充没事");
198
+ } finally {
199
+ store.close();
200
+ rmTempBestEffort(root);
201
+ }
202
+ });