sdd-agent-platform 0.4.0 → 0.4.2
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/README.md +36 -39
- package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js +72 -71
- package/node_modules/@sdd-agent-platform/core/dist/ai-tools.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/artifacts/ingestion.js +64 -9
- package/node_modules/@sdd-agent-platform/core/dist/artifacts/ingestion.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/artifacts/sdd-evidence.js +0 -1
- package/node_modules/@sdd-agent-platform/core/dist/artifacts/sdd-evidence.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/artifacts/sdd-result.js +26 -17
- package/node_modules/@sdd-agent-platform/core/dist/artifacts/sdd-result.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/config/init-project.d.ts +3 -0
- package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js +13 -9
- package/node_modules/@sdd-agent-platform/core/dist/config/init-project.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/config/project-config.d.ts +3 -1
- package/node_modules/@sdd-agent-platform/core/dist/config/project-config.js +7 -3
- package/node_modules/@sdd-agent-platform/core/dist/config/project-config.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.d.ts +4 -4
- package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js +16 -20
- package/node_modules/@sdd-agent-platform/core/dist/config/starter-documents.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/context/build-package.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/context/build-package.js +1 -7
- package/node_modules/@sdd-agent-platform/core/dist/context/build-package.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/context/evidence-summary.js +26 -8
- package/node_modules/@sdd-agent-platform/core/dist/context/evidence-summary.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/context/log-worker.js +2 -2
- package/node_modules/@sdd-agent-platform/core/dist/context/log-worker.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/context-offload/contracts.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/contracts.d.ts +4 -1
- package/node_modules/@sdd-agent-platform/core/dist/contracts.js +3 -0
- package/node_modules/@sdd-agent-platform/core/dist/contracts.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/delegation/model.d.ts +3 -0
- package/node_modules/@sdd-agent-platform/core/dist/delegation/validation.d.ts +3 -0
- package/node_modules/@sdd-agent-platform/core/dist/delegation/validation.js +7 -4
- package/node_modules/@sdd-agent-platform/core/dist/delegation/validation.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/document-chain.js +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/document-chain.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/project.js +8 -8
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/project.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/registries.js +0 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/registries.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js +7 -7
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-evidence.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-trust.js +0 -24
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/run-trust.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/runtime-contracts.js +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js +277 -3
- package/node_modules/@sdd-agent-platform/core/dist/doctor/doctor.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.d.ts +23 -0
- package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js +61 -0
- package/node_modules/@sdd-agent-platform/core/dist/evidence/lookup.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/evidence-runtime/contracts.d.ts +11 -1
- package/node_modules/@sdd-agent-platform/core/dist/execution/agent-execution-records.js +15 -8
- package/node_modules/@sdd-agent-platform/core/dist/execution/agent-execution-records.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js +4 -4
- package/node_modules/@sdd-agent-platform/core/dist/execution/background-executor.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js +3 -3
- package/node_modules/@sdd-agent-platform/core/dist/execution/foreground-subagents.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js +5 -4
- package/node_modules/@sdd-agent-platform/core/dist/execution/host-invocation.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js +16 -7
- package/node_modules/@sdd-agent-platform/core/dist/execution/resident-worker.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.d.ts +112 -0
- package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js +145 -0
- package/node_modules/@sdd-agent-platform/core/dist/execution/stage-team-runtime.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/governance/policy.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/governance/policy.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/instructions.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/instructions.js +59 -66
- package/node_modules/@sdd-agent-platform/core/dist/instructions.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/lifecycle/decision-gate.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.d.ts +3 -0
- package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js +55 -19
- package/node_modules/@sdd-agent-platform/core/dist/lifecycle/ship.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/orchestration/contracts.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.d.ts +12 -2
- package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js +62 -21
- package/node_modules/@sdd-agent-platform/core/dist/orchestration/runtime.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.d.ts +20 -2
- package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js +218 -18
- package/node_modules/@sdd-agent-platform/core/dist/registries/agent-capability-catalog.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js +17 -17
- package/node_modules/@sdd-agent-platform/core/dist/registries/agent-registry.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.d.ts +10 -0
- package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js +32 -2
- package/node_modules/@sdd-agent-platform/core/dist/registries/agent-runtime-static.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.d.ts +2 -17
- package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js +222 -10
- package/node_modules/@sdd-agent-platform/core/dist/registries/capability-sources.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js +9 -9
- package/node_modules/@sdd-agent-platform/core/dist/registries/command-team-runtime.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js +4 -4
- package/node_modules/@sdd-agent-platform/core/dist/registries/eval-learning-context.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js +2 -2
- package/node_modules/@sdd-agent-platform/core/dist/registries/query-status.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js +3 -3
- package/node_modules/@sdd-agent-platform/core/dist/registries/tool-capabilities.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js +2 -2
- package/node_modules/@sdd-agent-platform/core/dist/registries/tool-plugins.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js +11 -11
- package/node_modules/@sdd-agent-platform/core/dist/registries/worker-adapters.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js +12 -12
- package/node_modules/@sdd-agent-platform/core/dist/registries/workflow-gates.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/risk/contracts.d.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js +4 -4
- package/node_modules/@sdd-agent-platform/core/dist/risk/kernel.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js +4 -7
- package/node_modules/@sdd-agent-platform/core/dist/risk/legacy-adapters.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.d.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js +19 -17
- package/node_modules/@sdd-agent-platform/core/dist/risk/workflow-gates.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js +28 -13
- package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime-config.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/agent-runtime.d.ts +61 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.d.ts +3 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js +192 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/route-projection.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/routing.js +73 -17
- package/node_modules/@sdd-agent-platform/core/dist/router/routing.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.d.ts +28 -0
- package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js +373 -0
- package/node_modules/@sdd-agent-platform/core/dist/router/runtime-import.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/router/runtime-inspection.js +11 -4
- package/node_modules/@sdd-agent-platform/core/dist/router/runtime-inspection.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js +31 -3
- package/node_modules/@sdd-agent-platform/core/dist/router/runtime-validation.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.d.ts +37 -0
- package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js +235 -0
- package/node_modules/@sdd-agent-platform/core/dist/router/stage-route-binding.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/router.d.ts +2 -0
- package/node_modules/@sdd-agent-platform/core/dist/router.js +2 -0
- package/node_modules/@sdd-agent-platform/core/dist/router.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.d.ts +16 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js +168 -18
- package/node_modules/@sdd-agent-platform/core/dist/run-state/artifacts.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/run-state/events.js +2 -2
- package/node_modules/@sdd-agent-platform/core/dist/run-state/events.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.d.ts +3 -3
- package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js +22 -54
- package/node_modules/@sdd-agent-platform/core/dist/run-state/inspect-run.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/run-state/invocation-ledger.js +2 -2
- package/node_modules/@sdd-agent-platform/core/dist/run-state/invocation-ledger.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/run-state/model.d.ts +53 -9
- package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.d.ts +0 -2
- package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js +1 -3
- package/node_modules/@sdd-agent-platform/core/dist/run-state/run-index.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js +51 -34
- package/node_modules/@sdd-agent-platform/core/dist/run-state/run-state.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.d.ts +65 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js +169 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state/task-evidence.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.d.ts +8 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.js +131 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state/timing.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state.d.ts +2 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state.js +2 -0
- package/node_modules/@sdd-agent-platform/core/dist/run-state.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js +0 -3
- package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/build.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js +5 -44
- package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/findings.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/runtime-analysis/model.d.ts +1 -17
- package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.d.ts +20 -0
- package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js +109 -14
- package/node_modules/@sdd-agent-platform/core/dist/runtime-paths.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.d.ts +64 -0
- package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js +200 -0
- package/node_modules/@sdd-agent-platform/core/dist/runtime-projection-p0.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/context.js +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/context.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.d.ts +6 -0
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js +276 -0
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/document-hashes.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js +15 -4
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/run-binding.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.d.ts +21 -0
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js +139 -38
- package/node_modules/@sdd-agent-platform/core/dist/sdd-docs/task-parser.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.d.ts +55 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js +322 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-artifacts.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.d.ts +55 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js +241 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration-contracts.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.d.ts +888 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js +3870 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-collaboration.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js +8 -1
- package/node_modules/@sdd-agent-platform/core/dist/stage-runtime/runtime.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/status/project-status.d.ts +105 -1
- package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js +343 -8
- package/node_modules/@sdd-agent-platform/core/dist/status/project-status.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.d.ts +348 -3
- package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js +1017 -8
- package/node_modules/@sdd-agent-platform/core/dist/storage/runtime-store.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/subagents/contracts.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js +7 -7
- package/node_modules/@sdd-agent-platform/core/dist/subagents/runtime.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js +21 -0
- package/node_modules/@sdd-agent-platform/core/dist/test-support/fixtures.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js +19 -20
- package/node_modules/@sdd-agent-platform/core/dist/test-support/run-state.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.d.ts +44 -0
- package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js +138 -0
- package/node_modules/@sdd-agent-platform/core/dist/truth-reconciliation.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/tsconfig.tsbuildinfo +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.d.ts +0 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js +44 -37
- package/node_modules/@sdd-agent-platform/core/dist/verification/goal-verify.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.d.ts +0 -2
- package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js +19 -49
- package/node_modules/@sdd-agent-platform/core/dist/verification/rendering.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.d.ts +22 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.js +53 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/review-gate.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.d.ts +0 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js +213 -111
- package/node_modules/@sdd-agent-platform/core/dist/verification/single-task-loop.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.d.ts +28 -3
- package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js +546 -125
- package/node_modules/@sdd-agent-platform/core/dist/verification/test-runtime.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.d.ts +26 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.js +73 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/validation-cache.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.d.ts +76 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js +450 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/validation-wave.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.d.ts +3 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js +105 -30
- package/node_modules/@sdd-agent-platform/core/dist/verification/verify-contract.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/verification.d.ts +2 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification.js +2 -0
- package/node_modules/@sdd-agent-platform/core/dist/verification.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/work-units/contracts.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.d.ts +24 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js +395 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/evidence-packet.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.d.ts +4 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js +164 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/hard-checks.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.d.ts +4 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js +182 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/policy.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.d.ts +88 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.js +2 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-gate/types.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js +17 -3
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.d.ts +8 -4
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js +25 -11
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/dependencies.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.d.ts +37 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js +188 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/latest-eligible-run.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.d.ts +40 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.js +110 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/migration-recovery.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.d.ts +12 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.js +63 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/repair-contract.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.d.ts +21 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.js +95 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve-task-run.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.d.ts +80 -3
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js +674 -41
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/resolve.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.d.ts +228 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.js +452 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state/runtime-projections.js.map +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state.d.ts +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state.js +1 -0
- package/node_modules/@sdd-agent-platform/core/dist/workflow-state.js.map +1 -1
- package/node_modules/@sdd-agent-platform/core/package.json +3 -3
- package/node_modules/@sdd-agent-platform/core/src/ai-tools.test.ts +49 -1
- package/node_modules/@sdd-agent-platform/core/src/ai-tools.ts +72 -71
- package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.test.ts +38 -0
- package/node_modules/@sdd-agent-platform/core/src/artifacts/ingestion.ts +65 -9
- package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-evidence.ts +0 -1
- package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.test.ts +52 -6
- package/node_modules/@sdd-agent-platform/core/src/artifacts/sdd-result.ts +26 -17
- package/node_modules/@sdd-agent-platform/core/src/config/init-project.test.ts +44 -29
- package/node_modules/@sdd-agent-platform/core/src/config/init-project.ts +15 -11
- package/node_modules/@sdd-agent-platform/core/src/config/project-config.ts +10 -4
- package/node_modules/@sdd-agent-platform/core/src/config/starter-documents.ts +17 -20
- package/node_modules/@sdd-agent-platform/core/src/context/build-package.ts +2 -8
- package/node_modules/@sdd-agent-platform/core/src/context/context-build.test.ts +3 -2
- package/node_modules/@sdd-agent-platform/core/src/context/evidence-summary.ts +27 -8
- package/node_modules/@sdd-agent-platform/core/src/context/log-worker.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/src/context-offload/contracts.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/contracts.ts +4 -1
- package/node_modules/@sdd-agent-platform/core/src/delegation/model.ts +3 -0
- package/node_modules/@sdd-agent-platform/core/src/delegation/validation.ts +8 -5
- package/node_modules/@sdd-agent-platform/core/src/doctor/checks/document-chain.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/doctor/checks/project.ts +8 -8
- package/node_modules/@sdd-agent-platform/core/src/doctor/checks/registries.ts +0 -1
- package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-evidence.ts +7 -7
- package/node_modules/@sdd-agent-platform/core/src/doctor/checks/run-trust.ts +0 -21
- package/node_modules/@sdd-agent-platform/core/src/doctor/checks/runtime-contracts.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.test.ts +217 -7
- package/node_modules/@sdd-agent-platform/core/src/doctor/doctor.ts +301 -3
- package/node_modules/@sdd-agent-platform/core/src/evidence/lookup.ts +88 -0
- package/node_modules/@sdd-agent-platform/core/src/evidence-runtime/contracts.ts +12 -1
- package/node_modules/@sdd-agent-platform/core/src/execution/agent-execution-records.ts +16 -11
- package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.test.ts +57 -2
- package/node_modules/@sdd-agent-platform/core/src/execution/background-executor.ts +4 -4
- package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.test.ts +11 -2
- package/node_modules/@sdd-agent-platform/core/src/execution/foreground-subagents.ts +3 -3
- package/node_modules/@sdd-agent-platform/core/src/execution/host-invocation.ts +5 -4
- package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.test.ts +17 -1
- package/node_modules/@sdd-agent-platform/core/src/execution/resident-worker.ts +16 -7
- package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.test.ts +102 -0
- package/node_modules/@sdd-agent-platform/core/src/execution/stage-team-runtime.ts +271 -0
- package/node_modules/@sdd-agent-platform/core/src/execution/wave-executor.test.ts +14 -0
- package/node_modules/@sdd-agent-platform/core/src/governance/policy.test.ts +10 -0
- package/node_modules/@sdd-agent-platform/core/src/governance/policy.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/src/instructions.test.ts +34 -13
- package/node_modules/@sdd-agent-platform/core/src/instructions.ts +60 -67
- package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.test.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/lifecycle/decision-gate.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.test.ts +47 -0
- package/node_modules/@sdd-agent-platform/core/src/lifecycle/ship.ts +58 -19
- package/node_modules/@sdd-agent-platform/core/src/orchestration/contracts.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/orchestration/runtime.ts +74 -22
- package/node_modules/@sdd-agent-platform/core/src/phase8-contracts.test.ts +3 -3
- package/node_modules/@sdd-agent-platform/core/src/phase8-risk-kernel.test.ts +8 -3
- package/node_modules/@sdd-agent-platform/core/src/planning/task-graph.test.ts +2 -0
- package/node_modules/@sdd-agent-platform/core/src/planning/wave-plan.test.ts +3 -0
- package/node_modules/@sdd-agent-platform/core/src/registries/agent-capability-catalog.ts +319 -20
- package/node_modules/@sdd-agent-platform/core/src/registries/agent-registry.ts +17 -17
- package/node_modules/@sdd-agent-platform/core/src/registries/agent-runtime-static.ts +42 -2
- package/node_modules/@sdd-agent-platform/core/src/registries/capability-sources.ts +238 -15
- package/node_modules/@sdd-agent-platform/core/src/registries/command-team-runtime.ts +10 -10
- package/node_modules/@sdd-agent-platform/core/src/registries/eval-learning-context.ts +4 -4
- package/node_modules/@sdd-agent-platform/core/src/registries/query-status.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/src/registries/registries.test.ts +45 -4
- package/node_modules/@sdd-agent-platform/core/src/registries/tool-capabilities.ts +3 -3
- package/node_modules/@sdd-agent-platform/core/src/registries/tool-plugins.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/src/registries/worker-adapters.ts +11 -11
- package/node_modules/@sdd-agent-platform/core/src/registries/workflow-gates.ts +12 -12
- package/node_modules/@sdd-agent-platform/core/src/risk/contracts.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/src/risk/kernel.ts +4 -4
- package/node_modules/@sdd-agent-platform/core/src/risk/legacy-adapters.ts +4 -7
- package/node_modules/@sdd-agent-platform/core/src/risk/workflow-gates.ts +20 -18
- package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime-config.ts +32 -13
- package/node_modules/@sdd-agent-platform/core/src/router/agent-runtime.ts +68 -1
- package/node_modules/@sdd-agent-platform/core/src/router/route-projection.ts +212 -1
- package/node_modules/@sdd-agent-platform/core/src/router/route-sdd-task.test.ts +391 -6
- package/node_modules/@sdd-agent-platform/core/src/router/routing.ts +78 -17
- package/node_modules/@sdd-agent-platform/core/src/router/runtime-import.ts +453 -0
- package/node_modules/@sdd-agent-platform/core/src/router/runtime-inspection.ts +11 -4
- package/node_modules/@sdd-agent-platform/core/src/router/runtime-validation.ts +32 -3
- package/node_modules/@sdd-agent-platform/core/src/router/stage-route-binding.ts +279 -0
- package/node_modules/@sdd-agent-platform/core/src/router.ts +2 -0
- package/node_modules/@sdd-agent-platform/core/src/run-state/artifacts.ts +173 -18
- package/node_modules/@sdd-agent-platform/core/src/run-state/events.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/src/run-state/inspect-run.ts +24 -59
- package/node_modules/@sdd-agent-platform/core/src/run-state/invocation-ledger.ts +2 -2
- package/node_modules/@sdd-agent-platform/core/src/run-state/model.ts +59 -9
- package/node_modules/@sdd-agent-platform/core/src/run-state/run-index.ts +1 -5
- package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.test.ts +53 -2
- package/node_modules/@sdd-agent-platform/core/src/run-state/run-state.ts +55 -41
- package/node_modules/@sdd-agent-platform/core/src/run-state/task-evidence.ts +252 -0
- package/node_modules/@sdd-agent-platform/core/src/run-state/timing.ts +146 -0
- package/node_modules/@sdd-agent-platform/core/src/run-state.ts +2 -0
- package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/build.ts +0 -3
- package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/findings.ts +6 -46
- package/node_modules/@sdd-agent-platform/core/src/runtime-analysis/model.ts +1 -13
- package/node_modules/@sdd-agent-platform/core/src/runtime-analysis.test.ts +0 -2
- package/node_modules/@sdd-agent-platform/core/src/runtime-paths.ts +131 -14
- package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.test.ts +96 -0
- package/node_modules/@sdd-agent-platform/core/src/runtime-projection-p0.ts +292 -0
- package/node_modules/@sdd-agent-platform/core/src/sdd-docs/context.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/sdd-docs/document-hashes.ts +306 -0
- package/node_modules/@sdd-agent-platform/core/src/sdd-docs/run-binding.ts +15 -4
- package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.test.ts +261 -0
- package/node_modules/@sdd-agent-platform/core/src/sdd-docs/task-parser.ts +169 -41
- package/node_modules/@sdd-agent-platform/core/src/stage-artifacts.ts +450 -0
- package/node_modules/@sdd-agent-platform/core/src/stage-collaboration-contracts.ts +322 -0
- package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.test.ts +2903 -0
- package/node_modules/@sdd-agent-platform/core/src/stage-collaboration.ts +5831 -0
- package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.test.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/stage-runtime/runtime.ts +9 -1
- package/node_modules/@sdd-agent-platform/core/src/status/project-status.test.ts +239 -16
- package/node_modules/@sdd-agent-platform/core/src/status/project-status.ts +497 -8
- package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.test.ts +560 -4
- package/node_modules/@sdd-agent-platform/core/src/storage/runtime-store.ts +1510 -9
- package/node_modules/@sdd-agent-platform/core/src/subagents/contracts.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.test.ts +3 -3
- package/node_modules/@sdd-agent-platform/core/src/subagents/runtime.ts +7 -7
- package/node_modules/@sdd-agent-platform/core/src/test-support/fixtures.ts +21 -0
- package/node_modules/@sdd-agent-platform/core/src/test-support/run-state.ts +20 -20
- package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.test.ts +72 -0
- package/node_modules/@sdd-agent-platform/core/src/truth-reconciliation.ts +177 -0
- package/node_modules/@sdd-agent-platform/core/src/verification/goal-verify.test.ts +13 -87
- package/node_modules/@sdd-agent-platform/core/src/verification/goal-verify.ts +46 -42
- package/node_modules/@sdd-agent-platform/core/src/verification/rendering.ts +18 -52
- package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.test.ts +84 -0
- package/node_modules/@sdd-agent-platform/core/src/verification/review-gate.ts +77 -0
- package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.test.ts +138 -64
- package/node_modules/@sdd-agent-platform/core/src/verification/single-task-loop.ts +226 -116
- package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.test.ts +148 -48
- package/node_modules/@sdd-agent-platform/core/src/verification/test-runtime.ts +619 -136
- package/node_modules/@sdd-agent-platform/core/src/verification/validation-cache.ts +106 -0
- package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.test.ts +383 -0
- package/node_modules/@sdd-agent-platform/core/src/verification/validation-wave.ts +556 -0
- package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.test.ts +131 -8
- package/node_modules/@sdd-agent-platform/core/src/verification/verify-contract.ts +117 -30
- package/node_modules/@sdd-agent-platform/core/src/verification.ts +2 -0
- package/node_modules/@sdd-agent-platform/core/src/work-units/contracts.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/workflow-gate/evidence-packet.ts +425 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.test.ts +507 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-gate/hard-checks.ts +182 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.test.ts +174 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-gate/policy.ts +194 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-gate/types.ts +115 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/affected-file-conflicts.ts +19 -4
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.test.ts +1 -1
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/dependencies.ts +33 -11
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/latest-eligible-run.ts +224 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/migration-recovery.ts +158 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/repair-contract.ts +77 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve-task-run.ts +114 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.test.ts +851 -9
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/resolve.ts +862 -45
- package/node_modules/@sdd-agent-platform/core/src/workflow-state/runtime-projections.ts +712 -0
- package/node_modules/@sdd-agent-platform/core/src/workflow-state.ts +1 -0
- package/package.json +1 -1
- package/packages/cli/dist/args.js +2 -2
- package/packages/cli/dist/args.js.map +1 -1
- package/packages/cli/dist/commands/ai-tools.js +13 -2
- package/packages/cli/dist/commands/ai-tools.js.map +1 -1
- package/packages/cli/dist/commands/context.js +1 -1
- package/packages/cli/dist/commands/context.js.map +1 -1
- package/packages/cli/dist/commands/execution.js +49 -1
- package/packages/cli/dist/commands/execution.js.map +1 -1
- package/packages/cli/dist/commands/governance.js +1 -1
- package/packages/cli/dist/commands/governance.js.map +1 -1
- package/packages/cli/dist/commands/init.js +6 -1
- package/packages/cli/dist/commands/init.js.map +1 -1
- package/packages/cli/dist/commands/lifecycle.js +15 -2
- package/packages/cli/dist/commands/lifecycle.js.map +1 -1
- package/packages/cli/dist/commands/registry/runtime.js +48 -2
- package/packages/cli/dist/commands/registry/runtime.js.map +1 -1
- package/packages/cli/dist/commands/run.js +52 -2
- package/packages/cli/dist/commands/run.js.map +1 -1
- package/packages/cli/dist/commands/stage-close.d.ts +6 -0
- package/packages/cli/dist/commands/stage-close.js +295 -0
- package/packages/cli/dist/commands/stage-close.js.map +1 -0
- package/packages/cli/dist/commands/status.js +70 -4
- package/packages/cli/dist/commands/status.js.map +1 -1
- package/packages/cli/dist/commands/tasks.js +4 -4
- package/packages/cli/dist/commands/tasks.js.map +1 -1
- package/packages/cli/dist/commands/test.js +272 -5
- package/packages/cli/dist/commands/test.js.map +1 -1
- package/packages/cli/dist/commands/verifies.js +9 -5
- package/packages/cli/dist/commands/verifies.js.map +1 -1
- package/packages/cli/dist/commands/verify.js +257 -20
- package/packages/cli/dist/commands/verify.js.map +1 -1
- package/packages/cli/dist/dispatch.js +4 -9
- package/packages/cli/dist/dispatch.js.map +1 -1
- package/packages/cli/dist/help.js +42 -27
- package/packages/cli/dist/help.js.map +1 -1
- package/packages/cli/dist/renderers/doctor.js +1 -1
- package/packages/cli/dist/renderers/doctor.js.map +1 -1
- package/packages/cli/dist/renderers/execution.js +1 -1
- package/packages/cli/dist/renderers/execution.js.map +1 -1
- package/packages/cli/dist/renderers/json.d.ts +1 -0
- package/packages/cli/dist/renderers/json.js +3 -0
- package/packages/cli/dist/renderers/json.js.map +1 -1
- package/packages/cli/dist/renderers/registry-runtime.d.ts +2 -1
- package/packages/cli/dist/renderers/registry-runtime.js +27 -2
- package/packages/cli/dist/renderers/registry-runtime.js.map +1 -1
- package/packages/cli/dist/renderers/router.js +5 -3
- package/packages/cli/dist/renderers/router.js.map +1 -1
- package/packages/cli/dist/renderers/workflow.d.ts +0 -4
- package/packages/cli/dist/renderers/workflow.js +46 -84
- package/packages/cli/dist/renderers/workflow.js.map +1 -1
- package/packages/cli/dist/skill-import-args.d.ts +10 -0
- package/packages/cli/dist/skill-import-args.js +47 -0
- package/packages/cli/dist/skill-import-args.js.map +1 -0
- package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/cli/package.json +2 -2
- package/packages/core/dist/ai-tools.js +72 -71
- package/packages/core/dist/ai-tools.js.map +1 -1
- package/packages/core/dist/artifacts/ingestion.js +64 -9
- package/packages/core/dist/artifacts/ingestion.js.map +1 -1
- package/packages/core/dist/artifacts/sdd-evidence.js +0 -1
- package/packages/core/dist/artifacts/sdd-evidence.js.map +1 -1
- package/packages/core/dist/artifacts/sdd-result.js +26 -17
- package/packages/core/dist/artifacts/sdd-result.js.map +1 -1
- package/packages/core/dist/config/init-project.d.ts +3 -0
- package/packages/core/dist/config/init-project.js +13 -9
- package/packages/core/dist/config/init-project.js.map +1 -1
- package/packages/core/dist/config/project-config.d.ts +3 -1
- package/packages/core/dist/config/project-config.js +7 -3
- package/packages/core/dist/config/project-config.js.map +1 -1
- package/packages/core/dist/config/starter-documents.d.ts +4 -4
- package/packages/core/dist/config/starter-documents.js +16 -20
- package/packages/core/dist/config/starter-documents.js.map +1 -1
- package/packages/core/dist/context/build-package.d.ts +1 -1
- package/packages/core/dist/context/build-package.js +1 -7
- package/packages/core/dist/context/build-package.js.map +1 -1
- package/packages/core/dist/context/evidence-summary.js +26 -8
- package/packages/core/dist/context/evidence-summary.js.map +1 -1
- package/packages/core/dist/context/log-worker.js +2 -2
- package/packages/core/dist/context/log-worker.js.map +1 -1
- package/packages/core/dist/context-offload/contracts.d.ts +1 -1
- package/packages/core/dist/contracts.d.ts +4 -1
- package/packages/core/dist/contracts.js +3 -0
- package/packages/core/dist/contracts.js.map +1 -1
- package/packages/core/dist/delegation/model.d.ts +3 -0
- package/packages/core/dist/delegation/validation.d.ts +3 -0
- package/packages/core/dist/delegation/validation.js +7 -4
- package/packages/core/dist/delegation/validation.js.map +1 -1
- package/packages/core/dist/doctor/checks/document-chain.js +1 -1
- package/packages/core/dist/doctor/checks/document-chain.js.map +1 -1
- package/packages/core/dist/doctor/checks/project.js +8 -8
- package/packages/core/dist/doctor/checks/project.js.map +1 -1
- package/packages/core/dist/doctor/checks/registries.js +0 -1
- package/packages/core/dist/doctor/checks/registries.js.map +1 -1
- package/packages/core/dist/doctor/checks/run-evidence.js +7 -7
- package/packages/core/dist/doctor/checks/run-evidence.js.map +1 -1
- package/packages/core/dist/doctor/checks/run-trust.js +0 -24
- package/packages/core/dist/doctor/checks/run-trust.js.map +1 -1
- package/packages/core/dist/doctor/checks/runtime-contracts.js +1 -1
- package/packages/core/dist/doctor/checks/runtime-contracts.js.map +1 -1
- package/packages/core/dist/doctor/doctor.js +277 -3
- package/packages/core/dist/doctor/doctor.js.map +1 -1
- package/packages/core/dist/evidence/lookup.d.ts +23 -0
- package/packages/core/dist/evidence/lookup.js +61 -0
- package/packages/core/dist/evidence/lookup.js.map +1 -0
- package/packages/core/dist/evidence-runtime/contracts.d.ts +11 -1
- package/packages/core/dist/execution/agent-execution-records.js +15 -8
- package/packages/core/dist/execution/agent-execution-records.js.map +1 -1
- package/packages/core/dist/execution/background-executor.js +4 -4
- package/packages/core/dist/execution/background-executor.js.map +1 -1
- package/packages/core/dist/execution/foreground-subagents.js +3 -3
- package/packages/core/dist/execution/foreground-subagents.js.map +1 -1
- package/packages/core/dist/execution/host-invocation.js +5 -4
- package/packages/core/dist/execution/host-invocation.js.map +1 -1
- package/packages/core/dist/execution/resident-worker.js +16 -7
- package/packages/core/dist/execution/resident-worker.js.map +1 -1
- package/packages/core/dist/execution/stage-team-runtime.d.ts +112 -0
- package/packages/core/dist/execution/stage-team-runtime.js +145 -0
- package/packages/core/dist/execution/stage-team-runtime.js.map +1 -0
- package/packages/core/dist/governance/policy.d.ts +1 -1
- package/packages/core/dist/governance/policy.js +1 -1
- package/packages/core/dist/governance/policy.js.map +1 -1
- package/packages/core/dist/instructions.d.ts +1 -1
- package/packages/core/dist/instructions.js +59 -66
- package/packages/core/dist/instructions.js.map +1 -1
- package/packages/core/dist/lifecycle/decision-gate.js +1 -1
- package/packages/core/dist/lifecycle/decision-gate.js.map +1 -1
- package/packages/core/dist/lifecycle/ship.d.ts +3 -0
- package/packages/core/dist/lifecycle/ship.js +55 -19
- package/packages/core/dist/lifecycle/ship.js.map +1 -1
- package/packages/core/dist/orchestration/contracts.d.ts +1 -1
- package/packages/core/dist/orchestration/runtime.d.ts +12 -2
- package/packages/core/dist/orchestration/runtime.js +62 -21
- package/packages/core/dist/orchestration/runtime.js.map +1 -1
- package/packages/core/dist/registries/agent-capability-catalog.d.ts +20 -2
- package/packages/core/dist/registries/agent-capability-catalog.js +218 -18
- package/packages/core/dist/registries/agent-capability-catalog.js.map +1 -1
- package/packages/core/dist/registries/agent-registry.js +17 -17
- package/packages/core/dist/registries/agent-registry.js.map +1 -1
- package/packages/core/dist/registries/agent-runtime-static.d.ts +10 -0
- package/packages/core/dist/registries/agent-runtime-static.js +32 -2
- package/packages/core/dist/registries/agent-runtime-static.js.map +1 -1
- package/packages/core/dist/registries/capability-sources.d.ts +2 -17
- package/packages/core/dist/registries/capability-sources.js +222 -10
- package/packages/core/dist/registries/capability-sources.js.map +1 -1
- package/packages/core/dist/registries/command-team-runtime.d.ts +1 -1
- package/packages/core/dist/registries/command-team-runtime.js +9 -9
- package/packages/core/dist/registries/command-team-runtime.js.map +1 -1
- package/packages/core/dist/registries/eval-learning-context.js +4 -4
- package/packages/core/dist/registries/eval-learning-context.js.map +1 -1
- package/packages/core/dist/registries/query-status.js +2 -2
- package/packages/core/dist/registries/query-status.js.map +1 -1
- package/packages/core/dist/registries/tool-capabilities.js +3 -3
- package/packages/core/dist/registries/tool-capabilities.js.map +1 -1
- package/packages/core/dist/registries/tool-plugins.js +2 -2
- package/packages/core/dist/registries/tool-plugins.js.map +1 -1
- package/packages/core/dist/registries/worker-adapters.js +11 -11
- package/packages/core/dist/registries/worker-adapters.js.map +1 -1
- package/packages/core/dist/registries/workflow-gates.js +12 -12
- package/packages/core/dist/registries/workflow-gates.js.map +1 -1
- package/packages/core/dist/risk/contracts.d.ts +2 -2
- package/packages/core/dist/risk/kernel.js +4 -4
- package/packages/core/dist/risk/kernel.js.map +1 -1
- package/packages/core/dist/risk/legacy-adapters.js +4 -7
- package/packages/core/dist/risk/legacy-adapters.js.map +1 -1
- package/packages/core/dist/risk/workflow-gates.d.ts +2 -2
- package/packages/core/dist/risk/workflow-gates.js +19 -17
- package/packages/core/dist/risk/workflow-gates.js.map +1 -1
- package/packages/core/dist/router/agent-runtime-config.js +28 -13
- package/packages/core/dist/router/agent-runtime-config.js.map +1 -1
- package/packages/core/dist/router/agent-runtime.d.ts +61 -1
- package/packages/core/dist/router/route-projection.d.ts +3 -1
- package/packages/core/dist/router/route-projection.js +192 -1
- package/packages/core/dist/router/route-projection.js.map +1 -1
- package/packages/core/dist/router/routing.js +73 -17
- package/packages/core/dist/router/routing.js.map +1 -1
- package/packages/core/dist/router/runtime-import.d.ts +28 -0
- package/packages/core/dist/router/runtime-import.js +373 -0
- package/packages/core/dist/router/runtime-import.js.map +1 -0
- package/packages/core/dist/router/runtime-inspection.js +11 -4
- package/packages/core/dist/router/runtime-inspection.js.map +1 -1
- package/packages/core/dist/router/runtime-validation.js +31 -3
- package/packages/core/dist/router/runtime-validation.js.map +1 -1
- package/packages/core/dist/router/stage-route-binding.d.ts +37 -0
- package/packages/core/dist/router/stage-route-binding.js +235 -0
- package/packages/core/dist/router/stage-route-binding.js.map +1 -0
- package/packages/core/dist/router.d.ts +2 -0
- package/packages/core/dist/router.js +2 -0
- package/packages/core/dist/router.js.map +1 -1
- package/packages/core/dist/run-state/artifacts.d.ts +16 -0
- package/packages/core/dist/run-state/artifacts.js +168 -18
- package/packages/core/dist/run-state/artifacts.js.map +1 -1
- package/packages/core/dist/run-state/events.js +2 -2
- package/packages/core/dist/run-state/events.js.map +1 -1
- package/packages/core/dist/run-state/inspect-run.d.ts +3 -3
- package/packages/core/dist/run-state/inspect-run.js +22 -54
- package/packages/core/dist/run-state/inspect-run.js.map +1 -1
- package/packages/core/dist/run-state/invocation-ledger.js +2 -2
- package/packages/core/dist/run-state/invocation-ledger.js.map +1 -1
- package/packages/core/dist/run-state/model.d.ts +53 -9
- package/packages/core/dist/run-state/run-index.d.ts +0 -2
- package/packages/core/dist/run-state/run-index.js +1 -3
- package/packages/core/dist/run-state/run-index.js.map +1 -1
- package/packages/core/dist/run-state/run-state.js +51 -34
- package/packages/core/dist/run-state/run-state.js.map +1 -1
- package/packages/core/dist/run-state/task-evidence.d.ts +65 -0
- package/packages/core/dist/run-state/task-evidence.js +169 -0
- package/packages/core/dist/run-state/task-evidence.js.map +1 -0
- package/packages/core/dist/run-state/timing.d.ts +8 -0
- package/packages/core/dist/run-state/timing.js +131 -0
- package/packages/core/dist/run-state/timing.js.map +1 -0
- package/packages/core/dist/run-state.d.ts +2 -0
- package/packages/core/dist/run-state.js +2 -0
- package/packages/core/dist/run-state.js.map +1 -1
- package/packages/core/dist/runtime-analysis/build.js +0 -3
- package/packages/core/dist/runtime-analysis/build.js.map +1 -1
- package/packages/core/dist/runtime-analysis/findings.js +5 -44
- package/packages/core/dist/runtime-analysis/findings.js.map +1 -1
- package/packages/core/dist/runtime-analysis/model.d.ts +1 -17
- package/packages/core/dist/runtime-paths.d.ts +20 -0
- package/packages/core/dist/runtime-paths.js +109 -14
- package/packages/core/dist/runtime-paths.js.map +1 -1
- package/packages/core/dist/runtime-projection-p0.d.ts +64 -0
- package/packages/core/dist/runtime-projection-p0.js +200 -0
- package/packages/core/dist/runtime-projection-p0.js.map +1 -0
- package/packages/core/dist/sdd-docs/context.js +1 -1
- package/packages/core/dist/sdd-docs/context.js.map +1 -1
- package/packages/core/dist/sdd-docs/document-hashes.d.ts +6 -0
- package/packages/core/dist/sdd-docs/document-hashes.js +276 -0
- package/packages/core/dist/sdd-docs/document-hashes.js.map +1 -0
- package/packages/core/dist/sdd-docs/run-binding.d.ts +1 -1
- package/packages/core/dist/sdd-docs/run-binding.js +15 -4
- package/packages/core/dist/sdd-docs/run-binding.js.map +1 -1
- package/packages/core/dist/sdd-docs/task-parser.d.ts +21 -0
- package/packages/core/dist/sdd-docs/task-parser.js +139 -38
- package/packages/core/dist/sdd-docs/task-parser.js.map +1 -1
- package/packages/core/dist/stage-artifacts.d.ts +55 -0
- package/packages/core/dist/stage-artifacts.js +322 -0
- package/packages/core/dist/stage-artifacts.js.map +1 -0
- package/packages/core/dist/stage-collaboration-contracts.d.ts +55 -0
- package/packages/core/dist/stage-collaboration-contracts.js +241 -0
- package/packages/core/dist/stage-collaboration-contracts.js.map +1 -0
- package/packages/core/dist/stage-collaboration.d.ts +888 -0
- package/packages/core/dist/stage-collaboration.js +3870 -0
- package/packages/core/dist/stage-collaboration.js.map +1 -0
- package/packages/core/dist/stage-runtime/runtime.js +8 -1
- package/packages/core/dist/stage-runtime/runtime.js.map +1 -1
- package/packages/core/dist/status/project-status.d.ts +105 -1
- package/packages/core/dist/status/project-status.js +343 -8
- package/packages/core/dist/status/project-status.js.map +1 -1
- package/packages/core/dist/storage/runtime-store.d.ts +348 -3
- package/packages/core/dist/storage/runtime-store.js +1017 -8
- package/packages/core/dist/storage/runtime-store.js.map +1 -1
- package/packages/core/dist/subagents/contracts.d.ts +1 -1
- package/packages/core/dist/subagents/runtime.js +7 -7
- package/packages/core/dist/subagents/runtime.js.map +1 -1
- package/packages/core/dist/test-support/fixtures.js +21 -0
- package/packages/core/dist/test-support/fixtures.js.map +1 -1
- package/packages/core/dist/test-support/run-state.d.ts +1 -1
- package/packages/core/dist/test-support/run-state.js +19 -20
- package/packages/core/dist/test-support/run-state.js.map +1 -1
- package/packages/core/dist/truth-reconciliation.d.ts +44 -0
- package/packages/core/dist/truth-reconciliation.js +138 -0
- package/packages/core/dist/truth-reconciliation.js.map +1 -0
- package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/core/dist/verification/goal-verify.d.ts +0 -1
- package/packages/core/dist/verification/goal-verify.js +44 -37
- package/packages/core/dist/verification/goal-verify.js.map +1 -1
- package/packages/core/dist/verification/rendering.d.ts +0 -2
- package/packages/core/dist/verification/rendering.js +19 -49
- package/packages/core/dist/verification/rendering.js.map +1 -1
- package/packages/core/dist/verification/review-gate.d.ts +22 -0
- package/packages/core/dist/verification/review-gate.js +53 -0
- package/packages/core/dist/verification/review-gate.js.map +1 -0
- package/packages/core/dist/verification/single-task-loop.d.ts +0 -1
- package/packages/core/dist/verification/single-task-loop.js +213 -111
- package/packages/core/dist/verification/single-task-loop.js.map +1 -1
- package/packages/core/dist/verification/test-runtime.d.ts +28 -3
- package/packages/core/dist/verification/test-runtime.js +546 -125
- package/packages/core/dist/verification/test-runtime.js.map +1 -1
- package/packages/core/dist/verification/validation-cache.d.ts +26 -0
- package/packages/core/dist/verification/validation-cache.js +73 -0
- package/packages/core/dist/verification/validation-cache.js.map +1 -0
- package/packages/core/dist/verification/validation-wave.d.ts +76 -0
- package/packages/core/dist/verification/validation-wave.js +450 -0
- package/packages/core/dist/verification/validation-wave.js.map +1 -0
- package/packages/core/dist/verification/verify-contract.d.ts +3 -1
- package/packages/core/dist/verification/verify-contract.js +105 -30
- package/packages/core/dist/verification/verify-contract.js.map +1 -1
- package/packages/core/dist/verification.d.ts +2 -0
- package/packages/core/dist/verification.js +2 -0
- package/packages/core/dist/verification.js.map +1 -1
- package/packages/core/dist/work-units/contracts.d.ts +1 -1
- package/packages/core/dist/workflow-gate/evidence-packet.d.ts +24 -0
- package/packages/core/dist/workflow-gate/evidence-packet.js +395 -0
- package/packages/core/dist/workflow-gate/evidence-packet.js.map +1 -0
- package/packages/core/dist/workflow-gate/hard-checks.d.ts +4 -0
- package/packages/core/dist/workflow-gate/hard-checks.js +164 -0
- package/packages/core/dist/workflow-gate/hard-checks.js.map +1 -0
- package/packages/core/dist/workflow-gate/policy.d.ts +4 -0
- package/packages/core/dist/workflow-gate/policy.js +182 -0
- package/packages/core/dist/workflow-gate/policy.js.map +1 -0
- package/packages/core/dist/workflow-gate/types.d.ts +88 -0
- package/packages/core/dist/workflow-gate/types.js +2 -0
- package/packages/core/dist/workflow-gate/types.js.map +1 -0
- package/packages/core/dist/workflow-state/affected-file-conflicts.d.ts +1 -1
- package/packages/core/dist/workflow-state/affected-file-conflicts.js +17 -3
- package/packages/core/dist/workflow-state/affected-file-conflicts.js.map +1 -1
- package/packages/core/dist/workflow-state/dependencies.d.ts +8 -4
- package/packages/core/dist/workflow-state/dependencies.js +25 -11
- package/packages/core/dist/workflow-state/dependencies.js.map +1 -1
- package/packages/core/dist/workflow-state/latest-eligible-run.d.ts +37 -0
- package/packages/core/dist/workflow-state/latest-eligible-run.js +188 -0
- package/packages/core/dist/workflow-state/latest-eligible-run.js.map +1 -0
- package/packages/core/dist/workflow-state/migration-recovery.d.ts +40 -0
- package/packages/core/dist/workflow-state/migration-recovery.js +110 -0
- package/packages/core/dist/workflow-state/migration-recovery.js.map +1 -0
- package/packages/core/dist/workflow-state/repair-contract.d.ts +12 -0
- package/packages/core/dist/workflow-state/repair-contract.js +63 -0
- package/packages/core/dist/workflow-state/repair-contract.js.map +1 -0
- package/packages/core/dist/workflow-state/resolve-task-run.d.ts +21 -0
- package/packages/core/dist/workflow-state/resolve-task-run.js +95 -0
- package/packages/core/dist/workflow-state/resolve-task-run.js.map +1 -0
- package/packages/core/dist/workflow-state/resolve.d.ts +80 -3
- package/packages/core/dist/workflow-state/resolve.js +674 -41
- package/packages/core/dist/workflow-state/resolve.js.map +1 -1
- package/packages/core/dist/workflow-state/runtime-projections.d.ts +228 -0
- package/packages/core/dist/workflow-state/runtime-projections.js +452 -0
- package/packages/core/dist/workflow-state/runtime-projections.js.map +1 -0
- package/packages/core/dist/workflow-state.d.ts +1 -0
- package/packages/core/dist/workflow-state.js +1 -0
- package/packages/core/dist/workflow-state.js.map +1 -1
- package/packages/core/package.json +3 -3
- package/node_modules/@sdd-agent-platform/core/dist/doctor/render.d.ts +0 -2
- package/node_modules/@sdd-agent-platform/core/dist/doctor/render.js +0 -44
- package/node_modules/@sdd-agent-platform/core/dist/doctor/render.js.map +0 -1
- package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.d.ts +0 -17
- package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js +0 -221
- package/node_modules/@sdd-agent-platform/core/dist/sync-back/apply.js.map +0 -1
- package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.d.ts +0 -91
- package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js +0 -395
- package/node_modules/@sdd-agent-platform/core/dist/sync-back/inspect.js.map +0 -1
- package/node_modules/@sdd-agent-platform/core/dist/sync-back.d.ts +0 -2
- package/node_modules/@sdd-agent-platform/core/dist/sync-back.js +0 -3
- package/node_modules/@sdd-agent-platform/core/dist/sync-back.js.map +0 -1
- package/node_modules/@sdd-agent-platform/core/src/sync-back/apply.ts +0 -248
- package/node_modules/@sdd-agent-platform/core/src/sync-back/inspect.ts +0 -522
- package/node_modules/@sdd-agent-platform/core/src/sync-back/sync-back.test.ts +0 -446
- package/node_modules/@sdd-agent-platform/core/src/sync-back.ts +0 -2
- package/packages/cli/dist/commands/artifact.d.ts +0 -6
- package/packages/cli/dist/commands/artifact.js +0 -168
- package/packages/cli/dist/commands/artifact.js.map +0 -1
- package/packages/cli/dist/commands/sync-back.d.ts +0 -6
- package/packages/cli/dist/commands/sync-back.js +0 -82
- package/packages/cli/dist/commands/sync-back.js.map +0 -1
- package/packages/cli/dist/renderers/artifacts.d.ts +0 -5
- package/packages/cli/dist/renderers/artifacts.js +0 -43
- package/packages/cli/dist/renderers/artifacts.js.map +0 -1
- package/packages/core/dist/doctor/render.d.ts +0 -2
- package/packages/core/dist/doctor/render.js +0 -44
- package/packages/core/dist/doctor/render.js.map +0 -1
- package/packages/core/dist/sync-back/apply.d.ts +0 -17
- package/packages/core/dist/sync-back/apply.js +0 -221
- package/packages/core/dist/sync-back/apply.js.map +0 -1
- package/packages/core/dist/sync-back/inspect.d.ts +0 -91
- package/packages/core/dist/sync-back/inspect.js +0 -395
- package/packages/core/dist/sync-back/inspect.js.map +0 -1
- package/packages/core/dist/sync-back.d.ts +0 -2
- package/packages/core/dist/sync-back.js +0 -3
- package/packages/core/dist/sync-back.js.map +0 -1
|
@@ -0,0 +1,2903 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { access, mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
import { LIFECYCLE_RISK_DECISION_CONTRACT_VERSION, STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION, VERIFY_DOCUMENT_CONTRACT_VERSION, WORKFLOW_HANDOFF_CONTRACT_VERSION, type RuntimeRef, type RuntimeRefKind, type SddStage } from './contracts.js';
|
|
8
|
+
import type { LifecycleRiskDecision } from './risk/contracts.js';
|
|
9
|
+
import { initProject } from './config/init-project.js';
|
|
10
|
+
import {
|
|
11
|
+
adjudicateSpecStageClosureRequest,
|
|
12
|
+
buildDoStageWorkOrder,
|
|
13
|
+
buildGoalVerifyStageWorkOrder,
|
|
14
|
+
buildPlanStageWorkOrder,
|
|
15
|
+
buildShipStageWorkOrder,
|
|
16
|
+
buildSpecStageWorkOrder,
|
|
17
|
+
buildTasksStageWorkOrder,
|
|
18
|
+
buildTestStageWorkOrder,
|
|
19
|
+
buildVerifiesStageWorkOrder,
|
|
20
|
+
deriveSpecCollaborationProfile,
|
|
21
|
+
inspectFullStageChain,
|
|
22
|
+
inspectSpecCollaborationHealth,
|
|
23
|
+
reconcileDoCollaborationClosure,
|
|
24
|
+
reconcileGoalVerifyCollaborationClosure,
|
|
25
|
+
reconcileShipCollaborationClosure,
|
|
26
|
+
reconcilePlanCollaborationClosure,
|
|
27
|
+
reconcileTasksCollaborationClosure,
|
|
28
|
+
reconcileTestCollaborationClosure,
|
|
29
|
+
reconcileVerifiesCollaborationClosure,
|
|
30
|
+
reconcileSpecCollaborationClosure,
|
|
31
|
+
readTruthAlignmentProjection,
|
|
32
|
+
recordSpecCollaborationAdjudicationProjection,
|
|
33
|
+
type CapabilityFinding,
|
|
34
|
+
type SpecDocumentCandidate,
|
|
35
|
+
type SpecManagerCoordinationRecord,
|
|
36
|
+
type SpecProposalItemKind,
|
|
37
|
+
type SpecReviewResult,
|
|
38
|
+
type StageClosureRequest,
|
|
39
|
+
type StageCollaborationProfile
|
|
40
|
+
} from './stage-collaboration.js';
|
|
41
|
+
import { inspectWorkflowStageHandoff, readStageRunProjection, readWorkflowHandoffProjection, recordStageRunProjection, recordWorkflowHandoffProjection } from './stage-runtime/runtime.js';
|
|
42
|
+
import { hashDocumentContent } from './sdd-docs/document-hashes.js';
|
|
43
|
+
import { listRuntimeStageArtifacts, listRuntimeStageCollaborationContracts, recordRuntimeStageArtifact } from './storage/runtime-store.js';
|
|
44
|
+
import { expectedStageArtifactContracts, readMarkdownArtifact, validateStageArtifactFrontmatter } from './stage-artifacts.js';
|
|
45
|
+
|
|
46
|
+
const generatedAt = '2026-01-01T00:00:00.000Z';
|
|
47
|
+
|
|
48
|
+
test('direct lifecycle keeps spec collaboration as runtime no-op', () => {
|
|
49
|
+
const profile = deriveSpecCollaborationProfile(decision('direct', ['do', 'test']), generatedAt);
|
|
50
|
+
const result = adjudicateSpecStageClosureRequest({ profile, generatedAt });
|
|
51
|
+
|
|
52
|
+
assert.equal(profile.contract, STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION);
|
|
53
|
+
assert.equal(profile.intensity, 'noop');
|
|
54
|
+
assert.equal(profile.required, false);
|
|
55
|
+
assert.equal(profile.requiresStageClosure, false);
|
|
56
|
+
assert.equal(result.health, 'no-op');
|
|
57
|
+
assert.equal(result.stageDecision?.status, 'skipped');
|
|
58
|
+
assert.equal(result.handoffPacket, null);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('full lifecycle creates spec-manager work order with agent-team proposal-only authority', () => {
|
|
62
|
+
const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
|
|
63
|
+
const workOrder = buildSpecStageWorkOrder(profile, ref('projection', 'profile/spec'), generatedAt);
|
|
64
|
+
|
|
65
|
+
assert.equal(profile.intensity, 'team-required');
|
|
66
|
+
assert.equal(profile.required, true);
|
|
67
|
+
assert.equal(profile.requiresStageClosure, true);
|
|
68
|
+
assert.equal(workOrder.authorityCeiling, 'proposal_input');
|
|
69
|
+
assert.deepEqual(workOrder.forbiddenActions, ['stage_pass', 'risk_decision', 'gate_pass', 'ship_ready', 'truth_alignment_approved']);
|
|
70
|
+
assert.equal(workOrder.stageManager, 'spec-manager');
|
|
71
|
+
assert.deepEqual(workOrder.agentTeam, ['scout', 'spec-drafter', 'spec-reviewer']);
|
|
72
|
+
assert.deepEqual(workOrder.requiredCapabilities, ['norm_discovery', 'uncertainty_resolution']);
|
|
73
|
+
assert.equal(workOrder.collaborationPlan.topology, 'team-required');
|
|
74
|
+
assert.equal(workOrder.collaborationPlan.fanIn, 'runtime_adjudication');
|
|
75
|
+
assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'agent' && participant.id === 'spec-manager'), true);
|
|
76
|
+
assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'subagent' && participant.id === 'spec-reviewer' && participant.role === 'spec-document-reviewer'), true);
|
|
77
|
+
assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'subagent' && participant.id === 'scout' && participant.parallelGroup === 'discovery'), true);
|
|
78
|
+
assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'skill' && participant.id === 'cap.norm_discovery'), true);
|
|
79
|
+
assert.equal(workOrder.collaborationPlan.participants.some((participant) => participant.kind === 'material-pack' && participant.id === 'project-norms'), true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('stage run projection keeps completed stage from being downgraded by later rejected diagnostics', async () => {
|
|
83
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-run-terminal-'));
|
|
84
|
+
try {
|
|
85
|
+
await initProject(root);
|
|
86
|
+
const scope = { branch: 'feature' };
|
|
87
|
+
const completed = testStageRun('do-completed', scope, 'completed', '2026-01-01T00:00:00.000Z');
|
|
88
|
+
const rejected = testStageRun('do-rejected', scope, 'failed', '2026-01-01T00:01:00.000Z');
|
|
89
|
+
|
|
90
|
+
await recordStageRunProjection(root, completed);
|
|
91
|
+
const result = await recordStageRunProjection(root, rejected);
|
|
92
|
+
const stored = await readStageRunProjection(root, scope, 'do');
|
|
93
|
+
|
|
94
|
+
assert.equal(result.status, 'unchanged');
|
|
95
|
+
assert.equal(stored?.payload.id, completed.id);
|
|
96
|
+
assert.equal(stored?.payload.status, 'completed');
|
|
97
|
+
} finally {
|
|
98
|
+
await rm(root, { recursive: true, force: true });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('blocking ambiguity produces clarification gate instead of handoff', () => {
|
|
103
|
+
const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
|
|
104
|
+
const team = stageClosure(profile, { items: [candidateItem('amb-1', 'blocking_ambiguity')] });
|
|
105
|
+
const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt });
|
|
106
|
+
|
|
107
|
+
assert.equal(result.health, 'needs_clarification');
|
|
108
|
+
assert.equal(result.clarificationGate?.status, 'needs_clarification');
|
|
109
|
+
assert.deepEqual(result.clarificationGate?.blockingItemIds, ['amb-1']);
|
|
110
|
+
assert.equal(result.stageDecision, null);
|
|
111
|
+
assert.equal(result.handoffPacket, null);
|
|
112
|
+
assert.equal(profile.collaborationPlan.topology, 'team-required');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('answered clarification becomes spec decision and spec to plan handoff', () => {
|
|
116
|
+
const profile = deriveSpecCollaborationProfile(decision('research', ['spec', 'plan', 'verifies']), generatedAt);
|
|
117
|
+
const team = stageClosure(profile, { items: [candidateItem('amb-1', 'blocking_ambiguity'), candidateItem('find-1', 'advisory_finding')] });
|
|
118
|
+
const result = adjudicateSpecStageClosureRequest({
|
|
119
|
+
profile,
|
|
120
|
+
closureRequest: team.closureRequest,
|
|
121
|
+
answerRefs: [ref('external', 'answer/spec-ambiguity')],
|
|
122
|
+
generatedAt
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
assert.equal(result.health, 'ready_for_plan');
|
|
126
|
+
assert.equal(result.specDecisionRecord?.source, 'clarification-answer');
|
|
127
|
+
assert.deepEqual(result.specDecisionRecord?.acceptedItemIds, ['amb-1', 'find-1']);
|
|
128
|
+
assert.equal(result.stageDecision?.status, 'completed');
|
|
129
|
+
assert.equal(result.handoffPacket?.fromStage, 'spec');
|
|
130
|
+
assert.equal(result.handoffPacket?.toStage, 'plan');
|
|
131
|
+
assert.deepEqual(result.handoffPacket?.recommendedNextStageRoles, ['role.norm-scout', 'role.uncertainty-reviewer']);
|
|
132
|
+
assert.equal(profile.collaborationPlan.topology, 'parallel-research');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('valid stage closure is accepted into runtime-owned stage decision and handoff', () => {
|
|
136
|
+
const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
|
|
137
|
+
const team = stageClosure(profile);
|
|
138
|
+
const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt });
|
|
139
|
+
|
|
140
|
+
assert.equal(result.health, 'ready_for_plan');
|
|
141
|
+
assert.deepEqual(result.acceptedItemIds, ['find-1', 'cap-1', 'handoff-1']);
|
|
142
|
+
assert.equal(result.specDecisionRecord?.source, 'stage-closure-request');
|
|
143
|
+
assert.equal(result.stageDecision?.health, 'ready_for_plan');
|
|
144
|
+
assert.equal(result.handoffPacket?.status, 'proposed');
|
|
145
|
+
assert.deepEqual(result.handoffPacket?.recommendedNextStageRoles, ['role.norm-scout', 'role.performance-planner', 'role.verification-designer']);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('authority-violating closure request is rejected with actionable metadata', () => {
|
|
149
|
+
const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
|
|
150
|
+
const team = stageClosure(profile, { authorityAttempts: ['stage_pass'] });
|
|
151
|
+
const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt, retryBudgetRemaining: 2 });
|
|
152
|
+
|
|
153
|
+
assert.equal(result.health, 'rejected');
|
|
154
|
+
assert.equal(result.rejection?.reasonCode, 'authority_violation');
|
|
155
|
+
assert.equal(result.rejection?.retryAllowed, true);
|
|
156
|
+
assert.equal(result.rejection?.retryBudgetRemaining, 2);
|
|
157
|
+
assert.equal(result.rejection?.fallbackRoute, 'revise-proposal');
|
|
158
|
+
assert.match(result.rejection?.requiredNextAction ?? '', /Remove workflow-authority/);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('candidate items must carry refs', () => {
|
|
162
|
+
const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
|
|
163
|
+
const team = stageClosure(profile, { items: [candidateItem('find-1', 'advisory_finding', [])] });
|
|
164
|
+
const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt });
|
|
165
|
+
|
|
166
|
+
assert.equal(result.health, 'rejected');
|
|
167
|
+
assert.equal(result.rejection?.reasonCode, 'missing_refs');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('missing spec-reviewer result returns prioritized stage-manager next action', () => {
|
|
171
|
+
const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
|
|
172
|
+
const team = stageClosure(profile);
|
|
173
|
+
const closureRequest = { ...team.closureRequest, reviewRefs: [], reviewResults: [] };
|
|
174
|
+
const result = adjudicateSpecStageClosureRequest({ profile, closureRequest, generatedAt });
|
|
175
|
+
|
|
176
|
+
assert.equal(result.health, 'rejected');
|
|
177
|
+
assert.equal(result.rejection?.reasonCode, 'missing_required_review');
|
|
178
|
+
assert.equal(result.nextActions[0]?.reasonCode, 'missing_required_review');
|
|
179
|
+
assert.equal(result.nextActions[0]?.owner, 'stage-manager');
|
|
180
|
+
assert.equal(result.nextActions[0]?.kind, 'collect_required_review');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('spec collaboration adjudication projection exposes compact health', async () => {
|
|
184
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-collaboration-'));
|
|
185
|
+
try {
|
|
186
|
+
await initProject(root);
|
|
187
|
+
const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
|
|
188
|
+
const team = stageClosure(profile);
|
|
189
|
+
const result = adjudicateSpecStageClosureRequest({ profile, closureRequest: team.closureRequest, generatedAt });
|
|
190
|
+
|
|
191
|
+
await recordSpecCollaborationAdjudicationProjection(root, result);
|
|
192
|
+
const health = await inspectSpecCollaborationHealth(root, 'master');
|
|
193
|
+
|
|
194
|
+
assert.equal(health.status, 'ready_for_plan');
|
|
195
|
+
assert.equal(health.projectionCount, 1);
|
|
196
|
+
assert.equal(health.latestHandoffId, result.handoffPacket?.handoffId);
|
|
197
|
+
assert.equal(health.latestClarificationGateId, null);
|
|
198
|
+
assert.equal(health.latestRejectionReason, null);
|
|
199
|
+
assert.match(health.reasons.join(' '), /ready for plan handoff/);
|
|
200
|
+
} finally {
|
|
201
|
+
await rm(root, { recursive: true, force: true });
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('full-stage chain diagnostic reports missing projections before stage closure', async () => {
|
|
206
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-full-chain-missing-'));
|
|
207
|
+
try {
|
|
208
|
+
await initProject(root);
|
|
209
|
+
const chain = await inspectFullStageChain(root, 'master');
|
|
210
|
+
|
|
211
|
+
assert.equal(chain.contract, 'sdd-full-stage-chain-diagnostic-v1');
|
|
212
|
+
assert.equal(chain.status, 'missing');
|
|
213
|
+
assert.equal(chain.stages.length, 8);
|
|
214
|
+
assert.equal(chain.projectionCounts.adjudications, 0);
|
|
215
|
+
assert.equal(chain.projectionCounts.completedStages, 0);
|
|
216
|
+
assert.equal(chain.projectionCounts.validatedCollaborationContracts, 0);
|
|
217
|
+
assert.equal(chain.finalHealth, null);
|
|
218
|
+
assert.equal(chain.stages.every((stage) => stage.projectionRef === null), true);
|
|
219
|
+
} finally {
|
|
220
|
+
await rm(root, { recursive: true, force: true });
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('ready closure accepts reviewed spec document, agent-authored evidence, and sqlite projections', async () => {
|
|
225
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-closure-ready-'));
|
|
226
|
+
try {
|
|
227
|
+
await initProject(root);
|
|
228
|
+
const reviewedSpecContent = reviewedSpec();
|
|
229
|
+
const specHash = hashDocumentContent(reviewedSpecContent);
|
|
230
|
+
await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpecContent, 'utf8');
|
|
231
|
+
await writeSpecStageArtifacts(root, { specHash });
|
|
232
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
233
|
+
|
|
234
|
+
const closure = await reconcileSpecCollaborationClosure(root, {
|
|
235
|
+
decision: riskDecision,
|
|
236
|
+
generatedAt,
|
|
237
|
+
runId: 'run-ready-closure'
|
|
238
|
+
});
|
|
239
|
+
const specContent = await readFile(path.join(root, 'specs', 'master', 'spec.md'), 'utf8');
|
|
240
|
+
const health = await inspectSpecCollaborationHealth(root, 'master');
|
|
241
|
+
const handoff = await inspectWorkflowStageHandoff(root, 'master');
|
|
242
|
+
const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'spec' });
|
|
243
|
+
const registeredContracts = await listRuntimeStageCollaborationContracts(root, { branchSlug: 'master', stage: 'spec' });
|
|
244
|
+
|
|
245
|
+
assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'accepted');
|
|
246
|
+
assert.equal(closure.acceptedSpecRef?.ref, 'specs/master/spec.md');
|
|
247
|
+
assert.equal(closure.acceptedSpecRef?.hash, specHash);
|
|
248
|
+
assert.equal(specContent, reviewedSpecContent);
|
|
249
|
+
assert.equal(closure.artifactRefs.every((artifact) => artifact.ref.startsWith('.sdd/runs/master/spec/')), true);
|
|
250
|
+
assert.equal(closure.artifactRefs.some((artifact) => artifact.ref.startsWith(['artifacts', 'phase9.1'].join('/'))), false);
|
|
251
|
+
assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'scout_context', 'spec_review']);
|
|
252
|
+
assert.equal(registeredContracts.length, 1);
|
|
253
|
+
assert.equal(registeredContracts[0]?.status, 'validated');
|
|
254
|
+
assert.equal(closure.registeredCollaborationContracts[0]?.ref, '.sdd/runs/master/spec/spec-collaboration-contract-v1.md');
|
|
255
|
+
assert.equal(closure.adjudication.closureRefs?.collaborationContractRef?.ref, '.sdd/runs/master/spec/spec-collaboration-contract-v1.md');
|
|
256
|
+
assert.equal(health.status, 'ready_for_plan');
|
|
257
|
+
assert.equal(health.latest?.closureRefs?.runRef.ref, 'run-ready-closure');
|
|
258
|
+
assert.equal(health.latest?.closureRefs?.acceptedSpecRef?.ref, 'specs/master/spec.md');
|
|
259
|
+
assert.equal(handoff.status, 'fresh');
|
|
260
|
+
assert.equal(handoff.latestStageRun?.status, 'completed');
|
|
261
|
+
assert.equal(handoff.latestHandoff?.requiredInputRefs[0]?.ref, 'specs/master/spec.md');
|
|
262
|
+
assert.equal(handoff.latestHandoff?.requiredInputRefs[0]?.hash, specHash);
|
|
263
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.1')));
|
|
264
|
+
} finally {
|
|
265
|
+
await rm(root, { recursive: true, force: true });
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('ready structural closure rejects missing collaboration contract without handoff', async () => {
|
|
270
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-closure-missing-contract-'));
|
|
271
|
+
try {
|
|
272
|
+
await initProject(root);
|
|
273
|
+
const reviewedSpecContent = reviewedSpec();
|
|
274
|
+
const specHash = hashDocumentContent(reviewedSpecContent);
|
|
275
|
+
await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpecContent, 'utf8');
|
|
276
|
+
await writeSpecStageArtifacts(root, { specHash, omitContract: true });
|
|
277
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
278
|
+
|
|
279
|
+
const closure = await reconcileSpecCollaborationClosure(root, {
|
|
280
|
+
decision: riskDecision,
|
|
281
|
+
generatedAt,
|
|
282
|
+
runId: 'run-missing-contract-closure'
|
|
283
|
+
});
|
|
284
|
+
const handoff = await inspectWorkflowStageHandoff(root, 'master');
|
|
285
|
+
const registeredContracts = await listRuntimeStageCollaborationContracts(root, { branchSlug: 'master', stage: 'spec' });
|
|
286
|
+
|
|
287
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
288
|
+
assert.equal(closure.adjudication.rejection?.reasonCode, 'invalid_proposal');
|
|
289
|
+
assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'not_accepted_wrong_ref');
|
|
290
|
+
assert.equal(closure.adjudication.closureRefs?.collaborationContractRef, null);
|
|
291
|
+
assert.equal(closure.acceptedSpecRef, null);
|
|
292
|
+
assert.equal(registeredContracts.length, 0);
|
|
293
|
+
assert.equal(handoff.status, 'missing');
|
|
294
|
+
} finally {
|
|
295
|
+
await rm(root, { recursive: true, force: true });
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('stage artifact registration supports plan-stage review and manager artifacts', async () => {
|
|
300
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-artifact-registration-'));
|
|
301
|
+
try {
|
|
302
|
+
await initProject(root);
|
|
303
|
+
const planContent = reviewedPlan();
|
|
304
|
+
const planHash = hashDocumentContent(planContent);
|
|
305
|
+
await mkdir(path.join(root, 'specs', 'master'), { recursive: true });
|
|
306
|
+
await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
|
|
307
|
+
await writePlanStageArtifacts(root, { planHash });
|
|
308
|
+
|
|
309
|
+
const reviewArtifact = await readMarkdownArtifact(root, '.sdd/runs/master/plan/plan-review-v1.md');
|
|
310
|
+
const reviewRecord = validateStageArtifactFrontmatter({
|
|
311
|
+
branch: 'master',
|
|
312
|
+
stage: 'plan',
|
|
313
|
+
ref: reviewArtifact.ref,
|
|
314
|
+
hash: reviewArtifact.hash,
|
|
315
|
+
frontmatter: reviewArtifact.frontmatter,
|
|
316
|
+
registeredAt: generatedAt
|
|
317
|
+
});
|
|
318
|
+
const managerArtifact = await readMarkdownArtifact(root, '.sdd/runs/master/plan/plan-manager-v1.md');
|
|
319
|
+
const managerRecord = validateStageArtifactFrontmatter({
|
|
320
|
+
branch: 'master',
|
|
321
|
+
stage: 'plan',
|
|
322
|
+
ref: managerArtifact.ref,
|
|
323
|
+
hash: managerArtifact.hash,
|
|
324
|
+
frontmatter: managerArtifact.frontmatter,
|
|
325
|
+
registeredAt: generatedAt
|
|
326
|
+
});
|
|
327
|
+
await recordRuntimeStageArtifact(root, reviewRecord);
|
|
328
|
+
await recordRuntimeStageArtifact(root, managerRecord);
|
|
329
|
+
|
|
330
|
+
const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'plan' });
|
|
331
|
+
|
|
332
|
+
assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'plan_review']);
|
|
333
|
+
assert.equal(reviewRecord.targetRef?.ref, 'specs/master/plan.md');
|
|
334
|
+
assert.equal(reviewRecord.targetHash, planHash);
|
|
335
|
+
assert.equal(managerRecord.reviewRef?.ref, '.sdd/runs/master/plan/plan-review-v1.md');
|
|
336
|
+
assert.equal(managerRecord.reviewHash, reviewArtifact.hash);
|
|
337
|
+
assert.throws(() => validateStageArtifactFrontmatter({
|
|
338
|
+
branch: 'master',
|
|
339
|
+
stage: 'plan',
|
|
340
|
+
ref: reviewArtifact.ref,
|
|
341
|
+
hash: reviewArtifact.hash,
|
|
342
|
+
frontmatter: { ...reviewArtifact.frontmatter, producer: 'spec-reviewer' },
|
|
343
|
+
registeredAt: generatedAt
|
|
344
|
+
}), /must be produced by plan-reviewer/);
|
|
345
|
+
} finally {
|
|
346
|
+
await rm(root, { recursive: true, force: true });
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
test('stage artifact registration supports review and manager pairs through ship', async () => {
|
|
351
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-artifact-registration-'));
|
|
352
|
+
try {
|
|
353
|
+
await initProject(root);
|
|
354
|
+
for (const stageCase of stageArtifactPairCases) {
|
|
355
|
+
await writeStageArtifactPair(root, stageCase);
|
|
356
|
+
const reviewArtifact = await readMarkdownArtifact(root, `.sdd/runs/master/${stageCase.stage}/${stageCase.reviewFile}`);
|
|
357
|
+
const reviewRecord = validateStageArtifactFrontmatter({
|
|
358
|
+
branch: 'master',
|
|
359
|
+
stage: stageCase.stage,
|
|
360
|
+
ref: reviewArtifact.ref,
|
|
361
|
+
hash: reviewArtifact.hash,
|
|
362
|
+
frontmatter: reviewArtifact.frontmatter,
|
|
363
|
+
registeredAt: generatedAt
|
|
364
|
+
});
|
|
365
|
+
const managerArtifact = await readMarkdownArtifact(root, `.sdd/runs/master/${stageCase.stage}/${stageCase.managerFile}`);
|
|
366
|
+
const managerRecord = validateStageArtifactFrontmatter({
|
|
367
|
+
branch: 'master',
|
|
368
|
+
stage: stageCase.stage,
|
|
369
|
+
ref: managerArtifact.ref,
|
|
370
|
+
hash: managerArtifact.hash,
|
|
371
|
+
frontmatter: managerArtifact.frontmatter,
|
|
372
|
+
registeredAt: generatedAt
|
|
373
|
+
});
|
|
374
|
+
await recordRuntimeStageArtifact(root, reviewRecord);
|
|
375
|
+
await recordRuntimeStageArtifact(root, managerRecord);
|
|
376
|
+
|
|
377
|
+
const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: stageCase.stage });
|
|
378
|
+
|
|
379
|
+
assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', stageCase.reviewKind].sort());
|
|
380
|
+
assert.equal(reviewRecord.producer, stageCase.reviewProducer);
|
|
381
|
+
assert.equal(reviewRecord.targetRef?.ref, stageCase.targetRef);
|
|
382
|
+
assert.equal(managerRecord.producer, stageCase.managerProducer);
|
|
383
|
+
assert.equal(managerRecord.reviewRef?.ref, `.sdd/runs/master/${stageCase.stage}/${stageCase.reviewFile}`);
|
|
384
|
+
assert.equal(managerRecord.reviewHash, reviewArtifact.hash);
|
|
385
|
+
}
|
|
386
|
+
} finally {
|
|
387
|
+
await rm(root, { recursive: true, force: true });
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('clarification closure records agent-authored evidence without accepting spec or handoff', async () => {
|
|
392
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-closure-clarification-'));
|
|
393
|
+
try {
|
|
394
|
+
await initProject(root);
|
|
395
|
+
const reviewedSpecContent = reviewedSpec();
|
|
396
|
+
const specHash = hashDocumentContent(reviewedSpecContent);
|
|
397
|
+
await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpecContent, 'utf8');
|
|
398
|
+
await writeSpecStageArtifacts(root, { specHash, verdict: 'blocked', blockingCount: 1, recommendation: 'request_clarification' });
|
|
399
|
+
const riskDecision = decision('research', ['spec', 'plan', 'verifies']);
|
|
400
|
+
|
|
401
|
+
const closure = await reconcileSpecCollaborationClosure(root, {
|
|
402
|
+
decision: riskDecision,
|
|
403
|
+
generatedAt,
|
|
404
|
+
runId: 'run-clarification-closure'
|
|
405
|
+
});
|
|
406
|
+
const health = await inspectSpecCollaborationHealth(root, 'master');
|
|
407
|
+
const handoff = await inspectWorkflowStageHandoff(root, 'master');
|
|
408
|
+
|
|
409
|
+
assert.equal(closure.adjudication.health, 'needs_clarification');
|
|
410
|
+
assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'not_accepted_needs_clarification');
|
|
411
|
+
assert.equal(closure.acceptedSpecRef, null);
|
|
412
|
+
assert.equal(closure.artifactRefs.every((artifact) => artifact.ref.startsWith('.sdd/runs/master/spec/')), true);
|
|
413
|
+
assert.equal(health.status, 'needs_clarification');
|
|
414
|
+
assert.equal(handoff.status, 'missing');
|
|
415
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.1')));
|
|
416
|
+
} finally {
|
|
417
|
+
await rm(root, { recursive: true, force: true });
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
test('ready structural closure rejects mismatched canonical spec hash without handoff', async () => {
|
|
422
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-spec-closure-mismatch-'));
|
|
423
|
+
try {
|
|
424
|
+
await initProject(root);
|
|
425
|
+
const reviewedSpecContent = reviewedSpec();
|
|
426
|
+
await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpecContent, 'utf8');
|
|
427
|
+
await writeSpecStageArtifacts(root, { specHash: 'stale-hash' });
|
|
428
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
429
|
+
|
|
430
|
+
const closure = await reconcileSpecCollaborationClosure(root, {
|
|
431
|
+
decision: riskDecision,
|
|
432
|
+
generatedAt,
|
|
433
|
+
runId: 'run-hash-mismatch-closure'
|
|
434
|
+
});
|
|
435
|
+
const handoff = await inspectWorkflowStageHandoff(root, 'master');
|
|
436
|
+
|
|
437
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
438
|
+
assert.equal(closure.adjudication.rejection?.reasonCode, 'invalid_proposal');
|
|
439
|
+
assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'not_accepted_hash_mismatch');
|
|
440
|
+
assert.equal(closure.acceptedSpecRef, null);
|
|
441
|
+
assert.equal(handoff.status, 'missing');
|
|
442
|
+
} finally {
|
|
443
|
+
await rm(root, { recursive: true, force: true });
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test('ready plan closure accepts reviewed plan document and records plan to tasks handoff', async () => {
|
|
448
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-ready-'));
|
|
449
|
+
try {
|
|
450
|
+
await initProject(root);
|
|
451
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
452
|
+
await writeAcceptedSpecClosure(root, riskDecision, 'run-spec-ready-for-plan');
|
|
453
|
+
const planContent = reviewedPlan();
|
|
454
|
+
const planHash = hashDocumentContent(planContent);
|
|
455
|
+
await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
|
|
456
|
+
await writePlanStageArtifacts(root, { planHash });
|
|
457
|
+
|
|
458
|
+
const closure = await reconcilePlanCollaborationClosure(root, {
|
|
459
|
+
decision: riskDecision,
|
|
460
|
+
generatedAt,
|
|
461
|
+
runId: 'run-plan-ready-closure'
|
|
462
|
+
});
|
|
463
|
+
const planContentAfterClosure = await readFile(path.join(root, 'specs', 'master', 'plan.md'), 'utf8');
|
|
464
|
+
const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'plan' });
|
|
465
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
|
|
466
|
+
|
|
467
|
+
assert.equal(closure.adjudication.health, 'ready_for_tasks');
|
|
468
|
+
assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'accepted');
|
|
469
|
+
assert.equal(closure.acceptedPlanRef?.ref, 'specs/master/plan.md');
|
|
470
|
+
assert.equal(closure.acceptedPlanRef?.hash, planHash);
|
|
471
|
+
assert.equal(planContentAfterClosure, planContent);
|
|
472
|
+
assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'plan_review']);
|
|
473
|
+
assert.equal(handoff?.payload.fromStage, 'plan');
|
|
474
|
+
assert.equal(handoff?.payload.toStage, 'tasks');
|
|
475
|
+
assert.equal(handoff?.payload.requiredInputRefs[0]?.ref, 'specs/master/plan.md');
|
|
476
|
+
assert.equal(handoff?.payload.requiredInputRefs[0]?.hash, planHash);
|
|
477
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.2')));
|
|
478
|
+
} finally {
|
|
479
|
+
await rm(root, { recursive: true, force: true });
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test('plan closure rejects missing plan review without recording handoff', async () => {
|
|
484
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-missing-review-'));
|
|
485
|
+
try {
|
|
486
|
+
await initProject(root);
|
|
487
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
488
|
+
await writeAcceptedSpecClosure(root, riskDecision, 'run-spec-ready-for-plan-missing-review');
|
|
489
|
+
const planContent = reviewedPlan();
|
|
490
|
+
const planHash = hashDocumentContent(planContent);
|
|
491
|
+
await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
|
|
492
|
+
await writePlanStageArtifacts(root, { planHash, omitReview: true });
|
|
493
|
+
|
|
494
|
+
const closure = await reconcilePlanCollaborationClosure(root, {
|
|
495
|
+
decision: riskDecision,
|
|
496
|
+
generatedAt,
|
|
497
|
+
runId: 'run-plan-missing-review-closure'
|
|
498
|
+
});
|
|
499
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
|
|
500
|
+
|
|
501
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
502
|
+
assert.equal(closure.adjudication.rejection?.reasonCode, 'missing_required_review');
|
|
503
|
+
assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'not_accepted_missing_review');
|
|
504
|
+
assert.equal(closure.acceptedPlanRef, null);
|
|
505
|
+
assert.equal(handoff, null);
|
|
506
|
+
} finally {
|
|
507
|
+
await rm(root, { recursive: true, force: true });
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test('plan closure rejects missing accepted spec handoff without recording handoff', async () => {
|
|
512
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-missing-upstream-'));
|
|
513
|
+
try {
|
|
514
|
+
await initProject(root);
|
|
515
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
516
|
+
const planContent = reviewedPlan();
|
|
517
|
+
const planHash = hashDocumentContent(planContent);
|
|
518
|
+
await writeFile(path.join(root, 'specs', 'master', 'spec.md'), reviewedSpec(), 'utf8');
|
|
519
|
+
await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
|
|
520
|
+
await writePlanStageArtifacts(root, { planHash });
|
|
521
|
+
|
|
522
|
+
const closure = await reconcilePlanCollaborationClosure(root, {
|
|
523
|
+
decision: riskDecision,
|
|
524
|
+
generatedAt,
|
|
525
|
+
runId: 'run-plan-missing-upstream-closure'
|
|
526
|
+
});
|
|
527
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
|
|
528
|
+
|
|
529
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
530
|
+
assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'not_accepted_missing_upstream');
|
|
531
|
+
assert.equal(closure.acceptedPlanRef, null);
|
|
532
|
+
assert.equal(handoff, null);
|
|
533
|
+
} finally {
|
|
534
|
+
await rm(root, { recursive: true, force: true });
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test('plan closure rejects stale accepted spec handoff without recording handoff', async () => {
|
|
539
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-stale-upstream-'));
|
|
540
|
+
try {
|
|
541
|
+
await initProject(root);
|
|
542
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
543
|
+
await writeAcceptedSpecClosure(root, riskDecision, 'run-spec-ready-for-plan-stale');
|
|
544
|
+
await writeFile(path.join(root, 'specs', 'master', 'spec.md'), `${reviewedSpec()}\nStale after handoff.\n`, 'utf8');
|
|
545
|
+
const planContent = reviewedPlan();
|
|
546
|
+
const planHash = hashDocumentContent(planContent);
|
|
547
|
+
await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
|
|
548
|
+
await writePlanStageArtifacts(root, { planHash });
|
|
549
|
+
|
|
550
|
+
const closure = await reconcilePlanCollaborationClosure(root, {
|
|
551
|
+
decision: riskDecision,
|
|
552
|
+
generatedAt,
|
|
553
|
+
runId: 'run-plan-stale-upstream-closure'
|
|
554
|
+
});
|
|
555
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
|
|
556
|
+
|
|
557
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
558
|
+
assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'not_accepted_missing_upstream');
|
|
559
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
|
|
560
|
+
assert.equal(closure.acceptedPlanRef, null);
|
|
561
|
+
assert.equal(handoff, null);
|
|
562
|
+
} finally {
|
|
563
|
+
await rm(root, { recursive: true, force: true });
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
test('plan closure rejects mismatched canonical plan hash without recording handoff', async () => {
|
|
568
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-plan-closure-mismatch-'));
|
|
569
|
+
try {
|
|
570
|
+
await initProject(root);
|
|
571
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
572
|
+
await writeAcceptedSpecClosure(root, riskDecision, 'run-spec-ready-for-plan-hash-mismatch');
|
|
573
|
+
await writeFile(path.join(root, 'specs', 'master', 'plan.md'), reviewedPlan(), 'utf8');
|
|
574
|
+
await writePlanStageArtifacts(root, { planHash: 'stale-plan-hash' });
|
|
575
|
+
|
|
576
|
+
const closure = await reconcilePlanCollaborationClosure(root, {
|
|
577
|
+
decision: riskDecision,
|
|
578
|
+
generatedAt,
|
|
579
|
+
runId: 'run-plan-hash-mismatch-closure'
|
|
580
|
+
});
|
|
581
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'plan', 'tasks');
|
|
582
|
+
|
|
583
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
584
|
+
assert.equal(closure.adjudication.rejection?.reasonCode, 'invalid_proposal');
|
|
585
|
+
assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'not_accepted_hash_mismatch');
|
|
586
|
+
assert.equal(closure.acceptedPlanRef, null);
|
|
587
|
+
assert.equal(handoff, null);
|
|
588
|
+
} finally {
|
|
589
|
+
await rm(root, { recursive: true, force: true });
|
|
590
|
+
}
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
test('ready tasks closure accepts reviewed tasks document and records tasks to verifies handoff', async () => {
|
|
594
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-tasks-closure-ready-'));
|
|
595
|
+
try {
|
|
596
|
+
await initProject(root);
|
|
597
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
598
|
+
await writeAcceptedPlanClosure(root, riskDecision, 'run-plan-ready-for-tasks');
|
|
599
|
+
const tasksContent = reviewedTasks();
|
|
600
|
+
const tasksHash = hashDocumentContent(tasksContent);
|
|
601
|
+
await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
|
|
602
|
+
await writeTasksStageArtifacts(root, { tasksHash });
|
|
603
|
+
|
|
604
|
+
const closure = await reconcileTasksCollaborationClosure(root, {
|
|
605
|
+
decision: riskDecision,
|
|
606
|
+
generatedAt,
|
|
607
|
+
runId: 'run-tasks-ready-closure'
|
|
608
|
+
});
|
|
609
|
+
const tasksContentAfterClosure = await readFile(path.join(root, 'specs', 'master', 'tasks.md'), 'utf8');
|
|
610
|
+
const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'tasks' });
|
|
611
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'tasks', 'verifies');
|
|
612
|
+
|
|
613
|
+
assert.equal(closure.adjudication.health, 'ready_for_verifies');
|
|
614
|
+
assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'accepted');
|
|
615
|
+
assert.equal(closure.acceptedTasksRef?.ref, 'specs/master/tasks.md');
|
|
616
|
+
assert.equal(closure.acceptedTasksRef?.hash, tasksHash);
|
|
617
|
+
assert.equal(tasksContentAfterClosure, tasksContent);
|
|
618
|
+
assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'tasks_review']);
|
|
619
|
+
assert.equal(handoff?.payload.fromStage, 'tasks');
|
|
620
|
+
assert.equal(handoff?.payload.toStage, 'verifies');
|
|
621
|
+
assert.equal(handoff?.payload.requiredInputRefs[0]?.ref, 'specs/master/tasks.md');
|
|
622
|
+
assert.equal(handoff?.payload.requiredInputRefs[0]?.hash, tasksHash);
|
|
623
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.3')));
|
|
624
|
+
} finally {
|
|
625
|
+
await rm(root, { recursive: true, force: true });
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
test('tasks closure rejects missing tasks review without recording handoff', async () => {
|
|
630
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-tasks-closure-missing-review-'));
|
|
631
|
+
try {
|
|
632
|
+
await initProject(root);
|
|
633
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
634
|
+
await writeAcceptedPlanClosure(root, riskDecision, 'run-plan-ready-for-tasks-missing-review');
|
|
635
|
+
const tasksContent = reviewedTasks();
|
|
636
|
+
const tasksHash = hashDocumentContent(tasksContent);
|
|
637
|
+
await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
|
|
638
|
+
await writeTasksStageArtifacts(root, { tasksHash, omitReview: true });
|
|
639
|
+
|
|
640
|
+
const closure = await reconcileTasksCollaborationClosure(root, {
|
|
641
|
+
decision: riskDecision,
|
|
642
|
+
generatedAt,
|
|
643
|
+
runId: 'run-tasks-missing-review-closure'
|
|
644
|
+
});
|
|
645
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'tasks', 'verifies');
|
|
646
|
+
|
|
647
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
648
|
+
assert.equal(closure.adjudication.rejection?.reasonCode, 'missing_required_review');
|
|
649
|
+
assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'not_accepted_missing_review');
|
|
650
|
+
assert.equal(closure.acceptedTasksRef, null);
|
|
651
|
+
assert.equal(handoff, null);
|
|
652
|
+
} finally {
|
|
653
|
+
await rm(root, { recursive: true, force: true });
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
test('tasks closure rejects stale accepted plan handoff without recording handoff', async () => {
|
|
658
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-tasks-closure-stale-upstream-'));
|
|
659
|
+
try {
|
|
660
|
+
await initProject(root);
|
|
661
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
662
|
+
await writeAcceptedPlanClosure(root, riskDecision, 'run-plan-ready-for-tasks-stale');
|
|
663
|
+
await writeFile(path.join(root, 'specs', 'master', 'plan.md'), `${reviewedPlan()}\nStale after handoff.\n`, 'utf8');
|
|
664
|
+
const tasksContent = reviewedTasks();
|
|
665
|
+
const tasksHash = hashDocumentContent(tasksContent);
|
|
666
|
+
await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
|
|
667
|
+
await writeTasksStageArtifacts(root, { tasksHash });
|
|
668
|
+
|
|
669
|
+
const closure = await reconcileTasksCollaborationClosure(root, {
|
|
670
|
+
decision: riskDecision,
|
|
671
|
+
generatedAt,
|
|
672
|
+
runId: 'run-tasks-stale-upstream-closure'
|
|
673
|
+
});
|
|
674
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'tasks', 'verifies');
|
|
675
|
+
|
|
676
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
677
|
+
assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'not_accepted_missing_upstream');
|
|
678
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
|
|
679
|
+
assert.equal(closure.acceptedTasksRef, null);
|
|
680
|
+
assert.equal(handoff, null);
|
|
681
|
+
} finally {
|
|
682
|
+
await rm(root, { recursive: true, force: true });
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
test('tasks closure rejects duplicate task ids without recording handoff', async () => {
|
|
687
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-tasks-closure-duplicate-'));
|
|
688
|
+
try {
|
|
689
|
+
await initProject(root);
|
|
690
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
691
|
+
await writeAcceptedPlanClosure(root, riskDecision, 'run-plan-ready-for-tasks-duplicate');
|
|
692
|
+
const tasksContent = reviewedTasks({ duplicate: true });
|
|
693
|
+
const tasksHash = hashDocumentContent(tasksContent);
|
|
694
|
+
await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
|
|
695
|
+
await writeTasksStageArtifacts(root, { tasksHash });
|
|
696
|
+
|
|
697
|
+
const closure = await reconcileTasksCollaborationClosure(root, {
|
|
698
|
+
decision: riskDecision,
|
|
699
|
+
generatedAt,
|
|
700
|
+
runId: 'run-tasks-duplicate-closure'
|
|
701
|
+
});
|
|
702
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'tasks', 'verifies');
|
|
703
|
+
|
|
704
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
705
|
+
assert.equal(closure.adjudication.rejection?.reasonCode, 'invalid_proposal');
|
|
706
|
+
assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'not_accepted_duplicate_task_ids');
|
|
707
|
+
assert.equal(closure.acceptedTasksRef, null);
|
|
708
|
+
assert.equal(handoff, null);
|
|
709
|
+
} finally {
|
|
710
|
+
await rm(root, { recursive: true, force: true });
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
test('ready verifies closure accepts reviewed verify contract and records verifies to do handoff', async () => {
|
|
715
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-verifies-closure-ready-'));
|
|
716
|
+
try {
|
|
717
|
+
await initProject(root);
|
|
718
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
719
|
+
await writeAcceptedTasksClosure(root, riskDecision, 'run-tasks-ready-for-verifies');
|
|
720
|
+
const verifyContent = reviewedVerify(reviewedTasks());
|
|
721
|
+
const verifyHash = hashDocumentContent(verifyContent);
|
|
722
|
+
await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
|
|
723
|
+
await writeVerifiesStageArtifacts(root, { verifyHash });
|
|
724
|
+
|
|
725
|
+
const closure = await reconcileVerifiesCollaborationClosure(root, {
|
|
726
|
+
decision: riskDecision,
|
|
727
|
+
generatedAt,
|
|
728
|
+
runId: 'run-verifies-ready-closure'
|
|
729
|
+
});
|
|
730
|
+
const verifyContentAfterClosure = await readFile(path.join(root, 'specs', 'master', 'verify.md'), 'utf8');
|
|
731
|
+
const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'verifies' });
|
|
732
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'verifies', 'do');
|
|
733
|
+
|
|
734
|
+
assert.equal(closure.adjudication.health, 'ready_for_do');
|
|
735
|
+
assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'accepted');
|
|
736
|
+
assert.equal(closure.acceptedVerifyRef?.ref, 'specs/master/verify.md');
|
|
737
|
+
assert.equal(closure.acceptedVerifyRef?.hash, verifyHash);
|
|
738
|
+
assert.equal(verifyContentAfterClosure, verifyContent);
|
|
739
|
+
assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'verifies_review']);
|
|
740
|
+
assert.equal(handoff?.payload.fromStage, 'verifies');
|
|
741
|
+
assert.equal(handoff?.payload.toStage, 'do');
|
|
742
|
+
assert.equal(handoff?.payload.requiredInputRefs[0]?.ref, 'specs/master/verify.md');
|
|
743
|
+
assert.equal(handoff?.payload.requiredInputRefs[0]?.hash, verifyHash);
|
|
744
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.4')));
|
|
745
|
+
} finally {
|
|
746
|
+
await rm(root, { recursive: true, force: true });
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
test('verifies closure rejects missing verifies review without recording handoff', async () => {
|
|
751
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-verifies-closure-missing-review-'));
|
|
752
|
+
try {
|
|
753
|
+
await initProject(root);
|
|
754
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
755
|
+
await writeAcceptedTasksClosure(root, riskDecision, 'run-tasks-ready-for-verifies-missing-review');
|
|
756
|
+
const verifyContent = reviewedVerify(reviewedTasks());
|
|
757
|
+
const verifyHash = hashDocumentContent(verifyContent);
|
|
758
|
+
await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
|
|
759
|
+
await writeVerifiesStageArtifacts(root, { verifyHash, omitReview: true });
|
|
760
|
+
|
|
761
|
+
const closure = await reconcileVerifiesCollaborationClosure(root, {
|
|
762
|
+
decision: riskDecision,
|
|
763
|
+
generatedAt,
|
|
764
|
+
runId: 'run-verifies-missing-review-closure'
|
|
765
|
+
});
|
|
766
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'verifies', 'do');
|
|
767
|
+
|
|
768
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
769
|
+
assert.equal(closure.adjudication.rejection?.reasonCode, 'missing_required_review');
|
|
770
|
+
assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'not_accepted_missing_review');
|
|
771
|
+
assert.equal(closure.acceptedVerifyRef, null);
|
|
772
|
+
assert.equal(handoff, null);
|
|
773
|
+
} finally {
|
|
774
|
+
await rm(root, { recursive: true, force: true });
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
test('verifies closure rejects stale accepted tasks handoff without recording handoff', async () => {
|
|
779
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-verifies-closure-stale-upstream-'));
|
|
780
|
+
try {
|
|
781
|
+
await initProject(root);
|
|
782
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
783
|
+
await writeAcceptedTasksClosure(root, riskDecision, 'run-tasks-ready-for-verifies-stale');
|
|
784
|
+
await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), `${reviewedTasks()}\nStale after handoff.\n`, 'utf8');
|
|
785
|
+
const verifyContent = reviewedVerify(reviewedTasks());
|
|
786
|
+
const verifyHash = hashDocumentContent(verifyContent);
|
|
787
|
+
await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
|
|
788
|
+
await writeVerifiesStageArtifacts(root, { verifyHash });
|
|
789
|
+
|
|
790
|
+
const closure = await reconcileVerifiesCollaborationClosure(root, {
|
|
791
|
+
decision: riskDecision,
|
|
792
|
+
generatedAt,
|
|
793
|
+
runId: 'run-verifies-stale-upstream-closure'
|
|
794
|
+
});
|
|
795
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'verifies', 'do');
|
|
796
|
+
|
|
797
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
798
|
+
assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'not_accepted_missing_upstream');
|
|
799
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
|
|
800
|
+
assert.equal(closure.acceptedVerifyRef, null);
|
|
801
|
+
assert.equal(handoff, null);
|
|
802
|
+
} finally {
|
|
803
|
+
await rm(root, { recursive: true, force: true });
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
test('verifies closure rejects incomplete coverage without recording handoff', async () => {
|
|
808
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-verifies-closure-incomplete-coverage-'));
|
|
809
|
+
try {
|
|
810
|
+
await initProject(root);
|
|
811
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
812
|
+
await writeAcceptedTasksClosure(root, riskDecision, 'run-tasks-ready-for-verifies-incomplete');
|
|
813
|
+
const verifyContent = reviewedVerify(reviewedTasks(), { omitTaskId: 'T2' });
|
|
814
|
+
const verifyHash = hashDocumentContent(verifyContent);
|
|
815
|
+
await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
|
|
816
|
+
await writeVerifiesStageArtifacts(root, { verifyHash });
|
|
817
|
+
|
|
818
|
+
const closure = await reconcileVerifiesCollaborationClosure(root, {
|
|
819
|
+
decision: riskDecision,
|
|
820
|
+
generatedAt,
|
|
821
|
+
runId: 'run-verifies-incomplete-coverage-closure'
|
|
822
|
+
});
|
|
823
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'verifies', 'do');
|
|
824
|
+
|
|
825
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
826
|
+
assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'not_accepted_incomplete_coverage');
|
|
827
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /T2/);
|
|
828
|
+
assert.equal(closure.acceptedVerifyRef, null);
|
|
829
|
+
assert.equal(handoff, null);
|
|
830
|
+
} finally {
|
|
831
|
+
await rm(root, { recursive: true, force: true });
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
test('ready do closure accepts reviewed implementation state and records do to test handoff', async () => {
|
|
836
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-ready-'));
|
|
837
|
+
try {
|
|
838
|
+
await initProject(root);
|
|
839
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
840
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do');
|
|
841
|
+
const changedFileRef = 'packages/app/login.ts';
|
|
842
|
+
const changedFileContent = 'export const login = () => true;\n';
|
|
843
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
844
|
+
const changedFileHash = hashDocumentContent(changedFileContent);
|
|
845
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: changedFileHash }] });
|
|
846
|
+
await writeFile(path.join(root, '.sdd', 'runs', 'master', 'do', 't1-schema-review-v1.md'), '# Human note\n\nThis note is not a stage artifact.\n', 'utf8');
|
|
847
|
+
|
|
848
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
849
|
+
decision: riskDecision,
|
|
850
|
+
allowedChangedFileRefs: [changedFileRef],
|
|
851
|
+
generatedAt,
|
|
852
|
+
runId: 'run-do-ready-closure'
|
|
853
|
+
});
|
|
854
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
|
|
855
|
+
const doFiles = (await readdir(path.join(root, '.sdd', 'runs', 'master', 'do'))).sort();
|
|
856
|
+
const changedFileAfterClosure = await readFile(path.join(root, changedFileRef), 'utf8');
|
|
857
|
+
|
|
858
|
+
assert.equal(closure.adjudication.health, 'ready_for_test');
|
|
859
|
+
assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'accepted');
|
|
860
|
+
assert.equal(closure.acceptedImplementationRef?.ref, '.sdd/runs/master/do/implementation-v1.md');
|
|
861
|
+
assert.deepEqual(closure.changedFileRefs, [{ kind: 'evidence', ref: changedFileRef, hash: changedFileHash }]);
|
|
862
|
+
assert.equal(handoff?.payload.fromStage, 'do');
|
|
863
|
+
assert.equal(handoff?.payload.toStage, 'test');
|
|
864
|
+
assert.equal(handoff?.payload.requiredInputRefs.some((inputRef) => inputRef.ref === changedFileRef && inputRef.hash === changedFileHash), true);
|
|
865
|
+
assert.deepEqual(doFiles, ['code-review-v1.md', 'do-collaboration-contract-v1.md', 'do-manager-v1.md', 'implementation-v1.md', 't1-schema-review-v1.md']);
|
|
866
|
+
assert.equal(changedFileAfterClosure, changedFileContent);
|
|
867
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.5')));
|
|
868
|
+
} finally {
|
|
869
|
+
await rm(root, { recursive: true, force: true });
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
test('ready do closure accepts branch-scoped SQL implementation assets matched by task glob', async () => {
|
|
874
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-spec-sql-'));
|
|
875
|
+
try {
|
|
876
|
+
await initProject(root);
|
|
877
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
878
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-spec-sql');
|
|
879
|
+
const changedFileRef = 'specs/master/add_table.sql';
|
|
880
|
+
const changedFileContent = 'create table add_table (id int primary key);\n';
|
|
881
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
882
|
+
const changedFileHash = hashDocumentContent(changedFileContent);
|
|
883
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: changedFileHash }] });
|
|
884
|
+
|
|
885
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
886
|
+
decision: riskDecision,
|
|
887
|
+
allowedChangedFileRefs: ['specs/master/*.sql'],
|
|
888
|
+
generatedAt,
|
|
889
|
+
runId: 'run-do-spec-sql-closure'
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
assert.equal(closure.adjudication.health, 'ready_for_test');
|
|
893
|
+
assert.deepEqual(closure.changedFileRefs, [{ kind: 'evidence', ref: changedFileRef, hash: changedFileHash }]);
|
|
894
|
+
} finally {
|
|
895
|
+
await rm(root, { recursive: true, force: true });
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
test('do closure rejects missing code review without recording handoff', async () => {
|
|
900
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-missing-review-'));
|
|
901
|
+
try {
|
|
902
|
+
await initProject(root);
|
|
903
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
904
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-missing-review');
|
|
905
|
+
const changedFileRef = 'packages/app/login.ts';
|
|
906
|
+
const changedFileContent = 'export const login = () => true;\n';
|
|
907
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
908
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }], omitReview: true });
|
|
909
|
+
|
|
910
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
911
|
+
decision: riskDecision,
|
|
912
|
+
allowedChangedFileRefs: [changedFileRef],
|
|
913
|
+
generatedAt,
|
|
914
|
+
runId: 'run-do-missing-review-closure'
|
|
915
|
+
});
|
|
916
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
|
|
917
|
+
|
|
918
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
919
|
+
assert.equal(closure.adjudication.rejection?.reasonCode, 'missing_required_review');
|
|
920
|
+
assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_missing_review');
|
|
921
|
+
assert.equal(closure.acceptedImplementationRef, null);
|
|
922
|
+
assert.equal(handoff, null);
|
|
923
|
+
} finally {
|
|
924
|
+
await rm(root, { recursive: true, force: true });
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
test('do closure rejects stale accepted verifies handoff without recording handoff', async () => {
|
|
929
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-stale-upstream-'));
|
|
930
|
+
try {
|
|
931
|
+
await initProject(root);
|
|
932
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
933
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-stale');
|
|
934
|
+
await writeFile(path.join(root, 'specs', 'master', 'verify.md'), `${reviewedVerify(reviewedTasks())}\nStale after handoff.\n`, 'utf8');
|
|
935
|
+
const changedFileRef = 'packages/app/login.ts';
|
|
936
|
+
const changedFileContent = 'export const login = () => true;\n';
|
|
937
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
938
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }] });
|
|
939
|
+
|
|
940
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
941
|
+
decision: riskDecision,
|
|
942
|
+
allowedChangedFileRefs: [changedFileRef],
|
|
943
|
+
generatedAt,
|
|
944
|
+
runId: 'run-do-stale-upstream-closure'
|
|
945
|
+
});
|
|
946
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
|
|
947
|
+
|
|
948
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
949
|
+
assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_missing_upstream');
|
|
950
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
|
|
951
|
+
assert.equal(closure.acceptedImplementationRef, null);
|
|
952
|
+
assert.equal(handoff, null);
|
|
953
|
+
} finally {
|
|
954
|
+
await rm(root, { recursive: true, force: true });
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
test('do closure rejects changed-file hash drift without recording handoff', async () => {
|
|
959
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-hash-drift-'));
|
|
960
|
+
try {
|
|
961
|
+
await initProject(root);
|
|
962
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
963
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-hash-drift');
|
|
964
|
+
const changedFileRef = 'packages/app/login.ts';
|
|
965
|
+
const changedFileContent = 'export const login = () => true;\n';
|
|
966
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
967
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }] });
|
|
968
|
+
await writeWorkspaceFile(root, changedFileRef, 'export const login = () => false;\n');
|
|
969
|
+
|
|
970
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
971
|
+
decision: riskDecision,
|
|
972
|
+
allowedChangedFileRefs: [changedFileRef],
|
|
973
|
+
generatedAt,
|
|
974
|
+
runId: 'run-do-hash-drift-closure'
|
|
975
|
+
});
|
|
976
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
|
|
977
|
+
|
|
978
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
979
|
+
assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_hash_mismatch');
|
|
980
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
|
|
981
|
+
assert.equal(closure.acceptedImplementationRef, null);
|
|
982
|
+
assert.equal(handoff, null);
|
|
983
|
+
} finally {
|
|
984
|
+
await rm(root, { recursive: true, force: true });
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
test('do closure rejects changed files outside task boundary without recording handoff', async () => {
|
|
989
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-boundary-'));
|
|
990
|
+
try {
|
|
991
|
+
await initProject(root);
|
|
992
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
993
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-boundary');
|
|
994
|
+
const changedFileRef = 'packages/app/login.ts';
|
|
995
|
+
const changedFileContent = 'export const login = () => true;\n';
|
|
996
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
997
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }] });
|
|
998
|
+
|
|
999
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
1000
|
+
decision: riskDecision,
|
|
1001
|
+
allowedChangedFileRefs: ['packages/app/profile.ts'],
|
|
1002
|
+
generatedAt,
|
|
1003
|
+
runId: 'run-do-boundary-closure'
|
|
1004
|
+
});
|
|
1005
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
|
|
1006
|
+
|
|
1007
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1008
|
+
assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_boundary_violation');
|
|
1009
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /outside/);
|
|
1010
|
+
assert.equal(closure.acceptedImplementationRef, null);
|
|
1011
|
+
assert.equal(handoff, null);
|
|
1012
|
+
} finally {
|
|
1013
|
+
await rm(root, { recursive: true, force: true });
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
test('do closure rejects blocking code review without recording handoff', async () => {
|
|
1018
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-blocking-review-'));
|
|
1019
|
+
try {
|
|
1020
|
+
await initProject(root);
|
|
1021
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1022
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-blocking-review');
|
|
1023
|
+
const changedFileRef = 'packages/app/login.ts';
|
|
1024
|
+
const changedFileContent = 'export const login = () => true;\n';
|
|
1025
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
1026
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }], verdict: 'changes_requested', blockingCount: 1 });
|
|
1027
|
+
|
|
1028
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
1029
|
+
decision: riskDecision,
|
|
1030
|
+
allowedChangedFileRefs: [changedFileRef],
|
|
1031
|
+
generatedAt,
|
|
1032
|
+
runId: 'run-do-blocking-review-closure'
|
|
1033
|
+
});
|
|
1034
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
|
|
1035
|
+
|
|
1036
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1037
|
+
assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_missing_review');
|
|
1038
|
+
assert.equal(closure.acceptedImplementationRef, null);
|
|
1039
|
+
assert.equal(handoff, null);
|
|
1040
|
+
} finally {
|
|
1041
|
+
await rm(root, { recursive: true, force: true });
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
test('do closure rejects do-manager rework recommendation without recording handoff', async () => {
|
|
1046
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-do-closure-manager-rework-'));
|
|
1047
|
+
try {
|
|
1048
|
+
await initProject(root);
|
|
1049
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1050
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, 'run-verifies-ready-for-do-manager-rework');
|
|
1051
|
+
const changedFileRef = 'packages/app/login.ts';
|
|
1052
|
+
const changedFileContent = 'export const login = () => true;\n';
|
|
1053
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
1054
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: hashDocumentContent(changedFileContent) }], recommendation: 'request_clarification' });
|
|
1055
|
+
|
|
1056
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
1057
|
+
decision: riskDecision,
|
|
1058
|
+
allowedChangedFileRefs: [changedFileRef],
|
|
1059
|
+
generatedAt,
|
|
1060
|
+
runId: 'run-do-manager-rework-closure'
|
|
1061
|
+
});
|
|
1062
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'do', 'test');
|
|
1063
|
+
|
|
1064
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1065
|
+
assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'not_accepted_rejected');
|
|
1066
|
+
assert.equal(closure.acceptedImplementationRef, null);
|
|
1067
|
+
assert.equal(handoff, null);
|
|
1068
|
+
} finally {
|
|
1069
|
+
await rm(root, { recursive: true, force: true });
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
test('ready test closure accepts mapped validation evidence and records test to goal-verify handoff', async () => {
|
|
1074
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-ready-'));
|
|
1075
|
+
try {
|
|
1076
|
+
await initProject(root);
|
|
1077
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1078
|
+
await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test');
|
|
1079
|
+
await writeTestStageArtifacts(root, {});
|
|
1080
|
+
|
|
1081
|
+
const closure = await reconcileTestCollaborationClosure(root, {
|
|
1082
|
+
decision: riskDecision,
|
|
1083
|
+
generatedAt,
|
|
1084
|
+
runId: 'run-test-ready-closure'
|
|
1085
|
+
});
|
|
1086
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
|
|
1087
|
+
const testFiles = (await readdir(path.join(root, '.sdd', 'runs', 'master', 'test'))).sort();
|
|
1088
|
+
|
|
1089
|
+
assert.equal(closure.adjudication.health, 'ready_for_goal_verify');
|
|
1090
|
+
assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'accepted');
|
|
1091
|
+
assert.equal(closure.acceptedTestEvidenceRef?.ref, '.sdd/runs/master/test/validation-v1.md');
|
|
1092
|
+
assert.equal(handoff?.payload.fromStage, 'test');
|
|
1093
|
+
assert.equal(handoff?.payload.toStage, 'goal-verify');
|
|
1094
|
+
assert.equal(handoff?.payload.requiredInputRefs.some((inputRef) => inputRef.ref === '.sdd/runs/master/test/validation-v1.md'), true);
|
|
1095
|
+
assert.deepEqual(testFiles, ['test-collaboration-contract-v1.md', 'test-execution-v1.md', 'test-manager-v1.md', 'test-review-v1.md', 'validation-v1.md']);
|
|
1096
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.6')));
|
|
1097
|
+
} finally {
|
|
1098
|
+
await rm(root, { recursive: true, force: true });
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
test('test closure rejects missing command evidence without recording handoff', async () => {
|
|
1103
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-missing-execution-'));
|
|
1104
|
+
try {
|
|
1105
|
+
await initProject(root);
|
|
1106
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1107
|
+
await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-missing-execution');
|
|
1108
|
+
await writeTestStageArtifacts(root, { omitExecution: true });
|
|
1109
|
+
|
|
1110
|
+
const closure = await reconcileTestCollaborationClosure(root, {
|
|
1111
|
+
decision: riskDecision,
|
|
1112
|
+
generatedAt,
|
|
1113
|
+
runId: 'run-test-missing-execution-closure'
|
|
1114
|
+
});
|
|
1115
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
|
|
1116
|
+
|
|
1117
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1118
|
+
assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_missing_execution');
|
|
1119
|
+
assert.equal(closure.acceptedTestEvidenceRef, null);
|
|
1120
|
+
assert.equal(handoff, null);
|
|
1121
|
+
} finally {
|
|
1122
|
+
await rm(root, { recursive: true, force: true });
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
test('test closure rejects stale accepted do handoff without recording handoff', async () => {
|
|
1127
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-stale-upstream-'));
|
|
1128
|
+
try {
|
|
1129
|
+
await initProject(root);
|
|
1130
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1131
|
+
const doState = await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-stale');
|
|
1132
|
+
await writeWorkspaceFile(root, doState.changedFileRef, 'export const login = () => false;\n');
|
|
1133
|
+
await writeTestStageArtifacts(root, {});
|
|
1134
|
+
|
|
1135
|
+
const closure = await reconcileTestCollaborationClosure(root, {
|
|
1136
|
+
decision: riskDecision,
|
|
1137
|
+
generatedAt,
|
|
1138
|
+
runId: 'run-test-stale-upstream-closure'
|
|
1139
|
+
});
|
|
1140
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
|
|
1141
|
+
|
|
1142
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1143
|
+
assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_missing_upstream');
|
|
1144
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
|
|
1145
|
+
assert.equal(closure.acceptedTestEvidenceRef, null);
|
|
1146
|
+
assert.equal(handoff, null);
|
|
1147
|
+
} finally {
|
|
1148
|
+
await rm(root, { recursive: true, force: true });
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
test('test closure rejects failing command evidence without recording handoff', async () => {
|
|
1153
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-failing-execution-'));
|
|
1154
|
+
try {
|
|
1155
|
+
await initProject(root);
|
|
1156
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1157
|
+
await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-failing');
|
|
1158
|
+
await writeTestStageArtifacts(root, { executionStatus: 'failed', exitCode: 1 });
|
|
1159
|
+
|
|
1160
|
+
const closure = await reconcileTestCollaborationClosure(root, {
|
|
1161
|
+
decision: riskDecision,
|
|
1162
|
+
generatedAt,
|
|
1163
|
+
runId: 'run-test-failing-execution-closure'
|
|
1164
|
+
});
|
|
1165
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
|
|
1166
|
+
|
|
1167
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1168
|
+
assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_failed_tests');
|
|
1169
|
+
assert.equal(closure.acceptedTestEvidenceRef, null);
|
|
1170
|
+
assert.equal(handoff, null);
|
|
1171
|
+
} finally {
|
|
1172
|
+
await rm(root, { recursive: true, force: true });
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
test('test closure rejects unmapped acceptance evidence without recording handoff', async () => {
|
|
1177
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-unmapped-acceptance-'));
|
|
1178
|
+
try {
|
|
1179
|
+
await initProject(root);
|
|
1180
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1181
|
+
await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-unmapped');
|
|
1182
|
+
await writeTestStageArtifacts(root, { acceptanceMapped: false });
|
|
1183
|
+
|
|
1184
|
+
const closure = await reconcileTestCollaborationClosure(root, {
|
|
1185
|
+
decision: riskDecision,
|
|
1186
|
+
generatedAt,
|
|
1187
|
+
runId: 'run-test-unmapped-acceptance-closure'
|
|
1188
|
+
});
|
|
1189
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
|
|
1190
|
+
|
|
1191
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1192
|
+
assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_unmapped_acceptance');
|
|
1193
|
+
assert.equal(closure.acceptedTestEvidenceRef, null);
|
|
1194
|
+
assert.equal(handoff, null);
|
|
1195
|
+
} finally {
|
|
1196
|
+
await rm(root, { recursive: true, force: true });
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
|
|
1200
|
+
test('test closure rejects blocking test review without recording handoff', async () => {
|
|
1201
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-blocking-review-'));
|
|
1202
|
+
try {
|
|
1203
|
+
await initProject(root);
|
|
1204
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1205
|
+
await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-blocking-review');
|
|
1206
|
+
await writeTestStageArtifacts(root, { verdict: 'changes_requested', blockingCount: 1 });
|
|
1207
|
+
|
|
1208
|
+
const closure = await reconcileTestCollaborationClosure(root, {
|
|
1209
|
+
decision: riskDecision,
|
|
1210
|
+
generatedAt,
|
|
1211
|
+
runId: 'run-test-blocking-review-closure'
|
|
1212
|
+
});
|
|
1213
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
|
|
1214
|
+
|
|
1215
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1216
|
+
assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_missing_review');
|
|
1217
|
+
assert.equal(closure.acceptedTestEvidenceRef, null);
|
|
1218
|
+
assert.equal(handoff, null);
|
|
1219
|
+
} finally {
|
|
1220
|
+
await rm(root, { recursive: true, force: true });
|
|
1221
|
+
}
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
test('test closure rejects test-manager rework recommendation without recording handoff', async () => {
|
|
1225
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-test-closure-manager-rework-'));
|
|
1226
|
+
try {
|
|
1227
|
+
await initProject(root);
|
|
1228
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1229
|
+
await writeAcceptedDoClosure(root, riskDecision, 'run-do-ready-for-test-manager-rework');
|
|
1230
|
+
await writeTestStageArtifacts(root, { recommendation: 'request_clarification' });
|
|
1231
|
+
|
|
1232
|
+
const closure = await reconcileTestCollaborationClosure(root, {
|
|
1233
|
+
decision: riskDecision,
|
|
1234
|
+
generatedAt,
|
|
1235
|
+
runId: 'run-test-manager-rework-closure'
|
|
1236
|
+
});
|
|
1237
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'test', 'goal-verify');
|
|
1238
|
+
|
|
1239
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1240
|
+
assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'not_accepted_rejected');
|
|
1241
|
+
assert.equal(closure.acceptedTestEvidenceRef, null);
|
|
1242
|
+
assert.equal(handoff, null);
|
|
1243
|
+
} finally {
|
|
1244
|
+
await rm(root, { recursive: true, force: true });
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
test('ready goal-verify closure accepts reviewed goal evidence and records truthAlignment for ship', async () => {
|
|
1249
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-ready-'));
|
|
1250
|
+
try {
|
|
1251
|
+
await initProject(root);
|
|
1252
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1253
|
+
await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify');
|
|
1254
|
+
await writeGoalVerifyStageArtifacts(root, {});
|
|
1255
|
+
|
|
1256
|
+
const closure = await reconcileGoalVerifyCollaborationClosure(root, {
|
|
1257
|
+
decision: riskDecision,
|
|
1258
|
+
generatedAt,
|
|
1259
|
+
runId: 'run-goal-verify-ready-closure'
|
|
1260
|
+
});
|
|
1261
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
|
|
1262
|
+
const truthAlignment = await readTruthAlignmentProjection(root, riskDecision.scope);
|
|
1263
|
+
const chain = await inspectFullStageChain(root, 'master');
|
|
1264
|
+
const goalVerifyFiles = (await readdir(path.join(root, '.sdd', 'runs', 'master', 'goal-verify'))).sort();
|
|
1265
|
+
|
|
1266
|
+
assert.equal(closure.adjudication.health, 'ready_for_ship');
|
|
1267
|
+
assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'accepted');
|
|
1268
|
+
assert.equal(closure.acceptedGoalVerificationRef?.ref, '.sdd/runs/master/goal-verify/goal-verification-v1.md');
|
|
1269
|
+
assert.equal(handoff, null);
|
|
1270
|
+
assert.equal(closure.adjudication.closureRefs.truthAlignmentProjectionRef?.kind, 'projection');
|
|
1271
|
+
assert.equal(truthAlignment?.payload.status, 'aligned');
|
|
1272
|
+
assert.equal(chain.status, 'partial');
|
|
1273
|
+
assert.equal(chain.finalHealth, 'ready_for_ship');
|
|
1274
|
+
assert.equal(chain.projectionCounts.completedStages, 7);
|
|
1275
|
+
assert.equal(truthAlignment?.payload.sourceStage, 'goal-verify');
|
|
1276
|
+
assert.equal(truthAlignment?.payload.acceptedRealityRefs.some((inputRef) => inputRef.ref === '.sdd/runs/master/goal-verify/goal-verification-v1.md'), true);
|
|
1277
|
+
assert.deepEqual(goalVerifyFiles, ['goal-review-v1.md', 'goal-verification-v1.md', 'goal-verify-collaboration-contract-v1.md', 'goal-verify-manager-v1.md']);
|
|
1278
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.7')));
|
|
1279
|
+
} finally {
|
|
1280
|
+
await rm(root, { recursive: true, force: true });
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
test('goal-verify closure rejects incomplete acceptance coverage without recording handoff', async () => {
|
|
1285
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-incomplete-coverage-'));
|
|
1286
|
+
try {
|
|
1287
|
+
await initProject(root);
|
|
1288
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1289
|
+
await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-incomplete-coverage');
|
|
1290
|
+
await writeGoalVerifyStageArtifacts(root, { coverageComplete: false });
|
|
1291
|
+
|
|
1292
|
+
const closure = await reconcileGoalVerifyCollaborationClosure(root, {
|
|
1293
|
+
decision: riskDecision,
|
|
1294
|
+
generatedAt,
|
|
1295
|
+
runId: 'run-goal-verify-incomplete-coverage-closure'
|
|
1296
|
+
});
|
|
1297
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
|
|
1298
|
+
|
|
1299
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1300
|
+
assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_incomplete_coverage');
|
|
1301
|
+
assert.equal(closure.acceptedGoalVerificationRef, null);
|
|
1302
|
+
assert.equal(handoff, null);
|
|
1303
|
+
} finally {
|
|
1304
|
+
await rm(root, { recursive: true, force: true });
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
test('goal-verify closure rejects stale accepted test handoff without recording handoff', async () => {
|
|
1309
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-stale-test-'));
|
|
1310
|
+
try {
|
|
1311
|
+
await initProject(root);
|
|
1312
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1313
|
+
await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-stale');
|
|
1314
|
+
await writeFile(path.join(root, '.sdd', 'runs', 'master', 'test', 'validation-v1.md'), validationEvidenceArtifact('stale-execution-hash', 'passed', true), 'utf8');
|
|
1315
|
+
await writeGoalVerifyStageArtifacts(root, {});
|
|
1316
|
+
|
|
1317
|
+
const closure = await reconcileGoalVerifyCollaborationClosure(root, {
|
|
1318
|
+
decision: riskDecision,
|
|
1319
|
+
generatedAt,
|
|
1320
|
+
runId: 'run-goal-verify-stale-test-closure'
|
|
1321
|
+
});
|
|
1322
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
|
|
1323
|
+
|
|
1324
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1325
|
+
assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_missing_upstream');
|
|
1326
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /stale/);
|
|
1327
|
+
assert.equal(closure.acceptedGoalVerificationRef, null);
|
|
1328
|
+
assert.equal(handoff, null);
|
|
1329
|
+
} finally {
|
|
1330
|
+
await rm(root, { recursive: true, force: true });
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
test('goal-verify closure rejects open durable gaps without recording handoff', async () => {
|
|
1335
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-open-gap-'));
|
|
1336
|
+
try {
|
|
1337
|
+
await initProject(root);
|
|
1338
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1339
|
+
await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-open-gap');
|
|
1340
|
+
await writeGoalVerifyStageArtifacts(root, { durableGapCount: 1 });
|
|
1341
|
+
|
|
1342
|
+
const closure = await reconcileGoalVerifyCollaborationClosure(root, {
|
|
1343
|
+
decision: riskDecision,
|
|
1344
|
+
generatedAt,
|
|
1345
|
+
runId: 'run-goal-verify-open-gap-closure'
|
|
1346
|
+
});
|
|
1347
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
|
|
1348
|
+
|
|
1349
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1350
|
+
assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_open_gap');
|
|
1351
|
+
assert.equal(closure.acceptedGoalVerificationRef, null);
|
|
1352
|
+
assert.equal(handoff, null);
|
|
1353
|
+
} finally {
|
|
1354
|
+
await rm(root, { recursive: true, force: true });
|
|
1355
|
+
}
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
test('goal-verify closure rejects blocking goal review without recording handoff', async () => {
|
|
1359
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-blocking-review-'));
|
|
1360
|
+
try {
|
|
1361
|
+
await initProject(root);
|
|
1362
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1363
|
+
await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-blocking-review');
|
|
1364
|
+
await writeGoalVerifyStageArtifacts(root, { verdict: 'changes_requested', blockingCount: 1 });
|
|
1365
|
+
|
|
1366
|
+
const closure = await reconcileGoalVerifyCollaborationClosure(root, {
|
|
1367
|
+
decision: riskDecision,
|
|
1368
|
+
generatedAt,
|
|
1369
|
+
runId: 'run-goal-verify-blocking-review-closure'
|
|
1370
|
+
});
|
|
1371
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
|
|
1372
|
+
|
|
1373
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1374
|
+
assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_missing_review');
|
|
1375
|
+
assert.equal(closure.acceptedGoalVerificationRef, null);
|
|
1376
|
+
assert.equal(handoff, null);
|
|
1377
|
+
} finally {
|
|
1378
|
+
await rm(root, { recursive: true, force: true });
|
|
1379
|
+
}
|
|
1380
|
+
});
|
|
1381
|
+
|
|
1382
|
+
test('goal-verify closure rejects goal-verify-manager rework recommendation without recording handoff', async () => {
|
|
1383
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-goal-verify-closure-manager-rework-'));
|
|
1384
|
+
try {
|
|
1385
|
+
await initProject(root);
|
|
1386
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1387
|
+
await writeAcceptedTestClosure(root, riskDecision, 'run-test-ready-for-goal-verify-manager-rework');
|
|
1388
|
+
await writeGoalVerifyStageArtifacts(root, { recommendation: 'request_clarification' });
|
|
1389
|
+
|
|
1390
|
+
const closure = await reconcileGoalVerifyCollaborationClosure(root, {
|
|
1391
|
+
decision: riskDecision,
|
|
1392
|
+
generatedAt,
|
|
1393
|
+
runId: 'run-goal-verify-manager-rework-closure'
|
|
1394
|
+
});
|
|
1395
|
+
const handoff = await readWorkflowHandoffProjection(root, riskDecision.scope, 'goal-verify', 'ship');
|
|
1396
|
+
|
|
1397
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1398
|
+
assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'not_accepted_rejected');
|
|
1399
|
+
assert.equal(closure.acceptedGoalVerificationRef, null);
|
|
1400
|
+
assert.equal(handoff, null);
|
|
1401
|
+
} finally {
|
|
1402
|
+
await rm(root, { recursive: true, force: true });
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
test('ready ship closure accepts reviewed readiness evidence and records final ship-ready projection', async () => {
|
|
1408
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-ready-'));
|
|
1409
|
+
try {
|
|
1410
|
+
await initProject(root);
|
|
1411
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1412
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship');
|
|
1413
|
+
await writeShipStageArtifacts(root, {});
|
|
1414
|
+
|
|
1415
|
+
const closure = await reconcileShipCollaborationClosure(root, {
|
|
1416
|
+
decision: riskDecision,
|
|
1417
|
+
generatedAt,
|
|
1418
|
+
runId: 'run-ship-ready-closure'
|
|
1419
|
+
});
|
|
1420
|
+
const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'ship' });
|
|
1421
|
+
const shipFiles = await readdir(path.join(root, '.sdd', 'runs', 'master', 'ship'));
|
|
1422
|
+
|
|
1423
|
+
assert.equal(closure.adjudication.health, 'ship_ready');
|
|
1424
|
+
assert.equal(closure.adjudication.stageDecision.status, 'completed');
|
|
1425
|
+
assert.equal(closure.adjudication.handoffPacket, null);
|
|
1426
|
+
assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'accepted');
|
|
1427
|
+
assert.equal(closure.acceptedShipReadinessRef?.ref, '.sdd/runs/master/ship/ship-readiness-v1.md');
|
|
1428
|
+
assert.equal(closure.releaseDocumentRef, null);
|
|
1429
|
+
assert.deepEqual(registered.map((artifact) => artifact.kind).sort(), ['manager_closure_request', 'release_review', 'ship_readiness']);
|
|
1430
|
+
assert.deepEqual(shipFiles, ['release-review-v1.md', 'ship-collaboration-contract-v1.md', 'ship-manager-v1.md', 'ship-readiness-v1.md']);
|
|
1431
|
+
await assert.rejects(access(path.join(root, '.sdd', 'runs', 'master', 'evidence', 'artifacts', 'phase9.9')));
|
|
1432
|
+
} finally {
|
|
1433
|
+
await rm(root, { recursive: true, force: true });
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1436
|
+
|
|
1437
|
+
test('ship required release review is satisfied by registered release-review artifact', async () => {
|
|
1438
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-required-review-'));
|
|
1439
|
+
try {
|
|
1440
|
+
await initProject(root);
|
|
1441
|
+
const riskDecision: LifecycleRiskDecision = {
|
|
1442
|
+
...decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']),
|
|
1443
|
+
requiredEvidence: [{
|
|
1444
|
+
id: 'phase8-risk:command-evidence',
|
|
1445
|
+
acceptanceRef: 'phase8-risk-decision',
|
|
1446
|
+
kind: 'command',
|
|
1447
|
+
requiredBefore: 'ship',
|
|
1448
|
+
refs: [ref('task', 'T1'), ref('task', 'T2')],
|
|
1449
|
+
reasons: ['Task declares high-risk tags: evidence-semantics.']
|
|
1450
|
+
}],
|
|
1451
|
+
requiredReviews: [{
|
|
1452
|
+
id: 'phase8-risk:release-review',
|
|
1453
|
+
kind: 'release',
|
|
1454
|
+
requiredBefore: 'ship',
|
|
1455
|
+
reasons: ['Phase 8 risk signal requires release review.']
|
|
1456
|
+
}]
|
|
1457
|
+
};
|
|
1458
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-required-review');
|
|
1459
|
+
await writeShipStageArtifacts(root, {});
|
|
1460
|
+
|
|
1461
|
+
const closure = await reconcileShipCollaborationClosure(root, {
|
|
1462
|
+
decision: riskDecision,
|
|
1463
|
+
generatedAt,
|
|
1464
|
+
runId: 'run-ship-required-review-closure'
|
|
1465
|
+
});
|
|
1466
|
+
const registered = await listRuntimeStageArtifacts(root, { branchSlug: 'master', stage: 'ship' });
|
|
1467
|
+
|
|
1468
|
+
assert.equal(closure.adjudication.health, 'ship_ready');
|
|
1469
|
+
assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'accepted');
|
|
1470
|
+
assert.equal(closure.acceptedShipReadinessRef?.ref, '.sdd/runs/master/ship/ship-readiness-v1.md');
|
|
1471
|
+
assert.equal(registered.some((artifact) => artifact.kind === 'release_review'), true);
|
|
1472
|
+
} finally {
|
|
1473
|
+
await rm(root, { recursive: true, force: true });
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
test('full-stage chain diagnostic summarizes ready chain from runtime projections', async () => {
|
|
1478
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-full-chain-ready-'));
|
|
1479
|
+
try {
|
|
1480
|
+
await initProject(root);
|
|
1481
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1482
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-full-chain-ready-for-ship');
|
|
1483
|
+
await writeShipStageArtifacts(root, {});
|
|
1484
|
+
const shipClosure = await reconcileShipCollaborationClosure(root, {
|
|
1485
|
+
decision: riskDecision,
|
|
1486
|
+
generatedAt,
|
|
1487
|
+
runId: 'run-full-chain-ship-ready'
|
|
1488
|
+
});
|
|
1489
|
+
const chain = await inspectFullStageChain(root, 'master');
|
|
1490
|
+
|
|
1491
|
+
assert.equal(shipClosure.adjudication.health, 'ship_ready');
|
|
1492
|
+
assert.equal(chain.status, 'ready');
|
|
1493
|
+
assert.equal(chain.finalHealth, 'ship_ready');
|
|
1494
|
+
assert.equal(chain.projectionCounts.adjudications, 8);
|
|
1495
|
+
assert.equal(chain.projectionCounts.completedStages, 8);
|
|
1496
|
+
assert.equal(chain.projectionCounts.validatedCollaborationContracts, 8);
|
|
1497
|
+
assert.equal(chain.projectionCounts.handoffs, 6);
|
|
1498
|
+
assert.deepEqual(chain.stages.map((stage) => stage.stage), ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1499
|
+
assert.equal(chain.stages.every((stage) => !stage.acceptedRef?.ref.startsWith('runs/')), true);
|
|
1500
|
+
assert.equal(chain.stages.every((stage) => stage.acceptedRef?.ref.includes('evidence/artifacts') === false), true);
|
|
1501
|
+
assert.equal(chain.stages.every((stage) => stage.acceptedRef?.ref.startsWith('artifacts/') === false), true);
|
|
1502
|
+
} finally {
|
|
1503
|
+
await rm(root, { recursive: true, force: true });
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
test('runtime close replay does not create or mutate Markdown files', async () => {
|
|
1508
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-full-chain-markdown-immutable-'));
|
|
1509
|
+
try {
|
|
1510
|
+
await initProject(root);
|
|
1511
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1512
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-markdown-immutable-ready-for-ship');
|
|
1513
|
+
await writeShipStageArtifacts(root, {});
|
|
1514
|
+
await reconcileShipCollaborationClosure(root, {
|
|
1515
|
+
decision: riskDecision,
|
|
1516
|
+
generatedAt,
|
|
1517
|
+
runId: 'run-markdown-immutable-ship-ready'
|
|
1518
|
+
});
|
|
1519
|
+
const before = await markdownSnapshot(root);
|
|
1520
|
+
|
|
1521
|
+
await reconcileSpecCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-spec' });
|
|
1522
|
+
await reconcilePlanCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-plan' });
|
|
1523
|
+
await reconcileTasksCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-tasks' });
|
|
1524
|
+
await reconcileVerifiesCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-verifies' });
|
|
1525
|
+
await reconcileDoCollaborationClosure(root, {
|
|
1526
|
+
decision: riskDecision,
|
|
1527
|
+
allowedChangedFileRefs: ['packages/app/login.ts'],
|
|
1528
|
+
generatedAt,
|
|
1529
|
+
runId: 'run-markdown-immutable-replay-do'
|
|
1530
|
+
});
|
|
1531
|
+
await reconcileTestCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-test' });
|
|
1532
|
+
await reconcileGoalVerifyCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-goal-verify' });
|
|
1533
|
+
await reconcileShipCollaborationClosure(root, { decision: riskDecision, generatedAt, runId: 'run-markdown-immutable-replay-ship' });
|
|
1534
|
+
const after = await markdownSnapshot(root);
|
|
1535
|
+
|
|
1536
|
+
assert.deepEqual(after, before);
|
|
1537
|
+
assert.equal([...after.keys()].some((file) => file.startsWith('runs/')), false);
|
|
1538
|
+
} finally {
|
|
1539
|
+
await rm(root, { recursive: true, force: true });
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
|
|
1543
|
+
test('ship closure rejects missing release review without final readiness', async () => {
|
|
1544
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-missing-review-'));
|
|
1545
|
+
try {
|
|
1546
|
+
await initProject(root);
|
|
1547
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1548
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-missing-review');
|
|
1549
|
+
await writeShipStageArtifacts(root, { omitReview: true });
|
|
1550
|
+
|
|
1551
|
+
const closure = await reconcileShipCollaborationClosure(root, {
|
|
1552
|
+
decision: riskDecision,
|
|
1553
|
+
generatedAt,
|
|
1554
|
+
runId: 'run-ship-missing-review-closure'
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1558
|
+
assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_missing_review');
|
|
1559
|
+
assert.equal(closure.acceptedShipReadinessRef, null);
|
|
1560
|
+
} finally {
|
|
1561
|
+
await rm(root, { recursive: true, force: true });
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
|
|
1565
|
+
test('ship closure rejects stale truthAlignment reality refs without final readiness', async () => {
|
|
1566
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-stale-truth-alignment-'));
|
|
1567
|
+
try {
|
|
1568
|
+
await initProject(root);
|
|
1569
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1570
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-stale');
|
|
1571
|
+
await writeFile(path.join(root, '.sdd', 'runs', 'master', 'goal-verify', 'goal-verification-v1.md'), `${goalVerificationArtifact('passed', true, 0, 0)}\nDrift after truthAlignment.\n`, 'utf8');
|
|
1572
|
+
await writeShipStageArtifacts(root, {});
|
|
1573
|
+
|
|
1574
|
+
const closure = await reconcileShipCollaborationClosure(root, {
|
|
1575
|
+
decision: riskDecision,
|
|
1576
|
+
generatedAt,
|
|
1577
|
+
runId: 'run-ship-stale-truth-alignment-closure'
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1581
|
+
assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_missing_upstream');
|
|
1582
|
+
assert.match(closure.adjudication.closureRefs.reasons.join(' '), /truthAlignment|stale/);
|
|
1583
|
+
assert.equal(closure.acceptedShipReadinessRef, null);
|
|
1584
|
+
} finally {
|
|
1585
|
+
await rm(root, { recursive: true, force: true });
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
|
|
1589
|
+
test('ship closure rejects open readiness blockers without final readiness', async () => {
|
|
1590
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-open-blocker-'));
|
|
1591
|
+
try {
|
|
1592
|
+
await initProject(root);
|
|
1593
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1594
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-open-blocker');
|
|
1595
|
+
await writeShipStageArtifacts(root, { doctorBlockerCount: 1 });
|
|
1596
|
+
|
|
1597
|
+
const closure = await reconcileShipCollaborationClosure(root, {
|
|
1598
|
+
decision: riskDecision,
|
|
1599
|
+
generatedAt,
|
|
1600
|
+
runId: 'run-ship-open-blocker-closure'
|
|
1601
|
+
});
|
|
1602
|
+
|
|
1603
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1604
|
+
assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_open_blocker');
|
|
1605
|
+
assert.equal(closure.acceptedShipReadinessRef, null);
|
|
1606
|
+
} finally {
|
|
1607
|
+
await rm(root, { recursive: true, force: true });
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
|
|
1611
|
+
test('ship closure rejects release document hash drift when release doc is declared', async () => {
|
|
1612
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-release-drift-'));
|
|
1613
|
+
try {
|
|
1614
|
+
await initProject(root);
|
|
1615
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1616
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-release-drift');
|
|
1617
|
+
const releaseContent = '# Release Notes\n\nReady to ship.\n';
|
|
1618
|
+
await writeFile(path.join(root, 'specs', 'master', 'release.md'), releaseContent, 'utf8');
|
|
1619
|
+
await writeShipStageArtifacts(root, { release: { ref: 'specs/master/release.md', hash: hashDocumentContent(releaseContent) } });
|
|
1620
|
+
await writeFile(path.join(root, 'specs', 'master', 'release.md'), `${releaseContent}\nDrift after readiness.\n`, 'utf8');
|
|
1621
|
+
|
|
1622
|
+
const closure = await reconcileShipCollaborationClosure(root, {
|
|
1623
|
+
decision: riskDecision,
|
|
1624
|
+
generatedAt,
|
|
1625
|
+
runId: 'run-ship-release-drift-closure'
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1629
|
+
assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_release_drift');
|
|
1630
|
+
assert.equal(closure.acceptedShipReadinessRef, null);
|
|
1631
|
+
assert.equal(closure.releaseDocumentRef, null);
|
|
1632
|
+
} finally {
|
|
1633
|
+
await rm(root, { recursive: true, force: true });
|
|
1634
|
+
}
|
|
1635
|
+
});
|
|
1636
|
+
|
|
1637
|
+
test('ship closure rejects authority-violating readiness evidence', async () => {
|
|
1638
|
+
const root = await mkdtemp(path.join(tmpdir(), 'sdd-stage-ship-authority-'));
|
|
1639
|
+
try {
|
|
1640
|
+
await initProject(root);
|
|
1641
|
+
const riskDecision = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']);
|
|
1642
|
+
await writeAcceptedGoalVerifyClosure(root, riskDecision, 'run-goal-verify-ready-for-ship-authority');
|
|
1643
|
+
await writeShipStageArtifacts(root, { authorityAttempts: ['ship_ready'] });
|
|
1644
|
+
|
|
1645
|
+
const closure = await reconcileShipCollaborationClosure(root, {
|
|
1646
|
+
decision: riskDecision,
|
|
1647
|
+
generatedAt,
|
|
1648
|
+
runId: 'run-ship-authority-closure'
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
assert.equal(closure.adjudication.health, 'rejected');
|
|
1652
|
+
assert.equal(closure.adjudication.closureRefs.shipReadinessStatus, 'not_accepted_authority_violation');
|
|
1653
|
+
assert.equal(closure.acceptedShipReadinessRef, null);
|
|
1654
|
+
} finally {
|
|
1655
|
+
await rm(root, { recursive: true, force: true });
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
function decision(profile: LifecycleRiskDecision['profile'], requiredStages: LifecycleRiskDecision['requiredStages']): LifecycleRiskDecision {
|
|
1660
|
+
return {
|
|
1661
|
+
contract: LIFECYCLE_RISK_DECISION_CONTRACT_VERSION,
|
|
1662
|
+
scope: { branch: 'master' },
|
|
1663
|
+
profile,
|
|
1664
|
+
requiredStages,
|
|
1665
|
+
skippedStages: [],
|
|
1666
|
+
blockedStages: profile === 'blocked' ? ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship'] : [],
|
|
1667
|
+
requiredEvidence: [],
|
|
1668
|
+
requiredReviews: [],
|
|
1669
|
+
humanCheckpointRequired: profile === 'blocked',
|
|
1670
|
+
approvalPolicy: profile === 'blocked' ? 'blocked' : profile === 'direct' ? 'auto-allow' : 'review-required',
|
|
1671
|
+
inputRefs: [ref('document', 'specs/master/phase9.1-spec.md')],
|
|
1672
|
+
signalRefs: [],
|
|
1673
|
+
policyVersion: 'test-policy',
|
|
1674
|
+
inputHash: 'input-hash',
|
|
1675
|
+
confidence: 'high',
|
|
1676
|
+
reasons: [`${profile} test decision`],
|
|
1677
|
+
generatedAt
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
function stageClosure(profile: StageCollaborationProfile, overrides: { items?: CandidateItem[]; authorityAttempts?: StageClosureRequest['authorityAttempts']; canonicalSpecRef?: ReturnType<typeof ref> } = {}): { coordination: SpecManagerCoordinationRecord; closureRequest: StageClosureRequest } {
|
|
1682
|
+
const workOrder = buildSpecStageWorkOrder(profile, ref('projection', 'profile/spec'), generatedAt);
|
|
1683
|
+
const workOrderRef = ref('projection', `spec-work-order/${profile.scope.branch}`);
|
|
1684
|
+
const candidateRef = overrides.canonicalSpecRef ?? ref('document', 'specs/master/spec.md');
|
|
1685
|
+
const reviewRef = ref('artifact', '.sdd/runs/master/spec/spec-review-v1.md');
|
|
1686
|
+
const items = overrides.items ?? [candidateItem('find-1', 'advisory_finding'), candidateItem('cap-1', 'capability_suggestion'), candidateItem('handoff-1', 'handoff_proposal')];
|
|
1687
|
+
const unresolvedAmbiguityIds = items.filter((item) => item.kind === 'blocking_ambiguity').map((item) => item.id);
|
|
1688
|
+
const candidate: SpecDocumentCandidate = {
|
|
1689
|
+
contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
|
|
1690
|
+
candidateId: `candidate-${profile.scope.branch}`,
|
|
1691
|
+
stage: 'spec',
|
|
1692
|
+
scope: profile.scope,
|
|
1693
|
+
producedBy: 'spec-drafter',
|
|
1694
|
+
inputRefs: workOrder.inputRefs,
|
|
1695
|
+
evidenceRefs: items.flatMap((item) => item.refs),
|
|
1696
|
+
acceptedItems: items,
|
|
1697
|
+
generatedAt
|
|
1698
|
+
};
|
|
1699
|
+
const capabilityFindings = items
|
|
1700
|
+
.filter((item) => item.kind === 'capability_suggestion')
|
|
1701
|
+
.map((item): CapabilityFinding => ({
|
|
1702
|
+
contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
|
|
1703
|
+
findingId: `finding-${item.id}`,
|
|
1704
|
+
stage: 'spec',
|
|
1705
|
+
scope: profile.scope,
|
|
1706
|
+
capabilityDomain: 'solution-design',
|
|
1707
|
+
summary: item.summary,
|
|
1708
|
+
refs: item.refs,
|
|
1709
|
+
generatedAt
|
|
1710
|
+
}));
|
|
1711
|
+
const capabilityFindingRefs = capabilityFindings.map((finding) => ref('artifact', `.sdd/runs/master/spec/spec-capability-finding-${finding.findingId}.md`));
|
|
1712
|
+
const review: SpecReviewResult = {
|
|
1713
|
+
contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
|
|
1714
|
+
reviewId: `review-${profile.scope.branch}`,
|
|
1715
|
+
stage: 'spec',
|
|
1716
|
+
scope: profile.scope,
|
|
1717
|
+
reviewer: 'spec-reviewer',
|
|
1718
|
+
candidateRef,
|
|
1719
|
+
verdict: unresolvedAmbiguityIds.length > 0 ? 'blocked' : 'approved',
|
|
1720
|
+
findings: items.map((item) => item.summary),
|
|
1721
|
+
evidenceRefs: items.flatMap((item) => item.refs),
|
|
1722
|
+
generatedAt
|
|
1723
|
+
};
|
|
1724
|
+
const agentTeamRefs = [candidateRef, reviewRef, ...capabilityFindingRefs];
|
|
1725
|
+
const coordination: SpecManagerCoordinationRecord = {
|
|
1726
|
+
contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
|
|
1727
|
+
coordinationId: `coordination-${profile.scope.branch}`,
|
|
1728
|
+
stage: 'spec',
|
|
1729
|
+
scope: profile.scope,
|
|
1730
|
+
stageManager: 'spec-manager',
|
|
1731
|
+
workOrderRef,
|
|
1732
|
+
agentTeamRefs,
|
|
1733
|
+
recommendation: unresolvedAmbiguityIds.length > 0 ? 'request_clarification' : 'close_stage',
|
|
1734
|
+
generatedAt
|
|
1735
|
+
};
|
|
1736
|
+
const closureRequest: StageClosureRequest = {
|
|
1737
|
+
contract: STAGE_COLLABORATION_RUNTIME_CONTRACT_VERSION,
|
|
1738
|
+
closureRequestId: `closure-${profile.scope.branch}`,
|
|
1739
|
+
stage: 'spec',
|
|
1740
|
+
scope: profile.scope,
|
|
1741
|
+
submittedBy: 'spec-manager',
|
|
1742
|
+
workOrderRef,
|
|
1743
|
+
coordinationRef: ref('artifact', '.sdd/runs/master/spec/spec-manager-v1.md'),
|
|
1744
|
+
candidateRef,
|
|
1745
|
+
reviewRefs: [reviewRef],
|
|
1746
|
+
capabilityFindingRefs,
|
|
1747
|
+
evidenceRefs: [workOrderRef, ...agentTeamRefs, ...items.flatMap((item) => item.refs)],
|
|
1748
|
+
candidate,
|
|
1749
|
+
reviewResults: [review],
|
|
1750
|
+
capabilityFindings,
|
|
1751
|
+
unresolvedAmbiguityIds,
|
|
1752
|
+
authorityAttempts: overrides.authorityAttempts ?? [],
|
|
1753
|
+
managerRecommendation: unresolvedAmbiguityIds.length > 0 ? 'request_clarification' : 'close_stage',
|
|
1754
|
+
generatedAt
|
|
1755
|
+
};
|
|
1756
|
+
return { coordination, closureRequest };
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
type CandidateItem = { id: string; kind: SpecProposalItemKind; summary: string; refs: ReturnType<typeof ref>[] };
|
|
1760
|
+
|
|
1761
|
+
function candidateItem(id: string, kind: SpecProposalItemKind, refs = [ref('document', `specs/master/${id}.md`)]): CandidateItem {
|
|
1762
|
+
return { id, kind, summary: `${kind} ${id}`, refs };
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
async function writeAcceptedSpecClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
|
|
1766
|
+
const specContent = reviewedSpec();
|
|
1767
|
+
const specHash = hashDocumentContent(specContent);
|
|
1768
|
+
await writeFile(path.join(root, 'specs', 'master', 'spec.md'), specContent, 'utf8');
|
|
1769
|
+
await writeSpecStageArtifacts(root, { specHash });
|
|
1770
|
+
const closure = await reconcileSpecCollaborationClosure(root, {
|
|
1771
|
+
decision: riskDecision,
|
|
1772
|
+
generatedAt,
|
|
1773
|
+
runId
|
|
1774
|
+
});
|
|
1775
|
+
assert.equal(closure.adjudication.closureRefs?.specAcceptanceStatus, 'accepted');
|
|
1776
|
+
assert.equal(closure.acceptedSpecRef?.hash, specHash);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
async function writeAcceptedPlanClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
|
|
1780
|
+
await writeAcceptedSpecClosure(root, riskDecision, `${runId}-spec`);
|
|
1781
|
+
const planContent = reviewedPlan();
|
|
1782
|
+
const planHash = hashDocumentContent(planContent);
|
|
1783
|
+
await writeFile(path.join(root, 'specs', 'master', 'plan.md'), planContent, 'utf8');
|
|
1784
|
+
await writePlanStageArtifacts(root, { planHash });
|
|
1785
|
+
const closure = await reconcilePlanCollaborationClosure(root, {
|
|
1786
|
+
decision: riskDecision,
|
|
1787
|
+
generatedAt,
|
|
1788
|
+
runId
|
|
1789
|
+
});
|
|
1790
|
+
assert.equal(closure.adjudication.closureRefs.planAcceptanceStatus, 'accepted');
|
|
1791
|
+
assert.equal(closure.acceptedPlanRef?.hash, planHash);
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
async function writeAcceptedTasksClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
|
|
1795
|
+
await writeAcceptedPlanClosure(root, riskDecision, `${runId}-plan`);
|
|
1796
|
+
const tasksContent = reviewedTasks();
|
|
1797
|
+
const tasksHash = hashDocumentContent(tasksContent);
|
|
1798
|
+
await writeFile(path.join(root, 'specs', 'master', 'tasks.md'), tasksContent, 'utf8');
|
|
1799
|
+
await writeTasksStageArtifacts(root, { tasksHash });
|
|
1800
|
+
const closure = await reconcileTasksCollaborationClosure(root, {
|
|
1801
|
+
decision: riskDecision,
|
|
1802
|
+
generatedAt,
|
|
1803
|
+
runId
|
|
1804
|
+
});
|
|
1805
|
+
assert.equal(closure.adjudication.closureRefs.tasksAcceptanceStatus, 'accepted');
|
|
1806
|
+
assert.equal(closure.acceptedTasksRef?.hash, tasksHash);
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
async function writeAcceptedVerifiesClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
|
|
1810
|
+
await writeAcceptedTasksClosure(root, riskDecision, `${runId}-tasks`);
|
|
1811
|
+
const tasksContent = reviewedTasks();
|
|
1812
|
+
const verifyContent = reviewedVerify(tasksContent);
|
|
1813
|
+
const verifyHash = hashDocumentContent(verifyContent);
|
|
1814
|
+
await writeFile(path.join(root, 'specs', 'master', 'verify.md'), verifyContent, 'utf8');
|
|
1815
|
+
await writeVerifiesStageArtifacts(root, { verifyHash });
|
|
1816
|
+
const closure = await reconcileVerifiesCollaborationClosure(root, {
|
|
1817
|
+
decision: riskDecision,
|
|
1818
|
+
generatedAt,
|
|
1819
|
+
runId
|
|
1820
|
+
});
|
|
1821
|
+
assert.equal(closure.adjudication.closureRefs.verifyAcceptanceStatus, 'accepted');
|
|
1822
|
+
assert.equal(closure.acceptedVerifyRef?.hash, verifyHash);
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
async function writeAcceptedDoClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<{ changedFileRef: string; changedFileHash: string }> {
|
|
1826
|
+
await writeAcceptedVerifiesClosure(root, riskDecision, `${runId}-verifies`);
|
|
1827
|
+
const changedFileRef = 'packages/app/login.ts';
|
|
1828
|
+
const changedFileContent = 'export const login = () => true;\n';
|
|
1829
|
+
await writeWorkspaceFile(root, changedFileRef, changedFileContent);
|
|
1830
|
+
const changedFileHash = hashDocumentContent(changedFileContent);
|
|
1831
|
+
await writeDoStageArtifacts(root, { changedFiles: [{ ref: changedFileRef, hash: changedFileHash }] });
|
|
1832
|
+
const closure = await reconcileDoCollaborationClosure(root, {
|
|
1833
|
+
decision: riskDecision,
|
|
1834
|
+
allowedChangedFileRefs: [changedFileRef],
|
|
1835
|
+
generatedAt,
|
|
1836
|
+
runId
|
|
1837
|
+
});
|
|
1838
|
+
assert.equal(closure.adjudication.closureRefs.implementationAcceptanceStatus, 'accepted');
|
|
1839
|
+
assert.equal(closure.acceptedImplementationRef?.ref, '.sdd/runs/master/do/implementation-v1.md');
|
|
1840
|
+
return { changedFileRef, changedFileHash };
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
async function writeAcceptedTestClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<void> {
|
|
1844
|
+
await writeAcceptedDoClosure(root, riskDecision, `${runId}-do`);
|
|
1845
|
+
await writeTestStageArtifacts(root, {});
|
|
1846
|
+
const closure = await reconcileTestCollaborationClosure(root, {
|
|
1847
|
+
decision: riskDecision,
|
|
1848
|
+
generatedAt,
|
|
1849
|
+
runId
|
|
1850
|
+
});
|
|
1851
|
+
assert.equal(closure.adjudication.closureRefs.testAcceptanceStatus, 'accepted');
|
|
1852
|
+
assert.equal(closure.acceptedTestEvidenceRef?.ref, '.sdd/runs/master/test/validation-v1.md');
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
async function writeAcceptedGoalVerifyClosure(root: string, riskDecision: LifecycleRiskDecision, runId: string): Promise<{ acceptedGoalVerificationRef: RuntimeRef }> {
|
|
1856
|
+
await writeAcceptedTestClosure(root, riskDecision, `${runId}-test`);
|
|
1857
|
+
await writeGoalVerifyStageArtifacts(root, {});
|
|
1858
|
+
const closure = await reconcileGoalVerifyCollaborationClosure(root, {
|
|
1859
|
+
decision: riskDecision,
|
|
1860
|
+
generatedAt,
|
|
1861
|
+
runId
|
|
1862
|
+
});
|
|
1863
|
+
const acceptedGoalVerificationRef = closure.acceptedGoalVerificationRef;
|
|
1864
|
+
assert.equal(closure.adjudication.health, 'ready_for_ship');
|
|
1865
|
+
assert.equal(closure.adjudication.closureRefs.goalVerifyAcceptanceStatus, 'accepted');
|
|
1866
|
+
assert.ok(acceptedGoalVerificationRef);
|
|
1867
|
+
assert.equal(acceptedGoalVerificationRef.ref, '.sdd/runs/master/goal-verify/goal-verification-v1.md');
|
|
1868
|
+
assert.ok(acceptedGoalVerificationRef.hash);
|
|
1869
|
+
assert.equal(closure.adjudication.closureRefs.truthAlignmentProjectionRef?.kind, 'projection');
|
|
1870
|
+
return { acceptedGoalVerificationRef };
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
|
|
1874
|
+
async function writeWorkspaceFile(root: string, fileRef: string, content: string): Promise<void> {
|
|
1875
|
+
await mkdir(path.dirname(path.join(root, fileRef)), { recursive: true });
|
|
1876
|
+
await writeFile(path.join(root, fileRef), content, 'utf8');
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
async function writeSpecStageArtifacts(root: string, options: { specHash: string; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation']; omitContract?: boolean }): Promise<void> {
|
|
1880
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', 'spec');
|
|
1881
|
+
await mkdir(dir, { recursive: true });
|
|
1882
|
+
if (!options.omitContract) {
|
|
1883
|
+
await writeFile(path.join(dir, 'spec-collaboration-contract-v1.md'), specCollaborationContractArtifact(specWorkOrderId()), 'utf8');
|
|
1884
|
+
}
|
|
1885
|
+
await writeFile(path.join(dir, 'scout.md'), specScoutArtifact(), 'utf8');
|
|
1886
|
+
const review = specReviewArtifact(options.specHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
|
|
1887
|
+
const reviewHash = hashDocumentContent(review);
|
|
1888
|
+
await writeFile(path.join(dir, 'spec-review-v1.md'), review, 'utf8');
|
|
1889
|
+
await writeFile(path.join(dir, 'spec-manager-v1.md'), specManagerArtifact(options.specHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
function specWorkOrderId(): string {
|
|
1893
|
+
const profile = deriveSpecCollaborationProfile(decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']), generatedAt);
|
|
1894
|
+
return buildSpecStageWorkOrder(profile, ref('projection', 'profile/spec'), generatedAt).workOrderId;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
function specCollaborationContractArtifact(workOrderId: string): string {
|
|
1898
|
+
return `---
|
|
1899
|
+
contract: sdd-stage-collaboration-contract-v1
|
|
1900
|
+
branch: master
|
|
1901
|
+
stage: spec
|
|
1902
|
+
kind: stage_collaboration_contract
|
|
1903
|
+
producer: spec-manager
|
|
1904
|
+
workOrderId: ${workOrderId}
|
|
1905
|
+
selectedAgents:
|
|
1906
|
+
- scout
|
|
1907
|
+
- spec-drafter
|
|
1908
|
+
- spec-reviewer
|
|
1909
|
+
selectedSkills:
|
|
1910
|
+
- cap.norm_discovery
|
|
1911
|
+
- cap.uncertainty_resolution
|
|
1912
|
+
selectedMaterialPacks:
|
|
1913
|
+
- project-norms
|
|
1914
|
+
- uncertainty-map
|
|
1915
|
+
evidenceContracts:
|
|
1916
|
+
- sdd-spec-scout-artifact-v1
|
|
1917
|
+
- sdd-spec-review-artifact-v1
|
|
1918
|
+
- sdd-spec-manager-artifact-v1
|
|
1919
|
+
checkpointPlan:
|
|
1920
|
+
- scout_context
|
|
1921
|
+
- review_before_closure
|
|
1922
|
+
writableRefs:
|
|
1923
|
+
- specs/master/spec.md
|
|
1924
|
+
- .sdd/runs/master/spec/scout.md
|
|
1925
|
+
- .sdd/runs/master/spec/spec-review-v1.md
|
|
1926
|
+
- .sdd/runs/master/spec/spec-manager-v1.md
|
|
1927
|
+
authorityAttempts: []
|
|
1928
|
+
maxReworkAttempts: 2
|
|
1929
|
+
allowAutoWithinStage: true
|
|
1930
|
+
allowAutoNextStage: false
|
|
1931
|
+
authorityCeiling: proposal_input
|
|
1932
|
+
---
|
|
1933
|
+
|
|
1934
|
+
# Spec Collaboration Contract
|
|
1935
|
+
|
|
1936
|
+
Spec-manager selected the spec-stage team and evidence strategy within runtime constraints.
|
|
1937
|
+
`;
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
interface StageContractFixtureWorkOrder {
|
|
1941
|
+
workOrderId: string;
|
|
1942
|
+
stage: SddStage;
|
|
1943
|
+
stageManager: string;
|
|
1944
|
+
agentTeam: readonly string[];
|
|
1945
|
+
requiredCapabilities: readonly string[];
|
|
1946
|
+
materialPackIds: readonly string[];
|
|
1947
|
+
requiredOutputKinds: readonly string[];
|
|
1948
|
+
authorityCeiling: string;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
async function writeStageCollaborationContract(root: string, stage: SddStage, writableRefs: readonly string[]): Promise<void> {
|
|
1952
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', stage);
|
|
1953
|
+
const workOrder = stageContractFixtureWorkOrder(stage);
|
|
1954
|
+
await writeFile(path.join(dir, `${stage}-collaboration-contract-v1.md`), stageCollaborationContractArtifact(workOrder, writableRefs), 'utf8');
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
function stageContractFixtureWorkOrder(stage: SddStage): StageContractFixtureWorkOrder {
|
|
1958
|
+
const scope = decision('full', ['spec', 'plan', 'tasks', 'verifies', 'do', 'test', 'goal-verify', 'ship']).scope;
|
|
1959
|
+
const profileRef = ref('projection', `profile/${stage}`);
|
|
1960
|
+
const inputRefs = [ref('document', 'specs/master/phase9.1-spec.md')];
|
|
1961
|
+
if (stage === 'plan') return buildPlanStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
|
|
1962
|
+
if (stage === 'tasks') return buildTasksStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
|
|
1963
|
+
if (stage === 'verifies') return buildVerifiesStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
|
|
1964
|
+
if (stage === 'do') return buildDoStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
|
|
1965
|
+
if (stage === 'test') return buildTestStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
|
|
1966
|
+
if (stage === 'goal-verify') return buildGoalVerifyStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
|
|
1967
|
+
if (stage === 'ship') return buildShipStageWorkOrder(scope, profileRef, inputRefs, generatedAt);
|
|
1968
|
+
throw new Error(`Unsupported stage contract fixture ${stage}`);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
function stageCollaborationContractArtifact(workOrder: StageContractFixtureWorkOrder, writableRefs: readonly string[]): string {
|
|
1972
|
+
return `---
|
|
1973
|
+
contract: sdd-stage-collaboration-contract-v1
|
|
1974
|
+
branch: master
|
|
1975
|
+
stage: ${workOrder.stage}
|
|
1976
|
+
kind: stage_collaboration_contract
|
|
1977
|
+
producer: ${workOrder.stageManager}
|
|
1978
|
+
workOrderId: ${workOrder.workOrderId}
|
|
1979
|
+
selectedAgents:
|
|
1980
|
+
${yamlList(workOrder.agentTeam)}
|
|
1981
|
+
selectedSkills:
|
|
1982
|
+
${yamlList(workOrder.requiredCapabilities.map((capability) => `cap.${capability}`))}
|
|
1983
|
+
selectedMaterialPacks:
|
|
1984
|
+
${yamlList(workOrder.materialPackIds)}
|
|
1985
|
+
evidenceContracts:
|
|
1986
|
+
${yamlList(expectedStageArtifactContracts(workOrder.stage, workOrder.requiredOutputKinds))}
|
|
1987
|
+
checkpointPlan:
|
|
1988
|
+
${yamlList(workOrder.requiredOutputKinds)}
|
|
1989
|
+
writableRefs:
|
|
1990
|
+
${yamlList(writableRefs)}
|
|
1991
|
+
authorityAttempts: []
|
|
1992
|
+
maxReworkAttempts: 2
|
|
1993
|
+
allowAutoWithinStage: true
|
|
1994
|
+
allowAutoNextStage: false
|
|
1995
|
+
authorityCeiling: ${workOrder.authorityCeiling}
|
|
1996
|
+
---
|
|
1997
|
+
|
|
1998
|
+
# ${workOrder.stage} Collaboration Contract
|
|
1999
|
+
|
|
2000
|
+
${workOrder.stageManager} selected the stage team and evidence strategy within runtime constraints.
|
|
2001
|
+
`;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
function yamlList(values: readonly string[]): string {
|
|
2005
|
+
return values.map((value) => ` - ${value}`).join('\n');
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
function specScoutArtifact(): string {
|
|
2009
|
+
return `---
|
|
2010
|
+
contract: sdd-spec-scout-artifact-v1
|
|
2011
|
+
branch: master
|
|
2012
|
+
stage: spec
|
|
2013
|
+
kind: scout_context
|
|
2014
|
+
producer: scout
|
|
2015
|
+
---
|
|
2016
|
+
|
|
2017
|
+
# Scout Context
|
|
2018
|
+
|
|
2019
|
+
The scout captured project norms and constraints for the spec drafter and reviewer.
|
|
2020
|
+
`;
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
function specReviewArtifact(specHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
|
|
2024
|
+
return `---
|
|
2025
|
+
contract: sdd-spec-review-artifact-v1
|
|
2026
|
+
branch: master
|
|
2027
|
+
stage: spec
|
|
2028
|
+
kind: spec_review
|
|
2029
|
+
producer: spec-reviewer
|
|
2030
|
+
targetRef: specs/master/spec.md
|
|
2031
|
+
targetHash: ${specHash}
|
|
2032
|
+
verdict: ${verdict}
|
|
2033
|
+
findingCount: ${blockingCount}
|
|
2034
|
+
blockingCount: ${blockingCount}
|
|
2035
|
+
---
|
|
2036
|
+
|
|
2037
|
+
# Spec Review
|
|
2038
|
+
|
|
2039
|
+
Verdict: ${verdict}.
|
|
2040
|
+
`;
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
function specManagerArtifact(specHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
|
|
2044
|
+
return `---
|
|
2045
|
+
contract: sdd-spec-manager-artifact-v1
|
|
2046
|
+
branch: master
|
|
2047
|
+
stage: spec
|
|
2048
|
+
kind: manager_closure_request
|
|
2049
|
+
producer: spec-manager
|
|
2050
|
+
targetRef: specs/master/spec.md
|
|
2051
|
+
targetHash: ${specHash}
|
|
2052
|
+
reviewRef: .sdd/runs/master/spec/spec-review-v1.md
|
|
2053
|
+
reviewHash: ${reviewHash}
|
|
2054
|
+
recommendation: ${recommendation}
|
|
2055
|
+
---
|
|
2056
|
+
|
|
2057
|
+
# Spec Manager Coordination
|
|
2058
|
+
|
|
2059
|
+
Recommendation: ${recommendation}.
|
|
2060
|
+
`;
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
async function writePlanStageArtifacts(root: string, options: { planHash: string; omitContract?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
|
|
2064
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', 'plan');
|
|
2065
|
+
await mkdir(dir, { recursive: true });
|
|
2066
|
+
if (!options.omitContract) {
|
|
2067
|
+
await writeStageCollaborationContract(root, 'plan', ['specs/master/plan.md', '.sdd/runs/master/plan/plan-review-v1.md', '.sdd/runs/master/plan/plan-manager-v1.md']);
|
|
2068
|
+
}
|
|
2069
|
+
const review = planReviewArtifact(options.planHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
|
|
2070
|
+
const reviewHash = hashDocumentContent(review);
|
|
2071
|
+
if (!options.omitReview) {
|
|
2072
|
+
await writeFile(path.join(dir, 'plan-review-v1.md'), review, 'utf8');
|
|
2073
|
+
}
|
|
2074
|
+
await writeFile(path.join(dir, 'plan-manager-v1.md'), planManagerArtifact(options.planHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
function planReviewArtifact(planHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
|
|
2078
|
+
return `---
|
|
2079
|
+
contract: sdd-plan-review-artifact-v1
|
|
2080
|
+
branch: master
|
|
2081
|
+
stage: plan
|
|
2082
|
+
kind: plan_review
|
|
2083
|
+
producer: plan-reviewer
|
|
2084
|
+
targetRef: specs/master/plan.md
|
|
2085
|
+
targetHash: ${planHash}
|
|
2086
|
+
verdict: ${verdict}
|
|
2087
|
+
findingCount: ${blockingCount}
|
|
2088
|
+
blockingCount: ${blockingCount}
|
|
2089
|
+
---
|
|
2090
|
+
|
|
2091
|
+
# Plan Review
|
|
2092
|
+
|
|
2093
|
+
Verdict: ${verdict}.
|
|
2094
|
+
`;
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
function planManagerArtifact(planHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
|
|
2098
|
+
return `---
|
|
2099
|
+
contract: sdd-plan-manager-artifact-v1
|
|
2100
|
+
branch: master
|
|
2101
|
+
stage: plan
|
|
2102
|
+
kind: manager_closure_request
|
|
2103
|
+
producer: plan-manager
|
|
2104
|
+
targetRef: specs/master/plan.md
|
|
2105
|
+
targetHash: ${planHash}
|
|
2106
|
+
reviewRef: .sdd/runs/master/plan/plan-review-v1.md
|
|
2107
|
+
reviewHash: ${reviewHash}
|
|
2108
|
+
recommendation: ${recommendation}
|
|
2109
|
+
---
|
|
2110
|
+
|
|
2111
|
+
# Plan Manager Coordination
|
|
2112
|
+
|
|
2113
|
+
Recommendation: ${recommendation}.
|
|
2114
|
+
`;
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
async function writeTasksStageArtifacts(root: string, options: { tasksHash: string; omitContract?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
|
|
2118
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', 'tasks');
|
|
2119
|
+
await mkdir(dir, { recursive: true });
|
|
2120
|
+
if (!options.omitContract) {
|
|
2121
|
+
await writeStageCollaborationContract(root, 'tasks', ['specs/master/tasks.md', '.sdd/runs/master/tasks/tasks-review-v1.md', '.sdd/runs/master/tasks/tasks-manager-v1.md']);
|
|
2122
|
+
}
|
|
2123
|
+
const review = tasksReviewArtifact(options.tasksHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
|
|
2124
|
+
const reviewHash = hashDocumentContent(review);
|
|
2125
|
+
if (!options.omitReview) {
|
|
2126
|
+
await writeFile(path.join(dir, 'tasks-review-v1.md'), review, 'utf8');
|
|
2127
|
+
}
|
|
2128
|
+
await writeFile(path.join(dir, 'tasks-manager-v1.md'), tasksManagerArtifact(options.tasksHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
function tasksReviewArtifact(tasksHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
|
|
2132
|
+
return `---
|
|
2133
|
+
contract: sdd-tasks-review-artifact-v1
|
|
2134
|
+
branch: master
|
|
2135
|
+
stage: tasks
|
|
2136
|
+
kind: tasks_review
|
|
2137
|
+
producer: tasks-reviewer
|
|
2138
|
+
targetRef: specs/master/tasks.md
|
|
2139
|
+
targetHash: ${tasksHash}
|
|
2140
|
+
verdict: ${verdict}
|
|
2141
|
+
findingCount: ${blockingCount}
|
|
2142
|
+
blockingCount: ${blockingCount}
|
|
2143
|
+
---
|
|
2144
|
+
|
|
2145
|
+
# Tasks Review
|
|
2146
|
+
|
|
2147
|
+
Verdict: ${verdict}.
|
|
2148
|
+
`;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
function tasksManagerArtifact(tasksHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
|
|
2152
|
+
return `---
|
|
2153
|
+
contract: sdd-tasks-manager-artifact-v1
|
|
2154
|
+
branch: master
|
|
2155
|
+
stage: tasks
|
|
2156
|
+
kind: manager_closure_request
|
|
2157
|
+
producer: tasks-manager
|
|
2158
|
+
targetRef: specs/master/tasks.md
|
|
2159
|
+
targetHash: ${tasksHash}
|
|
2160
|
+
reviewRef: .sdd/runs/master/tasks/tasks-review-v1.md
|
|
2161
|
+
reviewHash: ${reviewHash}
|
|
2162
|
+
recommendation: ${recommendation}
|
|
2163
|
+
---
|
|
2164
|
+
|
|
2165
|
+
# Tasks Manager Coordination
|
|
2166
|
+
|
|
2167
|
+
Recommendation: ${recommendation}.
|
|
2168
|
+
`;
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
async function writeVerifiesStageArtifacts(root: string, options: { verifyHash: string; omitContract?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
|
|
2172
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', 'verifies');
|
|
2173
|
+
await mkdir(dir, { recursive: true });
|
|
2174
|
+
if (!options.omitContract) {
|
|
2175
|
+
await writeStageCollaborationContract(root, 'verifies', ['specs/master/verify.md', '.sdd/runs/master/verifies/verifies-review-v1.md', '.sdd/runs/master/verifies/verifies-manager-v1.md']);
|
|
2176
|
+
}
|
|
2177
|
+
const review = verifiesReviewArtifact(options.verifyHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
|
|
2178
|
+
const reviewHash = hashDocumentContent(review);
|
|
2179
|
+
if (!options.omitReview) {
|
|
2180
|
+
await writeFile(path.join(dir, 'verifies-review-v1.md'), review, 'utf8');
|
|
2181
|
+
}
|
|
2182
|
+
await writeFile(path.join(dir, 'verifies-manager-v1.md'), verifiesManagerArtifact(options.verifyHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
function verifiesReviewArtifact(verifyHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
|
|
2186
|
+
return `---
|
|
2187
|
+
contract: sdd-verifies-review-artifact-v1
|
|
2188
|
+
branch: master
|
|
2189
|
+
stage: verifies
|
|
2190
|
+
kind: verifies_review
|
|
2191
|
+
producer: verifies-reviewer
|
|
2192
|
+
targetRef: specs/master/verify.md
|
|
2193
|
+
targetHash: ${verifyHash}
|
|
2194
|
+
verdict: ${verdict}
|
|
2195
|
+
findingCount: ${blockingCount}
|
|
2196
|
+
blockingCount: ${blockingCount}
|
|
2197
|
+
---
|
|
2198
|
+
|
|
2199
|
+
# Verifies Review
|
|
2200
|
+
|
|
2201
|
+
Verdict: ${verdict}.
|
|
2202
|
+
`;
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
function verifiesManagerArtifact(verifyHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
|
|
2206
|
+
return `---
|
|
2207
|
+
contract: sdd-verifies-manager-artifact-v1
|
|
2208
|
+
branch: master
|
|
2209
|
+
stage: verifies
|
|
2210
|
+
kind: manager_closure_request
|
|
2211
|
+
producer: verifies-manager
|
|
2212
|
+
targetRef: specs/master/verify.md
|
|
2213
|
+
targetHash: ${verifyHash}
|
|
2214
|
+
reviewRef: .sdd/runs/master/verifies/verifies-review-v1.md
|
|
2215
|
+
reviewHash: ${reviewHash}
|
|
2216
|
+
recommendation: ${recommendation}
|
|
2217
|
+
---
|
|
2218
|
+
|
|
2219
|
+
# Verifies Manager Coordination
|
|
2220
|
+
|
|
2221
|
+
Recommendation: ${recommendation}.
|
|
2222
|
+
`;
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
interface ChangedFileFixture {
|
|
2226
|
+
ref: string;
|
|
2227
|
+
hash: string;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
async function writeDoStageArtifacts(root: string, options: { changedFiles: ChangedFileFixture[]; omitContract?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
|
|
2231
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', 'do');
|
|
2232
|
+
await mkdir(dir, { recursive: true });
|
|
2233
|
+
if (!options.omitContract) {
|
|
2234
|
+
await writeStageCollaborationContract(root, 'do', ['.sdd/runs/master/do/implementation-v1.md', '.sdd/runs/master/do/code-review-v1.md', '.sdd/runs/master/do/do-manager-v1.md']);
|
|
2235
|
+
}
|
|
2236
|
+
const implementation = implementationArtifact(options.changedFiles);
|
|
2237
|
+
const implementationHash = hashDocumentContent(implementation);
|
|
2238
|
+
await writeFile(path.join(dir, 'implementation-v1.md'), implementation, 'utf8');
|
|
2239
|
+
const review = codeReviewArtifact(implementationHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
|
|
2240
|
+
const reviewHash = hashDocumentContent(review);
|
|
2241
|
+
if (!options.omitReview) {
|
|
2242
|
+
await writeFile(path.join(dir, 'code-review-v1.md'), review, 'utf8');
|
|
2243
|
+
}
|
|
2244
|
+
await writeFile(path.join(dir, 'do-manager-v1.md'), doManagerArtifact(implementationHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
function implementationArtifact(changedFiles: ChangedFileFixture[]): string {
|
|
2248
|
+
const changedFileEntries = changedFiles
|
|
2249
|
+
.map((changedFile) => ` - ref: ${changedFile.ref}\n hash: ${changedFile.hash}`)
|
|
2250
|
+
.join('\n');
|
|
2251
|
+
return `---
|
|
2252
|
+
contract: sdd-do-implementation-artifact-v1
|
|
2253
|
+
branch: master
|
|
2254
|
+
stage: do
|
|
2255
|
+
kind: implementation_evidence
|
|
2256
|
+
producer: implementer
|
|
2257
|
+
changedFiles:
|
|
2258
|
+
${changedFileEntries}
|
|
2259
|
+
---
|
|
2260
|
+
|
|
2261
|
+
# Implementation Evidence
|
|
2262
|
+
|
|
2263
|
+
Changed files were produced by the implementer.
|
|
2264
|
+
`;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
function codeReviewArtifact(implementationHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
|
|
2268
|
+
return `---
|
|
2269
|
+
contract: sdd-do-code-review-artifact-v1
|
|
2270
|
+
branch: master
|
|
2271
|
+
stage: do
|
|
2272
|
+
kind: code_review
|
|
2273
|
+
producer: code-reviewer
|
|
2274
|
+
targetRef: .sdd/runs/master/do/implementation-v1.md
|
|
2275
|
+
targetHash: ${implementationHash}
|
|
2276
|
+
verdict: ${verdict}
|
|
2277
|
+
findingCount: ${blockingCount}
|
|
2278
|
+
blockingCount: ${blockingCount}
|
|
2279
|
+
---
|
|
2280
|
+
|
|
2281
|
+
# Code Review
|
|
2282
|
+
|
|
2283
|
+
Verdict: ${verdict}.
|
|
2284
|
+
`;
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
function doManagerArtifact(implementationHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
|
|
2288
|
+
return `---
|
|
2289
|
+
contract: sdd-do-manager-artifact-v1
|
|
2290
|
+
branch: master
|
|
2291
|
+
stage: do
|
|
2292
|
+
kind: manager_closure_request
|
|
2293
|
+
producer: do-manager
|
|
2294
|
+
targetRef: .sdd/runs/master/do/implementation-v1.md
|
|
2295
|
+
targetHash: ${implementationHash}
|
|
2296
|
+
reviewRef: .sdd/runs/master/do/code-review-v1.md
|
|
2297
|
+
reviewHash: ${reviewHash}
|
|
2298
|
+
recommendation: ${recommendation}
|
|
2299
|
+
---
|
|
2300
|
+
|
|
2301
|
+
# Do Manager Coordination
|
|
2302
|
+
|
|
2303
|
+
Recommendation: ${recommendation}.
|
|
2304
|
+
`;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
async function writeTestStageArtifacts(root: string, options: { omitContract?: boolean; omitExecution?: boolean; omitValidation?: boolean; executionStatus?: 'passed' | 'failed'; validationStatus?: 'passed' | 'failed'; exitCode?: number; acceptanceMapped?: boolean; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
|
|
2308
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', 'test');
|
|
2309
|
+
await mkdir(dir, { recursive: true });
|
|
2310
|
+
if (!options.omitContract) {
|
|
2311
|
+
await writeStageCollaborationContract(root, 'test', ['.sdd/runs/master/test/test-execution-v1.md', '.sdd/runs/master/test/validation-v1.md', '.sdd/runs/master/test/test-review-v1.md', '.sdd/runs/master/test/test-manager-v1.md']);
|
|
2312
|
+
}
|
|
2313
|
+
const execution = testExecutionArtifact(options.executionStatus ?? 'passed', options.exitCode ?? 0);
|
|
2314
|
+
const executionHash = hashDocumentContent(execution);
|
|
2315
|
+
if (!options.omitExecution) {
|
|
2316
|
+
await writeFile(path.join(dir, 'test-execution-v1.md'), execution, 'utf8');
|
|
2317
|
+
}
|
|
2318
|
+
const validation = validationEvidenceArtifact(executionHash, options.validationStatus ?? 'passed', options.acceptanceMapped ?? true);
|
|
2319
|
+
const validationHash = hashDocumentContent(validation);
|
|
2320
|
+
if (!options.omitValidation) {
|
|
2321
|
+
await writeFile(path.join(dir, 'validation-v1.md'), validation, 'utf8');
|
|
2322
|
+
}
|
|
2323
|
+
const review = testReviewArtifact(validationHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
|
|
2324
|
+
const reviewHash = hashDocumentContent(review);
|
|
2325
|
+
if (!options.omitReview) {
|
|
2326
|
+
await writeFile(path.join(dir, 'test-review-v1.md'), review, 'utf8');
|
|
2327
|
+
}
|
|
2328
|
+
await writeFile(path.join(dir, 'test-manager-v1.md'), testManagerArtifact(validationHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
function testExecutionArtifact(status: 'passed' | 'failed', exitCode: number): string {
|
|
2332
|
+
return `---
|
|
2333
|
+
contract: sdd-test-execution-artifact-v1
|
|
2334
|
+
branch: master
|
|
2335
|
+
stage: test
|
|
2336
|
+
kind: test_execution
|
|
2337
|
+
producer: test-runner
|
|
2338
|
+
status: ${status}
|
|
2339
|
+
exitCode: ${exitCode}
|
|
2340
|
+
---
|
|
2341
|
+
|
|
2342
|
+
# Test Execution
|
|
2343
|
+
|
|
2344
|
+
Status: ${status}.
|
|
2345
|
+
`;
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
function validationEvidenceArtifact(executionHash: string, status: 'passed' | 'failed', acceptanceMapped: boolean): string {
|
|
2349
|
+
return `---
|
|
2350
|
+
contract: sdd-test-validation-artifact-v1
|
|
2351
|
+
branch: master
|
|
2352
|
+
stage: test
|
|
2353
|
+
kind: validation_evidence
|
|
2354
|
+
producer: validator
|
|
2355
|
+
executionRef: .sdd/runs/master/test/test-execution-v1.md
|
|
2356
|
+
executionHash: ${executionHash}
|
|
2357
|
+
status: ${status}
|
|
2358
|
+
acceptanceMapped: ${acceptanceMapped}
|
|
2359
|
+
---
|
|
2360
|
+
|
|
2361
|
+
# Validation Evidence
|
|
2362
|
+
|
|
2363
|
+
Acceptance mapped: ${acceptanceMapped}.
|
|
2364
|
+
`;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
function testReviewArtifact(validationHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
|
|
2368
|
+
return `---
|
|
2369
|
+
contract: sdd-test-review-artifact-v1
|
|
2370
|
+
branch: master
|
|
2371
|
+
stage: test
|
|
2372
|
+
kind: test_review
|
|
2373
|
+
producer: test-reviewer
|
|
2374
|
+
targetRef: .sdd/runs/master/test/validation-v1.md
|
|
2375
|
+
targetHash: ${validationHash}
|
|
2376
|
+
verdict: ${verdict}
|
|
2377
|
+
findingCount: ${blockingCount}
|
|
2378
|
+
blockingCount: ${blockingCount}
|
|
2379
|
+
---
|
|
2380
|
+
|
|
2381
|
+
# Test Review
|
|
2382
|
+
|
|
2383
|
+
Verdict: ${verdict}.
|
|
2384
|
+
`;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
function testManagerArtifact(validationHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
|
|
2388
|
+
return `---
|
|
2389
|
+
contract: sdd-test-manager-artifact-v1
|
|
2390
|
+
branch: master
|
|
2391
|
+
stage: test
|
|
2392
|
+
kind: manager_closure_request
|
|
2393
|
+
producer: test-manager
|
|
2394
|
+
targetRef: .sdd/runs/master/test/validation-v1.md
|
|
2395
|
+
targetHash: ${validationHash}
|
|
2396
|
+
reviewRef: .sdd/runs/master/test/test-review-v1.md
|
|
2397
|
+
reviewHash: ${reviewHash}
|
|
2398
|
+
recommendation: ${recommendation}
|
|
2399
|
+
---
|
|
2400
|
+
|
|
2401
|
+
# Test Manager Coordination
|
|
2402
|
+
|
|
2403
|
+
Recommendation: ${recommendation}.
|
|
2404
|
+
`;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
async function writeGoalVerifyStageArtifacts(root: string, options: { omitContract?: boolean; omitVerification?: boolean; status?: 'passed' | 'failed'; coverageComplete?: boolean; durableGapCount?: number; openGapCount?: number; omitReview?: boolean; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
|
|
2408
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', 'goal-verify');
|
|
2409
|
+
await mkdir(dir, { recursive: true });
|
|
2410
|
+
if (!options.omitContract) {
|
|
2411
|
+
await writeStageCollaborationContract(root, 'goal-verify', ['.sdd/runs/master/goal-verify/goal-verification-v1.md', '.sdd/runs/master/goal-verify/goal-review-v1.md', '.sdd/runs/master/goal-verify/goal-verify-manager-v1.md']);
|
|
2412
|
+
}
|
|
2413
|
+
const verification = goalVerificationArtifact(options.status ?? 'passed', options.coverageComplete ?? true, options.durableGapCount ?? 0, options.openGapCount ?? 0);
|
|
2414
|
+
const verificationHash = hashDocumentContent(verification);
|
|
2415
|
+
if (!options.omitVerification) {
|
|
2416
|
+
await writeFile(path.join(dir, 'goal-verification-v1.md'), verification, 'utf8');
|
|
2417
|
+
}
|
|
2418
|
+
const review = goalReviewArtifact(verificationHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
|
|
2419
|
+
const reviewHash = hashDocumentContent(review);
|
|
2420
|
+
if (!options.omitReview) {
|
|
2421
|
+
await writeFile(path.join(dir, 'goal-review-v1.md'), review, 'utf8');
|
|
2422
|
+
}
|
|
2423
|
+
await writeFile(path.join(dir, 'goal-verify-manager-v1.md'), goalVerifyManagerArtifact(verificationHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
function goalVerificationArtifact(status: 'passed' | 'failed', coverageComplete: boolean, durableGapCount: number, openGapCount: number): string {
|
|
2427
|
+
return `---
|
|
2428
|
+
contract: sdd-goal-verify-verification-artifact-v1
|
|
2429
|
+
branch: master
|
|
2430
|
+
stage: goal-verify
|
|
2431
|
+
kind: goal_verification
|
|
2432
|
+
producer: goal-validator
|
|
2433
|
+
status: ${status}
|
|
2434
|
+
coverageComplete: ${coverageComplete}
|
|
2435
|
+
durableGapCount: ${durableGapCount}
|
|
2436
|
+
openGapCount: ${openGapCount}
|
|
2437
|
+
---
|
|
2438
|
+
|
|
2439
|
+
# Goal Verification
|
|
2440
|
+
|
|
2441
|
+
Coverage complete: ${coverageComplete}.
|
|
2442
|
+
`;
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
function goalReviewArtifact(verificationHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
|
|
2446
|
+
return `---
|
|
2447
|
+
contract: sdd-goal-verify-review-artifact-v1
|
|
2448
|
+
branch: master
|
|
2449
|
+
stage: goal-verify
|
|
2450
|
+
kind: goal_review
|
|
2451
|
+
producer: goal-reviewer
|
|
2452
|
+
targetRef: .sdd/runs/master/goal-verify/goal-verification-v1.md
|
|
2453
|
+
targetHash: ${verificationHash}
|
|
2454
|
+
verdict: ${verdict}
|
|
2455
|
+
findingCount: ${blockingCount}
|
|
2456
|
+
blockingCount: ${blockingCount}
|
|
2457
|
+
---
|
|
2458
|
+
|
|
2459
|
+
# Goal Review
|
|
2460
|
+
|
|
2461
|
+
Verdict: ${verdict}.
|
|
2462
|
+
`;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
function goalVerifyManagerArtifact(verificationHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
|
|
2466
|
+
return `---
|
|
2467
|
+
contract: sdd-goal-verify-manager-artifact-v1
|
|
2468
|
+
branch: master
|
|
2469
|
+
stage: goal-verify
|
|
2470
|
+
kind: manager_closure_request
|
|
2471
|
+
producer: goal-verify-manager
|
|
2472
|
+
targetRef: .sdd/runs/master/goal-verify/goal-verification-v1.md
|
|
2473
|
+
targetHash: ${verificationHash}
|
|
2474
|
+
reviewRef: .sdd/runs/master/goal-verify/goal-review-v1.md
|
|
2475
|
+
reviewHash: ${reviewHash}
|
|
2476
|
+
recommendation: ${recommendation}
|
|
2477
|
+
---
|
|
2478
|
+
|
|
2479
|
+
# Goal Verify Manager Coordination
|
|
2480
|
+
|
|
2481
|
+
Recommendation: ${recommendation}.
|
|
2482
|
+
`;
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
|
|
2486
|
+
async function writeShipStageArtifacts(root: string, options: { omitContract?: boolean; omitReadiness?: boolean; omitReview?: boolean; status?: 'ready' | 'blocked'; doctorBlockerCount?: number; capabilityBlockerCount?: number; repairBlockerCount?: number; gapBlockerCount?: number; migrationBlockerCount?: number; release?: { ref: string; hash: string }; authorityAttempts?: string[]; verdict?: SpecReviewResult['verdict']; blockingCount?: number; recommendation?: SpecManagerCoordinationRecord['recommendation'] }): Promise<void> {
|
|
2487
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', 'ship');
|
|
2488
|
+
await mkdir(dir, { recursive: true });
|
|
2489
|
+
if (!options.omitContract) {
|
|
2490
|
+
await writeStageCollaborationContract(root, 'ship', ['.sdd/runs/master/ship/ship-readiness-v1.md', '.sdd/runs/master/ship/release-review-v1.md', '.sdd/runs/master/ship/ship-manager-v1.md', 'specs/master/release.md']);
|
|
2491
|
+
}
|
|
2492
|
+
const readiness = shipReadinessArtifact({
|
|
2493
|
+
status: options.status ?? 'ready',
|
|
2494
|
+
doctorBlockerCount: options.doctorBlockerCount ?? 0,
|
|
2495
|
+
capabilityBlockerCount: options.capabilityBlockerCount ?? 0,
|
|
2496
|
+
repairBlockerCount: options.repairBlockerCount ?? 0,
|
|
2497
|
+
gapBlockerCount: options.gapBlockerCount ?? 0,
|
|
2498
|
+
migrationBlockerCount: options.migrationBlockerCount ?? 0,
|
|
2499
|
+
release: options.release,
|
|
2500
|
+
authorityAttempts: options.authorityAttempts ?? []
|
|
2501
|
+
});
|
|
2502
|
+
const readinessHash = hashDocumentContent(readiness);
|
|
2503
|
+
if (!options.omitReadiness) {
|
|
2504
|
+
await writeFile(path.join(dir, 'ship-readiness-v1.md'), readiness, 'utf8');
|
|
2505
|
+
}
|
|
2506
|
+
const review = shipReleaseReviewArtifact(readinessHash, options.verdict ?? 'approved', options.blockingCount ?? 0);
|
|
2507
|
+
const reviewHash = hashDocumentContent(review);
|
|
2508
|
+
if (!options.omitReview) {
|
|
2509
|
+
await writeFile(path.join(dir, 'release-review-v1.md'), review, 'utf8');
|
|
2510
|
+
}
|
|
2511
|
+
await writeFile(path.join(dir, 'ship-manager-v1.md'), shipManagerArtifact(readinessHash, reviewHash, options.recommendation ?? 'close_stage'), 'utf8');
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2514
|
+
function shipReadinessArtifact(options: { status: 'ready' | 'blocked'; doctorBlockerCount: number; capabilityBlockerCount: number; repairBlockerCount: number; gapBlockerCount: number; migrationBlockerCount: number; release?: { ref: string; hash: string }; authorityAttempts: string[] }): string {
|
|
2515
|
+
const releaseLines = options.release ? `releaseRef: ${options.release.ref}\nreleaseHash: ${options.release.hash}\n` : '';
|
|
2516
|
+
const authorityLines = options.authorityAttempts.length > 0
|
|
2517
|
+
? `authorityAttempts:\n${options.authorityAttempts.map((attempt) => ` - ${attempt}`).join('\n')}\n`
|
|
2518
|
+
: '';
|
|
2519
|
+
return `---
|
|
2520
|
+
contract: sdd-ship-readiness-artifact-v1
|
|
2521
|
+
branch: master
|
|
2522
|
+
stage: ship
|
|
2523
|
+
kind: ship_readiness
|
|
2524
|
+
producer: ship-validator
|
|
2525
|
+
status: ${options.status}
|
|
2526
|
+
doctorBlockerCount: ${options.doctorBlockerCount}
|
|
2527
|
+
capabilityBlockerCount: ${options.capabilityBlockerCount}
|
|
2528
|
+
repairBlockerCount: ${options.repairBlockerCount}
|
|
2529
|
+
gapBlockerCount: ${options.gapBlockerCount}
|
|
2530
|
+
migrationBlockerCount: ${options.migrationBlockerCount}
|
|
2531
|
+
${releaseLines}${authorityLines}---
|
|
2532
|
+
|
|
2533
|
+
# Ship Readiness
|
|
2534
|
+
|
|
2535
|
+
Status: ${options.status}.
|
|
2536
|
+
`;
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
function shipReleaseReviewArtifact(readinessHash: string, verdict: SpecReviewResult['verdict'], blockingCount: number): string {
|
|
2540
|
+
return `---
|
|
2541
|
+
contract: sdd-ship-release-review-artifact-v1
|
|
2542
|
+
branch: master
|
|
2543
|
+
stage: ship
|
|
2544
|
+
kind: release_review
|
|
2545
|
+
producer: release-reviewer
|
|
2546
|
+
targetRef: .sdd/runs/master/ship/ship-readiness-v1.md
|
|
2547
|
+
targetHash: ${readinessHash}
|
|
2548
|
+
verdict: ${verdict}
|
|
2549
|
+
findingCount: ${blockingCount}
|
|
2550
|
+
blockingCount: ${blockingCount}
|
|
2551
|
+
---
|
|
2552
|
+
|
|
2553
|
+
# Release Review
|
|
2554
|
+
|
|
2555
|
+
Verdict: ${verdict}.
|
|
2556
|
+
`;
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
function shipManagerArtifact(readinessHash: string, reviewHash: string, recommendation: SpecManagerCoordinationRecord['recommendation']): string {
|
|
2560
|
+
return `---
|
|
2561
|
+
contract: sdd-ship-manager-artifact-v1
|
|
2562
|
+
branch: master
|
|
2563
|
+
stage: ship
|
|
2564
|
+
kind: manager_closure_request
|
|
2565
|
+
producer: ship-manager
|
|
2566
|
+
targetRef: .sdd/runs/master/ship/ship-readiness-v1.md
|
|
2567
|
+
targetHash: ${readinessHash}
|
|
2568
|
+
reviewRef: .sdd/runs/master/ship/release-review-v1.md
|
|
2569
|
+
reviewHash: ${reviewHash}
|
|
2570
|
+
recommendation: ${recommendation}
|
|
2571
|
+
---
|
|
2572
|
+
|
|
2573
|
+
# Ship Manager Coordination
|
|
2574
|
+
|
|
2575
|
+
Recommendation: ${recommendation}.
|
|
2576
|
+
`;
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
function reviewedSpec(): string {
|
|
2580
|
+
return `---
|
|
2581
|
+
contract: sdd-spec-doc-v1
|
|
2582
|
+
title: Reviewed test spec
|
|
2583
|
+
---
|
|
2584
|
+
|
|
2585
|
+
# Reviewed Test Spec
|
|
2586
|
+
|
|
2587
|
+
This spec is produced by the spec-stage test fixture and must be preserved by runtime.
|
|
2588
|
+
`;
|
|
2589
|
+
}
|
|
2590
|
+
|
|
2591
|
+
function reviewedPlan(): string {
|
|
2592
|
+
return `---
|
|
2593
|
+
contract: sdd-plan-doc-v1
|
|
2594
|
+
title: Reviewed test plan
|
|
2595
|
+
---
|
|
2596
|
+
|
|
2597
|
+
# Reviewed Test Plan
|
|
2598
|
+
|
|
2599
|
+
This plan is produced by the plan-stage test fixture and must be preserved by runtime.
|
|
2600
|
+
`;
|
|
2601
|
+
}
|
|
2602
|
+
|
|
2603
|
+
function reviewedTasks(options: { duplicate?: boolean } = {}): string {
|
|
2604
|
+
const secondId = options.duplicate ? 'T1' : 'T2';
|
|
2605
|
+
return `---
|
|
2606
|
+
contract: sdd-tasks-doc-v1
|
|
2607
|
+
title: Reviewed test tasks
|
|
2608
|
+
---
|
|
2609
|
+
|
|
2610
|
+
# Reviewed Test Tasks
|
|
2611
|
+
|
|
2612
|
+
## T1 — Implement first reviewed task
|
|
2613
|
+
|
|
2614
|
+
\`\`\`sdd-task
|
|
2615
|
+
id: T1
|
|
2616
|
+
status: pending
|
|
2617
|
+
wave: 1
|
|
2618
|
+
implementation_wave: core
|
|
2619
|
+
validation_batch: core
|
|
2620
|
+
validation_timing: task_end
|
|
2621
|
+
requires_verify_before_next: true
|
|
2622
|
+
change_surface: backend_only
|
|
2623
|
+
depends_on: []
|
|
2624
|
+
affected_files:
|
|
2625
|
+
- packages/core/src/example.ts
|
|
2626
|
+
validation:
|
|
2627
|
+
- npm test
|
|
2628
|
+
risk: []
|
|
2629
|
+
acceptance_refs:
|
|
2630
|
+
- AC-1
|
|
2631
|
+
plan_refs:
|
|
2632
|
+
- PLAN-1
|
|
2633
|
+
file_ownership: []
|
|
2634
|
+
agent_fit: []
|
|
2635
|
+
verification_availability: []
|
|
2636
|
+
autonomy: supervised
|
|
2637
|
+
allowed_agents: []
|
|
2638
|
+
required_artifacts: []
|
|
2639
|
+
\`\`\`
|
|
2640
|
+
|
|
2641
|
+
#### Boundary
|
|
2642
|
+
|
|
2643
|
+
Implement only the reviewed T1 boundary.
|
|
2644
|
+
|
|
2645
|
+
#### Acceptance
|
|
2646
|
+
|
|
2647
|
+
- AC-1 is satisfied.
|
|
2648
|
+
|
|
2649
|
+
## ${secondId} — Implement second reviewed task
|
|
2650
|
+
|
|
2651
|
+
\`\`\`sdd-task
|
|
2652
|
+
id: ${secondId}
|
|
2653
|
+
status: pending
|
|
2654
|
+
wave: 1
|
|
2655
|
+
implementation_wave: core
|
|
2656
|
+
validation_batch: core
|
|
2657
|
+
validation_timing: task_end
|
|
2658
|
+
requires_verify_before_next: true
|
|
2659
|
+
change_surface: backend_only
|
|
2660
|
+
depends_on: []
|
|
2661
|
+
affected_files:
|
|
2662
|
+
- packages/core/src/example-2.ts
|
|
2663
|
+
validation:
|
|
2664
|
+
- npm test
|
|
2665
|
+
risk: []
|
|
2666
|
+
acceptance_refs:
|
|
2667
|
+
- AC-2
|
|
2668
|
+
plan_refs:
|
|
2669
|
+
- PLAN-2
|
|
2670
|
+
file_ownership: []
|
|
2671
|
+
agent_fit: []
|
|
2672
|
+
verification_availability: []
|
|
2673
|
+
autonomy: supervised
|
|
2674
|
+
allowed_agents: []
|
|
2675
|
+
required_artifacts: []
|
|
2676
|
+
\`\`\`
|
|
2677
|
+
|
|
2678
|
+
#### Boundary
|
|
2679
|
+
|
|
2680
|
+
Implement only the reviewed ${secondId} boundary.
|
|
2681
|
+
|
|
2682
|
+
#### Acceptance
|
|
2683
|
+
|
|
2684
|
+
- AC-2 is satisfied.
|
|
2685
|
+
`;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
function reviewedVerify(tasksContent: string, options: { omitTaskId?: string } = {}): string {
|
|
2689
|
+
const taskRows = ['T1', 'T2']
|
|
2690
|
+
.filter((taskId) => taskId !== options.omitTaskId)
|
|
2691
|
+
.map((taskId) => `| ${taskId} | AC-${taskId.slice(1)} | npm test | validation-report.md | available |`)
|
|
2692
|
+
.join('\n');
|
|
2693
|
+
const batches = ['T1', 'T2']
|
|
2694
|
+
.filter((taskId) => taskId !== options.omitTaskId)
|
|
2695
|
+
.map((taskId) => ` - task: ${taskId}`)
|
|
2696
|
+
.join('\n');
|
|
2697
|
+
return `---
|
|
2698
|
+
contract: ${VERIFY_DOCUMENT_CONTRACT_VERSION}
|
|
2699
|
+
version: 1.0.0
|
|
2700
|
+
branch: master
|
|
2701
|
+
author_role: verification-designer
|
|
2702
|
+
independent_from_roles:
|
|
2703
|
+
- task-planner
|
|
2704
|
+
- implementer
|
|
2705
|
+
---
|
|
2706
|
+
|
|
2707
|
+
# Verify Contract: master
|
|
2708
|
+
|
|
2709
|
+
## 1. Purpose
|
|
2710
|
+
|
|
2711
|
+
Reviewed verification contract produced by the verifies-stage fixture.
|
|
2712
|
+
|
|
2713
|
+
## 2. Verification Batches
|
|
2714
|
+
|
|
2715
|
+
verification_batches:
|
|
2716
|
+
${batches}
|
|
2717
|
+
|
|
2718
|
+
## 3. Task Verification Matrix
|
|
2719
|
+
|
|
2720
|
+
| Task | Acceptance refs | Validation commands | Required artifacts | Verification availability |
|
|
2721
|
+
|---|---|---|---|---|
|
|
2722
|
+
${taskRows}
|
|
2723
|
+
|
|
2724
|
+
## 4. Verification Rules
|
|
2725
|
+
|
|
2726
|
+
- PASS requires policy-backed acceptance evidence.
|
|
2727
|
+
|
|
2728
|
+
## 5. Out of Scope
|
|
2729
|
+
|
|
2730
|
+
- This document does not authorize publish, push, tag, or release actions.
|
|
2731
|
+
`;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
interface StageArtifactPairCase {
|
|
2735
|
+
stage: SddStage;
|
|
2736
|
+
reviewFile: string;
|
|
2737
|
+
managerFile: string;
|
|
2738
|
+
reviewKind: string;
|
|
2739
|
+
reviewProducer: string;
|
|
2740
|
+
managerProducer: string;
|
|
2741
|
+
reviewContract: string;
|
|
2742
|
+
managerContract: string;
|
|
2743
|
+
targetRef: string;
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
const stageArtifactPairCases: StageArtifactPairCase[] = [
|
|
2747
|
+
{
|
|
2748
|
+
stage: 'tasks',
|
|
2749
|
+
reviewFile: 'tasks-review-v1.md',
|
|
2750
|
+
managerFile: 'tasks-manager-v1.md',
|
|
2751
|
+
reviewKind: 'tasks_review',
|
|
2752
|
+
reviewProducer: 'tasks-reviewer',
|
|
2753
|
+
managerProducer: 'tasks-manager',
|
|
2754
|
+
reviewContract: 'sdd-tasks-review-artifact-v1',
|
|
2755
|
+
managerContract: 'sdd-tasks-manager-artifact-v1',
|
|
2756
|
+
targetRef: 'specs/master/tasks.md'
|
|
2757
|
+
},
|
|
2758
|
+
{
|
|
2759
|
+
stage: 'verifies',
|
|
2760
|
+
reviewFile: 'verifies-review-v1.md',
|
|
2761
|
+
managerFile: 'verifies-manager-v1.md',
|
|
2762
|
+
reviewKind: 'verifies_review',
|
|
2763
|
+
reviewProducer: 'verifies-reviewer',
|
|
2764
|
+
managerProducer: 'verifies-manager',
|
|
2765
|
+
reviewContract: 'sdd-verifies-review-artifact-v1',
|
|
2766
|
+
managerContract: 'sdd-verifies-manager-artifact-v1',
|
|
2767
|
+
targetRef: 'specs/master/verify.md'
|
|
2768
|
+
},
|
|
2769
|
+
{
|
|
2770
|
+
stage: 'do',
|
|
2771
|
+
reviewFile: 'code-review-v1.md',
|
|
2772
|
+
managerFile: 'do-manager-v1.md',
|
|
2773
|
+
reviewKind: 'code_review',
|
|
2774
|
+
reviewProducer: 'code-reviewer',
|
|
2775
|
+
managerProducer: 'do-manager',
|
|
2776
|
+
reviewContract: 'sdd-do-code-review-artifact-v1',
|
|
2777
|
+
managerContract: 'sdd-do-manager-artifact-v1',
|
|
2778
|
+
targetRef: '.sdd/runs/master/do/implementation-v1.md'
|
|
2779
|
+
},
|
|
2780
|
+
{
|
|
2781
|
+
stage: 'test',
|
|
2782
|
+
reviewFile: 'test-review-v1.md',
|
|
2783
|
+
managerFile: 'test-manager-v1.md',
|
|
2784
|
+
reviewKind: 'test_review',
|
|
2785
|
+
reviewProducer: 'test-reviewer',
|
|
2786
|
+
managerProducer: 'test-manager',
|
|
2787
|
+
reviewContract: 'sdd-test-review-artifact-v1',
|
|
2788
|
+
managerContract: 'sdd-test-manager-artifact-v1',
|
|
2789
|
+
targetRef: '.sdd/runs/master/test/test-execution-v1.md'
|
|
2790
|
+
},
|
|
2791
|
+
{
|
|
2792
|
+
stage: 'goal-verify',
|
|
2793
|
+
reviewFile: 'goal-review-v1.md',
|
|
2794
|
+
managerFile: 'goal-verify-manager-v1.md',
|
|
2795
|
+
reviewKind: 'goal_review',
|
|
2796
|
+
reviewProducer: 'goal-reviewer',
|
|
2797
|
+
managerProducer: 'goal-verify-manager',
|
|
2798
|
+
reviewContract: 'sdd-goal-verify-review-artifact-v1',
|
|
2799
|
+
managerContract: 'sdd-goal-verify-manager-artifact-v1',
|
|
2800
|
+
targetRef: '.sdd/runs/master/goal-verify/goal-verification-v1.md'
|
|
2801
|
+
},
|
|
2802
|
+
{
|
|
2803
|
+
stage: 'ship',
|
|
2804
|
+
reviewFile: 'release-review-v1.md',
|
|
2805
|
+
managerFile: 'ship-manager-v1.md',
|
|
2806
|
+
reviewKind: 'release_review',
|
|
2807
|
+
reviewProducer: 'release-reviewer',
|
|
2808
|
+
managerProducer: 'ship-manager',
|
|
2809
|
+
reviewContract: 'sdd-ship-release-review-artifact-v1',
|
|
2810
|
+
managerContract: 'sdd-ship-manager-artifact-v1',
|
|
2811
|
+
targetRef: '.sdd/runs/master/ship/ship-readiness-v1.md'
|
|
2812
|
+
}
|
|
2813
|
+
];
|
|
2814
|
+
|
|
2815
|
+
async function writeStageArtifactPair(root: string, stageCase: StageArtifactPairCase): Promise<void> {
|
|
2816
|
+
const dir = path.join(root, '.sdd', 'runs', 'master', stageCase.stage);
|
|
2817
|
+
await mkdir(dir, { recursive: true });
|
|
2818
|
+
const targetHash = `target-hash-${stageCase.stage}`;
|
|
2819
|
+
const review = stageReviewArtifact(stageCase, targetHash);
|
|
2820
|
+
const reviewHash = hashDocumentContent(review);
|
|
2821
|
+
await writeFile(path.join(dir, stageCase.reviewFile), review, 'utf8');
|
|
2822
|
+
await writeFile(path.join(dir, stageCase.managerFile), stageManagerArtifact(stageCase, targetHash, reviewHash), 'utf8');
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
function stageReviewArtifact(stageCase: StageArtifactPairCase, targetHash: string): string {
|
|
2826
|
+
return `---
|
|
2827
|
+
contract: ${stageCase.reviewContract}
|
|
2828
|
+
branch: master
|
|
2829
|
+
stage: ${stageCase.stage}
|
|
2830
|
+
kind: ${stageCase.reviewKind}
|
|
2831
|
+
producer: ${stageCase.reviewProducer}
|
|
2832
|
+
targetRef: ${stageCase.targetRef}
|
|
2833
|
+
targetHash: ${targetHash}
|
|
2834
|
+
verdict: approved
|
|
2835
|
+
findingCount: 0
|
|
2836
|
+
blockingCount: 0
|
|
2837
|
+
---
|
|
2838
|
+
|
|
2839
|
+
# ${stageCase.stage} Review
|
|
2840
|
+
|
|
2841
|
+
Verdict: approved.
|
|
2842
|
+
`;
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
function stageManagerArtifact(stageCase: StageArtifactPairCase, targetHash: string, reviewHash: string): string {
|
|
2846
|
+
return `---
|
|
2847
|
+
contract: ${stageCase.managerContract}
|
|
2848
|
+
branch: master
|
|
2849
|
+
stage: ${stageCase.stage}
|
|
2850
|
+
kind: manager_closure_request
|
|
2851
|
+
producer: ${stageCase.managerProducer}
|
|
2852
|
+
targetRef: ${stageCase.targetRef}
|
|
2853
|
+
targetHash: ${targetHash}
|
|
2854
|
+
reviewRef: .sdd/runs/master/${stageCase.stage}/${stageCase.reviewFile}
|
|
2855
|
+
reviewHash: ${reviewHash}
|
|
2856
|
+
recommendation: close_stage
|
|
2857
|
+
---
|
|
2858
|
+
|
|
2859
|
+
# ${stageCase.stage} Manager Coordination
|
|
2860
|
+
|
|
2861
|
+
Recommendation: close_stage.
|
|
2862
|
+
`;
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
function testStageRun(id: string, scope: { branch: string }, status: 'completed' | 'failed', timestamp: string) {
|
|
2866
|
+
return {
|
|
2867
|
+
contract: 'sdd-stage-run-v1' as const,
|
|
2868
|
+
id,
|
|
2869
|
+
scope,
|
|
2870
|
+
stage: 'do' as const,
|
|
2871
|
+
ownerAgent: 'do-manager',
|
|
2872
|
+
coMainAgents: ['implementer', 'code-reviewer'],
|
|
2873
|
+
status,
|
|
2874
|
+
inputRefs: [],
|
|
2875
|
+
outputRefs: status === 'completed' ? [ref('artifact', '.sdd/runs/feature/do/implementation-v1.md', 'hash')] : [],
|
|
2876
|
+
decisionRefs: [],
|
|
2877
|
+
blockingReasons: status === 'failed' ? ['Later diagnostic rejection must not downgrade completed stage.'] : [],
|
|
2878
|
+
createdAt: timestamp,
|
|
2879
|
+
updatedAt: timestamp
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
|
|
2883
|
+
function ref(kind: RuntimeRefKind, value: string, hash?: string) {
|
|
2884
|
+
return hash ? { kind, ref: value, hash } : { kind, ref: value };
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
async function markdownSnapshot(root: string): Promise<Map<string, string>> {
|
|
2888
|
+
const entries = new Map<string, string>();
|
|
2889
|
+
await collectMarkdownSnapshot(root, root, entries);
|
|
2890
|
+
return entries;
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
async function collectMarkdownSnapshot(root: string, dir: string, entries: Map<string, string>): Promise<void> {
|
|
2894
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
2895
|
+
const absolute = path.join(dir, entry.name);
|
|
2896
|
+
const relative = path.relative(root, absolute).replace(/\\/g, '/');
|
|
2897
|
+
if (entry.isDirectory()) {
|
|
2898
|
+
await collectMarkdownSnapshot(root, absolute, entries);
|
|
2899
|
+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
2900
|
+
entries.set(relative, hashDocumentContent(await readFile(absolute, 'utf8')));
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
}
|