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