@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,292 @@
1
+ 'use strict';
2
+ // Parallel knowledge lane — retrieval cases RET-001, RET-003, RET-004, RET-005.
3
+ // Every assertion runs against real Markdown on disk and the real vendored
4
+ // knowledge-engine over real loopback HTTP. The vector endpoint is an
5
+ // explicitly declared stub that returns no results, so nothing in this file
6
+ // may be counted as vector-channel evidence.
7
+ const assert = require('node:assert/strict');
8
+ const fs = require('node:fs');
9
+ const path = require('node:path');
10
+ const http = require('node:http');
11
+ const crypto = require('node:crypto');
12
+ const fixture = require('./helpers/parallel-knowledge-fixture');
13
+ // The entry point isolates itself: ambient module addresses, live data
14
+ // directories and the real HOME are cleared here, before any product module
15
+ // is loaded, so the test never depends on the caller running `env -u`.
16
+ fixture.isolateProcess();
17
+ const { StorageLayout } = require('../lib/storage-layout');
18
+ const { ProjectStore } = require('../lib/project-store');
19
+ const { ProjectRegistryStore } = require('../lib/project-registry-store');
20
+ const { KnowledgeRetrievalService } = require('../lib/knowledge-retrieval-service');
21
+ const { EngineKnowledgeClient } = require('../lib/engine-knowledge-client');
22
+ const { CHUNKER_VERSION } = require('../lib/markdown-knowledge-indexer');
23
+ const { sha256 } = require('../lib/knowledge-schema');
24
+ const { createEngineServer } = require('../../_modules/knowledge-engine/src/server');
25
+ const AtomicFile = require('../../_modules/knowledge-engine/src/runtime/atomic-file');
26
+
27
+ const results = [];
28
+ function record(caseId, status, detail) {
29
+ results.push({ caseId, status, detail });
30
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
31
+ }
32
+
33
+ async function stubVectorServer() {
34
+ const requests = [];
35
+ const server = http.createServer((req, res) => {
36
+ let body = '';
37
+ req.on('data', chunk => { body += chunk; });
38
+ req.on('end', () => {
39
+ requests.push({ url: req.url, method: req.method, body: body || null });
40
+ res.writeHead(200, { 'Content-Type': 'application/json' });
41
+ res.end(JSON.stringify({ ok: true, results: [], errors: [], projects: [] }));
42
+ });
43
+ });
44
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
45
+ return { requests, url: `http://127.0.0.1:${server.address().port}`, close: () => { server.closeAllConnections(); return new Promise(resolve => server.close(resolve)); } };
46
+ }
47
+
48
+ // The engine must never reach the user's event bridge (8790) from this lane.
49
+ function forbiddenBridge() {
50
+ const guard = name => async () => { throw new Error(`event bridge must not be contacted from the test lane (${name})`); };
51
+ return { registerConsumer: guard('registerConsumer'), listJournals: guard('listJournals'), readEvents: guard('readEvents'), ackConsumerCursor: guard('ackConsumerCursor'), close() {} };
52
+ }
53
+
54
+ // A declared stub analyzer: it never calls a model, it only stages Markdown
55
+ // so the promotion path can be exercised end to end.
56
+ function stubAnalyzer(events = []) {
57
+ return {
58
+ sourceReadMode: 'isolated-frozen-evidence',
59
+ async runClaim(input) {
60
+ events.push({ kind: 'start', projectId: input.projectId, sha: input.claim.commitSha });
61
+ const relative = `changes/${input.claim.commitSha.slice(0, 12)}.md`;
62
+ const content = `# 真实晋升条目 ${input.claim.commitSha.slice(0, 12)}\n\nRET001TOKEN 事实:本次提交把配额采样改为串行执行。\n\ncommit: ${input.claim.commitSha}\n`;
63
+ AtomicFile.writeFileAtomic(path.join(input.stagingPath, 'files', relative), content);
64
+ AtomicFile.writeJsonAtomic(input.manifestPath, {
65
+ schema: 'knowledge-staging-manifest/v1',
66
+ projectId: input.projectId,
67
+ runId: input.claim.runId,
68
+ commitSha: input.claim.commitSha,
69
+ operations: [{
70
+ path: relative,
71
+ operation: 'create',
72
+ sha256: `sha256:${crypto.createHash('sha256').update(content).digest('hex')}`,
73
+ reason: 'Frozen Git evidence',
74
+ evidenceReferences: [`commit:${input.claim.commitSha}`, `patch:${input.claim.patchHash}`, `conversation:${input.claim.conversationSnapshotHash}`, `retrieval:${input.claim.retrievalManifestHash}`],
75
+ }],
76
+ });
77
+ events.push({ kind: 'finish', projectId: input.projectId, sha: input.claim.commitSha });
78
+ return { ok: true };
79
+ },
80
+ };
81
+ }
82
+
83
+ (async () => {
84
+ const root = fixture.makeTempDir('parallel-knowledge-retrieval');
85
+ const dataDir = path.join(root, 'data');
86
+ const layout = new StorageLayout({ dataDir });
87
+ const projects = new ProjectStore({ layout });
88
+ const registry = new ProjectRegistryStore({ layout });
89
+ const vector = await stubVectorServer();
90
+ const analyzerEvents = [];
91
+ const engineDataDir = path.join(root, 'engine-data');
92
+ fs.mkdirSync(engineDataDir, { recursive: true });
93
+ const engine = createEngineServer({ dataDir: engineDataDir, vectorUrl: vector.url, bridge: forbiddenBridge(), analyzer: stubAnalyzer(analyzerEvents) });
94
+ await engine.ready;
95
+ await new Promise(resolve => engine.server.listen(0, '127.0.0.1', resolve));
96
+ const engineUrl = `http://127.0.0.1:${engine.server.address().port}`;
97
+ const call = async (route, body) => {
98
+ const response = await fetch(`${engineUrl}${route}`, {
99
+ method: body === undefined ? 'GET' : 'POST',
100
+ headers: { 'Content-Type': 'application/json' },
101
+ body: body === undefined ? undefined : JSON.stringify(body),
102
+ });
103
+ return { status: response.status, body: await response.json() };
104
+ };
105
+
106
+ try {
107
+ // ---------------------------------------------------------------- fixtures
108
+ const repoA = fixture.makeRepo(root, 'repo-a', [
109
+ { message: 'initial', files: { 'README.md': '# A\n\n初始内容\n' } },
110
+ { message: 'feature', files: { 'feature.txt': 'feature A\n' } },
111
+ ]);
112
+ const repoB = fixture.makeRepo(root, 'repo-b', [{ message: 'initial', files: { 'README.md': '# B\n' } }]);
113
+ const repoC = fixture.makeRepo(root, 'repo-c', [{ message: 'initial', files: { 'README.md': '# C\n' } }]);
114
+ const knowledgeA = fixture.makeKnowledgeDir(root, 'knowledge-a');
115
+ const knowledgeB = fixture.makeKnowledgeDir(root, 'knowledge-b');
116
+ const knowledgeC = fixture.makeKnowledgeDir(root, 'knowledge-c');
117
+ const promotedEntry = `changes/planted-${repoA.head.slice(0, 12)}.md`;
118
+ const promotedBody = `# 真实晋升条目\n\nRET001TOKEN 事实:本次提交把配额采样改为串行执行。\n\ncommit: ${repoA.head}\n`;
119
+ fs.mkdirSync(path.join(knowledgeA, 'changes'), { recursive: true });
120
+ fs.writeFileSync(path.join(knowledgeA, promotedEntry), promotedBody, 'utf8');
121
+ fs.writeFileSync(path.join(knowledgeA, 'changes', 'template.md'), '# 模板\n\nTODO: 待补充\n', 'utf8');
122
+ fs.writeFileSync(path.join(knowledgeB, 'README.md'), '# B\n\nDOMAINONLYTOKEN 属于领域知识\n', 'utf8');
123
+ fs.writeFileSync(path.join(knowledgeC, 'README.md'), '# C\n\nOTHERDOMAINTOKEN 属于其它领域\n', 'utf8');
124
+
125
+ await projects.create('project-a', { displayName: 'A', repoPath: repoA.repoPath, knowledgePath: knowledgeA, relatedProjectIds: ['project-b'], brainId: 'brain-default', domainId: 'domain-tools' });
126
+ await projects.create('project-b', { displayName: 'B', repoPath: repoB.repoPath, knowledgePath: knowledgeB, brainId: 'brain-default', domainId: 'domain-tools' });
127
+ await projects.create('project-c', { displayName: 'C', repoPath: repoC.repoPath, knowledgePath: knowledgeC, brainId: 'brain-default', domainId: 'domain-other' });
128
+ for (const id of ['project-a', 'project-b', 'project-c']) await registry.add(id, { displayNameSnapshot: id });
129
+
130
+ // The root product service is the authoritative-Markdown reader used by
131
+ // the shell. No embedder and no index exist yet.
132
+ const service = new KnowledgeRetrievalService({ layout, registryStore: registry, projectStore: projects });
133
+
134
+ // --------------------------------------------------------------- RET-001
135
+ {
136
+ const before = fixture.hashTree(knowledgeA);
137
+ const response = await service.search({ projectId: 'project-a', query: 'RET001TOKEN' });
138
+ assert.equal(response.ok, true);
139
+ assert.ok(response.results.length >= 1, 'the promoted fact must be retrievable');
140
+ const top = response.results[0];
141
+ assert.equal(top.entry_id, promotedEntry, 'citation must point at the promoted relative entry');
142
+ assert.ok(top.chunk_text.includes('RET001TOKEN'), 'the result must carry the real body text');
143
+ assert.ok(top.chunk_text.includes('配额采样改为串行执行'), 'the returned body must contain the promoted fact sentence');
144
+ const expectedDocumentHash = sha256(`chunker:${CHUNKER_VERSION}\n${promotedBody}`);
145
+ assert.equal(top.document_hash, expectedDocumentHash, 'document hash must be recomputable from the real file');
146
+ assert.equal(top.content_hash, crypto.createHash('sha256').update(top.chunk_text, 'utf8').digest('hex'), 'content hash must match the returned body');
147
+ assert.ok(!response.results.some(row => /TODO|待补充/.test(row.chunk_text)), 'template placeholders must not be presented as facts');
148
+ assert.equal(response.backend, 'markdown-hybrid-fallback', 'no vector channel exists in this lane, so the backend must not claim a hybrid hit');
149
+ assert.notEqual(response.health.index, 'ok');
150
+ assert.deepEqual(fixture.hashTree(knowledgeA), before, 'retrieval must not mutate knowledge files');
151
+ record('RET-001', 'PASS', { entryId: top.entry_id, documentHash: top.document_hash, backend: response.backend, healthIndex: response.health.index, resultCount: response.results.length });
152
+ }
153
+
154
+ // --------------------------------------------------------------- RET-003
155
+ {
156
+ const projectScope = await service.search({ projectId: 'project-a', query: 'RET001TOKEN' });
157
+ assert.ok(projectScope.results.every(row => row.scope_project_id === 'project-a' || row.scope_project_id === 'project-b'), 'project scope may only expose the project and its declared related projects');
158
+ assert.ok(!projectScope.results.some(row => row.chunk_text.includes('OTHERDOMAINTOKEN')), 'an unrelated project must never leak into the project scope');
159
+ const unrelated = await service.search({ projectId: 'project-c', query: 'OTHERDOMAINTOKEN' });
160
+ assert.ok(unrelated.results.every(row => row.scope_project_id === 'project-c'));
161
+ assert.ok(!unrelated.results.some(row => row.chunk_text.includes('RET001TOKEN')), 'another project must not borrow this project context');
162
+ await assert.rejects(service.search({ projectId: 'project-unknown', query: 'RET001TOKEN' }), 'an unknown project must fail instead of borrowing a registered context');
163
+
164
+ // Real engine scope semantics over real loopback HTTP.
165
+ for (const project of [
166
+ { projectId: 'project-a', name: 'A', repoPath: repoA.repoPath, knowledgePath: knowledgeA, brainId: 'brain-default', domainId: 'domain-tools' },
167
+ { projectId: 'project-b', name: 'B', repoPath: repoB.repoPath, knowledgePath: knowledgeB, brainId: 'brain-default', domainId: 'domain-tools' },
168
+ { projectId: 'project-c', name: 'C', repoPath: repoC.repoPath, knowledgePath: knowledgeC, brainId: 'brain-default', domainId: 'domain-other' },
169
+ ]) await call('/api/projects', project);
170
+ fs.writeFileSync(path.join(knowledgeA, 'shared.md'), '# A\n\nSHAREDTOKEN 共享事实 A\n', 'utf8');
171
+ fs.writeFileSync(path.join(knowledgeB, 'shared.md'), '# B\n\nSHAREDTOKEN 共享事实 B\n', 'utf8');
172
+ fs.writeFileSync(path.join(knowledgeC, 'shared.md'), '# C\n\nSHAREDTOKEN 共享事实 C\n', 'utf8');
173
+
174
+ const projectScopeEngine = await call('/api/search', { projectId: 'project-a', query: 'SHAREDTOKEN', scope: 'project' });
175
+ assert.equal(projectScopeEngine.status, 200);
176
+ assert.deepEqual([...new Set(projectScopeEngine.body.results.map(row => row.scope_project_id))], ['project-a'], 'engine project scope must stay inside the project');
177
+ const domainScopeEngine = await call('/api/search', { projectId: 'project-a', query: 'SHAREDTOKEN', scope: 'domain' });
178
+ assert.deepEqual([...new Set(domainScopeEngine.body.results.map(row => row.scope_project_id))].sort(), ['project-a', 'project-b'], 'engine domain scope must expose the same-domain project only');
179
+ const brainScopeEngine = await call('/api/search', { projectId: 'project-a', query: 'SHAREDTOKEN', scope: 'brain' });
180
+ assert.deepEqual([...new Set(brainScopeEngine.body.results.map(row => row.scope_project_id))].sort(), ['project-a', 'project-b', 'project-c'], 'engine brain scope must expose every project of the brain');
181
+ const unknownEngine = await call('/api/search', { projectId: 'project-unknown', query: 'SHAREDTOKEN' });
182
+ assert.equal(unknownEngine.status, 404, 'an unknown project must be refused by the engine');
183
+ assert.equal(unknownEngine.body.error, 'PROJECT_NOT_FOUND', 'the real engine reports the failure code as a string');
184
+ const invalidScope = await call('/api/search', { projectId: 'project-a', query: 'SHAREDTOKEN', scope: 'global' });
185
+ assert.equal(invalidScope.body.error, 'INVALID_ARGUMENT', 'an unknown scope must be refused');
186
+ record('RET-003', 'PASS', {
187
+ rootServiceScopes: [...new Set(projectScope.results.map(row => row.scope_project_id))].sort(),
188
+ engineProjectScope: [...new Set(projectScopeEngine.body.results.map(row => row.scope_project_id))],
189
+ engineDomainScope: [...new Set(domainScopeEngine.body.results.map(row => row.scope_project_id))].sort(),
190
+ engineBrainScope: [...new Set(brainScopeEngine.body.results.map(row => row.scope_project_id))].sort(),
191
+ unknownProjectStatus: unknownEngine.status,
192
+ vectorStubRequests: vector.requests.length,
193
+ });
194
+ }
195
+
196
+ // --------------------------------------------------------------- RET-004
197
+ {
198
+ // A real promoted run: the engine analyses the real commit SHA and the
199
+ // promoted file keeps that SHA as its source.
200
+ const run = engine.coordinator.enqueue({ projectId: 'project-a', commitSha: repoA.head });
201
+ const promoted = await fixture.eventually(() => engine.store.getRun(run.runId) && ['succeeded', 'failed', 'index-pending'].includes(engine.store.getRun(run.runId).state));
202
+ assert.equal(promoted, true, 'the run must reach a terminal state');
203
+ const finished = engine.store.getRun(run.runId);
204
+ assert.equal(finished.state, 'succeeded', `run failed: ${JSON.stringify(finished.error)}`);
205
+ const promotedFile = path.join(knowledgeA, `changes/${repoA.head.slice(0, 12)}.md`);
206
+ assert.ok(fs.existsSync(promotedFile), 'the promoted Markdown must exist on disk');
207
+ const promotedText = fs.readFileSync(promotedFile, 'utf8');
208
+ assert.ok(promotedText.includes(repoA.head), 'the promoted body must carry the real commit SHA');
209
+
210
+ const search = await call('/api/search', { projectId: 'project-a', query: repoA.head.slice(0, 12) });
211
+ assert.equal(search.status, 200);
212
+ const hit = search.body.results.find(row => row.entry_id === `changes/${repoA.head.slice(0, 12)}.md`);
213
+ assert.ok(hit, 'the promoted entry must be retrievable');
214
+ assert.equal(hit.commitSha, repoA.head, 'the citation must carry the full commit source of the current result');
215
+ assert.equal(hit.path, `changes/${repoA.head.slice(0, 12)}.md`, 'the citation path must be relative to the project knowledge root');
216
+
217
+ const read = await call('/api/get', { projectId: 'project-a', entryId: hit.entry_id });
218
+ assert.equal(read.status, 200);
219
+ assert.ok(read.body.chunks[0].chunk_text.includes(repoA.head), 'reading the citation must return the real body');
220
+ const outside = await call('/api/get', { projectId: 'project-a', entryId: '../outside.md' });
221
+ assert.equal(outside.status, 403, 'path traversal must be refused');
222
+ assert.equal(outside.body.error, 'PATH_OUTSIDE_ROOT', 'the real engine reports the failure code as a string');
223
+
224
+ // The product's own client must reach the same real engine.
225
+ const client = new EngineKnowledgeClient({ layout, engineUrl });
226
+ const viaClient = await client.request('get', { entry: hit.entry_id }, { projectId: 'project-a', repoPath: repoA.repoPath, knowledgePath: knowledgeA });
227
+ assert.ok(viaClient.chunks[0].chunk_text.includes(repoA.head));
228
+ await assert.rejects(client.request('get', { entry: '../outside.md' }, { projectId: 'project-a', repoPath: repoA.repoPath, knowledgePath: knowledgeA }), error => error.code === 'PATH_OUTSIDE_ROOT' && !error.message.includes(repoA.head));
229
+ await assert.rejects(client.request('search', { query: 'x', scope: 'global' }, { projectId: 'project-a', repoPath: repoA.repoPath, knowledgePath: knowledgeA }), error => error.code === 'INVALID_ARGUMENT');
230
+ record('RET-004', 'PASS', {
231
+ promotedEntry: hit.entry_id,
232
+ commitSha: hit.commitSha,
233
+ documentHash: hit.document_hash,
234
+ traversalStatus: outside.status,
235
+ traversalCode: outside.body.error.code,
236
+ analyzerStubRuns: analyzerEvents.length,
237
+ });
238
+ }
239
+
240
+ // --------------------------------------------------------------- RET-005
241
+ {
242
+ // The embedding channel is switched off: no embedder, then a failing
243
+ // embedder. The Markdown truth must still answer, and nothing may claim
244
+ // a vector success or delete knowledge.
245
+ const before = fixture.hashTree(knowledgeA);
246
+ fs.mkdirSync(layout.getIndexPath(), { recursive: true });
247
+ const disabled = new KnowledgeRetrievalService({ layout, registryStore: registry, projectStore: projects, embedderProvider: () => null });
248
+ const disabledResponse = await disabled.search({ projectId: 'project-a', query: 'RET001TOKEN' });
249
+ assert.equal(disabledResponse.backend, 'markdown-hybrid-fallback');
250
+ assert.equal(disabledResponse.health.index, 'unavailable');
251
+ assert.ok(disabledResponse.results.length >= 1, 'Markdown truth must stay visible when the embedding channel is off');
252
+ assert.ok(disabledResponse.results.every(row => !row.match_channels.includes('index:vector-hub')), 'no result may claim a vector channel');
253
+
254
+ const failing = new KnowledgeRetrievalService({
255
+ layout,
256
+ registryStore: registry,
257
+ projectStore: projects,
258
+ // Row-less index: the database refuses every query, so no vector row
259
+ // can ever reach a result. The only observable effect is the
260
+ // degradation path of the Markdown truth reader.
261
+ databaseProvider: () => ({ async hybridSearch() { throw new Error('index offline'); } }),
262
+ embedderProvider: () => ({ async embedQuery() { throw new Error('embedding disabled'); } }),
263
+ });
264
+ const failingResponse = await failing.search({ projectId: 'project-a', query: 'RET001TOKEN' });
265
+ assert.equal(failingResponse.health.index, 'degraded');
266
+ assert.equal(failingResponse.health.reason, 'index-query-failed');
267
+ assert.equal(failingResponse.backend, 'markdown-hybrid-fallback');
268
+ assert.ok(failingResponse.results.length >= 1, 'a degraded index must still expose the Markdown truth');
269
+ assert.ok(failingResponse.results.every(row => !row.match_channels.includes('index:vector-hub')));
270
+ assert.deepEqual(fixture.hashTree(knowledgeA), before, 'a disabled embedding channel must not delete or rewrite knowledge');
271
+ record('RET-005', 'PASS', {
272
+ disabledBackend: disabledResponse.backend,
273
+ disabledHealth: disabledResponse.health.index,
274
+ degradedHealth: failingResponse.health.index,
275
+ degradedReason: failingResponse.health.reason,
276
+ knowledgeFilesUnchanged: true,
277
+ resultCount: failingResponse.results.length,
278
+ });
279
+ }
280
+
281
+ const summary = { lane: 'test-knowledge', file: 'parallel-knowledge-retrieval-test.js', cases: results, vectorChannel: 'stubbed-empty-results (not vector evidence)' };
282
+ const target = process.env.PARALLEL_KNOWLEDGE_EVIDENCE;
283
+ if (target) fs.mkdirSync(path.dirname(target), { recursive: true });
284
+ if (target) fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
285
+ console.log('parallel-knowledge-retrieval-test PASS');
286
+ } finally {
287
+ await engine.close();
288
+ await vector.close();
289
+ fixture.removeTree(root);
290
+ fixture.cleanupIsolatedProcess();
291
+ }
292
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -0,0 +1,168 @@
1
+ 'use strict';
2
+ // Parallel knowledge lane — canonical vector-side contracts (RET-006 sub-items).
3
+ //
4
+ // Scope: only the deterministic, environment-independent contracts are proven
5
+ // here, because the file is collected by the offline regression runner:
6
+ // RET-006.a-root-switch the hub accepts and reports a new index root
7
+ // RET-006.b-writer-drain an in-flight writer is drained before the switch resolves
8
+ // RET-006.c-old-root-retained the old index root keeps its data byte-for-byte
9
+ // RET-006.d-project-knowledge-kept a project knowledge directory is never moved by the switch
10
+ //
11
+ // RET-006 as a whole is NOT claimed to pass here: this file proves the
12
+ // deterministic sub-items a-d only. The remaining sub-item ("新根重建":
13
+ // writing an actual index into the new root) needs a real embedding model,
14
+ // because the hub refuses any index write without one. That
15
+ // environment-dependent acceptance lives in the explicitly invoked probe
16
+ // qa-autopilot/tools/product-probes/parallel-knowledge-vector.js, which now
17
+ // drives a real hub upsert/search, a real root switch, a real rebuild in the
18
+ // new root and a real old-root retention check, and reports RET-002/RET-006
19
+ // as BLOCKED instead of faking a green result.
20
+ //
21
+ // This file performs no model load, no download and no paid API call, and it
22
+ // never touches the live 5757/5790/8787/8790 endpoints.
23
+ const assert = require('node:assert/strict');
24
+ const fs = require('node:fs');
25
+ const path = require('node:path');
26
+ const fixture = require('./helpers/parallel-knowledge-fixture');
27
+ // The entry point isolates itself: ambient module addresses, live data
28
+ // directories and the real HOME are cleared here, before anything else loads.
29
+ fixture.isolateProcess();
30
+
31
+ const results = [];
32
+ function record(caseId, status, detail) {
33
+ results.push({ caseId, status, detail });
34
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
35
+ }
36
+
37
+ const HUB_DIST = path.resolve(__dirname, '..', '..', 'node_modules', '@sanqianx', 'vector-hub', 'dist', 'cjs');
38
+
39
+ (async () => {
40
+ const root = fixture.makeTempDir('parallel-knowledge-vector');
41
+ try {
42
+ const oldRoot = path.join(root, 'indexes-old');
43
+ const newRoot = path.join(root, 'indexes-new');
44
+ const configPath = path.join(root, 'vector-hub', 'config.json');
45
+ fs.mkdirSync(path.join(oldRoot, 'demo'), { recursive: true });
46
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
47
+ fs.writeFileSync(path.join(oldRoot, 'demo', 'note.md'), '旧索引根下的真实数据\n', 'utf8');
48
+ const oldRootBefore = fixture.hashTree(oldRoot);
49
+ const knowledgePath = fixture.makeKnowledgeDir(root, 'knowledge-index');
50
+ fs.writeFileSync(path.join(knowledgePath, 'README.md'), '# 索引根切换\n\nRET006TOKEN 项目知识\n', 'utf8');
51
+ const knowledgeBefore = fixture.hashTree(knowledgePath);
52
+
53
+ const port = await fixture.freePort();
54
+ const token = 'parallel-knowledge-runtime-token';
55
+ const process_ = fixture.spawnNode([
56
+ path.join(HUB_DIST, 'bin.js'), 'serve', '--port', String(port), '--root', oldRoot, '--config', configPath,
57
+ ], {
58
+ KB_VECTOR_MANAGED: '1',
59
+ KB_VECTOR_RUNTIME_TOKEN: token,
60
+ VECTOR_HUB_DISABLE_PICKER: '1',
61
+ VECTOR_HUB_ROOT: oldRoot,
62
+ VECTOR_HUB_CONFIG: configPath,
63
+ MINIMAX_API_KEY: '',
64
+ });
65
+ try {
66
+ const base = `http://127.0.0.1:${port}`;
67
+ fixture.assertAllowedEndpoint(base, 'vector hub');
68
+ await fixture.waitForHttp(`${base}/api/projects`, 20000);
69
+ const before = await (await fetch(`${base}/api/projects`)).json();
70
+ assert.ok(before.projects.length >= 0);
71
+
72
+ // The real switch: the shell posts the new absolute index root.
73
+ const switched = await fetch(`${base}/api/internal/runtime-config`, {
74
+ method: 'POST',
75
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
76
+ body: JSON.stringify({ settings: null, storageRoot: newRoot }),
77
+ });
78
+ const switchedText = await switched.text();
79
+ assert.equal(switched.status, 200, `runtime-config switch failed: ${switched.status} ${switchedText}`);
80
+ const switchedBody = JSON.parse(switchedText);
81
+
82
+ // ---- RET-006.a-root-switch
83
+ assert.equal(switchedBody.rootPath, path.resolve(newRoot), 'the hub must report the new index root');
84
+ assert.ok(fs.existsSync(newRoot), 'the new index root must be created');
85
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
86
+ assert.equal(path.resolve(config.settings.rootPath), path.resolve(newRoot), 'the persisted configuration must point at the new root');
87
+ const after = await (await fetch(`${base}/api/projects`)).json();
88
+ assert.ok(Array.isArray(after.projects));
89
+ record('RET-006.a-root-switch', 'PASS', {
90
+ port,
91
+ reportedRootPath: switchedBody.rootPath,
92
+ persistedRoot: config.settings.rootPath,
93
+ newRootCreated: fs.existsSync(newRoot),
94
+ });
95
+
96
+ // ---- RET-006.c-old-root-retained
97
+ assert.deepEqual(fixture.hashTree(oldRoot), oldRootBefore, 'the old index root data must be retained byte-for-byte');
98
+ record('RET-006.c-old-root-retained', 'PASS', {
99
+ oldRoot: path.resolve(oldRoot),
100
+ oldRootRetained: JSON.stringify(fixture.hashTree(oldRoot)) === JSON.stringify(oldRootBefore),
101
+ oldRootStillExists: fs.existsSync(oldRoot),
102
+ });
103
+
104
+ // ---- RET-006.d-project-knowledge-kept
105
+ assert.deepEqual(fixture.hashTree(knowledgePath), knowledgeBefore, 'the project knowledge must not be moved by an index root switch');
106
+ record('RET-006.d-project-knowledge-kept', 'PASS', {
107
+ knowledgePath: path.resolve(knowledgePath),
108
+ projectKnowledgeUnchanged: JSON.stringify(fixture.hashTree(knowledgePath)) === JSON.stringify(knowledgeBefore),
109
+ });
110
+
111
+ // ---- RET-006.b-writer-drain
112
+ // The drain barrier, measured on the real HubManager: a real project
113
+ // write that is still running must finish before the root switch
114
+ // resolves.
115
+ const { HubManager } = require(path.join(HUB_DIST, 'server', 'manager.js'));
116
+ const drainOldRoot = path.join(root, 'drain-old');
117
+ const drainNewRoot = path.join(root, 'drain-new');
118
+ const drainConfig = path.join(root, 'drain', 'config.json');
119
+ fs.mkdirSync(drainOldRoot, { recursive: true });
120
+ fs.mkdirSync(path.dirname(drainConfig), { recursive: true });
121
+ const manager = await HubManager.load({ rootPath: drainOldRoot, configPath: drainConfig, managed: true });
122
+ const order = {};
123
+ const write = manager.withProjectWrite('demo', async () => {
124
+ await new Promise(resolve => setTimeout(resolve, 600));
125
+ order.writeFinished = Date.now();
126
+ return 'written';
127
+ });
128
+ await new Promise(resolve => setTimeout(resolve, 50));
129
+ const switchStart = Date.now();
130
+ await manager.applyRuntimeSettings(null, drainNewRoot);
131
+ order.switchFinished = Date.now();
132
+ order.switchStart = switchStart;
133
+ await write;
134
+ assert.ok(order.writeFinished, 'the in-flight write must have finished');
135
+ assert.ok(order.writeFinished <= order.switchFinished, 'the root switch must wait for the old writer to drain');
136
+ assert.ok(order.switchFinished - switchStart >= 400, 'the root switch must not resolve before the pending write completes');
137
+ assert.equal(path.resolve(manager.hub.rootPath), path.resolve(drainNewRoot), 'the manager must point at the new root after the switch');
138
+ assert.ok(fs.existsSync(drainOldRoot), 'the drained root must still exist');
139
+ record('RET-006.b-writer-drain', 'PASS', {
140
+ switchWaitedForWriter: order.switchFinished - order.switchStart,
141
+ writerFinishedBeforeSwitch: order.writeFinished <= order.switchFinished,
142
+ managerRootAfterSwitch: path.resolve(manager.hub.rootPath),
143
+ drainedRootStillExists: fs.existsSync(drainOldRoot),
144
+ });
145
+ } finally {
146
+ process_.child.kill();
147
+ await process_.exited;
148
+ }
149
+
150
+ const summary = {
151
+ lane: 'test-knowledge',
152
+ file: 'parallel-knowledge-vector-test.js',
153
+ cases: results,
154
+ ret006Overall: 'PARTIAL',
155
+ ret006ScopeHere: 'a-d only: this file proves the deterministic, environment-independent sub-items of RET-006 and nothing more.',
156
+ ret006NotExercisedHere: '新根重建(向新根写入真实索引)需要真实 embedding 模型,hub 在未配置模型时拒绝任何索引写入;该环境依赖验收在显式调用的 probe qa-autopilot/tools/product-probes/parallel-knowledge-vector.js 中判定——该 probe 已实现真实 hub upsert/search、根切换、新根重建检索与旧根保留,未取得模型时记 BLOCKED。',
157
+ vectorChannel: 'no index content was written: the environment has no authorised embedding model, so only the deterministic storage-root contracts are proven here',
158
+ probe: 'qa-autopilot/tools/product-probes/parallel-knowledge-vector.js',
159
+ };
160
+ const target = process.env.PARALLEL_KNOWLEDGE_EVIDENCE;
161
+ if (target) fs.mkdirSync(path.dirname(target), { recursive: true });
162
+ if (target) fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
163
+ console.log('parallel-knowledge-vector-test PASS (RET-006 sub-items a-d only; the new-root rebuild is covered by the tracked probe, not here)');
164
+ } finally {
165
+ fixture.removeTree(root);
166
+ fixture.cleanupIsolatedProcess();
167
+ }
168
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+ // P-LINUX-CI-CLOSEOUT-R7 focused case: the log007 day-filter contract, pinned
3
+ // deterministically OFFLINE (real Logger + real LogRepository; no server, no
4
+ // browser, no clock race).
5
+ //
6
+ // CI shape reproduced (run 40062048045, linux22+24, log007
7
+ // timeDayGranularity=false while every other filter stayed green): the live
8
+ // server writes a project-scoped bridge_consumer.identity_backfilled record
9
+ // into the same local-day file ~0.2-1s after health, so the old
10
+ // count === 4 day-window expectation raced the server's own async startup
11
+ // write and lost on the Linux boot timing (Windows CI landed it after the
12
+ // queries). Here the same five-record shape is built with the real logger and
13
+ // the record-anchored contract is asserted: all four fixture records appear in
14
+ // their day's window, and EVERY entry of a from=X&to=X window carries
15
+ // localDay(ts) === X.
16
+ //
17
+ // Day-boundary contract: records with explicit timestamps one millisecond
18
+ // either side of a UTC midnight keep whatever local days the machine's zone
19
+ // assigns; the windows derived from the records themselves return exactly the
20
+ // records dated inside them (file-day naming and matches-day must agree in
21
+ // any zone — the property the old now/now±24h window construction could not
22
+ // guarantee).
23
+
24
+ const fs = require('node:fs');
25
+ const os = require('node:os');
26
+ const path = require('node:path');
27
+ const assert = require('node:assert/strict');
28
+
29
+ const { installLoggingTestEnv, resolveEvidenceDir } = require('./helpers/parallel-logging-integration-5025-env');
30
+ const lane = installLoggingTestEnv({ caseId: 'LINUX-CI-R7-LOG007-DAY', prefix: 'lcr7day' });
31
+
32
+ const { Logger, LogRepository, localDay } = require('../lib/structured-logger');
33
+ const { StorageLayout } = require('../lib/storage-layout');
34
+
35
+ const ROOT = path.resolve(__dirname, '../..');
36
+ const ARTIFACTS = resolveEvidenceDir(path.resolve(ROOT, 'qa-autopilot/artifacts/parallel-20261023/linux-ci-closeout-r7/evidence/log007-day'));
37
+ const PROJECT = 'project-lcr7-day';
38
+
39
+ const shiftDay = (day, delta) => new Date(Date.UTC(Number(day.slice(0, 4)), Number(day.slice(5, 7)) - 1, Number(day.slice(8, 10)) + delta)).toISOString().slice(0, 10);
40
+
41
+ (async () => {
42
+ fs.mkdirSync(ARTIFACTS, { recursive: true });
43
+ const evidence = { caseId: 'LINUX-CI-R7-LOG007-DAY', startedAt: new Date().toISOString(), platform: process.platform };
44
+ const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pk-r7-day-'));
45
+ const layout = new StorageLayout({ dataDir });
46
+ const settings = { levels: ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] };
47
+ const logger = new Logger({ layout, settingsProvider: () => settings });
48
+ const repository = new LogRepository({ layout });
49
+ try {
50
+ // --- 1. the CI five-record shape ------------------------------------
51
+ const baseTs = new Date().toISOString();
52
+ const fixtureEvents = ['lcr7.early.info', 'lcr7.early.warn', 'lcr7.late.warn', 'lcr7.late.error'];
53
+ for (const [index, event] of fixtureEvents.entries()) {
54
+ await logger.log(index === 3 ? 'error' : index === 0 ? 'info' : 'warn', event, `LCR7 ${event}`, { ts: baseTs, projectId: PROJECT, component: `lcr7-comp-${'abc'[index % 3]}` });
55
+ }
56
+ // The server's own startup record: project-scoped, same day, written by
57
+ // the same real logger path.
58
+ await logger.log('info', 'bridge_consumer.identity_backfilled', 'identity backfilled', { ts: baseTs, projectId: PROJECT, component: 'bridge-consumer' });
59
+
60
+ const day = localDay(baseTs);
61
+ const window = await repository.query({ projectId: PROJECT, from: day, to: day, pageSize: 100 });
62
+ const events = window.entries.map(entry => entry.event).sort();
63
+ const oldExpectationHeld = window.entries.length === 4;
64
+ assert.equal(window.entries.length, 5, 'the CI shape holds five same-day project records, not four');
65
+ assert.equal(oldExpectationHeld, false, 'the old count === 4 day-window expectation is exactly what failed on CI');
66
+ for (const event of fixtureEvents) assert.ok(events.includes(event), `fixture record ${event} must appear in its day window`);
67
+ for (const entry of window.entries) assert.equal(localDay(entry.ts), day, `every from=X&to=X entry must be dated X (${entry.event})`);
68
+ const next = await repository.query({ projectId: PROJECT, from: shiftDay(day, 1), to: shiftDay(day, 1), pageSize: 100 });
69
+ const prev = await repository.query({ projectId: PROJECT, from: shiftDay(day, -1), to: shiftDay(day, -1), pageSize: 100 });
70
+ assert.equal(next.entries.length, 0, 'the next-day window is empty for same-day records');
71
+ assert.equal(prev.entries.length, 0, 'the previous-day window is empty for same-day records');
72
+ evidence.ciShape = { day, count: window.entries.length, events, oldCountFourWouldFail: true };
73
+
74
+ // --- 2. day-boundary records in whatever zone the machine runs ------
75
+ const beforeMidnight = '2026-01-01T23:59:59.999Z';
76
+ const afterMidnight = '2026-01-02T00:00:00.001Z';
77
+ await logger.log('info', 'lcr7.boundary.before', 'boundary before', { ts: beforeMidnight, projectId: PROJECT, component: 'lcr7-boundary' });
78
+ await logger.log('info', 'lcr7.boundary.after', 'boundary after', { ts: afterMidnight, projectId: PROJECT, component: 'lcr7-boundary' });
79
+ const dayBefore = localDay(beforeMidnight);
80
+ const dayAfter = localDay(afterMidnight);
81
+ const windowBefore = await repository.query({ projectId: PROJECT, component: 'lcr7-boundary', from: dayBefore, to: dayBefore, pageSize: 100 });
82
+ const windowAfter = await repository.query({ projectId: PROJECT, component: 'lcr7-boundary', from: dayAfter, to: dayAfter, pageSize: 100 });
83
+ assert.ok(windowBefore.entries.some(entry => entry.event === 'lcr7.boundary.before'), 'the pre-midnight record is in its own local-day window');
84
+ assert.ok(windowAfter.entries.some(entry => entry.event === 'lcr7.boundary.after'), 'the post-midnight record is in its own local-day window');
85
+ assert.equal(windowBefore.entries.some(entry => entry.event === 'lcr7.boundary.after'), dayBefore === dayAfter,
86
+ 'the post-midnight record appears in the pre-midnight window only when the zone maps both timestamps to one local day');
87
+ for (const entry of windowBefore.entries) assert.equal(localDay(entry.ts), dayBefore, `boundary window entry dated outside the window (${entry.event})`);
88
+ for (const entry of windowAfter.entries) assert.equal(localDay(entry.ts), dayAfter, `boundary window entry dated outside the window (${entry.event})`);
89
+ evidence.dayBoundary = { zoneOffsetMinutes: new Date().getTimezoneOffset(), dayBefore, dayAfter, sameLocalDay: dayBefore === dayAfter };
90
+
91
+ // --- 3. the day-string arithmetic the driver now uses ---------------
92
+ assert.equal(shiftDay('2026-01-01', -1), '2025-12-31', 'previous day crosses the year boundary');
93
+ assert.equal(shiftDay('2026-02-28', 1), '2026-03-01', 'next day crosses the non-leap February boundary');
94
+ assert.equal(shiftDay('2026-10-24', 1), '2026-10-25', 'next day of the CI failure date');
95
+ assert.equal(shiftDay('2026-10-24', -1), '2026-10-23', 'previous day of the CI failure date');
96
+ evidence.shiftDay = { yearBoundary: true, februaryBoundary: true };
97
+
98
+ evidence.verdict = 'PASS — five-record CI shape returns all four fixture records with every window entry correctly dated (old count===4 expectation reproduced as false); UTC-millisecond boundary records land in their zone-assigned local-day windows; day-string arithmetic is boundary-correct';
99
+ } finally {
100
+ evidence.finishedAt = new Date().toISOString();
101
+ fs.writeFileSync(path.join(ARTIFACTS, 'log007-day-evidence.json'), JSON.stringify(evidence, null, 2) + '\n', 'utf8');
102
+ fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
103
+ lane.cleanup();
104
+ }
105
+ console.log('LINUX-CI-R7 LOG-007 DAY PASS');
106
+ process.exitCode = 0;
107
+ })().catch(error => {
108
+ console.error(`LINUX-CI-R7 LOG-007 DAY FAIL: ${error.message}`);
109
+ process.exitCode = 1;
110
+ });