@sanqianx/project-knowledge 5.0.24 → 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 -1353
  4. package/LICENSE +201 -201
  5. package/NOTICE +5 -5
  6. package/README.md +349 -349
  7. package/_modules/knowledge-engine/package.json +1 -1
  8. package/_modules/knowledge-engine/src/coordinator.js +48 -3
  9. package/_modules/knowledge-engine/src/runtime/automation-config.js +140 -140
  10. package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +238 -238
  11. package/_modules/knowledge-engine/src/runtime/commit-processing-ledger.js +26 -26
  12. package/_modules/knowledge-engine/src/runtime/commit-prompt.js +193 -193
  13. package/_modules/knowledge-engine/src/runtime/commit-reconciler.js +394 -394
  14. package/_modules/knowledge-engine/src/runtime/contracts.js +282 -282
  15. package/_modules/knowledge-engine/src/runtime/conversation-store.js +389 -389
  16. package/_modules/knowledge-engine/src/runtime/evidence-bundle.js +228 -228
  17. package/_modules/knowledge-engine/src/runtime/git-runner.js +62 -62
  18. package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +505 -505
  19. package/_modules/knowledge-engine/src/runtime/knowledge-retrieval-service.js +400 -400
  20. package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +171 -171
  21. package/_modules/knowledge-engine/src/runtime/scanner.js +298 -298
  22. package/_modules/knowledge-engine/ui/app.js +23 -2
  23. package/_site/README.md +30 -30
  24. package/_site/_test/agent-capture-mcp-ui-test.js +68 -68
  25. package/_site/_test/agent-capture-ui-test.js +101 -101
  26. package/_site/_test/ai-profile-resolver-test.js +137 -137
  27. package/_site/_test/ai-profile-test.js +75 -75
  28. package/_site/_test/ai-vendor-presets-test.js +91 -91
  29. package/_site/_test/automatic-context-test.js +70 -70
  30. package/_site/_test/automation-queue-test.js +14 -14
  31. package/_site/_test/automation-ui-test.js +40 -40
  32. package/_site/_test/background-task-registry-test.js +43 -43
  33. package/_site/_test/baseline-schema-test.js +91 -91
  34. package/_site/_test/bin-cli-test.js +230 -230
  35. package/_site/_test/bridge-adapter-test.js +95 -95
  36. package/_site/_test/bridge-consumer-service-test.js +310 -310
  37. package/_site/_test/candidate-bin-content-test.js +35 -35
  38. package/_site/_test/candidate-product-assets-test.js +13 -13
  39. package/_site/_test/candidate-release-contract-test.js +14 -14
  40. package/_site/_test/capture-hook-dedup-test.js +158 -158
  41. package/_site/_test/cdp-browser-failure-test.js +103 -103
  42. package/_site/_test/chat-claudecodeui-match-test.js +92 -92
  43. package/_site/_test/claude-executable-discovery-test.js +90 -90
  44. package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
  45. package/_site/_test/claude-workbench-test.js +174 -174
  46. package/_site/_test/cli-instance-management-test.js +62 -62
  47. package/_site/_test/client-capture-restart-e2e-test.js +189 -189
  48. package/_site/_test/codex-conversation-projection-test.js +60 -60
  49. package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
  50. package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
  51. package/_site/_test/codex-notify-fanout-test.js +141 -134
  52. package/_site/_test/codex-notify-upgrade-test.js +253 -253
  53. package/_site/_test/commit-boundary-freeze-test.js +147 -147
  54. package/_site/_test/commit-conversation-binding-test.js +99 -99
  55. package/_site/_test/commit-evidence-test.js +110 -110
  56. package/_site/_test/configuration-generation-test.js +22 -22
  57. package/_site/_test/context-pack-test.js +200 -200
  58. package/_site/_test/conversation-api-test.js +119 -119
  59. package/_site/_test/conversation-origin-test.js +15 -15
  60. package/_site/_test/conversation-store-test.js +138 -138
  61. package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
  62. package/_site/_test/desktop-browser-compat-test.js +28 -28
  63. package/_site/_test/desktop-hook-runtime-regression-test.js +204 -204
  64. package/_site/_test/embedding-config-test.js +32 -32
  65. package/_site/_test/explicit-commit-processor-test.js +37 -37
  66. package/_site/_test/fixtures/fake-browser-exit.js +10 -10
  67. package/_site/_test/fixtures/fake-browser-hang.js +10 -10
  68. package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
  69. package/_site/_test/fixtures/make-git-repos.js +92 -92
  70. package/_site/_test/fixtures/retrieval-gold-queries.json +32 -32
  71. package/_site/_test/folder-picker-output-test.js +19 -19
  72. package/_site/_test/fresh-hook-reimport-test.js +53 -53
  73. package/_site/_test/fresh-product-data-test.js +56 -56
  74. package/_site/_test/full-integration-e2e-test.js +167 -167
  75. package/_site/_test/git-path-compatibility-test.js +80 -80
  76. package/_site/_test/git-validation-test.js +81 -81
  77. package/_site/_test/glm-ui-refresh-review-test.js +313 -404
  78. package/_site/_test/harness-isolation-test.js +138 -0
  79. package/_site/_test/helpers/cdp-browser.js +383 -0
  80. package/_site/_test/helpers/cdp-network-capture.js +66 -0
  81. package/_site/_test/helpers/find-chrome.js +34 -0
  82. package/_site/_test/helpers/free-port.js +8 -0
  83. package/_site/_test/helpers/logging-ui-fixture.js +131 -0
  84. package/_site/_test/helpers/module-fixture.js +282 -0
  85. package/_site/_test/helpers/parallel-engine-advanced-engine-child.js +45 -0
  86. package/_site/_test/helpers/parallel-engine-advanced-fault-writer.js +171 -0
  87. package/_site/_test/helpers/parallel-engine-advanced-harness.js +245 -0
  88. package/_site/_test/helpers/parallel-engine-advanced-standin-analyzer.js +80 -0
  89. package/_site/_test/helpers/parallel-engine-advanced-ui-host.js +177 -0
  90. package/_site/_test/helpers/parallel-engine-final-harness.js +314 -0
  91. package/_site/_test/helpers/parallel-engine-final-standin-analyzer.js +159 -0
  92. package/_site/_test/helpers/parallel-knowledge-fixture.js +255 -0
  93. package/_site/_test/helpers/parallel-logging-acceptance-fixture.js +111 -0
  94. package/_site/_test/helpers/parallel-logging-acceptance-proxy.js +106 -0
  95. package/_site/_test/helpers/parallel-logging-fix-r2-proxy.js +205 -0
  96. package/_site/_test/helpers/parallel-logging-integration-5025-env.js +432 -0
  97. package/_site/_test/helpers/parallel-memory-analyzer.js +81 -0
  98. package/_site/_test/helpers/parallel-memory-event-bridge.js +128 -0
  99. package/_site/_test/helpers/parallel-memory-shell.js +470 -0
  100. package/_site/_test/helpers/parallel-nav-isolation-env.js +389 -0
  101. package/_site/_test/helpers/parallel-nav-module-ui.js +186 -0
  102. package/_site/_test/helpers/parallel-nav-proxy.js +148 -0
  103. package/_site/_test/helpers/parallel-nav-shell.js +361 -0
  104. package/_site/_test/helpers/parallel-qa-engine-endpoint-guard.js +206 -0
  105. package/_site/_test/helpers/parallel-qa-engine-endpoint-isolate.js +257 -0
  106. package/_site/_test/helpers/parallel-retrieval-followup-corpus.js +293 -0
  107. package/_site/_test/helpers/parallel-retrieval-followup-next-session.js +70 -0
  108. package/_site/_test/helpers/parallel-retrieval-followup-r2-shell.js +737 -0
  109. package/_site/_test/helpers/parallel-retrieval-followup-r2-wb-host.js +82 -0
  110. package/_site/_test/helpers/parallel-retrieval-followup-shell.js +279 -0
  111. package/_site/_test/helpers/parallel-workbench-fixture.js +371 -0
  112. package/_site/_test/helpers/parallel-workbench-host.js +80 -0
  113. package/_site/_test/helpers/parallel-workbench-integration-5025-fixture.js +408 -0
  114. package/_site/_test/helpers/parallel-workbench-integration-5025-harness.js +593 -0
  115. package/_site/_test/helpers/parallel-workbench-integration-5025-host.js +70 -0
  116. package/_site/_test/helpers/parallel-workbench-integration-5025-launcher.js +240 -0
  117. package/_site/_test/helpers/parallel-workbench-integration-5025-runtime-harness.js +630 -0
  118. package/_site/_test/helpers/parallel-workbench-source-map.js +196 -0
  119. package/_site/_test/helpers/spawn-server.js +106 -0
  120. package/_site/_test/helpers/version-visibility-fixture.js +251 -0
  121. package/_site/_test/hook-runtime-endpoint-test.js +88 -88
  122. package/_site/_test/hook-status-repair-api-test.js +152 -152
  123. package/_site/_test/hook-trigger-test.js +85 -85
  124. package/_site/_test/hook-worktree-test.js +42 -42
  125. package/_site/_test/import-preflight-api-test.js +234 -234
  126. package/_site/_test/index-builder-test.js +41 -41
  127. package/_site/_test/index-error-display-contract-test.js +10 -10
  128. package/_site/_test/index-writer-concurrency-test.js +135 -135
  129. package/_site/_test/integration-adapters-test.js +262 -262
  130. package/_site/_test/integration-auto-repair-test.js +113 -113
  131. package/_site/_test/integration-capture-setup-test.js +118 -118
  132. package/_site/_test/integration-intent-test.js +81 -81
  133. package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
  134. package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
  135. package/_site/_test/integration-runtime-binding-test.js +473 -473
  136. package/_site/_test/integration-status-scan-test.js +29 -29
  137. package/_site/_test/integration-surface-coverage-test.js +164 -164
  138. package/_site/_test/kb-framework-test.js +57 -57
  139. package/_site/_test/kbpath-follow-test.js +63 -63
  140. package/_site/_test/knowledge-db-test.js +144 -144
  141. package/_site/_test/knowledge-engine-client-test.js +120 -109
  142. package/_site/_test/knowledge-language-control-test.js +170 -170
  143. package/_site/_test/knowledge-maintenance-test.js +40 -40
  144. package/_site/_test/knowledge-promotion-recovery-test.js +260 -260
  145. package/_site/_test/knowledge-query-test.js +54 -54
  146. package/_site/_test/knowledge-retrieval-service-test.js +77 -77
  147. package/_site/_test/knowledge-scopes-test.js +37 -37
  148. package/_site/_test/knowledge-storage-location-test.js +28 -28
  149. package/_site/_test/knowledge-storage-startup-test.js +52 -52
  150. package/_site/_test/knowledge-store-logs-supervision-test.js +89 -89
  151. package/_site/_test/launcher-readiness-test.js +80 -80
  152. package/_site/_test/legacy-claude-session-alias-test.js +106 -106
  153. package/_site/_test/legacy-routes-removed-test.js +47 -47
  154. package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
  155. package/_site/_test/live-gateway-test.js +48 -48
  156. package/_site/_test/local-marketplace-test.js +16 -16
  157. package/_site/_test/log-redaction-test.js +58 -58
  158. package/_site/_test/logging-api-test.js +89 -89
  159. package/_site/_test/logging-sse-no-gap-test.js +88 -88
  160. package/_site/_test/logging-ui-test.js +107 -107
  161. package/_site/_test/markdown-delta-overlay-test.js +85 -85
  162. package/_site/_test/markdown-maintenance-api-test.js +75 -75
  163. package/_site/_test/markdown-maintenance-test.js +11 -11
  164. package/_site/_test/mcp-server-test.js +149 -149
  165. package/_site/_test/model-context-windows-test.js +89 -89
  166. package/_site/_test/module-agent-start-proxy-test.js +65 -65
  167. package/_site/_test/module-artifact-integrity-test.js +27 -27
  168. package/_site/_test/module-boundary-test.js +30 -30
  169. package/_site/_test/module-bridge-eventbridge-test.js +202 -202
  170. package/_site/_test/module-dynamic-port-test.js +118 -118
  171. package/_site/_test/module-index-proxy-test.js +33 -33
  172. package/_site/_test/module-model-configuration-test.js +44 -44
  173. package/_site/_test/module-process-lifecycle-test.js +35 -35
  174. package/_site/_test/module-stream-proxy-test.js +34 -34
  175. package/_site/_test/module-terminal-restart-test.js +63 -63
  176. package/_site/_test/module-wiring-unbind-race-test.js +59 -59
  177. package/_site/_test/native-context-integrations-test.js +86 -86
  178. package/_site/_test/non-release-ci-test.js +34 -34
  179. package/_site/_test/offline-boundary-isolation-test.js +32 -32
  180. package/_site/_test/orphaned-claim-recovery-test.js +80 -80
  181. package/_site/_test/package-boundary-test.js +43 -43
  182. package/_site/_test/package-startup-test.js +56 -56
  183. package/_site/_test/packaged-ui-smoke-test.js +88 -88
  184. package/_site/_test/parallel-ci-r9-posix-socket-budget-test.js +156 -0
  185. package/_site/_test/parallel-engine-advanced-eng005-test.js +136 -0
  186. package/_site/_test/parallel-engine-advanced-eng006-test.js +171 -0
  187. package/_site/_test/parallel-engine-advanced-eng007-reconcile-test.js +231 -0
  188. package/_site/_test/parallel-engine-advanced-eng009-test.js +208 -0
  189. package/_site/_test/parallel-engine-advanced-eng010-test.js +213 -0
  190. package/_site/_test/parallel-engine-advanced-eng011-ui-probe.js +459 -0
  191. package/_site/_test/parallel-engine-final-eng002-test.js +255 -0
  192. package/_site/_test/parallel-engine-final-eng007-test.js +269 -0
  193. package/_site/_test/parallel-engine-final-eng011-test.js +197 -0
  194. package/_site/_test/parallel-engine-final-eng012-test.js +354 -0
  195. package/_site/_test/parallel-knowledge-client-error-code-test.js +184 -0
  196. package/_site/_test/parallel-knowledge-engine-test.js +353 -0
  197. package/_site/_test/parallel-knowledge-retrieval-test.js +292 -0
  198. package/_site/_test/parallel-knowledge-vector-test.js +168 -0
  199. package/_site/_test/parallel-linux-ci-r7-log007-day-test.js +110 -0
  200. package/_site/_test/parallel-logging-acceptance-log003-test.js +224 -0
  201. package/_site/_test/parallel-logging-acceptance-log004-test.js +231 -0
  202. package/_site/_test/parallel-logging-acceptance-log005-test.js +208 -0
  203. package/_site/_test/parallel-logging-acceptance-log006-test.js +214 -0
  204. package/_site/_test/parallel-logging-acceptance-log007-test.js +223 -0
  205. package/_site/_test/parallel-logging-acceptance-log008-test.js +180 -0
  206. package/_site/_test/parallel-logging-fix-r2-crash-probe.js +271 -0
  207. package/_site/_test/parallel-logging-fix-r2-gap-test.js +294 -0
  208. package/_site/_test/parallel-logging-fix-r2-normalhook-test.js +330 -0
  209. package/_site/_test/parallel-logging-integration-5025-portability-test.js +554 -0
  210. package/_site/_test/parallel-memory-candidate-test.js +301 -0
  211. package/_site/_test/parallel-memory-manual-test.js +197 -0
  212. package/_site/_test/parallel-memory-scope-test.js +228 -0
  213. package/_site/_test/parallel-memory-source-test.js +227 -0
  214. package/_site/_test/parallel-memory-sync-test.js +238 -0
  215. package/_site/_test/parallel-memory-view-r8-reload-gate-test.js +123 -0
  216. package/_site/_test/parallel-nav-failure-test.js +119 -0
  217. package/_site/_test/parallel-nav-isolation-test.js +308 -0
  218. package/_site/_test/parallel-nav-isolation-vector-path-test.js +90 -0
  219. package/_site/_test/parallel-nav-offline-test.js +272 -0
  220. package/_site/_test/parallel-nav-operability-test.js +257 -0
  221. package/_site/_test/parallel-nav-switch-test.js +247 -0
  222. package/_site/_test/parallel-nav-theme-test.js +130 -0
  223. package/_site/_test/parallel-nav-viewport-test.js +310 -0
  224. package/_site/_test/parallel-qa-engine-endpoint-guard-test.js +273 -0
  225. package/_site/_test/parallel-retrieval-followup-citation-test.js +219 -0
  226. package/_site/_test/parallel-retrieval-followup-corpus-test.js +287 -0
  227. package/_site/_test/parallel-workbench-independent-host.js +262 -0
  228. package/_site/_test/parallel-workbench-integration-5025-abort-probe.js +340 -0
  229. package/_site/_test/parallel-workbench-integration-5025-drafts-probe.js +588 -0
  230. package/_site/_test/parallel-workbench-integration-5025-env-check.js +317 -0
  231. package/_site/_test/parallel-workbench-integration-5025-model-probe.js +342 -0
  232. package/_site/_test/parallel-workbench-integration-5025-ordering-probe.js +326 -0
  233. package/_site/_test/parallel-workbench-model-probe.js +336 -0
  234. package/_site/_test/parallel-workbench-offline-test.js +169 -0
  235. package/_site/_test/parallel-workbench-provenance-trace.js +134 -0
  236. package/_site/_test/parallel-workbench-source-match.js +206 -0
  237. package/_site/_test/parallel-workbench-standalone-turn.js +349 -0
  238. package/_site/_test/path-consistency-test.js +151 -151
  239. package/_site/_test/pending-sweep-test.js +9 -9
  240. package/_site/_test/post-commit-automation-test.js +87 -87
  241. package/_site/_test/pr-consumer-contract-test.js +55 -55
  242. package/_site/_test/product-brand-contract-test.js +25 -25
  243. package/_site/_test/product-diagnostics-ui-test.js +93 -93
  244. package/_site/_test/product-empty-state-test.js +148 -0
  245. package/_site/_test/product-import-ui-test.js +219 -104
  246. package/_site/_test/product-navigation-test.js +152 -0
  247. package/_site/_test/product-progress-status-test.js +84 -84
  248. package/_site/_test/project-delete-recovery-test.js +64 -64
  249. package/_site/_test/project-domain-create-test.js +91 -0
  250. package/_site/_test/project-domain-scope-retrieval-test.js +214 -0
  251. package/_site/_test/project-git-init-confirm-test.js +115 -0
  252. package/_site/_test/project-goal-editor-test.js +144 -144
  253. package/_site/_test/project-import-duplicate-alias-test.js +134 -0
  254. package/_site/_test/project-import-unicode-restart-test.js +140 -0
  255. package/_site/_test/project-lifecycle-transaction-test.js +106 -106
  256. package/_site/_test/project-module-partial-recovery-test.js +222 -0
  257. package/_site/_test/project-remove-running-guard-test.js +79 -79
  258. package/_site/_test/project-store-test.js +90 -90
  259. package/_site/_test/project-unbind-late-read-ui-test.js +250 -0
  260. package/_site/_test/project-unbind-writer-guard-test.js +231 -0
  261. package/_site/_test/prompt-settings-test.js +115 -115
  262. package/_site/_test/protected-architecture-gate-test.js +122 -122
  263. package/_site/_test/refactor-characterization-test.js +36 -36
  264. package/_site/_test/registry-events-test.js +38 -38
  265. package/_site/_test/release-version-sync-test.js +115 -115
  266. package/_site/_test/requirement-binding-test.js +113 -113
  267. package/_site/_test/requirement-recorder-test.js +173 -173
  268. package/_site/_test/run-all-tests.js +173 -161
  269. package/_site/_test/runtime-endpoint-test.js +64 -64
  270. package/_site/_test/scanner-test.js +83 -83
  271. package/_site/_test/server-security-test.js +96 -96
  272. package/_site/_test/sessions-stream-test.js +93 -93
  273. package/_site/_test/shared-contracts-test.js +58 -58
  274. package/_site/_test/shared-scope-root-binding-test.js +74 -67
  275. package/_site/_test/simple-import-test.js +98 -98
  276. package/_site/_test/startup-analysis-disabled-test.js +16 -16
  277. package/_site/_test/storage-foundation-test.js +78 -78
  278. package/_site/_test/structured-logger-test.js +101 -101
  279. package/_site/_test/t07b-cln-005-coverage-test.js +212 -0
  280. package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
  281. package/_site/_test/team-knowledge-store-test.js +132 -132
  282. package/_site/_test/test-server-cleanup-test.js +35 -35
  283. package/_site/_test/test-server-module-isolation-test.js +37 -37
  284. package/_site/_test/tracking-start-test.js +144 -144
  285. package/_site/_test/vector-storage-root-test.js +75 -75
  286. package/_site/_test/version-visibility-browser-probe.js +249 -0
  287. package/_site/_test/version-visibility-test.js +92 -0
  288. package/_site/_test/workbench-context-receipts-test.js +22 -22
  289. package/_site/_test/workbench-permission-test.js +117 -117
  290. package/_site/_test/zcode-capture-integration-test.js +112 -112
  291. package/_site/lib/ai-profile-resolver.js +78 -78
  292. package/_site/lib/ai-vendor-presets.js +83 -83
  293. package/_site/lib/ai-workspace.js +104 -104
  294. package/_site/lib/atomic-file.js +204 -204
  295. package/_site/lib/automation-config.js +140 -140
  296. package/_site/lib/bridge-adapter.js +260 -260
  297. package/_site/lib/bridge-consumer-service.js +402 -402
  298. package/_site/lib/capture-hook-config.js +108 -108
  299. package/_site/lib/claude-cli-runner.js +1599 -1599
  300. package/_site/lib/cli-instances.js +43 -43
  301. package/_site/lib/codex-context-status.js +17 -17
  302. package/_site/lib/codex-notify-config.js +193 -193
  303. package/_site/lib/commit-conversation-binder.js +247 -247
  304. package/_site/lib/commit-processing-ledger.js +27 -27
  305. package/_site/lib/commit-prompt.js +202 -202
  306. package/_site/lib/commit-reconciler.js +444 -444
  307. package/_site/lib/context-pack-builder.js +299 -299
  308. package/_site/lib/contracts.js +291 -291
  309. package/_site/lib/conversation-exclusions.js +204 -204
  310. package/_site/lib/conversation-query-service.js +161 -161
  311. package/_site/lib/conversation-store.js +387 -387
  312. package/_site/lib/data-dir.js +13 -13
  313. package/_site/lib/embedding-service.js +85 -85
  314. package/_site/lib/engine-connection.js +93 -93
  315. package/_site/lib/engine-knowledge-client.js +76 -72
  316. package/_site/lib/evidence-bundle.js +228 -228
  317. package/_site/lib/folder-picker-output.js +24 -24
  318. package/_site/lib/git-path.js +14 -14
  319. package/_site/lib/git-runner.js +62 -62
  320. package/_site/lib/github-team-store.js +1175 -1175
  321. package/_site/lib/hook-manager.js +203 -203
  322. package/_site/lib/index-builder.js +157 -157
  323. package/_site/lib/index-service.js +173 -173
  324. package/_site/lib/integration-auto-repair.js +77 -77
  325. package/_site/lib/integration-installer.js +1240 -1240
  326. package/_site/lib/integration-intent.js +88 -88
  327. package/_site/lib/integration-status-scan.js +99 -99
  328. package/_site/lib/integration-status-worker.js +14 -14
  329. package/_site/lib/kb-framework.js +191 -191
  330. package/_site/lib/kb-validator.js +124 -124
  331. package/_site/lib/knowledge-db.js +363 -363
  332. package/_site/lib/knowledge-promotion.js +435 -435
  333. package/_site/lib/knowledge-retrieval-service.js +401 -401
  334. package/_site/lib/knowledge-schema.js +121 -121
  335. package/_site/lib/knowledge-tool-runtime.js +366 -366
  336. package/_site/lib/live-gateway.js +78 -78
  337. package/_site/lib/llm-client.js +173 -173
  338. package/_site/lib/markdown-knowledge-indexer.js +230 -230
  339. package/_site/lib/model-context-windows.js +82 -82
  340. package/_site/lib/module-bridge.js +874 -874
  341. package/_site/lib/module-port-resolver.js +57 -57
  342. package/_site/lib/post-commit-automation.js +141 -141
  343. package/_site/lib/product-data.js +35 -35
  344. package/_site/lib/project-lifecycle-service.js +554 -554
  345. package/_site/lib/project-registry-store.js +129 -129
  346. package/_site/lib/project-store.js +268 -268
  347. package/_site/lib/prompt-registry.js +148 -148
  348. package/_site/lib/public-version-info.js +37 -0
  349. package/_site/lib/registry-events.js +16 -16
  350. package/_site/lib/requirement-adapters.js +10 -10
  351. package/_site/lib/requirement-binder.js +112 -112
  352. package/_site/lib/requirement-recorder.js +278 -278
  353. package/_site/lib/runtime-control.js +89 -89
  354. package/_site/lib/runtime-endpoint.js +160 -160
  355. package/_site/lib/scanner.js +298 -298
  356. package/_site/lib/server-app.js +1672 -1664
  357. package/_site/lib/settings-store.js +153 -153
  358. package/_site/lib/storage-layout.js +166 -166
  359. package/_site/lib/structured-logger.js +574 -574
  360. package/_site/lib/zcode-context-integration.js +60 -60
  361. package/_site/scripts/codex-notify-fanout.cjs +95 -95
  362. package/_site/scripts/folder-picker.ps1 +156 -156
  363. package/_site/scripts/hook-trigger.js +158 -158
  364. package/_site/scripts/install-module-candidates.js +69 -69
  365. package/_site/scripts/pack-module-candidates.js +89 -89
  366. package/_site/scripts/sync-product-content.js +28 -28
  367. package/_site/scripts/sync-release-version.js +164 -164
  368. package/_site/scripts/vendor-modules.js +49 -49
  369. package/_site/scripts/verify-module-runtime.js +50 -50
  370. package/_site/scripts/verify-product-runtime.js +36 -36
  371. package/_site/scripts/verify-retrieval-gold.js +83 -83
  372. package/_site/server.js +15 -15
  373. package/_site/start.bat +26 -26
  374. package/_site/stop.bat +11 -11
  375. package/bin/project-knowledge-integrations.js +254 -254
  376. package/bin/project-knowledge-kb.js +67 -67
  377. package/bin/project-knowledge-mcp.js +201 -201
  378. package/bin/project-knowledge.js +269 -170
  379. package/claude-prompts.json +21 -21
  380. package/docs/README.zh-CN.md +207 -205
  381. package/docs/assets/architecture.svg +181 -181
  382. package/docs/assets/logo.svg +49 -49
  383. package/docs/fresh-v5-start.md +44 -44
  384. package/docs/leader-glm-workbench-playbook.md +122 -122
  385. package/docs/project-registry-schema.md +33 -33
  386. package/docs/release-5.0.16-acceptance.md +176 -176
  387. package/docs/release-5.0.17-acceptance.md +117 -117
  388. package/docs/release-5.0.19-acceptance.md +39 -39
  389. package/docs/team-knowledge-mode-a-plan.md +73 -73
  390. package/docs/testing-strategy.md +33 -33
  391. package/module-runtime-manifest.json +66 -63
  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,459 @@
