@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,255 @@
1
+ 'use strict';
2
+
3
+ // P-ENG-FINAL ENG-002 真实Hook闭环 (real hook closed loop) acceptance driver.
4
+ //
5
+ // Case (qa-autopilot/config/test-cases.json ENG-002, P1):
6
+ // steps: 导入fixture→新增真实git commit→等待journal→claim→晋升→查询。
7
+ // expected: 1) 按完整SHA绑定证据。
8
+ // 2) Markdown与检索引用一致。
9
+ // 3) 不能直接API造任务替代Hook验收。
10
+ //
11
+ // What is real here:
12
+ // * a real fixture Git repository and a real `git commit`;
13
+ // * a REAL post-commit hook (a separate OS process running the real
14
+ // ai-coding-event-bridge commit-boundary CLI) that appends the boundary
15
+ // event to an isolated bridge journal;
16
+ // * the real bridge console server (isolated --home) that the engine polls;
17
+ // * the real VENDORED knowledge-engine (consume mode) that drains the
18
+ // journal, binds the claim, promotes knowledge and syncs the index;
19
+ // * the real vector-hub server (isolated root) with deterministic test
20
+ // embeddings - the paid embedding API is NOT exercised.
21
+ //
22
+ // The driver NEVER calls POST /api/runs: the run must arrive through the
23
+ // journal (trigger 'git-hook'), while the API path would stamp
24
+ // trigger 'manual-debug' (server.js:137 vs coordinator.js:193).
25
+ //
26
+ // Analyzer = deterministic stand-in (NO real model output). No quota access, no
27
+ // PAYG, no user HOME/credentials/memory/trust, no production ports.
28
+
29
+ const assert = require('node:assert/strict');
30
+ const fs = require('node:fs');
31
+ const path = require('node:path');
32
+ const {
33
+ makeLaneDir, buildFixtureRepo, installPostCommitHook, startBridge, startVector,
34
+ startEngine, waitUntil, callJson, git, gitRun, listFilesRecursive, saveEvidence, hashText,
35
+ } = require('./helpers/parallel-engine-final-harness.js');
36
+ const { createStandinAnalyzer } = require('./helpers/parallel-engine-final-standin-analyzer.js');
37
+
38
+ const ARTIFACTS = process.env.PK_TEST_EVIDENCE_ROOT
39
+ ? path.join(process.env.PK_TEST_EVIDENCE_ROOT, 'eng002')
40
+ : path.resolve(__dirname, '../../qa-autopilot/artifacts/parallel-20261023/engine-review-r3/reruns/eng002');
41
+ const PROJECT_ID = 'project-eng002';
42
+
43
+ function findJournalFiles(root) {
44
+ const out = [];
45
+ const walk = dir => {
46
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
47
+ const absolute = path.join(dir, entry.name);
48
+ if (entry.isDirectory()) walk(absolute);
49
+ else if (entry.name === 'events.jsonl') out.push(absolute);
50
+ }
51
+ };
52
+ if (fs.existsSync(root)) walk(root);
53
+ return out;
54
+ }
55
+
56
+ async function main() {
57
+ const lane = makeLaneDir('eng002');
58
+ const commands = [];
59
+ const evidence = { caseId: 'ENG-002', laneDir: lane, phases: {}, commands };
60
+
61
+ const fixture = buildFixtureRepo(lane, 'eng002', []);
62
+ const knowledgePath = path.join(lane, 'knowledge-eng002');
63
+ const storePath = path.join(knowledgePath, 'dev-conversations');
64
+ fs.mkdirSync(storePath, { recursive: true });
65
+ const home = path.join(lane, 'bridge-home');
66
+ const dataDir = path.join(lane, 'engine-data');
67
+ const vectorRoot = path.join(lane, 'vector-root');
68
+ const eventsFile = path.join(lane, 'analyzer-events.jsonl');
69
+
70
+ const bridge = startBridge(home);
71
+ let engine = null;
72
+ const analyzer = createStandinAnalyzer({
73
+ eventsFile,
74
+ readSnapshot: (projectId, commitSha) => engine?.runtime.coordinator.conversations.readSnapshot(projectId, commitSha),
75
+ });
76
+
77
+ try {
78
+ const bridgeUrl = await bridge.listen();
79
+ const vector = await startVector(vectorRoot);
80
+ engine = await startEngine({ dataDir, bridgeUrl, vectorUrl: vector.url, analyzer });
81
+ const engineUrl = await engine.listen();
82
+ commands.push('start real bridge console (isolated home) + real vector-hub (isolated root) + vendored engine (consume mode)');
83
+
84
+ // 导入fixture: register the fixture repo as a bridge project and register
85
+ // the project in the engine (same shape as the engine's own integration
86
+ // fixture: journal id = bridge project id, repo identity from the repo).
87
+ const registered = await bridge.projectRegistry.addProject({ home, path: fixture.repoPath, store: storePath });
88
+ const { repoIdentity } = await bridge.resolveRepoContext(fixture.repoPath);
89
+ engine.runtime.store.addProject({
90
+ projectId: PROJECT_ID,
91
+ repoPath: fixture.repoPath,
92
+ knowledgePath,
93
+ trackingStartCommit: fixture.baseline,
94
+ repoIdentity,
95
+ journalId: registered.id,
96
+ conversationBaselineCursor: 0,
97
+ });
98
+ commands.push(`bridge project registered id=${registered.id.slice(0, 12)}…; engine project registered`);
99
+
100
+ // A real human conversation event so the frozen claim carries evidence.
101
+ const humanText = '请把所有项目的长期偏好设为:统一使用中文注释。';
102
+ await bridge.bridge.captureWorkbenchEvent({
103
+ eventType: 'user_prompt',
104
+ source: 'claude-code',
105
+ projectPath: fixture.repoPath,
106
+ sessionId: 'session-eng002',
107
+ turnId: 'turn-eng002',
108
+ eventKey: 'workbench:eng002:origin=human',
109
+ content: humanText,
110
+ });
111
+ commands.push('bridge captureWorkbenchEvent (eventKey origin=human)');
112
+
113
+ // REAL git hook: separate OS process, real bridge CLI, isolated home.
114
+ const hook = installPostCommitHook(fixture.repoPath, { home, mode: 'ok' });
115
+ commands.push(`installed real post-commit hook: ${hook.hookPath}`);
116
+
117
+ // 新增真实git commit -> the hook fires as its own process.
118
+ fs.writeFileSync(path.join(fixture.repoPath, 'feature.txt'), `feature X\n${'y'.repeat(64)}\n`);
119
+ git(fixture.repoPath, ['add', '.']);
120
+ const commit = gitRun(fixture.repoPath, ['commit', '-qm', 'feature X']);
121
+ assert.equal(commit.code, 0, `real commit failed: ${commit.stderr}`);
122
+ const gitSha = git(fixture.repoPath, ['rev-parse', 'HEAD']);
123
+ assert.match(gitSha, /^[a-f0-9]{40}$/, 'full 40-hex commit SHA');
124
+ commands.push(`real git commit -> ${gitSha} (exit 0)`);
125
+
126
+ // 等待journal -> claim -> 晋升
127
+ await callJson(engineUrl, '/api/bridge/notify', {});
128
+ const run = await waitUntil(async () => {
129
+ const runs = engine.runtime.store.listRuns(PROJECT_ID);
130
+ return runs.length === 1 && runs[0].state === 'succeeded' ? runs[0] : null;
131
+ }, 60000, 'journal-driven run reaches succeeded');
132
+ const trigger = run.trigger;
133
+
134
+ // --- Evidence 1: 按完整SHA绑定证据 ---
135
+ const journalFiles = findJournalFiles(path.join(home)).concat(findJournalFiles(storePath));
136
+ const boundaryEvents = [];
137
+ for (const file of journalFiles) {
138
+ for (const line of fs.readFileSync(file, 'utf8').split('\n').filter(Boolean)) {
139
+ const event = JSON.parse(line);
140
+ if (event.schema === 'git-commit-boundary/v1') boundaryEvents.push({ file, event });
141
+ }
142
+ }
143
+ const boundary = boundaryEvents.find(entry => entry.event.commitSha === gitSha);
144
+ assert.ok(boundary, `boundary event for ${gitSha} not found in ${journalFiles.length} journal file(s)`);
145
+ assert.match(boundary.event.commitSha, /^[a-f0-9]{40}$/, 'journal binds the FULL SHA');
146
+ assert.equal(boundary.event.repoIdentity?.workspaceRoot ? path.resolve(boundary.event.repoIdentity.workspaceRoot).toLowerCase() : null, path.resolve(fixture.repoPath).toLowerCase(), 'boundary carries the real repo identity');
147
+
148
+ assert.equal(run.commitSha, gitSha, 'engine run binds the full SHA');
149
+ assert.equal(run.commitSha.length, 40);
150
+ const stateOnDisk = JSON.parse(fs.readFileSync(path.join(dataDir, 'engine-state.json'), 'utf8'));
151
+ const runtimeState = stateOnDisk.projects.find(p => p.projectId === PROJECT_ID).runtime;
152
+ assert.equal(runtimeState.lastAnalyzedCommit, gitSha, 'pointer advanced to the full SHA');
153
+ const ledgerFile = path.join(dataDir, 'runtime', 'processed-commits', PROJECT_ID, `${gitSha}.json`);
154
+ assert.ok(fs.existsSync(ledgerFile), 'processing ledger is keyed by the FULL SHA');
155
+ const ledger = JSON.parse(fs.readFileSync(ledgerFile, 'utf8'));
156
+ assert.equal(ledger.commitSha, gitSha);
157
+ const promotionsDir = path.join(dataDir, 'runtime', 'promotions', PROJECT_ID);
158
+ const promotionFiles = fs.readdirSync(promotionsDir);
159
+ const promotion = JSON.parse(fs.readFileSync(path.join(promotionsDir, promotionFiles[0]), 'utf8'));
160
+ assert.ok(JSON.stringify(promotion).includes(gitSha), 'promotion journal binds the full SHA');
161
+ // The staging manifest lives under the RECONCILER's own run id (a content
162
+ // hash), not the store run id, so it is located by scanning the run roots.
163
+ const runRoots = fs.existsSync(path.join(dataDir, 'runtime', 'runs', PROJECT_ID))
164
+ ? fs.readdirSync(path.join(dataDir, 'runtime', 'runs', PROJECT_ID))
165
+ : [];
166
+ const manifestFiles = runRoots
167
+ .map(root => path.join(dataDir, 'runtime', 'runs', PROJECT_ID, root, 'output', 'manifest.json'))
168
+ .filter(file => fs.existsSync(file));
169
+ assert.equal(manifestFiles.length, 1, `exactly one staging manifest persisted (found ${manifestFiles.length} under ${runRoots.length} run root(s))`);
170
+ const manifestFile = manifestFiles[0];
171
+ const manifest = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
172
+ const commitRefs = manifest.operations.flatMap(op => op.evidenceReferences || []).filter(ref => ref.startsWith('commit:'));
173
+ assert.ok(commitRefs.includes(`commit:${gitSha}`), 'manifest evidence reference uses the full SHA');
174
+
175
+ evidence.phases.fullShaBinding = {
176
+ gitSha,
177
+ journalBoundary: { file: path.basename(boundary.file), sequence: boundary.event.sequence, commitSha: boundary.event.commitSha, workspaceRoot: boundary.event.repoIdentity?.workspaceRoot, parents: boundary.event.parents || null },
178
+ run: { runId: run.runId, trigger, state: run.state, phase: run.phase, commitSha: run.commitSha, promotedAt: run.promotedAt },
179
+ ledger: { path: path.relative(dataDir, ledgerFile), commitSha: ledger.commitSha, status: ledger.status },
180
+ manifestCommitReferences: commitRefs,
181
+ manifestFile: path.relative(dataDir, manifestFile).replace(/\\/g, '/'),
182
+ manifestRunId: manifest.runId,
183
+ reconcilerRunRoots: runRoots,
184
+ promotionJournalPhase: promotion.phase,
185
+ lastAnalyzedCommit: runtimeState.lastAnalyzedCommit,
186
+ };
187
+
188
+ // --- Evidence 2: Markdown与检索引用一致 ---
189
+ const promotedPath = path.join(knowledgePath, 'changes', `${gitSha.slice(0, 12)}.md`);
190
+ assert.ok(fs.existsSync(promotedPath), 'promoted markdown exists');
191
+ const markdown = fs.readFileSync(promotedPath, 'utf8');
192
+ assert.ok(markdown.includes(gitSha), 'promoted markdown cites the full SHA');
193
+ const search = await callJson(engineUrl, '/api/search', { projectId: PROJECT_ID, query: gitSha });
194
+ assert.equal(search.status, 200, `search failed: ${JSON.stringify(search.body).slice(0, 400)}`);
195
+ const hits = (search.body.results || []).filter(row => (row.chunk_text || row.snippet || '').includes(gitSha));
196
+ assert.ok(hits.length, `retrieval returned no hit for the full SHA (results=${(search.body.results || []).length})`);
197
+ const hit = hits[0];
198
+ const hubSearch = await vector.manager.hub.search(gitSha, { projects: [PROJECT_ID] });
199
+ assert.ok(hubSearch.results.length, 'real vector index holds the promoted document');
200
+ evidence.phases.markdownAndRetrieval = {
201
+ promotedFile: path.relative(knowledgePath, promotedPath).replace(/\\/g, '/'),
202
+ knowledgeTree: listFilesRecursive(knowledgePath),
203
+ markdownSha256: hashText(markdown),
204
+ markdownCitesFullSha: markdown.includes(gitSha),
205
+ retrieval: { hitCount: hits.length, entryId: hit.entry_id, spaceId: hit.space_id, score: hit.relevance_score, indexHealth: search.body.health?.index || null },
206
+ vectorIndex: { resultCount: hubSearch.results.length, firstUri: hubSearch.results[0]?.uri || null, snippetHasSha: (hubSearch.results[0]?.snippet || '').includes(gitSha) },
207
+ runIndexState: run.indexState,
208
+ };
209
+ assert.equal(hit.entry_id, `changes/${gitSha.slice(0, 12)}.md`, 'retrieval entry id matches the promoted file');
210
+ assert.equal(run.indexState, 'succeeded', 'index sync to the real vector service succeeded');
211
+
212
+ // --- Evidence 3: 不能直接API造任务替代Hook验收 ---
213
+ const cursors = engine.runtime.store.state.journalCursors || {};
214
+ const consumer = await bridge.bridge.getConsumer('knowledge-engine');
215
+ evidence.phases.hookProvenance = {
216
+ apiRunCallsMadeByDriver: 0,
217
+ runTrigger: trigger,
218
+ apiPathTriggerWouldBe: 'manual-debug',
219
+ journalCursor: cursors[registered.id],
220
+ bridgeConsumerCursor: consumer?.cursors?.[registered.id] ?? null,
221
+ totalRunsForProject: engine.runtime.store.listRuns(PROJECT_ID).length,
222
+ analyzerInvocations: fs.existsSync(eventsFile) ? fs.readFileSync(eventsFile, 'utf8').split('\n').filter(Boolean).length : 0,
223
+ };
224
+ assert.equal(trigger, 'git-hook', 'run came from the journal/hook path, not from POST /api/runs');
225
+ assert.ok(cursors[registered.id] >= 1, 'engine consumed the journal (cursor advanced)');
226
+ assert.ok((consumer?.cursors?.[registered.id] ?? 0) >= 1, 'engine acked the bridge journal cursor');
227
+ assert.equal(engine.runtime.store.listRuns(PROJECT_ID).length, 1, 'exactly one run - no API-created duplicate');
228
+
229
+ evidence.verdict = {
230
+ 'expected1 按完整SHA绑定证据': 'PASS',
231
+ 'expected2 Markdown与检索引用一致': 'PASS',
232
+ 'expected3 不能直接API造任务替代Hook验收': 'PASS',
233
+ realHook: 'real post-commit hook as a separate OS process running the real bridge commit-boundary CLI; journal boundary event verified on disk',
234
+ analyzerStandin: 'deterministic stand-in - NO real model output covered',
235
+ vectorService: 'real vector-hub server, deterministic test embeddings - paid embedding API NOT covered',
236
+ bridge: 'real ai-coding-event-bridge console + journal (isolated home) - real bridge, not a stub',
237
+ };
238
+ await engine.close();
239
+ await vector.close();
240
+ await bridge.close();
241
+ saveEvidence(ARTIFACTS, 'eng002-evidence.json', evidence);
242
+ console.log('ENG-002 PASS');
243
+ process.exit(0);
244
+ } catch (error) {
245
+ evidence.verdict = { failed: String((error && error.stack) || error) };
246
+ try { await engine?.close(); } catch {}
247
+ try { await vector?.close(); } catch {}
248
+ try { await bridge.close(); } catch {}
249
+ saveEvidence(ARTIFACTS, 'eng002-evidence.json', evidence);
250
+ console.error(`ENG-002 FAIL: ${error.message}`);
251
+ process.exit(1);
252
+ }
253
+ }
254
+
255
+ main();
@@ -0,0 +1,269 @@
1
+ 'use strict';
2
+
3
+ // P-ENG-FINAL ENG-007 离线提交补偿 (offline commit compensation) acceptance driver.
4
+ //
5
+ // Case (qa-autopilot/config/test-cases.json ENG-007, P1):
6
+ // steps: 仅停测试服务,Git提交成功后再启动。
7
+ // expected: 1) Git不被Hook故障阻塞。
8
+ // 2) 启动对账补漏。
9
+ // 3) 已完成SHA不重跑。
10
+ //
11
+ // Real behaviour under test (real Git, real bridge, real vendored engine):
12
+ // * the real fixture repository with a REAL post-commit hook running the real
13
+ // ai-coding-event-bridge commit-boundary CLI as a separate OS process
14
+ // * a broken hook (missing CLI) proves Git itself is never blocked: the
15
+ // post-commit exit status cannot fail an already-created commit
16
+ // * the journal is durable: while the engine process is DOWN the boundary
17
+ // event of a real commit is written to disk by the real bridge CLI
18
+ // * the engine's startup drain (coordinator.initialize -> drain ->
19
+ // consumeJournal) replays the journal from cursor 0, so the commit that
20
+ // happened while the engine was down is compensated ("启动对账补漏")
21
+ // * the engine's processing ledger
22
+ // (runtime/processed-commits/<projectId>/<sha>.json) blocks a completed
23
+ // SHA from being dispatched again ("已完成SHA不重跑")
24
+ //
25
+ // Isolation: real bridge console + real vector-hub + real vendored engine on
26
+ // isolated dirs, ephemeral loopback ports, scrubbed env. The broken hook is
27
+ // installed inside the FIXTURE repository only; the user's global Git config
28
+ // and global hooks are never touched. Analyzer = deterministic stand-in (NO
29
+ // real model output). No quota access, no PAYG.
30
+
31
+ const assert = require('node:assert/strict');
32
+ const fs = require('node:fs');
33
+ const path = require('node:path');
34
+ const { spawnSync } = require('node:child_process');
35
+ const {
36
+ makeLaneDir, buildFixtureRepo, installPostCommitHook, startBridge, startVector,
37
+ startEngine, waitUntil, callJson, git, gitRun, readJsonl, saveEvidence,
38
+ isolatedEnv, BRIDGE_CLI,
39
+ } = require('./helpers/parallel-engine-final-harness.js');
40
+ const { createStandinAnalyzer } = require('./helpers/parallel-engine-final-standin-analyzer.js');
41
+
42
+ const ARTIFACTS = process.env.PK_TEST_EVIDENCE_ROOT
43
+ ? path.join(process.env.PK_TEST_EVIDENCE_ROOT, 'eng007')
44
+ : path.resolve(__dirname, '../../qa-autopilot/artifacts/parallel-20261023/engine-review-r3/reruns/eng007');
45
+ const PROJECT_ID = 'project-eng007';
46
+
47
+ function findJournalFiles(root) {
48
+ const out = [];
49
+ const walk = dir => {
50
+ if (!fs.existsSync(dir)) return;
51
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
52
+ const absolute = path.join(dir, entry.name);
53
+ if (entry.isDirectory()) walk(absolute);
54
+ else if (entry.name === 'events.jsonl') out.push(absolute);
55
+ }
56
+ };
57
+ walk(root);
58
+ return out;
59
+ }
60
+
61
+ function boundaryEvents(...roots) {
62
+ const events = [];
63
+ const files = roots.flatMap(root => findJournalFiles(root));
64
+ for (const file of files) {
65
+ for (const line of fs.readFileSync(file, 'utf8').split('\n').filter(Boolean)) {
66
+ const event = JSON.parse(line);
67
+ if (event.schema === 'git-commit-boundary/v1') events.push({ file: path.basename(file), event });
68
+ }
69
+ }
70
+ return events;
71
+ }
72
+
73
+ async function main() {
74
+ const lane = makeLaneDir('eng007');
75
+ const commands = [];
76
+ const evidence = { caseId: 'ENG-007', laneDir: lane, phases: {}, commands };
77
+
78
+ const fixture = buildFixtureRepo(lane, 'eng007', []);
79
+ const knowledgePath = path.join(lane, 'knowledge-eng007');
80
+ const storePath = path.join(knowledgePath, 'dev-conversations');
81
+ fs.mkdirSync(storePath, { recursive: true });
82
+ const home = path.join(lane, 'bridge-home');
83
+ const dataDir = path.join(lane, 'engine-data');
84
+ const vectorRoot = path.join(lane, 'vector-root');
85
+ const eventsFile = path.join(lane, 'analyzer-events.jsonl');
86
+
87
+ const bridge = startBridge(home);
88
+ let engine = null;
89
+ let vector = null;
90
+ const analyzer = createStandinAnalyzer({ eventsFile });
91
+ const invocationsFor = sha => readJsonl(eventsFile).filter(entry => entry.kind === 'start' && entry.commitSha === sha).length;
92
+
93
+ try {
94
+ const bridgeUrl = await bridge.listen();
95
+ vector = await startVector(vectorRoot);
96
+ const registered = await bridge.projectRegistry.addProject({ home, path: fixture.repoPath, store: storePath });
97
+ const { repoIdentity } = await bridge.resolveRepoContext(fixture.repoPath);
98
+ commands.push(`bridge project registered id=${registered.id.slice(0, 12)}…; bridge console on ${bridgeUrl}`);
99
+
100
+ // ---------- Lifecycle 0: the project is registered while the engine runs ----------
101
+ // The engine is started WITHOUT the journal consumer so the registration is
102
+ // persisted before any startup drain can happen; then the engine process is
103
+ // stopped - this is the "仅停测试服务" state of the case.
104
+ engine = await startEngine({ dataDir, bridgeUrl, vectorUrl: vector.url, analyzer, consume: false });
105
+ await engine.listen();
106
+ engine.runtime.store.addProject({
107
+ projectId: PROJECT_ID,
108
+ repoPath: fixture.repoPath,
109
+ knowledgePath,
110
+ trackingStartCommit: fixture.baseline,
111
+ repoIdentity,
112
+ journalId: registered.id,
113
+ conversationBaselineCursor: 0,
114
+ });
115
+ await engine.close();
116
+ engine = null;
117
+ const persistedProject = JSON.parse(fs.readFileSync(path.join(dataDir, 'engine-state.json'), 'utf8'))
118
+ .projects.find(p => p.projectId === PROJECT_ID);
119
+ assert.ok(persistedProject, 'project registration is persisted before the engine is stopped');
120
+ commands.push('engine lifecycle 0: project registered, engine stopped (journal consumer was off during registration)');
121
+
122
+ // ---------- Expected 1: Git is never blocked by a hook fault ----------
123
+ const brokenHook = installPostCommitHook(fixture.repoPath, { home, mode: 'broken' });
124
+ fs.writeFileSync(path.join(fixture.repoPath, 'offline-a.txt'), `offline feature A\n${'a'.repeat(64)}\n`);
125
+ git(fixture.repoPath, ['add', '.']);
126
+ const commitA = gitRun(fixture.repoPath, ['commit', '-qm', 'offline feature A']);
127
+ assert.equal(commitA.code, 0, `Git must not be blocked by a failing hook (exit ${commitA.code}): ${commitA.stderr}`);
128
+ const shaA = git(fixture.repoPath, ['rev-parse', 'HEAD']);
129
+ assert.match(shaA, /^[a-f0-9]{40}$/);
130
+ assert.match(commitA.stderr, /commit-boundary\.missing\.js/, 'the hook really failed (missing CLI reported by git)');
131
+ assert.equal(boundaryEvents(home, storePath).filter(entry => entry.event.commitSha === shaA).length, 0, 'a failed hook writes no boundary event');
132
+ const headAfterBrokenHook = git(fixture.repoPath, ['log', '-1', '--format=%s']);
133
+ assert.equal(headAfterBrokenHook, 'offline feature A', 'the commit itself exists despite the hook failure');
134
+ commands.push(`real git commit with a BROKEN hook -> exit 0, HEAD=${shaA.slice(0, 8)}, hook error surfaced in stderr`);
135
+
136
+ // ---------- Expected 2 (part 1): the real commit lands in the journal while the engine is down ----------
137
+ const workingHook = installPostCommitHook(fixture.repoPath, { home, mode: 'ok' });
138
+ fs.writeFileSync(path.join(fixture.repoPath, 'offline-b.txt'), `offline feature B\n${'b'.repeat(64)}\n`);
139
+ git(fixture.repoPath, ['add', '.']);
140
+ const commitB = gitRun(fixture.repoPath, ['commit', '-qm', 'offline feature B']);
141
+ assert.equal(commitB.code, 0, `real commit failed: ${commitB.stderr}`);
142
+ const shaB = git(fixture.repoPath, ['rev-parse', 'HEAD']);
143
+ assert.match(shaB, /^[a-f0-9]{40}$/);
144
+ const offlineBoundaries = boundaryEvents(home, storePath).filter(entry => entry.event.commitSha === shaB);
145
+ assert.equal(offlineBoundaries.length, 1, 'the boundary event of the offline commit is on disk');
146
+ assert.equal(fs.existsSync(path.join(dataDir, 'engine-state.json')) ? JSON.parse(fs.readFileSync(path.join(dataDir, 'engine-state.json'), 'utf8')).journalCursors[registered.id] || 0 : 0, 0, 'the stopped engine consumed nothing');
147
+ commands.push(`real git commit while the engine was DOWN -> exit 0, HEAD=${shaB.slice(0, 8)}, boundary event written to the journal (sequence ${offlineBoundaries[0].event.sequence})`);
148
+
149
+ // ---------- Expected 2 (part 2): startup reconciliation compensates the missed SHA ----------
150
+ engine = await startEngine({ dataDir, bridgeUrl, vectorUrl: vector.url, analyzer, consume: true });
151
+ const engineUrl = await engine.listen();
152
+ const runB = await waitUntil(async () => {
153
+ const run = engine.runtime.store.listRuns(PROJECT_ID).find(r => r.commitSha === shaB);
154
+ return run && run.state === 'succeeded' ? run : null;
155
+ }, 60000, 'startup drain compensates the commit made while the engine was down');
156
+ const cursorAfterStart = engine.runtime.store.state.journalCursors[registered.id] || 0;
157
+ const ledgerB = path.join(dataDir, 'runtime', 'processed-commits', PROJECT_ID, `${shaB}.json`);
158
+ const promotedB = path.join(knowledgePath, 'changes', `${shaB.slice(0, 12)}.md`);
159
+ assert.ok(fs.existsSync(ledgerB), 'the compensated commit is written to the processing ledger');
160
+ assert.equal(JSON.parse(fs.readFileSync(ledgerB, 'utf8')).status, 'completed');
161
+ assert.ok(fs.existsSync(promotedB), 'the compensated commit produced promoted knowledge');
162
+ const runAExists = engine.runtime.store.listRuns(PROJECT_ID).some(r => r.commitSha === shaA);
163
+ evidence.phases.startupCompensation = {
164
+ offlineCommitSha: shaB,
165
+ journalSequence: offlineBoundaries[0].event.sequence,
166
+ engineWasDownDuringCommit: true,
167
+ run: { runId: runB.runId, trigger: runB.trigger, state: runB.state, phase: runB.phase, commitSha: runB.commitSha, indexState: runB.indexState, promotedAt: runB.promotedAt },
168
+ journalCursorAfterStart: cursorAfterStart,
169
+ ledger: { path: path.relative(dataDir, ledgerB).replace(/\\/g, '/'), status: JSON.parse(fs.readFileSync(ledgerB, 'utf8')).status },
170
+ promotedFile: path.relative(knowledgePath, promotedB).replace(/\\/g, '/'),
171
+ analyzerInvocationsForOfflineCommit: invocationsFor(shaB),
172
+ hookFailedCommitSha: shaA,
173
+ hookFailedCommitAnalyzed: runAExists,
174
+ hookFailedCommitJournalEvents: 0,
175
+ };
176
+ assert.equal(runB.trigger, 'git-hook', 'the compensated run came from the journal/hook path');
177
+ assert.ok(cursorAfterStart >= offlineBoundaries[0].event.sequence, 'the startup drain advanced the journal cursor past the offline event');
178
+ assert.equal(invocationsFor(shaB), 1, 'the missed commit was analysed exactly once');
179
+ assert.equal(runAExists, false, 'a commit whose hook failed produced no evidence and was not analysed (documented boundary)');
180
+ commands.push(`engine started -> startup drain compensated ${shaB.slice(0, 8)} (trigger=${runB.trigger}, cursor=${cursorAfterStart})`);
181
+
182
+ // ---------- Expected 3: a completed SHA is not run again ----------
183
+ // A duplicated boundary event for the already completed SHA (the same real
184
+ // bridge CLI, pointed at the same commit) is delivered to the journal.
185
+ const replay = spawnSync(process.execPath, [BRIDGE_CLI, '--home', home, '--cwd', fixture.repoPath, '--sha', shaB], {
186
+ cwd: fixture.repoPath, windowsHide: true, encoding: 'utf8', env: isolatedEnv(),
187
+ });
188
+ assert.equal(replay.status, 0, `replaying the boundary event failed: ${replay.stderr}`);
189
+ const replayed = JSON.parse(replay.stdout);
190
+ assert.equal(replayed.ok, true);
191
+ const cursorBeforeReplay = engine.runtime.store.state.journalCursors[registered.id] || 0;
192
+ await waitUntil(async () => (engine.runtime.store.state.journalCursors[registered.id] || 0) > cursorBeforeReplay, 20000, 'the duplicated boundary event is consumed');
193
+ const cursorAfterReplay = engine.runtime.store.state.journalCursors[registered.id] || 0;
194
+ const runsAfterReplay = engine.runtime.store.listRuns(PROJECT_ID);
195
+ const ledgerRecord = engine.runtime.coordinator.reconciler.processingLedger.read(PROJECT_ID, shaB);
196
+ const lastAnalyzed = engine.runtime.store.getProject(PROJECT_ID)?.runtime?.lastAnalyzedCommit || null;
197
+ const ancestor = gitRun(fixture.repoPath, ['merge-base', '--is-ancestor', shaB, lastAnalyzed || shaB]);
198
+ const promotedFiles = fs.existsSync(path.join(knowledgePath, 'changes'))
199
+ ? fs.readdirSync(path.join(knowledgePath, 'changes'))
200
+ : [];
201
+ evidence.phases.completedShaNotRerun = {
202
+ replayedBoundary: { ok: replayed.ok, commitSha: replayed.commitSha, sequence: replayed.sequence },
203
+ journalCursorBefore: cursorBeforeReplay,
204
+ journalCursorAfter: cursorAfterReplay,
205
+ duplicateEventConsumed: cursorAfterReplay > cursorBeforeReplay,
206
+ runRecordCount: runsAfterReplay.length,
207
+ runsForSha: runsAfterReplay.filter(r => r.commitSha === shaB).length,
208
+ analyzerInvocations: invocationsFor(shaB),
209
+ ledgerCompleted: !!ledgerRecord,
210
+ ledgerStatus: ledgerRecord?.status || null,
211
+ guards: { completedShaLedger: !!ledgerRecord, alreadyAnalyzedAncestor: ancestor.code === 0, lastAnalyzedCommit: lastAnalyzed },
212
+ promotedFiles,
213
+ healthCounts: (await callJson(engineUrl, '/api/health')).body.counts,
214
+ };
215
+ assert.ok(cursorAfterReplay > cursorBeforeReplay, 'the duplicated event really reached the engine');
216
+ assert.equal(runsAfterReplay.filter(r => r.commitSha === shaB).length, 1, 'no second run for the completed SHA');
217
+ assert.equal(invocationsFor(shaB), 1, 'the completed SHA was not analysed again');
218
+ assert.ok(ledgerRecord, 'the completed SHA is still recorded in the processing ledger');
219
+ assert.equal(promotedFiles.length, 1, 'no duplicate promoted knowledge');
220
+ commands.push(`duplicated boundary event consumed (cursor ${cursorBeforeReplay}->${cursorAfterReplay}); runs=${runsAfterReplay.length}, analyzer invocations=${invocationsFor(shaB)}`);
221
+
222
+ // ---------- Restart: the completed SHA stays completed ----------
223
+ await engine.close();
224
+ engine = await startEngine({ dataDir, bridgeUrl, vectorUrl: vector.url, analyzer, consume: true });
225
+ await engine.listen();
226
+ await new Promise(resolve => setTimeout(resolve, 3000));
227
+ const runsAfterRestart = engine.runtime.store.listRuns(PROJECT_ID);
228
+ evidence.phases.restartNoRerun = {
229
+ engineRestarted: true,
230
+ runRecordCount: runsAfterRestart.length,
231
+ analyzerInvocations: invocationsFor(shaB),
232
+ journalCursor: engine.runtime.store.state.journalCursors[registered.id] || 0,
233
+ ledgerStillCompleted: fs.existsSync(ledgerB) && JSON.parse(fs.readFileSync(ledgerB, 'utf8')).status === 'completed',
234
+ lastAnalyzedCommit: engine.runtime.store.getProject(PROJECT_ID)?.runtime?.lastAnalyzedCommit || null,
235
+ };
236
+ assert.equal(runsAfterRestart.length, 1, 'the restarted engine did not create a new run');
237
+ assert.equal(invocationsFor(shaB), 1, 'the restarted engine did not analyse the completed SHA again');
238
+ assert.equal(evidence.phases.restartNoRerun.ledgerStillCompleted, true);
239
+ commands.push('engine restarted on the same data dir -> no new run, no second analysis of the completed SHA');
240
+
241
+ evidence.verdict = {
242
+ 'expected1 Git不被Hook故障阻塞': 'PASS',
243
+ 'expected2 启动对账补漏': 'PASS',
244
+ 'expected3 已完成SHA不重跑': 'PASS',
245
+ realGit: 'real fixture repository, real post-commit hook (separate OS process) running the real bridge commit-boundary CLI; a deliberately broken hook proves Git is not blocked',
246
+ startupReconciliation: 'the durable journal is drained from cursor 0 when the engine starts, so the commit made while the engine was down is compensated through the real git-hook path',
247
+ completedShaGuard: 'the processing ledger (runtime/processed-commits/<projectId>/<sha>.json) plus the already-analysed-ancestor check block a second dispatch of a completed SHA; observed via analyzer invocation count and run count',
248
+ analyzerStandin: 'deterministic stand-in - NO real model output covered',
249
+ vectorService: 'real vector-hub server, deterministic test embeddings - paid embedding API NOT covered',
250
+ boundary: 'A commit whose hook FAILED leaves no boundary event in the journal, so there is no evidence to compensate and that commit is never analysed (recorded as hookFailedCommitAnalyzed=false). The compensation guarantee covers commits whose real hook succeeded; the engine does not scan Git history for commits without a journal event.',
251
+ };
252
+ await engine.close();
253
+ await vector.close();
254
+ await bridge.close();
255
+ saveEvidence(ARTIFACTS, 'eng007-evidence.json', evidence);
256
+ console.log('ENG-007 PASS');
257
+ process.exit(0);
258
+ } catch (error) {
259
+ evidence.verdict = { failed: String((error && error.stack) || error) };
260
+ try { await engine?.close(); } catch {}
261
+ try { await vector?.close(); } catch {}
262
+ try { await bridge.close(); } catch {}
263
+ saveEvidence(ARTIFACTS, 'eng007-evidence.json', evidence);
264
+ console.error(`ENG-007 FAIL: ${error.message}`);
265
+ process.exit(1);
266
+ }
267
+ }
268
+
269
+ main();