@sanqianx/project-knowledge 5.0.24 → 5.0.26

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 (401) hide show
  1. package/.agents/plugins/marketplace.json +20 -20
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/CHANGELOG.md +1361 -1353
  4. package/LICENSE +201 -201
  5. package/NOTICE +5 -5
  6. package/README.md +349 -349
  7. package/_modules/knowledge-engine/package.json +1 -1
  8. package/_modules/knowledge-engine/src/coordinator.js +48 -3
  9. package/_modules/knowledge-engine/src/runtime/automation-config.js +140 -140
  10. package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +238 -238
  11. package/_modules/knowledge-engine/src/runtime/commit-processing-ledger.js +26 -26
  12. package/_modules/knowledge-engine/src/runtime/commit-prompt.js +193 -193
  13. package/_modules/knowledge-engine/src/runtime/commit-reconciler.js +394 -394
  14. package/_modules/knowledge-engine/src/runtime/contracts.js +282 -282
  15. package/_modules/knowledge-engine/src/runtime/conversation-store.js +389 -389
  16. package/_modules/knowledge-engine/src/runtime/evidence-bundle.js +228 -228
  17. package/_modules/knowledge-engine/src/runtime/git-runner.js +62 -62
  18. package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +505 -505
  19. package/_modules/knowledge-engine/src/runtime/knowledge-retrieval-service.js +400 -400
  20. package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +171 -171
  21. package/_modules/knowledge-engine/src/runtime/scanner.js +298 -298
  22. package/_modules/knowledge-engine/ui/app.js +23 -2
  23. package/_site/README.md +30 -30
  24. package/_site/_test/agent-capture-mcp-ui-test.js +68 -68
  25. package/_site/_test/agent-capture-ui-test.js +101 -101
  26. package/_site/_test/ai-profile-resolver-test.js +137 -137
  27. package/_site/_test/ai-profile-test.js +75 -75
  28. package/_site/_test/ai-vendor-presets-test.js +91 -91
  29. package/_site/_test/automatic-context-test.js +70 -70
  30. package/_site/_test/automation-queue-test.js +14 -14
  31. package/_site/_test/automation-ui-test.js +40 -40
  32. package/_site/_test/background-task-registry-test.js +43 -43
  33. package/_site/_test/baseline-schema-test.js +91 -91
  34. package/_site/_test/bin-cli-test.js +230 -230
  35. package/_site/_test/bridge-adapter-test.js +95 -95
  36. package/_site/_test/bridge-consumer-service-test.js +310 -310
  37. package/_site/_test/candidate-bin-content-test.js +35 -35
  38. package/_site/_test/candidate-product-assets-test.js +13 -13
  39. package/_site/_test/candidate-release-contract-test.js +14 -14
  40. package/_site/_test/capture-hook-dedup-test.js +158 -158
  41. package/_site/_test/cdp-browser-failure-test.js +103 -103
  42. package/_site/_test/chat-claudecodeui-match-test.js +92 -92
  43. package/_site/_test/claude-executable-discovery-test.js +90 -90
  44. package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
  45. package/_site/_test/claude-workbench-test.js +174 -174
  46. package/_site/_test/cli-instance-management-test.js +62 -62
  47. package/_site/_test/client-capture-restart-e2e-test.js +189 -189
  48. package/_site/_test/codex-conversation-projection-test.js +60 -60
  49. package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
  50. package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
  51. package/_site/_test/codex-notify-fanout-test.js +141 -134
  52. package/_site/_test/codex-notify-upgrade-test.js +253 -253
  53. package/_site/_test/commit-boundary-freeze-test.js +147 -147
  54. package/_site/_test/commit-conversation-binding-test.js +99 -99
  55. package/_site/_test/commit-evidence-test.js +110 -110
  56. package/_site/_test/configuration-generation-test.js +22 -22
  57. package/_site/_test/context-pack-test.js +200 -200
  58. package/_site/_test/conversation-api-test.js +119 -119
  59. package/_site/_test/conversation-origin-test.js +15 -15
  60. package/_site/_test/conversation-store-test.js +138 -138
  61. package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
  62. package/_site/_test/desktop-browser-compat-test.js +28 -28
  63. package/_site/_test/desktop-hook-runtime-regression-test.js +204 -204
  64. package/_site/_test/embedding-config-test.js +32 -32
  65. package/_site/_test/explicit-commit-processor-test.js +37 -37
  66. package/_site/_test/fixtures/fake-browser-exit.js +10 -10
  67. package/_site/_test/fixtures/fake-browser-hang.js +10 -10
  68. package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
  69. package/_site/_test/fixtures/make-git-repos.js +92 -92
  70. package/_site/_test/fixtures/retrieval-gold-queries.json +32 -32
  71. package/_site/_test/folder-picker-output-test.js +19 -19
  72. package/_site/_test/fresh-hook-reimport-test.js +53 -53
  73. package/_site/_test/fresh-product-data-test.js +56 -56
  74. package/_site/_test/full-integration-e2e-test.js +167 -167
  75. package/_site/_test/git-path-compatibility-test.js +80 -80
  76. package/_site/_test/git-validation-test.js +81 -81
  77. package/_site/_test/glm-ui-refresh-review-test.js +313 -404
  78. package/_site/_test/harness-isolation-test.js +138 -0
  79. package/_site/_test/helpers/cdp-browser.js +383 -0
  80. package/_site/_test/helpers/cdp-network-capture.js +66 -0
  81. package/_site/_test/helpers/find-chrome.js +34 -0
  82. package/_site/_test/helpers/free-port.js +8 -0
  83. package/_site/_test/helpers/logging-ui-fixture.js +131 -0
  84. package/_site/_test/helpers/module-fixture.js +282 -0
  85. package/_site/_test/helpers/parallel-engine-advanced-engine-child.js +45 -0
  86. package/_site/_test/helpers/parallel-engine-advanced-fault-writer.js +171 -0
  87. package/_site/_test/helpers/parallel-engine-advanced-harness.js +245 -0
  88. package/_site/_test/helpers/parallel-engine-advanced-standin-analyzer.js +80 -0
  89. package/_site/_test/helpers/parallel-engine-advanced-ui-host.js +177 -0
  90. package/_site/_test/helpers/parallel-engine-final-harness.js +314 -0
  91. package/_site/_test/helpers/parallel-engine-final-standin-analyzer.js +159 -0
  92. package/_site/_test/helpers/parallel-knowledge-fixture.js +255 -0
  93. package/_site/_test/helpers/parallel-logging-acceptance-fixture.js +111 -0
  94. package/_site/_test/helpers/parallel-logging-acceptance-proxy.js +106 -0
  95. package/_site/_test/helpers/parallel-logging-fix-r2-proxy.js +205 -0
  96. package/_site/_test/helpers/parallel-logging-integration-5025-env.js +432 -0
  97. package/_site/_test/helpers/parallel-memory-analyzer.js +81 -0
  98. package/_site/_test/helpers/parallel-memory-event-bridge.js +128 -0
  99. package/_site/_test/helpers/parallel-memory-shell.js +470 -0
  100. package/_site/_test/helpers/parallel-nav-isolation-env.js +389 -0
  101. package/_site/_test/helpers/parallel-nav-module-ui.js +186 -0
  102. package/_site/_test/helpers/parallel-nav-proxy.js +148 -0
  103. package/_site/_test/helpers/parallel-nav-shell.js +361 -0
  104. package/_site/_test/helpers/parallel-qa-engine-endpoint-guard.js +206 -0
  105. package/_site/_test/helpers/parallel-qa-engine-endpoint-isolate.js +257 -0
  106. package/_site/_test/helpers/parallel-retrieval-followup-corpus.js +293 -0
  107. package/_site/_test/helpers/parallel-retrieval-followup-next-session.js +70 -0
  108. package/_site/_test/helpers/parallel-retrieval-followup-r2-shell.js +737 -0
  109. package/_site/_test/helpers/parallel-retrieval-followup-r2-wb-host.js +82 -0
  110. package/_site/_test/helpers/parallel-retrieval-followup-shell.js +279 -0
  111. package/_site/_test/helpers/parallel-workbench-fixture.js +371 -0
  112. package/_site/_test/helpers/parallel-workbench-host.js +80 -0
  113. package/_site/_test/helpers/parallel-workbench-integration-5025-fixture.js +408 -0
  114. package/_site/_test/helpers/parallel-workbench-integration-5025-harness.js +593 -0
  115. package/_site/_test/helpers/parallel-workbench-integration-5025-host.js +70 -0
  116. package/_site/_test/helpers/parallel-workbench-integration-5025-launcher.js +240 -0
  117. package/_site/_test/helpers/parallel-workbench-integration-5025-runtime-harness.js +630 -0
  118. package/_site/_test/helpers/parallel-workbench-source-map.js +196 -0
  119. package/_site/_test/helpers/spawn-server.js +106 -0
  120. package/_site/_test/helpers/version-visibility-fixture.js +251 -0
  121. package/_site/_test/hook-runtime-endpoint-test.js +88 -88
  122. package/_site/_test/hook-status-repair-api-test.js +152 -152
  123. package/_site/_test/hook-trigger-test.js +85 -85
  124. package/_site/_test/hook-worktree-test.js +42 -42
  125. package/_site/_test/import-preflight-api-test.js +234 -234
  126. package/_site/_test/index-builder-test.js +41 -41
  127. package/_site/_test/index-error-display-contract-test.js +10 -10
  128. package/_site/_test/index-writer-concurrency-test.js +135 -135
  129. package/_site/_test/integration-adapters-test.js +262 -262
  130. package/_site/_test/integration-auto-repair-test.js +113 -113
  131. package/_site/_test/integration-capture-setup-test.js +118 -118
  132. package/_site/_test/integration-intent-test.js +81 -81
  133. package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
  134. package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
  135. package/_site/_test/integration-runtime-binding-test.js +473 -473
  136. package/_site/_test/integration-status-scan-test.js +29 -29
  137. package/_site/_test/integration-surface-coverage-test.js +164 -164
  138. package/_site/_test/kb-framework-test.js +57 -57
  139. package/_site/_test/kbpath-follow-test.js +63 -63
  140. package/_site/_test/knowledge-db-test.js +144 -144
  141. package/_site/_test/knowledge-engine-client-test.js +120 -109
  142. package/_site/_test/knowledge-language-control-test.js +170 -170
  143. package/_site/_test/knowledge-maintenance-test.js +40 -40
  144. package/_site/_test/knowledge-promotion-recovery-test.js +260 -260
  145. package/_site/_test/knowledge-query-test.js +54 -54
  146. package/_site/_test/knowledge-retrieval-service-test.js +77 -77
  147. package/_site/_test/knowledge-scopes-test.js +37 -37
  148. package/_site/_test/knowledge-storage-location-test.js +28 -28
  149. package/_site/_test/knowledge-storage-startup-test.js +52 -52
  150. package/_site/_test/knowledge-store-logs-supervision-test.js +89 -89
  151. package/_site/_test/launcher-readiness-test.js +80 -80
  152. package/_site/_test/legacy-claude-session-alias-test.js +106 -106
  153. package/_site/_test/legacy-routes-removed-test.js +47 -47
  154. package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
  155. package/_site/_test/live-gateway-test.js +48 -48
  156. package/_site/_test/local-marketplace-test.js +16 -16
  157. package/_site/_test/log-redaction-test.js +58 -58
  158. package/_site/_test/logging-api-test.js +89 -89
  159. package/_site/_test/logging-sse-no-gap-test.js +88 -88
  160. package/_site/_test/logging-ui-test.js +107 -107
  161. package/_site/_test/markdown-delta-overlay-test.js +85 -85
  162. package/_site/_test/markdown-maintenance-api-test.js +75 -75
  163. package/_site/_test/markdown-maintenance-test.js +11 -11
  164. package/_site/_test/mcp-server-test.js +149 -149
  165. package/_site/_test/model-context-windows-test.js +89 -89
  166. package/_site/_test/module-agent-start-proxy-test.js +65 -65
  167. package/_site/_test/module-artifact-integrity-test.js +27 -27
  168. package/_site/_test/module-boundary-test.js +30 -30
  169. package/_site/_test/module-bridge-eventbridge-test.js +202 -202
  170. package/_site/_test/module-dynamic-port-test.js +118 -118
  171. package/_site/_test/module-index-proxy-test.js +33 -33
  172. package/_site/_test/module-model-configuration-test.js +44 -44
  173. package/_site/_test/module-process-lifecycle-test.js +35 -35
  174. package/_site/_test/module-stream-proxy-test.js +34 -34
  175. package/_site/_test/module-terminal-restart-test.js +63 -63
  176. package/_site/_test/module-wiring-unbind-race-test.js +59 -59
  177. package/_site/_test/native-context-integrations-test.js +86 -86
  178. package/_site/_test/non-release-ci-test.js +34 -34
  179. package/_site/_test/offline-boundary-isolation-test.js +32 -32
  180. package/_site/_test/orphaned-claim-recovery-test.js +80 -80
  181. package/_site/_test/package-boundary-test.js +43 -43
  182. package/_site/_test/package-startup-test.js +56 -56
  183. package/_site/_test/packaged-ui-smoke-test.js +88 -88
  184. package/_site/_test/parallel-ci-r9-posix-socket-budget-test.js +156 -0
  185. package/_site/_test/parallel-engine-advanced-eng005-test.js +136 -0
  186. package/_site/_test/parallel-engine-advanced-eng006-test.js +171 -0
  187. package/_site/_test/parallel-engine-advanced-eng007-reconcile-test.js +231 -0
  188. package/_site/_test/parallel-engine-advanced-eng009-test.js +208 -0
  189. package/_site/_test/parallel-engine-advanced-eng010-test.js +213 -0
  190. package/_site/_test/parallel-engine-advanced-eng011-ui-probe.js +459 -0
  191. package/_site/_test/parallel-engine-final-eng002-test.js +255 -0
  192. package/_site/_test/parallel-engine-final-eng007-test.js +269 -0
  193. package/_site/_test/parallel-engine-final-eng011-test.js +197 -0
  194. package/_site/_test/parallel-engine-final-eng012-test.js +354 -0
  195. package/_site/_test/parallel-knowledge-client-error-code-test.js +184 -0
  196. package/_site/_test/parallel-knowledge-engine-test.js +353 -0
  197. package/_site/_test/parallel-knowledge-retrieval-test.js +292 -0
  198. package/_site/_test/parallel-knowledge-vector-test.js +168 -0
  199. package/_site/_test/parallel-linux-ci-r7-log007-day-test.js +110 -0
  200. package/_site/_test/parallel-logging-acceptance-log003-test.js +224 -0
  201. package/_site/_test/parallel-logging-acceptance-log004-test.js +231 -0
  202. package/_site/_test/parallel-logging-acceptance-log005-test.js +208 -0
  203. package/_site/_test/parallel-logging-acceptance-log006-test.js +214 -0
  204. package/_site/_test/parallel-logging-acceptance-log007-test.js +223 -0
  205. package/_site/_test/parallel-logging-acceptance-log008-test.js +180 -0
  206. package/_site/_test/parallel-logging-fix-r2-crash-probe.js +271 -0
  207. package/_site/_test/parallel-logging-fix-r2-gap-test.js +294 -0
  208. package/_site/_test/parallel-logging-fix-r2-normalhook-test.js +330 -0
  209. package/_site/_test/parallel-logging-integration-5025-portability-test.js +554 -0
  210. package/_site/_test/parallel-memory-candidate-test.js +301 -0
  211. package/_site/_test/parallel-memory-manual-test.js +197 -0
  212. package/_site/_test/parallel-memory-scope-test.js +228 -0
  213. package/_site/_test/parallel-memory-source-test.js +227 -0
  214. package/_site/_test/parallel-memory-sync-test.js +238 -0
  215. package/_site/_test/parallel-memory-view-r8-reload-gate-test.js +123 -0
  216. package/_site/_test/parallel-nav-failure-test.js +119 -0
  217. package/_site/_test/parallel-nav-isolation-test.js +308 -0
  218. package/_site/_test/parallel-nav-isolation-vector-path-test.js +90 -0
  219. package/_site/_test/parallel-nav-offline-test.js +272 -0
  220. package/_site/_test/parallel-nav-operability-test.js +257 -0
  221. package/_site/_test/parallel-nav-switch-test.js +247 -0
  222. package/_site/_test/parallel-nav-theme-test.js +130 -0
  223. package/_site/_test/parallel-nav-viewport-test.js +310 -0
  224. package/_site/_test/parallel-qa-engine-endpoint-guard-test.js +273 -0
  225. package/_site/_test/parallel-retrieval-followup-citation-test.js +219 -0
  226. package/_site/_test/parallel-retrieval-followup-corpus-test.js +287 -0
  227. package/_site/_test/parallel-workbench-independent-host.js +262 -0
  228. package/_site/_test/parallel-workbench-integration-5025-abort-probe.js +340 -0
  229. package/_site/_test/parallel-workbench-integration-5025-drafts-probe.js +588 -0
  230. package/_site/_test/parallel-workbench-integration-5025-env-check.js +317 -0
  231. package/_site/_test/parallel-workbench-integration-5025-model-probe.js +342 -0
  232. package/_site/_test/parallel-workbench-integration-5025-ordering-probe.js +326 -0
  233. package/_site/_test/parallel-workbench-model-probe.js +336 -0
  234. package/_site/_test/parallel-workbench-offline-test.js +169 -0
  235. package/_site/_test/parallel-workbench-provenance-trace.js +134 -0
  236. package/_site/_test/parallel-workbench-source-match.js +206 -0
  237. package/_site/_test/parallel-workbench-standalone-turn.js +349 -0
  238. package/_site/_test/path-consistency-test.js +151 -151
  239. package/_site/_test/pending-sweep-test.js +9 -9
  240. package/_site/_test/post-commit-automation-test.js +87 -87
  241. package/_site/_test/pr-consumer-contract-test.js +55 -55
  242. package/_site/_test/product-brand-contract-test.js +25 -25
  243. package/_site/_test/product-diagnostics-ui-test.js +93 -93
  244. package/_site/_test/product-empty-state-test.js +148 -0
  245. package/_site/_test/product-import-ui-test.js +219 -104
  246. package/_site/_test/product-navigation-test.js +152 -0
  247. package/_site/_test/product-progress-status-test.js +84 -84
  248. package/_site/_test/project-delete-recovery-test.js +64 -64
  249. package/_site/_test/project-domain-create-test.js +91 -0
  250. package/_site/_test/project-domain-scope-retrieval-test.js +214 -0
  251. package/_site/_test/project-git-init-confirm-test.js +115 -0
  252. package/_site/_test/project-goal-editor-test.js +144 -144
  253. package/_site/_test/project-import-duplicate-alias-test.js +134 -0
  254. package/_site/_test/project-import-unicode-restart-test.js +140 -0
  255. package/_site/_test/project-lifecycle-transaction-test.js +106 -106
  256. package/_site/_test/project-module-partial-recovery-test.js +222 -0
  257. package/_site/_test/project-remove-running-guard-test.js +79 -79
  258. package/_site/_test/project-store-test.js +90 -90
  259. package/_site/_test/project-unbind-late-read-ui-test.js +250 -0
  260. package/_site/_test/project-unbind-writer-guard-test.js +231 -0
  261. package/_site/_test/prompt-settings-test.js +115 -115
  262. package/_site/_test/protected-architecture-gate-test.js +122 -122
  263. package/_site/_test/refactor-characterization-test.js +36 -36
  264. package/_site/_test/registry-events-test.js +38 -38
  265. package/_site/_test/release-version-sync-test.js +115 -115
  266. package/_site/_test/requirement-binding-test.js +113 -113
  267. package/_site/_test/requirement-recorder-test.js +173 -173
  268. package/_site/_test/run-all-tests.js +173 -161
  269. package/_site/_test/runtime-endpoint-test.js +64 -64
  270. package/_site/_test/scanner-test.js +83 -83
  271. package/_site/_test/server-security-test.js +96 -96
  272. package/_site/_test/sessions-stream-test.js +93 -93
  273. package/_site/_test/shared-contracts-test.js +58 -58
  274. package/_site/_test/shared-scope-root-binding-test.js +74 -67
  275. package/_site/_test/simple-import-test.js +98 -98
  276. package/_site/_test/startup-analysis-disabled-test.js +16 -16
  277. package/_site/_test/storage-foundation-test.js +78 -78
  278. package/_site/_test/structured-logger-test.js +101 -101
  279. package/_site/_test/t07b-cln-005-coverage-test.js +212 -0
  280. package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
  281. package/_site/_test/team-knowledge-store-test.js +132 -132
  282. package/_site/_test/test-server-cleanup-test.js +35 -35
  283. package/_site/_test/test-server-module-isolation-test.js +37 -37
  284. package/_site/_test/tracking-start-test.js +144 -144
  285. package/_site/_test/vector-storage-root-test.js +75 -75
  286. package/_site/_test/version-visibility-browser-probe.js +249 -0
  287. package/_site/_test/version-visibility-test.js +92 -0
  288. package/_site/_test/workbench-context-receipts-test.js +22 -22
  289. package/_site/_test/workbench-permission-test.js +117 -117
  290. package/_site/_test/zcode-capture-integration-test.js +112 -112
  291. package/_site/lib/ai-profile-resolver.js +78 -78
  292. package/_site/lib/ai-vendor-presets.js +83 -83
  293. package/_site/lib/ai-workspace.js +104 -104
  294. package/_site/lib/atomic-file.js +204 -204
  295. package/_site/lib/automation-config.js +140 -140
  296. package/_site/lib/bridge-adapter.js +260 -260
  297. package/_site/lib/bridge-consumer-service.js +402 -402
  298. package/_site/lib/capture-hook-config.js +108 -108
  299. package/_site/lib/claude-cli-runner.js +1599 -1599
  300. package/_site/lib/cli-instances.js +43 -43
  301. package/_site/lib/codex-context-status.js +17 -17
  302. package/_site/lib/codex-notify-config.js +193 -193
  303. package/_site/lib/commit-conversation-binder.js +247 -247
  304. package/_site/lib/commit-processing-ledger.js +27 -27
  305. package/_site/lib/commit-prompt.js +202 -202
  306. package/_site/lib/commit-reconciler.js +444 -444
  307. package/_site/lib/context-pack-builder.js +299 -299
  308. package/_site/lib/contracts.js +291 -291
  309. package/_site/lib/conversation-exclusions.js +204 -204
  310. package/_site/lib/conversation-query-service.js +161 -161
  311. package/_site/lib/conversation-store.js +387 -387
  312. package/_site/lib/data-dir.js +13 -13
  313. package/_site/lib/embedding-service.js +85 -85
  314. package/_site/lib/engine-connection.js +93 -93
  315. package/_site/lib/engine-knowledge-client.js +76 -72
  316. package/_site/lib/evidence-bundle.js +228 -228
  317. package/_site/lib/folder-picker-output.js +24 -24
  318. package/_site/lib/git-path.js +14 -14
  319. package/_site/lib/git-runner.js +62 -62
  320. package/_site/lib/github-team-store.js +1175 -1175
  321. package/_site/lib/hook-manager.js +203 -203
  322. package/_site/lib/index-builder.js +157 -157
  323. package/_site/lib/index-service.js +173 -173
  324. package/_site/lib/integration-auto-repair.js +77 -77
  325. package/_site/lib/integration-installer.js +1240 -1240
  326. package/_site/lib/integration-intent.js +88 -88
  327. package/_site/lib/integration-status-scan.js +99 -99
  328. package/_site/lib/integration-status-worker.js +14 -14
  329. package/_site/lib/kb-framework.js +191 -191
  330. package/_site/lib/kb-validator.js +124 -124
  331. package/_site/lib/knowledge-db.js +363 -363
  332. package/_site/lib/knowledge-promotion.js +435 -435
  333. package/_site/lib/knowledge-retrieval-service.js +401 -401
  334. package/_site/lib/knowledge-schema.js +121 -121
  335. package/_site/lib/knowledge-tool-runtime.js +366 -366
  336. package/_site/lib/live-gateway.js +78 -78
  337. package/_site/lib/llm-client.js +173 -173
  338. package/_site/lib/markdown-knowledge-indexer.js +230 -230
  339. package/_site/lib/model-context-windows.js +82 -82
  340. package/_site/lib/module-bridge.js +874 -874
  341. package/_site/lib/module-port-resolver.js +57 -57
  342. package/_site/lib/post-commit-automation.js +141 -141
  343. package/_site/lib/product-data.js +35 -35
  344. package/_site/lib/project-lifecycle-service.js +554 -554
  345. package/_site/lib/project-registry-store.js +129 -129
  346. package/_site/lib/project-store.js +268 -268
  347. package/_site/lib/prompt-registry.js +148 -148
  348. package/_site/lib/public-version-info.js +37 -0
  349. package/_site/lib/registry-events.js +16 -16
  350. package/_site/lib/requirement-adapters.js +10 -10
  351. package/_site/lib/requirement-binder.js +112 -112
  352. package/_site/lib/requirement-recorder.js +278 -278
  353. package/_site/lib/runtime-control.js +89 -89
  354. package/_site/lib/runtime-endpoint.js +160 -160
  355. package/_site/lib/scanner.js +298 -298
  356. package/_site/lib/server-app.js +1672 -1664
  357. package/_site/lib/settings-store.js +153 -153
  358. package/_site/lib/storage-layout.js +166 -166
  359. package/_site/lib/structured-logger.js +574 -574
  360. package/_site/lib/zcode-context-integration.js +60 -60
  361. package/_site/scripts/codex-notify-fanout.cjs +95 -95
  362. package/_site/scripts/folder-picker.ps1 +156 -156
  363. package/_site/scripts/hook-trigger.js +158 -158
  364. package/_site/scripts/install-module-candidates.js +69 -69
  365. package/_site/scripts/pack-module-candidates.js +89 -89
  366. package/_site/scripts/sync-product-content.js +28 -28
  367. package/_site/scripts/sync-release-version.js +164 -164
  368. package/_site/scripts/vendor-modules.js +49 -49
  369. package/_site/scripts/verify-module-runtime.js +50 -50
  370. package/_site/scripts/verify-product-runtime.js +36 -36
  371. package/_site/scripts/verify-retrieval-gold.js +83 -83
  372. package/_site/server.js +15 -15
  373. package/_site/start.bat +26 -26
  374. package/_site/stop.bat +11 -11
  375. package/bin/project-knowledge-integrations.js +254 -254
  376. package/bin/project-knowledge-kb.js +67 -67
  377. package/bin/project-knowledge-mcp.js +201 -201
  378. package/bin/project-knowledge.js +269 -170
  379. package/claude-prompts.json +21 -21
  380. package/docs/README.zh-CN.md +207 -205
  381. package/docs/assets/architecture.svg +181 -181
  382. package/docs/assets/logo.svg +49 -49
  383. package/docs/fresh-v5-start.md +44 -44
  384. package/docs/leader-glm-workbench-playbook.md +122 -122
  385. package/docs/project-registry-schema.md +33 -33
  386. package/docs/release-5.0.16-acceptance.md +176 -176
  387. package/docs/release-5.0.17-acceptance.md +117 -117
  388. package/docs/release-5.0.19-acceptance.md +39 -39
  389. package/docs/team-knowledge-mode-a-plan.md +73 -73
  390. package/docs/testing-strategy.md +33 -33
  391. package/module-runtime-manifest.json +68 -65
  392. package/package.json +113 -110
  393. package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
  394. package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
  395. package/plugins/project-knowledge/.mcp.json +1 -1
  396. package/templates/change.md +30 -30
  397. package/templates/project-readme.md +52 -52
  398. package/ui/index.html +3 -2
  399. package/ui/version-info.css +44 -0
  400. package/ui/version-info.js +24 -0
  401. package/vendor-manifest.json +55 -55
