create-principles-disciple 1.144.26 → 1.145.0
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/_release/product-identity.json +2 -2
- package/codex-adapter/dist/codex-evidence-recorder.js +8 -2
- package/codex-adapter/dist/index.d.ts +2 -2
- package/codex-adapter/dist/index.js +1 -1
- package/codex-adapter/dist/ingestion/admission.js +16 -0
- package/codex-adapter/dist/ingestion/codex-version.d.ts +10 -4
- package/codex-adapter/dist/ingestion/codex-version.js +10 -4
- package/codex-adapter/dist/ingestion/explicit-owner-statement.d.ts +48 -0
- package/codex-adapter/dist/ingestion/explicit-owner-statement.js +123 -0
- package/codex-adapter/dist/ingestion/ingestion.js +50 -1
- package/codex-adapter/dist/pd-hook.js +66 -3
- package/codex-adapter/dist/worker/workspace-worker.d.ts +14 -0
- package/codex-adapter/dist/worker/workspace-worker.js +194 -11
- package/codex-adapter/package.json +2 -2
- package/console/dist/server/models/ApprovalsConsoleModel.d.ts +2 -2
- package/console/dist/server/models/ApprovalsConsoleModel.js +1 -1
- package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +1 -1
- package/console/dist/server/models/PrinciplesConsoleModel.d.ts +26 -1
- package/console/dist/server/models/PrinciplesConsoleModel.js +18 -1
- package/console/dist/server/routes/activations.js +4 -4
- package/console/dist/server/routes/approvals.js +2 -2
- package/console/dist/server/routes/failed-tasks.d.ts +9 -0
- package/console/dist/server/routes/failed-tasks.js +116 -7
- package/console/dist/server/routes/principles.js +73 -1
- package/console/dist/shared/prompt-injection-contract.d.ts +1 -1
- package/console/dist/ui/api.d.ts +5 -5
- package/console/dist/ui/i18n/en.json +13 -2
- package/console/dist/ui/i18n/zh-CN.json +13 -2
- package/console/dist/ui/pages/activation/ActivationPage.js +2 -1
- package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +42 -4
- package/console/dist/ui/pages/focus/FocusPage.js +3 -3
- package/console/dist/ui/pages/principles/PrinciplesPage.js +7 -2
- package/console/dist/ui/utils/validators.d.ts +6 -2
- package/console/dist/ui/utils/validators.js +19 -4
- package/console/dist/web/assets/app.css +26 -0
- package/console/dist/web/assets/app.js +24 -24
- package/console/package.json +4 -4
- package/core/dist/host/host-installer.d.ts +3 -1
- package/core/dist/host/host-installer.d.ts.map +1 -1
- package/core/dist/host/host-installer.js.map +1 -1
- package/core/dist/runtime-v2/admission-gate.d.ts +8 -0
- package/core/dist/runtime-v2/admission-gate.d.ts.map +1 -1
- package/core/dist/runtime-v2/admission-gate.js +3 -2
- package/core/dist/runtime-v2/admission-gate.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-defaults.js +16 -1
- package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -1
- package/core/dist/runtime-v2/context-payload.d.ts +43 -3
- package/core/dist/runtime-v2/context-payload.d.ts.map +1 -1
- package/core/dist/runtime-v2/context-payload.js +15 -1
- package/core/dist/runtime-v2/context-payload.js.map +1 -1
- package/core/dist/runtime-v2/evidence-guards.d.ts +6 -0
- package/core/dist/runtime-v2/evidence-guards.d.ts.map +1 -1
- package/core/dist/runtime-v2/evidence-guards.js +11 -0
- package/core/dist/runtime-v2/evidence-guards.js.map +1 -1
- package/core/dist/runtime-v2/evolution/evolution-types.d.ts +4 -4
- package/core/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/evolution/evolution-types.js +1 -1
- package/core/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +6 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +8 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +5 -2
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +3 -1
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/adversarial-feedback.d.ts +3 -1
- package/core/dist/runtime-v2/internalization/adversarial-feedback.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/adversarial-feedback.js +10 -1
- package/core/dist/runtime-v2/internalization/adversarial-feedback.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +11 -0
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-output.js +10 -0
- package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +3 -3
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +49 -5
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +13 -16
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-decision-review.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-decision-review.js +5 -1
- package/core/dist/runtime-v2/internalization/owner-decision-review.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.d.ts +2 -2
- package/core/dist/runtime-v2/internalization/owner-review.js +2 -2
- package/core/dist/runtime-v2/internalization/scribe-output.d.ts +12 -0
- package/core/dist/runtime-v2/internalization/scribe-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-output.js +20 -0
- package/core/dist/runtime-v2/internalization/scribe-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts +32 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js +38 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization-queue-read-model.d.ts +6 -0
- package/core/dist/runtime-v2/internalization-queue-read-model.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization-queue-read-model.js +8 -1
- package/core/dist/runtime-v2/internalization-queue-read-model.js.map +1 -1
- package/core/dist/runtime-v2/intervention-evidence-normalizer.d.ts.map +1 -1
- package/core/dist/runtime-v2/intervention-evidence-normalizer.js +10 -3
- package/core/dist/runtime-v2/intervention-evidence-normalizer.js.map +1 -1
- package/core/dist/runtime-v2/pain-ingress.d.ts +1 -1
- package/core/dist/runtime-v2/pain-ingress.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-bridge.d.ts +68 -2
- package/core/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-bridge.js +541 -18
- package/core/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js +4 -0
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
- package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts +2 -2
- package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js +2 -2
- package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js.map +1 -1
- package/core/dist/runtime-v2/store/candidate/admission-document.d.ts +90 -0
- package/core/dist/runtime-v2/store/candidate/admission-document.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/candidate/admission-document.js +142 -0
- package/core/dist/runtime-v2/store/candidate/admission-document.js.map +1 -0
- package/core/dist/runtime-v2/store/candidate/candidate-store.d.ts +53 -0
- package/core/dist/runtime-v2/store/candidate/candidate-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/candidate/memory-candidate-store.d.ts +35 -0
- package/core/dist/runtime-v2/store/candidate/memory-candidate-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/candidate/memory-candidate-store.js +48 -0
- package/core/dist/runtime-v2/store/candidate/memory-candidate-store.js.map +1 -1
- package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.d.ts +44 -0
- package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.js +136 -0
- package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.js.map +1 -1
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.js.map +1 -1
- package/core/dist/runtime-v2/store/intervention-evidence-schema.js +1 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +65 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.js +46 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.js +42 -0
- package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts +1 -1
- package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.js.map +1 -1
- package/core/dist/runtime-v2/store/task/sqlite-family-reopen.d.ts +19 -0
- package/core/dist/runtime-v2/store/task/sqlite-family-reopen.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/task/sqlite-family-reopen.js +36 -0
- package/core/dist/runtime-v2/store/task/sqlite-family-reopen.js.map +1 -0
- package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +23 -0
- package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/task/sqlite-task-store.js +50 -0
- package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
- package/core/dist/runtime-v2/task-status.d.ts +3 -1
- package/core/dist/runtime-v2/task-status.d.ts.map +1 -1
- package/core/dist/runtime-v2/task-status.js +1 -1
- package/core/dist/runtime-v2/task-status.js.map +1 -1
- package/core/dist/runtime-v2/trajectory-schema.d.ts.map +1 -1
- package/core/dist/runtime-v2/trajectory-schema.js +11 -0
- package/core/dist/runtime-v2/trajectory-schema.js.map +1 -1
- package/core/dist/runtime-v2/types/event-types.d.ts +2 -2
- package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/types/event-types.js +1 -1
- package/core/dist/runtime-v2/types/event-types.js.map +1 -1
- package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts +1 -1
- package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/types/evidence-chain-contract.js +2 -2
- package/core/dist/runtime-v2/types/evidence-chain-contract.js.map +1 -1
- package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts +3 -3
- package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/types/intervention-evidence-contract.js +1 -0
- package/core/dist/runtime-v2/types/intervention-evidence-contract.js.map +1 -1
- package/core/dist/telemetry-event.d.ts +2 -2
- package/core/dist/telemetry-event.d.ts.map +1 -1
- package/core/dist/telemetry-event.js +4 -0
- package/core/dist/telemetry-event.js.map +1 -1
- package/core/dist/trajectory-types.d.ts +2 -2
- package/core/dist/trajectory-types.d.ts.map +1 -1
- package/core/package.json +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +84 -8
- package/dist/index.js.map +1 -1
- package/dist/installer.d.ts +42 -11
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +140 -36
- package/dist/installer.js.map +1 -1
- package/dist/installers/index.d.ts +18 -7
- package/dist/installers/index.d.ts.map +1 -1
- package/dist/installers/index.js +13 -4
- package/dist/installers/index.js.map +1 -1
- package/dist/installers/workbuddy-host-installer.d.ts +246 -0
- package/dist/installers/workbuddy-host-installer.d.ts.map +1 -0
- package/dist/installers/workbuddy-host-installer.js +1419 -0
- package/dist/installers/workbuddy-host-installer.js.map +1 -0
- package/dist/mvp-config.d.ts +24 -0
- package/dist/mvp-config.d.ts.map +1 -1
- package/dist/mvp-config.js +36 -0
- package/dist/mvp-config.js.map +1 -1
- package/dist/prompts.d.ts +13 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +45 -8
- package/dist/prompts.js.map +1 -1
- package/dist/uninstaller.d.ts +1 -1
- package/dist/uninstaller.d.ts.map +1 -1
- package/dist/uninstaller.js +29 -4
- package/dist/uninstaller.js.map +1 -1
- package/host-runtime/dist/governance-signal-admission.d.ts +63 -0
- package/host-runtime/dist/governance-signal-admission.js +322 -53
- package/host-runtime/dist/host-liveness-contract.js +7 -1
- package/host-runtime/dist/index.d.ts +1 -0
- package/host-runtime/dist/index.js +5 -1
- package/host-runtime/dist/internalization-consumer-cycle.d.ts +12 -0
- package/host-runtime/dist/internalization-consumer-cycle.js +43 -1
- package/host-runtime/dist/intervention-evidence-ingress.d.ts +1 -1
- package/host-runtime/dist/product-telemetry/service.js +1 -1
- package/host-runtime/dist/production-pain-evidence.d.ts +59 -1
- package/host-runtime/dist/production-pain-evidence.js +200 -17
- package/host-runtime/dist/prompt-activation-deliverability.js +5 -4
- package/host-runtime/dist/prompt-injection-event-evidence.d.ts +1 -1
- package/host-runtime/dist/prompt-injection-event-evidence.js +5 -1
- package/host-runtime/dist/prompt-injection-projection.d.ts +14 -5
- package/host-runtime/dist/prompt-injection-projection.js +30 -16
- package/host-runtime/dist/workbuddy-worker-status.d.ts +24 -0
- package/host-runtime/dist/workbuddy-worker-status.js +52 -0
- package/host-runtime/package.json +3 -3
- package/install-layout/dist/index.d.ts +18 -1
- package/install-layout/dist/index.js +15 -2
- package/install-layout/package.json +1 -1
- package/package.json +6 -5
- package/pd-cli/dist/commands/candidate.d.ts.map +1 -1
- package/pd-cli/dist/commands/candidate.js +44 -4
- package/pd-cli/dist/commands/candidate.js.map +1 -1
- package/pd-cli/dist/commands/codex-worker.d.ts.map +1 -1
- package/pd-cli/dist/commands/codex-worker.js +30 -1
- package/pd-cli/dist/commands/codex-worker.js.map +1 -1
- package/pd-cli/dist/commands/config-doctor.d.ts.map +1 -1
- package/pd-cli/dist/commands/config-doctor.js +19 -4
- package/pd-cli/dist/commands/config-doctor.js.map +1 -1
- package/pd-cli/dist/commands/diagnose.d.ts.map +1 -1
- package/pd-cli/dist/commands/diagnose.js +4 -1
- package/pd-cli/dist/commands/diagnose.js.map +1 -1
- package/pd-cli/dist/commands/health-codex-install.d.ts +85 -0
- package/pd-cli/dist/commands/health-codex-install.d.ts.map +1 -0
- package/pd-cli/dist/commands/health-codex-install.js +207 -0
- package/pd-cli/dist/commands/health-codex-install.js.map +1 -0
- package/pd-cli/dist/commands/health-codex.d.ts.map +1 -1
- package/pd-cli/dist/commands/health-codex.js +78 -0
- package/pd-cli/dist/commands/health-codex.js.map +1 -1
- package/pd-cli/dist/commands/pain-list.d.ts +3 -2
- package/pd-cli/dist/commands/pain-list.d.ts.map +1 -1
- package/pd-cli/dist/commands/pain-list.js +10 -10
- package/pd-cli/dist/commands/pain-list.js.map +1 -1
- package/pd-cli/dist/commands/pain-record.d.ts +2 -0
- package/pd-cli/dist/commands/pain-record.d.ts.map +1 -1
- package/pd-cli/dist/commands/pain-record.js +36 -0
- package/pd-cli/dist/commands/pain-record.js.map +1 -1
- package/pd-cli/dist/commands/pain-retry.d.ts +6 -0
- package/pd-cli/dist/commands/pain-retry.d.ts.map +1 -1
- package/pd-cli/dist/commands/pain-retry.js +152 -5
- package/pd-cli/dist/commands/pain-retry.js.map +1 -1
- package/pd-cli/dist/commands/runtime-activation.d.ts +1 -1
- package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-activation.js +3 -3
- package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
- package/pd-cli/dist/commands/workbuddy-worker.d.ts +19 -0
- package/pd-cli/dist/commands/workbuddy-worker.d.ts.map +1 -0
- package/pd-cli/dist/commands/workbuddy-worker.js +168 -0
- package/pd-cli/dist/commands/workbuddy-worker.js.map +1 -0
- package/pd-cli/dist/index.js +29 -1
- package/pd-cli/dist/index.js.map +1 -1
- package/pd-cli/dist/resolve-workspace.d.ts +14 -2
- package/pd-cli/dist/resolve-workspace.d.ts.map +1 -1
- package/pd-cli/dist/resolve-workspace.js +88 -21
- package/pd-cli/dist/resolve-workspace.js.map +1 -1
- package/pd-cli/dist/services/admission-write-gate.d.ts +24 -0
- package/pd-cli/dist/services/admission-write-gate.d.ts.map +1 -0
- package/pd-cli/dist/services/admission-write-gate.js +41 -0
- package/pd-cli/dist/services/admission-write-gate.js.map +1 -0
- package/pd-cli/dist/services/pd-config-loader.d.ts +39 -7
- package/pd-cli/dist/services/pd-config-loader.d.ts.map +1 -1
- package/pd-cli/dist/services/pd-config-loader.js +111 -18
- package/pd-cli/dist/services/pd-config-loader.js.map +1 -1
- package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts +10 -0
- package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/pd-cli/dist/services/rulehost-pipeline-runner.js +59 -4
- package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/pd-cli/package.json +4 -3
- package/plugin/dist/bundle.js +564 -509
- package/plugin/dist/governance-audit.js +1 -1
- package/plugin/dist/openclaw.plugin.json +1 -1
- package/plugin/dist/rulehost-evidence.js +1 -1
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +3 -3
- package/release-manager/dist/index.d.ts.map +1 -1
- package/release-manager/dist/index.js +84 -8
- package/release-manager/dist/index.js.map +1 -1
- package/release-manager/dist/installer.d.ts +42 -11
- package/release-manager/dist/installer.d.ts.map +1 -1
- package/release-manager/dist/installer.js +140 -36
- package/release-manager/dist/installer.js.map +1 -1
- package/release-manager/dist/installers/index.d.ts +18 -7
- package/release-manager/dist/installers/index.d.ts.map +1 -1
- package/release-manager/dist/installers/index.js +13 -4
- package/release-manager/dist/installers/index.js.map +1 -1
- package/release-manager/dist/installers/workbuddy-host-installer.d.ts +246 -0
- package/release-manager/dist/installers/workbuddy-host-installer.d.ts.map +1 -0
- package/release-manager/dist/installers/workbuddy-host-installer.js +1419 -0
- package/release-manager/dist/installers/workbuddy-host-installer.js.map +1 -0
- package/release-manager/dist/mvp-config.d.ts +24 -0
- package/release-manager/dist/mvp-config.d.ts.map +1 -1
- package/release-manager/dist/mvp-config.js +36 -0
- package/release-manager/dist/mvp-config.js.map +1 -1
- package/release-manager/dist/prompts.d.ts +13 -0
- package/release-manager/dist/prompts.d.ts.map +1 -1
- package/release-manager/dist/prompts.js +45 -8
- package/release-manager/dist/prompts.js.map +1 -1
- package/release-manager/dist/uninstaller.d.ts +1 -1
- package/release-manager/dist/uninstaller.d.ts.map +1 -1
- package/release-manager/dist/uninstaller.js +29 -4
- package/release-manager/dist/uninstaller.js.map +1 -1
- package/release-manager/package.json +5 -4
- package/workbuddy-adapter/dist/codec/index.d.ts +2 -0
- package/workbuddy-adapter/dist/codec/index.js +2 -0
- package/workbuddy-adapter/dist/codec/input-decoder.d.ts +30 -0
- package/workbuddy-adapter/dist/codec/input-decoder.js +232 -0
- package/workbuddy-adapter/dist/codec/output-encoder.d.ts +62 -0
- package/workbuddy-adapter/dist/codec/output-encoder.js +75 -0
- package/workbuddy-adapter/dist/host-adapter.d.ts +33 -0
- package/workbuddy-adapter/dist/host-adapter.js +23 -0
- package/workbuddy-adapter/dist/index.d.ts +16 -0
- package/workbuddy-adapter/dist/index.js +16 -0
- package/workbuddy-adapter/dist/pd-hook.d.ts +17 -0
- package/workbuddy-adapter/dist/pd-hook.js +505 -0
- package/workbuddy-adapter/dist/tool-semantics.d.ts +22 -0
- package/workbuddy-adapter/dist/tool-semantics.js +33 -0
- package/workbuddy-adapter/dist/workbuddy-evidence-recorder.d.ts +50 -0
- package/workbuddy-adapter/dist/workbuddy-evidence-recorder.js +238 -0
- package/workbuddy-adapter/dist/worker/workspace-worker.d.ts +41 -0
- package/workbuddy-adapter/dist/worker/workspace-worker.js +219 -0
- package/workbuddy-adapter/dist/workspace-resolution.d.ts +15 -0
- package/workbuddy-adapter/dist/workspace-resolution.js +53 -0
- package/workbuddy-adapter/package.json +62 -0
- package/workbuddy-adapter/plugin/.codebuddy-plugin/plugin.json +6 -0
- package/workbuddy-adapter/plugin/hooks/hooks.json +62 -0
- package/workbuddy-adapter/plugin/scripts/wrapper.cjs +79 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { DiagnosticianOutputV1Schema } from './diagnostician-output.js';
|
|
2
2
|
import { Value } from '@sinclair/typebox/value';
|
|
3
3
|
import { evaluateCandidateAdmissions, normalizePainProvenance } from './admission-gate.js';
|
|
4
|
-
import { shouldShortCircuitEmptyEvidence } from './evidence-guards.js';
|
|
4
|
+
import { shouldShortCircuitEmptyEvidence, isOwnerStatementEvidence } from './evidence-guards.js';
|
|
5
|
+
import { ADMISSION_POLICY_VERSION, fingerprintMaterial, fingerprintStatement, } from './store/candidate/admission-document.js';
|
|
5
6
|
import { parseRootCauseCategory } from './store/pain-diagnosis/pain-diagnosis-store.js';
|
|
6
7
|
import { buildDreamerSeedFromCandidate, findExistingDreamerTask, ROUTE_CHANNEL_MAP, CANDIDATE_KIND_TO_ROUTE } from './internalization/intake-to-internalization-bridge.js';
|
|
7
8
|
import { isRetryWaitBackoffElapsed } from './internalization/internalization-task-guards.js';
|
|
@@ -19,6 +20,8 @@ export const MAX_EVIDENCE_NOTE_CHARS = 200;
|
|
|
19
20
|
* point of the field is that the Owner's own words survive verbatim.
|
|
20
21
|
*/
|
|
21
22
|
export const MAX_CORRECTION_EVIDENCE_TEXT_CHARS = 2000;
|
|
23
|
+
/** SPEC-CODEX-PAIN-CLOSURE-001 CR-05 (§9.2): automatic post-processing continuation budget. */
|
|
24
|
+
export const MAX_POST_PROCESSING_ATTEMPTS = 3;
|
|
22
25
|
/**
|
|
23
26
|
* PRI-844: sanitize (strip PD tags / redact tokens / redact absolute paths)
|
|
24
27
|
* without the 200-char generic evidence cap, then apply the defensive
|
|
@@ -105,6 +108,14 @@ function validatePersistedIngressFacts(diagnosticJson) {
|
|
|
105
108
|
return { provenance: null, evidenceCount: null, errorCode: 'diagnostic_payload_invalid:not_an_object' };
|
|
106
109
|
}
|
|
107
110
|
const record = parsed;
|
|
111
|
+
// CR-02: the persisted correction evidence (including an explicit Owner
|
|
112
|
+
// statement) is part of the re-entry material — validated, never fabricated.
|
|
113
|
+
const persistedCorrection = record.correctionEvidence;
|
|
114
|
+
const correctionEvidence = typeof persistedCorrection === 'object' && persistedCorrection !== null && !Array.isArray(persistedCorrection)
|
|
115
|
+
&& typeof persistedCorrection.text === 'string'
|
|
116
|
+
&& persistedCorrection.text.length > 0
|
|
117
|
+
? persistedCorrection
|
|
118
|
+
: undefined;
|
|
108
119
|
const evidenceCount = countSubmittedEvidence(diagnosticJson);
|
|
109
120
|
const hasProvenanceField = Object.hasOwn(record, 'provenance');
|
|
110
121
|
const provenance = hasProvenanceField ? normalizePainProvenance(record.provenance) : undefined;
|
|
@@ -132,13 +143,14 @@ function validatePersistedIngressFacts(diagnosticJson) {
|
|
|
132
143
|
provenance: deriveProvenanceFromIngressFacts(ingress.payload.origin, ingress.payload.correlation),
|
|
133
144
|
evidenceCount,
|
|
134
145
|
errorCode: null,
|
|
146
|
+
...(correctionEvidence !== undefined ? { correctionEvidence } : {}),
|
|
135
147
|
};
|
|
136
148
|
}
|
|
137
149
|
// Legacy payload without the v1 namespace: the persisted top-level
|
|
138
150
|
// provenance is the authority. This branch fabricates nothing — it only
|
|
139
151
|
// reads what the writer persisted.
|
|
140
152
|
if (provenance !== undefined) {
|
|
141
|
-
return { provenance, evidenceCount, errorCode: null };
|
|
153
|
+
return { provenance, evidenceCount, errorCode: null, ...(correctionEvidence !== undefined ? { correctionEvidence } : {}) };
|
|
142
154
|
}
|
|
143
155
|
// Legacy normalization branch (SPEC §9): no top-level provenance and no
|
|
144
156
|
// painIngress block — re-derive from the payload's own persisted fields
|
|
@@ -150,7 +162,7 @@ function validatePersistedIngressFacts(diagnosticJson) {
|
|
|
150
162
|
reason: typeof record.reasonSummary === 'string' ? record.reasonSummary : '',
|
|
151
163
|
sessionId: typeof record.sessionIdHint === 'string' ? record.sessionIdHint : undefined,
|
|
152
164
|
};
|
|
153
|
-
return { provenance: inferProvenance(recordData), evidenceCount, errorCode: null };
|
|
165
|
+
return { provenance: inferProvenance(recordData), evidenceCount, errorCode: null, ...(correctionEvidence !== undefined ? { correctionEvidence } : {}) };
|
|
154
166
|
}
|
|
155
167
|
function provenanceReason(provenance) {
|
|
156
168
|
switch (provenance) {
|
|
@@ -243,6 +255,184 @@ export async function persistPainDiagnosis(deps, opts) {
|
|
|
243
255
|
});
|
|
244
256
|
}
|
|
245
257
|
}
|
|
258
|
+
/** Resolve the persisted diagnosis material that actually minted one candidate. */
|
|
259
|
+
async function resolveCandidateRunOutput(stateManager, candidate) {
|
|
260
|
+
const refuse = (reason) => ({ ok: false, reason: `candidate_round_unresolved:${candidate.candidateId}:${reason}` });
|
|
261
|
+
const sourceRunId = typeof candidate.sourceRunId === 'string' ? candidate.sourceRunId : '';
|
|
262
|
+
if (sourceRunId.length === 0)
|
|
263
|
+
return refuse('missing_source_run_id');
|
|
264
|
+
let run;
|
|
265
|
+
try {
|
|
266
|
+
run = await stateManager.getRun(sourceRunId);
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return refuse('source_run_lookup_failed');
|
|
270
|
+
}
|
|
271
|
+
if (run === null || run === undefined)
|
|
272
|
+
return refuse('source_run_missing');
|
|
273
|
+
if (run.taskId !== candidate.taskId)
|
|
274
|
+
return refuse('source_run_task_mismatch');
|
|
275
|
+
if (run.executionStatus !== 'succeeded')
|
|
276
|
+
return refuse('source_run_not_succeeded');
|
|
277
|
+
const artifactId = typeof candidate.artifactId === 'string' ? candidate.artifactId : '';
|
|
278
|
+
if (artifactId.length > 0) {
|
|
279
|
+
let artifact;
|
|
280
|
+
try {
|
|
281
|
+
artifact = await stateManager.getArtifact(artifactId);
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return refuse('artifact_lookup_failed');
|
|
285
|
+
}
|
|
286
|
+
// Some legacy candidates may have no artifact row. A present row must
|
|
287
|
+
// identify the same task and run as the candidate's source.
|
|
288
|
+
if (artifact && (artifact.runId !== sourceRunId || artifact.taskId !== candidate.taskId)) {
|
|
289
|
+
return refuse('artifact_run_mismatch');
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
let output;
|
|
293
|
+
if (typeof run.outputPayload === 'string' && run.outputPayload.length > 0) {
|
|
294
|
+
try {
|
|
295
|
+
const parsed = JSON.parse(run.outputPayload);
|
|
296
|
+
if (Value.Check(DiagnosticianOutputV1Schema, parsed))
|
|
297
|
+
output = parsed;
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
// Refuse this candidate; never substitute a different run's output.
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (output === undefined)
|
|
304
|
+
return refuse('owning_output_unparseable');
|
|
305
|
+
return { ok: true, output, sourceRunId };
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* SPEC-CODEX-PAIN-CLOSURE-001 CR-03 (§7.2): full-gate re-evaluation from
|
|
309
|
+
* PERSISTED materials only (task diagnosticJson + candidate's source run), shared
|
|
310
|
+
* by the CLI mutation paths so their write permission can never contradict
|
|
311
|
+
* the production gate on the same material. The evaluation is PERSISTED as a
|
|
312
|
+
* new admission version (append-only; idempotent by material fingerprint).
|
|
313
|
+
*/
|
|
314
|
+
export async function evaluateAndPersistAdmissionFromStore(stateManager, candidateIds, input) {
|
|
315
|
+
// SPEC review round-4 P1: resolve the REAL diagnosis lineage. A split-pipeline
|
|
316
|
+
// candidate hangs off the diag_router SUBTASK; the PARENT diagnostician task
|
|
317
|
+
// holds the original pain input (feedback + Owner statement + evidence) and
|
|
318
|
+
// the router subtask's own succeeded runs hold the diagnostic output. A flat
|
|
319
|
+
// pipeline keeps task === ingress === output source. The parent–child relation
|
|
320
|
+
// is validated (subtask.inputRef === parent.taskId, parent kind/status) — a
|
|
321
|
+
// mismatch refuses with a lineage reason instead of re-typing materials.
|
|
322
|
+
const task = await stateManager.getTask(input.taskId);
|
|
323
|
+
if (task === null)
|
|
324
|
+
return { ok: false, reason: 'task_missing', nextAction: 'the diagnosis task no longer exists; re-run the diagnosis' };
|
|
325
|
+
// Persisted rows always carry task_kind (NOT NULL DDL); an undefined kind
|
|
326
|
+
// can only come from a partial test-double fixture and follows the flat path.
|
|
327
|
+
const isSplitSubtask = typeof task.taskKind === 'string' && task.taskKind.startsWith('diag_');
|
|
328
|
+
let ingressTask = task;
|
|
329
|
+
if (isSplitSubtask) {
|
|
330
|
+
const parentRef = typeof task.inputRef === 'string' ? task.inputRef : '';
|
|
331
|
+
const parent = parentRef.length > 0 ? await stateManager.getTask(parentRef) : null;
|
|
332
|
+
if (parent === null || parent.taskKind !== 'diagnostician' || parentRef !== task.inputRef || parent.status !== 'succeeded') {
|
|
333
|
+
return { ok: false, reason: 'diagnosis_lineage_inconsistent', nextAction: 'the router subtask does not resolve to a succeeded diagnostician parent; re-run the diagnosis before intake' };
|
|
334
|
+
}
|
|
335
|
+
ingressTask = parent;
|
|
336
|
+
}
|
|
337
|
+
else if (typeof task.taskKind === 'string' && task.taskKind !== 'diagnostician') {
|
|
338
|
+
return { ok: false, reason: 'diagnosis_lineage_inconsistent', nextAction: `candidate task kind '${task.taskKind}' is not a diagnostician task or split stage; re-run the diagnosis` };
|
|
339
|
+
}
|
|
340
|
+
const reentry = validatePersistedIngressFacts(ingressTask.diagnosticJson);
|
|
341
|
+
if (reentry.errorCode !== null) {
|
|
342
|
+
return { ok: false, reason: reentry.errorCode, nextAction: 'the persisted diagnosis payload is unusable; re-run the diagnosis before intake' };
|
|
343
|
+
}
|
|
344
|
+
const ownerStatementPresent = isOwnerStatementEvidence(reentry.correctionEvidence);
|
|
345
|
+
const candidates = (await stateManager.getCandidatesByTaskId(ingressTask.taskId))
|
|
346
|
+
.filter((candidate) => candidateIds.includes(candidate.candidateId))
|
|
347
|
+
.filter((candidate) => candidate.taskId === input.taskId || candidate.taskId === ingressTask.taskId);
|
|
348
|
+
if (candidates.length !== candidateIds.length) {
|
|
349
|
+
return { ok: false, reason: 'diagnosis_lineage_inconsistent', nextAction: 'requested candidates do not resolve through the validated diagnosis lineage; re-run the diagnosis' };
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* SPEC review round-4 P1 (round binding): the diagnostic output is resolved
|
|
353
|
+
* PER CANDIDATE from that candidate's OWN `source_run_id`, cross-checked
|
|
354
|
+
* against its `artifact_id` + `task_id`. The previous shape picked the first
|
|
355
|
+
* parseable run by reverse task order and evaluated EVERY candidate against
|
|
356
|
+
* that one shared output, so a later round (r2) silently rewrote an earlier
|
|
357
|
+
* round's (r1) admission basis — confidence, evidence count and material
|
|
358
|
+
* fingerprint all came from the wrong diagnosis.
|
|
359
|
+
*
|
|
360
|
+
* Fail-closed (rc-3/rc-9): a missing run row, a non-succeeded run, a
|
|
361
|
+
* run/task mismatch, an artifact that belongs to a different run, or an
|
|
362
|
+
* unparseable owning output refuses the candidate. It NEVER falls back to
|
|
363
|
+
* another historical successful run of the same task — substituting a
|
|
364
|
+
* different round's material is the exact defect being closed.
|
|
365
|
+
*/
|
|
366
|
+
// Resolve EVERY candidate's own round BEFORE any evaluation, so a single
|
|
367
|
+
// unresolved round refuses the whole call with zero writes instead of
|
|
368
|
+
// partially persisting a mixed-round admission set.
|
|
369
|
+
const resolvedRounds = new Map();
|
|
370
|
+
for (const candidate of candidates) {
|
|
371
|
+
const resolved = await resolveCandidateRunOutput(stateManager, candidate);
|
|
372
|
+
if (!resolved.ok) {
|
|
373
|
+
return {
|
|
374
|
+
ok: false,
|
|
375
|
+
reason: 'candidate_diagnosis_round_unresolved',
|
|
376
|
+
nextAction: `candidate ${candidate.candidateId} does not resolve to its own diagnosis run output (${resolved.reason}); re-run the diagnosis for this round before intake`,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
resolvedRounds.set(candidate.candidateId, resolved);
|
|
380
|
+
}
|
|
381
|
+
const decisions = new Map();
|
|
382
|
+
const evaluatedAt = new Date().toISOString();
|
|
383
|
+
for (const candidate of candidates) {
|
|
384
|
+
const round = resolvedRounds.get(candidate.candidateId);
|
|
385
|
+
if (round === undefined)
|
|
386
|
+
continue;
|
|
387
|
+
const { output } = round;
|
|
388
|
+
// The fingerprint is computed from THIS candidate's own material, so two
|
|
389
|
+
// rounds never collide on the same idempotency key.
|
|
390
|
+
const materialFingerprint = fingerprintMaterial([
|
|
391
|
+
JSON.stringify(output),
|
|
392
|
+
reentry.provenance,
|
|
393
|
+
String(reentry.evidenceCount),
|
|
394
|
+
ownerStatementPresent && reentry.correctionEvidence ? fingerprintStatement(reentry.correctionEvidence.text) : 'no-owner-statement',
|
|
395
|
+
round.sourceRunId,
|
|
396
|
+
candidate.candidateId,
|
|
397
|
+
]);
|
|
398
|
+
const [admission] = evaluateCandidateAdmissions([candidate], output, {
|
|
399
|
+
provenance: reentry.provenance,
|
|
400
|
+
inputEvidenceCount: reentry.evidenceCount,
|
|
401
|
+
...(ownerStatementPresent ? { ownerStatementPresent } : {}),
|
|
402
|
+
});
|
|
403
|
+
if (admission === undefined)
|
|
404
|
+
continue;
|
|
405
|
+
decisions.set(candidate.candidateId, {
|
|
406
|
+
decision: admission.admission.decision,
|
|
407
|
+
reason: admission.admission.reason,
|
|
408
|
+
nextAction: admission.admission.nextAction,
|
|
409
|
+
});
|
|
410
|
+
const persisted = await stateManager.appendAdmissionEvaluation(candidate.candidateId, {
|
|
411
|
+
evaluationId: `ae_${fingerprintMaterial([candidate.candidateId, materialFingerprint, ADMISSION_POLICY_VERSION]).slice(0, 16)}`,
|
|
412
|
+
decision: admission.admission.decision,
|
|
413
|
+
reason: admission.admission.reason,
|
|
414
|
+
nextAction: admission.admission.nextAction,
|
|
415
|
+
evaluatedAt,
|
|
416
|
+
policyVersion: ADMISSION_POLICY_VERSION,
|
|
417
|
+
provenance: reentry.provenance,
|
|
418
|
+
inputEvidenceCount: reentry.evidenceCount,
|
|
419
|
+
ownerStatementPresent,
|
|
420
|
+
recommendationKind: candidate.rawRecommendationKind,
|
|
421
|
+
confidence: output.confidence,
|
|
422
|
+
materialFingerprint,
|
|
423
|
+
materialSummary: {
|
|
424
|
+
inputEvidenceCount: reentry.evidenceCount,
|
|
425
|
+
outputEvidenceCount: output.evidence.length,
|
|
426
|
+
ownerStatementPresent,
|
|
427
|
+
...(ownerStatementPresent && reentry.correctionEvidence ? { statementFingerprint: fingerprintStatement(reentry.correctionEvidence.text) } : {}),
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
if (persisted.status === 'refused') {
|
|
431
|
+
return { ok: false, reason: `admission_persistence_failed:${persisted.reason}`, nextAction: 'inspect state.db principle_candidates; the evaluation was not saved and intake is blocked' };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return { ok: true, decisions };
|
|
435
|
+
}
|
|
246
436
|
export class PainSignalBridge {
|
|
247
437
|
stateManager;
|
|
248
438
|
runner;
|
|
@@ -293,6 +483,132 @@ export class PainSignalBridge {
|
|
|
293
483
|
* host process) may skip this; per-cycle workers MUST dispose to avoid
|
|
294
484
|
* pinning the workspace SQLite files.
|
|
295
485
|
*/
|
|
486
|
+
/**
|
|
487
|
+
* SPEC-CODEX-PAIN-CLOSURE-001 CR-05 (SPEC §9.1): continue the post-processing
|
|
488
|
+
* of a SUCCEEDED diagnosis whose candidates never finished admission /
|
|
489
|
+
* intake / seeding (crash between router `markTaskSucceeded` and this
|
|
490
|
+
* bridge's post-processing, intake/seed exceptions, consumed-but-unseeded
|
|
491
|
+
* orphans, pre-CR-03 rows with no persisted disposition).
|
|
492
|
+
*
|
|
493
|
+
* Hard guarantees:
|
|
494
|
+
* - NEVER re-runs the diagnosis LLM (materials come from the persisted
|
|
495
|
+
* run output + diagnosticJson);
|
|
496
|
+
* - idempotent (Ledger writes dedup via existsForCandidate, seeds dedup
|
|
497
|
+
* via findExistingDreamerTask, admission appends dedup by material
|
|
498
|
+
* fingerprint);
|
|
499
|
+
* - bounded (candidates whose processing state is terminal — complete /
|
|
500
|
+
* awaiting_owner / exhausted — are skipped and reported, not rescanned);
|
|
501
|
+
* - read/CLI paths never call this; only the worker continuation does.
|
|
502
|
+
*/
|
|
503
|
+
async resumeSucceededDiagnosisPostProcessing(input) {
|
|
504
|
+
const task = await this.stateManager.getTask(input.taskId);
|
|
505
|
+
if (task === null)
|
|
506
|
+
return { status: 'not_applicable', reason: 'task_missing' };
|
|
507
|
+
if (task.status !== 'succeeded')
|
|
508
|
+
return { status: 'not_applicable', reason: `task_not_succeeded:${task.status}` };
|
|
509
|
+
const candidates = await this.stateManager.getCandidatesByTaskId(input.taskId);
|
|
510
|
+
if (candidates.length === 0)
|
|
511
|
+
return { status: 'not_applicable', reason: 'no_candidates' };
|
|
512
|
+
// Reconstruct the persisted diagnosis materials (output + ingress facts +
|
|
513
|
+
// correction evidence) — exactly what a fresh post-processing round sees.
|
|
514
|
+
const reentry = validatePersistedIngressFacts(task.diagnosticJson);
|
|
515
|
+
if (reentry.errorCode !== null) {
|
|
516
|
+
// SPEC §9.2 (review round-3 P1):确定无效材料不得每周期反复占预算。每个
|
|
517
|
+
// 候选持久化一条 needs_evidence 处置(材料不可解析)+ exhausted 终态,
|
|
518
|
+
// backlog 不再返回;原因与恢复入口(修复诊断)可查、可恢复。
|
|
519
|
+
const blockedAt = new Date().toISOString();
|
|
520
|
+
for (const candidate of candidates) {
|
|
521
|
+
try {
|
|
522
|
+
await this.stateManager.appendAdmissionEvaluation(candidate.candidateId, {
|
|
523
|
+
evaluationId: `ae_blocked_${fingerprintMaterial([candidate.candidateId, reentry.errorCode]).slice(0, 16)}`,
|
|
524
|
+
decision: 'needs_evidence',
|
|
525
|
+
reason: `diagnostic_material_unusable:${reentry.errorCode}`,
|
|
526
|
+
nextAction: 'repair or re-run the diagnosis via the supported CLI command; the candidate resumes automatically afterwards',
|
|
527
|
+
evaluatedAt: blockedAt,
|
|
528
|
+
policyVersion: ADMISSION_POLICY_VERSION,
|
|
529
|
+
provenance: 'automatic_hook',
|
|
530
|
+
inputEvidenceCount: 0,
|
|
531
|
+
ownerStatementPresent: false,
|
|
532
|
+
recommendationKind: candidate.rawRecommendationKind,
|
|
533
|
+
confidence: null,
|
|
534
|
+
materialFingerprint: fingerprintMaterial(['unusable', reentry.errorCode]),
|
|
535
|
+
materialSummary: { inputEvidenceCount: 0, outputEvidenceCount: 0, ownerStatementPresent: false },
|
|
536
|
+
});
|
|
537
|
+
await this.stateManager.updateAdmissionProcessing(candidate.candidateId, {
|
|
538
|
+
state: 'exhausted',
|
|
539
|
+
attempts: MAX_POST_PROCESSING_ATTEMPTS,
|
|
540
|
+
blockedReason: `diagnostic_material_unusable:${reentry.errorCode}`,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
catch {
|
|
544
|
+
// persistence of the terminal state itself failed → stay scannable
|
|
545
|
+
// (rc-9: the next cycle retries this bookkeeping)
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
return { status: 'blocked', reason: reentry.errorCode, nextAction: 'the persisted diagnosis payload is unusable; repair or re-run the diagnosis via the supported CLI command (candidates recorded terminal manual_action_required states)' };
|
|
549
|
+
}
|
|
550
|
+
const runs = await this.stateManager.getRunsByTask(input.taskId);
|
|
551
|
+
let output;
|
|
552
|
+
const parseV1 = (payload) => {
|
|
553
|
+
if (typeof payload !== 'string' || payload.length === 0)
|
|
554
|
+
return undefined;
|
|
555
|
+
try {
|
|
556
|
+
const parsedOutput = JSON.parse(payload);
|
|
557
|
+
return Value.Check(DiagnosticianOutputV1Schema, parsedOutput) ? parsedOutput : undefined;
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
return undefined;
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
// SPEC §9.1 (review P1): the split pipeline commits candidates from the
|
|
564
|
+
// diag_router SUBTASK — the router's latest run output is the authoritative
|
|
565
|
+
// diagnostician output for continuation. The parent task's own run output
|
|
566
|
+
// is only a fallback for legacy flat pipelines (never a cache bypass: both
|
|
567
|
+
// sources are validated DiagnosticianOutputV1 persisted by the real run).
|
|
568
|
+
const routerRuns = await this.stateManager.getRunsByTask(`diag_router-${input.taskId}`);
|
|
569
|
+
for (let i = routerRuns.length - 1; i >= 0 && output === undefined; i -= 1) {
|
|
570
|
+
output = parseV1(routerRuns[i]?.outputPayload);
|
|
571
|
+
}
|
|
572
|
+
if (output === undefined) {
|
|
573
|
+
const latestRun = runs.at(-1);
|
|
574
|
+
output = parseV1(latestRun?.outputPayload);
|
|
575
|
+
}
|
|
576
|
+
// Skip candidates whose processing domain says the work is finished or
|
|
577
|
+
// blocked-with-budget-spent; they stay visible in `skipped`.
|
|
578
|
+
const skipped = [];
|
|
579
|
+
const eligible = [];
|
|
580
|
+
for (const candidate of candidates) {
|
|
581
|
+
const admission = await this.stateManager.getAdmissionDocument(candidate.candidateId);
|
|
582
|
+
if (!admission.ok) {
|
|
583
|
+
skipped.push({ candidateId: candidate.candidateId, reason: `admission_document_unreadable:${admission.reason}` });
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
const processingState = admission.document?.processing?.state;
|
|
587
|
+
if (processingState === 'complete' || processingState === 'awaiting_owner' || processingState === 'exhausted') {
|
|
588
|
+
skipped.push({ candidateId: candidate.candidateId, reason: `processing_${processingState}` });
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
eligible.push(candidate);
|
|
592
|
+
}
|
|
593
|
+
if (eligible.length === 0) {
|
|
594
|
+
return { status: 'not_applicable', reason: 'no_eligible_candidates', skipped, remainingCount: 0 };
|
|
595
|
+
}
|
|
596
|
+
const result = await this.onDiagnosisComplete({
|
|
597
|
+
taskId: input.taskId,
|
|
598
|
+
diagnosticianOutput: output,
|
|
599
|
+
painId: typeof task.inputRef === 'string' && task.inputRef.length > 0 ? task.inputRef : `diagnosis-source:${input.taskId}`,
|
|
600
|
+
provenance: reentry.provenance,
|
|
601
|
+
inputEvidenceCount: reentry.evidenceCount,
|
|
602
|
+
...(reentry.correctionEvidence !== undefined ? { correctionEvidence: reentry.correctionEvidence } : {}),
|
|
603
|
+
});
|
|
604
|
+
return {
|
|
605
|
+
status: 'resumed',
|
|
606
|
+
result,
|
|
607
|
+
processedCandidateIds: eligible.map((c) => c.candidateId),
|
|
608
|
+
skipped,
|
|
609
|
+
remainingCount: 0,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
296
612
|
async dispose() {
|
|
297
613
|
for (const resource of this.ownedResources) {
|
|
298
614
|
try {
|
|
@@ -366,7 +682,8 @@ export class PainSignalBridge {
|
|
|
366
682
|
// and source is not owner-initiated (manual/pain/skill:pain).
|
|
367
683
|
// Zero side effects: no task creation, no runner call, no ledger writes.
|
|
368
684
|
// IMPORTANT: Only short-circuit when there is NO existing task — idempotency must work.
|
|
369
|
-
if (!existingTask &&
|
|
685
|
+
if (!existingTask && !isOwnerStatementEvidence(data.correctionEvidence)
|
|
686
|
+
&& shouldShortCircuitEmptyEvidence(data.evidence?.length ?? 0, data.source)) {
|
|
370
687
|
return {
|
|
371
688
|
status: 'skipped',
|
|
372
689
|
painId,
|
|
@@ -440,6 +757,7 @@ export class PainSignalBridge {
|
|
|
440
757
|
painId,
|
|
441
758
|
provenance,
|
|
442
759
|
inputEvidenceCount: data.evidence?.length ?? 0,
|
|
760
|
+
...(data.correctionEvidence !== undefined ? { correctionEvidence: data.correctionEvidence } : {}),
|
|
443
761
|
});
|
|
444
762
|
}
|
|
445
763
|
/**
|
|
@@ -535,8 +853,57 @@ export class PainSignalBridge {
|
|
|
535
853
|
painId,
|
|
536
854
|
provenance: reentry.provenance,
|
|
537
855
|
inputEvidenceCount: reentry.evidenceCount,
|
|
856
|
+
...(reentry.correctionEvidence !== undefined ? { correctionEvidence: reentry.correctionEvidence } : {}),
|
|
538
857
|
});
|
|
539
858
|
}
|
|
859
|
+
/** Fire-and-forget processing-domain update that must never break the loop (sync-safe). */
|
|
860
|
+
safeUpdateAdmissionProcessing(candidateId, processing) {
|
|
861
|
+
try {
|
|
862
|
+
void this.stateManager.updateAdmissionProcessing(candidateId, processing).catch(() => undefined);
|
|
863
|
+
}
|
|
864
|
+
catch {
|
|
865
|
+
// partial state-manager seams (test doubles) may not implement the
|
|
866
|
+
// processing domain; the primary outcome path stays authoritative
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* SPEC-CODEX-PAIN-CLOSURE-001 CR-05 (§9.2): bounded post-processing
|
|
871
|
+
* failure bookkeeping in the independent processing domain. Default budget
|
|
872
|
+
* is 3 automatic continuation attempts; exhaustion is a saved, readable
|
|
873
|
+
* terminal state (manual_action_required), never a silent loop. The
|
|
874
|
+
* diagnosis task's own attemptCount is never consumed by this.
|
|
875
|
+
*/
|
|
876
|
+
async recordPostProcessingFailure(candidateId, rawReason) {
|
|
877
|
+
try {
|
|
878
|
+
const read = await this.stateManager.getAdmissionDocument(candidateId);
|
|
879
|
+
if (!read.ok)
|
|
880
|
+
return;
|
|
881
|
+
const current = read.document?.processing;
|
|
882
|
+
const attempts = (current?.attempts ?? 0) + 1;
|
|
883
|
+
const nowIso = new Date().toISOString();
|
|
884
|
+
const reason = rawReason.slice(0, 300);
|
|
885
|
+
const processing = attempts >= MAX_POST_PROCESSING_ATTEMPTS
|
|
886
|
+
? {
|
|
887
|
+
state: 'exhausted',
|
|
888
|
+
attempts,
|
|
889
|
+
lastAttemptAt: nowIso,
|
|
890
|
+
lastFailure: { reason, nextAction: 'manual_action_required: post-processing retry budget exhausted; inspect state.db principle_candidates and telemetry, then use the supported recovery command', at: nowIso },
|
|
891
|
+
blockedReason: `post_processing_attempts_exhausted:${rawReason.slice(0, 120)}`,
|
|
892
|
+
}
|
|
893
|
+
: {
|
|
894
|
+
state: 'in_progress',
|
|
895
|
+
attempts,
|
|
896
|
+
lastAttemptAt: nowIso,
|
|
897
|
+
lastFailure: { reason, nextAction: 'the workspace worker continuation retries this candidate automatically', at: nowIso },
|
|
898
|
+
nextRetryAt: new Date(Date.now() + Math.min(60_000 * 2 ** attempts, 15 * 60_000)).toISOString(),
|
|
899
|
+
};
|
|
900
|
+
await this.stateManager.updateAdmissionProcessing(candidateId, processing);
|
|
901
|
+
}
|
|
902
|
+
catch {
|
|
903
|
+
// telemetry for the failure itself was already emitted by the caller;
|
|
904
|
+
// this bookkeeping must never mask the original processing result.
|
|
905
|
+
}
|
|
906
|
+
}
|
|
540
907
|
/**
|
|
541
908
|
* PRI-372 (T-G): Post-diagnosis processing extracted from onPainDetected().
|
|
542
909
|
* Handles admission → intake → seedDreamer after a successful diagnosis.
|
|
@@ -544,7 +911,9 @@ export class PainSignalBridge {
|
|
|
544
911
|
* NOT call it (INF-9, PRI-857): the router only commits stage output.
|
|
545
912
|
*/
|
|
546
913
|
async onDiagnosisComplete(opts) {
|
|
547
|
-
const { taskId, diagnosticianOutput, painId, provenance, inputEvidenceCount = 0 } = opts;
|
|
914
|
+
const { taskId, diagnosticianOutput, painId, provenance, inputEvidenceCount = 0, correctionEvidence } = opts;
|
|
915
|
+
const ownerStatementPresent = isOwnerStatementEvidence(correctionEvidence);
|
|
916
|
+
const statementFingerprint = ownerStatementPresent && correctionEvidence ? fingerprintStatement(correctionEvidence.text) : undefined;
|
|
548
917
|
const candidates = await this.stateManager.getCandidatesByTaskId(taskId);
|
|
549
918
|
const ledgerEntryIds = [];
|
|
550
919
|
if (this.diagnosisPersistenceEnabled && diagnosticianOutput) {
|
|
@@ -555,18 +924,110 @@ export class PainSignalBridge {
|
|
|
555
924
|
artifactId: candidates[0]?.artifactId ?? null,
|
|
556
925
|
});
|
|
557
926
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
927
|
+
// Fresh completion and worker resume share the same per-candidate round
|
|
928
|
+
// resolver as CLI writes. Never apply the in-memory/latest task output to
|
|
929
|
+
// candidates minted by other runs of that task.
|
|
930
|
+
const candidateRounds = new Map();
|
|
931
|
+
const admissionResults = [];
|
|
932
|
+
for (const candidate of candidates) {
|
|
933
|
+
const resolved = await resolveCandidateRunOutput(this.stateManager, candidate);
|
|
934
|
+
if (!resolved.ok) {
|
|
935
|
+
admissionResults.push({
|
|
936
|
+
candidateId: candidate.candidateId,
|
|
937
|
+
recommendationKind: candidate.recommendationKind,
|
|
938
|
+
admission: {
|
|
939
|
+
decision: 'needs_evidence',
|
|
940
|
+
reason: resolved.reason,
|
|
941
|
+
nextAction: 're-run the diagnosis that produced this candidate or send it for manual review',
|
|
942
|
+
evidenceStatus: provenance,
|
|
943
|
+
},
|
|
944
|
+
});
|
|
945
|
+
continue;
|
|
946
|
+
}
|
|
947
|
+
candidateRounds.set(candidate.candidateId, resolved);
|
|
948
|
+
const [admission] = evaluateCandidateAdmissions([candidate], resolved.output, {
|
|
949
|
+
provenance,
|
|
950
|
+
inputEvidenceCount,
|
|
951
|
+
...(ownerStatementPresent ? { ownerStatementPresent } : {}),
|
|
952
|
+
});
|
|
953
|
+
if (admission !== undefined)
|
|
954
|
+
admissionResults.push(admission);
|
|
955
|
+
}
|
|
956
|
+
// SPEC-CODEX-PAIN-CLOSURE-001 CR-03 (PRI-958): persist every full-gate
|
|
957
|
+
// disposition on the candidate row BEFORE any intake/seed/consume write.
|
|
958
|
+
// A persistence failure must block this candidate's governance writes for
|
|
959
|
+
// the round (SPEC §7.1) — the diagnosis-success fact itself is untouched.
|
|
960
|
+
const admissionPersistBlocked = new Set();
|
|
961
|
+
if (candidates.length > 0) {
|
|
962
|
+
const evaluatedAt = new Date().toISOString();
|
|
963
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
964
|
+
const candidate = candidates[i];
|
|
965
|
+
const admission = admissionResults[i];
|
|
966
|
+
if (!candidate || !admission)
|
|
967
|
+
continue;
|
|
968
|
+
const round = candidateRounds.get(candidate.candidateId);
|
|
969
|
+
const materialFingerprint = round
|
|
970
|
+
? fingerprintMaterial([
|
|
971
|
+
JSON.stringify(round.output),
|
|
972
|
+
provenance,
|
|
973
|
+
String(inputEvidenceCount),
|
|
974
|
+
statementFingerprint ?? 'no-owner-statement',
|
|
975
|
+
round.sourceRunId,
|
|
976
|
+
candidate.candidateId,
|
|
977
|
+
])
|
|
978
|
+
: fingerprintMaterial([
|
|
979
|
+
'candidate-round-unresolved',
|
|
980
|
+
admission.admission.reason,
|
|
981
|
+
provenance,
|
|
982
|
+
String(inputEvidenceCount),
|
|
983
|
+
candidate.candidateId,
|
|
984
|
+
]);
|
|
985
|
+
const evaluation = {
|
|
986
|
+
evaluationId: `ae_${fingerprintMaterial([candidate.candidateId, materialFingerprint, ADMISSION_POLICY_VERSION]).slice(0, 16)}`,
|
|
987
|
+
decision: admission.admission.decision,
|
|
988
|
+
reason: admission.admission.reason,
|
|
989
|
+
nextAction: admission.admission.nextAction,
|
|
990
|
+
evaluatedAt,
|
|
991
|
+
policyVersion: ADMISSION_POLICY_VERSION,
|
|
992
|
+
provenance,
|
|
993
|
+
inputEvidenceCount,
|
|
994
|
+
ownerStatementPresent,
|
|
995
|
+
recommendationKind: candidate.rawRecommendationKind,
|
|
996
|
+
confidence: round?.output.confidence ?? null,
|
|
997
|
+
materialFingerprint,
|
|
998
|
+
materialSummary: {
|
|
999
|
+
inputEvidenceCount,
|
|
1000
|
+
outputEvidenceCount: round?.output.evidence.length ?? 0,
|
|
1001
|
+
ownerStatementPresent,
|
|
1002
|
+
...(statementFingerprint !== undefined ? { statementFingerprint } : {}),
|
|
1003
|
+
},
|
|
1004
|
+
};
|
|
1005
|
+
try {
|
|
1006
|
+
if (typeof this.stateManager.appendAdmissionEvaluation !== 'function') {
|
|
1007
|
+
throw new Error('state_manager_admission_persistence_unavailable');
|
|
1008
|
+
}
|
|
1009
|
+
const persisted = await this.stateManager.appendAdmissionEvaluation(candidate.candidateId, evaluation);
|
|
1010
|
+
if (persisted.status === 'refused') {
|
|
1011
|
+
admissionPersistBlocked.add(candidate.candidateId);
|
|
1012
|
+
this.eventEmitter?.emitTelemetry({
|
|
1013
|
+
eventType: 'admission_persistence_failed',
|
|
1014
|
+
traceId: candidate.candidateId,
|
|
1015
|
+
timestamp: evaluatedAt,
|
|
1016
|
+
payload: { reason: persisted.reason, nextAction: 'inspect state.db principle_candidates; the disposition was not saved and intake is blocked for this candidate this round' },
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
catch (persistErr) {
|
|
1021
|
+
admissionPersistBlocked.add(candidate.candidateId);
|
|
1022
|
+
this.eventEmitter?.emitTelemetry({
|
|
1023
|
+
eventType: 'admission_persistence_failed',
|
|
1024
|
+
traceId: candidate.candidateId,
|
|
1025
|
+
timestamp: evaluatedAt,
|
|
1026
|
+
payload: { reason: persistErr instanceof Error ? persistErr.message : String(persistErr), nextAction: 'inspect state.db principle_candidates; the disposition was not saved and intake is blocked for this candidate this round' },
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
570
1031
|
const seedFailureCandidateIds = [];
|
|
571
1032
|
const notInternalizable = [];
|
|
572
1033
|
// PRI-642 §10: per-candidate ledger/seed linkage for outcome reporting.
|
|
@@ -586,12 +1047,39 @@ export class PainSignalBridge {
|
|
|
586
1047
|
continue;
|
|
587
1048
|
if (admission.admission.decision !== 'admitted') {
|
|
588
1049
|
this.emitAdmissionEvent(candidate.candidateId, admission.admission);
|
|
1050
|
+
// CR-03: a non-admitted disposition is a saved, explainable terminal
|
|
1051
|
+
// state for this material — the worker must not keep rescanning it.
|
|
1052
|
+
this.safeUpdateAdmissionProcessing(candidate.candidateId, {
|
|
1053
|
+
state: 'awaiting_owner',
|
|
1054
|
+
attempts: 0,
|
|
1055
|
+
blockedReason: `${admission.admission.decision}:${admission.admission.reason}`,
|
|
1056
|
+
});
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
// CR-03 (SPEC §7.1): the disposition itself failed to persist — no
|
|
1060
|
+
// Ledger write, no consume, no seed for this candidate this round.
|
|
1061
|
+
if (admissionPersistBlocked.has(candidate.candidateId)) {
|
|
589
1062
|
continue;
|
|
590
1063
|
}
|
|
1064
|
+
// CR-05 (SPEC §9.1): resume idempotency — when a previous round already
|
|
1065
|
+
// wrote this candidate's Ledger entry, skip intake (no duplicate
|
|
1066
|
+
// writes) and fall through to seed/link recovery below.
|
|
1067
|
+
// CR-05 resume idempotency (guarded for adapters whose seam returns
|
|
1068
|
+
// undefined instead of null): only a REAL persisted entry short-circuits.
|
|
1069
|
+
const existingLedgerEntry = this.ledgerAdapter.existsForCandidate(candidate.candidateId) ?? null;
|
|
1070
|
+
if (existingLedgerEntry !== null && typeof existingLedgerEntry.id === 'string') {
|
|
1071
|
+
ledgerEntryIds.push(existingLedgerEntry.id);
|
|
1072
|
+
ledgerEntryByCandidate.set(candidate.candidateId, existingLedgerEntry.id);
|
|
1073
|
+
}
|
|
591
1074
|
// sibling 隔离:一个 candidate 的 intake 失败不应中止整批(ERR-089 模式)
|
|
592
1075
|
let intakeResult;
|
|
593
1076
|
try {
|
|
594
|
-
|
|
1077
|
+
if (existingLedgerEntry !== null && typeof existingLedgerEntry.id === 'string') {
|
|
1078
|
+
intakeResult = { outcome: 'ledger_entry', written: false, entry: existingLedgerEntry };
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
intakeResult = await this.intakeService.intake(candidate.candidateId);
|
|
1082
|
+
}
|
|
595
1083
|
}
|
|
596
1084
|
catch (intakeErr) {
|
|
597
1085
|
this.eventEmitter?.emitTelemetry({
|
|
@@ -600,6 +1088,10 @@ export class PainSignalBridge {
|
|
|
600
1088
|
timestamp: new Date().toISOString(),
|
|
601
1089
|
payload: { reason: intakeErr instanceof Error ? intakeErr.message : String(intakeErr) },
|
|
602
1090
|
});
|
|
1091
|
+
// CR-05 (SPEC §9.1): the succeeded diagnosis with an unfinished
|
|
1092
|
+
// intake is an orphan the worker continuation must pick up — record
|
|
1093
|
+
// the attempt in the independent processing domain.
|
|
1094
|
+
void this.recordPostProcessingFailure(candidate.candidateId, `intake_failed:${intakeErr instanceof Error ? intakeErr.message : String(intakeErr)}`).catch(() => undefined);
|
|
603
1095
|
continue;
|
|
604
1096
|
}
|
|
605
1097
|
// Phase 1 / PR1 (Principle Ledger write boundary): the intake service only
|
|
@@ -643,6 +1135,13 @@ export class PainSignalBridge {
|
|
|
643
1135
|
selectedPrincipleId: recommended?.selectedPrincipleId,
|
|
644
1136
|
confidence: recommended?.confidence,
|
|
645
1137
|
});
|
|
1138
|
+
// CR-05: waiting for the Owner's reuse decision is a saved
|
|
1139
|
+
// awaiting-owner state, not a technical retry.
|
|
1140
|
+
this.safeUpdateAdmissionProcessing(candidate.candidateId, {
|
|
1141
|
+
state: 'awaiting_owner',
|
|
1142
|
+
attempts: 0,
|
|
1143
|
+
blockedReason: 'reuse_pending_owner',
|
|
1144
|
+
});
|
|
646
1145
|
continue;
|
|
647
1146
|
}
|
|
648
1147
|
try {
|
|
@@ -715,12 +1214,36 @@ export class PainSignalBridge {
|
|
|
715
1214
|
timestamp: new Date().toISOString(),
|
|
716
1215
|
payload: { error: String(seedErr) },
|
|
717
1216
|
});
|
|
1217
|
+
// CR-05 (SPEC §9.1): seed failure is a post-processing orphan —
|
|
1218
|
+
// consumed-but-unseeded must stay discoverable by the worker
|
|
1219
|
+
// continuation (never hidden behind the consumed status).
|
|
1220
|
+
void this.recordPostProcessingFailure(candidate.candidateId, `seed_failed:${String(seedErr)}`).catch(() => undefined);
|
|
718
1221
|
}
|
|
719
1222
|
if (candidate.status !== 'consumed') {
|
|
720
1223
|
await this.stateManager.updateCandidateStatus(candidate.candidateId, { status: 'consumed' });
|
|
721
1224
|
}
|
|
722
1225
|
}
|
|
723
1226
|
}
|
|
1227
|
+
// CR-03/CR-05: mark the candidates whose post-processing reached a real
|
|
1228
|
+
// terminal state (seeded / ledger-written / not internalizable) complete
|
|
1229
|
+
// so the worker continuation stops rescanning them.
|
|
1230
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
1231
|
+
const candidate = candidates[i];
|
|
1232
|
+
const admission = admissionResults[i];
|
|
1233
|
+
if (!candidate || !admission || admission.admission.decision !== 'admitted')
|
|
1234
|
+
continue;
|
|
1235
|
+
if (admissionPersistBlocked.has(candidate.candidateId))
|
|
1236
|
+
continue;
|
|
1237
|
+
if (seedFailureCandidateIds.includes(candidate.candidateId))
|
|
1238
|
+
continue;
|
|
1239
|
+
if (reuseReviewRequiredByCandidate.has(candidate.candidateId))
|
|
1240
|
+
continue;
|
|
1241
|
+
this.safeUpdateAdmissionProcessing(candidate.candidateId, {
|
|
1242
|
+
state: 'complete',
|
|
1243
|
+
attempts: 0,
|
|
1244
|
+
completedAt: new Date().toISOString(),
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
724
1247
|
const seedFailureNote = seedFailureCandidateIds.length > 0
|
|
725
1248
|
? `dreamer_seed_failed:${seedFailureCandidateIds.join(',')}`
|
|
726
1249
|
: '';
|