@sanqianx/project-knowledge 5.0.23 → 5.0.25

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 -1346
  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 +70 -67
  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,184 @@
1
+ 'use strict';
2
+ // Parallel knowledge lane — regression for the engine error-code contract.
3
+ //
4
+ // The vendored knowledge-engine reports a failed read as { ok:false,
5
+ // error:'<CODE>' } with the code as a plain string. The product client used to
6
+ // read the code only from payload.error.code, so every genuine business
7
+ // failure was flattened into the generic ENGINE_REQUEST_FAILED and the real
8
+ // reason was lost for the shell and for the MCP tools.
9
+ //
10
+ // This file pins the contract on a real loopback HTTP server:
11
+ // * the string envelope keeps its code;
12
+ // * the object envelope keeps its code;
13
+ // * an invalid code still falls back to ENGINE_REQUEST_FAILED;
14
+ // * the HTTP status is preserved on the raised DomainError;
15
+ // * a successful read is unchanged.
16
+ //
17
+ // It validates the CURRENT PRODUCT only. It never reads the Git history, the
18
+ // current HEAD, a remote repository or any pre-existing object, so it stays
19
+ // green after the fix is committed and in a shallow checkout or an exported
20
+ // tree without a .git directory.
21
+ //
22
+ // The historical red reproduction (the pre-fix client failing this matrix)
23
+ // was removed from this entry point on purpose: it depended on `git show
24
+ // HEAD:...`, which turns a fixed defect red the moment the fix lands. It is
25
+ // preserved as ignored evidence instead, together with a fixed snapshot of
26
+ // the pre-fix source, so the defect stays documented without the canonical
27
+ // regression depending on Git history:
28
+ // qa-autopilot/artifacts/parallel-20261023/test-knowledge/legacy/
29
+ //
30
+ // Nothing here touches the live 5757/5790/8787/8790 endpoints or the real
31
+ // HOME: the entry point isolates itself before loading any product module.
32
+ const assert = require('node:assert/strict');
33
+ const fs = require('node:fs');
34
+ const path = require('node:path');
35
+ const http = require('node:http');
36
+ const fixture = require('./helpers/parallel-knowledge-fixture');
37
+ fixture.isolateProcess();
38
+ const { StorageLayout } = require('../lib/storage-layout');
39
+ const { EngineKnowledgeClient } = require('../lib/engine-knowledge-client');
40
+
41
+ // --only=patched is accepted as an explicit alias of the default run. No
42
+ // other mode exists here: the historical --only=head mode was moved to the
43
+ // ignored legacy evidence and must not be reintroduced, because a canonical
44
+ // test that depends on Git history is red by construction after the fix is
45
+ // committed.
46
+ const LEGACY_HEAD_MODE = '--only=head';
47
+
48
+ function createEngineStub({ repoPath, knowledgePath, projectId, envelope }) {
49
+ const server = http.createServer((req, res) => {
50
+ const send = (status, body) => {
51
+ res.writeHead(status, { 'Content-Type': 'application/json' });
52
+ res.end(JSON.stringify(body));
53
+ };
54
+ if (req.url === '/api/health') return send(200, { name: 'knowledge-engine', dataSchemaVersion: 3 });
55
+ if (req.url === '/api/projects') {
56
+ return send(200, { ok: true, projects: [{ projectId, enabled: true, repoPath, knowledgePath }] });
57
+ }
58
+ if (req.method === 'POST') {
59
+ let body = '';
60
+ req.on('data', chunk => { body += chunk; });
61
+ req.on('end', () => {
62
+ if (envelope.kind === 'success') return send(200, { ok: true, results: [], answer: 'ok', citations: [] });
63
+ return send(envelope.status, envelope.body);
64
+ });
65
+ return;
66
+ }
67
+ return send(404, { ok: false, error: 'NOT_FOUND' });
68
+ });
69
+ return server;
70
+ }
71
+
72
+ async function listen(server) {
73
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
74
+ const { port } = server.address();
75
+ fixture.assertAllowedPort(port, 'test engine port');
76
+ return `http://127.0.0.1:${port}`;
77
+ }
78
+
79
+ const SCENARIOS = [
80
+ {
81
+ id: 'string-envelope-code-kept',
82
+ envelope: { kind: 'failure', status: 403, body: { ok: false, error: 'PATH_OUTSIDE_ROOT' } },
83
+ expectedCode: 'PATH_OUTSIDE_ROOT',
84
+ expectedStatus: 403,
85
+ },
86
+ {
87
+ id: 'object-envelope-code-kept',
88
+ envelope: { kind: 'failure', status: 404, body: { ok: false, error: { code: 'PROJECT_NOT_FOUND' } } },
89
+ expectedCode: 'PROJECT_NOT_FOUND',
90
+ expectedStatus: 404,
91
+ },
92
+ {
93
+ id: 'invalid-code-falls-back',
94
+ envelope: { kind: 'failure', status: 500, body: { ok: false, error: 'not a valid code!' } },
95
+ expectedCode: 'ENGINE_REQUEST_FAILED',
96
+ expectedStatus: 500,
97
+ },
98
+ {
99
+ id: 'ok-false-on-200-becomes-502',
100
+ envelope: { kind: 'failure', status: 200, body: { ok: false, error: 'INVALID_ARGUMENT' } },
101
+ expectedCode: 'INVALID_ARGUMENT',
102
+ expectedStatus: 502,
103
+ },
104
+ ];
105
+
106
+ (async () => {
107
+ const args = process.argv.slice(2);
108
+ if (args.includes(LEGACY_HEAD_MODE)) {
109
+ console.error(`${LEGACY_HEAD_MODE} was removed from this canonical entry point.`);
110
+ console.error('The pre-fix red reproduction now lives in the ignored evidence:');
111
+ console.error(' qa-autopilot/artifacts/parallel-20261023/test-knowledge/legacy/reproduce-pre-fix-client.js');
112
+ console.error('It uses a fixed source snapshot, so the canonical regression never depends on Git history.');
113
+ process.exitCode = 2;
114
+ return;
115
+ }
116
+
117
+ const root = fixture.makeTempDir('parallel-knowledge-client');
118
+ const layout = new StorageLayout({ dataDir: path.join(root, 'data') });
119
+ const repoPath = path.join(root, 'repo');
120
+ const knowledgePath = path.join(root, 'knowledge');
121
+ fs.mkdirSync(repoPath, { recursive: true });
122
+ fs.mkdirSync(knowledgePath, { recursive: true });
123
+ const project = { projectId: 'project-client', repoPath, knowledgePath };
124
+ const results = [];
125
+
126
+ function record(caseId, status, detail) {
127
+ results.push({ caseId, status, detail });
128
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
129
+ }
130
+
131
+ try {
132
+ for (const scenario of SCENARIOS) {
133
+ const server = createEngineStub({ ...project, envelope: scenario.envelope });
134
+ const engineUrl = await listen(server);
135
+ try {
136
+ const client = new EngineKnowledgeClient({ layout, engineUrl });
137
+ let raised = null;
138
+ try {
139
+ await client.request('search', { query: 'x' }, project);
140
+ } catch (error) { raised = error; }
141
+ assert.ok(raised, 'the client must reject a failed read');
142
+ assert.equal(raised.code, scenario.expectedCode, `unexpected code for ${scenario.id}`);
143
+ assert.equal(raised.status, scenario.expectedStatus, `unexpected status for ${scenario.id}`);
144
+ record(`CLIENT-${scenario.id}`, 'PASS', { code: raised.code, status: raised.status, engineUrl });
145
+ } finally {
146
+ server.closeAllConnections();
147
+ await new Promise(resolve => server.close(resolve));
148
+ }
149
+ }
150
+
151
+ // A successful read must still succeed.
152
+ {
153
+ const server = createEngineStub({ ...project, envelope: { kind: 'success' } });
154
+ const engineUrl = await listen(server);
155
+ try {
156
+ const client = new EngineKnowledgeClient({ layout, engineUrl });
157
+ const payload = await client.request('search', { query: 'x' }, project);
158
+ assert.equal(payload.ok, true);
159
+ assert.ok(Array.isArray(payload.results));
160
+ record('CLIENT-success-envelope-untouched', 'PASS', { ok: payload.ok, resultCount: payload.results.length });
161
+ } finally {
162
+ server.closeAllConnections();
163
+ await new Promise(resolve => server.close(resolve));
164
+ }
165
+ }
166
+
167
+ const summary = {
168
+ lane: 'test-knowledge',
169
+ file: 'parallel-knowledge-client-error-code-test.js',
170
+ cases: results,
171
+ productionTouched: false,
172
+ gitHistoryUsed: false,
173
+ note: 'the canonical regression validates the current product only; the pre-fix red reproduction was moved to the ignored legacy evidence and runs against a fixed source snapshot',
174
+ legacyRedEvidence: 'qa-autopilot/artifacts/parallel-20261023/test-knowledge/legacy/',
175
+ };
176
+ const target = process.env.PARALLEL_KNOWLEDGE_EVIDENCE;
177
+ if (target) fs.mkdirSync(path.dirname(target), { recursive: true });
178
+ if (target) fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
179
+ console.log('parallel-knowledge-client-error-code-test PASS');
180
+ } finally {
181
+ fixture.removeTree(root);
182
+ fixture.cleanupIsolatedProcess();
183
+ }
184
+ })().catch(error => { console.error(error); process.exitCode = 1; });
@@ -0,0 +1,353 @@
1
+ 'use strict';
2
+ // Parallel knowledge lane — engine cases ENG-001, ENG-003, ENG-004, ENG-008.
3
+ // The real vendored knowledge-engine runs in-process on an isolated port with
4
+ // an isolated data directory, and every Git fixture is a real repository with
5
+ // real commits. The analyzer is a declared stub (no model is called) and the
6
+ // vector endpoint is a declared stub that returns no rows, so nothing here is
7
+ // evidence of model or vector behaviour.
8
+ const assert = require('node:assert/strict');
9
+ const fs = require('node:fs');
10
+ const path = require('node:path');
11
+ const http = require('node:http');
12
+ const crypto = require('node:crypto');
13
+ const fixture = require('./helpers/parallel-knowledge-fixture');
14
+ // The entry point isolates itself: ambient module addresses, live data
15
+ // directories and the real HOME are cleared here, before any product module
16
+ // is loaded, so the test never depends on the caller running `env -u`.
17
+ fixture.isolateProcess();
18
+ const { StorageLayout } = require('../lib/storage-layout');
19
+ const { ProjectStore } = require('../lib/project-store');
20
+ const { ProjectRegistryStore } = require('../lib/project-registry-store');
21
+ const { ProjectLifecycleService } = require('../lib/project-lifecycle-service');
22
+ const { createEngineServer } = require('../../_modules/knowledge-engine/src/server');
23
+ const { CommitScanner } = require('../../_modules/knowledge-engine/src/runtime/scanner');
24
+ const AtomicFile = require('../../_modules/knowledge-engine/src/runtime/atomic-file');
25
+
26
+ const results = [];
27
+ function record(caseId, status, detail) {
28
+ results.push({ caseId, status, detail });
29
+ console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
30
+ }
31
+
32
+ async function stubVectorServer() {
33
+ const requests = [];
34
+ const server = http.createServer((req, res) => {
35
+ let body = '';
36
+ req.on('data', chunk => { body += chunk; });
37
+ req.on('end', () => {
38
+ requests.push({ url: req.url, method: req.method });
39
+ res.writeHead(200, { 'Content-Type': 'application/json' });
40
+ res.end(JSON.stringify({ ok: true, results: [], errors: [], projects: [] }));
41
+ });
42
+ });
43
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
44
+ return { requests, url: `http://127.0.0.1:${server.address().port}`, close: () => { server.closeAllConnections(); return new Promise(resolve => server.close(resolve)); } };
45
+ }
46
+
47
+ function forbiddenBridge() {
48
+ const guard = name => async () => { throw new Error(`event bridge must not be contacted from the test lane (${name})`); };
49
+ return { registerConsumer: guard('registerConsumer'), listJournals: guard('listJournals'), readEvents: guard('readEvents'), ackConsumerCursor: guard('ackConsumerCursor'), close() {} };
50
+ }
51
+
52
+ // Declared stub analyzer: stages Markdown plus a manifest, exactly like the
53
+ // canonical engine suite does. It never contacts a model.
54
+ function stubAnalyzer(events = [], options = {}) {
55
+ return {
56
+ sourceReadMode: 'isolated-frozen-evidence',
57
+ async runClaim(input) {
58
+ events.push({ kind: 'start', projectId: input.projectId, sha: input.claim.commitSha });
59
+ const relative = options.manifestPath || `changes/${input.claim.commitSha.slice(0, 12)}.md`;
60
+ const content = `# 引擎晋升条目\n\nENGTOKEN 事实:提交 ${input.claim.commitSha} 已被分析。\n`;
61
+ // An escaping manifest path is the attack under test, so the staged
62
+ // bytes stay inside the staging directory in every case.
63
+ const stagedRelative = relative.split('/').includes('..') ? 'escaped.md' : relative;
64
+ AtomicFile.writeFileAtomic(path.join(input.stagingPath, 'files', stagedRelative), content);
65
+ AtomicFile.writeJsonAtomic(input.manifestPath, {
66
+ schema: 'knowledge-staging-manifest/v1',
67
+ projectId: input.projectId,
68
+ runId: input.claim.runId,
69
+ commitSha: input.claim.commitSha,
70
+ operations: [{
71
+ path: relative,
72
+ operation: 'create',
73
+ sha256: `sha256:${crypto.createHash('sha256').update(content).digest('hex')}`,
74
+ reason: 'Frozen Git evidence',
75
+ evidenceReferences: [`commit:${input.claim.commitSha}`, `patch:${input.claim.patchHash}`, `conversation:${input.claim.conversationSnapshotHash}`, `retrieval:${input.claim.retrievalManifestHash}`],
76
+ }],
77
+ });
78
+ events.push({ kind: 'finish', projectId: input.projectId, sha: input.claim.commitSha });
79
+ return { ok: true };
80
+ },
81
+ };
82
+ }
83
+
84
+ async function startEngine({ dataDir, vectorUrl, analyzer }) {
85
+ fs.mkdirSync(dataDir, { recursive: true });
86
+ const runtime = createEngineServer({ dataDir, vectorUrl, bridge: forbiddenBridge(), analyzer });
87
+ await runtime.ready;
88
+ await new Promise(resolve => runtime.server.listen(0, '127.0.0.1', resolve));
89
+ const base = `http://127.0.0.1:${runtime.server.address().port}`;
90
+ return {
91
+ ...runtime,
92
+ base,
93
+ async call(route, body) {
94
+ const response = await fetch(`${base}${route}`, {
95
+ method: body === undefined ? 'GET' : 'POST',
96
+ headers: { 'Content-Type': 'application/json' },
97
+ body: body === undefined ? undefined : JSON.stringify(body),
98
+ });
99
+ return { status: response.status, body: await response.json() };
100
+ },
101
+ };
102
+ }
103
+
104
+ (async () => {
105
+ const root = fixture.makeTempDir('parallel-knowledge-engine');
106
+ const vector = await stubVectorServer();
107
+ const analyzerEvents = [];
108
+ let engine = null;
109
+ try {
110
+ // --------------------------------------------------------------- ENG-001
111
+ {
112
+ const repo = fixture.makeRepo(root, 'repo-import', [
113
+ { message: '历史提交一', files: { 'a.txt': 'a\n' } },
114
+ { message: '历史提交二', files: { 'b.txt': 'b\n' } },
115
+ { message: '历史提交三', files: { 'c.txt': 'c\n' } },
116
+ ]);
117
+ const repoBefore = fixture.hashTree(repo.repoPath);
118
+ const knowledgeRoot = path.join(root, 'knowledge-root');
119
+ fs.mkdirSync(knowledgeRoot, { recursive: true });
120
+ const layout = new StorageLayout({ dataDir: path.join(root, 'shell-data') });
121
+ const registry = new ProjectRegistryStore({ layout });
122
+ const projects = new ProjectStore({ layout });
123
+ await registry.initialize();
124
+ const lifecycle = new ProjectLifecycleService({
125
+ layout,
126
+ registryStore: registry,
127
+ projectStore: projects,
128
+ settingsStore: { read: () => ({ knowledge: { rootPath: knowledgeRoot }, ai: { schema: 'ai-profiles/v1', profiles: [{ id: 'test-profile', enabled: true, vendor: 'anthropic', model: 'm' }], defaultProfileId: null } }) },
129
+ triggerScriptPath: path.join(root, 'hook-trigger.js'),
130
+ hookManager: { readHookStatus: () => ({ kbManaged: false }), installHook: () => ({ ok: true, managedVersion: 2 }), uninstallHook: () => ({ ok: true }) },
131
+ });
132
+ const imported = await lifecycle.importProject({ localPath: repo.repoPath, projectId: 'project-import', aiProfileId: 'test-profile' });
133
+ assert.equal(imported.state.trackingStartCommit, repo.head, 'the import must record the imported HEAD as the baseline');
134
+ assert.equal(imported.state.lastAnalyzedCommit, null, 'an import must not claim an analysed commit');
135
+ assert.equal(imported.state.trackingMode, 'normal');
136
+ const projectKnowledge = imported.config.knowledgePath;
137
+ assert.deepEqual(fs.readdirSync(projectKnowledge).filter(name => name !== '.gitkeep'), [], 'an import must not create knowledge files');
138
+ assert.deepEqual(fixture.hashTree(repo.repoPath), repoBefore, 'the import must not touch the source repository');
139
+
140
+ // The real engine scanner must see zero pending history behind the baseline.
141
+ const scanner = new CommitScanner();
142
+ const scan = await scanner.scan({ repoPath: repo.repoPath }, { trackingStartCommit: repo.head, lastAnalyzedCommit: null, trackingMode: 'normal' });
143
+ assert.equal(scan.status, 'ok');
144
+ assert.equal(scan.baseline, repo.head);
145
+ assert.deepEqual(scan.commits, [], 'the baseline must cover the whole imported history');
146
+ assert.equal(scan.pendingCount, 0);
147
+
148
+ // A real engine started on that registration must not analyse history.
149
+ engine = await startEngine({ dataDir: path.join(root, 'engine-import'), vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents) });
150
+ const registration = await engine.call('/api/projects', {
151
+ projectId: 'project-import',
152
+ name: 'import',
153
+ repoPath: repo.repoPath,
154
+ knowledgePath: projectKnowledge,
155
+ trackingStartCommit: repo.head,
156
+ brainId: 'brain-default',
157
+ domainId: 'domain-tools',
158
+ });
159
+ assert.equal(registration.status, 200);
160
+ await fixture.eventually(() => engine.store.state.runs.length > 0, 2000);
161
+ assert.deepEqual(engine.store.listRuns(), [], 'starting the engine must not create an analysis run for imported history');
162
+ assert.deepEqual(fixture.hashTree(repo.repoPath), repoBefore, 'the source repository must stay byte-identical');
163
+ record('ENG-001', 'PASS', {
164
+ importedHead: repo.head,
165
+ trackingStartCommit: imported.state.trackingStartCommit,
166
+ lastAnalyzedCommit: imported.state.lastAnalyzedCommit,
167
+ scanStatus: scan.status,
168
+ pendingCommits: scan.pendingCount,
169
+ runsCreated: engine.store.listRuns().length,
170
+ knowledgeFiles: fs.readdirSync(projectKnowledge).filter(name => name !== '.gitkeep').length,
171
+ sourceRepoUnchanged: true,
172
+ });
173
+ await engine.close();
174
+ engine = null;
175
+ }
176
+
177
+ // --------------------------------------------------------------- ENG-003
178
+ {
179
+ const repoPath = path.join(root, 'repo-empty');
180
+ fs.mkdirSync(repoPath, { recursive: true });
181
+ fixture.git(repoPath, ['init', '-q']);
182
+ fixture.git(repoPath, ['config', 'user.email', 'parallel-knowledge@local.invalid']);
183
+ fixture.git(repoPath, ['config', 'user.name', 'Parallel Knowledge Test']);
184
+ const knowledgeRoot = path.join(root, 'knowledge-root-empty');
185
+ fs.mkdirSync(knowledgeRoot, { recursive: true });
186
+ const layout = new StorageLayout({ dataDir: path.join(root, 'shell-data-empty') });
187
+ const registry = new ProjectRegistryStore({ layout });
188
+ const projects = new ProjectStore({ layout });
189
+ await registry.initialize();
190
+ const lifecycle = new ProjectLifecycleService({
191
+ layout,
192
+ registryStore: registry,
193
+ projectStore: projects,
194
+ settingsStore: { read: () => ({ knowledge: { rootPath: knowledgeRoot }, ai: { schema: 'ai-profiles/v1', profiles: [{ id: 'test-profile', enabled: true, vendor: 'anthropic', model: 'm' }], defaultProfileId: null } }) },
195
+ triggerScriptPath: path.join(root, 'hook-trigger.js'),
196
+ hookManager: { readHookStatus: () => ({ kbManaged: false }), installHook: () => ({ ok: true, managedVersion: 2 }), uninstallHook: () => ({ ok: true }) },
197
+ });
198
+ const imported = await lifecycle.importProject({ localPath: repoPath, projectId: 'project-empty', aiProfileId: 'test-profile' });
199
+ assert.equal(imported.state.trackingMode, 'empty-repo');
200
+ assert.equal(imported.state.trackingStartCommit, null, 'a repository without HEAD must not get a baseline commit');
201
+ assert.equal(imported.state.lastAnalyzedCommit, null);
202
+
203
+ const scanner = new CommitScanner();
204
+ const emptyScan = await scanner.scan({ repoPath }, { trackingStartCommit: null, lastAnalyzedCommit: null, trackingMode: 'empty-repo' });
205
+ assert.equal(emptyScan.status, 'empty-repo', 'a repository without HEAD must be reported as empty, not as analysed');
206
+ assert.deepEqual(emptyScan.commits, []);
207
+
208
+ engine = await startEngine({ dataDir: path.join(root, 'engine-empty'), vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents) });
209
+ const knowledgePath = imported.config.knowledgePath;
210
+ await engine.call('/api/projects', { projectId: 'project-empty', name: 'empty', repoPath, knowledgePath, trackingStartCommit: null, brainId: 'brain-default', domainId: 'domain-tools' });
211
+ assert.deepEqual(engine.store.listRuns(), [], 'no analysis may be invented before the first commit exists');
212
+ assert.equal(engine.store.readRuntime('project-empty').lastAnalyzedCommit, null);
213
+
214
+ // The first real commit after the empty import must be processable.
215
+ fs.writeFileSync(path.join(repoPath, 'first.txt'), 'first\n', 'utf8');
216
+ fixture.git(repoPath, ['add', '-A']);
217
+ fixture.git(repoPath, ['commit', '-q', '-m', 'first commit']);
218
+ const firstSha = fixture.git(repoPath, ['rev-parse', 'HEAD']);
219
+ const run = engine.coordinator.enqueue({ projectId: 'project-empty', commitSha: firstSha });
220
+ const settled = await fixture.eventually(() => ['succeeded', 'failed', 'index-pending'].includes(engine.store.getRun(run.runId).state));
221
+ assert.equal(settled, true, 'the first commit must reach a terminal state');
222
+ const finished = engine.store.getRun(run.runId);
223
+ assert.equal(finished.state, 'succeeded', `first commit failed: ${JSON.stringify(finished.error)}`);
224
+ assert.equal(engine.store.readRuntime('project-empty').lastAnalyzedCommit, firstSha);
225
+ assert.ok(fs.existsSync(path.join(knowledgePath, 'changes', `${firstSha.slice(0, 12)}.md`)), 'the first commit must produce knowledge');
226
+ record('ENG-003', 'PASS', {
227
+ trackingMode: imported.state.trackingMode,
228
+ trackingStartCommit: imported.state.trackingStartCommit,
229
+ emptyScanStatus: emptyScan.status,
230
+ runsBeforeFirstCommit: 0,
231
+ firstCommitSha: firstSha,
232
+ firstCommitState: finished.state,
233
+ lastAnalyzedCommit: engine.store.readRuntime('project-empty').lastAnalyzedCommit,
234
+ });
235
+ await engine.close();
236
+ engine = null;
237
+ }
238
+
239
+ // --------------------------------------------------------------- ENG-004
240
+ {
241
+ const repo = fixture.makeRepo(root, 'repo-duplicate', [
242
+ { message: '初始', files: { 'README.md': '# dup\n' } },
243
+ { message: '功能', files: { 'feature.txt': 'feature\n' } },
244
+ ]);
245
+ const knowledgePath = fixture.makeKnowledgeDir(root, 'knowledge-duplicate');
246
+ const dataDir = path.join(root, 'engine-duplicate');
247
+ engine = await startEngine({ dataDir, vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents) });
248
+ await engine.call('/api/projects', { projectId: 'project-dup', name: 'dup', repoPath: repo.repoPath, knowledgePath, brainId: 'brain-default', domainId: 'domain-tools' });
249
+
250
+ const first = engine.coordinator.enqueue({ projectId: 'project-dup', commitSha: repo.head });
251
+ const second = engine.coordinator.enqueue({ projectId: 'project-dup', commitSha: repo.head });
252
+ assert.equal(second.runId, first.runId, 'the same SHA must reuse one run');
253
+ const settled = await fixture.eventually(() => ['succeeded', 'failed', 'index-pending'].includes(engine.store.getRun(first.runId).state));
254
+ assert.equal(settled, true);
255
+ assert.equal(engine.store.getRun(first.runId).state, 'succeeded', `run failed: ${JSON.stringify(engine.store.getRun(first.runId).error)}`);
256
+ const analysesAfterFirst = analyzerEvents.filter(event => event.kind === 'start' && event.sha === repo.head).length;
257
+ assert.equal(analysesAfterFirst, 1, 'one SHA must produce exactly one analysis');
258
+ assert.equal(engine.store.listRuns('project-dup').length, 1, 'one SHA must produce exactly one run');
259
+
260
+ // A repeated explicit reconciliation of the same SHA must be refused by
261
+ // the processing ledger instead of analysing again.
262
+ const repeated = await engine.coordinator.reconciler.processCommitEvent({ projectId: 'project-dup', commitSha: repo.head });
263
+ assert.equal(repeated.status, 'already-completed', 'a repeated SHA must be reported as already completed');
264
+ assert.equal(analyzerEvents.filter(event => event.kind === 'start' && event.sha === repo.head).length, 1, 'a repeated SHA must not analyse again');
265
+
266
+ // Startup reconciliation: a fresh engine on the same data directory.
267
+ await engine.close();
268
+ engine = await startEngine({ dataDir, vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents) });
269
+ const afterRestart = await engine.coordinator.reconciler.processCommitEvent({ projectId: 'project-dup', commitSha: repo.head });
270
+ assert.equal(afterRestart.status, 'already-completed', 'startup reconciliation must not re-analyse a completed SHA');
271
+ assert.equal(analyzerEvents.filter(event => event.kind === 'start' && event.sha === repo.head).length, 1, 'startup reconciliation must not run the model again');
272
+ assert.equal(engine.store.listRuns('project-dup').length, 1);
273
+ const promotedFiles = fs.readdirSync(path.join(knowledgePath, 'changes'));
274
+ assert.deepEqual(promotedFiles, [`${repo.head.slice(0, 12)}.md`], 'exactly one promotion must exist');
275
+ record('ENG-004', 'PASS', {
276
+ runId: first.runId,
277
+ runsCreated: engine.store.listRuns('project-dup').length,
278
+ analysesForSha: analyzerEvents.filter(event => event.kind === 'start' && event.sha === repo.head).length,
279
+ repeatedStatus: repeated.status,
280
+ restartStatus: afterRestart.status,
281
+ promotedFiles,
282
+ });
283
+ await engine.close();
284
+ engine = null;
285
+ }
286
+
287
+ // --------------------------------------------------------------- ENG-008
288
+ {
289
+ const repo = fixture.makeRepo(root, 'repo-escape', [
290
+ { message: '初始', files: { 'README.md': '# escape\n' } },
291
+ { message: '功能', files: { 'feature.txt': 'feature\n' } },
292
+ ]);
293
+ const knowledgePath = fixture.makeKnowledgeDir(root, 'knowledge-escape');
294
+ fs.writeFileSync(path.join(knowledgePath, 'README.md'), '# 已有知识\n\nENG008TOKEN 已有正文\n', 'utf8');
295
+ const knowledgeBefore = fixture.hashTree(knowledgePath);
296
+ const repoBefore = fixture.hashTree(repo.repoPath);
297
+ const outsideMarker = path.join(root, 'escaped.md');
298
+ engine = await startEngine({ dataDir: path.join(root, 'engine-escape'), vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents, { manifestPath: '../escaped.md' }) });
299
+ await engine.call('/api/projects', { projectId: 'project-escape', name: 'escape', repoPath: repo.repoPath, knowledgePath, brainId: 'brain-default', domainId: 'domain-tools' });
300
+ const run = engine.coordinator.enqueue({ projectId: 'project-escape', commitSha: repo.head });
301
+ const settled = await fixture.eventually(() => ['succeeded', 'failed', 'index-pending'].includes(engine.store.getRun(run.runId).state));
302
+ assert.equal(settled, true);
303
+ const failed = engine.store.getRun(run.runId);
304
+ assert.equal(failed.state, 'failed', 'an out-of-bounds staging path must be refused');
305
+ assert.equal(failed.error.code, 'PATH_OUTSIDE_ROOT', `unexpected failure code: ${failed.error.code}`);
306
+ assert.deepEqual(fixture.hashTree(knowledgePath), knowledgeBefore, 'the final knowledge must stay byte-identical');
307
+ assert.deepEqual(fixture.hashTree(repo.repoPath), repoBefore, 'the source repository must stay byte-identical');
308
+ assert.equal(fs.existsSync(outsideMarker), false, 'nothing may be written outside the project knowledge root');
309
+ const state = engine.store.readRuntime('project-escape');
310
+ assert.equal(state.lastAnalyzedCommit, null, 'a refused promotion must not advance the analysed commit');
311
+ assert.equal(state.analysis.status, 'failed');
312
+ assert.equal(state.analysis.activeClaim.phase, 'failed', 'the failed claim must be retained for diagnosis');
313
+ assert.ok(state.analysis.activeClaim.error, 'the failure evidence must be retained');
314
+ const claimFiles = fs.existsSync(path.join(root, 'engine-escape', 'runtime', 'claims', 'project-escape'))
315
+ ? fs.readdirSync(path.join(root, 'engine-escape', 'runtime', 'claims', 'project-escape'))
316
+ : [];
317
+ assert.ok(claimFiles.includes(`${repo.head}.json`), 'the frozen claim snapshot must be retained');
318
+ const runs = await engine.call('/api/runs');
319
+ const reported = runs.body.runs.find(row => row.runId === run.runId);
320
+ assert.ok(reported && reported.error && reported.error.code === 'PATH_OUTSIDE_ROOT', 'the failure must stay visible in the run projection');
321
+ const logDir = path.join(root, 'engine-escape', 'logs', 'projects', 'project-escape');
322
+ const logFiles = fs.existsSync(logDir) ? fs.readdirSync(logDir) : [];
323
+ let failureLogged = false;
324
+ for (const file of logFiles) {
325
+ const records = fs.readFileSync(path.join(logDir, file), 'utf8').trim().split('\n').filter(Boolean).map(line => JSON.parse(line));
326
+ if (records.some(record => record.event === 'knowledge.maintenance.failed' && record.commitSha === repo.head)) failureLogged = true;
327
+ }
328
+ assert.equal(failureLogged, true, 'the failure must be written to the diagnostics log');
329
+ record('ENG-008', 'PASS', {
330
+ runState: failed.state,
331
+ errorCode: failed.error.code,
332
+ knowledgeUnchanged: true,
333
+ sourceRepoUnchanged: true,
334
+ claimRetained: claimFiles.includes(`${repo.head}.json`),
335
+ diagnosticsLogged: failureLogged,
336
+ lastAnalyzedCommit: state.lastAnalyzedCommit,
337
+ });
338
+ await engine.close();
339
+ engine = null;
340
+ }
341
+
342
+ const summary = { lane: 'test-knowledge', file: 'parallel-knowledge-engine-test.js', cases: results, model: 'declared stub analyzer (no model call)', vector: 'declared stub returning no rows' };
343
+ const target = process.env.PARALLEL_KNOWLEDGE_EVIDENCE;
344
+ if (target) fs.mkdirSync(path.dirname(target), { recursive: true });
345
+ if (target) fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
346
+ console.log('parallel-knowledge-engine-test PASS');
347
+ } finally {
348
+ if (engine) await engine.close();
349
+ await vector.close();
350
+ fixture.removeTree(root);
351
+ fixture.cleanupIsolatedProcess();
352
+ }
353
+ })().catch(error => { console.error(error); process.exitCode = 1; });