@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,219 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Offline regression for the RET-007 attribution contract.
|
|
3
|
+
//
|
|
4
|
+
// What is locked here: a promoted knowledge entry must carry the FULL commit
|
|
5
|
+
// SHA and the project it belongs to, a later session must retrieve that entry
|
|
6
|
+
// by a semantic query that contains NO identifier, and an unrelated hex query
|
|
7
|
+
// must not reach the entry through the exact-commit channel.
|
|
8
|
+
//
|
|
9
|
+
// What is NOT claimed here: the analyzer is a DECLARED STUB (no model is
|
|
10
|
+
// called) and the vector endpoint is a DECLARED STUB that returns no rows, so
|
|
11
|
+
// this file says nothing about model behaviour or about vector recall. The
|
|
12
|
+
// real-model chain and the real index live in
|
|
13
|
+
// qa-autopilot/tools/product-probes/parallel-retrieval-followup-ret007.js,
|
|
14
|
+
// which is deliberately outside the offline CI.
|
|
15
|
+
//
|
|
16
|
+
// Everything runs in isolation: own data directory, own knowledge root, own
|
|
17
|
+
// temporary repository, own HTTP ports, no ambient module address, no real
|
|
18
|
+
// HOME.
|
|
19
|
+
const assert = require('node:assert/strict');
|
|
20
|
+
const fs = require('node:fs');
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
const http = require('node:http');
|
|
23
|
+
const crypto = require('node:crypto');
|
|
24
|
+
|
|
25
|
+
const shell = require('./helpers/parallel-retrieval-followup-shell');
|
|
26
|
+
// Isolate before any product module is loaded.
|
|
27
|
+
shell.isolateProcess();
|
|
28
|
+
|
|
29
|
+
const { createEngineServer } = require('../../_modules/knowledge-engine/src/server');
|
|
30
|
+
const AtomicFile = require('../../_modules/knowledge-engine/src/runtime/atomic-file');
|
|
31
|
+
|
|
32
|
+
const TOKEN = 'RET007OFFLINE';
|
|
33
|
+
const results = [];
|
|
34
|
+
|
|
35
|
+
function record(caseId, status, detail) {
|
|
36
|
+
results.push({ caseId, status, detail });
|
|
37
|
+
console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function stubVectorServer() {
|
|
41
|
+
const requests = [];
|
|
42
|
+
const server = http.createServer((req, res) => {
|
|
43
|
+
let body = '';
|
|
44
|
+
req.on('data', chunk => { body += chunk; });
|
|
45
|
+
req.on('end', () => {
|
|
46
|
+
requests.push({ url: req.url, method: req.method });
|
|
47
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
48
|
+
res.end(JSON.stringify({ ok: true, results: [], errors: [], projects: [] }));
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
52
|
+
return {
|
|
53
|
+
requests,
|
|
54
|
+
url: `http://127.0.0.1:${server.address().port}`,
|
|
55
|
+
close: () => { server.closeAllConnections(); return new Promise(resolve => server.close(resolve)); },
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function forbiddenBridge() {
|
|
60
|
+
const guard = name => async () => { throw new Error(`event bridge must not be contacted from the test lane (${name})`); };
|
|
61
|
+
return {
|
|
62
|
+
registerConsumer: guard('registerConsumer'),
|
|
63
|
+
listJournals: guard('listJournals'),
|
|
64
|
+
readEvents: guard('readEvents'),
|
|
65
|
+
ackConsumerCursor: guard('ackConsumerCursor'),
|
|
66
|
+
close() {},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Declared stub analyzer: stages one Markdown file plus a manifest that
|
|
71
|
+
// carries the four required evidence references, exactly like the real
|
|
72
|
+
// analyzer's contract. It never contacts a model.
|
|
73
|
+
function stubAnalyzer(events = []) {
|
|
74
|
+
return {
|
|
75
|
+
sourceReadMode: 'isolated-frozen-evidence',
|
|
76
|
+
async runClaim(input) {
|
|
77
|
+
events.push({ kind: 'start', projectId: input.projectId, sha: input.claim.commitSha });
|
|
78
|
+
const relative = `changes/${input.claim.commitSha.slice(0, 12)}.md`;
|
|
79
|
+
const content = `# 离线归属条目\n\n${TOKEN} 事实:重试上限调整为 7 次,提交 ${input.claim.commitSha}。\n`;
|
|
80
|
+
AtomicFile.writeFileAtomic(path.join(input.stagingPath, 'files', ...relative.split('/')), content);
|
|
81
|
+
AtomicFile.writeJsonAtomic(input.manifestPath, {
|
|
82
|
+
schema: 'knowledge-staging-manifest/v1',
|
|
83
|
+
projectId: input.projectId,
|
|
84
|
+
runId: input.claim.runId,
|
|
85
|
+
commitSha: input.claim.commitSha,
|
|
86
|
+
operations: [{
|
|
87
|
+
path: relative,
|
|
88
|
+
operation: 'create',
|
|
89
|
+
sha256: `sha256:${crypto.createHash('sha256').update(content).digest('hex')}`,
|
|
90
|
+
reason: 'Frozen Git evidence',
|
|
91
|
+
evidenceReferences: [
|
|
92
|
+
`commit:${input.claim.commitSha}`,
|
|
93
|
+
`patch:${input.claim.patchHash}`,
|
|
94
|
+
`conversation:${input.claim.conversationSnapshotHash}`,
|
|
95
|
+
`retrieval:${input.claim.retrievalManifestHash}`,
|
|
96
|
+
],
|
|
97
|
+
}],
|
|
98
|
+
});
|
|
99
|
+
events.push({ kind: 'finish', projectId: input.projectId, sha: input.claim.commitSha });
|
|
100
|
+
return { ok: true };
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function startEngine({ dataDir, vectorUrl, analyzer }) {
|
|
106
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
107
|
+
const runtime = createEngineServer({ dataDir, vectorUrl, bridge: forbiddenBridge(), analyzer });
|
|
108
|
+
await runtime.ready;
|
|
109
|
+
await new Promise(resolve => runtime.server.listen(0, '127.0.0.1', resolve));
|
|
110
|
+
const base = `http://127.0.0.1:${runtime.server.address().port}`;
|
|
111
|
+
return {
|
|
112
|
+
...runtime,
|
|
113
|
+
base,
|
|
114
|
+
async call(route, body) {
|
|
115
|
+
const response = await fetch(`${base}${route}`, {
|
|
116
|
+
method: body === undefined ? 'GET' : 'POST',
|
|
117
|
+
headers: { 'Content-Type': 'application/json' },
|
|
118
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
119
|
+
});
|
|
120
|
+
return { status: response.status, body: await response.json() };
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
(async () => {
|
|
126
|
+
const root = shell.makeTempDir('parallel-retrieval-followup-citation');
|
|
127
|
+
const vector = await stubVectorServer();
|
|
128
|
+
const analyzerEvents = [];
|
|
129
|
+
let engine = null;
|
|
130
|
+
try {
|
|
131
|
+
const repo = shell.makeRepo(root, 'repo-citation', [
|
|
132
|
+
{ message: '初始提交', files: { 'README.md': '# citation\n' } },
|
|
133
|
+
{ message: '事实提交', files: { 'note.txt': `${TOKEN} 重试上限调整为 7 次。\n` } },
|
|
134
|
+
]);
|
|
135
|
+
const knowledgePath = shell.makeKnowledgeDir(root, 'knowledge-citation');
|
|
136
|
+
engine = await startEngine({
|
|
137
|
+
dataDir: path.join(root, 'engine-citation'),
|
|
138
|
+
vectorUrl: vector.url,
|
|
139
|
+
analyzer: stubAnalyzer(analyzerEvents),
|
|
140
|
+
});
|
|
141
|
+
const registration = await engine.call('/api/projects', {
|
|
142
|
+
projectId: 'project-citation',
|
|
143
|
+
name: 'citation',
|
|
144
|
+
repoPath: repo.repoPath,
|
|
145
|
+
knowledgePath,
|
|
146
|
+
brainId: 'brain-default',
|
|
147
|
+
domainId: 'domain-tools',
|
|
148
|
+
});
|
|
149
|
+
assert.equal(registration.status, 200, `the project registration failed: ${JSON.stringify(registration.body)}`);
|
|
150
|
+
|
|
151
|
+
const run = engine.coordinator.enqueue({ projectId: 'project-citation', commitSha: repo.head });
|
|
152
|
+
const settled = await shell.eventually(() => ['succeeded', 'failed'].includes(engine.store.getRun(run.runId).state), 20000);
|
|
153
|
+
assert.equal(settled, true, 'the run must reach a terminal state');
|
|
154
|
+
const finished = engine.store.getRun(run.runId);
|
|
155
|
+
assert.equal(finished.state, 'succeeded', `the promotion failed: ${JSON.stringify(finished.error)}`);
|
|
156
|
+
|
|
157
|
+
const commitSha = repo.head;
|
|
158
|
+
const entryRelative = `changes/${commitSha.slice(0, 12)}.md`;
|
|
159
|
+
const promoted = fs.readFileSync(path.join(knowledgePath, ...entryRelative.split('/')), 'utf8');
|
|
160
|
+
assert.ok(promoted.includes(TOKEN), 'the promoted body must carry the planted fact');
|
|
161
|
+
assert.ok(promoted.includes(commitSha), 'the promoted body must carry the full commit SHA');
|
|
162
|
+
|
|
163
|
+
// A later session asks with a semantic query that contains no identifier.
|
|
164
|
+
const semanticQuery = `${TOKEN} 重试上限调整为几次`;
|
|
165
|
+
assert.ok(!/[0-9a-f]{7}/i.test(semanticQuery), 'the semantic query must not contain a hex identifier');
|
|
166
|
+
|
|
167
|
+
const search = await engine.coordinator.retrieval.search({ query: semanticQuery, projectId: 'project-citation', client: 'workbench', sessionId: 'offline-session' });
|
|
168
|
+
const rows = search.results || [];
|
|
169
|
+
const hit = rows.find(row => row.commitSha === commitSha);
|
|
170
|
+
assert.ok(hit, `the promoted entry must be retrieved with its full SHA; rows: ${JSON.stringify(rows.map(row => ({ entry: row.entry_id, sha: row.commitSha, channels: row.match_channels })))}`);
|
|
171
|
+
assert.equal(hit.projectId, 'project-citation', 'the retrieved entry must keep its project');
|
|
172
|
+
assert.ok(String(hit.chunk_text || hit.snippet || '').includes(TOKEN), 'the retrieved entry must carry the planted body');
|
|
173
|
+
assert.ok(!(hit.match_channels || []).includes('exact-commit'), 'a semantic query must not reach the entry through the exact-commit channel');
|
|
174
|
+
|
|
175
|
+
const asked = await engine.coordinator.retrieval.ask({ query: semanticQuery, projectId: 'project-citation' });
|
|
176
|
+
const citation = (asked.citations || []).find(entry => entry.commitSha === commitSha);
|
|
177
|
+
assert.ok(citation, 'the answer must cite the promoted entry with its full SHA');
|
|
178
|
+
assert.equal(citation.projectId, 'project-citation', 'the citation must keep its project');
|
|
179
|
+
assert.ok(String(citation.text || '').includes(TOKEN), 'the citation must carry the planted body');
|
|
180
|
+
|
|
181
|
+
// Control: an unrelated hex query must not reach the entry.
|
|
182
|
+
const controlQuery = 'deadbeefcafe';
|
|
183
|
+
const control = await engine.coordinator.retrieval.search({ query: controlQuery, projectId: 'project-citation', client: 'workbench', sessionId: 'offline-session' });
|
|
184
|
+
const controlHit = (control.results || []).find(row => row.commitSha === commitSha);
|
|
185
|
+
assert.equal(controlHit, undefined, 'an unrelated hex query must not return the promoted entry');
|
|
186
|
+
assert.equal((control.results || []).some(row => (row.match_channels || []).includes('exact-commit')), false, 'an unrelated hex query must not use the exact-commit channel');
|
|
187
|
+
|
|
188
|
+
record('RET-007-CITE', 'PASS', {
|
|
189
|
+
commitSha,
|
|
190
|
+
entryRelative,
|
|
191
|
+
retrievedRows: rows.length,
|
|
192
|
+
hitEntryId: hit.entry_id,
|
|
193
|
+
hitChannels: hit.match_channels,
|
|
194
|
+
citationEntryId: citation.entryId,
|
|
195
|
+
controlRows: (control.results || []).length,
|
|
196
|
+
analyzer: 'declared stub analyzer (no model call)',
|
|
197
|
+
vector: 'declared stub returning no rows',
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const summary = {
|
|
201
|
+
lane: 'parallel-retrieval-followup',
|
|
202
|
+
file: 'parallel-retrieval-followup-citation-test.js',
|
|
203
|
+
cases: results,
|
|
204
|
+
model: 'declared stub analyzer (no model call)',
|
|
205
|
+
vector: 'declared stub returning no rows',
|
|
206
|
+
};
|
|
207
|
+
const target = process.env.PARALLEL_RETRIEVAL_EVIDENCE;
|
|
208
|
+
if (target) {
|
|
209
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
210
|
+
fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
|
|
211
|
+
}
|
|
212
|
+
console.log('parallel-retrieval-followup-citation-test PASS');
|
|
213
|
+
} finally {
|
|
214
|
+
if (engine) await engine.close();
|
|
215
|
+
await vector.close();
|
|
216
|
+
shell.removeTree(root);
|
|
217
|
+
shell.cleanupIsolatedProcess();
|
|
218
|
+
}
|
|
219
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Offline regression for the RET-008 harness contract.
|
|
3
|
+
//
|
|
4
|
+
// This file locks the pieces of the RET-008 comparison that do NOT need a
|
|
5
|
+
// model and do NOT need the vector-hub source checkout:
|
|
6
|
+
//
|
|
7
|
+
// 1) the fixed corpus and the human-labelled ground truth stay consistent
|
|
8
|
+
// (unique ids, every relevant document exists, no query carries a hex
|
|
9
|
+
// identifier, no query is a verbatim copy of a document sentence);
|
|
10
|
+
// 2) the ranking and latency helpers compute what the report claims;
|
|
11
|
+
// 3) the engine and the PINNED vector-hub package agree on the body hash,
|
|
12
|
+
// which is the precondition the engine needs before it may merge a
|
|
13
|
+
// vector-hub chunk row into a retrieved fact;
|
|
14
|
+
// 4) the PINNED package still has the identity the comparison was made
|
|
15
|
+
// against, and its query cache still BEHAVES the way the comparison
|
|
16
|
+
// assumed: an identical query is served from the cache, a failed query
|
|
17
|
+
// is evicted, and a late failure of an evicted owner cannot destroy the
|
|
18
|
+
// replacement cached for the same text. The behaviour is observed
|
|
19
|
+
// through the public API with deterministic counts; the source text of
|
|
20
|
+
// the pinned build is never matched.
|
|
21
|
+
//
|
|
22
|
+
// What is NOT claimed here: the embedder is a DECLARED STUB (a deterministic
|
|
23
|
+
// character-gram hashing vector, NOT a neural model), so this file says
|
|
24
|
+
// nothing about retrieval quality or recall. The real-model measurement
|
|
25
|
+
// (Xenova/bge-small-zh-v1.5) and the baseline/candidate comparison live in
|
|
26
|
+
// qa-autopilot/tools/product-probes/parallel-retrieval-followup-ret008.js,
|
|
27
|
+
// which is deliberately outside the offline CI.
|
|
28
|
+
//
|
|
29
|
+
// Everything runs in isolation: own temp root, own hub root, own ports, no
|
|
30
|
+
// ambient module address, no real HOME, no network.
|
|
31
|
+
const assert = require('node:assert/strict');
|
|
32
|
+
const fs = require('node:fs');
|
|
33
|
+
const path = require('node:path');
|
|
34
|
+
const crypto = require('node:crypto');
|
|
35
|
+
|
|
36
|
+
const shell = require('./helpers/parallel-retrieval-followup-shell');
|
|
37
|
+
// Isolate before any product module is loaded.
|
|
38
|
+
shell.isolateProcess();
|
|
39
|
+
|
|
40
|
+
const corpusDefinitions = require('./helpers/parallel-retrieval-followup-corpus');
|
|
41
|
+
const ranking = require('../../_modules/knowledge-engine/src/retrieval-ranking');
|
|
42
|
+
|
|
43
|
+
const HUB_DIST = path.join(shell.REPO_ROOT, 'node_modules', '@sanqianx', 'vector-hub', 'dist', 'cjs');
|
|
44
|
+
const DIMENSIONS = 512;
|
|
45
|
+
const results = [];
|
|
46
|
+
|
|
47
|
+
function record(caseId, status, detail) {
|
|
48
|
+
results.push({ caseId, status, detail });
|
|
49
|
+
console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// DECLARED STUB embedder: a deterministic character-gram hashing vector.
|
|
53
|
+
// It is not a model and makes no claim about semantic quality; it only gives
|
|
54
|
+
// the hub real numbers to store and compare. The counters separate the
|
|
55
|
+
// document embeddings (a batch) from the query embeddings (a single string),
|
|
56
|
+
// because the query cache is only observable on the single-string path.
|
|
57
|
+
function stubEmbedder() {
|
|
58
|
+
const state = { batchCalls: 0, singleCalls: 0, failNext: false, deferFor: null, pending: [] };
|
|
59
|
+
const vector = text => {
|
|
60
|
+
const out = new Array(DIMENSIONS).fill(0);
|
|
61
|
+
const source = String(text || '').toLowerCase();
|
|
62
|
+
for (let i = 0; i < source.length; i += 1) {
|
|
63
|
+
const gram = source.slice(i, i + 2);
|
|
64
|
+
const slot = crypto.createHash('sha256').update(gram).digest();
|
|
65
|
+
const index = slot.readUInt16BE(0) % DIMENSIONS;
|
|
66
|
+
out[index] += 1 + (slot[2] / 255);
|
|
67
|
+
}
|
|
68
|
+
const norm = Math.sqrt(out.reduce((sum, value) => sum + value * value, 0)) || 1;
|
|
69
|
+
return out.map(value => value / norm);
|
|
70
|
+
};
|
|
71
|
+
state.vector = vector;
|
|
72
|
+
const embedder = {
|
|
73
|
+
kind: 'declared-stub-hashing-embedder',
|
|
74
|
+
model: 'stub-hashing-embedder',
|
|
75
|
+
modelId: 'stub-hashing-embedder',
|
|
76
|
+
maxTokens: 500,
|
|
77
|
+
async createEmbeddings(inputs) {
|
|
78
|
+
if (Array.isArray(inputs)) {
|
|
79
|
+
state.batchCalls += 1;
|
|
80
|
+
return { status: 'success', output: inputs.map(vector) };
|
|
81
|
+
}
|
|
82
|
+
state.singleCalls += 1;
|
|
83
|
+
if (state.failNext) {
|
|
84
|
+
state.failNext = false;
|
|
85
|
+
return { status: 'failure', output: [] };
|
|
86
|
+
}
|
|
87
|
+
if (state.deferFor && state.deferFor(String(inputs))) {
|
|
88
|
+
return new Promise((resolve, reject) => { state.pending.push({ text: String(inputs), resolve, reject }); });
|
|
89
|
+
}
|
|
90
|
+
return { status: 'success', output: [vector(inputs)] };
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
return { embedder, state };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function waitUntil(predicate, timeoutMs = 5000, intervalMs = 20) {
|
|
97
|
+
const deadline = Date.now() + timeoutMs;
|
|
98
|
+
while (Date.now() < deadline) {
|
|
99
|
+
if (predicate()) return true;
|
|
100
|
+
await new Promise(resolve => setTimeout(resolve, intervalMs));
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
(async () => {
|
|
106
|
+
const root = shell.makeTempDir('parallel-retrieval-followup-corpus');
|
|
107
|
+
try {
|
|
108
|
+
// ------------------------------------------------------- definitions
|
|
109
|
+
const ids = corpusDefinitions.CORPUS.map(doc => doc.id);
|
|
110
|
+
assert.equal(new Set(ids).size, ids.length, 'every corpus id must be unique');
|
|
111
|
+
const files = corpusDefinitions.CORPUS.map(doc => doc.file);
|
|
112
|
+
assert.equal(new Set(files).size, files.length, 'every corpus file name must be unique');
|
|
113
|
+
for (const doc of corpusDefinitions.CORPUS) {
|
|
114
|
+
assert.ok(doc.content.trim().length > 0, `the corpus document ${doc.id} must not be empty`);
|
|
115
|
+
}
|
|
116
|
+
assert.ok(corpusDefinitions.QUERIES.length >= 5, 'the query set must be usable');
|
|
117
|
+
for (const definition of corpusDefinitions.QUERIES) {
|
|
118
|
+
assert.ok(definition.relevant.length > 0, `the query ${definition.id} must name at least one relevant document`);
|
|
119
|
+
for (const relevant of definition.relevant) {
|
|
120
|
+
assert.ok(ids.includes(relevant), `the query ${definition.id} names the unknown document ${relevant}`);
|
|
121
|
+
}
|
|
122
|
+
assert.ok(!/[0-9a-f]{7}/i.test(definition.query), `the query ${definition.id} must not carry a hex identifier`);
|
|
123
|
+
for (const doc of corpusDefinitions.CORPUS) {
|
|
124
|
+
assert.ok(!doc.content.includes(definition.query), `the query ${definition.id} is copied verbatim from ${doc.id}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
record('RET-008-DEFINITIONS', 'PASS', {
|
|
128
|
+
documents: corpusDefinitions.CORPUS.length,
|
|
129
|
+
queries: corpusDefinitions.QUERIES.length,
|
|
130
|
+
multiRelevantQueries: corpusDefinitions.QUERIES.filter(definition => definition.relevant.length > 1).map(definition => definition.id),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ------------------------------------------------------------ helpers
|
|
134
|
+
const ranked = corpusDefinitions.evaluateRanking(['b', 'a', 'c'], ['a', 'c']);
|
|
135
|
+
assert.equal(ranked.firstRelevantRank, 2, 'the first relevant rank must be reported');
|
|
136
|
+
assert.equal(ranked.reciprocalRank, 0.5, 'the reciprocal rank must be 1/2');
|
|
137
|
+
assert.equal(ranked.recallAt1, 0, 'nothing relevant is in the top 1');
|
|
138
|
+
assert.equal(ranked.recallAt5, 1, 'both relevant documents are within the top 5');
|
|
139
|
+
const empty = corpusDefinitions.evaluateRanking(['x'], ['a']);
|
|
140
|
+
assert.equal(empty.hit, false, 'a query without any relevant document must not count as a hit');
|
|
141
|
+
assert.equal(empty.reciprocalRank, 0, 'a query without any relevant document has no reciprocal rank');
|
|
142
|
+
const latency = corpusDefinitions.summariseLatency([10, 20, 30, 40, 100]);
|
|
143
|
+
assert.equal(latency.count, 5, 'every finite sample must be counted');
|
|
144
|
+
assert.equal(latency.min, 10, 'the minimum must be reported');
|
|
145
|
+
assert.equal(latency.max, 100, 'the maximum must be reported');
|
|
146
|
+
assert.equal(latency.p50, 30, 'the median must be reported');
|
|
147
|
+
assert.equal(corpusDefinitions.summariseLatency([]).p50, null, 'an empty sample has no percentile');
|
|
148
|
+
record('RET-008-METRICS', 'PASS', { ranked, latency });
|
|
149
|
+
|
|
150
|
+
// -------------------------------------------- engine/hub body hash
|
|
151
|
+
const corpusDir = path.join(root, 'corpus');
|
|
152
|
+
const corpusIndex = corpusDefinitions.materialiseCorpus(corpusDir);
|
|
153
|
+
assert.equal(fs.readdirSync(corpusDir).length, corpusDefinitions.CORPUS.length, 'every corpus document must be written');
|
|
154
|
+
assert.equal(corpusIndex.idForFile('quota-sampling.md'), 'quota-sampling', 'a result file must map back to its corpus id');
|
|
155
|
+
assert.equal(corpusIndex.idForFile('missing.md'), null, 'an unknown file must not map to a corpus id');
|
|
156
|
+
|
|
157
|
+
const { VectorHub } = require(path.join(HUB_DIST, 'core', 'hub.js'));
|
|
158
|
+
const hub = new VectorHub({ rootPath: path.join(root, 'hub') });
|
|
159
|
+
const corpusEmbedder = stubEmbedder();
|
|
160
|
+
hub.setEmbeddings(corpusEmbedder.embedder);
|
|
161
|
+
const tracked = await hub.syncFolder('ret008-offline', corpusDir);
|
|
162
|
+
assert.equal(tracked, corpusDefinitions.CORPUS.length, 'every corpus document must reach the index');
|
|
163
|
+
|
|
164
|
+
const response = await hub.search(corpusDefinitions.QUERIES[0].query, { maxResults: 10, resultLevel: 'chunk' });
|
|
165
|
+
assert.ok(response.results.length > 0, 'the chunk search must return rows');
|
|
166
|
+
const expectedHashes = new Map();
|
|
167
|
+
for (const doc of corpusDefinitions.CORPUS) {
|
|
168
|
+
const content = fs.readFileSync(path.join(corpusDir, doc.file), 'utf8');
|
|
169
|
+
expectedHashes.set(doc.file, ranking.bodyHash(content));
|
|
170
|
+
}
|
|
171
|
+
let checked = 0;
|
|
172
|
+
for (const row of response.results) {
|
|
173
|
+
const expected = expectedHashes.get(row.file);
|
|
174
|
+
assert.ok(expected, `the search returned an unknown document ${row.file}`);
|
|
175
|
+
assert.equal(row.bodyHash, expected, `the engine and the pinned hub must agree on the body hash of ${row.file}`);
|
|
176
|
+
assert.equal(row.scoreType, 'cosine', 'a chunk row must carry the cosine score type');
|
|
177
|
+
assert.ok(Number.isInteger(row.startPos) && Number.isInteger(row.endPos), 'a chunk row must carry integer positions');
|
|
178
|
+
checked += 1;
|
|
179
|
+
}
|
|
180
|
+
record('RET-008-BODY-HASH', 'PASS', { tracked, rowsChecked: checked, distinctFiles: new Set(response.results.map(row => row.file)).size });
|
|
181
|
+
|
|
182
|
+
// The pinned dependency must still be the one the RET-008 comparison was
|
|
183
|
+
// made against. A dependency change has to re-run the explicit probe.
|
|
184
|
+
// Only the PACKAGE IDENTITY is asserted here: the source text of the
|
|
185
|
+
// pinned build is never matched, because a string match against the
|
|
186
|
+
// implementation is not a behaviour.
|
|
187
|
+
const pinnedPackage = path.join(shell.REPO_ROOT, 'node_modules', '@sanqianx', 'vector-hub');
|
|
188
|
+
const pinnedManifest = JSON.parse(fs.readFileSync(path.join(pinnedPackage, 'package.json'), 'utf8'));
|
|
189
|
+
assert.equal(pinnedManifest.name, '@sanqianx/vector-hub', 'the pinned package identity changed');
|
|
190
|
+
assert.equal(
|
|
191
|
+
pinnedManifest.version,
|
|
192
|
+
'1.0.8-rc.8',
|
|
193
|
+
`the pinned @sanqianx/vector-hub is ${pinnedManifest.version} instead of 1.0.8-rc.8; re-run qa-autopilot/tools/product-probes/parallel-retrieval-followup-ret008.js`,
|
|
194
|
+
);
|
|
195
|
+
record('RET-008-PINNED-PACKAGE', 'PASS', { pinnedName: pinnedManifest.name, pinnedVersion: pinnedManifest.version });
|
|
196
|
+
|
|
197
|
+
// ---- real query-cache behaviour of the pinned package -----------------
|
|
198
|
+
// The RET-008 comparison assumed that the pinned hub caches the query
|
|
199
|
+
// embedding and that a late failure of an evicted owner cannot destroy
|
|
200
|
+
// the replacement cached for the same text. Both are checked here through
|
|
201
|
+
// the public API with DETERMINISTIC COUNTS. The embedder is the DECLARED
|
|
202
|
+
// STUB, so this case says nothing about neural semantic quality; it only
|
|
203
|
+
// says that the cache still behaves the way the comparison assumed.
|
|
204
|
+
const cacheEmbedder = stubEmbedder();
|
|
205
|
+
const cacheHub = new VectorHub({ rootPath: path.join(root, 'hub-cache') });
|
|
206
|
+
cacheHub.setEmbeddings(cacheEmbedder.embedder);
|
|
207
|
+
const cacheTracked = await cacheHub.syncFolder('ret008-cache', corpusDir);
|
|
208
|
+
assert.equal(cacheTracked, corpusDefinitions.CORPUS.length, 'every corpus document must reach the cache index');
|
|
209
|
+
const search = text => cacheHub.search(text, { maxResults: 10, resultLevel: 'chunk' });
|
|
210
|
+
|
|
211
|
+
const queryA = corpusDefinitions.QUERIES[0].query;
|
|
212
|
+
const queryB = corpusDefinitions.QUERIES[1].query;
|
|
213
|
+
const beforeFirst = cacheEmbedder.state.singleCalls;
|
|
214
|
+
await search(queryA);
|
|
215
|
+
const afterFirst = cacheEmbedder.state.singleCalls;
|
|
216
|
+
assert.ok(afterFirst > beforeFirst, 'the first query must be embedded');
|
|
217
|
+
await search(queryA);
|
|
218
|
+
const afterRepeated = cacheEmbedder.state.singleCalls;
|
|
219
|
+
assert.equal(afterRepeated, afterFirst, 'the identical query must be served from the query cache');
|
|
220
|
+
const beforeSecond = cacheEmbedder.state.singleCalls;
|
|
221
|
+
await search(queryB);
|
|
222
|
+
const afterDifferent = cacheEmbedder.state.singleCalls;
|
|
223
|
+
assert.ok(afterDifferent > beforeSecond, 'a different query must be embedded again');
|
|
224
|
+
record('RET-008-QUERY-CACHE-HIT', 'PASS', {
|
|
225
|
+
firstQueryEmbeddings: afterFirst - beforeFirst,
|
|
226
|
+
repeatedQueryEmbeddings: afterRepeated - afterFirst,
|
|
227
|
+
differentQueryEmbeddings: afterDifferent - beforeSecond,
|
|
228
|
+
documentEmbeddings: cacheEmbedder.state.batchCalls,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// A failed query must not survive in the cache.
|
|
232
|
+
const queryFail = `${queryB} 失败用例`;
|
|
233
|
+
cacheEmbedder.state.failNext = true;
|
|
234
|
+
await search(queryFail).catch(() => {});
|
|
235
|
+
const afterFailed = cacheEmbedder.state.singleCalls;
|
|
236
|
+
await search(queryFail).catch(() => {});
|
|
237
|
+
assert.ok(cacheEmbedder.state.singleCalls > afterFailed, 'a failed query must not stay cached');
|
|
238
|
+
record('RET-008-QUERY-CACHE-FAILURE-EVICTION', 'PASS', {
|
|
239
|
+
embeddingsAfterFailedQuery: afterFailed,
|
|
240
|
+
embeddingsAfterRetry: cacheEmbedder.state.singleCalls,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// The ownership guard: a late failure of an evicted owner must not
|
|
244
|
+
// destroy the replacement that was cached for the same text.
|
|
245
|
+
const ownerQuery = '归属校验用的查询文本';
|
|
246
|
+
cacheEmbedder.state.deferFor = text => text === ownerQuery;
|
|
247
|
+
const firstOwner = search(ownerQuery).catch(() => null);
|
|
248
|
+
assert.ok(await waitUntil(() => cacheEmbedder.state.pending.length === 1), 'the first owner was never created');
|
|
249
|
+
// The hub keeps 100 cached queries; 100 distinct queries evict the
|
|
250
|
+
// oldest one, which is the entry of the first owner.
|
|
251
|
+
for (let index = 0; index < 100; index += 1) await search(`驱逐查询 ${index}`);
|
|
252
|
+
const replacement = search(ownerQuery).catch(() => null);
|
|
253
|
+
assert.ok(await waitUntil(() => cacheEmbedder.state.pending.length === 2), 'the replacement owner was never created');
|
|
254
|
+
// The evicted owner now fails late. It must not delete the replacement.
|
|
255
|
+
cacheEmbedder.state.pending[0].reject(new Error('late failure of the evicted owner'));
|
|
256
|
+
await firstOwner;
|
|
257
|
+
const beforeThird = cacheEmbedder.state.singleCalls;
|
|
258
|
+
const thirdSearch = search(ownerQuery);
|
|
259
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
260
|
+
assert.equal(cacheEmbedder.state.pending.length, 2, 'the replacement must still be cached: no new query embedding was created');
|
|
261
|
+
assert.equal(cacheEmbedder.state.singleCalls, beforeThird, 'the replacement must still be cached: the embedder was called again');
|
|
262
|
+
cacheEmbedder.state.pending[1].resolve({ status: 'success', output: [cacheEmbedder.state.vector(ownerQuery)] });
|
|
263
|
+
const thirdResult = await thirdSearch;
|
|
264
|
+
cacheEmbedder.state.deferFor = null;
|
|
265
|
+
record('RET-008-QUERY-CACHE-OWNERSHIP', 'PASS', {
|
|
266
|
+
evictingQueries: 100,
|
|
267
|
+
embedderCallsAfterLateFailure: cacheEmbedder.state.singleCalls - beforeThird,
|
|
268
|
+
replacementRows: Array.isArray(thirdResult && thirdResult.results) ? thirdResult.results.length : null,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const summary = {
|
|
272
|
+
lane: 'parallel-retrieval-followup',
|
|
273
|
+
file: 'parallel-retrieval-followup-corpus-test.js',
|
|
274
|
+
cases: results,
|
|
275
|
+
model: 'declared stub embedder (no model call)',
|
|
276
|
+
};
|
|
277
|
+
const target = process.env.PARALLEL_RETRIEVAL_EVIDENCE;
|
|
278
|
+
if (target) {
|
|
279
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
280
|
+
fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
|
|
281
|
+
}
|
|
282
|
+
console.log('parallel-retrieval-followup-corpus-test PASS');
|
|
283
|
+
} finally {
|
|
284
|
+
shell.removeTree(root);
|
|
285
|
+
shell.cleanupIsolatedProcess();
|
|
286
|
+
}
|
|
287
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });
|