@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,224 @@
1
+ 'use strict';
2
+ // P-LOG-ACCEPTANCE-01 LOG-003 日志查询 acceptance driver (real product page in a
3
+ // real browser against an isolated managed instance).
4
+ //
5
+ // Case (qa-autopilot/config/test-cases.json LOG-003, P1):
6
+ // steps: 系统状态→诊断,输入唯一operationId,选择当前项目。
7
+ // expected: 1) GET返回与UI一致。
8
+ // 2) 不含其他项目敏感信息。
9
+ //
10
+ // Real behaviour under test: the diagnostics dialog (ui/product.js loadLogs)
11
+ // queries GET /api/logs?pageSize=100&q=<operationId>&projectId=<current>; the
12
+ // repository filters by q (full text incl. operationId) and projectId; project
13
+ // logs live in per-project directories (<dataDir>/logs/projects/<id>/). Two
14
+ // isolated projects with unique per-run operationIds and private markers prove
15
+ // the project boundary in DOM, API and on disk. No stubs on the UI path.
16
+
17
+ const fs = require('node:fs');
18
+ const path = require('node:path');
19
+ const crypto = require('node:crypto');
20
+
21
+ // Portable isolation (P-QA-LOG-INTEGRATION-5025): the temp root comes from
22
+ // PK_TEST_TEMP_ROOT or os.tmpdir() and must live outside the source tree;
23
+ // HOME/APPDATA/LOCALAPPDATA/TEMP/CODEX_HOME/CLAUDE_CONFIG_DIR and the dual
24
+ // product data roots are redirected before any other module is loaded.
25
+ const { installLoggingTestEnv, resolveEvidenceDir } = require('./helpers/parallel-logging-integration-5025-env');
26
+ const lane = installLoggingTestEnv({ caseId: 'LOG-003', prefix: 'log003' });
27
+ const laneTmp = lane.laneTmp;
28
+
29
+ const { spawnServer } = require('./helpers/spawn-server');
30
+ const { findChrome } = require('./helpers/find-chrome');
31
+ const { launchCdpBrowser, waitFor } = require('./helpers/cdp-browser');
32
+ const freePort = require('./helpers/free-port');
33
+ const { createLoggingAcceptanceFixture, writeProjectLogs } = require('./helpers/parallel-logging-acceptance-fixture');
34
+ const { createObservationProxy } = require('./helpers/parallel-logging-acceptance-proxy');
35
+ // The logger names its day files by LOCAL day; the disk assertions must use
36
+ // the same clock, not the UTC day (a UTC+8 machine runs 00:00-08:00 on the
37
+ // previous UTC date).
38
+ const { localDay } = require('../lib/structured-logger');
39
+
40
+ const ROOT = path.resolve(__dirname, '../..');
41
+ const ARTIFACTS = resolveEvidenceDir(path.resolve(ROOT, 'qa-autopilot/artifacts/parallel-20261023/logging-acceptance-r1/evidence/log003'));
42
+
43
+ const PROJECT_A = 'project-log003-alpha';
44
+ const PROJECT_B = 'project-log003-beta';
45
+ const run = crypto.randomBytes(6).toString('hex');
46
+ const OP_ALPHA = `op-log003-alpha-${run}`;
47
+ const OP_BETA = `op-log003-beta-${run}`;
48
+ const MARKER_ALPHA = `LOG003-ALPHA-PRIVATE-${run}`;
49
+ const MARKER_BETA = `LOG003-BETA-PRIVATE-${run}`;
50
+
51
+ async function main() {
52
+ fs.mkdirSync(ARTIFACTS, { recursive: true });
53
+ const evidence = { caseId: 'LOG-003', startedAt: new Date().toISOString(), opAlpha: OP_ALPHA, opBeta: OP_BETA, markerAlpha: MARKER_ALPHA, markerBeta: MARKER_BETA, phases: {} };
54
+
55
+ const fixture = await createLoggingAcceptanceFixture({
56
+ caseId: 'log003',
57
+ projects: [
58
+ { projectId: PROJECT_A, displayName: 'LOG003甲项目' },
59
+ { projectId: PROJECT_B, displayName: 'LOG003乙项目' },
60
+ ],
61
+ });
62
+ await writeProjectLogs(fixture.logger, PROJECT_A, [
63
+ ...Array.from({ length: 4 }, (_, index) => ({ level: index % 2 ? 'warn' : 'info', event: `log003.alpha.flow.${index}`, message: `${MARKER_ALPHA} 甲项目流程记录 ${index}`, operationId: OP_ALPHA })),
64
+ ...Array.from({ length: 6 }, (_, index) => ({ level: 'info', event: `log003.alpha.other.${index}`, message: `${MARKER_ALPHA} 甲项目普通记录 ${index}`, operationId: `op-log003-alpha-other-${run}-${index}` })),
65
+ ]);
66
+ await writeProjectLogs(fixture.logger, PROJECT_B, [
67
+ ...Array.from({ length: 3 }, (_, index) => ({ level: 'info', event: `log003.beta.flow.${index}`, message: `${MARKER_BETA} 乙项目流程记录 ${index}`, operationId: OP_BETA })),
68
+ ...Array.from({ length: 5 }, (_, index) => ({ level: 'debug', event: `log003.beta.other.${index}`, message: `${MARKER_BETA} 乙项目普通记录 ${index}`, operationId: `op-log003-beta-other-${run}-${index}` })),
69
+ ]);
70
+ await fixture.logger.info('log003.system.note', 'LOG003 系统范围记录', {});
71
+ await fixture.logger.close();
72
+
73
+ const port = await freePort();
74
+ const proxyPort = await freePort();
75
+ const base = `http://127.0.0.1:${port}`;
76
+ const server = spawnServer({ root: ROOT, port, dataDir: fixture.dataDir, tag: 'log003', stdio: 'ignore' });
77
+ let browser;
78
+ let proxy;
79
+ try {
80
+ await waitFor(async () => { try { return (await fetch(`${base}/api/health`)).ok; } catch { return false; } }, 'isolated web shell', 20000);
81
+ proxy = await createObservationProxy({ targetPort: port, port: proxyPort });
82
+ proxy.trackPrefix('/api/logs/stream');
83
+ evidence.server = { pid: server.child.pid, port, dataDir: fixture.dataDir, proxyPort };
84
+
85
+ browser = await launchCdpBrowser({ chrome: findChrome(), profileDir: path.join(laneTmp, 'browser'), url: proxy.url, width: 1440, height: 900 });
86
+ const runExpr = expression => browser.evaluate(expression);
87
+ const click = selector => runExpr(`document.querySelector(${JSON.stringify(selector)}).click()`);
88
+ evidence.browser = await runExpr('navigator.userAgent');
89
+
90
+ await waitFor(() => runExpr(`document.getElementById('projectSelect') && document.getElementById('projectSelect').length >= 2`), 'both isolated projects registered', 20000);
91
+ await runExpr(`document.getElementById('projectSelect').value = ${JSON.stringify(PROJECT_A)}; document.getElementById('projectSelect').dispatchEvent(new Event('change'))`);
92
+ await waitFor(() => runExpr(`document.getElementById('projectSelect').value === ${JSON.stringify(PROJECT_A)}`), 'project A selected');
93
+
94
+ // Real server activity (e.g. project-scoped engine snapshot calls) also
95
+ // lands in per-project logs, so fixed counts are not the invariant here.
96
+ // The isolation invariants are: every rendered record belongs to the
97
+ // selected project, private markers never cross the boundary, the unique
98
+ // operationId query returns exactly its own 4 records, and the same-filter
99
+ // GET returns exactly what the UI rendered (id-set identity).
100
+ const readDom = () => runExpr(`JSON.stringify({ count: document.querySelectorAll('#logsContent .log-entry').length, records: [...document.querySelectorAll('#logsContent .log-detail')].map(node => JSON.parse(node.textContent)), status: document.getElementById('logsStatus').textContent, dialogText: document.getElementById('logsDialog').textContent })`);
101
+
102
+ // 系统状态 → 诊断 with 当前项目 scope.
103
+ await click('#openSystem');
104
+ await click('#openLogs');
105
+ await runExpr(`document.getElementById('logsScope').value = 'project'; document.getElementById('logsScope').dispatchEvent(new Event('change'))`);
106
+ await waitFor(async () => {
107
+ const dom = JSON.parse(await readDom());
108
+ return dom.count >= 10 && dom.status.includes('当前项目') && dom.records.every(record => record.projectId === PROJECT_A);
109
+ }, 'project A scope renders only project A records', 20000);
110
+ const domA = JSON.parse(await readDom());
111
+ evidence.phases.projectScopeA = {
112
+ domEntries: domA.count,
113
+ status: domA.status,
114
+ domProjectIds: [...new Set(domA.records.map(record => record.projectId))],
115
+ alphaMarkerPresent: domA.dialogText.includes(MARKER_ALPHA),
116
+ betaMarkerVisibleInA: domA.dialogText.includes(MARKER_BETA),
117
+ };
118
+ if (evidence.phases.projectScopeA.domProjectIds.some(id => id !== PROJECT_A)) throw new Error('project A view rendered records from another project');
119
+ if (evidence.phases.projectScopeA.betaMarkerVisibleInA) throw new Error('project A view leaked project B records');
120
+ if (!evidence.phases.projectScopeA.alphaMarkerPresent) throw new Error('project A view lost its own records');
121
+
122
+ // expected1 (project scope): same-filter GET matches the UI rendering.
123
+ const getA = await (await fetch(`${base}/api/logs?pageSize=100&q=&projectId=${PROJECT_A}`)).json();
124
+ const domAAfterGet = JSON.parse(await readDom());
125
+ const getAIds = getA.entries.map(entry => entry.id).sort();
126
+ const domAIds = domA.records.map(record => record.id).sort();
127
+ const domAAfterIds = domAAfterGet.records.map(record => record.id).sort();
128
+ const consistentA = JSON.stringify(getAIds) === JSON.stringify(domAIds) || JSON.stringify(getAIds) === JSON.stringify(domAAfterIds);
129
+ evidence.phases.getUiConsistencyProjectA = {
130
+ getEntryCount: getA.entries.length,
131
+ domEntryCount: domA.count,
132
+ domEntryCountAfterGet: domAAfterGet.count,
133
+ identical: consistentA,
134
+ getIdSet: getAIds,
135
+ };
136
+ if (!consistentA) throw new Error('GET (project A scope) differs from the UI rendering');
137
+
138
+ // Case step: 输入唯一operationId → exactly its 4 flow records.
139
+ await runExpr(`document.getElementById('logsSearch').value = ${JSON.stringify(OP_ALPHA)}`);
140
+ await click('#loadLogs');
141
+ await waitFor(async () => {
142
+ const dom = JSON.parse(await readDom());
143
+ return dom.count === 4 && dom.records.every(record => record.operationId === OP_ALPHA);
144
+ }, 'unique operationId query narrows the DOM to its 4 records', 20000);
145
+ const domOp = JSON.parse(await readDom());
146
+ if (domOp.records.some(record => record.projectId !== PROJECT_A)) throw new Error('unique operationId query returned records from another project');
147
+
148
+ // expected1 (unique operationId): GET returns exactly what the UI shows.
149
+ const getOp = await (await fetch(`${base}/api/logs?pageSize=100&q=${encodeURIComponent(OP_ALPHA)}&projectId=${PROJECT_A}`)).json();
150
+ const getOpIds = getOp.entries.map(entry => entry.id).sort();
151
+ const domOpIds = domOp.records.map(record => record.id).sort();
152
+ evidence.phases.getUiConsistencyOperationId = {
153
+ getEntryCount: getOp.entries.length,
154
+ domEntryCount: domOp.count,
155
+ identical: JSON.stringify(getOpIds) === JSON.stringify(domOpIds),
156
+ getIdSet: getOpIds,
157
+ domIdSet: domOpIds,
158
+ };
159
+ if (JSON.stringify(getOpIds) !== JSON.stringify(domOpIds)) throw new Error('GET (unique operationId) differs from the UI rendering');
160
+
161
+ await browser.screenshot(path.join(ARTIFACTS, 'log003-alpha-operationId.png'));
162
+
163
+ // expected2: scope 系统与所有项目 keeps the unique query exact.
164
+ await runExpr(`document.getElementById('logsScope').value = 'all'; document.getElementById('logsScope').dispatchEvent(new Event('change'))`);
165
+ await waitFor(async () => {
166
+ const dom = JSON.parse(await readDom());
167
+ return dom.count === 4 && dom.records.every(record => record.operationId === OP_ALPHA) && dom.status.includes('系统与所有项目');
168
+ }, 'scope all still finds exactly the alpha flow');
169
+ // And switching the current project flips the isolation boundary.
170
+ // (Scope and search are set silently; selectProject's reload reads them.)
171
+ await runExpr(`document.getElementById('logsScope').value = 'project'; document.getElementById('logsSearch').value = ''`);
172
+ await runExpr(`document.getElementById('projectSelect').value = ${JSON.stringify(PROJECT_B)}; document.getElementById('projectSelect').dispatchEvent(new Event('change'))`);
173
+ await waitFor(async () => {
174
+ const dom = JSON.parse(await readDom());
175
+ return dom.count >= 8 && dom.status.includes('当前项目') && dom.records.every(record => record.projectId === PROJECT_B) && dom.dialogText.includes(MARKER_BETA);
176
+ }, 'project B scope renders only project B records', 20000);
177
+ const domB = JSON.parse(await readDom());
178
+ const alphaLeakInB = domB.dialogText.includes(OP_ALPHA) || domB.dialogText.includes(MARKER_ALPHA);
179
+ evidence.phases.projectScopeB = { domEntries: domB.count, domProjectIds: [...new Set(domB.records.map(record => record.projectId))], betaMarkerPresent: true, alphaVisibleInB: alphaLeakInB };
180
+ if (evidence.phases.projectScopeB.domProjectIds.some(id => id !== PROJECT_B)) throw new Error('project B view rendered records from another project');
181
+ if (alphaLeakInB) throw new Error('project B view leaked project A records');
182
+ await browser.screenshot(path.join(ARTIFACTS, 'log003-beta-isolation.png'));
183
+
184
+ // Disk correspondence: per-project directories hold only their own flows.
185
+ const day = localDay();
186
+ const readDay = dir => fs.readFileSync(path.join(fixture.dataDir, 'logs', 'projects', dir, `${day}.jsonl`), 'utf8');
187
+ const diskA = readDay(PROJECT_A);
188
+ const diskB = readDay(PROJECT_B);
189
+ evidence.phases.disk = {
190
+ alphaFileHasFlow: diskA.split(OP_ALPHA).length - 1 === 4,
191
+ alphaFileHasNoBeta: !diskA.includes(MARKER_BETA),
192
+ betaFileHasFlow: diskB.split(OP_BETA).length - 1 === 3,
193
+ betaFileHasNoAlpha: !diskB.includes(MARKER_ALPHA),
194
+ };
195
+ if (!evidence.phases.disk.alphaFileHasFlow || !evidence.phases.disk.alphaFileHasNoBeta || !evidence.phases.disk.betaFileHasFlow || !evidence.phases.disk.betaFileHasNoAlpha) {
196
+ throw new Error('per-project log directories violated the isolation boundary');
197
+ }
198
+
199
+ if (browser.exceptions.length) throw new Error(`browser exceptions: ${JSON.stringify(browser.exceptions)}`);
200
+ evidence.verdict = { 'expected1 GET返回与UI一致': 'PASS', 'expected2 不含其他项目敏感信息': 'PASS' };
201
+ console.log('LOG-003 PASS');
202
+ process.exitCode = 0;
203
+ } catch (error) {
204
+ evidence.verdict = { failed: String((error && error.stack) || error) };
205
+ try {
206
+ if (browser) {
207
+ evidence.debugDom = JSON.parse(await browser.evaluate(`JSON.stringify({ dialogOpen: document.getElementById('logsDialog').open, scope: document.getElementById('logsScope').value, status: document.getElementById('logsStatus').textContent, entries: document.querySelectorAll('#logsContent .log-entry').length, project: document.getElementById('projectSelect').value, projectOptions: [...document.getElementById('projectSelect').options].map(o => o.value) })`));
208
+ evidence.debugConsoleErrors = browser.consoleErrors;
209
+ }
210
+ if (proxy) evidence.debugProxyLogsRequests = proxy.state.requests.filter(r => r.pathname.startsWith('/api/logs')).map(r => ({ method: r.method, url: r.url, status: r.status, error: r.error || null }));
211
+ } catch {}
212
+ process.exitCode = 1;
213
+ console.error(`LOG-003 FAIL: ${error.message}`);
214
+ } finally {
215
+ evidence.finishedAt = new Date().toISOString();
216
+ try { if (browser) await browser.screenshot(path.join(ARTIFACTS, 'log003-final.png')); } catch {}
217
+ fs.mkdirSync(ARTIFACTS, { recursive: true });
218
+ fs.writeFileSync(path.join(ARTIFACTS, 'log003-evidence.json'), JSON.stringify(evidence, null, 2) + '\n');
219
+ try { await browser?.close(); } finally { try { if (proxy) await proxy.close(); } finally { try { await server.cleanup(); } finally { fixture.cleanup(); } } }
220
+ lane.cleanup();
221
+ }
222
+ }
223
+
224
+ main();
@@ -0,0 +1,231 @@
1
+ 'use strict';
2
+ // P-LOG-ACCEPTANCE-01 LOG-004 查询-订阅交界 acceptance driver (real product page
3
+ // in a real browser against an isolated managed instance).
4
+ //
5
+ // Case (qa-autopilot/config/test-cases.json LOG-004, P1):
6
+ // steps: 系统状态→诊断,在查询与订阅交界插入新记录,检查无丢失无重复;
7
+ // 关闭窗口后流断开。
8
+ // expected: 1) 交界插入的记录在DOM与流中不丢失不重复。
9
+ // 2) 关闭窗口后无残留订阅、流断开。
10
+ //
11
+ // Real behaviour under test: loadLogs (ui/product.js) GETs /api/logs then
12
+ // subscribes the stream channel (live-client.js multiplexes /api/live WS;
13
+ // the live-gateway re-fetches /api/logs/stream SSE with a streamCursor
14
+ // watermark and replays with dedup, so boundary inserts must arrive exactly
15
+ // once). New project records are inserted through the REAL project GOAL.md
16
+ // update API (PUT /api/projects/<id>/goal → logger.info goal.updated with
17
+ // context.bytes; a unique byte size identifies each insert).
18
+ //
19
+ // Lane finding (documented separately, server-app.js is read-only for this
20
+ // lane): POST /api/hooks/post-commit with a semantically invalid event makes
21
+ // the background task reject; the route drops taskForProject's promise, the
22
+ // unhandledRejection process handler then terminates the whole server
23
+ // (reproduced in parallel-logging-acceptance-log004-probe.js).
24
+
25
+ const fs = require('node:fs');
26
+ const path = require('node:path');
27
+ const crypto = require('node:crypto');
28
+
29
+ // Portable isolation (P-QA-LOG-INTEGRATION-5025): the temp root comes from
30
+ // PK_TEST_TEMP_ROOT or os.tmpdir() and must live outside the source tree;
31
+ // HOME/APPDATA/LOCALAPPDATA/TEMP/CODEX_HOME/CLAUDE_CONFIG_DIR and the dual
32
+ // product data roots are redirected before any other module is loaded.
33
+ const { installLoggingTestEnv, resolveEvidenceDir } = require('./helpers/parallel-logging-integration-5025-env');
34
+ const lane = installLoggingTestEnv({ caseId: 'LOG-004', prefix: 'log004' });
35
+ const laneTmp = lane.laneTmp;
36
+
37
+ const { spawnServer } = require('./helpers/spawn-server');
38
+ const { findChrome } = require('./helpers/find-chrome');
39
+ const { launchCdpBrowser, waitFor } = require('./helpers/cdp-browser');
40
+ const freePort = require('./helpers/free-port');
41
+ const { createLoggingAcceptanceFixture, writeProjectLogs } = require('./helpers/parallel-logging-acceptance-fixture');
42
+ const { createObservationProxy } = require('./helpers/parallel-logging-acceptance-proxy');
43
+ // The logger names its day files by LOCAL day; the disk assertions must use
44
+ // the same clock, not the UTC day (a UTC+8 machine runs 00:00-08:00 on the
45
+ // previous UTC date).
46
+ const { localDay } = require('../lib/structured-logger');
47
+
48
+ const ROOT = path.resolve(__dirname, '../..');
49
+ const ARTIFACTS = resolveEvidenceDir(path.resolve(ROOT, 'qa-autopilot/artifacts/parallel-20261023/logging-acceptance-r1/evidence/log004'));
50
+
51
+ const PROJECT_A = 'project-log004-alpha';
52
+ const run = crypto.randomBytes(6).toString('hex');
53
+ const GOAL_EVENT = 'goal.updated';
54
+ const GOAL_BYTES = { insert1: 1500, insert2: 1501, insert3: 1502 };
55
+ const goalContent = target => `LOG004-LIVE ${run} ${target}`.padEnd(target, '.');
56
+
57
+ async function main() {
58
+ fs.mkdirSync(ARTIFACTS, { recursive: true });
59
+ const evidence = { caseId: 'LOG-004', startedAt: new Date().toISOString(), inserts: GOAL_BYTES, phases: {} };
60
+
61
+ const fixture = await createLoggingAcceptanceFixture({
62
+ caseId: 'log004',
63
+ projects: [{ projectId: PROJECT_A, displayName: 'LOG004甲项目' }],
64
+ });
65
+ await writeProjectLogs(fixture.logger, PROJECT_A, [
66
+ { level: 'info', event: 'log004.seed.1', message: `LOG004 seed 1 ${run}`, operationId: `op-log004-seed-1-${run}` },
67
+ { level: 'warn', event: 'log004.seed.2', message: `LOG004 seed 2 ${run}`, operationId: `op-log004-seed-2-${run}` },
68
+ { level: 'info', event: 'log004.seed.3', message: `LOG004 seed 3 ${run}`, operationId: `op-log004-seed-3-${run}` },
69
+ ]);
70
+ await fixture.logger.close();
71
+
72
+ const port = await freePort();
73
+ const proxyPort = await freePort();
74
+ const base = `http://127.0.0.1:${port}`;
75
+ const server = spawnServer({ root: ROOT, port, dataDir: fixture.dataDir, tag: 'log004', stdio: 'ignore' });
76
+ const putGoal = async target => {
77
+ const content = goalContent(target);
78
+ const res = await fetch(`${base}/api/projects/${PROJECT_A}/goal`, { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ content }) });
79
+ return { status: res.status, bytes: Buffer.byteLength(content, 'utf8') };
80
+ };
81
+ let browser;
82
+ let proxy;
83
+ try {
84
+ await waitFor(async () => { try { return (await fetch(`${base}/api/health`)).ok; } catch { return false; } }, 'isolated web shell', 20000);
85
+ proxy = await createObservationProxy({ targetPort: port, port: proxyPort });
86
+ evidence.server = { pid: server.child.pid, port, proxyPort, dataDir: fixture.dataDir };
87
+
88
+ browser = await launchCdpBrowser({ chrome: findChrome(), profileDir: path.join(laneTmp, 'browser'), url: proxy.url, width: 1440, height: 900 });
89
+ const runExpr = expression => browser.evaluate(expression);
90
+ const click = selector => runExpr(`document.querySelector(${JSON.stringify(selector)}).click()`);
91
+ const readDom = () => runExpr(`JSON.stringify({ count: document.querySelectorAll('#logsContent .log-entry').length, records: [...document.querySelectorAll('#logsContent .log-detail')].map(node => JSON.parse(node.textContent)), liveStats: window.KnowledgeLive ? KnowledgeLive.stats() : null })`);
92
+ evidence.browser = await runExpr('navigator.userAgent');
93
+
94
+ await waitFor(() => runExpr(`document.getElementById('projectSelect') && document.getElementById('projectSelect').length >= 1`), 'isolated project registered', 20000);
95
+ await runExpr(`document.getElementById('projectSelect').value = ${JSON.stringify(PROJECT_A)}; document.getElementById('projectSelect').dispatchEvent(new Event('change'))`);
96
+ await waitFor(() => runExpr(`document.getElementById('projectSelect').value === ${JSON.stringify(PROJECT_A)}`), 'project A selected');
97
+
98
+ // 系统状态 → 诊断, project scope, empty search → snapshot + live stream.
99
+ await click('#openSystem');
100
+ await runExpr(`document.getElementById('logsScope').value = 'project'`);
101
+ await click('#openLogs');
102
+ await waitFor(async () => {
103
+ const dom = JSON.parse(await readDom());
104
+ return dom.count >= 3 && dom.records.length === dom.count;
105
+ }, 'project A diagnostics rendered', 20000);
106
+
107
+ // Boundary insert #1: fire the real GOAL update while the query→subscribe
108
+ // hand-off is in progress.
109
+ const put1 = await putGoal(GOAL_BYTES.insert1);
110
+ await waitFor(async () => {
111
+ const dom = JSON.parse(await readDom());
112
+ return dom.records.filter(record => record.event === GOAL_EVENT && record.context && record.context.bytes === GOAL_BYTES.insert1).length === 1;
113
+ }, 'boundary insert #1 (during query→subscribe hand-off) appears in the DOM exactly once', 20000);
114
+
115
+ // Insert #2 after the stream has settled: steady-state live append.
116
+ await new Promise(resolve => setTimeout(resolve, 600));
117
+ const put2 = await putGoal(GOAL_BYTES.insert2);
118
+ await waitFor(async () => {
119
+ const dom = JSON.parse(await readDom());
120
+ return dom.records.filter(record => record.event === GOAL_EVENT && record.context && record.context.bytes === GOAL_BYTES.insert2).length === 1;
121
+ }, 'steady-state insert #2 appears in the DOM exactly once', 20000);
122
+
123
+ const domLive = JSON.parse(await readDom());
124
+ const domIds = domLive.records.map(record => record.id);
125
+ const uniqueDomIds = new Set(domIds).size === domIds.length;
126
+ evidence.phases.boundaryNoLossNoDup = {
127
+ put1, put2,
128
+ insert1InDom: domLive.records.filter(record => record.event === GOAL_EVENT && record.context && record.context.bytes === GOAL_BYTES.insert1).length,
129
+ insert2InDom: domLive.records.filter(record => record.event === GOAL_EVENT && record.context && record.context.bytes === GOAL_BYTES.insert2).length,
130
+ domEntryCount: domLive.count,
131
+ domIdsUnique: uniqueDomIds,
132
+ liveStats: domLive.liveStats,
133
+ };
134
+ if (evidence.phases.boundaryNoLossNoDup.insert1InDom !== 1 || evidence.phases.boundaryNoLossNoDup.insert2InDom !== 1) throw new Error('boundary insert was lost or duplicated in the DOM');
135
+ if (!uniqueDomIds) throw new Error('DOM rendered duplicate record ids');
136
+
137
+ // API parity for the same filters the UI holds.
138
+ const getAll = await fetch(`${base}/api/logs?pageSize=100&q=&projectId=${PROJECT_A}`).then(r => r.json());
139
+ const apiInsert1 = getAll.entries.filter(entry => entry.event === GOAL_EVENT && entry.context && entry.context.bytes === GOAL_BYTES.insert1);
140
+ const apiInsert2 = getAll.entries.filter(entry => entry.event === GOAL_EVENT && entry.context && entry.context.bytes === GOAL_BYTES.insert2);
141
+ evidence.phases.apiParity = {
142
+ apiInsert1Count: apiInsert1.length,
143
+ apiInsert2Count: apiInsert2.length,
144
+ apiInsert1Ids: apiInsert1.map(entry => entry.id),
145
+ apiInsert2Ids: apiInsert2.map(entry => entry.id),
146
+ };
147
+ if (apiInsert1.length !== 1 || apiInsert2.length !== 1) throw new Error('API query path lost or duplicated a boundary record');
148
+ await browser.screenshot(path.join(ARTIFACTS, 'log004-live-appends.png'));
149
+
150
+ // expected2: close the dialog → the logs channel must unsubscribe.
151
+ const statsBefore = JSON.parse(await readDom()).liveStats;
152
+ await click('#logsDialog [data-close="logsDialog"]');
153
+ await waitFor(() => runExpr(`document.getElementById('logsDialog').open === false`), 'dialog closed');
154
+ await new Promise(resolve => setTimeout(resolve, 800));
155
+ const statsAfter = JSON.parse(await readDom()).liveStats;
156
+
157
+ // A record inserted after the close must NOT stream into the closed dialog.
158
+ const put3 = await putGoal(GOAL_BYTES.insert3);
159
+ await new Promise(resolve => setTimeout(resolve, 1600));
160
+ const domAfterClose = JSON.parse(await readDom());
161
+ const insert3InClosedDom = domAfterClose.records.some(record => record.event === GOAL_EVENT && record.context && record.context.bytes === GOAL_BYTES.insert3);
162
+ evidence.phases.streamDisconnect = {
163
+ statsBefore,
164
+ statsAfter,
165
+ subscriptionsDroppedByOne: statsAfter && statsBefore && statsAfter.subscriptions === statsBefore.subscriptions - 1,
166
+ put3,
167
+ entriesWhileClosed: domAfterClose.count,
168
+ insert3StreamedIntoClosedDialog: insert3InClosedDom,
169
+ };
170
+ if (!evidence.phases.streamDisconnect.subscriptionsDroppedByOne) throw new Error(`logs stream subscription survived the dialog close: ${JSON.stringify({ statsBefore, statsAfter })}`);
171
+ if (insert3InClosedDom) throw new Error('closed dialog still received streamed records');
172
+ await browser.screenshot(path.join(ARTIFACTS, 'log004-closed-dialog.png'));
173
+
174
+ // Reopen: durable catch-up shows the record inserted while closed.
175
+ await click('#openLogs');
176
+ await waitFor(async () => {
177
+ const dom = JSON.parse(await readDom());
178
+ return dom.records.filter(record => record.event === GOAL_EVENT && record.context && record.context.bytes === GOAL_BYTES.insert3).length === 1;
179
+ }, 'reopen catches up the record inserted while the dialog was closed', 20000);
180
+ const domReopen = JSON.parse(await readDom());
181
+ const reopenIds = domReopen.records.map(record => record.id);
182
+ evidence.phases.reopenCatchUp = {
183
+ insert3InDom: domReopen.records.filter(record => record.event === GOAL_EVENT && record.context && record.context.bytes === GOAL_BYTES.insert3).length,
184
+ domEntryCount: domReopen.count,
185
+ domIdsUnique: new Set(reopenIds).size === reopenIds.length,
186
+ };
187
+ if (evidence.phases.reopenCatchUp.insert3InDom !== 1) throw new Error('reopen lost the record inserted while closed');
188
+ if (!evidence.phases.reopenCatchUp.domIdsUnique) throw new Error('reopen rendered duplicate record ids');
189
+ await browser.screenshot(path.join(ARTIFACTS, 'log004-reopen-catchup.png'));
190
+
191
+ // Disk truth: each inserted record exists exactly once in the project file.
192
+ const day = localDay();
193
+ const projectFile = path.join(fixture.dataDir, 'logs', 'projects', PROJECT_A, `${day}.jsonl`);
194
+ const diskRecords = fs.readFileSync(projectFile, 'utf8').trim().split('\n').filter(Boolean).map(line => JSON.parse(line));
195
+ const diskIds = diskRecords.map(record => record.id);
196
+ const diskCount = key => diskRecords.filter(record => record.event === GOAL_EVENT && record.context && record.context.bytes === GOAL_BYTES[key]).length;
197
+ evidence.phases.disk = {
198
+ insert1OnDisk: diskCount('insert1'),
199
+ insert2OnDisk: diskCount('insert2'),
200
+ insert3OnDisk: diskCount('insert3'),
201
+ diskIdsUnique: new Set(diskIds).size === diskIds.length,
202
+ };
203
+ if (evidence.phases.disk.insert1OnDisk !== 1 || evidence.phases.disk.insert2OnDisk !== 1 || evidence.phases.disk.insert3OnDisk !== 1) throw new Error('disk lost or duplicated a boundary record');
204
+ if (!evidence.phases.disk.diskIdsUnique) throw new Error('disk contains duplicate record ids');
205
+
206
+ if (browser.exceptions.length) throw new Error(`browser exceptions: ${JSON.stringify(browser.exceptions)}`);
207
+ evidence.verdict = { 'expected1 交界插入不丢失不重复': 'PASS', 'expected2 关闭窗口后流断开': 'PASS' };
208
+ console.log('LOG-004 PASS');
209
+ process.exitCode = 0;
210
+ } catch (error) {
211
+ evidence.verdict = { failed: String((error && error.stack) || error) };
212
+ try {
213
+ if (browser) {
214
+ evidence.debugDom = JSON.parse(await browser.evaluate(`JSON.stringify({ dialogOpen: document.getElementById('logsDialog').open, scope: document.getElementById('logsScope').value, entries: document.querySelectorAll('#logsContent .log-entry').length, liveStats: window.KnowledgeLive ? KnowledgeLive.stats() : null })`));
215
+ evidence.debugConsoleErrors = browser.consoleErrors;
216
+ }
217
+ if (proxy) evidence.debugProxy = proxy.state.snapshot();
218
+ } catch {}
219
+ process.exitCode = 1;
220
+ console.error(`LOG-004 FAIL: ${error.message}`);
221
+ } finally {
222
+ evidence.finishedAt = new Date().toISOString();
223
+ try { if (browser) await browser.screenshot(path.join(ARTIFACTS, 'log004-final.png')); } catch {}
224
+ fs.mkdirSync(ARTIFACTS, { recursive: true });
225
+ fs.writeFileSync(path.join(ARTIFACTS, 'log004-evidence.json'), JSON.stringify(evidence, null, 2) + '\n');
226
+ try { await browser?.close(); } finally { try { if (proxy) await proxy.close(); } finally { try { await server.cleanup(); } finally { fixture.cleanup(); } } }
227
+ lane.cleanup();
228
+ }
229
+ }
230
+
231
+ main();