@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,301 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// _site/_test/parallel-memory-candidate-test.js
|
|
3
|
+
//
|
|
4
|
+
// MEM-003 候选记忆 / MEM-005 忽略候选.
|
|
5
|
+
//
|
|
6
|
+
// Both cases run against a real isolated product instance with the REAL
|
|
7
|
+
// vendored knowledge engine behind a pass-through proxy: its own port, data
|
|
8
|
+
// dir, knowledge root, Bridge journal home and browser profile.
|
|
9
|
+
//
|
|
10
|
+
// The candidate is produced by the REAL pipeline: a real follow-up commit in a
|
|
11
|
+
// real Git fixture, a real frozen user event, the real promotion of a staged
|
|
12
|
+
// Markdown file and the engine's own MemoryService.capture() with its own
|
|
13
|
+
// provenance gate. The review happens in the real UI in a real Chrome.
|
|
14
|
+
//
|
|
15
|
+
// HONEST SCOPE LABEL: the analysis step is this lane's declared stand-in
|
|
16
|
+
// analyzer (helpers/parallel-memory-analyzer.js). It replaces the model, so
|
|
17
|
+
// these cases are evidence about the engine's admission, review, scope and
|
|
18
|
+
// injection machinery, never about the model's judgement.
|
|
19
|
+
//
|
|
20
|
+
// MEM-003 expected:
|
|
21
|
+
// * 保存为candidate。 -> the memory the engine really stored has status
|
|
22
|
+
// 'candidate', no manual source and a frozen
|
|
23
|
+
// source record;
|
|
24
|
+
// * 未确认不注入。 -> before the user confirms it the engine injects
|
|
25
|
+
// nothing for that memory, and the real UI lists
|
|
26
|
+
// it in 候选箱 with 审核确认 / 忽略.
|
|
27
|
+
// MEM-005 expected:
|
|
28
|
+
// * 状态ignored。 -> the real PATCH answers with status 'ignored';
|
|
29
|
+
// * 来源保持记录(冻结)。 -> the ignored record keeps the same commit, entry,
|
|
30
|
+
// quote, source event and eligibility, and injects
|
|
31
|
+
// nothing.
|
|
32
|
+
|
|
33
|
+
const fs = require('node:fs');
|
|
34
|
+
const path = require('node:path');
|
|
35
|
+
const assert = require('node:assert/strict');
|
|
36
|
+
|
|
37
|
+
const { startHarness, makeRepo } = require('./helpers/parallel-memory-shell');
|
|
38
|
+
|
|
39
|
+
const EVIDENCE = process.env.PK_TEST_EVIDENCE_DIR ? path.resolve(process.env.PK_TEST_EVIDENCE_DIR) : null;
|
|
40
|
+
const CANDIDATE_TEXT = '对外错误信息使用中文表述,并优先复用已有模块。';
|
|
41
|
+
const IGNORED_TEXT = '新增对外接口必须补充中文字段说明文档。';
|
|
42
|
+
|
|
43
|
+
async function openMemoryView(harness) {
|
|
44
|
+
await harness.nativeClick('nav [data-view="memory"]');
|
|
45
|
+
await harness.waitFor(
|
|
46
|
+
() => harness.run("document.getElementById('view-memory').classList.contains('active')"),
|
|
47
|
+
'the memory view is open',
|
|
48
|
+
10000,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function openCandidateBox(harness) {
|
|
53
|
+
await openMemoryView(harness);
|
|
54
|
+
await harness.nativeClick('[data-memory-tab="candidate"]');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function cardText(harness, memoryId) {
|
|
58
|
+
return harness.run(`(() => {
|
|
59
|
+
const el = document.querySelector('[data-memory-card=${JSON.stringify(memoryId)}]');
|
|
60
|
+
return el ? el.textContent : null;
|
|
61
|
+
})()`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function main() {
|
|
65
|
+
const harness = await startHarness({ tag: 'memcand', evidenceDir: EVIDENCE || undefined });
|
|
66
|
+
const evidence = [];
|
|
67
|
+
try {
|
|
68
|
+
const repo = makeRepo(harness.fixture, 'memcand-repo', { 'README.md': '# 候选甲\n' });
|
|
69
|
+
const { projectId } = await harness.importProject(repo, '候选甲');
|
|
70
|
+
await harness.requestRegistryRefresh();
|
|
71
|
+
await harness.waitFor(
|
|
72
|
+
() => harness.run("document.getElementById('homeContent').textContent.includes('候选甲')"),
|
|
73
|
+
'the imported project is listed',
|
|
74
|
+
20000,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------- MEM-003
|
|
78
|
+
const produced = await harness.produceCandidate({ repo, projectId, text: CANDIDATE_TEXT, scopeType: 'global' });
|
|
79
|
+
assert.equal(produced.capture.accepted, 1, `the real run admitted one candidate: ${JSON.stringify(produced.capture)}`);
|
|
80
|
+
assert.deepEqual(produced.capture.rejected, [], 'the real gate rejected nothing for a human source');
|
|
81
|
+
|
|
82
|
+
const stored = (await harness.productMemories()).find(memory => memory.text === CANDIDATE_TEXT);
|
|
83
|
+
assert.ok(stored, 'the candidate really exists in the engine');
|
|
84
|
+
assert.equal(stored.status, 'candidate', 'the automatic memory is saved as a candidate');
|
|
85
|
+
assert.notEqual(stored.sourceType, 'manual', 'an automatic candidate is not a manual memory');
|
|
86
|
+
assert.equal(stored.scopeType, 'global', 'a global candidate keeps the global scope');
|
|
87
|
+
assert.equal(stored.scopeId, stored.brainId, 'a global candidate targets the brain itself');
|
|
88
|
+
assert.equal(stored.evidence.projectId, projectId, 'the frozen source names the source project');
|
|
89
|
+
assert.equal(stored.evidence.commitSha, produced.commitSha, 'the frozen source names the real commit');
|
|
90
|
+
assert.ok(stored.evidence.entryId, 'the frozen source names the promoted entry');
|
|
91
|
+
assert.ok(stored.evidence.quote, 'the frozen source keeps the quoted text');
|
|
92
|
+
assert.equal(stored.sourceEventId, produced.event.eventId, 'the frozen source names the human event');
|
|
93
|
+
assert.equal(stored.sourceHash, produced.event.contentHash, 'the frozen source keeps the event hash');
|
|
94
|
+
assert.equal(stored.eligibility.durable, true, 'the candidate records why it is durable');
|
|
95
|
+
assert.equal(stored.eligibility.crossProject, true, 'the candidate records why it crosses projects');
|
|
96
|
+
|
|
97
|
+
// The candidate is really injected nowhere before the user confirms it.
|
|
98
|
+
const beforeReview = await harness.injectedMemories(projectId);
|
|
99
|
+
assert.ok(
|
|
100
|
+
!beforeReview.some(entry => entry.memoryId === stored.id),
|
|
101
|
+
`an unconfirmed candidate is never injected: ${JSON.stringify(beforeReview)}`,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// The real UI lists it in 候选箱 with the review actions.
|
|
105
|
+
await harness.reload();
|
|
106
|
+
await openCandidateBox(harness);
|
|
107
|
+
await harness.waitFor(
|
|
108
|
+
() => harness.run(`document.getElementById('memoryContent').textContent.includes(${JSON.stringify(CANDIDATE_TEXT)})`),
|
|
109
|
+
'the candidate is listed in the candidate box',
|
|
110
|
+
15000,
|
|
111
|
+
);
|
|
112
|
+
const listed = await cardText(harness, stored.id);
|
|
113
|
+
assert.ok(listed, 'the candidate really has a card in the candidate box');
|
|
114
|
+
assert.match(listed, /来源/, 'the card names the source of the candidate');
|
|
115
|
+
assert.ok(listed.includes(produced.commitSha.slice(0, 8)), 'the card shows the frozen commit');
|
|
116
|
+
assert.ok(
|
|
117
|
+
await harness.run(`Boolean(document.querySelector('[data-memory-action="confirm"][data-memory-id=${JSON.stringify(stored.id)}]'))`),
|
|
118
|
+
'the candidate offers 审核确认',
|
|
119
|
+
);
|
|
120
|
+
assert.ok(
|
|
121
|
+
await harness.run(`Boolean(document.querySelector('[data-memory-action="ignore"][data-memory-id=${JSON.stringify(stored.id)}]'))`),
|
|
122
|
+
'the candidate offers 忽略',
|
|
123
|
+
);
|
|
124
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem003-candidate-box.png'));
|
|
125
|
+
|
|
126
|
+
// The step's own path first: edit the candidate and click 保存候选 WITHOUT
|
|
127
|
+
// confirming — the memory has to stay a candidate and stay un-injected.
|
|
128
|
+
const EDIT_SUFFIX = '(修订)';
|
|
129
|
+
await harness.nativeClick(`[data-memory-action="confirm"][data-memory-id="${stored.id}"]`);
|
|
130
|
+
await harness.waitFor(
|
|
131
|
+
() => harness.run("document.getElementById('memoryDialog').open === true"),
|
|
132
|
+
'the review dialog is open',
|
|
133
|
+
10000,
|
|
134
|
+
);
|
|
135
|
+
await harness.nativeClick('#memoryText');
|
|
136
|
+
await harness.pressKey('End', { code: 'End', keyCode: 35 });
|
|
137
|
+
await harness.typeText(EDIT_SUFFIX);
|
|
138
|
+
const editedBody = await harness.run("document.getElementById('memoryText').value");
|
|
139
|
+
assert.equal(editedBody, CANDIDATE_TEXT + EDIT_SUFFIX, 'the real edit reached the textarea');
|
|
140
|
+
|
|
141
|
+
await harness.nativeClick('#saveMemoryCandidate');
|
|
142
|
+
await harness.waitFor(
|
|
143
|
+
() => harness.run("document.getElementById('memoryDialog').open === false"),
|
|
144
|
+
'the dialog closes after saving the candidate',
|
|
145
|
+
15000,
|
|
146
|
+
);
|
|
147
|
+
const saveCandidatePatch = harness.network.find(entry => entry.method === 'PATCH' && entry.url.includes(`/api/engine/memories/${stored.id}`)).pop();
|
|
148
|
+
assert.ok(saveCandidatePatch, 'the shell really patched the candidate');
|
|
149
|
+
assert.deepEqual(Object.keys(saveCandidatePatch.body).sort(), ['scopeId', 'scopeType', 'text'], '保存候选 sends no status at all');
|
|
150
|
+
const saveCandidateBody = await harness.network.body(saveCandidatePatch);
|
|
151
|
+
assert.equal(saveCandidatePatch.status, 200, `保存候选 answers 200: ${JSON.stringify(saveCandidatePatch.body)}`);
|
|
152
|
+
assert.equal(saveCandidateBody.memory.status, 'candidate', '保存候选 keeps the memory a candidate');
|
|
153
|
+
assert.equal(saveCandidateBody.memory.text, CANDIDATE_TEXT + EDIT_SUFFIX, '保存候选 keeps the edited text');
|
|
154
|
+
assert.equal(saveCandidateBody.memory.originalText, CANDIDATE_TEXT, '保存候选 keeps the frozen original summary');
|
|
155
|
+
const saveToast = await harness.run("document.getElementById('toast').textContent");
|
|
156
|
+
assert.match(String(saveToast), /候选已保存,尚未生效。/, `the toast says the candidate is not yet effective: ${saveToast}`);
|
|
157
|
+
|
|
158
|
+
const afterSaveCandidate = await harness.injectedMemories(projectId);
|
|
159
|
+
assert.ok(
|
|
160
|
+
!afterSaveCandidate.some(entry => entry.memoryId === stored.id),
|
|
161
|
+
`a saved-but-unconfirmed candidate is never injected: ${JSON.stringify(afterSaveCandidate)}`,
|
|
162
|
+
);
|
|
163
|
+
const stillCandidate = (await harness.productMemories()).find(memory => memory.id === stored.id);
|
|
164
|
+
assert.equal(stillCandidate.status, 'candidate', 'the stored memory is still a candidate after 保存候选');
|
|
165
|
+
await harness.waitFor(
|
|
166
|
+
() => harness.run(`document.getElementById('memoryContent').textContent.includes(${JSON.stringify(CANDIDATE_TEXT + EDIT_SUFFIX)})`),
|
|
167
|
+
'the edited candidate is listed in the candidate box',
|
|
168
|
+
15000,
|
|
169
|
+
);
|
|
170
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem003-saved-candidate.png'));
|
|
171
|
+
|
|
172
|
+
// 审核确认: the dialog shows the frozen source, and confirming confirms it.
|
|
173
|
+
await harness.nativeClick(`[data-memory-action="confirm"][data-memory-id="${stored.id}"]`);
|
|
174
|
+
await harness.waitFor(
|
|
175
|
+
() => harness.run("document.getElementById('memoryDialog').open === true"),
|
|
176
|
+
'the review dialog is open',
|
|
177
|
+
10000,
|
|
178
|
+
);
|
|
179
|
+
const shownEvidence = await harness.run("document.getElementById('memoryEvidence').textContent");
|
|
180
|
+
assert.ok(shownEvidence.includes(produced.commitSha), `the dialog shows the frozen commit: ${shownEvidence}`);
|
|
181
|
+
assert.ok(shownEvidence.includes(stored.evidence.entryId), 'the dialog shows the frozen entry');
|
|
182
|
+
const shownQuote = await harness.run("document.getElementById('memoryQuote').textContent");
|
|
183
|
+
assert.ok(shownQuote.includes(stored.evidence.quote), 'the dialog shows the frozen quote');
|
|
184
|
+
assert.ok(
|
|
185
|
+
await harness.run("document.getElementById('memoryEvidenceDetails').hidden === false"),
|
|
186
|
+
'the frozen source is really visible for a candidate',
|
|
187
|
+
);
|
|
188
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem003-review-dialog.png'));
|
|
189
|
+
|
|
190
|
+
await harness.nativeClick('#confirmMemory');
|
|
191
|
+
await harness.waitFor(
|
|
192
|
+
() => harness.run("document.getElementById('memoryDialog').open === false"),
|
|
193
|
+
'the dialog closes after the confirmation',
|
|
194
|
+
15000,
|
|
195
|
+
);
|
|
196
|
+
const patch = harness.network.find(entry => entry.method === 'PATCH' && entry.url.includes('/api/engine/memories/')).pop();
|
|
197
|
+
assert.ok(patch, 'the shell really patched the candidate');
|
|
198
|
+
assert.equal(patch.status, 200, `the confirmation answers 200: ${JSON.stringify(patch.body)}`);
|
|
199
|
+
const patchBody = await harness.network.body(patch);
|
|
200
|
+
assert.ok(patchBody && patchBody.memory, `the confirmation answers with the memory: ${JSON.stringify(patchBody)}`);
|
|
201
|
+
assert.equal(patchBody.memory.status, 'confirmed', 'the confirmed memory is confirmed');
|
|
202
|
+
assert.equal(patchBody.memory.evidence.commitSha, produced.commitSha, 'the confirmation keeps the frozen source');
|
|
203
|
+
|
|
204
|
+
const afterConfirm = await harness.injectedMemories(projectId);
|
|
205
|
+
assert.ok(
|
|
206
|
+
afterConfirm.some(entry => entry.memoryId === stored.id),
|
|
207
|
+
`the confirmed memory is injected: ${JSON.stringify(afterConfirm)}`,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
// The confirmed memory is listed in the confirmed tab of the real UI.
|
|
211
|
+
await harness.nativeClick('[data-memory-tab="confirmed"]');
|
|
212
|
+
await harness.waitFor(
|
|
213
|
+
() => harness.run(`document.getElementById('memoryContent').textContent.includes(${JSON.stringify(CANDIDATE_TEXT)})`),
|
|
214
|
+
'the confirmed memory is listed',
|
|
215
|
+
15000,
|
|
216
|
+
);
|
|
217
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem003-confirmed.png'));
|
|
218
|
+
|
|
219
|
+
evidence.push({
|
|
220
|
+
case: 'MEM-003',
|
|
221
|
+
run: { commitSha: produced.commitSha, memoryCapture: produced.capture },
|
|
222
|
+
candidate: { id: stored.id, status: stored.status, scopeType: stored.scopeType, scopeId: stored.scopeId, evidence: stored.evidence, sourceEventId: stored.sourceEventId, sourceHash: stored.sourceHash, eligibility: stored.eligibility },
|
|
223
|
+
saveCandidateWithoutConfirm: { method: saveCandidatePatch.method, url: saveCandidatePatch.url.replace(harness.base, ''), status: saveCandidatePatch.status, request: saveCandidatePatch.body, response: saveCandidateBody },
|
|
224
|
+
injectedBeforeReview: beforeReview.map(entry => entry.memoryId),
|
|
225
|
+
confirmation: { method: patch.method, url: patch.url.replace(harness.base, ''), status: patch.status, request: patch.body, response: patchBody },
|
|
226
|
+
injectedAfterReview: afterConfirm.map(entry => entry.memoryId),
|
|
227
|
+
scopeLabel: 'honest: the analysis step is the lane stand-in analyzer, not a model',
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// ---------------------------------------------------------------- MEM-005
|
|
231
|
+
const second = await harness.produceCandidate({ repo, projectId, text: IGNORED_TEXT, scopeType: 'global', file: 'src/ignored.js' });
|
|
232
|
+
assert.equal(second.capture.accepted, 1, `the second real run admitted one candidate: ${JSON.stringify(second.capture)}`);
|
|
233
|
+
const toIgnore = (await harness.productMemories()).find(memory => memory.text === IGNORED_TEXT);
|
|
234
|
+
assert.ok(toIgnore, 'the second candidate really exists in the engine');
|
|
235
|
+
assert.equal(toIgnore.status, 'candidate', 'the second candidate starts as a candidate');
|
|
236
|
+
|
|
237
|
+
await harness.reload();
|
|
238
|
+
await openCandidateBox(harness);
|
|
239
|
+
await harness.waitFor(
|
|
240
|
+
() => harness.run(`document.getElementById('memoryContent').textContent.includes(${JSON.stringify(IGNORED_TEXT)})`),
|
|
241
|
+
'the second candidate is listed in the candidate box',
|
|
242
|
+
15000,
|
|
243
|
+
);
|
|
244
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem005-candidate-before-ignore.png'));
|
|
245
|
+
|
|
246
|
+
await harness.nativeClick(`[data-memory-action="ignore"][data-memory-id="${toIgnore.id}"]`);
|
|
247
|
+
await harness.waitFor(
|
|
248
|
+
() => harness.run(`!document.getElementById('memoryContent').textContent.includes(${JSON.stringify(IGNORED_TEXT)})`),
|
|
249
|
+
'the ignored candidate leaves the candidate box',
|
|
250
|
+
15000,
|
|
251
|
+
);
|
|
252
|
+
const ignorePatch = harness.network.find(entry => entry.method === 'PATCH' && entry.url.includes(`/api/engine/memories/${toIgnore.id}`)).pop();
|
|
253
|
+
assert.ok(ignorePatch, 'the shell really ignored the candidate');
|
|
254
|
+
assert.equal(ignorePatch.status, 200, `the ignore answers 200: ${JSON.stringify(ignorePatch.body)}`);
|
|
255
|
+
assert.deepEqual(ignorePatch.body, { status: 'ignored' }, 'the ignore only changes the status');
|
|
256
|
+
const ignoreBody = await harness.network.body(ignorePatch);
|
|
257
|
+
assert.equal(ignoreBody.memory.status, 'ignored', 'the ignored memory is ignored');
|
|
258
|
+
|
|
259
|
+
const ignored = (await harness.productMemories()).find(memory => memory.id === toIgnore.id);
|
|
260
|
+
assert.ok(ignored, 'the ignored memory is still stored');
|
|
261
|
+
assert.equal(ignored.status, 'ignored', 'the stored memory carries the ignored status');
|
|
262
|
+
assert.equal(ignored.evidence.commitSha, second.commitSha, 'the ignored memory keeps the frozen commit');
|
|
263
|
+
assert.equal(ignored.evidence.entryId, toIgnore.evidence.entryId, 'the ignored memory keeps the frozen entry');
|
|
264
|
+
assert.equal(ignored.evidence.quote, toIgnore.evidence.quote, 'the ignored memory keeps the frozen quote');
|
|
265
|
+
assert.equal(ignored.sourceEventId, toIgnore.sourceEventId, 'the ignored memory keeps the frozen source event');
|
|
266
|
+
assert.equal(ignored.sourceHash, toIgnore.sourceHash, 'the ignored memory keeps the frozen source hash');
|
|
267
|
+
assert.equal(ignored.eligibility.reason, toIgnore.eligibility.reason, 'the ignored memory keeps the recorded reason');
|
|
268
|
+
|
|
269
|
+
const afterIgnore = await harness.injectedMemories(projectId);
|
|
270
|
+
assert.ok(
|
|
271
|
+
!afterIgnore.some(entry => entry.memoryId === toIgnore.id),
|
|
272
|
+
`an ignored memory is never injected: ${JSON.stringify(afterIgnore)}`,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// The confirmed memory is untouched by the ignore of another candidate.
|
|
276
|
+
const stillConfirmed = (await harness.productMemories()).find(memory => memory.id === stored.id);
|
|
277
|
+
assert.equal(stillConfirmed.status, 'confirmed', 'ignoring one candidate does not change the confirmed memory');
|
|
278
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem005-candidate-after-ignore.png'));
|
|
279
|
+
|
|
280
|
+
evidence.push({
|
|
281
|
+
case: 'MEM-005',
|
|
282
|
+
run: { commitSha: second.commitSha, memoryCapture: second.capture },
|
|
283
|
+
ignored: { id: ignored.id, status: ignored.status, evidence: ignored.evidence, sourceEventId: ignored.sourceEventId, sourceHash: ignored.sourceHash, eligibility: ignored.eligibility },
|
|
284
|
+
ignoreRequest: { method: ignorePatch.method, url: ignorePatch.url.replace(harness.base, ''), status: ignorePatch.status, request: ignorePatch.body, response: ignoreBody },
|
|
285
|
+
injectedAfterIgnore: afterIgnore.map(entry => entry.memoryId),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
assert.equal(harness.browser.exceptions.length, 0, `no uncaught product JavaScript exceptions: ${JSON.stringify(harness.browser.exceptions)}`);
|
|
289
|
+
|
|
290
|
+
if (harness.evidence) {
|
|
291
|
+
fs.mkdirSync(harness.evidence, { recursive: true });
|
|
292
|
+
fs.writeFileSync(path.join(harness.evidence, 'mem003-mem005-candidate.json'), `${JSON.stringify(evidence, null, 2)}\n`, 'utf8');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
console.log('parallel-memory-candidate-test PASS (MEM-003 candidate saved and not injected before the review; MEM-005 ignored keeps the frozen source and injects nothing)');
|
|
296
|
+
} finally {
|
|
297
|
+
await harness.cleanup();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
main().catch(error => { console.error(error.stack || error.message); process.exitCode = 1; });
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// _site/_test/parallel-memory-manual-test.js
|
|
3
|
+
//
|
|
4
|
+
// MEM-001 手动添加 / MEM-002 空白拒绝.
|
|
5
|
+
//
|
|
6
|
+
// Every step runs against a real isolated product instance: its own port,
|
|
7
|
+
// data dir, knowledge root, Bridge journal home and browser profile, with the
|
|
8
|
+
// REAL vendored knowledge engine in-process behind a pass-through proxy. The
|
|
9
|
+
// memory is added through the real UI in a real Chrome: a real pointer click
|
|
10
|
+
// opens the dialog, real key events type the content, a real pointer click
|
|
11
|
+
// submits it.
|
|
12
|
+
//
|
|
13
|
+
// MEM-001 expected:
|
|
14
|
+
// * POST真实保存为confirmed。 -> the captured POST really answers
|
|
15
|
+
// status 'confirmed', sourceType
|
|
16
|
+
// 'manual', evidence null;
|
|
17
|
+
// * 刷新后保留。 -> after a real page reload the memory
|
|
18
|
+
// is still listed, still confirmed, and
|
|
19
|
+
// the memory file on disk still holds
|
|
20
|
+
// it.
|
|
21
|
+
// MEM-002 expected:
|
|
22
|
+
// * 前后端均拒绝。 -> the UI refuses a blank body without
|
|
23
|
+
// sending a request, and the engine
|
|
24
|
+
// itself rejects a blank body;
|
|
25
|
+
// * 不出现空记忆。 -> no memory exists anywhere afterwards.
|
|
26
|
+
|
|
27
|
+
const fs = require('node:fs');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const assert = require('node:assert/strict');
|
|
30
|
+
|
|
31
|
+
const { startHarness, makeRepo } = require('./helpers/parallel-memory-shell');
|
|
32
|
+
|
|
33
|
+
const EVIDENCE = process.env.PK_TEST_EVIDENCE_DIR ? path.resolve(process.env.PK_TEST_EVIDENCE_DIR) : null;
|
|
34
|
+
const MEMORY_TEXT = '对外错误信息使用中文表述,并优先复用已有模块。';
|
|
35
|
+
|
|
36
|
+
async function openMemoryView(harness) {
|
|
37
|
+
await harness.nativeClick('nav [data-view="memory"]');
|
|
38
|
+
await harness.waitFor(
|
|
39
|
+
() => harness.run("document.getElementById('view-memory').classList.contains('active')"),
|
|
40
|
+
'the memory view is open',
|
|
41
|
+
10000,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function main() {
|
|
46
|
+
const harness = await startHarness({ tag: 'mem001', evidenceDir: EVIDENCE || undefined });
|
|
47
|
+
const evidence = [];
|
|
48
|
+
try {
|
|
49
|
+
const repo = makeRepo(harness.fixture, 'mem001-repo', { 'README.md': '# 记忆甲\n' });
|
|
50
|
+
const { projectId } = await harness.importProject(repo, '记忆甲');
|
|
51
|
+
|
|
52
|
+
// The shell has to know the project before it can bind a project scope.
|
|
53
|
+
await harness.requestRegistryRefresh();
|
|
54
|
+
await harness.waitFor(
|
|
55
|
+
() => harness.run("document.getElementById('homeContent').textContent.includes('记忆甲')"),
|
|
56
|
+
'the imported project is listed',
|
|
57
|
+
20000,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
await openMemoryView(harness);
|
|
61
|
+
await harness.waitFor(
|
|
62
|
+
() => harness.run("!document.getElementById('addMemory').disabled"),
|
|
63
|
+
'the engine is reachable and the add button is live',
|
|
64
|
+
20000,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------- MEM-002
|
|
68
|
+
await harness.nativeClick('#addMemory');
|
|
69
|
+
await harness.waitFor(
|
|
70
|
+
() => harness.run("document.getElementById('memoryDialog').open === true"),
|
|
71
|
+
'the add dialog is open',
|
|
72
|
+
10000,
|
|
73
|
+
);
|
|
74
|
+
// Real key events into the really focused textarea.
|
|
75
|
+
await harness.typeText(' ');
|
|
76
|
+
const typed = await harness.run("document.getElementById('memoryText').value");
|
|
77
|
+
assert.equal(typed, ' ', 'the blank body really reached the textarea');
|
|
78
|
+
|
|
79
|
+
const postsBefore = harness.network.find(entry => entry.method === 'POST' && entry.url.includes('/api/engine/memories')).length;
|
|
80
|
+
await harness.nativeClick('#confirmMemory');
|
|
81
|
+
await harness.waitFor(
|
|
82
|
+
() => harness.run("document.getElementById('memoryFeedback').hidden === false"),
|
|
83
|
+
'the shell refuses the blank body in the dialog',
|
|
84
|
+
10000,
|
|
85
|
+
);
|
|
86
|
+
const feedback = await harness.run("document.getElementById('memoryFeedback').textContent");
|
|
87
|
+
assert.match(String(feedback), /请填写记忆内容。/, 'the shell names the missing content');
|
|
88
|
+
const postsAfter = harness.network.find(entry => entry.method === 'POST' && entry.url.includes('/api/engine/memories')).length;
|
|
89
|
+
assert.equal(postsAfter, postsBefore, 'the shell must not send a request for a blank body');
|
|
90
|
+
|
|
91
|
+
// The backend refuses the same blank body on its own.
|
|
92
|
+
const backendBlank = await harness.productApi('POST', '/api/engine/memories', { text: ' ', scopeType: 'global', scopeId: 'brain-default', brainId: 'brain-default' });
|
|
93
|
+
assert.equal(backendBlank.status, 400, `the engine refuses a blank body: ${JSON.stringify(backendBlank.body)}`);
|
|
94
|
+
assert.equal(backendBlank.body.error, 'INVALID_ARGUMENT', 'the engine reports an invalid argument');
|
|
95
|
+
assert.match(String(backendBlank.body.message), /记忆内容必须为 1–2000 个字符。/, 'the engine names the length rule');
|
|
96
|
+
|
|
97
|
+
const afterBlank = await harness.productMemories();
|
|
98
|
+
assert.deepEqual(afterBlank, [], 'a refused blank body creates no memory at all');
|
|
99
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem002-blank-refused.png'));
|
|
100
|
+
|
|
101
|
+
evidence.push({
|
|
102
|
+
case: 'MEM-002',
|
|
103
|
+
frontEnd: { feedback, requestsSent: postsAfter - postsBefore },
|
|
104
|
+
backEnd: { status: backendBlank.status, body: backendBlank.body },
|
|
105
|
+
memoriesAfter: afterBlank.length,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------- MEM-001
|
|
109
|
+
// The same dialog now carries a real body. The rejected submit left the
|
|
110
|
+
// focus on the confirm button, so the real user clicks back into the field.
|
|
111
|
+
await harness.nativeClick('#memoryText');
|
|
112
|
+
await harness.pressBackspace(3);
|
|
113
|
+
await harness.typeText(MEMORY_TEXT);
|
|
114
|
+
const body = await harness.run("document.getElementById('memoryText').value");
|
|
115
|
+
assert.equal(body, MEMORY_TEXT, 'the real body reached the textarea');
|
|
116
|
+
|
|
117
|
+
await harness.nativeClick('#confirmMemory');
|
|
118
|
+
await harness.waitFor(
|
|
119
|
+
() => harness.run("document.getElementById('memoryDialog').open === false"),
|
|
120
|
+
'the dialog closes after a confirmed save',
|
|
121
|
+
15000,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const post = harness.network.find(entry => entry.method === 'POST' && entry.url.includes('/api/engine/memories')).pop();
|
|
125
|
+
assert.ok(post, 'the shell really posted a memory');
|
|
126
|
+
assert.equal(post.status, 200, `the real POST answers 200: ${JSON.stringify(post.body)}`);
|
|
127
|
+
const postBody = await harness.network.body(post);
|
|
128
|
+
assert.ok(postBody && postBody.memory, `the real POST answers with the memory: ${JSON.stringify(postBody)}`);
|
|
129
|
+
const created = postBody.memory;
|
|
130
|
+
assert.equal(created.status, 'confirmed', 'the memory is saved as confirmed');
|
|
131
|
+
assert.equal(created.sourceType, 'manual', 'a manual memory has no model source');
|
|
132
|
+
assert.equal(created.evidence, null, 'a manual memory invents no evidence');
|
|
133
|
+
assert.equal(created.text, MEMORY_TEXT, 'the saved text is the text that was typed');
|
|
134
|
+
assert.equal(created.scopeType, 'project', 'the default scope of an active project is the project itself');
|
|
135
|
+
assert.equal(created.scopeId, projectId, 'the scope target is the active project');
|
|
136
|
+
assert.ok(created.id, 'the memory has an id');
|
|
137
|
+
|
|
138
|
+
// The memory is listed in the real UI.
|
|
139
|
+
await harness.waitFor(
|
|
140
|
+
() => harness.run(`document.getElementById('memoryContent').textContent.includes(${JSON.stringify(MEMORY_TEXT)})`),
|
|
141
|
+
'the new memory is listed',
|
|
142
|
+
15000,
|
|
143
|
+
);
|
|
144
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem001-confirmed.png'));
|
|
145
|
+
|
|
146
|
+
// The memory file really holds the memory.
|
|
147
|
+
const brainScope = harness.brainScopePath();
|
|
148
|
+
const memoryFile = path.join(brainScope, 'memories', 'projects', `${projectId}.md`);
|
|
149
|
+
assert.ok(fs.existsSync(memoryFile), `the memory file exists: ${memoryFile}`);
|
|
150
|
+
const fileText = fs.readFileSync(memoryFile, 'utf8');
|
|
151
|
+
assert.ok(fileText.includes(created.id), 'the memory file names the memory');
|
|
152
|
+
assert.ok(fileText.includes(MEMORY_TEXT), 'the memory file holds the text');
|
|
153
|
+
assert.match(fileText, /来源:手动添加(用户确认)/, 'the memory file records the manual source');
|
|
154
|
+
|
|
155
|
+
// A confirmed, applicable memory is injected into the task.
|
|
156
|
+
const injected = await harness.injectedMemories(projectId);
|
|
157
|
+
assert.ok(injected.some(memory => memory.memoryId === created.id), `the confirmed memory is injected: ${JSON.stringify(injected)}`);
|
|
158
|
+
|
|
159
|
+
// 刷新后保留: a real reload of the real page.
|
|
160
|
+
await harness.reload();
|
|
161
|
+
await harness.waitFor(
|
|
162
|
+
() => harness.run(`document.getElementById('memoryContent').textContent.includes(${JSON.stringify(MEMORY_TEXT)})`),
|
|
163
|
+
'the memory is still listed after a real reload',
|
|
164
|
+
25000,
|
|
165
|
+
);
|
|
166
|
+
if (harness.evidence) await harness.browser.screenshot(path.join(harness.evidence, 'mem001-after-reload.png'));
|
|
167
|
+
const afterReload = await harness.productMemories();
|
|
168
|
+
const kept = afterReload.find(memory => memory.id === created.id);
|
|
169
|
+
assert.ok(kept, 'the memory survives the reload');
|
|
170
|
+
assert.equal(kept.status, 'confirmed', 'the memory is still confirmed after the reload');
|
|
171
|
+
assert.equal(kept.text, MEMORY_TEXT, 'the memory text survives the reload');
|
|
172
|
+
assert.equal(kept.scopeType, 'project', 'the scope survives the reload');
|
|
173
|
+
assert.equal(kept.scopeId, projectId, 'the scope target survives the reload');
|
|
174
|
+
assert.equal(kept.syncStatus, 'ready', 'the memory file is in sync');
|
|
175
|
+
|
|
176
|
+
evidence.push({
|
|
177
|
+
case: 'MEM-001',
|
|
178
|
+
request: { method: post.method, url: post.url.replace(harness.base, ''), status: post.status, request: post.body, response: postBody },
|
|
179
|
+
memoryFile: { path: memoryFile, sha256: require('node:crypto').createHash('sha256').update(fileText).digest('hex') },
|
|
180
|
+
afterReload: kept,
|
|
181
|
+
injected: injected.map(memory => memory.memoryId),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
assert.equal(harness.browser.exceptions.length, 0, `no uncaught product JavaScript exceptions: ${JSON.stringify(harness.browser.exceptions)}`);
|
|
185
|
+
|
|
186
|
+
if (harness.evidence) {
|
|
187
|
+
fs.mkdirSync(harness.evidence, { recursive: true });
|
|
188
|
+
fs.writeFileSync(path.join(harness.evidence, 'mem001-manual.json'), `${JSON.stringify(evidence, null, 2)}\n`, 'utf8');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
console.log(`parallel-memory-manual-test PASS (MEM-001 confirmed + survives a real reload; MEM-002 refused by the shell without a request and by the engine with 400)`);
|
|
192
|
+
} finally {
|
|
193
|
+
await harness.cleanup();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
main().catch(error => { console.error(error.stack || error.message); process.exitCode = 1; });
|