@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,197 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// P-ENG-FINAL ENG-011 故障重试 (fault retry) acceptance driver.
|
|
4
|
+
//
|
|
5
|
+
// Case (qa-autopilot/config/test-cases.json ENG-011, P1):
|
|
6
|
+
// steps: UI点击指定run重试,连续双击与网络响应丢失分别测试。
|
|
7
|
+
// expected: 1) 同一意图不会重复派发。
|
|
8
|
+
// 2) 活动状态正确。
|
|
9
|
+
// 3) 请求结果未知先查询。
|
|
10
|
+
//
|
|
11
|
+
// Real behaviour under test (vendored knowledge-engine, real HTTP surface):
|
|
12
|
+
// * POST /api/runs/:runId/retry (server.js:130-133 -> coordinator.retry)
|
|
13
|
+
// - 409 when the run is no longer in a retryable state
|
|
14
|
+
// - re-queues the SAME run (no second run record for the same commit)
|
|
15
|
+
// * the engine's per-project `active` map (coordinator.pump) keeps a single
|
|
16
|
+
// writer per project, so a double click cannot dispatch the analysis twice
|
|
17
|
+
// * the analyzer stand-in fails only the FIRST attempt of a commit (a
|
|
18
|
+
// transient fault), so the retry can really succeed
|
|
19
|
+
// * the engine's own event log (/api/events) is the record of the active
|
|
20
|
+
// state machine - no sampling races involved
|
|
21
|
+
//
|
|
22
|
+
// Isolation: real vendored engine on an isolated data dir, real vector-hub on
|
|
23
|
+
// an isolated root, ephemeral loopback port. The engine runs with consume:false
|
|
24
|
+
// and a dead bridge URL so it can never reach the production bridge/vector
|
|
25
|
+
// services (8787/8790 untouched). Analyzer = deterministic stand-in (NO real
|
|
26
|
+
// model output). No quota access, no PAYG.
|
|
27
|
+
|
|
28
|
+
const assert = require('node:assert/strict');
|
|
29
|
+
const fs = require('node:fs');
|
|
30
|
+
const path = require('node:path');
|
|
31
|
+
const {
|
|
32
|
+
makeLaneDir, buildFixtureRepo, startVector, startEngine, waitUntil, callJson,
|
|
33
|
+
callJsonAndLoseResponse, readJsonl, saveEvidence,
|
|
34
|
+
} = require('./helpers/parallel-engine-final-harness.js');
|
|
35
|
+
const { createStandinAnalyzer } = require('./helpers/parallel-engine-final-standin-analyzer.js');
|
|
36
|
+
|
|
37
|
+
const ARTIFACTS = process.env.PK_TEST_EVIDENCE_ROOT
|
|
38
|
+
? path.join(process.env.PK_TEST_EVIDENCE_ROOT, 'eng011')
|
|
39
|
+
: path.resolve(__dirname, '../../qa-autopilot/artifacts/parallel-20261023/engine-review-r3/reruns/eng011');
|
|
40
|
+
const PROJECT_ID = 'project-eng011';
|
|
41
|
+
|
|
42
|
+
function analyzerInvocations(eventsFile, sha) {
|
|
43
|
+
return readJsonl(eventsFile).filter(entry => entry.kind === 'start' && entry.commitSha === sha).length;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function main() {
|
|
47
|
+
const lane = makeLaneDir('eng011');
|
|
48
|
+
const commands = [];
|
|
49
|
+
const evidence = { caseId: 'ENG-011', laneDir: lane, phases: {}, commands };
|
|
50
|
+
|
|
51
|
+
const fixture = buildFixtureRepo(lane, 'eng011', ['A', 'B']);
|
|
52
|
+
const knowledgePath = path.join(lane, 'knowledge-eng011');
|
|
53
|
+
fs.mkdirSync(knowledgePath, { recursive: true });
|
|
54
|
+
const dataDir = path.join(lane, 'engine-data');
|
|
55
|
+
const vectorRoot = path.join(lane, 'vector-root');
|
|
56
|
+
const eventsFile = path.join(lane, 'analyzer-events.jsonl');
|
|
57
|
+
|
|
58
|
+
const vector = await startVector(vectorRoot);
|
|
59
|
+
const analyzer = createStandinAnalyzer({ eventsFile, failOnceShas: new Set([fixture.shas.A, fixture.shas.B]) });
|
|
60
|
+
// consume:false -> the engine never contacts a bridge; the dead URL can only
|
|
61
|
+
// fail fast if something tried.
|
|
62
|
+
const engine = await startEngine({ dataDir, bridgeUrl: 'http://127.0.0.1:9', vectorUrl: vector.url, analyzer });
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const base = await engine.listen();
|
|
66
|
+
engine.runtime.store.addProject({
|
|
67
|
+
projectId: PROJECT_ID,
|
|
68
|
+
name: 'eng011',
|
|
69
|
+
repoPath: fixture.repoPath,
|
|
70
|
+
knowledgePath,
|
|
71
|
+
trackingStartCommit: fixture.baseline,
|
|
72
|
+
});
|
|
73
|
+
commands.push('engine project registered (consume:false, dead bridge URL)');
|
|
74
|
+
|
|
75
|
+
const runsOf = async () => (await callJson(base, `/api/runs?projectId=${PROJECT_ID}`)).body.runs;
|
|
76
|
+
const runOf = async runId => (await runsOf()).find(r => r.runId === runId);
|
|
77
|
+
const waitState = async (runId, state, ms, what) => waitUntil(async () => {
|
|
78
|
+
const run = await runOf(runId);
|
|
79
|
+
return run?.state === state ? run : null;
|
|
80
|
+
}, ms, what);
|
|
81
|
+
const phasesOf = async runId => (await callJson(base, `/api/events?projectId=${PROJECT_ID}`)).body.events
|
|
82
|
+
.filter(event => event.runId === runId)
|
|
83
|
+
.map(event => ({ phase: event.phase, summary: event.summary, at: event.at || null }));
|
|
84
|
+
|
|
85
|
+
// ---------- Scenario 1: double click on retry ----------
|
|
86
|
+
const createdA = await callJson(base, '/api/runs', { projectId: PROJECT_ID, commitSha: fixture.shas.A });
|
|
87
|
+
assert.equal(createdA.status, 200, `run creation failed: ${JSON.stringify(createdA.body)}`);
|
|
88
|
+
const runA = createdA.body.run;
|
|
89
|
+
const failedA = await waitState(runA.runId, 'failed', 30000, 'first attempt fails (transient stand-in fault)');
|
|
90
|
+
assert.equal(failedA.error.code, 'STANDIN_ANALYZER_FAILURE');
|
|
91
|
+
commands.push(`run A created -> failed (${failedA.error.code})`);
|
|
92
|
+
|
|
93
|
+
const [doubleA, doubleB] = await Promise.all([
|
|
94
|
+
callJson(base, `/api/runs/${encodeURIComponent(runA.runId)}/retry`, {}),
|
|
95
|
+
callJson(base, `/api/runs/${encodeURIComponent(runA.runId)}/retry`, {}),
|
|
96
|
+
]);
|
|
97
|
+
commands.push(`double click retry A -> statuses ${doubleA.status}/${doubleB.status}`);
|
|
98
|
+
const healedA = await waitState(runA.runId, 'succeeded', 30000, 'retried run A succeeds');
|
|
99
|
+
const runsAfterDouble = await runsOf();
|
|
100
|
+
const runsForA = runsAfterDouble.filter(r => r.commitSha === fixture.shas.A);
|
|
101
|
+
const health = (await callJson(base, '/api/health')).body;
|
|
102
|
+
const invocationsA = analyzerInvocations(eventsFile, fixture.shas.A);
|
|
103
|
+
const phasesA = await phasesOf(runA.runId);
|
|
104
|
+
evidence.phases.doubleClick = {
|
|
105
|
+
retryResponses: [{ status: doubleA.status, error: doubleA.body?.error || null }, { status: doubleB.status, error: doubleB.body?.error || null }],
|
|
106
|
+
runRecordCountForCommitA: runsForA.length,
|
|
107
|
+
analyzerInvocationsForA: invocationsA,
|
|
108
|
+
finalState: healedA.state,
|
|
109
|
+
finalPhase: healedA.phase,
|
|
110
|
+
indexState: healedA.indexState,
|
|
111
|
+
engineStateEvents: phasesA,
|
|
112
|
+
healthCounts: health.counts,
|
|
113
|
+
healthConcurrency: health.concurrency,
|
|
114
|
+
};
|
|
115
|
+
assert.equal(runsForA.length, 1, 'double click did not create a second run for the same commit');
|
|
116
|
+
assert.equal(invocationsA, 2, `one intent dispatched once: expected 2 analyzer runs (1 failed + 1 retry), got ${invocationsA}`);
|
|
117
|
+
assert.equal(healedA.state, 'succeeded');
|
|
118
|
+
assert.equal(healedA.phase, 'ready');
|
|
119
|
+
assert.equal(healedA.indexState, 'succeeded');
|
|
120
|
+
assert.ok(doubleA.status === 200 || doubleB.status === 200, 'at least one retry request was accepted');
|
|
121
|
+
assert.ok([doubleA.status, doubleB.status].every(status => status === 200 || status === 409), `unexpected retry status (${doubleA.status}/${doubleB.status})`);
|
|
122
|
+
assert.equal(health.counts.running, 0, 'no run left active after the retry settled');
|
|
123
|
+
const phasesSeen = phasesA.map(entry => entry.phase);
|
|
124
|
+
for (const phase of ['preparing', 'analyzing', 'promoting', 'ready']) {
|
|
125
|
+
assert.ok(phasesSeen.includes(phase), `engine state machine did not record phase ${phase}: ${phasesSeen.join(',')}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ---------- Scenario 2: lost network response ----------
|
|
129
|
+
const createdB = await callJson(base, '/api/runs', { projectId: PROJECT_ID, commitSha: fixture.shas.B });
|
|
130
|
+
const runB = createdB.body.run;
|
|
131
|
+
await waitState(runB.runId, 'failed', 30000, 'run B first attempt fails');
|
|
132
|
+
commands.push('run B created -> failed');
|
|
133
|
+
|
|
134
|
+
const lost = await callJsonAndLoseResponse(base, `/api/runs/${encodeURIComponent(runB.runId)}/retry`, {});
|
|
135
|
+
commands.push(`retry B with the response dropped: ${JSON.stringify(lost)}`);
|
|
136
|
+
assert.equal(lost.lost, true, 'response was not actually dropped');
|
|
137
|
+
|
|
138
|
+
// 结果未知先查询: the client lost the answer, so it asks the engine first.
|
|
139
|
+
const queried = await callJson(base, `/api/runs?projectId=${PROJECT_ID}`);
|
|
140
|
+
assert.equal(queried.status, 200);
|
|
141
|
+
const queriedRun = queried.body.runs.find(r => r.runId === runB.runId);
|
|
142
|
+
assert.ok(queriedRun, 'query after an unknown result returns the run');
|
|
143
|
+
const queriedState = queriedRun.state;
|
|
144
|
+
const healedB = await waitState(runB.runId, 'succeeded', 30000, 'the dropped-response retry really took effect');
|
|
145
|
+
const invocationsB = analyzerInvocations(eventsFile, fixture.shas.B);
|
|
146
|
+
|
|
147
|
+
// The client retries once more because it never saw the answer.
|
|
148
|
+
const repeated = await callJson(base, `/api/runs/${encodeURIComponent(runB.runId)}/retry`, {});
|
|
149
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
150
|
+
const invocationsBAfterRepeat = analyzerInvocations(eventsFile, fixture.shas.B);
|
|
151
|
+
const runsForB = (await runsOf()).filter(r => r.commitSha === fixture.shas.B);
|
|
152
|
+
const ledgerB = path.join(dataDir, 'runtime', 'processed-commits', PROJECT_ID, `${fixture.shas.B}.json`);
|
|
153
|
+
evidence.phases.lostResponse = {
|
|
154
|
+
droppedResponse: lost,
|
|
155
|
+
queriedStateImmediatelyAfterDrop: queriedState,
|
|
156
|
+
queriedRunId: queriedRun.runId,
|
|
157
|
+
finalState: healedB.state,
|
|
158
|
+
repeatRetryStatus: repeated.status,
|
|
159
|
+
repeatRetryError: repeated.body?.error || null,
|
|
160
|
+
runRecordCountForCommitB: runsForB.length,
|
|
161
|
+
analyzerInvocationsForB: invocationsB,
|
|
162
|
+
analyzerInvocationsAfterRepeat: invocationsBAfterRepeat,
|
|
163
|
+
ledgerExists: fs.existsSync(ledgerB),
|
|
164
|
+
ledgerStatus: fs.existsSync(ledgerB) ? JSON.parse(fs.readFileSync(ledgerB, 'utf8')).status : null,
|
|
165
|
+
engineStateEvents: await phasesOf(runB.runId),
|
|
166
|
+
};
|
|
167
|
+
assert.equal(healedB.state, 'succeeded', 'the retry behind the lost response really executed');
|
|
168
|
+
assert.equal(invocationsB, 2, `lost-response retry dispatched exactly once (got ${invocationsB} analyzer runs)`);
|
|
169
|
+
assert.equal(invocationsBAfterRepeat, invocationsB, 'repeating the retry after an unknown result did not dispatch again');
|
|
170
|
+
assert.equal(runsForB.length, 1, 'no duplicate run after the unknown-result retry');
|
|
171
|
+
assert.equal(repeated.status, 409, `repeat retry must be refused, got ${repeated.status}`);
|
|
172
|
+
assert.ok(fs.existsSync(ledgerB), 'completed SHA recorded in the processing ledger');
|
|
173
|
+
|
|
174
|
+
evidence.verdict = {
|
|
175
|
+
'expected1 同一意图不会重复派发': 'PASS',
|
|
176
|
+
'expected2 活动状态正确': 'PASS',
|
|
177
|
+
'expected3 请求结果未知先查询': 'PASS',
|
|
178
|
+
analyzerStandin: 'deterministic stand-in with a one-shot transient fault - NO real model output covered',
|
|
179
|
+
vectorService: 'real vector-hub server, deterministic test embeddings - paid embedding API NOT covered',
|
|
180
|
+
boundary: 'engine-level retry surface (HTTP retry endpoint, run state machine, per-project active map, processing ledger, engine event log). The Workbench UI double-click handling itself is outside the engine and was not exercised.',
|
|
181
|
+
};
|
|
182
|
+
await engine.close();
|
|
183
|
+
await vector.close();
|
|
184
|
+
saveEvidence(ARTIFACTS, 'eng011-evidence.json', evidence);
|
|
185
|
+
console.log('ENG-011 PASS');
|
|
186
|
+
process.exitCode = 0;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
evidence.verdict = { failed: String((error && error.stack) || error) };
|
|
189
|
+
try { await engine.close(); } catch {}
|
|
190
|
+
try { await vector.close(); } catch {}
|
|
191
|
+
saveEvidence(ARTIFACTS, 'eng011-evidence.json', evidence);
|
|
192
|
+
console.error(`ENG-011 FAIL: ${error.message}`);
|
|
193
|
+
process.exitCode = 1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
main();
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// P-ENG-FINAL ENG-012 分析来源排除 (analysis source exclusion) acceptance driver.
|
|
4
|
+
//
|
|
5
|
+
// Case (qa-autopilot/config/test-cases.json ENG-012, P1):
|
|
6
|
+
// steps: 内部维护任务与真实用户任务使用不同origin,读取采集事件。
|
|
7
|
+
// expected: 1) 维护/委派不能自动成为人类长期偏好。
|
|
8
|
+
// 2) C01未定项不得自动删除实现。
|
|
9
|
+
//
|
|
10
|
+
// Real behaviour under test (real bridge, real vendored engine):
|
|
11
|
+
// * the real bridge captures TWO workbench events for the same session with
|
|
12
|
+
// different origins: ':origin=human' and ':origin=agent-delegation'. The
|
|
13
|
+
// engine derives `inputOrigin` from the eventKey suffix
|
|
14
|
+
// (conversation-store.js:239) and freezes the origin into the commit
|
|
15
|
+
// conversation snapshot on disk.
|
|
16
|
+
// * the analyzer stand-in reads that frozen snapshot and - on purpose - emits
|
|
17
|
+
// BOTH quotes as memory candidates, i.e. it behaves like an over-eager
|
|
18
|
+
// maintenance model. The engine's MemoryEligibility.verify must reject the
|
|
19
|
+
// delegated one with SOURCE_NOT_HUMAN and keep the human one as a plain
|
|
20
|
+
// 'candidate' (never automatically 'confirmed').
|
|
21
|
+
// * C01 (specs-coverage.json MEM-008: a manual memory may not carry source
|
|
22
|
+
// evidence) is still an open product question. The engine therefore blocks
|
|
23
|
+
// evidence injection explicitly (INVALID_ARGUMENT) and stores manual
|
|
24
|
+
// records with evidence null / sourceType 'manual'. The implementation is
|
|
25
|
+
// NOT deleted and nothing is removed automatically.
|
|
26
|
+
// * an "over-eager maintenance" manifest that tries to delete an
|
|
27
|
+
// implementation file is refused by the promotion path
|
|
28
|
+
// (knowledge-promotion.js canonicalRelativePath -> PATH_OUTSIDE_ROOT); the
|
|
29
|
+
// implementation file survives byte-identical and no knowledge operation
|
|
30
|
+
// is applied.
|
|
31
|
+
//
|
|
32
|
+
// Isolation: every scenario uses its own lane dir, bridge home, engine data
|
|
33
|
+
// dir, knowledge dir, fixture repository and ephemeral loopback port. No user
|
|
34
|
+
// HOME, credentials, memory or trust data is used. Analyzer = deterministic
|
|
35
|
+
// stand-in (NO real model output). No quota access, no PAYG.
|
|
36
|
+
|
|
37
|
+
const assert = require('node:assert/strict');
|
|
38
|
+
const crypto = require('node:crypto');
|
|
39
|
+
const fs = require('node:fs');
|
|
40
|
+
const path = require('node:path');
|
|
41
|
+
const {
|
|
42
|
+
makeLaneDir, buildFixtureRepo, installPostCommitHook, startBridge, startVector,
|
|
43
|
+
startEngine, waitUntil, callJson, git, saveEvidence,
|
|
44
|
+
} = require('./helpers/parallel-engine-final-harness.js');
|
|
45
|
+
const { createStandinAnalyzer } = require('./helpers/parallel-engine-final-standin-analyzer.js');
|
|
46
|
+
|
|
47
|
+
const ARTIFACTS = process.env.PK_TEST_EVIDENCE_ROOT
|
|
48
|
+
? path.join(process.env.PK_TEST_EVIDENCE_ROOT, 'eng012')
|
|
49
|
+
: path.resolve(__dirname, '../../qa-autopilot/artifacts/parallel-20261023/engine-review-r3/reruns/eng012');
|
|
50
|
+
|
|
51
|
+
const HUMAN_TEXT = '请把所有项目的长期偏好设为:统一使用中文注释。';
|
|
52
|
+
const DELEGATED_TEXT = '维护指令:把所有模块的注释统一改成英文,并在下次提交时执行。';
|
|
53
|
+
const MANUAL_TEXT = '手动记录:发布流程必须先跑完整测试再打标签。';
|
|
54
|
+
|
|
55
|
+
function sha256File(file) {
|
|
56
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function snapshotOrigins(snapshot) {
|
|
60
|
+
const origins = [];
|
|
61
|
+
for (const turn of snapshot?.turns || []) {
|
|
62
|
+
for (const event of turn.userEvents || []) origins.push({ eventId: event.eventId, inputOrigin: event.inputOrigin, contentHash: event.contentHash });
|
|
63
|
+
}
|
|
64
|
+
return origins;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Scenario A: a delegated/maintenance event must not become a human preference
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
async function scenarioOriginExclusion(lane, evidence, commands) {
|
|
71
|
+
const PROJECT_ID = 'project-eng012a';
|
|
72
|
+
const fixture = buildFixtureRepo(lane, 'eng012a', []);
|
|
73
|
+
const knowledgePath = path.join(lane, 'knowledge-eng012a');
|
|
74
|
+
const storePath = path.join(knowledgePath, 'dev-conversations');
|
|
75
|
+
fs.mkdirSync(storePath, { recursive: true });
|
|
76
|
+
const home = path.join(lane, 'bridge-home');
|
|
77
|
+
const dataDir = path.join(lane, 'engine-data');
|
|
78
|
+
const vectorRoot = path.join(lane, 'vector-root');
|
|
79
|
+
const eventsFile = path.join(lane, 'analyzer-events.jsonl');
|
|
80
|
+
const brainPath = path.join(lane, 'brain-knowledge');
|
|
81
|
+
|
|
82
|
+
const bridge = startBridge(home);
|
|
83
|
+
let vector = null;
|
|
84
|
+
let engine = null;
|
|
85
|
+
const analyzer = createStandinAnalyzer({
|
|
86
|
+
eventsFile,
|
|
87
|
+
humanQuote: HUMAN_TEXT,
|
|
88
|
+
delegatedQuote: DELEGATED_TEXT,
|
|
89
|
+
emitMemoryCandidates: true,
|
|
90
|
+
readSnapshot: (projectId, commitSha) => engine?.runtime.coordinator.conversations.readSnapshot(projectId, commitSha),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const bridgeUrl = await bridge.listen();
|
|
95
|
+
vector = await startVector(vectorRoot);
|
|
96
|
+
const registered = await bridge.projectRegistry.addProject({ home, path: fixture.repoPath, store: storePath });
|
|
97
|
+
const { repoIdentity } = await bridge.resolveRepoContext(fixture.repoPath);
|
|
98
|
+
engine = await startEngine({ dataDir, bridgeUrl, vectorUrl: vector.url, analyzer, runtimeToken: 'eng012-lane-token' });
|
|
99
|
+
const engineUrl = await engine.listen();
|
|
100
|
+
engine.runtime.store.addProject({
|
|
101
|
+
projectId: PROJECT_ID,
|
|
102
|
+
repoPath: fixture.repoPath,
|
|
103
|
+
knowledgePath,
|
|
104
|
+
trackingStartCommit: fixture.baseline,
|
|
105
|
+
repoIdentity,
|
|
106
|
+
journalId: registered.id,
|
|
107
|
+
conversationBaselineCursor: 0,
|
|
108
|
+
});
|
|
109
|
+
commands.push('scenario A: real bridge + real vector-hub + vendored engine started, project registered');
|
|
110
|
+
|
|
111
|
+
// Two real captured events, same session, DIFFERENT origin.
|
|
112
|
+
await bridge.bridge.captureWorkbenchEvent({
|
|
113
|
+
eventType: 'user_prompt', source: 'claude-code', projectPath: fixture.repoPath,
|
|
114
|
+
sessionId: 'session-eng012', turnId: 'turn-eng012',
|
|
115
|
+
eventKey: 'workbench:eng012:origin=human', content: HUMAN_TEXT,
|
|
116
|
+
});
|
|
117
|
+
await bridge.bridge.captureWorkbenchEvent({
|
|
118
|
+
eventType: 'user_prompt', source: 'claude-code', projectPath: fixture.repoPath,
|
|
119
|
+
sessionId: 'session-eng012', turnId: 'turn-eng012',
|
|
120
|
+
eventKey: 'workbench:eng012:origin=agent-delegation', content: DELEGATED_TEXT,
|
|
121
|
+
});
|
|
122
|
+
commands.push('bridge captureWorkbenchEvent x2 (eventKey origins: human, agent-delegation)');
|
|
123
|
+
|
|
124
|
+
// Real commit -> the real hook writes the boundary event -> the engine
|
|
125
|
+
// freezes the conversation snapshot for that commit.
|
|
126
|
+
installPostCommitHook(fixture.repoPath, { home, mode: 'ok' });
|
|
127
|
+
fs.writeFileSync(path.join(fixture.repoPath, 'feature.txt'), `feature eng012\n${'z'.repeat(64)}\n`);
|
|
128
|
+
git(fixture.repoPath, ['add', '.']);
|
|
129
|
+
git(fixture.repoPath, ['commit', '-qm', 'feature eng012']);
|
|
130
|
+
const sha = git(fixture.repoPath, ['rev-parse', 'HEAD']);
|
|
131
|
+
await callJson(engineUrl, '/api/bridge/notify', {});
|
|
132
|
+
|
|
133
|
+
const run = await waitUntil(async () => {
|
|
134
|
+
const found = engine.runtime.store.listRuns(PROJECT_ID).find(r => r.commitSha === sha);
|
|
135
|
+
return found && found.state === 'succeeded' ? found : null;
|
|
136
|
+
}, 60000, 'the analysed run succeeds');
|
|
137
|
+
|
|
138
|
+
// The frozen snapshot on disk must carry both origins.
|
|
139
|
+
const snapshotFile = path.join(dataDir, 'runtime', 'projects', PROJECT_ID, 'commit-conversations', `${sha}.json`);
|
|
140
|
+
assert.ok(fs.existsSync(snapshotFile), 'the commit conversation snapshot is frozen on disk');
|
|
141
|
+
const snapshot = JSON.parse(fs.readFileSync(snapshotFile, 'utf8'));
|
|
142
|
+
const origins = snapshotOrigins(snapshot);
|
|
143
|
+
const originValues = origins.map(entry => entry.inputOrigin);
|
|
144
|
+
assert.ok(originValues.includes('human'), `the human origin is missing from the snapshot: ${originValues.join(',')}`);
|
|
145
|
+
assert.ok(originValues.includes('agent-delegation'), `the delegated origin is missing from the snapshot: ${originValues.join(',')}`);
|
|
146
|
+
commands.push(`frozen snapshot carries both origins: ${originValues.join(', ')}`);
|
|
147
|
+
|
|
148
|
+
// The engine's own capture report.
|
|
149
|
+
const capture = run.memoryCapture;
|
|
150
|
+
assert.ok(capture, 'the run records a memory capture report');
|
|
151
|
+
assert.equal(capture.accepted, 1, `exactly one candidate may be accepted (got ${capture.accepted})`);
|
|
152
|
+
assert.equal(capture.rejected.length, 1, `exactly one candidate must be rejected (got ${JSON.stringify(capture.rejected)})`);
|
|
153
|
+
assert.equal(capture.rejected[0].code, 'SOURCE_NOT_HUMAN', `the delegated candidate must be rejected as SOURCE_NOT_HUMAN (got ${capture.rejected[0].code})`);
|
|
154
|
+
|
|
155
|
+
const memories = (await callJson(engineUrl, '/api/memories')).body.memories;
|
|
156
|
+
const texts = memories.map(m => m.text);
|
|
157
|
+
assert.equal(memories.length, 1, `only the human candidate may exist (got ${memories.length})`);
|
|
158
|
+
assert.equal(memories[0].text, HUMAN_TEXT, 'the stored candidate is the human quote');
|
|
159
|
+
assert.equal(memories[0].status, 'candidate', 'an automatically captured memory stays a candidate');
|
|
160
|
+
assert.ok(!texts.some(text => text.includes('维护指令')), 'the delegated/maintenance text never became a memory');
|
|
161
|
+
assert.ok(!memories.some(m => m.status === 'confirmed'), 'nothing was confirmed automatically');
|
|
162
|
+
assert.equal(memories[0].evidence?.origin || memories[0].evidence?.inputOrigin || null, null, 'the candidate carries the frozen event id, not an invented origin');
|
|
163
|
+
commands.push(`memory capture: accepted=${capture.accepted}, rejected=${JSON.stringify(capture.rejected)}, memories=${memories.length} (status=${memories[0].status})`);
|
|
164
|
+
|
|
165
|
+
// ---- C01: the open item is blocked explicitly, the implementation is intact ----
|
|
166
|
+
const injected = await callJson(engineUrl, '/api/memories', {
|
|
167
|
+
text: '注入来源证据的记忆', scopeType: 'global', evidence: { projectId: PROJECT_ID, commitSha: sha },
|
|
168
|
+
});
|
|
169
|
+
assert.equal(injected.status, 400, `evidence injection must be refused (status ${injected.status})`);
|
|
170
|
+
assert.equal(injected.body?.error, 'INVALID_ARGUMENT', `unexpected error code ${injected.body?.error}`);
|
|
171
|
+
assert.match(String(injected.body?.message || ''), /不能指定来源证据/, 'the refusal states the C01 rule explicitly');
|
|
172
|
+
|
|
173
|
+
// A manual memory (explicit user confirmation) still stores no evidence.
|
|
174
|
+
const brainScope = await callJson(engineUrl, '/api/scopes', {
|
|
175
|
+
scopes: [{ id: 'brain-default', kind: 'brain', brainId: 'brain-default', name: '默认智脑', knowledgePath: brainPath }],
|
|
176
|
+
}, { headers: { authorization: 'Bearer eng012-lane-token' } });
|
|
177
|
+
assert.equal(brainScope.status, 200, `brain scope binding failed: ${JSON.stringify(brainScope.body)}`);
|
|
178
|
+
const manual = await callJson(engineUrl, '/api/memories', { text: MANUAL_TEXT, scopeType: 'global' });
|
|
179
|
+
assert.equal(manual.status, 200, `manual memory creation failed: ${JSON.stringify(manual.body)}`);
|
|
180
|
+
assert.equal(manual.body?.memory?.sourceType, 'manual');
|
|
181
|
+
assert.equal(manual.body?.memory?.evidence, null, 'a manual record stores no evidence');
|
|
182
|
+
assert.equal(manual.body?.memory?.status, 'confirmed');
|
|
183
|
+
const afterManual = (await callJson(engineUrl, '/api/memories')).body.memories;
|
|
184
|
+
assert.ok(!afterManual.some(m => m.text.includes('维护指令')), 'the delegated text is still not a memory after a manual create');
|
|
185
|
+
assert.equal(afterManual.filter(m => m.status === 'confirmed').length, 1, 'only the explicit manual memory is confirmed');
|
|
186
|
+
commands.push('C01: evidence injection refused with INVALID_ARGUMENT; manual memory stored with evidence=null / sourceType=manual');
|
|
187
|
+
|
|
188
|
+
evidence.phases.originExclusion = {
|
|
189
|
+
commitSha: sha,
|
|
190
|
+
snapshotFile: path.relative(dataDir, snapshotFile).replace(/\\/g, '/'),
|
|
191
|
+
snapshotOrigins: origins,
|
|
192
|
+
memoryCapture: capture,
|
|
193
|
+
memories: memories.map(m => ({ id: m.id, text: m.text, status: m.status, scopeType: m.scopeType, sourceEventId: m.sourceEventId, evidence: m.evidence })),
|
|
194
|
+
delegatedTextPresent: texts.some(text => text.includes('维护指令')),
|
|
195
|
+
c01EvidenceInjection: { status: injected.status, error: injected.body?.error || null, message: injected.body?.message || null },
|
|
196
|
+
manualMemory: { status: manual.status, sourceType: manual.body?.memory?.sourceType, evidence: manual.body?.memory?.evidence, memoryStatus: manual.body?.memory?.status },
|
|
197
|
+
memoriesAfterManualCreate: afterManual.map(m => ({ text: m.text, status: m.status })),
|
|
198
|
+
};
|
|
199
|
+
return { engine, vector, bridge, dataDir, knowledgePath, PROJECT_ID, sha };
|
|
200
|
+
} catch (error) {
|
|
201
|
+
try { await engine?.close(); } catch {}
|
|
202
|
+
try { await vector?.close(); } catch {}
|
|
203
|
+
try { await bridge.close(); } catch {}
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// Scenario B: an implementation file must never be deleted automatically
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
async function scenarioNoImplementationDeletion(lane, evidence, commands) {
|
|
212
|
+
const PROJECT_ID = 'project-eng012b';
|
|
213
|
+
const fixture = buildFixtureRepo(lane, 'eng012b', []);
|
|
214
|
+
const knowledgePath = path.join(lane, 'knowledge-eng012b');
|
|
215
|
+
const storePath = path.join(knowledgePath, 'dev-conversations');
|
|
216
|
+
fs.mkdirSync(storePath, { recursive: true });
|
|
217
|
+
const home = path.join(lane, 'bridge-home');
|
|
218
|
+
const dataDir = path.join(lane, 'engine-data');
|
|
219
|
+
const vectorRoot = path.join(lane, 'vector-root');
|
|
220
|
+
const eventsFile = path.join(lane, 'analyzer-events.jsonl');
|
|
221
|
+
|
|
222
|
+
// A REAL implementation file, both in the repository and inside the
|
|
223
|
+
// knowledge root (outside the owned knowledge structure).
|
|
224
|
+
const repoImplementation = path.join(fixture.repoPath, 'src', 'core-engine.ts');
|
|
225
|
+
fs.mkdirSync(path.dirname(repoImplementation), { recursive: true });
|
|
226
|
+
fs.writeFileSync(repoImplementation, 'export const core = () => "implementation";\n');
|
|
227
|
+
git(fixture.repoPath, ['add', '.']);
|
|
228
|
+
git(fixture.repoPath, ['commit', '-qm', 'add implementation']);
|
|
229
|
+
const knowledgeImplementation = path.join(knowledgePath, 'implementation', 'core-engine.md');
|
|
230
|
+
fs.mkdirSync(path.dirname(knowledgeImplementation), { recursive: true });
|
|
231
|
+
fs.writeFileSync(knowledgeImplementation, '# 核心实现说明\n\n真实实现文件,不得被自动删除。\n');
|
|
232
|
+
|
|
233
|
+
const before = {
|
|
234
|
+
repoImplementation: sha256File(repoImplementation),
|
|
235
|
+
knowledgeImplementation: sha256File(knowledgeImplementation),
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const bridge = startBridge(home);
|
|
239
|
+
let vector = null;
|
|
240
|
+
let engine = null;
|
|
241
|
+
// An over-eager maintenance analyzer that tries to delete an implementation.
|
|
242
|
+
const analyzer = createStandinAnalyzer({
|
|
243
|
+
eventsFile,
|
|
244
|
+
extraOperations: [
|
|
245
|
+
{ operation: 'delete', path: 'implementation/core-engine.md', reason: '尝试删除实现文件' },
|
|
246
|
+
{ operation: 'delete', path: '../knowledge-eng012b/implementation/core-engine.md', reason: '尝试越界删除' },
|
|
247
|
+
],
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const bridgeUrl = await bridge.listen();
|
|
252
|
+
vector = await startVector(vectorRoot);
|
|
253
|
+
const registered = await bridge.projectRegistry.addProject({ home, path: fixture.repoPath, store: storePath });
|
|
254
|
+
const { repoIdentity } = await bridge.resolveRepoContext(fixture.repoPath);
|
|
255
|
+
engine = await startEngine({ dataDir, bridgeUrl, vectorUrl: vector.url, analyzer });
|
|
256
|
+
const engineUrl = await engine.listen();
|
|
257
|
+
engine.runtime.store.addProject({
|
|
258
|
+
projectId: PROJECT_ID,
|
|
259
|
+
repoPath: fixture.repoPath,
|
|
260
|
+
knowledgePath,
|
|
261
|
+
trackingStartCommit: fixture.baseline,
|
|
262
|
+
repoIdentity,
|
|
263
|
+
journalId: registered.id,
|
|
264
|
+
conversationBaselineCursor: 0,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
installPostCommitHook(fixture.repoPath, { home, mode: 'ok' });
|
|
268
|
+
fs.writeFileSync(path.join(fixture.repoPath, 'feature.txt'), `feature eng012b\n${'y'.repeat(64)}\n`);
|
|
269
|
+
git(fixture.repoPath, ['add', '.']);
|
|
270
|
+
git(fixture.repoPath, ['commit', '-qm', 'feature eng012b']);
|
|
271
|
+
const sha = git(fixture.repoPath, ['rev-parse', 'HEAD']);
|
|
272
|
+
await callJson(engineUrl, '/api/bridge/notify', {});
|
|
273
|
+
|
|
274
|
+
const run = await waitUntil(async () => {
|
|
275
|
+
const found = engine.runtime.store.listRuns(PROJECT_ID).find(r => r.commitSha === sha);
|
|
276
|
+
return found && ['failed', 'succeeded'].includes(found.state) ? found : null;
|
|
277
|
+
}, 60000, 'the run with the deletion attempt settles');
|
|
278
|
+
|
|
279
|
+
assert.equal(run.state, 'failed', `the deletion attempt must not succeed (state ${run.state})`);
|
|
280
|
+
assert.equal(run.error?.code, 'PATH_OUTSIDE_ROOT', `unexpected rejection code ${run.error?.code}`);
|
|
281
|
+
assert.ok(fs.existsSync(repoImplementation), 'the repository implementation file still exists');
|
|
282
|
+
assert.ok(fs.existsSync(knowledgeImplementation), 'the implementation file inside the knowledge root still exists');
|
|
283
|
+
assert.equal(sha256File(repoImplementation), before.repoImplementation, 'the repository implementation is byte-identical');
|
|
284
|
+
assert.equal(sha256File(knowledgeImplementation), before.knowledgeImplementation, 'the knowledge-root implementation is byte-identical');
|
|
285
|
+
const changesDir = path.join(knowledgePath, 'changes');
|
|
286
|
+
const promoted = fs.existsSync(changesDir) ? fs.readdirSync(changesDir) : [];
|
|
287
|
+
assert.equal(promoted.length, 0, `no knowledge operation may be applied when one is illegal (promoted: ${promoted.join(',')})`);
|
|
288
|
+
const memories = (await callJson(engineUrl, '/api/memories')).body.memories;
|
|
289
|
+
assert.equal(memories.length, 0, 'a refused maintenance run creates no memory');
|
|
290
|
+
commands.push(`deletion attempt refused (${run.error?.code}); implementation files byte-identical; nothing promoted`);
|
|
291
|
+
|
|
292
|
+
evidence.phases.noImplementationDeletion = {
|
|
293
|
+
commitSha: sha,
|
|
294
|
+
run: { runId: run.runId, trigger: run.trigger, state: run.state, phase: run.phase, error: run.error },
|
|
295
|
+
attemptedDeleteOperations: [
|
|
296
|
+
{ path: 'implementation/core-engine.md', kind: 'outside the owned knowledge structure' },
|
|
297
|
+
{ path: '../knowledge-eng012b/implementation/core-engine.md', kind: 'escape attempt' },
|
|
298
|
+
],
|
|
299
|
+
implementationFiles: {
|
|
300
|
+
repository: { path: path.relative(fixture.repoPath, repoImplementation).replace(/\\/g, '/'), sha256Before: before.repoImplementation, sha256After: sha256File(repoImplementation), exists: fs.existsSync(repoImplementation) },
|
|
301
|
+
knowledgeRoot: { path: path.relative(knowledgePath, knowledgeImplementation).replace(/\\/g, '/'), sha256Before: before.knowledgeImplementation, sha256After: sha256File(knowledgeImplementation), exists: fs.existsSync(knowledgeImplementation) },
|
|
302
|
+
},
|
|
303
|
+
promotedKnowledgeFiles: promoted,
|
|
304
|
+
memoriesCreated: memories.length,
|
|
305
|
+
};
|
|
306
|
+
return { engine, vector, bridge };
|
|
307
|
+
} catch (error) {
|
|
308
|
+
try { await engine?.close(); } catch {}
|
|
309
|
+
try { await vector?.close(); } catch {}
|
|
310
|
+
try { await bridge.close(); } catch {}
|
|
311
|
+
throw error;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function main() {
|
|
316
|
+
const evidence = { caseId: 'ENG-012', phases: {}, commands: [] };
|
|
317
|
+
const commands = evidence.commands;
|
|
318
|
+
try {
|
|
319
|
+
const laneA = makeLaneDir('eng012a');
|
|
320
|
+
evidence.laneA = laneA;
|
|
321
|
+
const a = await scenarioOriginExclusion(laneA, evidence, commands);
|
|
322
|
+
await a.engine.close();
|
|
323
|
+
await a.vector.close();
|
|
324
|
+
await a.bridge.close();
|
|
325
|
+
|
|
326
|
+
const laneB = makeLaneDir('eng012b');
|
|
327
|
+
evidence.laneB = laneB;
|
|
328
|
+
const b = await scenarioNoImplementationDeletion(laneB, evidence, commands);
|
|
329
|
+
await b.engine.close();
|
|
330
|
+
await b.vector.close();
|
|
331
|
+
await b.bridge.close();
|
|
332
|
+
|
|
333
|
+
evidence.verdict = {
|
|
334
|
+
'expected1 维护/委派不能自动成为人类长期偏好': 'PASS',
|
|
335
|
+
'expected2 C01未定项不得自动删除实现': 'PASS',
|
|
336
|
+
originExclusion: 'the frozen commit conversation snapshot carries both origins; the engine rejected the delegated candidate with SOURCE_NOT_HUMAN and kept the human one as a plain candidate - nothing was confirmed automatically',
|
|
337
|
+
c01Block: 'POST /api/memories with an injected source-evidence field is refused with INVALID_ARGUMENT ("不能指定来源证据"); a manual memory is stored with evidence null and sourceType manual, so the MEM-008 implementation was not deleted',
|
|
338
|
+
noImplementationDeletion: 'a manifest that tries to delete an implementation file is refused by the promotion path (PATH_OUTSIDE_ROOT); the run fails, no knowledge operation is applied and the implementation files stay byte-identical',
|
|
339
|
+
analyzerStandin: 'deterministic stand-in that deliberately emits both the human and the delegated candidate - NO real model output covered',
|
|
340
|
+
vectorService: 'real vector-hub server, deterministic test embeddings - paid embedding API NOT covered',
|
|
341
|
+
boundary: 'PARTIAL on the product side: the product memory panel UI (addMemory/memoryContent/memoryTabs/memoryScopeFilter/candidateCount/memoryFeedback) and the shared memory ledger are owned by other lanes and were not exercised; specs-coverage.json MEM-008 records that those UI paths have zero assertions in the 156 product tests. Everything asserted here is engine-level behaviour.',
|
|
342
|
+
};
|
|
343
|
+
saveEvidence(ARTIFACTS, 'eng012-evidence.json', evidence);
|
|
344
|
+
console.log('ENG-012 PASS');
|
|
345
|
+
process.exit(0);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
evidence.verdict = { failed: String((error && error.stack) || error) };
|
|
348
|
+
saveEvidence(ARTIFACTS, 'eng012-evidence.json', evidence);
|
|
349
|
+
console.error(`ENG-012 FAIL: ${error.message}`);
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
main();
|