@sanqianx/project-knowledge 5.0.24 → 5.0.26
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.
- package/.agents/plugins/marketplace.json +20 -20
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +1361 -1353
- package/LICENSE +201 -201
- package/NOTICE +5 -5
- package/README.md +349 -349
- package/_modules/knowledge-engine/package.json +1 -1
- package/_modules/knowledge-engine/src/coordinator.js +48 -3
- package/_modules/knowledge-engine/src/runtime/automation-config.js +140 -140
- package/_modules/knowledge-engine/src/runtime/commit-conversation-binder.js +238 -238
- package/_modules/knowledge-engine/src/runtime/commit-processing-ledger.js +26 -26
- package/_modules/knowledge-engine/src/runtime/commit-prompt.js +193 -193
- package/_modules/knowledge-engine/src/runtime/commit-reconciler.js +394 -394
- package/_modules/knowledge-engine/src/runtime/contracts.js +282 -282
- package/_modules/knowledge-engine/src/runtime/conversation-store.js +389 -389
- package/_modules/knowledge-engine/src/runtime/evidence-bundle.js +228 -228
- package/_modules/knowledge-engine/src/runtime/git-runner.js +62 -62
- package/_modules/knowledge-engine/src/runtime/knowledge-promotion.js +505 -505
- package/_modules/knowledge-engine/src/runtime/knowledge-retrieval-service.js +400 -400
- package/_modules/knowledge-engine/src/runtime/markdown-knowledge-indexer.js +171 -171
- package/_modules/knowledge-engine/src/runtime/scanner.js +298 -298
- package/_modules/knowledge-engine/ui/app.js +23 -2
- package/_site/README.md +30 -30
- package/_site/_test/agent-capture-mcp-ui-test.js +68 -68
- package/_site/_test/agent-capture-ui-test.js +101 -101
- package/_site/_test/ai-profile-resolver-test.js +137 -137
- package/_site/_test/ai-profile-test.js +75 -75
- package/_site/_test/ai-vendor-presets-test.js +91 -91
- package/_site/_test/automatic-context-test.js +70 -70
- package/_site/_test/automation-queue-test.js +14 -14
- package/_site/_test/automation-ui-test.js +40 -40
- package/_site/_test/background-task-registry-test.js +43 -43
- package/_site/_test/baseline-schema-test.js +91 -91
- package/_site/_test/bin-cli-test.js +230 -230
- package/_site/_test/bridge-adapter-test.js +95 -95
- package/_site/_test/bridge-consumer-service-test.js +310 -310
- package/_site/_test/candidate-bin-content-test.js +35 -35
- package/_site/_test/candidate-product-assets-test.js +13 -13
- package/_site/_test/candidate-release-contract-test.js +14 -14
- package/_site/_test/capture-hook-dedup-test.js +158 -158
- package/_site/_test/cdp-browser-failure-test.js +103 -103
- package/_site/_test/chat-claudecodeui-match-test.js +92 -92
- package/_site/_test/claude-executable-discovery-test.js +90 -90
- package/_site/_test/claude-session-lifecycle-sweep-test.js +252 -252
- package/_site/_test/claude-workbench-test.js +174 -174
- package/_site/_test/cli-instance-management-test.js +62 -62
- package/_site/_test/client-capture-restart-e2e-test.js +189 -189
- package/_site/_test/codex-conversation-projection-test.js +60 -60
- package/_site/_test/codex-marketplace-scoped-path-test.js +186 -186
- package/_site/_test/codex-notify-capture-e2e-test.js +101 -101
- package/_site/_test/codex-notify-fanout-test.js +141 -134
- package/_site/_test/codex-notify-upgrade-test.js +253 -253
- package/_site/_test/commit-boundary-freeze-test.js +147 -147
- package/_site/_test/commit-conversation-binding-test.js +99 -99
- package/_site/_test/commit-evidence-test.js +110 -110
- package/_site/_test/configuration-generation-test.js +22 -22
- package/_site/_test/context-pack-test.js +200 -200
- package/_site/_test/conversation-api-test.js +119 -119
- package/_site/_test/conversation-origin-test.js +15 -15
- package/_site/_test/conversation-store-test.js +138 -138
- package/_site/_test/cross-repo-isolation-e2e-test.js +187 -187
- package/_site/_test/desktop-browser-compat-test.js +28 -28
- package/_site/_test/desktop-hook-runtime-regression-test.js +204 -204
- package/_site/_test/embedding-config-test.js +32 -32
- package/_site/_test/explicit-commit-processor-test.js +37 -37
- package/_site/_test/fixtures/fake-browser-exit.js +10 -10
- package/_site/_test/fixtures/fake-browser-hang.js +10 -10
- package/_site/_test/fixtures/integration-busy-worker.cjs +6 -6
- package/_site/_test/fixtures/make-git-repos.js +92 -92
- package/_site/_test/fixtures/retrieval-gold-queries.json +32 -32
- package/_site/_test/folder-picker-output-test.js +19 -19
- package/_site/_test/fresh-hook-reimport-test.js +53 -53
- package/_site/_test/fresh-product-data-test.js +56 -56
- package/_site/_test/full-integration-e2e-test.js +167 -167
- package/_site/_test/git-path-compatibility-test.js +80 -80
- package/_site/_test/git-validation-test.js +81 -81
- package/_site/_test/glm-ui-refresh-review-test.js +313 -404
- package/_site/_test/harness-isolation-test.js +138 -0
- package/_site/_test/helpers/cdp-browser.js +383 -0
- package/_site/_test/helpers/cdp-network-capture.js +66 -0
- package/_site/_test/helpers/find-chrome.js +34 -0
- package/_site/_test/helpers/free-port.js +8 -0
- package/_site/_test/helpers/logging-ui-fixture.js +131 -0
- package/_site/_test/helpers/module-fixture.js +282 -0
- package/_site/_test/helpers/parallel-engine-advanced-engine-child.js +45 -0
- package/_site/_test/helpers/parallel-engine-advanced-fault-writer.js +171 -0
- package/_site/_test/helpers/parallel-engine-advanced-harness.js +245 -0
- package/_site/_test/helpers/parallel-engine-advanced-standin-analyzer.js +80 -0
- package/_site/_test/helpers/parallel-engine-advanced-ui-host.js +177 -0
- package/_site/_test/helpers/parallel-engine-final-harness.js +314 -0
- package/_site/_test/helpers/parallel-engine-final-standin-analyzer.js +159 -0
- package/_site/_test/helpers/parallel-knowledge-fixture.js +255 -0
- package/_site/_test/helpers/parallel-logging-acceptance-fixture.js +111 -0
- package/_site/_test/helpers/parallel-logging-acceptance-proxy.js +106 -0
- package/_site/_test/helpers/parallel-logging-fix-r2-proxy.js +205 -0
- package/_site/_test/helpers/parallel-logging-integration-5025-env.js +432 -0
- package/_site/_test/helpers/parallel-memory-analyzer.js +81 -0
- package/_site/_test/helpers/parallel-memory-event-bridge.js +128 -0
- package/_site/_test/helpers/parallel-memory-shell.js +470 -0
- package/_site/_test/helpers/parallel-nav-isolation-env.js +389 -0
- package/_site/_test/helpers/parallel-nav-module-ui.js +186 -0
- package/_site/_test/helpers/parallel-nav-proxy.js +148 -0
- package/_site/_test/helpers/parallel-nav-shell.js +361 -0
- package/_site/_test/helpers/parallel-qa-engine-endpoint-guard.js +206 -0
- package/_site/_test/helpers/parallel-qa-engine-endpoint-isolate.js +257 -0
- package/_site/_test/helpers/parallel-retrieval-followup-corpus.js +293 -0
- package/_site/_test/helpers/parallel-retrieval-followup-next-session.js +70 -0
- package/_site/_test/helpers/parallel-retrieval-followup-r2-shell.js +737 -0
- package/_site/_test/helpers/parallel-retrieval-followup-r2-wb-host.js +82 -0
- package/_site/_test/helpers/parallel-retrieval-followup-shell.js +279 -0
- package/_site/_test/helpers/parallel-workbench-fixture.js +371 -0
- package/_site/_test/helpers/parallel-workbench-host.js +80 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-fixture.js +408 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-harness.js +593 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-host.js +70 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-launcher.js +240 -0
- package/_site/_test/helpers/parallel-workbench-integration-5025-runtime-harness.js +630 -0
- package/_site/_test/helpers/parallel-workbench-source-map.js +196 -0
- package/_site/_test/helpers/spawn-server.js +106 -0
- package/_site/_test/helpers/version-visibility-fixture.js +251 -0
- package/_site/_test/hook-runtime-endpoint-test.js +88 -88
- package/_site/_test/hook-status-repair-api-test.js +152 -152
- package/_site/_test/hook-trigger-test.js +85 -85
- package/_site/_test/hook-worktree-test.js +42 -42
- package/_site/_test/import-preflight-api-test.js +234 -234
- package/_site/_test/index-builder-test.js +41 -41
- package/_site/_test/index-error-display-contract-test.js +10 -10
- package/_site/_test/index-writer-concurrency-test.js +135 -135
- package/_site/_test/integration-adapters-test.js +262 -262
- package/_site/_test/integration-auto-repair-test.js +113 -113
- package/_site/_test/integration-capture-setup-test.js +118 -118
- package/_site/_test/integration-intent-test.js +81 -81
- package/_site/_test/integration-runtime-binding-a2-test.js +566 -566
- package/_site/_test/integration-runtime-binding-a3-test.js +227 -227
- package/_site/_test/integration-runtime-binding-test.js +473 -473
- package/_site/_test/integration-status-scan-test.js +29 -29
- package/_site/_test/integration-surface-coverage-test.js +164 -164
- package/_site/_test/kb-framework-test.js +57 -57
- package/_site/_test/kbpath-follow-test.js +63 -63
- package/_site/_test/knowledge-db-test.js +144 -144
- package/_site/_test/knowledge-engine-client-test.js +120 -109
- package/_site/_test/knowledge-language-control-test.js +170 -170
- package/_site/_test/knowledge-maintenance-test.js +40 -40
- package/_site/_test/knowledge-promotion-recovery-test.js +260 -260
- package/_site/_test/knowledge-query-test.js +54 -54
- package/_site/_test/knowledge-retrieval-service-test.js +77 -77
- package/_site/_test/knowledge-scopes-test.js +37 -37
- package/_site/_test/knowledge-storage-location-test.js +28 -28
- package/_site/_test/knowledge-storage-startup-test.js +52 -52
- package/_site/_test/knowledge-store-logs-supervision-test.js +89 -89
- package/_site/_test/launcher-readiness-test.js +80 -80
- package/_site/_test/legacy-claude-session-alias-test.js +106 -106
- package/_site/_test/legacy-routes-removed-test.js +47 -47
- package/_site/_test/legacy-workbench-exclusion-test.js +92 -92
- package/_site/_test/live-gateway-test.js +48 -48
- package/_site/_test/local-marketplace-test.js +16 -16
- package/_site/_test/log-redaction-test.js +58 -58
- package/_site/_test/logging-api-test.js +89 -89
- package/_site/_test/logging-sse-no-gap-test.js +88 -88
- package/_site/_test/logging-ui-test.js +107 -107
- package/_site/_test/markdown-delta-overlay-test.js +85 -85
- package/_site/_test/markdown-maintenance-api-test.js +75 -75
- package/_site/_test/markdown-maintenance-test.js +11 -11
- package/_site/_test/mcp-server-test.js +149 -149
- package/_site/_test/model-context-windows-test.js +89 -89
- package/_site/_test/module-agent-start-proxy-test.js +65 -65
- package/_site/_test/module-artifact-integrity-test.js +27 -27
- package/_site/_test/module-boundary-test.js +30 -30
- package/_site/_test/module-bridge-eventbridge-test.js +202 -202
- package/_site/_test/module-dynamic-port-test.js +118 -118
- package/_site/_test/module-index-proxy-test.js +33 -33
- package/_site/_test/module-model-configuration-test.js +44 -44
- package/_site/_test/module-process-lifecycle-test.js +35 -35
- package/_site/_test/module-stream-proxy-test.js +34 -34
- package/_site/_test/module-terminal-restart-test.js +63 -63
- package/_site/_test/module-wiring-unbind-race-test.js +59 -59
- package/_site/_test/native-context-integrations-test.js +86 -86
- package/_site/_test/non-release-ci-test.js +34 -34
- package/_site/_test/offline-boundary-isolation-test.js +32 -32
- package/_site/_test/orphaned-claim-recovery-test.js +80 -80
- package/_site/_test/package-boundary-test.js +43 -43
- package/_site/_test/package-startup-test.js +56 -56
- package/_site/_test/packaged-ui-smoke-test.js +88 -88
- package/_site/_test/parallel-ci-r9-posix-socket-budget-test.js +156 -0
- package/_site/_test/parallel-engine-advanced-eng005-test.js +136 -0
- package/_site/_test/parallel-engine-advanced-eng006-test.js +171 -0
- package/_site/_test/parallel-engine-advanced-eng007-reconcile-test.js +231 -0
- package/_site/_test/parallel-engine-advanced-eng009-test.js +208 -0
- package/_site/_test/parallel-engine-advanced-eng010-test.js +213 -0
- package/_site/_test/parallel-engine-advanced-eng011-ui-probe.js +459 -0
- package/_site/_test/parallel-engine-final-eng002-test.js +255 -0
- package/_site/_test/parallel-engine-final-eng007-test.js +269 -0
- package/_site/_test/parallel-engine-final-eng011-test.js +197 -0
- package/_site/_test/parallel-engine-final-eng012-test.js +354 -0
- package/_site/_test/parallel-knowledge-client-error-code-test.js +184 -0
- package/_site/_test/parallel-knowledge-engine-test.js +353 -0
- package/_site/_test/parallel-knowledge-retrieval-test.js +292 -0
- package/_site/_test/parallel-knowledge-vector-test.js +168 -0
- package/_site/_test/parallel-linux-ci-r7-log007-day-test.js +110 -0
- package/_site/_test/parallel-logging-acceptance-log003-test.js +224 -0
- package/_site/_test/parallel-logging-acceptance-log004-test.js +231 -0
- package/_site/_test/parallel-logging-acceptance-log005-test.js +208 -0
- package/_site/_test/parallel-logging-acceptance-log006-test.js +214 -0
- package/_site/_test/parallel-logging-acceptance-log007-test.js +223 -0
- package/_site/_test/parallel-logging-acceptance-log008-test.js +180 -0
- package/_site/_test/parallel-logging-fix-r2-crash-probe.js +271 -0
- package/_site/_test/parallel-logging-fix-r2-gap-test.js +294 -0
- package/_site/_test/parallel-logging-fix-r2-normalhook-test.js +330 -0
- package/_site/_test/parallel-logging-integration-5025-portability-test.js +554 -0
- package/_site/_test/parallel-memory-candidate-test.js +301 -0
- package/_site/_test/parallel-memory-manual-test.js +197 -0
- package/_site/_test/parallel-memory-scope-test.js +228 -0
- package/_site/_test/parallel-memory-source-test.js +227 -0
- package/_site/_test/parallel-memory-sync-test.js +238 -0
- package/_site/_test/parallel-memory-view-r8-reload-gate-test.js +123 -0
- package/_site/_test/parallel-nav-failure-test.js +119 -0
- package/_site/_test/parallel-nav-isolation-test.js +308 -0
- package/_site/_test/parallel-nav-isolation-vector-path-test.js +90 -0
- package/_site/_test/parallel-nav-offline-test.js +272 -0
- package/_site/_test/parallel-nav-operability-test.js +257 -0
- package/_site/_test/parallel-nav-switch-test.js +247 -0
- package/_site/_test/parallel-nav-theme-test.js +130 -0
- package/_site/_test/parallel-nav-viewport-test.js +310 -0
- package/_site/_test/parallel-qa-engine-endpoint-guard-test.js +273 -0
- package/_site/_test/parallel-retrieval-followup-citation-test.js +219 -0
- package/_site/_test/parallel-retrieval-followup-corpus-test.js +287 -0
- package/_site/_test/parallel-workbench-independent-host.js +262 -0
- package/_site/_test/parallel-workbench-integration-5025-abort-probe.js +340 -0
- package/_site/_test/parallel-workbench-integration-5025-drafts-probe.js +588 -0
- package/_site/_test/parallel-workbench-integration-5025-env-check.js +317 -0
- package/_site/_test/parallel-workbench-integration-5025-model-probe.js +342 -0
- package/_site/_test/parallel-workbench-integration-5025-ordering-probe.js +326 -0
- package/_site/_test/parallel-workbench-model-probe.js +336 -0
- package/_site/_test/parallel-workbench-offline-test.js +169 -0
- package/_site/_test/parallel-workbench-provenance-trace.js +134 -0
- package/_site/_test/parallel-workbench-source-match.js +206 -0
- package/_site/_test/parallel-workbench-standalone-turn.js +349 -0
- package/_site/_test/path-consistency-test.js +151 -151
- package/_site/_test/pending-sweep-test.js +9 -9
- package/_site/_test/post-commit-automation-test.js +87 -87
- package/_site/_test/pr-consumer-contract-test.js +55 -55
- package/_site/_test/product-brand-contract-test.js +25 -25
- package/_site/_test/product-diagnostics-ui-test.js +93 -93
- package/_site/_test/product-empty-state-test.js +148 -0
- package/_site/_test/product-import-ui-test.js +219 -104
- package/_site/_test/product-navigation-test.js +152 -0
- package/_site/_test/product-progress-status-test.js +84 -84
- package/_site/_test/project-delete-recovery-test.js +64 -64
- package/_site/_test/project-domain-create-test.js +91 -0
- package/_site/_test/project-domain-scope-retrieval-test.js +214 -0
- package/_site/_test/project-git-init-confirm-test.js +115 -0
- package/_site/_test/project-goal-editor-test.js +144 -144
- package/_site/_test/project-import-duplicate-alias-test.js +134 -0
- package/_site/_test/project-import-unicode-restart-test.js +140 -0
- package/_site/_test/project-lifecycle-transaction-test.js +106 -106
- package/_site/_test/project-module-partial-recovery-test.js +222 -0
- package/_site/_test/project-remove-running-guard-test.js +79 -79
- package/_site/_test/project-store-test.js +90 -90
- package/_site/_test/project-unbind-late-read-ui-test.js +250 -0
- package/_site/_test/project-unbind-writer-guard-test.js +231 -0
- package/_site/_test/prompt-settings-test.js +115 -115
- package/_site/_test/protected-architecture-gate-test.js +122 -122
- package/_site/_test/refactor-characterization-test.js +36 -36
- package/_site/_test/registry-events-test.js +38 -38
- package/_site/_test/release-version-sync-test.js +115 -115
- package/_site/_test/requirement-binding-test.js +113 -113
- package/_site/_test/requirement-recorder-test.js +173 -173
- package/_site/_test/run-all-tests.js +173 -161
- package/_site/_test/runtime-endpoint-test.js +64 -64
- package/_site/_test/scanner-test.js +83 -83
- package/_site/_test/server-security-test.js +96 -96
- package/_site/_test/sessions-stream-test.js +93 -93
- package/_site/_test/shared-contracts-test.js +58 -58
- package/_site/_test/shared-scope-root-binding-test.js +74 -67
- package/_site/_test/simple-import-test.js +98 -98
- package/_site/_test/startup-analysis-disabled-test.js +16 -16
- package/_site/_test/storage-foundation-test.js +78 -78
- package/_site/_test/structured-logger-test.js +101 -101
- package/_site/_test/t07b-cln-005-coverage-test.js +212 -0
- package/_site/_test/team-knowledge-store-sparse-checkout-test.js +196 -196
- package/_site/_test/team-knowledge-store-test.js +132 -132
- package/_site/_test/test-server-cleanup-test.js +35 -35
- package/_site/_test/test-server-module-isolation-test.js +37 -37
- package/_site/_test/tracking-start-test.js +144 -144
- package/_site/_test/vector-storage-root-test.js +75 -75
- package/_site/_test/version-visibility-browser-probe.js +249 -0
- package/_site/_test/version-visibility-test.js +92 -0
- package/_site/_test/workbench-context-receipts-test.js +22 -22
- package/_site/_test/workbench-permission-test.js +117 -117
- package/_site/_test/zcode-capture-integration-test.js +112 -112
- package/_site/lib/ai-profile-resolver.js +78 -78
- package/_site/lib/ai-vendor-presets.js +83 -83
- package/_site/lib/ai-workspace.js +104 -104
- package/_site/lib/atomic-file.js +204 -204
- package/_site/lib/automation-config.js +140 -140
- package/_site/lib/bridge-adapter.js +260 -260
- package/_site/lib/bridge-consumer-service.js +402 -402
- package/_site/lib/capture-hook-config.js +108 -108
- package/_site/lib/claude-cli-runner.js +1599 -1599
- package/_site/lib/cli-instances.js +43 -43
- package/_site/lib/codex-context-status.js +17 -17
- package/_site/lib/codex-notify-config.js +193 -193
- package/_site/lib/commit-conversation-binder.js +247 -247
- package/_site/lib/commit-processing-ledger.js +27 -27
- package/_site/lib/commit-prompt.js +202 -202
- package/_site/lib/commit-reconciler.js +444 -444
- package/_site/lib/context-pack-builder.js +299 -299
- package/_site/lib/contracts.js +291 -291
- package/_site/lib/conversation-exclusions.js +204 -204
- package/_site/lib/conversation-query-service.js +161 -161
- package/_site/lib/conversation-store.js +387 -387
- package/_site/lib/data-dir.js +13 -13
- package/_site/lib/embedding-service.js +85 -85
- package/_site/lib/engine-connection.js +93 -93
- package/_site/lib/engine-knowledge-client.js +76 -72
- package/_site/lib/evidence-bundle.js +228 -228
- package/_site/lib/folder-picker-output.js +24 -24
- package/_site/lib/git-path.js +14 -14
- package/_site/lib/git-runner.js +62 -62
- package/_site/lib/github-team-store.js +1175 -1175
- package/_site/lib/hook-manager.js +203 -203
- package/_site/lib/index-builder.js +157 -157
- package/_site/lib/index-service.js +173 -173
- package/_site/lib/integration-auto-repair.js +77 -77
- package/_site/lib/integration-installer.js +1240 -1240
- package/_site/lib/integration-intent.js +88 -88
- package/_site/lib/integration-status-scan.js +99 -99
- package/_site/lib/integration-status-worker.js +14 -14
- package/_site/lib/kb-framework.js +191 -191
- package/_site/lib/kb-validator.js +124 -124
- package/_site/lib/knowledge-db.js +363 -363
- package/_site/lib/knowledge-promotion.js +435 -435
- package/_site/lib/knowledge-retrieval-service.js +401 -401
- package/_site/lib/knowledge-schema.js +121 -121
- package/_site/lib/knowledge-tool-runtime.js +366 -366
- package/_site/lib/live-gateway.js +78 -78
- package/_site/lib/llm-client.js +173 -173
- package/_site/lib/markdown-knowledge-indexer.js +230 -230
- package/_site/lib/model-context-windows.js +82 -82
- package/_site/lib/module-bridge.js +874 -874
- package/_site/lib/module-port-resolver.js +57 -57
- package/_site/lib/post-commit-automation.js +141 -141
- package/_site/lib/product-data.js +35 -35
- package/_site/lib/project-lifecycle-service.js +554 -554
- package/_site/lib/project-registry-store.js +129 -129
- package/_site/lib/project-store.js +268 -268
- package/_site/lib/prompt-registry.js +148 -148
- package/_site/lib/public-version-info.js +37 -0
- package/_site/lib/registry-events.js +16 -16
- package/_site/lib/requirement-adapters.js +10 -10
- package/_site/lib/requirement-binder.js +112 -112
- package/_site/lib/requirement-recorder.js +278 -278
- package/_site/lib/runtime-control.js +89 -89
- package/_site/lib/runtime-endpoint.js +160 -160
- package/_site/lib/scanner.js +298 -298
- package/_site/lib/server-app.js +1672 -1664
- package/_site/lib/settings-store.js +153 -153
- package/_site/lib/storage-layout.js +166 -166
- package/_site/lib/structured-logger.js +574 -574
- package/_site/lib/zcode-context-integration.js +60 -60
- package/_site/scripts/codex-notify-fanout.cjs +95 -95
- package/_site/scripts/folder-picker.ps1 +156 -156
- package/_site/scripts/hook-trigger.js +158 -158
- package/_site/scripts/install-module-candidates.js +69 -69
- package/_site/scripts/pack-module-candidates.js +89 -89
- package/_site/scripts/sync-product-content.js +28 -28
- package/_site/scripts/sync-release-version.js +164 -164
- package/_site/scripts/vendor-modules.js +49 -49
- package/_site/scripts/verify-module-runtime.js +50 -50
- package/_site/scripts/verify-product-runtime.js +36 -36
- package/_site/scripts/verify-retrieval-gold.js +83 -83
- package/_site/server.js +15 -15
- package/_site/start.bat +26 -26
- package/_site/stop.bat +11 -11
- package/bin/project-knowledge-integrations.js +254 -254
- package/bin/project-knowledge-kb.js +67 -67
- package/bin/project-knowledge-mcp.js +201 -201
- package/bin/project-knowledge.js +269 -170
- package/claude-prompts.json +21 -21
- package/docs/README.zh-CN.md +207 -205
- package/docs/assets/architecture.svg +181 -181
- package/docs/assets/logo.svg +49 -49
- package/docs/fresh-v5-start.md +44 -44
- package/docs/leader-glm-workbench-playbook.md +122 -122
- package/docs/project-registry-schema.md +33 -33
- package/docs/release-5.0.16-acceptance.md +176 -176
- package/docs/release-5.0.17-acceptance.md +117 -117
- package/docs/release-5.0.19-acceptance.md +39 -39
- package/docs/team-knowledge-mode-a-plan.md +73 -73
- package/docs/testing-strategy.md +33 -33
- package/module-runtime-manifest.json +68 -65
- package/package.json +113 -110
- package/plugins/project-knowledge/.claude-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.codex-plugin/plugin.json +1 -1
- package/plugins/project-knowledge/.mcp.json +1 -1
- package/templates/change.md +30 -30
- package/templates/project-readme.md +52 -52
- package/ui/index.html +3 -2
- package/ui/version-info.css +44 -0
- package/ui/version-info.js +24 -0
- package/vendor-manifest.json +55 -55
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// NAV-005 — 项目快速切换。
|
|
3
|
+
//
|
|
4
|
+
// Switching projects quickly must show one project at a time, a late answer
|
|
5
|
+
// must not paint another project over the current one, and the project that
|
|
6
|
+
// reaches the embedded module must be the current one.
|
|
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). The project switch is a real pointer click
|
|
11
|
+
// on the real project row. The late answer is a real held HTTP response in a
|
|
12
|
+
// proxy in front of the product: the shell really receives it, late. The
|
|
13
|
+
// knowledge snapshot is real engine state — the vendored knowledge engine is
|
|
14
|
+
// started for this case and the runs are written into its store.
|
|
15
|
+
//
|
|
16
|
+
// The embedded module UI is a test double that speaks the real kb:*
|
|
17
|
+
// protocol; it records the kb:select-project the shell really posted, so the
|
|
18
|
+
// project -> module mapping under test is the shell's own mapping.
|
|
19
|
+
//
|
|
20
|
+
// Screenshots are written to PK_TEST_EVIDENCE_DIR when the batch sets it, so
|
|
21
|
+
// this tracked file stays free of machine-specific paths.
|
|
22
|
+
|
|
23
|
+
const path = require('node:path');
|
|
24
|
+
const assert = require('node:assert/strict');
|
|
25
|
+
|
|
26
|
+
const { startHarness, makeRepo } = require('./helpers/parallel-nav-shell');
|
|
27
|
+
|
|
28
|
+
const EVIDENCE = process.env.PK_TEST_EVIDENCE_DIR ? path.resolve(process.env.PK_TEST_EVIDENCE_DIR) : null;
|
|
29
|
+
|
|
30
|
+
const SNAPSHOT = '/api/engine/snapshot';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Ask the shell to read the registry again and wait until a new matching
|
|
34
|
+
* read really left the browser.
|
|
35
|
+
*
|
|
36
|
+
* The case owns this loop instead of the shared helper because the held
|
|
37
|
+
* read lives inside the shell's three second read timeout: every hundred
|
|
38
|
+
* milliseconds of polling is a hundred milliseconds less of the budget the
|
|
39
|
+
* late answer has to arrive in.
|
|
40
|
+
*/
|
|
41
|
+
async function refreshUntil(harness, match, timeoutMs = 20000) {
|
|
42
|
+
const before = harness.productProxy.requests.filter(match).length;
|
|
43
|
+
const deadline = Date.now() + timeoutMs;
|
|
44
|
+
while (Date.now() < deadline) {
|
|
45
|
+
await harness.requestRegistryRefresh();
|
|
46
|
+
await harness.wait(50);
|
|
47
|
+
const now = harness.productProxy.requests.filter(match).length;
|
|
48
|
+
if (now > before) return now;
|
|
49
|
+
}
|
|
50
|
+
throw new Error('the shell did not make the expected read');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Record every read the page really made and how it ended.
|
|
55
|
+
*
|
|
56
|
+
* The late answer of the previous project is only evidence if it really
|
|
57
|
+
* reached the product's promise chain: a read that the shell abandons after
|
|
58
|
+
* its three second timeout never reaches the guard, so the case would pass
|
|
59
|
+
* without a late answer ever arriving. The hook therefore records the state
|
|
60
|
+
* of every read, and the case waits for the late answer to be consumed.
|
|
61
|
+
*/
|
|
62
|
+
const FETCH_HOOK = `(() => {
|
|
63
|
+
window.__fetches = [];
|
|
64
|
+
const nativeFetch = window.fetch;
|
|
65
|
+
window.fetch = function (input) {
|
|
66
|
+
const url = typeof input === 'string' ? input : (input && input.url) || '';
|
|
67
|
+
const entry = { url: String(url), state: 'pending', at: Date.now() };
|
|
68
|
+
window.__fetches.push(entry);
|
|
69
|
+
const args = arguments;
|
|
70
|
+
return nativeFetch.apply(this, args).then(response => {
|
|
71
|
+
entry.state = 'responded';
|
|
72
|
+
entry.status = response.status;
|
|
73
|
+
const originalJson = response.json.bind(response);
|
|
74
|
+
response.json = () => originalJson().then(data => {
|
|
75
|
+
entry.state = 'consumed';
|
|
76
|
+
entry.endedAt = Date.now();
|
|
77
|
+
return data;
|
|
78
|
+
}, error => {
|
|
79
|
+
entry.state = 'failed';
|
|
80
|
+
entry.endedAt = Date.now();
|
|
81
|
+
throw error;
|
|
82
|
+
});
|
|
83
|
+
return response;
|
|
84
|
+
}, error => {
|
|
85
|
+
entry.state = 'aborted';
|
|
86
|
+
entry.endedAt = Date.now();
|
|
87
|
+
throw error;
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
})()`;
|
|
91
|
+
|
|
92
|
+
/** The reads of the given project the page really made. */
|
|
93
|
+
const projectReads = (harness, projectId, skip = 0) => harness.run(`(window.__fetches || []).filter(entry => entry.url.includes('/api/engine/snapshot') && entry.url.includes(${JSON.stringify(`projectId=${projectId}`)})).slice(${skip})`);
|
|
94
|
+
|
|
95
|
+
async function main() {
|
|
96
|
+
const harness = await startHarness({ tag: 'nav005', evidenceDir: EVIDENCE || undefined });
|
|
97
|
+
try {
|
|
98
|
+
// The hook is installed before the case starts, so every read of the
|
|
99
|
+
// case is recorded.
|
|
100
|
+
await harness.run(FETCH_HOOK);
|
|
101
|
+
const repoA = makeRepo(harness.fixture, 'nav005-a');
|
|
102
|
+
const repoB = makeRepo(harness.fixture, 'nav005-b');
|
|
103
|
+
const projectA = await harness.importProject(repoA, 'NAV005 甲');
|
|
104
|
+
const projectB = await harness.importProject(repoB, 'NAV005 乙');
|
|
105
|
+
|
|
106
|
+
// Real engine state: one run per project, on the real baseline commit.
|
|
107
|
+
harness.engine.store.addRun({ projectId: projectA.projectId, commitSha: repoA.headCommit, trigger: 'manual' });
|
|
108
|
+
harness.engine.store.addRun({ projectId: projectB.projectId, commitSha: repoB.headCommit, trigger: 'manual' });
|
|
109
|
+
const shortA = repoA.headCommit.slice(0, 8);
|
|
110
|
+
const shortB = repoB.headCommit.slice(0, 8);
|
|
111
|
+
|
|
112
|
+
await harness.requestRegistryRefresh();
|
|
113
|
+
await harness.waitFor(
|
|
114
|
+
() => harness.run(`document.getElementById('homeContent').textContent.includes(${JSON.stringify(shortA)})`),
|
|
115
|
+
'the current project shows its own run',
|
|
116
|
+
20000,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const homeText = () => harness.run("document.getElementById('homeContent').textContent");
|
|
120
|
+
const currentProject = () => harness.run("document.getElementById('projectSelect').value");
|
|
121
|
+
|
|
122
|
+
// The terminal frame is opened once so the project mapping the shell
|
|
123
|
+
// really posts is observable.
|
|
124
|
+
await harness.run("document.querySelector('[data-view=\"work\"]').click()");
|
|
125
|
+
await harness.waitFor(
|
|
126
|
+
async () => {
|
|
127
|
+
const messages = await harness.frameMessages('workFrame');
|
|
128
|
+
return Array.isArray(messages) && messages.some(message => message.type === 'kb:select-project');
|
|
129
|
+
},
|
|
130
|
+
'the terminal frame receives kb:select-project',
|
|
131
|
+
20000,
|
|
132
|
+
);
|
|
133
|
+
await harness.run("document.querySelector('[data-view=\"home\"]').click()");
|
|
134
|
+
await harness.waitFor(() => harness.run("document.getElementById('view-home').classList.contains('active')"), 'the home view');
|
|
135
|
+
|
|
136
|
+
assert.equal(await currentProject(), projectA.projectId, 'the first project is the current one');
|
|
137
|
+
|
|
138
|
+
// How many reads of the first project the page already made: the held
|
|
139
|
+
// read is the next one, and the late answer is the one after it.
|
|
140
|
+
const readsBeforeHold = (await projectReads(harness, projectA.projectId)).length;
|
|
141
|
+
|
|
142
|
+
// A registry refresh reads the snapshot of the current project; that read
|
|
143
|
+
// is held so it can be released after the switch.
|
|
144
|
+
harness.productProxy.hold(entry => entry.method === 'GET' && entry.path.startsWith(SNAPSHOT) && entry.path.includes(`projectId=${projectA.projectId}`), { once: true });
|
|
145
|
+
await refreshUntil(harness, entry => entry.path.startsWith(SNAPSHOT) && entry.path.includes(`projectId=${projectA.projectId}`));
|
|
146
|
+
await harness.waitFor(
|
|
147
|
+
() => harness.productProxy.heldCount() > 0,
|
|
148
|
+
'the snapshot read of the current project is held',
|
|
149
|
+
20000,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// The switch to the other project is a real pointer click on the row.
|
|
153
|
+
await harness.nativeClick(`#homeContent [data-select-project="${projectB.projectId}"]`);
|
|
154
|
+
await harness.waitFor(async () => (await currentProject()) === projectB.projectId, 'the second project becomes the current one');
|
|
155
|
+
await harness.waitFor(
|
|
156
|
+
() => harness.run(`document.getElementById('homeContent').textContent.includes(${JSON.stringify(shortB)})`),
|
|
157
|
+
'the second project shows its own run',
|
|
158
|
+
20000,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
// NAV-005 expected 1: only the current project is shown.
|
|
162
|
+
let text = await homeText();
|
|
163
|
+
assert.ok(text.includes(shortB), 'the current project run is shown');
|
|
164
|
+
assert.equal(text.includes(shortA), false, 'the other project run is not shown while the second project is current');
|
|
165
|
+
|
|
166
|
+
// NAV-005 expected 2: the late answer of the previous project must not
|
|
167
|
+
// paint over the current project.
|
|
168
|
+
//
|
|
169
|
+
// The snapshot reads are broken from here on, so that no automatic
|
|
170
|
+
// refresh can repair the page and hide a late answer that was wrongly
|
|
171
|
+
// applied. Without the shell's epoch and project guard the late snapshot
|
|
172
|
+
// of the other project would replace the current one, and the home would
|
|
173
|
+
// lose the run of the current project.
|
|
174
|
+
harness.productProxy.fail(entry => entry.method === 'GET' && entry.path.startsWith(SNAPSHOT), 503, '注入的快照读取故障');
|
|
175
|
+
const released = harness.productProxy.releaseHeld();
|
|
176
|
+
assert.ok(released >= 1, 'the held snapshot read is really delivered late');
|
|
177
|
+
|
|
178
|
+
// The late answer must really have reached the page: a read the shell
|
|
179
|
+
// abandons after its three second timeout never reaches the guard, and
|
|
180
|
+
// the case would prove nothing about a late answer that never arrived.
|
|
181
|
+
const lateRead = await harness.waitFor(
|
|
182
|
+
async () => {
|
|
183
|
+
const reads = await projectReads(harness, projectA.projectId, readsBeforeHold);
|
|
184
|
+
const last = reads[reads.length - 1];
|
|
185
|
+
if (!last || last.state === 'pending' || last.state === 'responded') return null;
|
|
186
|
+
return last;
|
|
187
|
+
},
|
|
188
|
+
'the late answer really reached the page',
|
|
189
|
+
20000,
|
|
190
|
+
);
|
|
191
|
+
assert.equal(lateRead.state, 'consumed', `the late answer was really consumed by the page: ${JSON.stringify(lateRead)}`);
|
|
192
|
+
// Two frames: the page really painted the outcome of the late answer.
|
|
193
|
+
await harness.run('new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve(true))))');
|
|
194
|
+
|
|
195
|
+
// The fault must really bite a real read. The shell reads the snapshot
|
|
196
|
+
// again on its own schedule, but the case does not wait for that: it
|
|
197
|
+
// asks the shell to read the registry again and waits until the proxy
|
|
198
|
+
// really saw a failed snapshot read. Without a failed read the case
|
|
199
|
+
// could not claim that no successful refresh was able to mask the late
|
|
200
|
+
// answer.
|
|
201
|
+
const failedReads = await refreshUntil(harness, entry => entry.path.startsWith(SNAPSHOT) && entry.status === 503);
|
|
202
|
+
assert.ok(failedReads >= 1, `the snapshot read really failed while the late answer was delivered: ${failedReads}`);
|
|
203
|
+
|
|
204
|
+
text = await homeText();
|
|
205
|
+
assert.equal(await currentProject(), projectB.projectId, 'the current project did not change because of the late answer');
|
|
206
|
+
assert.ok(text.includes(shortB), 'the current project run is still shown after the late answer');
|
|
207
|
+
assert.equal(text.includes(shortA), false, 'the late answer of the other project did not overwrite the current project');
|
|
208
|
+
const failedSnapshotReads = harness.productProxy.requests.filter(entry => entry.path.startsWith(SNAPSHOT) && entry.status === 503).length;
|
|
209
|
+
assert.ok(failedSnapshotReads >= 1, `the snapshot reads really failed while the late answer was delivered, so no refresh could mask it: ${failedSnapshotReads} failed reads`);
|
|
210
|
+
harness.productProxy.clearFaults();
|
|
211
|
+
|
|
212
|
+
// The switch back is a real pointer click as well.
|
|
213
|
+
await harness.nativeClick(`#homeContent [data-select-project="${projectA.projectId}"]`);
|
|
214
|
+
await harness.waitFor(async () => (await currentProject()) === projectA.projectId, 'the first project becomes the current one again');
|
|
215
|
+
await harness.waitFor(
|
|
216
|
+
() => harness.run(`document.getElementById('homeContent').textContent.includes(${JSON.stringify(shortA)})`),
|
|
217
|
+
'the first project shows its own run again',
|
|
218
|
+
20000,
|
|
219
|
+
);
|
|
220
|
+
text = await homeText();
|
|
221
|
+
assert.equal(text.includes(shortB), false, 'the other project run is not shown after switching back');
|
|
222
|
+
await harness.browser.screenshot(path.join(harness.evidence, 'nav005-switch-back.png'));
|
|
223
|
+
|
|
224
|
+
// NAV-005 expected 3: the project that reaches the embedded module is the
|
|
225
|
+
// current one, and the mapping is the one the product registered.
|
|
226
|
+
const mappings = (await harness.frameMessages('workFrame')).filter(message => message.type === 'kb:select-project');
|
|
227
|
+
const last = mappings[mappings.length - 1];
|
|
228
|
+
assert.ok(last, 'the terminal frame really received kb:select-project');
|
|
229
|
+
assert.equal(last.projectId, projectA.projectId, 'the newest kb:select-project carries the current project');
|
|
230
|
+
assert.equal(last.project, projectA.project.state.modules.terminal.projectId, 'the newest kb:select-project carries the module project the product registered for it');
|
|
231
|
+
const forB = mappings.find(message => message.projectId === projectB.projectId);
|
|
232
|
+
assert.ok(forB, 'the terminal frame really received the mapping of the other project');
|
|
233
|
+
assert.equal(forB.project, projectB.project.state.modules.terminal.projectId, 'the mapping of the other project is the module project the product registered for it');
|
|
234
|
+
const requestIds = mappings.map(message => message.requestId);
|
|
235
|
+
assert.ok(requestIds.every(requestId => /^\d+:terminal$/.test(String(requestId))), `every mapping carries the shell request id: ${JSON.stringify(requestIds)}`);
|
|
236
|
+
assert.notEqual(forB.requestId, last.requestId, 'the switch to the other project used a new request id');
|
|
237
|
+
assert.ok(mappings.every(message => message.projectId === projectA.projectId || message.projectId === projectB.projectId), 'no mapping carries a project that was never current');
|
|
238
|
+
|
|
239
|
+
assert.equal(harness.browser.exceptions.length, 0, `no uncaught product JavaScript exceptions: ${JSON.stringify(harness.browser.exceptions)}`);
|
|
240
|
+
|
|
241
|
+
console.log('parallel-nav-switch-test PASS (one project at a time, the late answer does not cross projects, the module mapping follows the current project)');
|
|
242
|
+
} finally {
|
|
243
|
+
await harness.cleanup();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
main().catch(error => { console.error(error.stack || error.message); process.exitCode = 1; });
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// NAV-002 — 主题同步。
|
|
3
|
+
//
|
|
4
|
+
// The shell owns the product theme: switching it must persist, every embedded
|
|
5
|
+
// module that is already ready must be told about the new theme, and the
|
|
6
|
+
// switch must not rebuild the module task.
|
|
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, own vendored knowledge engine). The embedded
|
|
11
|
+
// module UI is a test double that speaks the real kb:* protocol: it announces
|
|
12
|
+
// kb:ready, records every kb:* message the shell really posted, and counts
|
|
13
|
+
// how often its document was really served — a rebuilt task is a served
|
|
14
|
+
// document. The substituted part is the module's own UI, not the shell.
|
|
15
|
+
//
|
|
16
|
+
// Screenshots are written to PK_TEST_EVIDENCE_DIR when the batch sets it, so
|
|
17
|
+
// this tracked file stays free of machine-specific paths.
|
|
18
|
+
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const assert = require('node:assert/strict');
|
|
21
|
+
|
|
22
|
+
const { startHarness, makeRepo } = require('./helpers/parallel-nav-shell');
|
|
23
|
+
|
|
24
|
+
const EVIDENCE = process.env.PK_TEST_EVIDENCE_DIR ? path.resolve(process.env.PK_TEST_EVIDENCE_DIR) : null;
|
|
25
|
+
|
|
26
|
+
const FRAMES = [
|
|
27
|
+
{ key: 'terminal', id: 'workFrame', view: 'work' },
|
|
28
|
+
{ key: 'vectorHub', id: 'vectorFrame', view: 'search' },
|
|
29
|
+
{ key: 'eventBridge', id: 'bridgeFrame', view: 'activity' },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
async function main() {
|
|
33
|
+
const harness = await startHarness({ tag: 'nav002', evidenceDir: EVIDENCE || undefined });
|
|
34
|
+
try {
|
|
35
|
+
const repo = makeRepo(harness.fixture, 'nav002-project');
|
|
36
|
+
await harness.importProject(repo, 'NAV002 项目');
|
|
37
|
+
|
|
38
|
+
// The shell boots on its own registry read.
|
|
39
|
+
await harness.waitFor(
|
|
40
|
+
() => harness.run("document.getElementById('homeContent').textContent.trim().length > 0"),
|
|
41
|
+
'the shell reads the registry',
|
|
42
|
+
20000,
|
|
43
|
+
);
|
|
44
|
+
await harness.requestRegistryRefresh();
|
|
45
|
+
await harness.waitFor(
|
|
46
|
+
() => harness.run("document.getElementById('homeContent').textContent.includes('NAV002 项目')"),
|
|
47
|
+
'the imported project is listed',
|
|
48
|
+
20000,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const shellTheme = () => harness.run("document.documentElement.dataset.theme");
|
|
52
|
+
const storedTheme = () => harness.run("localStorage.getItem('kb.theme')");
|
|
53
|
+
|
|
54
|
+
// NAV-002 expected 1: the theme persists.
|
|
55
|
+
assert.equal(await shellTheme(), 'light', 'the shell starts in the light theme');
|
|
56
|
+
assert.equal(await storedTheme(), null, 'no theme is stored before the first switch');
|
|
57
|
+
await harness.nativeClick('#themeToggle');
|
|
58
|
+
await harness.waitFor(async () => (await shellTheme()) === 'dark', 'the shell switches to the dark theme');
|
|
59
|
+
assert.equal(await storedTheme(), 'dark', 'the dark theme is persisted');
|
|
60
|
+
await harness.browser.screenshot(path.join(harness.evidence, 'nav002-theme-dark.png'));
|
|
61
|
+
|
|
62
|
+
// NAV-002 expected 2: a ready frame receives kb:theme.
|
|
63
|
+
// The three embedded modules are opened one after another; each frame has
|
|
64
|
+
// to be ready (the shell only talks to a frame that announced itself).
|
|
65
|
+
for (const frame of FRAMES) {
|
|
66
|
+
await harness.run(`document.querySelector('[data-view="${frame.view}"]').click()`);
|
|
67
|
+
if (frame.key === 'eventBridge') {
|
|
68
|
+
await harness.run("document.querySelector('[data-activity-tab=\"sessions\"]').click()");
|
|
69
|
+
}
|
|
70
|
+
await harness.waitFor(
|
|
71
|
+
async () => {
|
|
72
|
+
const messages = await harness.frameMessages(frame.id);
|
|
73
|
+
return Array.isArray(messages) && messages.some(message => message.type === 'kb:theme');
|
|
74
|
+
},
|
|
75
|
+
`the ${frame.key} frame announces itself and receives kb:theme`,
|
|
76
|
+
20000,
|
|
77
|
+
);
|
|
78
|
+
const before = await harness.frameMessages(frame.id);
|
|
79
|
+
const themesBefore = before.filter(message => message.type === 'kb:theme').map(message => message.theme);
|
|
80
|
+
const currentTheme = await shellTheme();
|
|
81
|
+
assert.ok(themesBefore.includes(currentTheme), `the ${frame.key} frame is told the current theme when it becomes ready: ${JSON.stringify(themesBefore)} (shell theme ${currentTheme})`);
|
|
82
|
+
|
|
83
|
+
// Switch the theme while this frame is ready and connected.
|
|
84
|
+
const themeBeforeSwitch = await shellTheme();
|
|
85
|
+
const expected = themeBeforeSwitch === 'dark' ? 'light' : 'dark';
|
|
86
|
+
await harness.nativeClick('#themeToggle');
|
|
87
|
+
await harness.waitFor(async () => {
|
|
88
|
+
const messages = await harness.frameMessages(frame.id);
|
|
89
|
+
return Array.isArray(messages) && messages.some(message => message.type === 'kb:theme' && message.theme === expected);
|
|
90
|
+
}, `the ${frame.key} frame receives the new kb:theme`, 10000);
|
|
91
|
+
const after = (await harness.frameMessages(frame.id)).filter(message => message.type === 'kb:theme').map(message => message.theme);
|
|
92
|
+
assert.equal(after[after.length - 1], expected, `the ${frame.key} frame's newest kb:theme is the new theme: ${JSON.stringify(after)}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// NAV-002 expected 3: a theme switch never rebuilds the module task.
|
|
96
|
+
const loadsBefore = harness.modulePageLoads();
|
|
97
|
+
await harness.nativeClick('#themeToggle');
|
|
98
|
+
await harness.waitFor(async () => (await storedTheme()) === (await shellTheme()), 'the switched theme is persisted');
|
|
99
|
+
await harness.wait(500);
|
|
100
|
+
assert.equal(harness.modulePageLoads(), loadsBefore, 'a theme switch does not reload an embedded module document');
|
|
101
|
+
const registrations = harness.moduleUi.state.requests.filter(entry => entry.method === 'POST' && entry.path === '/api/claude-workbench/v1/projects').length;
|
|
102
|
+
await harness.nativeClick('#themeToggle');
|
|
103
|
+
await harness.wait(500);
|
|
104
|
+
assert.equal(
|
|
105
|
+
harness.moduleUi.state.requests.filter(entry => entry.method === 'POST' && entry.path === '/api/claude-workbench/v1/projects').length,
|
|
106
|
+
registrations,
|
|
107
|
+
'a theme switch does not re-register the project with the module',
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// The persisted theme survives a real reload of the product page.
|
|
111
|
+
const themeBeforeReload = await shellTheme();
|
|
112
|
+
await harness.browser.send('Page.reload');
|
|
113
|
+
await harness.waitFor(
|
|
114
|
+
async () => (await harness.run("document.documentElement.dataset.theme")) === themeBeforeReload && (await harness.run("document.readyState")) === 'complete',
|
|
115
|
+
'the reloaded shell restores the persisted theme',
|
|
116
|
+
20000,
|
|
117
|
+
);
|
|
118
|
+
assert.equal(await shellTheme(), themeBeforeReload, 'the theme is restored from localStorage after a reload');
|
|
119
|
+
assert.equal(await storedTheme(), themeBeforeReload, 'the persisted theme is unchanged by the reload');
|
|
120
|
+
await harness.browser.screenshot(path.join(harness.evidence, 'nav002-theme-after-reload.png'));
|
|
121
|
+
|
|
122
|
+
assert.equal(harness.browser.exceptions.length, 0, `no uncaught product JavaScript exceptions: ${JSON.stringify(harness.browser.exceptions)}`);
|
|
123
|
+
|
|
124
|
+
console.log('parallel-nav-theme-test PASS (theme persisted, ready frames receive kb:theme, no module task rebuilt)');
|
|
125
|
+
} finally {
|
|
126
|
+
await harness.cleanup();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
main().catch(error => { console.error(error.stack || error.message); process.exitCode = 1; });
|