1
+ 'use strict';
2
+
3
+ // P-ENGINE-REVIEW-R3 ENG-011 UI PRODUCT PROBE (explicit probe, not a canonical
4
+ // *-test.js entry).
5
+ //
6
+ // Why this is a probe and not a canonical test entry: it measures the UI bytes
7
+ // that are actually SHIPPED (the pinned/vendored copy in _modules). The
8
+ // knowledge-engine fix for this defect lives in the module SOURCE repo and is
9
+ // NOT vendored yet (no re-vendor, no publish, no pin change is authorized), so
10
+ // the shipped bytes still carry the defect and a default-red canonical entry
11
+ // would misrepresent the pinned package. This probe therefore keeps both
12
+ // truths visible:
13
+ // * default run -> vendored bytes -> RED, with the defect signature
14
+ // * KB_ENG011_UI_DIR=<module source ui> -> candidate bytes -> GREEN
15
+ // The canonical runner (_site/_test/run-all-tests.js) collects only *-test.js,
16
+ // so this probe is never part of the default suite.
17
+ //
18
+ // Case (qa-autopilot/config/test-cases.json ENG-011, P1):
19
+ // steps: UI点击指定run重试,连续双击与网络响应丢失分别测试。
20
+ // expected: 1) 同一意图不会重复派发。
21
+ // 2) 活动状态正确。
22
+ // 3) 请求结果未知先查询。
23
+ //
24
+ // Every acceptance observation comes from the PRODUCT'S OWN behaviour: CDP
25
+ // Network events emitted by the product page after native
26
+ // Input.dispatchMouseEvent clicks, and the product's own DOM (badge, toast,
27
+ // button state). The driver NEVER issues the "correct GET" on the client's
28
+ // behalf; driver-side API calls only prepare the failed-run precondition and
29
+ // read the engine's internal state for cross-checking.
30
+ //
31
+ // Topology (mirrors the product shell wiring): real vendored engine on an
32
+ // ephemeral port <- lane-exclusive ui-host reverse proxy (same-origin /api/*,
33
+ // browser Origin terminated) <- real product UI bytes served from UI_DIR.
34
+ //
35
+ // Engine-side isolation: the engine refuses to start a NEW run for a project
36
+ // whose previous run is still failed (coordinator.pump: "等待前一条失败提交
37
+ // 恢复"). Every scenario therefore gets its OWN project (own knowledge path,
38
+ // own commit) inside the same engine, so one failed run never blocks another
39
+ // scenario's precondition.
40
+ //
41
+ // Fault injection lives in the ui-host proxy only:
42
+ // * dropRetry - the retry really executes upstream, the response is
43
+ // killed mid-flight (truncated body -> BAD_JSON).
44
+ // * retryDelayMs - the retry response is held (real latency).
45
+ // * holdRetryForward - the retry POST is parked unforwarded, so the engine
46
+ // still reports the run as failed and every detail
47
+ // re-render rebuilds a retry button for the SAME
48
+ // in-flight intent (the re-render counter-proof).
49
+ // * rejectRetryStatus - the retry is refused with an explicit error status.
50
+ //
51
+ // Scenarios, in order (so a RED baseline captures the earliest defect first):
52
+ // 1) detail 重绘/切回 — slow response (>2s in flight), real detail rebuild +
53
+ // switch to another run and back, then a second native click: the
54
+ // product must still dispatch exactly ONE retry.
55
+ // 2) 网络响应丢失 - the response is lost: the client must query first.
56
+ // 3) 连续双击 - two native clicks in flight: exactly ONE dispatch.
57
+ // 4) 显式 409 拒绝 - an explicit refusal must release the lock so the user
58
+ // can retry again (no permanent lockout).
59
+ //
60
+ // Browser dependency: requires helpers/cdp-browser (Chrome or Edge).
61
+ // Isolation: own lane temp root (PK_TEST_TEMP_ROOT or the OS temp dir),
62
+ // ephemeral 127.0.0.1 ports, consume:false engine with a dead bridge URL,
63
+ // real vector-hub with deterministic test embeddings (paid embedding API NOT
64
+ // exercised), analyzer = deterministic stand-in (NO real model output).
65
+ // No quota access, no PAYG, no publish/vendor.
66
+
67
+ const assert = require('node:assert/strict');
68
+ const fs = require('node:fs');
69
+ const path = require('node:path');
70
+ const {
71
+ REPO_ROOT, makeLaneDir, buildFixtureRepo, startVector, startEngine, waitUntil, callJson,
72
+ readJsonl, saveEvidence, RUNTIME_IDENTITY,
73
+ } = require('./helpers/parallel-engine-final-harness.js');
74
+ const { createStandinAnalyzer } = require('./helpers/parallel-engine-final-standin-analyzer.js');
75
+ const { startUiHost } = require('./helpers/parallel-engine-advanced-ui-host.js');
76
+ const { launchCdpBrowser } = require('./helpers/cdp-browser.js');
77
+ const { findChrome } = require('./helpers/find-chrome.js');
78
+ const { attachNetworkCapture } = require('./helpers/cdp-network-capture.js');
79
+
80
+ const ARTIFACTS = process.env.KB_ENG011_EVIDENCE_DIR
81
+ || path.resolve(__dirname, '../../qa-autopilot/artifacts/parallel-20261023/engine-review-r3/evidence/eng011ui');
82
+ const UI_DIR = process.env.KB_ENG011_UI_DIR
83
+ || path.join(REPO_ROOT, '_modules', 'knowledge-engine', 'ui');
84
+
85
+ function isRetryPost(entry) {
86
+ return entry.method === 'POST' && /\/api\/runs\/[^/]+\/retry$/.test(new URL(entry.url).pathname);
87
+ }
88
+ function isApi(entry) {
89
+ return new URL(entry.url).pathname.startsWith('/api/');
90
+ }
91
+
92
+ async function nativeClick(browser, selector) {
93
+ const rect = await browser.evaluate(`(() => { const el = document.querySelector(${JSON.stringify(selector)}); if (!el) return null; const r = el.getBoundingClientRect(); return { x: Math.round(r.left + r.width / 2), y: Math.round(r.top + r.height / 2) }; })()`);
94
+ if (!rect || typeof rect.x !== 'number') throw new Error(`element not clickable: ${selector}`);
95
+ await browser.send('Input.dispatchMouseEvent', { type: 'mousePressed', x: rect.x, y: rect.y, button: 'left', clickCount: 1 });
96
+ await browser.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x: rect.x, y: rect.y, button: 'left', clickCount: 1 });
97
+ }
98
+
99
+ function domTools(browser) {
100
+ return {
101
+ badge: () => browser.evaluate(`(() => { const el = document.querySelector('#runDetail #runMeta .badge'); return el ? el.textContent.trim() : null; })()`),
102
+ toastShown: () => browser.evaluate(`(() => { const el = document.querySelector('#toast'); return el ? { text: el.textContent, shown: el.classList.contains('show') } : null; })()`),
103
+ retryDisabled: () => browser.evaluate(`(() => { const b = document.querySelector('#retryRun'); return b ? b.disabled : null; })()`),
104
+ detailRunId: () => browser.evaluate(`(() => { const el = document.querySelector('#runDetailId'); return el ? el.textContent.trim() : null; })()`),
105
+ };
106
+ }
107
+
108
+ async function openRunsView(browser, base) {
109
+ await browser.send('Page.navigate', { url: `${base}/` });
110
+ await waitUntil(() => browser.evaluate("document.readyState === 'complete'"), 15000, 'page load');
111
+ await nativeClick(browser, '.nav-item[data-view="runs"]');
112
+ await waitUntil(() => browser.evaluate('document.querySelectorAll(\'#runsBody tr[data-run]\').length > 0'), 15000, 'runs table rows');
113
+ }
114
+
115
+ // Select a run in the runs table and wait until the DETAIL really shows it
116
+ // (a real re-render of the detail panel, which is where the retry button
117
+ // lives).
118
+ async function selectRun(browser, runId) {
119
+ await nativeClick(browser, `#runsBody tr[data-run="${runId}"]`);
120
+ await waitUntil(async () => (await browser.evaluate(`(() => { const el = document.querySelector('#runDetailId'); return el ? el.textContent.trim() : null; })()`)) === runId, 15000, `detail shows run ${runId}`);
121
+ }
122
+
123
+ async function main() {
124
+ const lane = makeLaneDir('eng011ui-probe');
125
+ const fixture = buildFixtureRepo(lane, 'eng011ui-probe', ['A', 'B', 'C', 'D', 'E']);
126
+ const chrome = findChrome();
127
+ const evidence = {
128
+ caseId: 'ENG-011 UI product probe (real browser native clicks)',
129
+ probe: 'explicit product probe - NOT a canonical *-test.js entry (the vendored/pinned UI bytes still carry the defect until the module fix is vendored, which is NOT authorized here)',
130
+ targetIdentity: {
131
+ shipped: 'knowledge-engine 0.2.7-rc.10 vendored at _modules/knowledge-engine (pinned, unchanged by this lane)',
132
+ candidate: 'knowledge-engine source ui/ (module repo main, uncommitted fix) - reachable only through KB_ENG011_UI_DIR',
133
+ },
134
+ runtimeIdentity: RUNTIME_IDENTITY,
135
+ laneDir: lane,
136
+ uiDir: path.resolve(UI_DIR),
137
+ chrome: path.resolve(chrome),
138
+ fixture: { repoPath: fixture.repoPath, baseline: fixture.baseline, shas: fixture.shas },
139
+ scenarios: {},
140
+ engineStandin: 'deterministic stand-in analyzer (no real model output); vector-hub real with deterministic test embeddings',
141
+ runs: [],
142
+ };
143
+ const persist = label => { evidence.runs.push(label); saveEvidence(ARTIFACTS, 'eng011-ui-probe-evidence.json', evidence); };
144
+
145
+ const projectIds = {
146
+ rerender: 'project-eng011ui-rerender',
147
+ other: 'project-eng011ui-other',
148
+ lost: 'project-eng011ui-lost',
149
+ double: 'project-eng011ui-double',
150
+ refusal: 'project-eng011ui-refusal',
151
+ };
152
+ const knowledgePath = path.join(lane, 'knowledge-ui');
153
+ fs.mkdirSync(knowledgePath, { recursive: true });
154
+ const dataDir = path.join(lane, 'engine-data');
155
+ const vectorRoot = path.join(lane, 'vector-root');
156
+ const eventsFile = path.join(lane, 'analyzer-events.jsonl');
157
+
158
+ const vector = await startVector(vectorRoot);
159
+ const analyzer = createStandinAnalyzer({ eventsFile });
160
+ const engine = await startEngine({ dataDir, bridgeUrl: 'http://127.0.0.1:9', vectorUrl: vector.url, analyzer });
161
+ let browser = null;
162
+ let host = null;
163
+ try {
164
+ const base = await engine.listen();
165
+ for (const [key, projectId] of Object.entries(projectIds)) {
166
+ engine.runtime.store.addProject({
167
+ projectId, name: `eng011ui-${key}`, repoPath: fixture.repoPath,
168
+ knowledgePath: path.join(knowledgePath, key), trackingStartCommit: fixture.baseline,
169
+ });
170
+ }
171
+
172
+ host = startUiHost({ uiDir: UI_DIR, engineBase: base });
173
+ const hostBase = await host.listen();
174
+ evidence.uiHashes = host.uiHashes;
175
+ evidence.engineBase = base;
176
+ evidence.hostBase = hostBase;
177
+
178
+ const runsOf = async projectId => (await callJson(base, `/api/runs?projectId=${projectId}`)).body.runs;
179
+ const waitState = (projectId, runId, state, ms, what) => waitUntil(async () => {
180
+ const run = (await runsOf(projectId)).find(r => r.runId === runId);
181
+ return run?.state === state ? run : null;
182
+ }, ms, what);
183
+ const invocationsFor = sha => readJsonl(eventsFile).filter(e => e.kind === 'start' && e.commitSha === sha).length;
184
+ // Precondition: a real run that fails with the engine's debug simulated
185
+ // fault (retryable) - the UI then renders its "重试失败阶段" button.
186
+ const createFailedRun = async (projectId, sha) => {
187
+ const created = await callJson(base, '/api/runs', { projectId, commitSha: sha, simulateFailure: true });
188
+ assert.equal(created.status, 200, `precondition run creation failed: ${JSON.stringify(created.body)}`);
189
+ const run = created.body.run;
190
+ const failed = await waitState(projectId, run.runId, 'failed', 30000, `precondition: first attempt fails (${projectId}, debug simulated fault, retryable)`);
191
+ assert.equal(failed.error.code, 'DEBUG_SIMULATED_FAILURE');
192
+ assert.equal(failed.error.retryable, true, 'precondition: failed run must expose a retryable error for the UI button');
193
+ return { run, failed };
194
+ };
195
+
196
+ const target = await createFailedRun(projectIds.rerender, fixture.shas.A);
197
+ const other = await createFailedRun(projectIds.other, fixture.shas.B);
198
+ const lost = await createFailedRun(projectIds.lost, fixture.shas.C);
199
+ const twice = await createFailedRun(projectIds.double, fixture.shas.D);
200
+ const refused = await createFailedRun(projectIds.refusal, fixture.shas.E);
201
+ evidence.preconditions = {
202
+ rerender: { projectId: projectIds.rerender, runId: target.run.runId, state: target.failed.state, error: target.failed.error },
203
+ other: { projectId: projectIds.other, runId: other.run.runId, state: other.failed.state, error: other.failed.error },
204
+ lost: { projectId: projectIds.lost, runId: lost.run.runId, state: lost.failed.state, error: lost.failed.error },
205
+ double: { projectId: projectIds.double, runId: twice.run.runId, state: twice.failed.state, error: twice.failed.error },
206
+ refusal: { projectId: projectIds.refusal, runId: refused.run.runId, state: refused.failed.state, error: refused.failed.error },
207
+ };
208
+ persist('preconditions ready');
209
+
210
+ browser = await launchCdpBrowser({
211
+ chrome,
212
+ profileDir: path.join(lane, 'browser-profile'),
213
+ url: `${hostBase}/`,
214
+ });
215
+ const capture = attachNetworkCapture(browser);
216
+ await capture.enable();
217
+ const networkFailures = [];
218
+ browser.socket.on('message', raw => {
219
+ try {
220
+ const message = JSON.parse(String(raw));
221
+ if (message.method === 'Network.loadingFailed') {
222
+ networkFailures.push({ requestId: message.params.requestId, errorText: message.params.errorText, canceled: message.params.canceled });
223
+ }
224
+ } catch {}
225
+ });
226
+ const dom = domTools(browser);
227
+
228
+ // ---------- Scenario 1: detail 重绘/切回后同一在途意图仍只派发一次 ----------
229
+ await openRunsView(browser, hostBase);
230
+ await selectRun(browser, target.run.runId);
231
+ host.configure({ retryDelayMs: 2000, dropRetry: false, rejectRetryStatus: 0, holdRetryForward: true });
232
+ const markRerender = capture.requests.length;
233
+ const receivedBefore = host.state.receivedRetryPosts;
234
+ const clickedAt = Date.now();
235
+ await nativeClick(browser, '#retryRun');
236
+ await waitUntil(() => host.state.receivedRetryPosts > receivedBefore, 15000, 'the retry intent reached the ui-host');
237
+ assert.equal(host.state.forwardedRetryPosts, 0, 'the parked intent has not reached the engine yet');
238
+ await new Promise(resolve => setTimeout(resolve, 2200)); // the intent stays in flight well past the 2s response delay
239
+
240
+ await selectRun(browser, other.run.runId); // switch to another failed run
241
+ const otherRunDisabled = await dom.retryDisabled();
242
+ await selectRun(browser, target.run.runId); // switch back: the detail (and the button) is rebuilt
243
+ const rebuiltDisabled = await dom.retryDisabled();
244
+ const receivedBeforeSecondClick = host.state.receivedRetryPosts;
245
+ await nativeClick(browser, '#retryRun'); // second native click on the REBUILT button
246
+ await new Promise(resolve => setTimeout(resolve, 500));
247
+ const receivedAfterSecondClick = host.state.receivedRetryPosts;
248
+ const inFlightWindowMs = Date.now() - clickedAt;
249
+ const releasedAt = Date.now();
250
+ const productRetryPostsRerender = capture.requests.slice(markRerender).filter(isRetryPost);
251
+ evidence.scenarios.detailRerender = {
252
+ faultInjection: 'ui-host parked the retry POST (holdRetryForward) and answers the released retry with a 2s delayed response; the engine keeps reporting the run as failed so every detail re-render rebuilds a retry button for the SAME in-flight intent',
253
+ nativeClicks: 2,
254
+ inFlightWindowMs,
255
+ rebuiltButtonDisabledDuringFlight: rebuiltDisabled,
256
+ otherRunButtonDisabled: otherRunDisabled,
257
+ hostReceivedRetryPosts: { before: receivedBeforeSecondClick, after: receivedAfterSecondClick },
258
+ hostForwardedRetryPosts: host.state.forwardedRetryPosts,
259
+ productRetryPostCount: productRetryPostsRerender.length,
260
+ productRetryPosts: productRetryPostsRerender.map(r => ({ url: r.url, status: r.status })),
261
+ };
262
+ persist('scenario detailRerender observed');
263
+ assert.equal(receivedAfterSecondClick, receivedBeforeSecondClick, 'ENG-011 UI defect: the rebuilt button re-dispatched the same in-flight retry intent (the guard must be stable per runId, not per DOM element)');
264
+ assert.equal(productRetryPostsRerender.length, 1, 'ENG-011 UI defect: the product dispatched the same retry intent twice across the detail re-render');
265
+ assert.equal(rebuiltDisabled, true, 'ENG-011 UI defect: the rebuilt retry button does not reflect the in-flight state');
266
+ assert.equal(otherRunDisabled, false, 'ENG-011 UI defect: an unrelated run is blocked by the in-flight guard');
267
+ assert.ok(inFlightWindowMs >= 2000, `the in-flight window must exceed the 2s response delay (got ${inFlightWindowMs}ms)`);
268
+
269
+ host.configure({ holdRetryForward: false });
270
+ const released = host.releaseHeldRetries();
271
+ const healed = await waitState(projectIds.rerender, target.run.runId, 'succeeded', 60000, 'the released retry really executed');
272
+ await waitUntil(async () => (await dom.badge()) === '可检索', 30000, 'the badge converges after the delayed response');
273
+ const invocationsA = invocationsFor(fixture.shas.A);
274
+ const runsForA = (await runsOf(projectIds.rerender)).filter(r => r.commitSha === fixture.shas.A);
275
+ evidence.scenarios.detailRerender.releasedHeldRetries = released;
276
+ evidence.scenarios.detailRerender.totalInFlightMs = Date.now() - clickedAt;
277
+ evidence.scenarios.detailRerender.responseDeliveredAfterReleaseMs = Date.now() - releasedAt;
278
+ evidence.scenarios.detailRerender.engineTruth = {
279
+ state: healed.state, phase: healed.phase, indexState: healed.indexState,
280
+ analyzerInvocationsForCommit: invocationsA, runRecordsForCommit: runsForA.length,
281
+ };
282
+ assert.equal(host.state.forwardedRetryPosts, 1, 'exactly one retry reached the engine');
283
+ assert.equal(invocationsA, 1, `the retry dispatched exactly one analysis (got ${invocationsA})`);
284
+ assert.equal(runsForA.length, 1, 'no duplicate run record');
285
+ persist('scenario detailRerender PASS');
286
+
287
+ // ---------- Scenario 2: 网络响应丢失 (retry really executes, browser never sees the answer) ----------
288
+ await selectRun(browser, lost.run.runId);
289
+ const mark1 = capture.requests.length;
290
+ const forwardsBeforeClick = host.state.forwardedRetryPosts;
291
+ host.configure({ retryDelayMs: 0, dropRetry: true, rejectRetryStatus: 0, holdRetryForward: false });
292
+ await nativeClick(browser, '#retryRun');
293
+ await waitUntil(() => host.state.droppedRetryResponses >= 1, 15000, 'ui-host dropped the retry response after the engine executed it');
294
+ await new Promise(resolve => setTimeout(resolve, 1200)); // early sample: the toast auto-hides after 2.6s
295
+ const toastEarly = await dom.toastShown();
296
+ await new Promise(resolve => setTimeout(resolve, 1800)); // rest of the observation window (3s total)
297
+ const toastLate = await dom.toastShown();
298
+ const badgeStale = await dom.badge();
299
+
300
+ const pageAfterDrop = capture.requests.slice(mark1);
301
+ const droppedPost = pageAfterDrop.filter(isRetryPost)[0] || null;
302
+ const droppedPostFailure = droppedPost ? networkFailures.find(f => f.requestId === droppedPost.id) : null;
303
+ const apiAfterPost = pageAfterDrop.filter(entry => isApi(entry) && entry !== droppedPost);
304
+ const specificQuery = apiAfterPost.find(entry => entry.method === 'GET' && new URL(entry.url).pathname === `/api/runs/${lost.run.runId}`);
305
+ const healedLost = await waitState(projectIds.lost, lost.run.runId, 'succeeded', 60000, 'the retry behind the dropped response really executed');
306
+ const invocationsC = invocationsFor(fixture.shas.C);
307
+ const runsForC = (await runsOf(projectIds.lost)).filter(r => r.commitSha === fixture.shas.C);
308
+ const ledgerC = path.join(dataDir, 'runtime', 'processed-commits', projectIds.lost, `${fixture.shas.C}.json`);
309
+ evidence.scenarios.lostResponse = {
310
+ faultInjection: 'ui-host forwarded the retry POST (engine executed it), started the response, then killed the socket mid-flight',
311
+ droppedPostObservedByPage: droppedPost ? { url: droppedPost.url, status: droppedPost.status, loadingFailed: droppedPostFailure ? { errorText: droppedPostFailure.errorText, canceled: droppedPostFailure.canceled === true } : null } : null,
312
+ hostForwardedRetryPostsDuringScenario: host.state.forwardedRetryPosts - forwardsBeforeClick,
313
+ hostDroppedRetryResponses: host.state.droppedRetryResponses,
314
+ productApiRequestsAfterDrop: apiAfterPost.map(r => ({ method: r.method, url: r.url, status: r.status })),
315
+ productSpecificRunQueryFired: !!specificQuery,
316
+ productFirstApiRequestAfterDrop: apiAfterPost[0] ? { method: apiAfterPost[0].method, url: apiAfterPost[0].url } : null,
317
+ toastEarly,
318
+ toastLate,
319
+ domBadgeWhileEngineHadSucceeded: badgeStale,
320
+ engineTruth: {
321
+ stateBehindLostResponse: healedLost.state,
322
+ analyzerInvocationsForCommit: invocationsC,
323
+ runRecordsForCommit: runsForC.length,
324
+ ledgerStatus: fs.existsSync(ledgerC) ? JSON.parse(fs.readFileSync(ledgerC, 'utf8')).status : null,
325
+ },
326
+ browserConsoleErrors: browser.consoleErrors.map(entry => entry.text).slice(-10),
327
+ };
328
+ persist('scenario lostResponse observed');
329
+ assert.ok(droppedPost, 'the page dispatched the retry POST (captured from the browser network layer)');
330
+ assert.ok(droppedPostFailure && droppedPostFailure.canceled !== true, `the retry fetch failed in the browser (unknown result), loadingFailed=${droppedPostFailure ? droppedPostFailure.errorText : 'missing'}`);
331
+ assert.equal(host.state.forwardedRetryPosts - forwardsBeforeClick, 1, 'exactly one retry POST reached the engine in this scenario (no transport-level re-dispatch)');
332
+ assert.ok(specificQuery, 'ENG-011 UI defect: after the lost response the product client never queried the run state (请求结果未知先查询 violated)');
333
+ assert.ok(apiAfterPost.length >= 1 && apiAfterPost[0] === specificQuery, 'ENG-011 UI defect: the first product API call after the unknown result was not the specific run query');
334
+ assert.equal(pageAfterDrop.filter(isRetryPost).length, 1, 'ENG-011 UI defect: the product client re-dispatched the retry after the unknown result');
335
+ assert.ok(toastEarly && toastEarly.shown === true && toastEarly.text, 'ENG-011 UI defect: the product client left the unknown result invisible to the user (no toast)');
336
+ assert.notEqual(badgeStale, '失败', 'ENG-011 UI defect: the UI kept showing 失败 while the engine had already executed the retry');
337
+ await waitUntil(async () => (await dom.badge()) === '可检索', 20000, 'UI badge converges to 可检索 after the query');
338
+ assert.equal(invocationsC, 1, `the retry behind the lost response dispatched exactly one analysis (got ${invocationsC})`);
339
+ assert.equal(runsForC.length, 1, 'no duplicate run record after the unknown-result retry');
340
+ assert.ok(fs.existsSync(ledgerC), 'completed SHA recorded in the processing ledger');
341
+ persist('scenario lostResponse PASS');
342
+
343
+ // ---------- Scenario 3: 连续双击 (two native clicks while the retry response is in flight) ----------
344
+ await selectRun(browser, twice.run.runId);
345
+ const mark2 = capture.requests.length;
346
+ host.configure({ retryDelayMs: 800, dropRetry: false });
347
+ await nativeClick(browser, '#retryRun');
348
+ await new Promise(resolve => setTimeout(resolve, 60));
349
+ const disabledDuringFlight = await dom.retryDisabled();
350
+ await nativeClick(browser, '#retryRun');
351
+ await new Promise(resolve => setTimeout(resolve, 2200)); // delayed responses (800ms) settle + refresh
352
+
353
+ const pageAfterClick1 = capture.requests.slice(mark2);
354
+ const retryPostsDoubleClick = pageAfterClick1.filter(isRetryPost);
355
+ const healedTwice = await waitState(projectIds.double, twice.run.runId, 'succeeded', 60000, 'retried run reaches 可检索');
356
+ const invocationsD = invocationsFor(fixture.shas.D);
357
+ const runsForD = (await runsOf(projectIds.double)).filter(r => r.commitSha === fixture.shas.D);
358
+ const badgeD = await dom.badge();
359
+ evidence.scenarios.doubleClick = {
360
+ faultInjection: 'ui-host holds retry responses 800ms so the second native click lands in-flight',
361
+ nativeClicks: 2,
362
+ productRetryPostCount: retryPostsDoubleClick.length,
363
+ productRetryPosts: retryPostsDoubleClick.map(r => ({ url: r.url, status: r.status })),
364
+ buttonDisabledDuringFlight: disabledDuringFlight,
365
+ engineContainment: {
366
+ analyzerInvocationsForCommit: invocationsD,
367
+ runRecordsForCommit: runsForD.length,
368
+ finalState: healedTwice.state,
369
+ finalPhase: healedTwice.phase,
370
+ indexState: healedTwice.indexState,
371
+ },
372
+ finalDomBadge: badgeD,
373
+ };
374
+ persist('scenario doubleClick observed');
375
+ assert.equal(retryPostsDoubleClick.length, 1, `ENG-011 UI defect: the product page dispatched the same retry intent ${retryPostsDoubleClick.length} times under a double click (expected exactly 1)`);
376
+ assert.equal(disabledDuringFlight, true, 'ENG-011 UI defect: the retry button stayed enabled while the intent was in flight');
377
+ assert.equal(invocationsD, 1, `the accepted retry dispatched exactly one analysis (got ${invocationsD})`);
378
+ assert.equal(runsForD.length, 1, 'no duplicate run record for the same commit');
379
+ assert.equal(healedTwice.state, 'succeeded');
380
+ assert.equal(healedTwice.phase, 'ready');
381
+ assert.equal(healedTwice.indexState, 'succeeded');
382
+ assert.equal(badgeD, '可检索', 'UI run detail badge must reach 可检索 after the retry');
383
+ persist('scenario doubleClick PASS');
384
+
385
+ // ---------- Scenario 4: 显式 409 拒绝后可恢复重试 ----------
386
+ await selectRun(browser, refused.run.runId);
387
+ host.configure({ retryDelayMs: 0, dropRetry: false, rejectRetryStatus: 409 });
388
+ const forwardsBeforeRefusal = host.state.forwardedRetryPosts;
389
+ const mark4 = capture.requests.length;
390
+ await nativeClick(browser, '#retryRun');
391
+ await waitUntil(() => host.state.rejectedRetryResponses >= 1, 15000, 'the explicit refusal reached the product');
392
+ const refusalToastSamples = [];
393
+ for (let index = 0; index < 8; index += 1) {
394
+ refusalToastSamples.push(await dom.toastShown());
395
+ await new Promise(resolve => setTimeout(resolve, 120));
396
+ }
397
+ const refusalToast = refusalToastSamples.find(sample => sample && sample.shown === true) || refusalToastSamples[0];
398
+ const forwardedAfterRefusal = host.state.forwardedRetryPosts;
399
+ await selectRun(browser, refused.run.runId); // the detail is rebuilt after the refusal
400
+ const disabledAfterRefusal = await dom.retryDisabled();
401
+ host.configure({ rejectRetryStatus: 0 });
402
+ await nativeClick(browser, '#retryRun');
403
+ await waitUntil(() => host.state.forwardedRetryPosts > forwardedAfterRefusal, 15000, 'the second intent reached the engine');
404
+ const healedRefused = await waitState(projectIds.refusal, refused.run.runId, 'succeeded', 60000, 'the retried run converges');
405
+ await waitUntil(async () => (await dom.badge()) === '可检索', 30000, 'the badge converges after the recovered retry');
406
+ const invocationsE = invocationsFor(fixture.shas.E);
407
+ const runsForE = (await runsOf(projectIds.refusal)).filter(r => r.commitSha === fixture.shas.E);
408
+ evidence.scenarios.explicitRefusalRecovery = {
409
+ faultInjection: 'ui-host refuses the retry POST with an explicit 409 (the intent never reaches the engine)',
410
+ toastAfterRefusal: refusalToast,
411
+ toastSamplesAfterRefusal: refusalToastSamples,
412
+ productRequestsAfterRefusal: capture.requests.slice(mark4).map(r => ({ method: r.method, url: r.url, status: r.status })),
413
+ browserConsoleErrors: browser.consoleErrors.map(entry => entry.text).slice(-10),
414
+ hostForwardedRetryPostsBeforeRefusal: forwardsBeforeRefusal,
415
+ hostForwardedRetryPostsAfterRefusal: forwardedAfterRefusal,
416
+ rebuiltButtonDisabledAfterRefusal: disabledAfterRefusal,
417
+ hostForwardedRetryPostsAfterRecovery: host.state.forwardedRetryPosts,
418
+ engineTruth: {
419
+ state: healedRefused.state, phase: healedRefused.phase, indexState: healedRefused.indexState,
420
+ analyzerInvocationsForCommit: invocationsE, runRecordsForCommit: runsForE.length,
421
+ },
422
+ finalDomBadge: await dom.badge(),
423
+ };
424
+ persist('scenario explicitRefusalRecovery observed');
425
+ assert.ok(refusalToast && refusalToast.shown === true && /refusal/.test(refusalToast.text), `ENG-011 UI defect: the explicit refusal was not surfaced to the user (toast: ${JSON.stringify(refusalToast)})`);
426
+ assert.equal(forwardedAfterRefusal, forwardsBeforeRefusal, 'the refused intent must not reach the engine');
427
+ assert.equal(disabledAfterRefusal, false, 'ENG-011 UI defect: an explicit API refusal left the retry permanently locked');
428
+ assert.equal(invocationsE, 1, `the recovered retry dispatched exactly one analysis (got ${invocationsE})`);
429
+ assert.equal(runsForE.length, 1, 'no duplicate run record');
430
+ persist('scenario explicitRefusalRecovery PASS');
431
+
432
+ evidence.verdict = {
433
+ 'expected1 同一意图不会重复派发': 'PASS (product client: exactly 1 retry POST under a native double click AND across a real detail re-render/switch-back; the guard is stable per runId, not per DOM element; button disabled in flight; engine: 1 analysis, 1 run record)',
434
+ 'expected2 活动状态正确': 'PASS (engine state machine preparing/analyzing/promoting/ready -> succeeded; UI badge converges to 可检索 in every scenario, including after the unknown result)',
435
+ 'expected3 请求结果未知先查询': 'PASS (after the dropped response the product client queried GET /api/runs/<runId> FIRST and never re-dispatched; user informed via toast)',
436
+ 'explicit refusal recovery': 'PASS (a 409 releases the lock: the rebuilt button is enabled and the next click dispatches again)',
437
+ boundary: 'engine-level guards (per-project single writer, 409 on non-retryable, completed-SHA ledger) are covered by parallel-engine-final-eng011-test.js; this probe covers the product UI client behaviour with a real browser',
438
+ targetIdentity: 'these observations describe the UI bytes served from ' + path.resolve(UI_DIR) + '; the pinned/vendored bytes are unchanged by this lane',
439
+ };
440
+ await browser.close(); browser = null;
441
+ await host.close(); host = null;
442
+ await engine.close();
443
+ await vector.close();
444
+ saveEvidence(ARTIFACTS, 'eng011-ui-probe-evidence.json', evidence);
445
+ console.log('ENG-011 UI PROBE PASS');
446
+ process.exit(0);
447
+ } catch (error) {
448
+ evidence.verdict = { failed: String((error && error.stack) || error) };
449
+ try { if (browser) await browser.close(); } catch {}
450
+ try { if (host) await host.close(); } catch {}
451
+ try { await engine.close(); } catch {}
452
+ try { await vector.close(); } catch {}
453
+ saveEvidence(ARTIFACTS, 'eng011-ui-probe-evidence.json', evidence);
454
+ console.error(`ENG-011 UI PROBE FAIL: ${error.message}`);
455
+ process.exit(1);
456
+ }
457
+ }
458
+
459
+ main();