@@ -0,0 +1,219 @@
1
+ 'use strict';
2
+ // Offline regression for the RET-007 attribution contract.
3
+ //
4
+ // What is locked here: a promoted knowledge entry must carry the FULL commit
5
+ // SHA and the project it belongs to, a later session must retrieve that entry
6
+ // by a semantic query that contains NO identifier, and an unrelated hex query
7
+ // must not reach the entry through the exact-commit channel.
8
+ //
9
+ // What is NOT claimed here: the analyzer is a DECLARED STUB (no model is
10
+ // called) and the vector endpoint is a DECLARED STUB that returns no rows, so
11
+ // this file says nothing about model behaviour or about vector recall. The
12
+ // real-model chain and the real index live in
13
+ // qa-autopilot/tools/product-probes/parallel-retrieval-followup-ret007.js,
14
+ // which is deliberately outside the offline CI.
15
+ //
16
+ // Everything runs in isolation: own data directory, own knowledge root, own
17
+ // temporary repository, own HTTP ports, no ambient module address, no real
18
+ // HOME.
19
+ const assert = require('node:assert/strict');
20
+ const fs = require('node:fs');
21
+ const path = require('node:path');
22
+ const http = require('node:http');
23
+ const crypto = require('node:crypto');
24
+
25
+ const shell = require('./helpers/parallel-retrieval-followup-shell');
26
+ // Isolate before any product module is loaded.
27
+ shell.isolateProcess();
28
+
29
+ const { createEngineServer } = require('../../_modules/knowledge-engine/src/server');
30
+ const AtomicFile = require('../../_modules/knowledge-engine/src/runtime/atomic-file');
31
+
32
+ const TOKEN = 'RET007OFFLINE';
33
+ const results = [];
34
+
35
+ function record(caseId, status, detail) {
36
+ results.push({ caseId, status, detail });
37
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
38
+ }
39
+
40
+ async function stubVectorServer() {
41
+ const requests = [];
42
+ const server = http.createServer((req, res) => {
43
+ let body = '';
44
+ req.on('data', chunk => { body += chunk; });
45
+ req.on('end', () => {
46
+ requests.push({ url: req.url, method: req.method });
47
+ res.writeHead(200, { 'Content-Type': 'application/json' });
48
+ res.end(JSON.stringify({ ok: true, results: [], errors: [], projects: [] }));
49
+ });
50
+ });
51
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
52
+ return {
53
+ requests,
54
+ url: `http://127.0.0.1:${server.address().port}`,
55
+ close: () => { server.closeAllConnections(); return new Promise(resolve => server.close(resolve)); },
56
+ };
57
+ }
58
+
59
+ function forbiddenBridge() {
60
+ const guard = name => async () => { throw new Error(`event bridge must not be contacted from the test lane (${name})`); };
61
+ return {
62
+ registerConsumer: guard('registerConsumer'),
63
+ listJournals: guard('listJournals'),
64
+ readEvents: guard('readEvents'),
65
+ ackConsumerCursor: guard('ackConsumerCursor'),
66
+ close() {},
67
+ };
68
+ }
69
+
70
+ // Declared stub analyzer: stages one Markdown file plus a manifest that
71
+ // carries the four required evidence references, exactly like the real
72
+ // analyzer's contract. It never contacts a model.
73
+ function stubAnalyzer(events = []) {
74
+ return {
75
+ sourceReadMode: 'isolated-frozen-evidence',
76
+ async runClaim(input) {
77
+ events.push({ kind: 'start', projectId: input.projectId, sha: input.claim.commitSha });
78
+ const relative = `changes/${input.claim.commitSha.slice(0, 12)}.md`;
79
+ const content = `# 离线归属条目\n\n${TOKEN} 事实:重试上限调整为 7 次,提交 ${input.claim.commitSha}。\n`;
80
+ AtomicFile.writeFileAtomic(path.join(input.stagingPath, 'files', ...relative.split('/')), content);
81
+ AtomicFile.writeJsonAtomic(input.manifestPath, {
82
+ schema: 'knowledge-staging-manifest/v1',
83
+ projectId: input.projectId,
84
+ runId: input.claim.runId,
85
+ commitSha: input.claim.commitSha,
86
+ operations: [{
87
+ path: relative,
88
+ operation: 'create',
89
+ sha256: `sha256:${crypto.createHash('sha256').update(content).digest('hex')}`,
90
+ reason: 'Frozen Git evidence',
91
+ evidenceReferences: [
92
+ `commit:${input.claim.commitSha}`,
93
+ `patch:${input.claim.patchHash}`,
94
+ `conversation:${input.claim.conversationSnapshotHash}`,
95
+ `retrieval:${input.claim.retrievalManifestHash}`,
96
+ ],
97
+ }],
98
+ });
99
+ events.push({ kind: 'finish', projectId: input.projectId, sha: input.claim.commitSha });
100
+ return { ok: true };
101
+ },
102
+ };
103
+ }
104
+
105
+ async function startEngine({ dataDir, vectorUrl, analyzer }) {
106
+ fs.mkdirSync(dataDir, { recursive: true });
107
+ const runtime = createEngineServer({ dataDir, vectorUrl, bridge: forbiddenBridge(), analyzer });
108
+ await runtime.ready;
109
+ await new Promise(resolve => runtime.server.listen(0, '127.0.0.1', resolve));
110
+ const base = `http://127.0.0.1:${runtime.server.address().port}`;
111
+ return {
112
+ ...runtime,
113
+ base,
114
+ async call(route, body) {
115
+ const response = await fetch(`${base}${route}`, {
116
+ method: body === undefined ? 'GET' : 'POST',
117
+ headers: { 'Content-Type': 'application/json' },
118
+ body: body === undefined ? undefined : JSON.stringify(body),
119
+ });
120
+ return { status: response.status, body: await response.json() };
121
+ },
122
+ };
123
+ }
124
+
125
+ (async () => {
126
+ const root = shell.makeTempDir('parallel-retrieval-followup-citation');
127
+ const vector = await stubVectorServer();
128
+ const analyzerEvents = [];
129
+ let engine = null;
130
+ try {
131
+ const repo = shell.makeRepo(root, 'repo-citation', [
132
+ { message: '初始提交', files: { 'README.md': '# citation\n' } },
133
+ { message: '事实提交', files: { 'note.txt': `${TOKEN} 重试上限调整为 7 次。\n` } },
134
+ ]);
135
+ const knowledgePath = shell.makeKnowledgeDir(root, 'knowledge-citation');
136
+ engine = await startEngine({
137
+ dataDir: path.join(root, 'engine-citation'),
138
+ vectorUrl: vector.url,
139
+ analyzer: stubAnalyzer(analyzerEvents),
140
+ });
141
+ const registration = await engine.call('/api/projects', {
142
+ projectId: 'project-citation',
143
+ name: 'citation',
144
+ repoPath: repo.repoPath,
145
+ knowledgePath,
146
+ brainId: 'brain-default',
147
+ domainId: 'domain-tools',
148
+ });
149
+ assert.equal(registration.status, 200, `the project registration failed: ${JSON.stringify(registration.body)}`);
150
+
151
+ const run = engine.coordinator.enqueue({ projectId: 'project-citation', commitSha: repo.head });
152
+ const settled = await shell.eventually(() => ['succeeded', 'failed'].includes(engine.store.getRun(run.runId).state), 20000);
153
+ assert.equal(settled, true, 'the run must reach a terminal state');
154
+ const finished = engine.store.getRun(run.runId);
155
+ assert.equal(finished.state, 'succeeded', `the promotion failed: ${JSON.stringify(finished.error)}`);
156
+
157
+ const commitSha = repo.head;
158
+ const entryRelative = `changes/${commitSha.slice(0, 12)}.md`;
159
+ const promoted = fs.readFileSync(path.join(knowledgePath, ...entryRelative.split('/')), 'utf8');
160
+ assert.ok(promoted.includes(TOKEN), 'the promoted body must carry the planted fact');
161
+ assert.ok(promoted.includes(commitSha), 'the promoted body must carry the full commit SHA');
162
+
163
+ // A later session asks with a semantic query that contains no identifier.
164
+ const semanticQuery = `${TOKEN} 重试上限调整为几次`;
165
+ assert.ok(!/[0-9a-f]{7}/i.test(semanticQuery), 'the semantic query must not contain a hex identifier');
166
+
167
+ const search = await engine.coordinator.retrieval.search({ query: semanticQuery, projectId: 'project-citation', client: 'workbench', sessionId: 'offline-session' });
168
+ const rows = search.results || [];
169
+ const hit = rows.find(row => row.commitSha === commitSha);
170
+ assert.ok(hit, `the promoted entry must be retrieved with its full SHA; rows: ${JSON.stringify(rows.map(row => ({ entry: row.entry_id, sha: row.commitSha, channels: row.match_channels })))}`);
171
+ assert.equal(hit.projectId, 'project-citation', 'the retrieved entry must keep its project');
172
+ assert.ok(String(hit.chunk_text || hit.snippet || '').includes(TOKEN), 'the retrieved entry must carry the planted body');
173
+ assert.ok(!(hit.match_channels || []).includes('exact-commit'), 'a semantic query must not reach the entry through the exact-commit channel');
174
+
175
+ const asked = await engine.coordinator.retrieval.ask({ query: semanticQuery, projectId: 'project-citation' });
176
+ const citation = (asked.citations || []).find(entry => entry.commitSha === commitSha);
177
+ assert.ok(citation, 'the answer must cite the promoted entry with its full SHA');
178
+ assert.equal(citation.projectId, 'project-citation', 'the citation must keep its project');
179
+ assert.ok(String(citation.text || '').includes(TOKEN), 'the citation must carry the planted body');
180
+
181
+ // Control: an unrelated hex query must not reach the entry.
182
+ const controlQuery = 'deadbeefcafe';
183
+ const control = await engine.coordinator.retrieval.search({ query: controlQuery, projectId: 'project-citation', client: 'workbench', sessionId: 'offline-session' });
184
+ const controlHit = (control.results || []).find(row => row.commitSha === commitSha);
185
+ assert.equal(controlHit, undefined, 'an unrelated hex query must not return the promoted entry');
186
+ assert.equal((control.results || []).some(row => (row.match_channels || []).includes('exact-commit')), false, 'an unrelated hex query must not use the exact-commit channel');
187
+
188
+ record('RET-007-CITE', 'PASS', {
189
+ commitSha,
190
+ entryRelative,
191
+ retrievedRows: rows.length,
192
+ hitEntryId: hit.entry_id,
193
+ hitChannels: hit.match_channels,
194
+ citationEntryId: citation.entryId,
195
+ controlRows: (control.results || []).length,
196
+ analyzer: 'declared stub analyzer (no model call)',
197
+ vector: 'declared stub returning no rows',
198
+ });
199
+
200
+ const summary = {
201
+ lane: 'parallel-retrieval-followup',
202
+ file: 'parallel-retrieval-followup-citation-test.js',
203
+ cases: results,
204
+ model: 'declared stub analyzer (no model call)',
205
+ vector: 'declared stub returning no rows',
206
+ };
207
+ const target = process.env.PARALLEL_RETRIEVAL_EVIDENCE;
208
+ if (target) {
209
+ fs.mkdirSync(path.dirname(target), { recursive: true });
210
+ fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
211
+ }
212
+ console.log('parallel-retrieval-followup-citation-test PASS');
213
+ } finally {
214
+ if (engine) await engine.close();
215
+ await vector.close();
216
+ shell.removeTree(root);
217
+ shell.cleanupIsolatedProcess();
218
+ }
219
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -0,0 +1,287 @@
1
+ 'use strict';
2
+ // Offline regression for the RET-008 harness contract.
3
+ //
4
+ // This file locks the pieces of the RET-008 comparison that do NOT need a
5
+ // model and do NOT need the vector-hub source checkout:
6
+ //
7
+ // 1) the fixed corpus and the human-labelled ground truth stay consistent
8
+ // (unique ids, every relevant document exists, no query carries a hex
9
+ // identifier, no query is a verbatim copy of a document sentence);
10
+ // 2) the ranking and latency helpers compute what the report claims;
11
+ // 3) the engine and the PINNED vector-hub package agree on the body hash,
12
+ // which is the precondition the engine needs before it may merge a
13
+ // vector-hub chunk row into a retrieved fact;
14
+ // 4) the PINNED package still has the identity the comparison was made
15
+ // against, and its query cache still BEHAVES the way the comparison
16
+ // assumed: an identical query is served from the cache, a failed query
17
+ // is evicted, and a late failure of an evicted owner cannot destroy the
18
+ // replacement cached for the same text. The behaviour is observed
19
+ // through the public API with deterministic counts; the source text of
20
+ // the pinned build is never matched.
21
+ //
22
+ // What is NOT claimed here: the embedder is a DECLARED STUB (a deterministic
23
+ // character-gram hashing vector, NOT a neural model), so this file says
24
+ // nothing about retrieval quality or recall. The real-model measurement
25
+ // (Xenova/bge-small-zh-v1.5) and the baseline/candidate comparison live in
26
+ // qa-autopilot/tools/product-probes/parallel-retrieval-followup-ret008.js,
27
+ // which is deliberately outside the offline CI.
28
+ //
29
+ // Everything runs in isolation: own temp root, own hub root, own ports, no
30
+ // ambient module address, no real HOME, no network.
31
+ const assert = require('node:assert/strict');
32
+ const fs = require('node:fs');
33
+ const path = require('node:path');
34
+ const crypto = require('node:crypto');
35
+
36
+ const shell = require('./helpers/parallel-retrieval-followup-shell');
37
+ // Isolate before any product module is loaded.
38
+ shell.isolateProcess();
39
+
40
+ const corpusDefinitions = require('./helpers/parallel-retrieval-followup-corpus');
41
+ const ranking = require('../../_modules/knowledge-engine/src/retrieval-ranking');
42
+
43
+ const HUB_DIST = path.join(shell.REPO_ROOT, 'node_modules', '@sanqianx', 'vector-hub', 'dist', 'cjs');
44
+ const DIMENSIONS = 512;
45
+ const results = [];
46
+
47
+ function record(caseId, status, detail) {
48
+ results.push({ caseId, status, detail });
49
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
50
+ }
51
+
52
+ // DECLARED STUB embedder: a deterministic character-gram hashing vector.
53
+ // It is not a model and makes no claim about semantic quality; it only gives
54
+ // the hub real numbers to store and compare. The counters separate the
55
+ // document embeddings (a batch) from the query embeddings (a single string),
56
+ // because the query cache is only observable on the single-string path.
57
+ function stubEmbedder() {
58
+ const state = { batchCalls: 0, singleCalls: 0, failNext: false, deferFor: null, pending: [] };
59
+ const vector = text => {
60
+ const out = new Array(DIMENSIONS).fill(0);
61
+ const source = String(text || '').toLowerCase();
62
+ for (let i = 0; i < source.length; i += 1) {
63
+ const gram = source.slice(i, i + 2);
64
+ const slot = crypto.createHash('sha256').update(gram).digest();
65
+ const index = slot.readUInt16BE(0) % DIMENSIONS;
66
+ out[index] += 1 + (slot[2] / 255);
67
+ }
68
+ const norm = Math.sqrt(out.reduce((sum, value) => sum + value * value, 0)) || 1;
69
+ return out.map(value => value / norm);
70
+ };
71
+ state.vector = vector;
72
+ const embedder = {
73
+ kind: 'declared-stub-hashing-embedder',
74
+ model: 'stub-hashing-embedder',
75
+ modelId: 'stub-hashing-embedder',
76
+ maxTokens: 500,
77
+ async createEmbeddings(inputs) {
78
+ if (Array.isArray(inputs)) {
79
+ state.batchCalls += 1;
80
+ return { status: 'success', output: inputs.map(vector) };
81
+ }
82
+ state.singleCalls += 1;
83
+ if (state.failNext) {
84
+ state.failNext = false;
85
+ return { status: 'failure', output: [] };
86
+ }
87
+ if (state.deferFor && state.deferFor(String(inputs))) {
88
+ return new Promise((resolve, reject) => { state.pending.push({ text: String(inputs), resolve, reject }); });
89
+ }
90
+ return { status: 'success', output: [vector(inputs)] };
91
+ },
92
+ };
93
+ return { embedder, state };
94
+ }
95
+
96
+ async function waitUntil(predicate, timeoutMs = 5000, intervalMs = 20) {
97
+ const deadline = Date.now() + timeoutMs;
98
+ while (Date.now() < deadline) {
99
+ if (predicate()) return true;
100
+ await new Promise(resolve => setTimeout(resolve, intervalMs));
101
+ }
102
+ return false;
103
+ }
104
+
105
+ (async () => {
106
+ const root = shell.makeTempDir('parallel-retrieval-followup-corpus');
107
+ try {
108
+ // ------------------------------------------------------- definitions
109
+ const ids = corpusDefinitions.CORPUS.map(doc => doc.id);
110
+ assert.equal(new Set(ids).size, ids.length, 'every corpus id must be unique');
111
+ const files = corpusDefinitions.CORPUS.map(doc => doc.file);
112
+ assert.equal(new Set(files).size, files.length, 'every corpus file name must be unique');
113
+ for (const doc of corpusDefinitions.CORPUS) {
114
+ assert.ok(doc.content.trim().length > 0, `the corpus document ${doc.id} must not be empty`);
115
+ }
116
+ assert.ok(corpusDefinitions.QUERIES.length >= 5, 'the query set must be usable');
117
+ for (const definition of corpusDefinitions.QUERIES) {
118
+ assert.ok(definition.relevant.length > 0, `the query ${definition.id} must name at least one relevant document`);
119
+ for (const relevant of definition.relevant) {
120
+ assert.ok(ids.includes(relevant), `the query ${definition.id} names the unknown document ${relevant}`);
121
+ }
122
+ assert.ok(!/[0-9a-f]{7}/i.test(definition.query), `the query ${definition.id} must not carry a hex identifier`);
123
+ for (const doc of corpusDefinitions.CORPUS) {
124
+ assert.ok(!doc.content.includes(definition.query), `the query ${definition.id} is copied verbatim from ${doc.id}`);
125
+ }
126
+ }
127
+ record('RET-008-DEFINITIONS', 'PASS', {
128
+ documents: corpusDefinitions.CORPUS.length,
129
+ queries: corpusDefinitions.QUERIES.length,
130
+ multiRelevantQueries: corpusDefinitions.QUERIES.filter(definition => definition.relevant.length > 1).map(definition => definition.id),
131
+ });
132
+
133
+ // ------------------------------------------------------------ helpers
134
+ const ranked = corpusDefinitions.evaluateRanking(['b', 'a', 'c'], ['a', 'c']);
135
+ assert.equal(ranked.firstRelevantRank, 2, 'the first relevant rank must be reported');
136
+ assert.equal(ranked.reciprocalRank, 0.5, 'the reciprocal rank must be 1/2');
137
+ assert.equal(ranked.recallAt1, 0, 'nothing relevant is in the top 1');
138
+ assert.equal(ranked.recallAt5, 1, 'both relevant documents are within the top 5');
139
+ const empty = corpusDefinitions.evaluateRanking(['x'], ['a']);
140
+ assert.equal(empty.hit, false, 'a query without any relevant document must not count as a hit');
141
+ assert.equal(empty.reciprocalRank, 0, 'a query without any relevant document has no reciprocal rank');
142
+ const latency = corpusDefinitions.summariseLatency([10, 20, 30, 40, 100]);
143
+ assert.equal(latency.count, 5, 'every finite sample must be counted');
144
+ assert.equal(latency.min, 10, 'the minimum must be reported');
145
+ assert.equal(latency.max, 100, 'the maximum must be reported');
146
+ assert.equal(latency.p50, 30, 'the median must be reported');
147
+ assert.equal(corpusDefinitions.summariseLatency([]).p50, null, 'an empty sample has no percentile');
148
+ record('RET-008-METRICS', 'PASS', { ranked, latency });
149
+
150
+ // -------------------------------------------- engine/hub body hash
151
+ const corpusDir = path.join(root, 'corpus');
152
+ const corpusIndex = corpusDefinitions.materialiseCorpus(corpusDir);
153
+ assert.equal(fs.readdirSync(corpusDir).length, corpusDefinitions.CORPUS.length, 'every corpus document must be written');
154
+ assert.equal(corpusIndex.idForFile('quota-sampling.md'), 'quota-sampling', 'a result file must map back to its corpus id');
155
+ assert.equal(corpusIndex.idForFile('missing.md'), null, 'an unknown file must not map to a corpus id');
156
+
157
+ const { VectorHub } = require(path.join(HUB_DIST, 'core', 'hub.js'));
158
+ const hub = new VectorHub({ rootPath: path.join(root, 'hub') });
159
+ const corpusEmbedder = stubEmbedder();
160
+ hub.setEmbeddings(corpusEmbedder.embedder);
161
+ const tracked = await hub.syncFolder('ret008-offline', corpusDir);
162
+ assert.equal(tracked, corpusDefinitions.CORPUS.length, 'every corpus document must reach the index');
163
+
164
+ const response = await hub.search(corpusDefinitions.QUERIES[0].query, { maxResults: 10, resultLevel: 'chunk' });
165
+ assert.ok(response.results.length > 0, 'the chunk search must return rows');
166
+ const expectedHashes = new Map();
167
+ for (const doc of corpusDefinitions.CORPUS) {
168
+ const content = fs.readFileSync(path.join(corpusDir, doc.file), 'utf8');
169
+ expectedHashes.set(doc.file, ranking.bodyHash(content));
170
+ }
171
+ let checked = 0;
172
+ for (const row of response.results) {
173
+ const expected = expectedHashes.get(row.file);
174
+ assert.ok(expected, `the search returned an unknown document ${row.file}`);
175
+ assert.equal(row.bodyHash, expected, `the engine and the pinned hub must agree on the body hash of ${row.file}`);
176
+ assert.equal(row.scoreType, 'cosine', 'a chunk row must carry the cosine score type');
177
+ assert.ok(Number.isInteger(row.startPos) && Number.isInteger(row.endPos), 'a chunk row must carry integer positions');
178
+ checked += 1;
179
+ }
180
+ record('RET-008-BODY-HASH', 'PASS', { tracked, rowsChecked: checked, distinctFiles: new Set(response.results.map(row => row.file)).size });
181
+
182
+ // The pinned dependency must still be the one the RET-008 comparison was
183
+ // made against. A dependency change has to re-run the explicit probe.
184
+ // Only the PACKAGE IDENTITY is asserted here: the source text of the
185
+ // pinned build is never matched, because a string match against the
186
+ // implementation is not a behaviour.
187
+ const pinnedPackage = path.join(shell.REPO_ROOT, 'node_modules', '@sanqianx', 'vector-hub');
188
+ const pinnedManifest = JSON.parse(fs.readFileSync(path.join(pinnedPackage, 'package.json'), 'utf8'));
189
+ assert.equal(pinnedManifest.name, '@sanqianx/vector-hub', 'the pinned package identity changed');
190
+ assert.equal(
191
+ pinnedManifest.version,
192
+ '1.0.8-rc.8',
193
+ `the pinned @sanqianx/vector-hub is ${pinnedManifest.version} instead of 1.0.8-rc.8; re-run qa-autopilot/tools/product-probes/parallel-retrieval-followup-ret008.js`,
194
+ );
195
+ record('RET-008-PINNED-PACKAGE', 'PASS', { pinnedName: pinnedManifest.name, pinnedVersion: pinnedManifest.version });
196
+
197
+ // ---- real query-cache behaviour of the pinned package -----------------
198
+ // The RET-008 comparison assumed that the pinned hub caches the query
199
+ // embedding and that a late failure of an evicted owner cannot destroy
200
+ // the replacement cached for the same text. Both are checked here through
201
+ // the public API with DETERMINISTIC COUNTS. The embedder is the DECLARED
202
+ // STUB, so this case says nothing about neural semantic quality; it only
203
+ // says that the cache still behaves the way the comparison assumed.
204
+ const cacheEmbedder = stubEmbedder();
205
+ const cacheHub = new VectorHub({ rootPath: path.join(root, 'hub-cache') });
206
+ cacheHub.setEmbeddings(cacheEmbedder.embedder);
207
+ const cacheTracked = await cacheHub.syncFolder('ret008-cache', corpusDir);
208
+ assert.equal(cacheTracked, corpusDefinitions.CORPUS.length, 'every corpus document must reach the cache index');
209
+ const search = text => cacheHub.search(text, { maxResults: 10, resultLevel: 'chunk' });
210
+
211
+ const queryA = corpusDefinitions.QUERIES[0].query;
212
+ const queryB = corpusDefinitions.QUERIES[1].query;
213
+ const beforeFirst = cacheEmbedder.state.singleCalls;
214
+ await search(queryA);
215
+ const afterFirst = cacheEmbedder.state.singleCalls;
216
+ assert.ok(afterFirst > beforeFirst, 'the first query must be embedded');
217
+ await search(queryA);
218
+ const afterRepeated = cacheEmbedder.state.singleCalls;
219
+ assert.equal(afterRepeated, afterFirst, 'the identical query must be served from the query cache');
220
+ const beforeSecond = cacheEmbedder.state.singleCalls;
221
+ await search(queryB);
222
+ const afterDifferent = cacheEmbedder.state.singleCalls;
223
+ assert.ok(afterDifferent > beforeSecond, 'a different query must be embedded again');
224
+ record('RET-008-QUERY-CACHE-HIT', 'PASS', {
225
+ firstQueryEmbeddings: afterFirst - beforeFirst,
226
+ repeatedQueryEmbeddings: afterRepeated - afterFirst,
227
+ differentQueryEmbeddings: afterDifferent - beforeSecond,
228
+ documentEmbeddings: cacheEmbedder.state.batchCalls,
229
+ });
230
+
231
+ // A failed query must not survive in the cache.
232
+ const queryFail = `${queryB} 失败用例`;
233
+ cacheEmbedder.state.failNext = true;
234
+ await search(queryFail).catch(() => {});
235
+ const afterFailed = cacheEmbedder.state.singleCalls;
236
+ await search(queryFail).catch(() => {});
237
+ assert.ok(cacheEmbedder.state.singleCalls > afterFailed, 'a failed query must not stay cached');
238
+ record('RET-008-QUERY-CACHE-FAILURE-EVICTION', 'PASS', {
239
+ embeddingsAfterFailedQuery: afterFailed,
240
+ embeddingsAfterRetry: cacheEmbedder.state.singleCalls,
241
+ });
242
+
243
+ // The ownership guard: a late failure of an evicted owner must not
244
+ // destroy the replacement that was cached for the same text.
245
+ const ownerQuery = '归属校验用的查询文本';
246
+ cacheEmbedder.state.deferFor = text => text === ownerQuery;
247
+ const firstOwner = search(ownerQuery).catch(() => null);
248
+ assert.ok(await waitUntil(() => cacheEmbedder.state.pending.length === 1), 'the first owner was never created');
249
+ // The hub keeps 100 cached queries; 100 distinct queries evict the
250
+ // oldest one, which is the entry of the first owner.
251
+ for (let index = 0; index < 100; index += 1) await search(`驱逐查询 ${index}`);
252
+ const replacement = search(ownerQuery).catch(() => null);
253
+ assert.ok(await waitUntil(() => cacheEmbedder.state.pending.length === 2), 'the replacement owner was never created');
254
+ // The evicted owner now fails late. It must not delete the replacement.
255
+ cacheEmbedder.state.pending[0].reject(new Error('late failure of the evicted owner'));
256
+ await firstOwner;
257
+ const beforeThird = cacheEmbedder.state.singleCalls;
258
+ const thirdSearch = search(ownerQuery);
259
+ await new Promise(resolve => setTimeout(resolve, 300));
260
+ assert.equal(cacheEmbedder.state.pending.length, 2, 'the replacement must still be cached: no new query embedding was created');
261
+ assert.equal(cacheEmbedder.state.singleCalls, beforeThird, 'the replacement must still be cached: the embedder was called again');
262
+ cacheEmbedder.state.pending[1].resolve({ status: 'success', output: [cacheEmbedder.state.vector(ownerQuery)] });
263
+ const thirdResult = await thirdSearch;
264
+ cacheEmbedder.state.deferFor = null;
265
+ record('RET-008-QUERY-CACHE-OWNERSHIP', 'PASS', {
266
+ evictingQueries: 100,
267
+ embedderCallsAfterLateFailure: cacheEmbedder.state.singleCalls - beforeThird,
268
+ replacementRows: Array.isArray(thirdResult && thirdResult.results) ? thirdResult.results.length : null,
269
+ });
270
+
271
+ const summary = {
272
+ lane: 'parallel-retrieval-followup',
273
+ file: 'parallel-retrieval-followup-corpus-test.js',
274
+ cases: results,
275
+ model: 'declared stub embedder (no model call)',
276
+ };
277
+ const target = process.env.PARALLEL_RETRIEVAL_EVIDENCE;
278
+ if (target) {
279
+ fs.mkdirSync(path.dirname(target), { recursive: true });
280
+ fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
281
+ }
282
+ console.log('parallel-retrieval-followup-corpus-test PASS');
283
+ } finally {
284
+ shell.removeTree(root);
285
+ shell.cleanupIsolatedProcess();
286
+ }
287
+ })().catch(error => { console.error(error); process.exitCode = 1; });