@sanqianx/project-knowledge 5.0.23 → 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 -1346
- 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 +70 -67
- 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,184 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Parallel knowledge lane — regression for the engine error-code contract.
|
|
3
|
+
//
|
|
4
|
+
// The vendored knowledge-engine reports a failed read as { ok:false,
|
|
5
|
+
// error:'<CODE>' } with the code as a plain string. The product client used to
|
|
6
|
+
// read the code only from payload.error.code, so every genuine business
|
|
7
|
+
// failure was flattened into the generic ENGINE_REQUEST_FAILED and the real
|
|
8
|
+
// reason was lost for the shell and for the MCP tools.
|
|
9
|
+
//
|
|
10
|
+
// This file pins the contract on a real loopback HTTP server:
|
|
11
|
+
// * the string envelope keeps its code;
|
|
12
|
+
// * the object envelope keeps its code;
|
|
13
|
+
// * an invalid code still falls back to ENGINE_REQUEST_FAILED;
|
|
14
|
+
// * the HTTP status is preserved on the raised DomainError;
|
|
15
|
+
// * a successful read is unchanged.
|
|
16
|
+
//
|
|
17
|
+
// It validates the CURRENT PRODUCT only. It never reads the Git history, the
|
|
18
|
+
// current HEAD, a remote repository or any pre-existing object, so it stays
|
|
19
|
+
// green after the fix is committed and in a shallow checkout or an exported
|
|
20
|
+
// tree without a .git directory.
|
|
21
|
+
//
|
|
22
|
+
// The historical red reproduction (the pre-fix client failing this matrix)
|
|
23
|
+
// was removed from this entry point on purpose: it depended on `git show
|
|
24
|
+
// HEAD:...`, which turns a fixed defect red the moment the fix lands. It is
|
|
25
|
+
// preserved as ignored evidence instead, together with a fixed snapshot of
|
|
26
|
+
// the pre-fix source, so the defect stays documented without the canonical
|
|
27
|
+
// regression depending on Git history:
|
|
28
|
+
// qa-autopilot/artifacts/parallel-20261023/test-knowledge/legacy/
|
|
29
|
+
//
|
|
30
|
+
// Nothing here touches the live 5757/5790/8787/8790 endpoints or the real
|
|
31
|
+
// HOME: the entry point isolates itself before loading any product module.
|
|
32
|
+
const assert = require('node:assert/strict');
|
|
33
|
+
const fs = require('node:fs');
|
|
34
|
+
const path = require('node:path');
|
|
35
|
+
const http = require('node:http');
|
|
36
|
+
const fixture = require('./helpers/parallel-knowledge-fixture');
|
|
37
|
+
fixture.isolateProcess();
|
|
38
|
+
const { StorageLayout } = require('../lib/storage-layout');
|
|
39
|
+
const { EngineKnowledgeClient } = require('../lib/engine-knowledge-client');
|
|
40
|
+
|
|
41
|
+
// --only=patched is accepted as an explicit alias of the default run. No
|
|
42
|
+
// other mode exists here: the historical --only=head mode was moved to the
|
|
43
|
+
// ignored legacy evidence and must not be reintroduced, because a canonical
|
|
44
|
+
// test that depends on Git history is red by construction after the fix is
|
|
45
|
+
// committed.
|
|
46
|
+
const LEGACY_HEAD_MODE = '--only=head';
|
|
47
|
+
|
|
48
|
+
function createEngineStub({ repoPath, knowledgePath, projectId, envelope }) {
|
|
49
|
+
const server = http.createServer((req, res) => {
|
|
50
|
+
const send = (status, body) => {
|
|
51
|
+
res.writeHead(status, { 'Content-Type': 'application/json' });
|
|
52
|
+
res.end(JSON.stringify(body));
|
|
53
|
+
};
|
|
54
|
+
if (req.url === '/api/health') return send(200, { name: 'knowledge-engine', dataSchemaVersion: 3 });
|
|
55
|
+
if (req.url === '/api/projects') {
|
|
56
|
+
return send(200, { ok: true, projects: [{ projectId, enabled: true, repoPath, knowledgePath }] });
|
|
57
|
+
}
|
|
58
|
+
if (req.method === 'POST') {
|
|
59
|
+
let body = '';
|
|
60
|
+
req.on('data', chunk => { body += chunk; });
|
|
61
|
+
req.on('end', () => {
|
|
62
|
+
if (envelope.kind === 'success') return send(200, { ok: true, results: [], answer: 'ok', citations: [] });
|
|
63
|
+
return send(envelope.status, envelope.body);
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
return send(404, { ok: false, error: 'NOT_FOUND' });
|
|
68
|
+
});
|
|
69
|
+
return server;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function listen(server) {
|
|
73
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
74
|
+
const { port } = server.address();
|
|
75
|
+
fixture.assertAllowedPort(port, 'test engine port');
|
|
76
|
+
return `http://127.0.0.1:${port}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const SCENARIOS = [
|
|
80
|
+
{
|
|
81
|
+
id: 'string-envelope-code-kept',
|
|
82
|
+
envelope: { kind: 'failure', status: 403, body: { ok: false, error: 'PATH_OUTSIDE_ROOT' } },
|
|
83
|
+
expectedCode: 'PATH_OUTSIDE_ROOT',
|
|
84
|
+
expectedStatus: 403,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'object-envelope-code-kept',
|
|
88
|
+
envelope: { kind: 'failure', status: 404, body: { ok: false, error: { code: 'PROJECT_NOT_FOUND' } } },
|
|
89
|
+
expectedCode: 'PROJECT_NOT_FOUND',
|
|
90
|
+
expectedStatus: 404,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'invalid-code-falls-back',
|
|
94
|
+
envelope: { kind: 'failure', status: 500, body: { ok: false, error: 'not a valid code!' } },
|
|
95
|
+
expectedCode: 'ENGINE_REQUEST_FAILED',
|
|
96
|
+
expectedStatus: 500,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'ok-false-on-200-becomes-502',
|
|
100
|
+
envelope: { kind: 'failure', status: 200, body: { ok: false, error: 'INVALID_ARGUMENT' } },
|
|
101
|
+
expectedCode: 'INVALID_ARGUMENT',
|
|
102
|
+
expectedStatus: 502,
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
(async () => {
|
|
107
|
+
const args = process.argv.slice(2);
|
|
108
|
+
if (args.includes(LEGACY_HEAD_MODE)) {
|
|
109
|
+
console.error(`${LEGACY_HEAD_MODE} was removed from this canonical entry point.`);
|
|
110
|
+
console.error('The pre-fix red reproduction now lives in the ignored evidence:');
|
|
111
|
+
console.error(' qa-autopilot/artifacts/parallel-20261023/test-knowledge/legacy/reproduce-pre-fix-client.js');
|
|
112
|
+
console.error('It uses a fixed source snapshot, so the canonical regression never depends on Git history.');
|
|
113
|
+
process.exitCode = 2;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const root = fixture.makeTempDir('parallel-knowledge-client');
|
|
118
|
+
const layout = new StorageLayout({ dataDir: path.join(root, 'data') });
|
|
119
|
+
const repoPath = path.join(root, 'repo');
|
|
120
|
+
const knowledgePath = path.join(root, 'knowledge');
|
|
121
|
+
fs.mkdirSync(repoPath, { recursive: true });
|
|
122
|
+
fs.mkdirSync(knowledgePath, { recursive: true });
|
|
123
|
+
const project = { projectId: 'project-client', repoPath, knowledgePath };
|
|
124
|
+
const results = [];
|
|
125
|
+
|
|
126
|
+
function record(caseId, status, detail) {
|
|
127
|
+
results.push({ caseId, status, detail });
|
|
128
|
+
console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
for (const scenario of SCENARIOS) {
|
|
133
|
+
const server = createEngineStub({ ...project, envelope: scenario.envelope });
|
|
134
|
+
const engineUrl = await listen(server);
|
|
135
|
+
try {
|
|
136
|
+
const client = new EngineKnowledgeClient({ layout, engineUrl });
|
|
137
|
+
let raised = null;
|
|
138
|
+
try {
|
|
139
|
+
await client.request('search', { query: 'x' }, project);
|
|
140
|
+
} catch (error) { raised = error; }
|
|
141
|
+
assert.ok(raised, 'the client must reject a failed read');
|
|
142
|
+
assert.equal(raised.code, scenario.expectedCode, `unexpected code for ${scenario.id}`);
|
|
143
|
+
assert.equal(raised.status, scenario.expectedStatus, `unexpected status for ${scenario.id}`);
|
|
144
|
+
record(`CLIENT-${scenario.id}`, 'PASS', { code: raised.code, status: raised.status, engineUrl });
|
|
145
|
+
} finally {
|
|
146
|
+
server.closeAllConnections();
|
|
147
|
+
await new Promise(resolve => server.close(resolve));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// A successful read must still succeed.
|
|
152
|
+
{
|
|
153
|
+
const server = createEngineStub({ ...project, envelope: { kind: 'success' } });
|
|
154
|
+
const engineUrl = await listen(server);
|
|
155
|
+
try {
|
|
156
|
+
const client = new EngineKnowledgeClient({ layout, engineUrl });
|
|
157
|
+
const payload = await client.request('search', { query: 'x' }, project);
|
|
158
|
+
assert.equal(payload.ok, true);
|
|
159
|
+
assert.ok(Array.isArray(payload.results));
|
|
160
|
+
record('CLIENT-success-envelope-untouched', 'PASS', { ok: payload.ok, resultCount: payload.results.length });
|
|
161
|
+
} finally {
|
|
162
|
+
server.closeAllConnections();
|
|
163
|
+
await new Promise(resolve => server.close(resolve));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const summary = {
|
|
168
|
+
lane: 'test-knowledge',
|
|
169
|
+
file: 'parallel-knowledge-client-error-code-test.js',
|
|
170
|
+
cases: results,
|
|
171
|
+
productionTouched: false,
|
|
172
|
+
gitHistoryUsed: false,
|
|
173
|
+
note: 'the canonical regression validates the current product only; the pre-fix red reproduction was moved to the ignored legacy evidence and runs against a fixed source snapshot',
|
|
174
|
+
legacyRedEvidence: 'qa-autopilot/artifacts/parallel-20261023/test-knowledge/legacy/',
|
|
175
|
+
};
|
|
176
|
+
const target = process.env.PARALLEL_KNOWLEDGE_EVIDENCE;
|
|
177
|
+
if (target) fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
178
|
+
if (target) fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
|
|
179
|
+
console.log('parallel-knowledge-client-error-code-test PASS');
|
|
180
|
+
} finally {
|
|
181
|
+
fixture.removeTree(root);
|
|
182
|
+
fixture.cleanupIsolatedProcess();
|
|
183
|
+
}
|
|
184
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Parallel knowledge lane — engine cases ENG-001, ENG-003, ENG-004, ENG-008.
|
|
3
|
+
// The real vendored knowledge-engine runs in-process on an isolated port with
|
|
4
|
+
// an isolated data directory, and every Git fixture is a real repository with
|
|
5
|
+
// real commits. The analyzer is a declared stub (no model is called) and the
|
|
6
|
+
// vector endpoint is a declared stub that returns no rows, so nothing here is
|
|
7
|
+
// evidence of model or vector behaviour.
|
|
8
|
+
const assert = require('node:assert/strict');
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const path = require('node:path');
|
|
11
|
+
const http = require('node:http');
|
|
12
|
+
const crypto = require('node:crypto');
|
|
13
|
+
const fixture = require('./helpers/parallel-knowledge-fixture');
|
|
14
|
+
// The entry point isolates itself: ambient module addresses, live data
|
|
15
|
+
// directories and the real HOME are cleared here, before any product module
|
|
16
|
+
// is loaded, so the test never depends on the caller running `env -u`.
|
|
17
|
+
fixture.isolateProcess();
|
|
18
|
+
const { StorageLayout } = require('../lib/storage-layout');
|
|
19
|
+
const { ProjectStore } = require('../lib/project-store');
|
|
20
|
+
const { ProjectRegistryStore } = require('../lib/project-registry-store');
|
|
21
|
+
const { ProjectLifecycleService } = require('../lib/project-lifecycle-service');
|
|
22
|
+
const { createEngineServer } = require('../../_modules/knowledge-engine/src/server');
|
|
23
|
+
const { CommitScanner } = require('../../_modules/knowledge-engine/src/runtime/scanner');
|
|
24
|
+
const AtomicFile = require('../../_modules/knowledge-engine/src/runtime/atomic-file');
|
|
25
|
+
|
|
26
|
+
const results = [];
|
|
27
|
+
function record(caseId, status, detail) {
|
|
28
|
+
results.push({ caseId, status, detail });
|
|
29
|
+
console.log(`CASE ${caseId} ${status} ${JSON.stringify(detail)}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function stubVectorServer() {
|
|
33
|
+
const requests = [];
|
|
34
|
+
const server = http.createServer((req, res) => {
|
|
35
|
+
let body = '';
|
|
36
|
+
req.on('data', chunk => { body += chunk; });
|
|
37
|
+
req.on('end', () => {
|
|
38
|
+
requests.push({ url: req.url, method: req.method });
|
|
39
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
40
|
+
res.end(JSON.stringify({ ok: true, results: [], errors: [], projects: [] }));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
|
|
44
|
+
return { requests, url: `http://127.0.0.1:${server.address().port}`, close: () => { server.closeAllConnections(); return new Promise(resolve => server.close(resolve)); } };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function forbiddenBridge() {
|
|
48
|
+
const guard = name => async () => { throw new Error(`event bridge must not be contacted from the test lane (${name})`); };
|
|
49
|
+
return { registerConsumer: guard('registerConsumer'), listJournals: guard('listJournals'), readEvents: guard('readEvents'), ackConsumerCursor: guard('ackConsumerCursor'), close() {} };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Declared stub analyzer: stages Markdown plus a manifest, exactly like the
|
|
53
|
+
// canonical engine suite does. It never contacts a model.
|
|
54
|
+
function stubAnalyzer(events = [], options = {}) {
|
|
55
|
+
return {
|
|
56
|
+
sourceReadMode: 'isolated-frozen-evidence',
|
|
57
|
+
async runClaim(input) {
|
|
58
|
+
events.push({ kind: 'start', projectId: input.projectId, sha: input.claim.commitSha });
|
|
59
|
+
const relative = options.manifestPath || `changes/${input.claim.commitSha.slice(0, 12)}.md`;
|
|
60
|
+
const content = `# 引擎晋升条目\n\nENGTOKEN 事实:提交 ${input.claim.commitSha} 已被分析。\n`;
|
|
61
|
+
// An escaping manifest path is the attack under test, so the staged
|
|
62
|
+
// bytes stay inside the staging directory in every case.
|
|
63
|
+
const stagedRelative = relative.split('/').includes('..') ? 'escaped.md' : relative;
|
|
64
|
+
AtomicFile.writeFileAtomic(path.join(input.stagingPath, 'files', stagedRelative), content);
|
|
65
|
+
AtomicFile.writeJsonAtomic(input.manifestPath, {
|
|
66
|
+
schema: 'knowledge-staging-manifest/v1',
|
|
67
|
+
projectId: input.projectId,
|
|
68
|
+
runId: input.claim.runId,
|
|
69
|
+
commitSha: input.claim.commitSha,
|
|
70
|
+
operations: [{
|
|
71
|
+
path: relative,
|
|
72
|
+
operation: 'create',
|
|
73
|
+
sha256: `sha256:${crypto.createHash('sha256').update(content).digest('hex')}`,
|
|
74
|
+
reason: 'Frozen Git evidence',
|
|
75
|
+
evidenceReferences: [`commit:${input.claim.commitSha}`, `patch:${input.claim.patchHash}`, `conversation:${input.claim.conversationSnapshotHash}`, `retrieval:${input.claim.retrievalManifestHash}`],
|
|
76
|
+
}],
|
|
77
|
+
});
|
|
78
|
+
events.push({ kind: 'finish', projectId: input.projectId, sha: input.claim.commitSha });
|
|
79
|
+
return { ok: true };
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function startEngine({ dataDir, vectorUrl, analyzer }) {
|
|
85
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
86
|
+
const runtime = createEngineServer({ dataDir, vectorUrl, bridge: forbiddenBridge(), analyzer });
|
|
87
|
+
await runtime.ready;
|
|
88
|
+
await new Promise(resolve => runtime.server.listen(0, '127.0.0.1', resolve));
|
|
89
|
+
const base = `http://127.0.0.1:${runtime.server.address().port}`;
|
|
90
|
+
return {
|
|
91
|
+
...runtime,
|
|
92
|
+
base,
|
|
93
|
+
async call(route, body) {
|
|
94
|
+
const response = await fetch(`${base}${route}`, {
|
|
95
|
+
method: body === undefined ? 'GET' : 'POST',
|
|
96
|
+
headers: { 'Content-Type': 'application/json' },
|
|
97
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
98
|
+
});
|
|
99
|
+
return { status: response.status, body: await response.json() };
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
(async () => {
|
|
105
|
+
const root = fixture.makeTempDir('parallel-knowledge-engine');
|
|
106
|
+
const vector = await stubVectorServer();
|
|
107
|
+
const analyzerEvents = [];
|
|
108
|
+
let engine = null;
|
|
109
|
+
try {
|
|
110
|
+
// --------------------------------------------------------------- ENG-001
|
|
111
|
+
{
|
|
112
|
+
const repo = fixture.makeRepo(root, 'repo-import', [
|
|
113
|
+
{ message: '历史提交一', files: { 'a.txt': 'a\n' } },
|
|
114
|
+
{ message: '历史提交二', files: { 'b.txt': 'b\n' } },
|
|
115
|
+
{ message: '历史提交三', files: { 'c.txt': 'c\n' } },
|
|
116
|
+
]);
|
|
117
|
+
const repoBefore = fixture.hashTree(repo.repoPath);
|
|
118
|
+
const knowledgeRoot = path.join(root, 'knowledge-root');
|
|
119
|
+
fs.mkdirSync(knowledgeRoot, { recursive: true });
|
|
120
|
+
const layout = new StorageLayout({ dataDir: path.join(root, 'shell-data') });
|
|
121
|
+
const registry = new ProjectRegistryStore({ layout });
|
|
122
|
+
const projects = new ProjectStore({ layout });
|
|
123
|
+
await registry.initialize();
|
|
124
|
+
const lifecycle = new ProjectLifecycleService({
|
|
125
|
+
layout,
|
|
126
|
+
registryStore: registry,
|
|
127
|
+
projectStore: projects,
|
|
128
|
+
settingsStore: { read: () => ({ knowledge: { rootPath: knowledgeRoot }, ai: { schema: 'ai-profiles/v1', profiles: [{ id: 'test-profile', enabled: true, vendor: 'anthropic', model: 'm' }], defaultProfileId: null } }) },
|
|
129
|
+
triggerScriptPath: path.join(root, 'hook-trigger.js'),
|
|
130
|
+
hookManager: { readHookStatus: () => ({ kbManaged: false }), installHook: () => ({ ok: true, managedVersion: 2 }), uninstallHook: () => ({ ok: true }) },
|
|
131
|
+
});
|
|
132
|
+
const imported = await lifecycle.importProject({ localPath: repo.repoPath, projectId: 'project-import', aiProfileId: 'test-profile' });
|
|
133
|
+
assert.equal(imported.state.trackingStartCommit, repo.head, 'the import must record the imported HEAD as the baseline');
|
|
134
|
+
assert.equal(imported.state.lastAnalyzedCommit, null, 'an import must not claim an analysed commit');
|
|
135
|
+
assert.equal(imported.state.trackingMode, 'normal');
|
|
136
|
+
const projectKnowledge = imported.config.knowledgePath;
|
|
137
|
+
assert.deepEqual(fs.readdirSync(projectKnowledge).filter(name => name !== '.gitkeep'), [], 'an import must not create knowledge files');
|
|
138
|
+
assert.deepEqual(fixture.hashTree(repo.repoPath), repoBefore, 'the import must not touch the source repository');
|
|
139
|
+
|
|
140
|
+
// The real engine scanner must see zero pending history behind the baseline.
|
|
141
|
+
const scanner = new CommitScanner();
|
|
142
|
+
const scan = await scanner.scan({ repoPath: repo.repoPath }, { trackingStartCommit: repo.head, lastAnalyzedCommit: null, trackingMode: 'normal' });
|
|
143
|
+
assert.equal(scan.status, 'ok');
|
|
144
|
+
assert.equal(scan.baseline, repo.head);
|
|
145
|
+
assert.deepEqual(scan.commits, [], 'the baseline must cover the whole imported history');
|
|
146
|
+
assert.equal(scan.pendingCount, 0);
|
|
147
|
+
|
|
148
|
+
// A real engine started on that registration must not analyse history.
|
|
149
|
+
engine = await startEngine({ dataDir: path.join(root, 'engine-import'), vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents) });
|
|
150
|
+
const registration = await engine.call('/api/projects', {
|
|
151
|
+
projectId: 'project-import',
|
|
152
|
+
name: 'import',
|
|
153
|
+
repoPath: repo.repoPath,
|
|
154
|
+
knowledgePath: projectKnowledge,
|
|
155
|
+
trackingStartCommit: repo.head,
|
|
156
|
+
brainId: 'brain-default',
|
|
157
|
+
domainId: 'domain-tools',
|
|
158
|
+
});
|
|
159
|
+
assert.equal(registration.status, 200);
|
|
160
|
+
await fixture.eventually(() => engine.store.state.runs.length > 0, 2000);
|
|
161
|
+
assert.deepEqual(engine.store.listRuns(), [], 'starting the engine must not create an analysis run for imported history');
|
|
162
|
+
assert.deepEqual(fixture.hashTree(repo.repoPath), repoBefore, 'the source repository must stay byte-identical');
|
|
163
|
+
record('ENG-001', 'PASS', {
|
|
164
|
+
importedHead: repo.head,
|
|
165
|
+
trackingStartCommit: imported.state.trackingStartCommit,
|
|
166
|
+
lastAnalyzedCommit: imported.state.lastAnalyzedCommit,
|
|
167
|
+
scanStatus: scan.status,
|
|
168
|
+
pendingCommits: scan.pendingCount,
|
|
169
|
+
runsCreated: engine.store.listRuns().length,
|
|
170
|
+
knowledgeFiles: fs.readdirSync(projectKnowledge).filter(name => name !== '.gitkeep').length,
|
|
171
|
+
sourceRepoUnchanged: true,
|
|
172
|
+
});
|
|
173
|
+
await engine.close();
|
|
174
|
+
engine = null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// --------------------------------------------------------------- ENG-003
|
|
178
|
+
{
|
|
179
|
+
const repoPath = path.join(root, 'repo-empty');
|
|
180
|
+
fs.mkdirSync(repoPath, { recursive: true });
|
|
181
|
+
fixture.git(repoPath, ['init', '-q']);
|
|
182
|
+
fixture.git(repoPath, ['config', 'user.email', 'parallel-knowledge@local.invalid']);
|
|
183
|
+
fixture.git(repoPath, ['config', 'user.name', 'Parallel Knowledge Test']);
|
|
184
|
+
const knowledgeRoot = path.join(root, 'knowledge-root-empty');
|
|
185
|
+
fs.mkdirSync(knowledgeRoot, { recursive: true });
|
|
186
|
+
const layout = new StorageLayout({ dataDir: path.join(root, 'shell-data-empty') });
|
|
187
|
+
const registry = new ProjectRegistryStore({ layout });
|
|
188
|
+
const projects = new ProjectStore({ layout });
|
|
189
|
+
await registry.initialize();
|
|
190
|
+
const lifecycle = new ProjectLifecycleService({
|
|
191
|
+
layout,
|
|
192
|
+
registryStore: registry,
|
|
193
|
+
projectStore: projects,
|
|
194
|
+
settingsStore: { read: () => ({ knowledge: { rootPath: knowledgeRoot }, ai: { schema: 'ai-profiles/v1', profiles: [{ id: 'test-profile', enabled: true, vendor: 'anthropic', model: 'm' }], defaultProfileId: null } }) },
|
|
195
|
+
triggerScriptPath: path.join(root, 'hook-trigger.js'),
|
|
196
|
+
hookManager: { readHookStatus: () => ({ kbManaged: false }), installHook: () => ({ ok: true, managedVersion: 2 }), uninstallHook: () => ({ ok: true }) },
|
|
197
|
+
});
|
|
198
|
+
const imported = await lifecycle.importProject({ localPath: repoPath, projectId: 'project-empty', aiProfileId: 'test-profile' });
|
|
199
|
+
assert.equal(imported.state.trackingMode, 'empty-repo');
|
|
200
|
+
assert.equal(imported.state.trackingStartCommit, null, 'a repository without HEAD must not get a baseline commit');
|
|
201
|
+
assert.equal(imported.state.lastAnalyzedCommit, null);
|
|
202
|
+
|
|
203
|
+
const scanner = new CommitScanner();
|
|
204
|
+
const emptyScan = await scanner.scan({ repoPath }, { trackingStartCommit: null, lastAnalyzedCommit: null, trackingMode: 'empty-repo' });
|
|
205
|
+
assert.equal(emptyScan.status, 'empty-repo', 'a repository without HEAD must be reported as empty, not as analysed');
|
|
206
|
+
assert.deepEqual(emptyScan.commits, []);
|
|
207
|
+
|
|
208
|
+
engine = await startEngine({ dataDir: path.join(root, 'engine-empty'), vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents) });
|
|
209
|
+
const knowledgePath = imported.config.knowledgePath;
|
|
210
|
+
await engine.call('/api/projects', { projectId: 'project-empty', name: 'empty', repoPath, knowledgePath, trackingStartCommit: null, brainId: 'brain-default', domainId: 'domain-tools' });
|
|
211
|
+
assert.deepEqual(engine.store.listRuns(), [], 'no analysis may be invented before the first commit exists');
|
|
212
|
+
assert.equal(engine.store.readRuntime('project-empty').lastAnalyzedCommit, null);
|
|
213
|
+
|
|
214
|
+
// The first real commit after the empty import must be processable.
|
|
215
|
+
fs.writeFileSync(path.join(repoPath, 'first.txt'), 'first\n', 'utf8');
|
|
216
|
+
fixture.git(repoPath, ['add', '-A']);
|
|
217
|
+
fixture.git(repoPath, ['commit', '-q', '-m', 'first commit']);
|
|
218
|
+
const firstSha = fixture.git(repoPath, ['rev-parse', 'HEAD']);
|
|
219
|
+
const run = engine.coordinator.enqueue({ projectId: 'project-empty', commitSha: firstSha });
|
|
220
|
+
const settled = await fixture.eventually(() => ['succeeded', 'failed', 'index-pending'].includes(engine.store.getRun(run.runId).state));
|
|
221
|
+
assert.equal(settled, true, 'the first commit must reach a terminal state');
|
|
222
|
+
const finished = engine.store.getRun(run.runId);
|
|
223
|
+
assert.equal(finished.state, 'succeeded', `first commit failed: ${JSON.stringify(finished.error)}`);
|
|
224
|
+
assert.equal(engine.store.readRuntime('project-empty').lastAnalyzedCommit, firstSha);
|
|
225
|
+
assert.ok(fs.existsSync(path.join(knowledgePath, 'changes', `${firstSha.slice(0, 12)}.md`)), 'the first commit must produce knowledge');
|
|
226
|
+
record('ENG-003', 'PASS', {
|
|
227
|
+
trackingMode: imported.state.trackingMode,
|
|
228
|
+
trackingStartCommit: imported.state.trackingStartCommit,
|
|
229
|
+
emptyScanStatus: emptyScan.status,
|
|
230
|
+
runsBeforeFirstCommit: 0,
|
|
231
|
+
firstCommitSha: firstSha,
|
|
232
|
+
firstCommitState: finished.state,
|
|
233
|
+
lastAnalyzedCommit: engine.store.readRuntime('project-empty').lastAnalyzedCommit,
|
|
234
|
+
});
|
|
235
|
+
await engine.close();
|
|
236
|
+
engine = null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// --------------------------------------------------------------- ENG-004
|
|
240
|
+
{
|
|
241
|
+
const repo = fixture.makeRepo(root, 'repo-duplicate', [
|
|
242
|
+
{ message: '初始', files: { 'README.md': '# dup\n' } },
|
|
243
|
+
{ message: '功能', files: { 'feature.txt': 'feature\n' } },
|
|
244
|
+
]);
|
|
245
|
+
const knowledgePath = fixture.makeKnowledgeDir(root, 'knowledge-duplicate');
|
|
246
|
+
const dataDir = path.join(root, 'engine-duplicate');
|
|
247
|
+
engine = await startEngine({ dataDir, vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents) });
|
|
248
|
+
await engine.call('/api/projects', { projectId: 'project-dup', name: 'dup', repoPath: repo.repoPath, knowledgePath, brainId: 'brain-default', domainId: 'domain-tools' });
|
|
249
|
+
|
|
250
|
+
const first = engine.coordinator.enqueue({ projectId: 'project-dup', commitSha: repo.head });
|
|
251
|
+
const second = engine.coordinator.enqueue({ projectId: 'project-dup', commitSha: repo.head });
|
|
252
|
+
assert.equal(second.runId, first.runId, 'the same SHA must reuse one run');
|
|
253
|
+
const settled = await fixture.eventually(() => ['succeeded', 'failed', 'index-pending'].includes(engine.store.getRun(first.runId).state));
|
|
254
|
+
assert.equal(settled, true);
|
|
255
|
+
assert.equal(engine.store.getRun(first.runId).state, 'succeeded', `run failed: ${JSON.stringify(engine.store.getRun(first.runId).error)}`);
|
|
256
|
+
const analysesAfterFirst = analyzerEvents.filter(event => event.kind === 'start' && event.sha === repo.head).length;
|
|
257
|
+
assert.equal(analysesAfterFirst, 1, 'one SHA must produce exactly one analysis');
|
|
258
|
+
assert.equal(engine.store.listRuns('project-dup').length, 1, 'one SHA must produce exactly one run');
|
|
259
|
+
|
|
260
|
+
// A repeated explicit reconciliation of the same SHA must be refused by
|
|
261
|
+
// the processing ledger instead of analysing again.
|
|
262
|
+
const repeated = await engine.coordinator.reconciler.processCommitEvent({ projectId: 'project-dup', commitSha: repo.head });
|
|
263
|
+
assert.equal(repeated.status, 'already-completed', 'a repeated SHA must be reported as already completed');
|
|
264
|
+
assert.equal(analyzerEvents.filter(event => event.kind === 'start' && event.sha === repo.head).length, 1, 'a repeated SHA must not analyse again');
|
|
265
|
+
|
|
266
|
+
// Startup reconciliation: a fresh engine on the same data directory.
|
|
267
|
+
await engine.close();
|
|
268
|
+
engine = await startEngine({ dataDir, vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents) });
|
|
269
|
+
const afterRestart = await engine.coordinator.reconciler.processCommitEvent({ projectId: 'project-dup', commitSha: repo.head });
|
|
270
|
+
assert.equal(afterRestart.status, 'already-completed', 'startup reconciliation must not re-analyse a completed SHA');
|
|
271
|
+
assert.equal(analyzerEvents.filter(event => event.kind === 'start' && event.sha === repo.head).length, 1, 'startup reconciliation must not run the model again');
|
|
272
|
+
assert.equal(engine.store.listRuns('project-dup').length, 1);
|
|
273
|
+
const promotedFiles = fs.readdirSync(path.join(knowledgePath, 'changes'));
|
|
274
|
+
assert.deepEqual(promotedFiles, [`${repo.head.slice(0, 12)}.md`], 'exactly one promotion must exist');
|
|
275
|
+
record('ENG-004', 'PASS', {
|
|
276
|
+
runId: first.runId,
|
|
277
|
+
runsCreated: engine.store.listRuns('project-dup').length,
|
|
278
|
+
analysesForSha: analyzerEvents.filter(event => event.kind === 'start' && event.sha === repo.head).length,
|
|
279
|
+
repeatedStatus: repeated.status,
|
|
280
|
+
restartStatus: afterRestart.status,
|
|
281
|
+
promotedFiles,
|
|
282
|
+
});
|
|
283
|
+
await engine.close();
|
|
284
|
+
engine = null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// --------------------------------------------------------------- ENG-008
|
|
288
|
+
{
|
|
289
|
+
const repo = fixture.makeRepo(root, 'repo-escape', [
|
|
290
|
+
{ message: '初始', files: { 'README.md': '# escape\n' } },
|
|
291
|
+
{ message: '功能', files: { 'feature.txt': 'feature\n' } },
|
|
292
|
+
]);
|
|
293
|
+
const knowledgePath = fixture.makeKnowledgeDir(root, 'knowledge-escape');
|
|
294
|
+
fs.writeFileSync(path.join(knowledgePath, 'README.md'), '# 已有知识\n\nENG008TOKEN 已有正文\n', 'utf8');
|
|
295
|
+
const knowledgeBefore = fixture.hashTree(knowledgePath);
|
|
296
|
+
const repoBefore = fixture.hashTree(repo.repoPath);
|
|
297
|
+
const outsideMarker = path.join(root, 'escaped.md');
|
|
298
|
+
engine = await startEngine({ dataDir: path.join(root, 'engine-escape'), vectorUrl: vector.url, analyzer: stubAnalyzer(analyzerEvents, { manifestPath: '../escaped.md' }) });
|
|
299
|
+
await engine.call('/api/projects', { projectId: 'project-escape', name: 'escape', repoPath: repo.repoPath, knowledgePath, brainId: 'brain-default', domainId: 'domain-tools' });
|
|
300
|
+
const run = engine.coordinator.enqueue({ projectId: 'project-escape', commitSha: repo.head });
|
|
301
|
+
const settled = await fixture.eventually(() => ['succeeded', 'failed', 'index-pending'].includes(engine.store.getRun(run.runId).state));
|
|
302
|
+
assert.equal(settled, true);
|
|
303
|
+
const failed = engine.store.getRun(run.runId);
|
|
304
|
+
assert.equal(failed.state, 'failed', 'an out-of-bounds staging path must be refused');
|
|
305
|
+
assert.equal(failed.error.code, 'PATH_OUTSIDE_ROOT', `unexpected failure code: ${failed.error.code}`);
|
|
306
|
+
assert.deepEqual(fixture.hashTree(knowledgePath), knowledgeBefore, 'the final knowledge must stay byte-identical');
|
|
307
|
+
assert.deepEqual(fixture.hashTree(repo.repoPath), repoBefore, 'the source repository must stay byte-identical');
|
|
308
|
+
assert.equal(fs.existsSync(outsideMarker), false, 'nothing may be written outside the project knowledge root');
|
|
309
|
+
const state = engine.store.readRuntime('project-escape');
|
|
310
|
+
assert.equal(state.lastAnalyzedCommit, null, 'a refused promotion must not advance the analysed commit');
|
|
311
|
+
assert.equal(state.analysis.status, 'failed');
|
|
312
|
+
assert.equal(state.analysis.activeClaim.phase, 'failed', 'the failed claim must be retained for diagnosis');
|
|
313
|
+
assert.ok(state.analysis.activeClaim.error, 'the failure evidence must be retained');
|
|
314
|
+
const claimFiles = fs.existsSync(path.join(root, 'engine-escape', 'runtime', 'claims', 'project-escape'))
|
|
315
|
+
? fs.readdirSync(path.join(root, 'engine-escape', 'runtime', 'claims', 'project-escape'))
|
|
316
|
+
: [];
|
|
317
|
+
assert.ok(claimFiles.includes(`${repo.head}.json`), 'the frozen claim snapshot must be retained');
|
|
318
|
+
const runs = await engine.call('/api/runs');
|
|
319
|
+
const reported = runs.body.runs.find(row => row.runId === run.runId);
|
|
320
|
+
assert.ok(reported && reported.error && reported.error.code === 'PATH_OUTSIDE_ROOT', 'the failure must stay visible in the run projection');
|
|
321
|
+
const logDir = path.join(root, 'engine-escape', 'logs', 'projects', 'project-escape');
|
|
322
|
+
const logFiles = fs.existsSync(logDir) ? fs.readdirSync(logDir) : [];
|
|
323
|
+
let failureLogged = false;
|
|
324
|
+
for (const file of logFiles) {
|
|
325
|
+
const records = fs.readFileSync(path.join(logDir, file), 'utf8').trim().split('\n').filter(Boolean).map(line => JSON.parse(line));
|
|
326
|
+
if (records.some(record => record.event === 'knowledge.maintenance.failed' && record.commitSha === repo.head)) failureLogged = true;
|
|
327
|
+
}
|
|
328
|
+
assert.equal(failureLogged, true, 'the failure must be written to the diagnostics log');
|
|
329
|
+
record('ENG-008', 'PASS', {
|
|
330
|
+
runState: failed.state,
|
|
331
|
+
errorCode: failed.error.code,
|
|
332
|
+
knowledgeUnchanged: true,
|
|
333
|
+
sourceRepoUnchanged: true,
|
|
334
|
+
claimRetained: claimFiles.includes(`${repo.head}.json`),
|
|
335
|
+
diagnosticsLogged: failureLogged,
|
|
336
|
+
lastAnalyzedCommit: state.lastAnalyzedCommit,
|
|
337
|
+
});
|
|
338
|
+
await engine.close();
|
|
339
|
+
engine = null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const summary = { lane: 'test-knowledge', file: 'parallel-knowledge-engine-test.js', cases: results, model: 'declared stub analyzer (no model call)', vector: 'declared stub returning no rows' };
|
|
343
|
+
const target = process.env.PARALLEL_KNOWLEDGE_EVIDENCE;
|
|
344
|
+
if (target) fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
345
|
+
if (target) fs.writeFileSync(target, JSON.stringify(summary, null, 2), 'utf8');
|
|
346
|
+
console.log('parallel-knowledge-engine-test PASS');
|
|
347
|
+
} finally {
|
|
348
|
+
if (engine) await engine.close();
|
|
349
|
+
await vector.close();
|
|
350
|
+
fixture.removeTree(root);
|
|
351
|
+
fixture.cleanupIsolatedProcess();
|
|
352
|
+
}
|
|
353
|
+
})().catch(error => { console.error(error); process.exitCode = 1; });
|