@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,272 @@
1
+ 'use strict';
2
+ // NAV-008 — 断网恢复。
3
+ //
4
+ // When the browser loses the network the shell must close the old stream, and
5
+ // when the network comes back it must restore the snapshot and open a new
6
+ // stream — exactly one subscription per stream, and no content that pretends
7
+ // to be live while it is not.
8
+ //
9
+ // The regression drives the real product UI in a real browser against an
10
+ // isolated instance (own port, own data dir, own knowledge dir, own Bridge
11
+ // journal, own browser profile, own vendored knowledge engine). The network
12
+ // outage is a real browser outage: CDP Network.emulateNetworkConditions puts
13
+ // the browser offline, so navigator.onLine really turns false, the browser
14
+ // really cannot reach the product, and the real 'offline' and 'online' events
15
+ // really fire.
16
+ //
17
+ // The evidence is taken from three independent places:
18
+ //
19
+ // * the page itself: every WebSocket the shell opened and every
20
+ // subscription message it sent, recorded before the page's own scripts
21
+ // run;
22
+ // * the product proxy: the reads the browser really made;
23
+ // * the engine proxy: the streams the product really opened on the engine.
24
+ //
25
+ // Screenshots are written to PK_TEST_EVIDENCE_DIR when the batch sets it, so
26
+ // this tracked file stays free of machine-specific paths.
27
+
28
+ const path = require('node:path');
29
+ const assert = require('node:assert/strict');
30
+ const { spawnSync } = require('node:child_process');
31
+
32
+ const { startHarness, makeRepo } = require('./helpers/parallel-nav-shell');
33
+
34
+ const EVIDENCE = process.env.PK_TEST_EVIDENCE_DIR ? path.resolve(process.env.PK_TEST_EVIDENCE_DIR) : null;
35
+
36
+ const SNAPSHOT = '/api/engine/snapshot';
37
+
38
+ /**
39
+ * Record every WebSocket the page opens, every subscription message it
40
+ * sends and every close the browser really performed. The hook is installed
41
+ * before the page's own scripts run, so the first stream of the page is
42
+ * recorded too.
43
+ *
44
+ * The close event is the evidence that the browser really closed the old
45
+ * stream: the shell drops its own bookkeeping as soon as it asks the socket
46
+ * to close, while the browser reaches the closed state only when the
47
+ * connection is really gone.
48
+ */
49
+ const SOCKET_HOOK = `(() => {
50
+ window.__sockets = [];
51
+ window.__sent = [];
52
+ window.__closed = [];
53
+ const Native = window.WebSocket;
54
+ const Wrapped = function (...args) {
55
+ const ws = new Native(...args);
56
+ const generation = window.__sockets.length;
57
+ window.__sockets.push(ws);
58
+ ws.addEventListener('close', event => {
59
+ window.__closed.push({ generation, code: event.code, wasClean: event.wasClean, readyState: ws.readyState, at: Date.now() });
60
+ });
61
+ const send = ws.send.bind(ws);
62
+ ws.send = function (data) {
63
+ try {
64
+ const parsed = JSON.parse(data);
65
+ window.__sent.push({ generation, type: parsed.type || '', id: parsed.id || '', route: parsed.route || '' });
66
+ } catch {}
67
+ return send(data);
68
+ };
69
+ return ws;
70
+ };
71
+ Wrapped.prototype = Native.prototype;
72
+ Object.setPrototypeOf(Wrapped, Native);
73
+ window.WebSocket = Wrapped;
74
+ })()`;
75
+
76
+ const socketStates = harness => harness.run('window.__sockets.map(socket => socket.readyState)');
77
+ const sentMessages = harness => harness.run('window.__sent || []');
78
+ const closeEvents = harness => harness.run('window.__closed || []');
79
+ const liveStats = harness => harness.run('window.KnowledgeLive.stats()');
80
+ const connectionLabel = harness => harness.run("document.getElementById('activityConnection').textContent");
81
+ const homeText = harness => harness.run("document.getElementById('homeContent').textContent");
82
+
83
+ /** The subscriptions that are still live on one socket generation. */
84
+ function activeSubscriptions(messages, generation) {
85
+ const active = new Map();
86
+ for (const message of messages) {
87
+ if (message.generation !== generation) continue;
88
+ if (message.type === 'subscribe') active.set(message.id, message.route);
89
+ else if (message.type === 'unsubscribe') active.delete(message.id);
90
+ }
91
+ return active;
92
+ }
93
+
94
+ function gitCommit(repo, fileName, content) {
95
+ const fs = require('node:fs');
96
+ fs.writeFileSync(path.join(repo, fileName), content);
97
+ const run = args => {
98
+ const result = spawnSync('git', ['-C', repo, ...args], { encoding: 'utf8', windowsHide: true });
99
+ assert.equal(result.status, 0, `git ${args.join(' ')} failed: ${result.stderr}`);
100
+ return String(result.stdout || '').trim();
101
+ };
102
+ run(['add', '.']);
103
+ run(['commit', '-qm', `second ${fileName}`]);
104
+ return run(['rev-parse', 'HEAD']);
105
+ }
106
+
107
+ async function main() {
108
+ const harness = await startHarness({ tag: 'nav008', evidenceDir: EVIDENCE || undefined });
109
+ try {
110
+ const repo = makeRepo(harness.fixture, 'nav008-a');
111
+ const project = await harness.importProject(repo, 'NAV008 甲');
112
+ const shortFirst = repo.headCommit.slice(0, 8);
113
+ // Real engine state: the first run of the project.
114
+ harness.engine.store.addRun({ projectId: project.projectId, commitSha: repo.headCommit, trigger: 'manual' });
115
+
116
+ // The page is reloaded with the socket hook in place, so the very first
117
+ // stream of the page is recorded as well.
118
+ await harness.browser.send('Page.addScriptToEvaluateOnNewDocument', { source: SOCKET_HOOK });
119
+ await harness.browser.send('Page.reload', { ignoreCache: true });
120
+ await harness.waitFor(() => harness.run("document.readyState === 'complete'"), 'the reloaded page');
121
+ await harness.waitFor(
122
+ async () => (await homeText(harness)).includes(shortFirst),
123
+ 'the reloaded page shows the run of the project',
124
+ 20000,
125
+ );
126
+ await harness.waitFor(
127
+ async () => {
128
+ const stats = await liveStats(harness);
129
+ return stats.connections === 1 && stats.subscriptions >= 2;
130
+ },
131
+ 'the live channel is connected',
132
+ 20000,
133
+ );
134
+ await harness.waitFor(
135
+ async () => (await connectionLabel(harness)).includes('已连接实时更新'),
136
+ 'the shell reports the live connection',
137
+ 20000,
138
+ );
139
+
140
+ // The baseline of the healthy state.
141
+ const baselineStats = await liveStats(harness);
142
+ const baselineSockets = await socketStates(harness);
143
+ const baselineSent = await sentMessages(harness);
144
+ assert.equal(baselineSockets.length, 1, `the page opened one live socket: ${JSON.stringify(baselineSockets)}`);
145
+ assert.equal(baselineSockets[0], 1, 'the live socket is open');
146
+ assert.equal(baselineStats.subscriptions, 2, `the shell holds two subscriptions: ${JSON.stringify(baselineStats)}`);
147
+ const baselineActive = activeSubscriptions(baselineSent, 0);
148
+ assert.equal(baselineActive.size, 2, `the shell subscribed two channels: ${JSON.stringify([...baselineActive])}`);
149
+ assert.equal(new Set(baselineActive.values()).size, 2, `the two baseline subscriptions are for two different streams: ${JSON.stringify([...baselineActive])}`);
150
+ const baselineProgressReads = harness.engineProxy.requests.filter(entry => entry.path.startsWith('/api/progress')).length;
151
+ assert.ok(baselineProgressReads >= 1, `the engine really holds a progress stream: ${baselineProgressReads}`);
152
+
153
+ // The second commit of the fixture repository gives the second run a
154
+ // real commit of its own.
155
+ const secondSha = gitCommit(repo.path, 'SECOND.md', '# second\n');
156
+ const shortSecond = secondSha.slice(0, 8);
157
+
158
+ // ---- the network goes away ----
159
+ await harness.setOffline(true);
160
+ await harness.waitFor(() => harness.run('navigator.onLine === false'), 'the browser really goes offline');
161
+ await harness.waitFor(
162
+ async () => {
163
+ const stats = await liveStats(harness);
164
+ return stats.connections === 0 && stats.subscriptions === 0;
165
+ },
166
+ 'the shell closes the old stream',
167
+ 20000,
168
+ );
169
+
170
+ const offlineSockets = await socketStates(harness);
171
+ assert.equal(offlineSockets.length, 1, `the page still holds one socket object: ${JSON.stringify(offlineSockets)}`);
172
+ // The browser closes the stream on its own schedule: the shell drops its
173
+ // bookkeeping as soon as it asks the socket to close, so the closed
174
+ // state has to be waited for instead of being sampled right away.
175
+ const closedOld = await harness.waitFor(
176
+ async () => {
177
+ const events = await closeEvents(harness);
178
+ const close = events.find(event => event.generation === 0);
179
+ if (!close) return null;
180
+ const states = await socketStates(harness);
181
+ return { close, states };
182
+ },
183
+ 'the browser really closes the old stream',
184
+ 20000,
185
+ );
186
+ assert.equal(closedOld.states[0], 3, `the old stream is really closed by the browser: ${JSON.stringify(closedOld)}`);
187
+ assert.equal(closedOld.close.readyState, 3, `the browser closed the old stream in the closed state: ${JSON.stringify(closedOld.close)}`);
188
+
189
+ // NAV-008 expected 3: the content does not pretend to be live. The shell
190
+ // says the connection is broken, and it does not show the update list as
191
+ // if it were live information.
192
+ const offlineLabel = await connectionLabel(harness);
193
+ assert.equal(offlineLabel.includes('已连接实时更新'), false, `the shell does not claim a live connection while it is offline: ${offlineLabel}`);
194
+ assert.ok(offlineLabel.includes('连接中断'), `the shell reports the broken connection: ${offlineLabel}`);
195
+ const offlineHome = await homeText(harness);
196
+ assert.ok(offlineHome.includes('知识引擎暂时不可用'), `the home does not show stale updates as live information: ${offlineHome}`);
197
+ await harness.browser.screenshot(path.join(harness.evidence, 'nav008-offline.png'));
198
+
199
+ // No read of the snapshot reached the product while the browser was
200
+ // offline: the outage is real, not a silent retry.
201
+ const requestsBeforeOffline = harness.productProxy.requests.length;
202
+ await harness.wait(1200);
203
+ const requestsDuringOffline = harness.productProxy.requests.slice(requestsBeforeOffline);
204
+ const snapshotDuringOffline = requestsDuringOffline.filter(entry => entry.path.startsWith(SNAPSHOT));
205
+ assert.deepEqual(snapshotDuringOffline, [], `no snapshot read reached the product while the browser was offline: ${JSON.stringify(snapshotDuringOffline)}`);
206
+
207
+ // Real engine state that the browser cannot know while it is offline.
208
+ harness.engine.store.addRun({ projectId: project.projectId, commitSha: secondSha, trigger: 'manual' });
209
+
210
+ // ---- the network comes back ----
211
+ await harness.setOffline(false);
212
+ await harness.waitFor(() => harness.run('navigator.onLine === true'), 'the browser really comes online');
213
+ await harness.waitFor(
214
+ async () => {
215
+ const stats = await liveStats(harness);
216
+ return stats.connections === 1 && stats.subscriptions === 2;
217
+ },
218
+ 'the shell opens the new stream',
219
+ 20000,
220
+ );
221
+ await harness.waitFor(
222
+ async () => (await connectionLabel(harness)).includes('已连接实时更新'),
223
+ 'the shell reports the new live connection',
224
+ 20000,
225
+ );
226
+
227
+ const recoveredStats = await liveStats(harness);
228
+ const recoveredSockets = await socketStates(harness);
229
+ const recoveredSent = await sentMessages(harness);
230
+ const recoveredClosed = await closeEvents(harness);
231
+ assert.equal(recoveredSockets.length, 2, `the page opened a second socket after the recovery: ${JSON.stringify(recoveredSockets)}`);
232
+ assert.equal(recoveredSockets[0], 3, `the old socket stays closed: ${JSON.stringify(recoveredSockets)} ${JSON.stringify(recoveredClosed)}`);
233
+ assert.equal(recoveredSockets[1], 1, 'the new socket is open');
234
+ assert.equal(recoveredClosed.filter(event => event.generation === 1).length, 0, `the new stream was not closed by the outage: ${JSON.stringify(recoveredClosed)}`);
235
+
236
+ // NAV-008 expected 2: no duplicated subscription. The subscriptions of
237
+ // the new socket are exactly the two streams of the shell, each of them
238
+ // once, and the count of the shell matches them.
239
+ const recoveredActive = activeSubscriptions(recoveredSent, 1);
240
+ assert.equal(recoveredActive.size, 2, `the new socket carries exactly two subscriptions: ${JSON.stringify([...recoveredActive])}`);
241
+ assert.equal(new Set(recoveredActive.values()).size, 2, `the two recovered subscriptions are for two different streams: ${JSON.stringify([...recoveredActive])}`);
242
+ assert.equal(recoveredStats.subscriptions, recoveredActive.size, `the shell counts the live subscriptions exactly: ${JSON.stringify(recoveredStats)} ${JSON.stringify([...recoveredActive])}`);
243
+ assert.equal(recoveredStats.connections, 1, 'the shell holds exactly one live connection');
244
+ const progressReads = harness.engineProxy.requests.filter(entry => entry.path.startsWith('/api/progress')).length;
245
+ assert.equal(progressReads, baselineProgressReads + 1, `the engine holds one progress stream more, not more: ${progressReads}`);
246
+
247
+ // NAV-008 expected 1: the snapshot is restored. The run that appeared
248
+ // while the browser was offline is on the page now, and the page was
249
+ // updated by a real read of the snapshot after the recovery.
250
+ const snapshotReads = harness.productProxy.requests.filter(entry => entry.path.startsWith(SNAPSHOT));
251
+ assert.ok(snapshotReads.length >= 1, 'the shell read the snapshot after the recovery');
252
+ const lastSnapshot = snapshotReads[snapshotReads.length - 1];
253
+ assert.equal(lastSnapshot.status, 200, `the last snapshot read really succeeded: ${JSON.stringify(lastSnapshot)}`);
254
+ await harness.waitFor(
255
+ async () => (await homeText(harness)).includes(shortSecond),
256
+ 'the run that appeared while the browser was offline is on the page now',
257
+ 20000,
258
+ );
259
+ const recoveredHome = await homeText(harness);
260
+ assert.ok(recoveredHome.includes(shortFirst), 'the run of before the outage is still on the page');
261
+ assert.equal(recoveredHome.includes('知识引擎暂时不可用'), false, 'the page shows the real records again');
262
+ await harness.browser.screenshot(path.join(harness.evidence, 'nav008-recovered.png'));
263
+
264
+ assert.equal(harness.browser.exceptions.length, 0, `no uncaught product JavaScript exceptions: ${JSON.stringify(harness.browser.exceptions)}`);
265
+
266
+ console.log('parallel-nav-offline-test PASS (the old stream is closed, the snapshot and a new stream are restored, no duplicated subscription, nothing pretends to be live)');
267
+ } finally {
268
+ await harness.cleanup();
269
+ }
270
+ }
271
+
272
+ main().catch(error => { console.error(error.stack || error.message); process.exitCode = 1; });
@@ -0,0 +1,257 @@
1
+ 'use strict';
2
+ // NAV-006 — 可操作性。
3
+ //
4
+ // The shell must be operable by a real pointer and by a real keyboard: a
5
+ // native pointer click must reach the control, the focus must be visible, and
6
+ // nothing transparent may intercept the click.
7
+ //
8
+ // The regression drives the real product UI in a real browser against an
9
+ // isolated instance (own port, own data dir, own knowledge dir, own Bridge
10
+ // journal, own browser profile). Every pointer action is a CDP
11
+ // Input.dispatchMouseEvent on the real coordinates of the real control, and
12
+ // the hit test (document.elementFromPoint) must resolve to that control, so
13
+ // no transparent layer can stand in for a click. Every key action is a CDP
14
+ // Input.dispatchKeyEvent. element.click() is never used.
15
+ //
16
+ // The proof that the pointer really was the browser's is not the harness but
17
+ // the page: the shell's own listener records the event, and the recorded
18
+ // event carries isTrusted === true, which a synthesized DOM click can never
19
+ // produce.
20
+ //
21
+ // Screenshots are written to PK_TEST_EVIDENCE_DIR when the batch sets it, so
22
+ // this tracked file stays free of machine-specific paths.
23
+
24
+ const path = require('node:path');
25
+ const assert = require('node:assert/strict');
26
+
27
+ const { startHarness, makeRepo } = require('./helpers/parallel-nav-shell');
28
+
29
+ const EVIDENCE = process.env.PK_TEST_EVIDENCE_DIR ? path.resolve(process.env.PK_TEST_EVIDENCE_DIR) : null;
30
+
31
+ /** Record every click the page really receives, trusted or not. */
32
+ async function installClickRecorder(harness) {
33
+ await harness.run(`(() => {
34
+ window.__navClicks = [];
35
+ if (!window.__navClickHooked) {
36
+ window.__navClickHooked = true;
37
+ document.addEventListener('click', event => {
38
+ const target = event.target;
39
+ const nav = target.closest ? target.closest('[data-view]') : null;
40
+ window.__navClicks.push({
41
+ trusted: event.isTrusted === true,
42
+ tag: target.tagName,
43
+ id: target.id || '',
44
+ view: nav ? nav.getAttribute('data-view') : '',
45
+ label: (target.textContent || '').trim().slice(0, 30),
46
+ });
47
+ }, true);
48
+ }
49
+ return true;
50
+ })()`);
51
+ }
52
+
53
+ const clicks = harness => harness.run('window.__navClicks || []');
54
+
55
+ /** The focus ring the browser draws for the keyboard-focused element. */
56
+ const focusState = harness => harness.run(`(() => {
57
+ const el = document.activeElement;
58
+ if (!el || el === document.body) return { none: true };
59
+ const style = getComputedStyle(el);
60
+ const rect = el.getBoundingClientRect();
61
+ return {
62
+ none: false,
63
+ tag: el.tagName,
64
+ id: el.id || '',
65
+ view: el.getAttribute ? (el.getAttribute('data-view') || '') : '',
66
+ label: (el.textContent || '').trim().slice(0, 24),
67
+ outlineStyle: style.outlineStyle,
68
+ outlineWidth: style.outlineWidth,
69
+ outlineColor: style.outlineColor,
70
+ boxShadow: style.boxShadow,
71
+ width: rect.width,
72
+ height: rect.height,
73
+ visible: rect.width > 0 && rect.height > 0 && style.visibility !== 'hidden' && style.display !== 'none',
74
+ inViewport: rect.left >= 0 && rect.top >= 0 && rect.right <= innerWidth + 1 && rect.bottom <= innerHeight + 1,
75
+ };
76
+ })()`);
77
+
78
+ /**
79
+ * Every element that could swallow a pointer across the whole viewport.
80
+ * A transparent full-screen layer with pointer-events enabled is exactly the
81
+ * defect this case looks for.
82
+ */
83
+ const overlayScan = harness => harness.run(`(() => {
84
+ const full = [];
85
+ const covers = (rect) => rect.width >= innerWidth - 2 && rect.height >= innerHeight - 2;
86
+ for (const el of document.querySelectorAll('body *')) {
87
+ const style = getComputedStyle(el);
88
+ if (style.display === 'none' || style.visibility === 'hidden') continue;
89
+ if (style.pointerEvents === 'none') continue;
90
+ // Only a positioned element can float over the page; the layout
91
+ // containers are static and hold the controls themselves.
92
+ if (!['fixed', 'absolute', 'sticky'].includes(style.position)) continue;
93
+ const rect = el.getBoundingClientRect();
94
+ if (rect.width <= 0 || rect.height <= 0) continue;
95
+ if (!covers(rect)) continue;
96
+ const opacity = Number(style.opacity);
97
+ full.push({
98
+ tag: el.tagName,
99
+ id: el.id || '',
100
+ className: typeof el.className === 'string' ? el.className : '',
101
+ opacity: Number.isFinite(opacity) ? opacity : 1,
102
+ zIndex: style.zIndex,
103
+ background: style.backgroundColor,
104
+ });
105
+ }
106
+ return full;
107
+ })()`);
108
+
109
+ async function main() {
110
+ const harness = await startHarness({ tag: 'nav006', evidenceDir: EVIDENCE || undefined });
111
+ try {
112
+ const repo = makeRepo(harness.fixture, 'nav006-a');
113
+ await harness.importProject(repo, 'NAV006 甲');
114
+ await harness.requestRegistryRefresh();
115
+ await harness.waitFor(
116
+ () => harness.run("document.getElementById('homeContent').textContent.includes('NAV006 甲')"),
117
+ 'the imported project is listed',
118
+ 20000,
119
+ );
120
+ await installClickRecorder(harness);
121
+
122
+ // NAV-006 expected 4: the pointer is the browser's own, not a
123
+ // synthesized DOM event. The page records what it really received.
124
+ await harness.nativeClick('nav [data-view="search"]');
125
+ const afterNavClick = await clicks(harness);
126
+ assert.ok(afterNavClick.length >= 1, 'the page really received the click');
127
+ const navClick = afterNavClick[afterNavClick.length - 1];
128
+ assert.equal(navClick.trusted, true, `the click the page received is a trusted browser event: ${JSON.stringify(navClick)}`);
129
+ assert.equal(navClick.view, 'search', `the click reached the navigation control: ${JSON.stringify(navClick)}`);
130
+ await harness.waitFor(
131
+ () => harness.run("document.getElementById('view-search').classList.contains('active')"),
132
+ 'the search view is open',
133
+ );
134
+
135
+ // NAV-006 expected 4 (continued): the hit test of the very same control
136
+ // resolves to the control, so nothing transparent stands in front.
137
+ const hit = await harness.nativeClick('nav [data-view="memory"]');
138
+ assert.equal(hit.hitIsTarget, true, `the control is the hit target: ${JSON.stringify(hit)}`);
139
+ await harness.waitFor(
140
+ () => harness.run("document.getElementById('view-memory').classList.contains('active')"),
141
+ 'the memory view is open',
142
+ );
143
+ const memoryClick = (await clicks(harness)).pop();
144
+ assert.equal(memoryClick.trusted, true, 'the memory navigation click is trusted too');
145
+ assert.equal(memoryClick.view, 'memory', 'the memory navigation click reached the control');
146
+
147
+ // NAV-006 expected 1: no transparent layer intercepts the page.
148
+ const overlays = await overlayScan(harness);
149
+ assert.deepEqual(overlays, [], `no transparent full-viewport layer covers the page: ${JSON.stringify(overlays)}`);
150
+
151
+ // NAV-006 expected 2: the input focus is visible.
152
+ //
153
+ // The keyboard walk starts at the top of the document. Every Tab stop is
154
+ // recorded, and every stop must be visible on the screen and must carry
155
+ // a visible focus ring.
156
+ await harness.run("document.querySelector('nav [data-view=\"home\"]').focus()");
157
+ const stops = [];
158
+ for (let index = 0; index < 22; index += 1) {
159
+ await harness.pressTab();
160
+ const state = await focusState(harness);
161
+ if (!state.none) stops.push(state);
162
+ }
163
+ assert.ok(stops.length >= 8, `the keyboard walk reaches the main controls: ${stops.length} stops`);
164
+ const distinct = new Set(stops.map(stop => `${stop.tag}#${stop.id}.${stop.view}:${stop.label}`));
165
+ assert.ok(distinct.size >= 6, `the keyboard focus really moves between controls: ${[...distinct].join(' | ')}`);
166
+ for (const stop of stops) {
167
+ assert.equal(stop.visible, true, `the focused control is visible: ${JSON.stringify(stop)}`);
168
+ assert.equal(stop.inViewport, true, `the focused control is on the screen: ${JSON.stringify(stop)}`);
169
+ const hasRing = stop.outlineStyle !== 'none' && parseFloat(stop.outlineWidth) > 0;
170
+ assert.equal(hasRing, true, `the focused control shows a focus ring: ${JSON.stringify(stop)}`);
171
+ }
172
+
173
+ // NAV-006 expected 2 (continued): the focus ring is really painted, not
174
+ // only declared. The computed style of the focused element is compared
175
+ // with the computed style of the same element when it is not focused.
176
+ await harness.run("document.querySelector('nav [data-view=\"home\"]').focus()");
177
+ const focused = await harness.run(`(() => {
178
+ const el = document.querySelector('nav [data-view="home"]');
179
+ const style = getComputedStyle(el);
180
+ return { outlineStyle: style.outlineStyle, outlineWidth: style.outlineWidth, outlineColor: style.outlineColor };
181
+ })()`);
182
+ assert.equal(focused.outlineStyle, 'solid', `the focused control has a solid focus ring: ${JSON.stringify(focused)}`);
183
+ assert.ok(parseFloat(focused.outlineWidth) >= 2, `the focus ring is thick enough to see: ${JSON.stringify(focused)}`);
184
+ await harness.browser.screenshot(path.join(harness.evidence, 'nav006-focus-ring.png'));
185
+
186
+ // NAV-006 expected 1 and 2: the keyboard alone can open a page and
187
+ // reach the fields of a form.
188
+ //
189
+ // The navigation control is reached with Tab only, then Enter opens the
190
+ // view. The click the keyboard produces is trusted as well, because the
191
+ // browser itself synthesises it from the key press.
192
+ await harness.run("document.body.focus(); if (document.activeElement) document.activeElement.blur();");
193
+ let reachedSearch = false;
194
+ for (let index = 0; index < 12 && !reachedSearch; index += 1) {
195
+ await harness.pressTab();
196
+ const state = await focusState(harness);
197
+ if (state.view === 'search') reachedSearch = true;
198
+ }
199
+ assert.equal(reachedSearch, true, 'the keyboard reaches the search control');
200
+ const before = (await clicks(harness)).length;
201
+ await harness.pressEnter();
202
+ await harness.waitFor(
203
+ () => harness.run("document.getElementById('view-search').classList.contains('active')"),
204
+ 'the search view is open by keyboard alone',
205
+ );
206
+ const after = await clicks(harness);
207
+ assert.ok(after.length > before, 'the key press really produced a click');
208
+ const keyboardClick = after[after.length - 1];
209
+ assert.equal(keyboardClick.trusted, true, `the keyboard click is a trusted browser event: ${JSON.stringify(keyboardClick)}`);
210
+ assert.equal(keyboardClick.view, 'search', `the keyboard click reached the search control: ${JSON.stringify(keyboardClick)}`);
211
+
212
+ // The form of the import dialog is reachable and operable by keyboard.
213
+ await harness.nativeClick('nav [data-view="projects"]');
214
+ await harness.waitFor(
215
+ () => harness.run("document.getElementById('view-projects').classList.contains('active')"),
216
+ 'the projects view is open',
217
+ );
218
+ await harness.nativeClick('#createDomain');
219
+ await harness.waitFor(
220
+ () => harness.run("document.getElementById('domainDialog').open === true"),
221
+ 'the domain dialog is open',
222
+ );
223
+ await harness.run("document.querySelector('#domainDialog input[name=\"name\"]').focus()");
224
+ const fieldStop = await focusState(harness);
225
+ assert.equal(fieldStop.visible, true, `the form field is visible: ${JSON.stringify(fieldStop)}`);
226
+ assert.equal(fieldStop.inViewport, true, `the form field is on the screen: ${JSON.stringify(fieldStop)}`);
227
+ assert.equal(fieldStop.outlineStyle !== 'none' && parseFloat(fieldStop.outlineWidth) > 0, true, `the form field shows a focus ring: ${JSON.stringify(fieldStop)}`);
228
+ await harness.browser.screenshot(path.join(harness.evidence, 'nav006-form-focus.png'));
229
+
230
+ // The keyboard can type into the focused field, and the value really
231
+ // lands in the product's own input.
232
+ await harness.pressKey('a', { code: 'KeyA', keyCode: 65, text: 'a' });
233
+ await harness.pressKey('b', { code: 'KeyB', keyCode: 66, text: 'b' });
234
+ const typed = await harness.run("document.querySelector('#domainDialog input[name=\"name\"]').value");
235
+ assert.equal(typed, 'ab', `the keyboard typed into the focused field: ${JSON.stringify(typed)}`);
236
+
237
+ // Escape closes the dialog again, so the keyboard owns the whole flow.
238
+ await harness.pressKey('Escape', { code: 'Escape', keyCode: 27 });
239
+ await harness.waitFor(
240
+ () => harness.run("document.getElementById('domainDialog').open === false"),
241
+ 'the dialog closes by keyboard',
242
+ );
243
+
244
+ // Every click the page received during the case was a browser click.
245
+ const recorded = await clicks(harness);
246
+ assert.ok(recorded.length >= 5, `the page received the clicks of the case: ${recorded.length}`);
247
+ assert.equal(recorded.every(entry => entry.trusted === true), true, `no synthesized click reached the page: ${JSON.stringify(recorded)}`);
248
+
249
+ assert.equal(harness.browser.exceptions.length, 0, `no uncaught product JavaScript exceptions: ${JSON.stringify(harness.browser.exceptions)}`);
250
+
251
+ console.log('parallel-nav-operability-test PASS (native pointer and keyboard reach the controls, the focus is visible, nothing transparent intercepts)');
252
+ } finally {
253
+ await harness.cleanup();
254
+ }
255
+ }
256
+
257
+ main().catch(error => { console.error(error.stack || error.message); process.exitCode = 1; });