@planu/cli 4.14.1 → 5.1.1
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/CHANGELOG.md +82 -0
- package/LICENSE +55 -12
- package/README.md +10 -2
- package/dist/cli/commands/install.d.ts +1 -12
- package/dist/cli/commands/install.js +9 -65
- package/dist/cli/commands/job.js +4 -3
- package/dist/cli/commands/spec.js +12 -3
- package/dist/cli/commands/validate.js +2 -2
- package/dist/cli/router.js +1 -3
- package/dist/config/environment-schema.json +1 -3
- package/dist/config/official-sdd-tools.d.ts +4 -0
- package/dist/config/official-sdd-tools.js +35 -0
- package/dist/config/product-public.d.ts +139 -3
- package/dist/config/product-public.js +8 -1
- package/dist/config/product-public.json +7 -36
- package/dist/config/registries/hosts/codex.json +20 -363
- package/dist/config/release-policy.json +84 -2
- package/dist/config/runtime-policy.json +12 -1
- package/dist/config/skill-templates/planu-resume-work.md +2 -2
- package/dist/core/index.d.ts +1 -1
- package/dist/core/spec-validator.d.ts +4 -4
- package/dist/core/spec-validator.js +33 -19
- package/dist/engine/abortable-process-runner.d.ts +20 -2
- package/dist/engine/abortable-process-runner.js +243 -74
- package/dist/engine/ambiguity-scorer.js +4 -26
- package/dist/engine/auth/scope-mapper.js +1 -7
- package/dist/engine/autopilot/state-updater.js +4 -4
- package/dist/engine/cascade-hooks/hooks/review-enricher.hook.js +9 -6
- package/dist/engine/cascade-hooks/state-drift-detector.d.ts +3 -3
- package/dist/engine/cascade-hooks/state-drift-detector.js +1 -1
- package/dist/engine/ci-generator/planu-config.js +1 -7
- package/dist/engine/clarification-gate/gate.d.ts +3 -3
- package/dist/engine/code-scanner/layer-scanner.js +6 -22
- package/dist/engine/core-bridge.d.ts +3 -3
- package/dist/engine/cost-guardrails.d.ts +1 -1
- package/dist/engine/cost-guardrails.js +1 -1
- package/dist/engine/criterion-identity.d.ts +1 -0
- package/dist/engine/criterion-identity.js +7 -0
- package/dist/engine/dashboard/data-loader.js +2 -7
- package/dist/engine/dashboard/templates-layout.js +0 -3
- package/dist/engine/dashboard/templates-project.js +1 -13
- package/dist/engine/docs-site-generator/data-collector.d.ts +1 -1
- package/dist/engine/docs-site-generator/data-collector.js +7 -28
- package/dist/engine/docs-site-generator/tools-renderer.js +3 -10
- package/dist/engine/evidence-gates/artifact-reader.d.ts +12 -1
- package/dist/engine/evidence-gates/artifact-reader.js +174 -4
- package/dist/engine/evidence-gates/evidence-autofill.d.ts +10 -0
- package/dist/engine/evidence-gates/evidence-autofill.js +148 -0
- package/dist/engine/evidence-gates/evidence-skeletons.d.ts +19 -0
- package/dist/engine/evidence-gates/evidence-skeletons.js +69 -0
- package/dist/engine/evidence-gates/lifecycle-gate.js +4 -3
- package/dist/engine/evidence-gates/reconciliation-freshness.d.ts +11 -0
- package/dist/engine/evidence-gates/reconciliation-freshness.js +72 -0
- package/dist/engine/evidence-index/done-drift.d.ts +1 -1
- package/dist/engine/evidence-index/done-drift.js +40 -18
- package/dist/engine/execution/context.js +3 -0
- package/dist/engine/execution/durable-job-heartbeat-runtime.d.ts +4 -0
- package/dist/engine/execution/durable-job-heartbeat-runtime.js +400 -0
- package/dist/engine/execution/job-runtime.d.ts +30 -0
- package/dist/engine/execution/job-runtime.js +114 -6
- package/dist/engine/execution/operation-journal.js +10 -4
- package/dist/engine/execution/public-job-sanitizer.d.ts +4 -0
- package/dist/engine/execution/public-job-sanitizer.js +31 -0
- package/dist/engine/execution/validate-job-executor.d.ts +12 -1
- package/dist/engine/execution/validate-job-executor.js +549 -86
- package/dist/engine/execution/validate-job-payload.js +107 -50
- package/dist/engine/execution/validate-job-recovery.js +1 -1
- package/dist/engine/execution/validate-worker-runtime.d.ts +5 -0
- package/dist/engine/execution/validate-worker-runtime.js +90 -2
- package/dist/engine/execution/validate-worker-thread.js +17 -24
- package/dist/engine/handoff-artifacts/schemas.d.ts +2 -1
- package/dist/engine/handoff-artifacts/schemas.js +33 -0
- package/dist/engine/handoff-packager.d.ts +19 -0
- package/dist/engine/handoff-packager.js +415 -13
- package/dist/engine/hooks/effort-configurator.js +1 -1
- package/dist/engine/impact-detector/test-break-predictor.js +3 -3
- package/dist/engine/impact-detector/tool-registration.js +1 -1
- package/dist/engine/implementation-contract/renderer.js +23 -8
- package/dist/engine/lifecycle-reconciliation-io.d.ts +11 -0
- package/dist/engine/lifecycle-reconciliation-io.js +180 -0
- package/dist/engine/lifecycle-reconciliation.d.ts +16 -0
- package/dist/engine/lifecycle-reconciliation.js +406 -0
- package/dist/engine/local-first/tool-classification.js +2 -0
- package/dist/engine/macos-keychain-native-port.d.ts +5 -0
- package/dist/engine/macos-keychain-native-port.js +19 -7
- package/dist/engine/migrations/remove-commercial-state.d.ts +3 -0
- package/dist/engine/migrations/remove-commercial-state.js +343 -0
- package/dist/engine/minimality/policy-loader.js +247 -6
- package/dist/engine/network-policy.js +1 -0
- package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
- package/dist/engine/planu-core.darwin-arm64.node.sbom.json +20 -20
- package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
- package/dist/engine/planu-core.darwin-x64.node.sbom.json +20 -20
- package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +20 -20
- package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +20 -20
- package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +20 -20
- package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +20 -20
- package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
- package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +20 -20
- package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
- package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +20 -20
- package/dist/engine/readiness-checker.d.ts +1 -16
- package/dist/engine/readiness-checker.js +27 -88
- package/dist/engine/reverse-engineer/api-detector.js +2 -13
- package/dist/engine/reverse-engineer/complexity-analyzer.js +2 -13
- package/dist/engine/reverse-engineer/config-analyzer.js +2 -13
- package/dist/engine/reverse-engineer/dependency-graph.js +2 -13
- package/dist/engine/reverse-engineer/test-analyzer.js +2 -13
- package/dist/engine/reverse-engineer/walk-ignore.d.ts +3 -0
- package/dist/engine/reverse-engineer/walk-ignore.js +26 -0
- package/dist/engine/runtime-policy.d.ts +2 -0
- package/dist/engine/runtime-policy.js +42 -5
- package/dist/engine/runtime-security/rate-limiter.js +0 -1
- package/dist/engine/safety/contained-project-file.d.ts +3 -0
- package/dist/engine/safety/contained-project-file.js +23 -0
- package/dist/engine/scope-boundaries/contradiction-checker.js +63 -1
- package/dist/engine/scope-boundaries/scope-validator.js +45 -7
- package/dist/engine/session/checkpoint-writer.js +0 -9
- package/dist/engine/spec-format/acceptance-criteria.d.ts +6 -2
- package/dist/engine/spec-format/acceptance-criteria.js +82 -22
- package/dist/engine/spec-format/markdown-sections.d.ts +14 -0
- package/dist/engine/spec-format/markdown-sections.js +105 -0
- package/dist/engine/spec-format/read-technical-section.js +33 -5
- package/dist/engine/spec-format/technical-md-populator.js +3 -2
- package/dist/engine/spec-format/text-fences.js +20 -2
- package/dist/engine/spec-format/unified-spec-builder.d.ts +7 -0
- package/dist/engine/spec-format/unified-spec-builder.js +202 -0
- package/dist/engine/spec-migrator/canonical-deduplicator.d.ts +18 -0
- package/dist/engine/spec-migrator/canonical-deduplicator.js +479 -0
- package/dist/engine/spec-migrator/filesystem-import.d.ts +4 -8
- package/dist/engine/spec-migrator/filesystem-import.js +121 -14
- package/dist/engine/spec-migrator/frontmatter-parser.js +32 -15
- package/dist/engine/spec-migrator/index.d.ts +1 -0
- package/dist/engine/spec-migrator/index.js +1 -0
- package/dist/engine/spec-state-machine/transition-spec.d.ts +6 -1
- package/dist/engine/spec-state-machine/transition-spec.js +10 -1
- package/dist/engine/spec-state-syncer.js +1 -1
- package/dist/engine/technical-enricher/index.js +16 -8
- package/dist/engine/technical-enricher/render-enriched.d.ts +1 -0
- package/dist/engine/technical-enricher/render-enriched.js +42 -14
- package/dist/engine/telemetry/health-checker.js +53 -17
- package/dist/engine/telemetry/telemetry-client.js +26 -8
- package/dist/engine/text-signal-boundaries.d.ts +2 -0
- package/dist/engine/text-signal-boundaries.js +25 -2
- package/dist/engine/time/relative-date.d.ts +3 -0
- package/dist/engine/time/relative-date.js +16 -0
- package/dist/engine/timing/budget.d.ts +11 -0
- package/dist/engine/timing/budget.js +31 -9
- package/dist/engine/token-optimizer/analytics.d.ts +1 -1
- package/dist/engine/token-optimizer/analytics.js +5 -6
- package/dist/engine/usage-tracker/core.d.ts +2 -2
- package/dist/engine/usage-tracker/core.js +1 -2
- package/dist/engine/usage-tracker/index.d.ts +0 -3
- package/dist/engine/usage-tracker/index.js +0 -3
- package/dist/engine/usage-tracker/stats.d.ts +2 -2
- package/dist/engine/usage-tracker/stats.js +1 -28
- package/dist/engine/validation/durable-validation.d.ts +1 -1
- package/dist/engine/validation/durable-validation.js +3 -82
- package/dist/engine/validation/validation-freshness.d.ts +11 -1
- package/dist/engine/validation/validation-freshness.js +231 -91
- package/dist/engine/validation/validation-receipt.d.ts +9 -6
- package/dist/engine/validation/validation-receipt.js +84 -24
- package/dist/engine/validation/validation-source-digest.d.ts +2 -0
- package/dist/engine/validation/validation-source-digest.js +86 -0
- package/dist/engine/validation/validation-submission.d.ts +5 -4
- package/dist/engine/validation/validation-submission.js +25 -11
- package/dist/engine/validation/validation-worktree.d.ts +21 -0
- package/dist/engine/validation/validation-worktree.js +260 -0
- package/dist/engine/validation-impact-planner.d.ts +14 -2
- package/dist/engine/validation-impact-planner.js +0 -3
- package/dist/engine/validator/dor-dod.d.ts +1 -0
- package/dist/engine/validator/dor-dod.js +5 -3
- package/dist/engine/validator/extractors.d.ts +1 -5
- package/dist/engine/validator/extractors.js +24 -69
- package/dist/engine/validator/spec-compliance-runner.d.ts +12 -24
- package/dist/engine/validator/spec-compliance-runner.js +429 -211
- package/dist/engine/validator.js +35 -22
- package/dist/index.js +4 -16
- package/dist/resources/usage-stats-resource.js +2 -18
- package/dist/security/adapters/linux-secret-service.js +7 -3
- package/dist/security/adapters/windows-credential-manager.js +6 -4
- package/dist/server/routes/specs.js +13 -1
- package/dist/storage/base-store.d.ts +1 -1
- package/dist/storage/base-store.js +1 -1
- package/dist/storage/feedback-remote.d.ts +2 -3
- package/dist/storage/feedback-remote.js +45 -31
- package/dist/storage/global-store.d.ts +1 -17
- package/dist/storage/global-store.js +1 -42
- package/dist/storage/index.d.ts +0 -2
- package/dist/storage/index.js +0 -2
- package/dist/storage/migrations/canonical-storage.js +124 -11
- package/dist/storage/runtime-db.d.ts +9 -0
- package/dist/storage/runtime-db.js +64 -8
- package/dist/storage/spec-store.d.ts +14 -2
- package/dist/storage/spec-store.js +104 -14
- package/dist/storage/status-store/atomic-status-write.d.ts +5 -0
- package/dist/storage/status-store/atomic-status-write.js +57 -0
- package/dist/storage/status-store/file-lock.js +30 -11
- package/dist/storage/status-store/self-healing.js +3 -3
- package/dist/storage/status-store/version-sync.d.ts +2 -1
- package/dist/storage/status-store/version-sync.js +35 -40
- package/dist/storage/usage-store.d.ts +1 -5
- package/dist/storage/usage-store.js +0 -35
- package/dist/tools/auto-update-handler.d.ts +3 -3
- package/dist/tools/auto-update-handler.js +3 -3
- package/dist/tools/challenge-spec/resilience-challenge-scenarios.d.ts +2 -1
- package/dist/tools/challenge-spec/resilience-challenge-scenarios.js +10 -7
- package/dist/tools/challenge-spec/scenario-collector.js +1 -1
- package/dist/tools/challenge-spec/scenarios-data.js +1 -1
- package/dist/tools/challenge-spec/scenarios-utils.d.ts +1 -1
- package/dist/tools/challenge-spec/scenarios-utils.js +35 -11
- package/dist/tools/challenge-spec-helpers.d.ts +10 -1
- package/dist/tools/challenge-spec-helpers.js +63 -22
- package/dist/tools/challenge-spec.js +23 -8
- package/dist/tools/check-readiness.js +38 -14
- package/dist/tools/configure-telemetry.js +30 -2
- package/dist/tools/create-spec/spec-builder.d.ts +7 -0
- package/dist/tools/create-spec/spec-builder.js +19 -15
- package/dist/tools/create-spec.d.ts +1 -0
- package/dist/tools/create-spec.js +584 -309
- package/dist/tools/git/branch-ops.d.ts +1 -1
- package/dist/tools/git/branch-ops.js +3 -2
- package/dist/tools/init-project/handler.js +10 -12
- package/dist/tools/init-project/host-assets-writer.js +26 -1
- package/dist/tools/init-project/lint-scaffolder.d.ts +3 -3
- package/dist/tools/init-project/migration-runner.d.ts +2 -4
- package/dist/tools/init-project/migration-runner.js +3 -2
- package/dist/tools/init-project/portable-index-reconciler.d.ts +4 -0
- package/dist/tools/init-project/portable-index-reconciler.js +14 -0
- package/dist/tools/init-project/result-builder.js +1 -3
- package/dist/tools/jobs/handlers.js +38 -6
- package/dist/tools/list-specs.js +5 -26
- package/dist/tools/merge-risk-handler.js +2 -2
- package/dist/tools/package-handoff.js +2 -0
- package/dist/tools/reconcile-spec.js +0 -27
- package/dist/tools/register-agent-squad-tools.js +2 -2
- package/dist/tools/register-platform-tools/design-stack-tools.js +11 -11
- package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +7 -7
- package/dist/tools/register-sdd-tools.d.ts +1 -1
- package/dist/tools/register-sdd-tools.js +2 -29
- package/dist/tools/register-spec-tools/analysis-tools.js +8 -8
- package/dist/tools/register-spec-tools/core-spec-tools.js +29 -19
- package/dist/tools/safe-handler.d.ts +5 -13
- package/dist/tools/safe-handler.js +7 -23
- package/dist/tools/schemas/index.d.ts +1 -1
- package/dist/tools/schemas/index.js +1 -1
- package/dist/tools/schemas/output-schemas.d.ts +0 -23
- package/dist/tools/schemas/output-schemas.js +0 -28
- package/dist/tools/schemas/spec.d.ts +4 -0
- package/dist/tools/schemas/spec.js +3 -0
- package/dist/tools/schemas/validate-output-schema.d.ts +11 -0
- package/dist/tools/schemas/validate-output-schema.js +7 -0
- package/dist/tools/spec-prompt-handler.js +3 -3
- package/dist/tools/sync-spec-state-handler.d.ts +1 -1
- package/dist/tools/sync-spec-state-handler.js +50 -2
- package/dist/tools/tool-entry.d.ts +4 -4
- package/dist/tools/tool-entry.js +3 -3
- package/dist/tools/tool-registry/core-tools.js +31 -15
- package/dist/tools/tool-registry/group-infra.js +27 -110
- package/dist/tools/tool-registry/group-quality-compliance.js +26 -26
- package/dist/tools/tool-registry/inventory.d.ts +2 -0
- package/dist/tools/tool-registry/inventory.js +2 -0
- package/dist/tools/tool-registry-helpers.d.ts +3 -3
- package/dist/tools/tool-registry-helpers.js +3 -3
- package/dist/tools/tool-usage-report/registered-tools.d.ts +1 -1
- package/dist/tools/tool-usage-report/registered-tools.js +3 -15
- package/dist/tools/update-status/approval-gates.js +3 -0
- package/dist/tools/update-status/batch.d.ts +6 -2
- package/dist/tools/update-status/batch.js +58 -1
- package/dist/tools/update-status/dod-gates.d.ts +16 -1
- package/dist/tools/update-status/dod-gates.js +191 -1
- package/dist/tools/update-status/done-receipt-verifier.d.ts +8 -0
- package/dist/tools/update-status/done-receipt-verifier.js +75 -27
- package/dist/tools/update-status/evidence-gate.d.ts +4 -0
- package/dist/tools/update-status/evidence-gate.js +103 -3
- package/dist/tools/update-status/file-sync.d.ts +4 -3
- package/dist/tools/update-status/file-sync.js +57 -6
- package/dist/tools/update-status/index.d.ts +25 -1
- package/dist/tools/update-status/index.js +292 -78
- package/dist/tools/update-status/response-builder.js +3 -3
- package/dist/tools/update-status/side-effects.d.ts +2 -0
- package/dist/tools/update-status/side-effects.js +2 -2
- package/dist/tools/update-status/transition-guard.js +45 -16
- package/dist/tools/update-status-actions.d.ts +4 -2
- package/dist/tools/update-status-actions.js +16 -14
- package/dist/tools/usage-report.js +1 -16
- package/dist/tools/usage-stats.js +1 -18
- package/dist/tools/usage-tracking.d.ts +4 -0
- package/dist/tools/usage-tracking.js +20 -0
- package/dist/tools/validate-lint.d.ts +1 -0
- package/dist/tools/validate-lint.js +1 -1
- package/dist/tools/validate.d.ts +2 -1
- package/dist/tools/validate.js +114 -34
- package/dist/tools/workspace-dashboard-handler.js +38 -0
- package/dist/transports/http-transport.js +6 -8
- package/dist/transports/mcp-types.d.ts +0 -2
- package/dist/transports/session-context.js +1 -1
- package/dist/types/cascade-hooks.d.ts +1 -1
- package/dist/types/ci.d.ts +0 -4
- package/dist/types/clarification-token.d.ts +1 -1
- package/dist/types/claude-code-runtime.d.ts +0 -27
- package/dist/types/commercial-migration.d.ts +17 -0
- package/dist/types/commercial-migration.js +2 -0
- package/dist/types/common/primitives.d.ts +0 -2
- package/dist/types/core-bridge.d.ts +4 -10
- package/dist/types/dashboard.d.ts +3 -7
- package/dist/types/docs.d.ts +0 -1
- package/dist/types/durable-job.d.ts +4 -4
- package/dist/types/durable-validation.d.ts +100 -2
- package/dist/types/durable-validation.js +165 -1
- package/dist/types/errors/structured-error.d.ts +3 -3
- package/dist/types/estimation.d.ts +2 -0
- package/dist/types/evidence-autofill.d.ts +34 -0
- package/dist/types/evidence-autofill.js +2 -0
- package/dist/types/evidence-gates.d.ts +9 -1
- package/dist/types/execution.d.ts +2 -0
- package/dist/types/gemini-integration.d.ts +0 -11
- package/dist/types/handoff-artifacts.d.ts +41 -0
- package/dist/types/hooks-advanced.d.ts +0 -13
- package/dist/types/impact-detection.d.ts +2 -2
- package/dist/types/index.d.ts +3 -6
- package/dist/types/index.js +2 -5
- package/dist/types/kiro-integration.d.ts +0 -6
- package/dist/types/network-policy.d.ts +1 -1
- package/dist/types/project/lint-scaffolder.d.ts +1 -1
- package/dist/types/runtime-policy.d.ts +11 -0
- package/dist/types/session-safeguard.d.ts +0 -31
- package/dist/types/spec/canonical-repair.d.ts +81 -0
- package/dist/types/spec/canonical-repair.js +2 -0
- package/dist/types/spec/core.d.ts +20 -0
- package/dist/types/spec/index.d.ts +1 -0
- package/dist/types/spec/index.js +1 -0
- package/dist/types/spec/inputs.d.ts +35 -1
- package/dist/types/spec-format.d.ts +25 -1
- package/dist/types/telemetry.d.ts +4 -1
- package/dist/types/transition-log.d.ts +1 -1
- package/dist/types/transport.d.ts +0 -2
- package/dist/types/usage.d.ts +1 -36
- package/dist/types/usage.js +1 -1
- package/dist/types/validate-worker.d.ts +6 -0
- package/dist/types/validation-receipt.d.ts +1 -1
- package/dist/types/validation.d.ts +10 -4
- package/package.json +17 -12
- package/planu-native.json +1 -1
- package/planu-plugin.json +23 -40
- package/scripts/lib/pending-release-file.mjs +93 -0
- package/src/i18n/messages/en.json +0 -41
- package/src/i18n/messages/es.json +0 -41
- package/src/i18n/messages/pt.json +0 -41
- package/dist/cli/commands/activate.d.ts +0 -14
- package/dist/cli/commands/activate.js +0 -174
- package/dist/config/license-plans.json +0 -251
- package/dist/config/product-public.ts +0 -148
- package/dist/config/server-instructions.ts +0 -121
- package/dist/config/version-defaults.ts +0 -76
- package/dist/config/version.ts +0 -21
- package/dist/engine/ai-integration/gemini/settings-generator.d.ts +0 -4
- package/dist/engine/ai-integration/gemini/settings-generator.js +0 -195
- package/dist/engine/ai-integration/kiro/hooks-generator.d.ts +0 -6
- package/dist/engine/ai-integration/kiro/hooks-generator.js +0 -75
- package/dist/engine/hooks/full-spectrum-generator.d.ts +0 -32
- package/dist/engine/hooks/full-spectrum-generator.js +0 -117
- package/dist/engine/license-validator/config-loader.d.ts +0 -5
- package/dist/engine/license-validator/config-loader.js +0 -16
- package/dist/engine/license-validator/core.d.ts +0 -15
- package/dist/engine/license-validator/core.js +0 -151
- package/dist/engine/license-validator/fingerprint.d.ts +0 -3
- package/dist/engine/license-validator/fingerprint.js +0 -17
- package/dist/engine/license-validator/lemon-squeezy.d.ts +0 -5
- package/dist/engine/license-validator/lemon-squeezy.js +0 -55
- package/dist/engine/license-validator.d.ts +0 -5
- package/dist/engine/license-validator.js +0 -6
- package/dist/engine/product-intelligence/index.d.ts +0 -7
- package/dist/engine/product-intelligence/index.js +0 -211
- package/dist/engine/session/auto-save.d.ts +0 -18
- package/dist/engine/session/auto-save.js +0 -98
- package/dist/engine/session/context-snapshot.d.ts +0 -8
- package/dist/engine/session/context-snapshot.js +0 -49
- package/dist/engine/session/memory-sync.d.ts +0 -12
- package/dist/engine/session/memory-sync.js +0 -34
- package/dist/engine/session/session-diff.d.ts +0 -11
- package/dist/engine/session/session-diff.js +0 -42
- package/dist/engine/session/session-merge.d.ts +0 -12
- package/dist/engine/session/session-merge.js +0 -55
- package/dist/engine/session-safeguard/checkpoint-runner.d.ts +0 -4
- package/dist/engine/session-safeguard/checkpoint-runner.js +0 -70
- package/dist/engine/session-safeguard/learnings-buffer.d.ts +0 -15
- package/dist/engine/session-safeguard/learnings-buffer.js +0 -91
- package/dist/engine/session-safeguard/precompaction-drain.d.ts +0 -10
- package/dist/engine/session-safeguard/precompaction-drain.js +0 -82
- package/dist/engine/session-safeguard/unpushed-detector.d.ts +0 -7
- package/dist/engine/session-safeguard/unpushed-detector.js +0 -59
- package/dist/engine/spec-changelog/core.d.ts +0 -16
- package/dist/engine/spec-changelog/core.js +0 -176
- package/dist/engine/spec-changelog/diff.d.ts +0 -18
- package/dist/engine/spec-changelog/diff.js +0 -121
- package/dist/engine/spec-changelog/index.d.ts +0 -3
- package/dist/engine/spec-changelog/index.js +0 -4
- package/dist/engine/trial-engine.d.ts +0 -5
- package/dist/engine/trial-engine.js +0 -14
- package/dist/engine/usage-tracker/rate-check.d.ts +0 -7
- package/dist/engine/usage-tracker/rate-check.js +0 -18
- package/dist/engine/usage-tracker/rate-limiter.d.ts +0 -14
- package/dist/engine/usage-tracker/rate-limiter.js +0 -73
- package/dist/engine/usage-tracker/trial.d.ts +0 -27
- package/dist/engine/usage-tracker/trial.js +0 -49
- package/dist/engine/webhook/event-handlers.d.ts +0 -25
- package/dist/engine/webhook/event-handlers.js +0 -89
- package/dist/engine/webhook/index.d.ts +0 -4
- package/dist/engine/webhook/index.js +0 -5
- package/dist/engine/webhook/server.d.ts +0 -12
- package/dist/engine/webhook/server.js +0 -249
- package/dist/engine/webhook/signature.d.ts +0 -16
- package/dist/engine/webhook/signature.js +0 -49
- package/dist/storage/license-store.d.ts +0 -6
- package/dist/storage/license-store.js +0 -63
- package/dist/storage/session-store.d.ts +0 -20
- package/dist/storage/session-store.js +0 -136
- package/dist/tools/activate-license.d.ts +0 -6
- package/dist/tools/activate-license.js +0 -132
- package/dist/tools/checkpoint-handler.d.ts +0 -3
- package/dist/tools/checkpoint-handler.js +0 -130
- package/dist/tools/export-session.d.ts +0 -3
- package/dist/tools/export-session.js +0 -90
- package/dist/tools/license-gate.d.ts +0 -18
- package/dist/tools/license-gate.js +0 -222
- package/dist/tools/license-status.d.ts +0 -3
- package/dist/tools/license-status.js +0 -93
- package/dist/tools/list-sessions.d.ts +0 -3
- package/dist/tools/list-sessions.js +0 -46
- package/dist/tools/planu-session-checkpoint.d.ts +0 -6
- package/dist/tools/planu-session-checkpoint.js +0 -42
- package/dist/tools/restore-session.d.ts +0 -3
- package/dist/tools/restore-session.js +0 -71
- package/dist/tools/spec-history.d.ts +0 -3
- package/dist/tools/spec-history.js +0 -59
- package/dist/tools/webhook.d.ts +0 -6
- package/dist/tools/webhook.js +0 -150
- package/dist/types/changelog.d.ts +0 -49
- package/dist/types/changelog.js +0 -3
- package/dist/types/licensing.d.ts +0 -109
- package/dist/types/licensing.js +0 -4
- package/dist/types/product-intelligence.d.ts +0 -60
- package/dist/types/product-intelligence.js +0 -3
- package/dist/types/session.d.ts +0 -84
- package/dist/types/session.js +0 -3
- package/dist/types/webhook.d.ts +0 -62
- package/dist/types/webhook.js +0 -4
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import type { UpdateStatusInput, ToolResult } from '../../types/index.js';
|
|
2
|
+
import type { UpdateStatusInput, ToolResult, Actuals, ReconciliationInvocationContext } from '../../types/index.js';
|
|
3
|
+
type LifecycleAuthorityRoute = {
|
|
4
|
+
route: 'ordinary';
|
|
5
|
+
} | {
|
|
6
|
+
route: 'reconcile';
|
|
7
|
+
} | {
|
|
8
|
+
route: 'error';
|
|
9
|
+
result: ToolResult;
|
|
10
|
+
};
|
|
11
|
+
/** Select the lifecycle path before any transition, artifact, receipt, or log mutation. */
|
|
12
|
+
export declare function classifyLifecycleAuthority(input: UpdateStatusInput, context: ReconciliationInvocationContext, options?: {
|
|
13
|
+
requireDoneDigest?: boolean;
|
|
14
|
+
}): LifecycleAuthorityRoute;
|
|
15
|
+
/**
|
|
16
|
+
* SPEC-1356 (T-A2): a `done` transition no longer requires the caller to supply
|
|
17
|
+
* actuals. When the whole object is omitted, synthesize a minimal record instead
|
|
18
|
+
* of blocking the transition with `actuals_required`. Every caller-provided
|
|
19
|
+
* field — including a caller-provided partial object once the MCP input schema
|
|
20
|
+
* accepts one — is preserved verbatim; only genuinely unmeasured numeric fields
|
|
21
|
+
* default to 0, and `completedAt`/`notes` only default when absent.
|
|
22
|
+
*/
|
|
23
|
+
export declare function synthesizeActuals(actuals: Partial<Actuals> | undefined, transitionedAt: string): Actuals;
|
|
24
|
+
/** Bind deferred lifecycle work to one normalized absolute root without persisting that path. */
|
|
25
|
+
export declare function computeTransitionRootBinding(projectPath: string): string;
|
|
3
26
|
/** Recover durable post-commit work for every registered project after server startup. */
|
|
4
27
|
export declare function recoverPendingPostCommitTasksAtStartup(): Promise<void>;
|
|
5
28
|
export declare function handleUpdateStatus(params: UpdateStatusInput, server?: McpServer): Promise<ToolResult>;
|
|
29
|
+
export {};
|
|
6
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -15,20 +15,21 @@ import { checkApprovalGate } from '../../engine/approval-workflow.js';
|
|
|
15
15
|
import * as approvalStore from '../../storage/approval-store.js';
|
|
16
16
|
import { withApprovalSpecLock } from '../../storage/approval-operation-lock.js';
|
|
17
17
|
import { isLocked, getLock } from '../../storage/spec-lock-store.js';
|
|
18
|
-
import { checkDoneGates, checkComplianceGate, checkApprovedFormatGate, checkSpecReviewGate, writeSpecReviewArtifact, } from './dod-gates.js';
|
|
18
|
+
import { checkDoneGates, checkComplianceGate, checkApprovedFormatGate, checkSpecReviewGate, writeSpecReviewArtifact, deriveDoneReviewDigest, } from './dod-gates.js';
|
|
19
19
|
import { checkLifecycleEvidenceTransitionGate } from './evidence-gate.js';
|
|
20
|
+
import { autofillTraceabilityMatrix } from '../../engine/evidence-gates/evidence-autofill.js';
|
|
20
21
|
import { buildStatusResponse, buildDryRunResponse } from './response-builder.js';
|
|
21
22
|
import { recordDoneMetrics, syncSpecFiles, tryReconcile, recordTerminalTransitionEvent, } from './file-sync.js';
|
|
22
23
|
import { appendEntry, getLastHash } from '../../storage/audit-trail-store.js';
|
|
23
24
|
import { appendTransitionEvent } from '../../storage/transition-log.js';
|
|
24
25
|
import { createHash, randomUUID as uuid } from 'node:crypto';
|
|
25
|
-
import { updateFrontmatterField } from '../../engine/frontmatter-parser.js';
|
|
26
26
|
import { atomicWriteFile } from '../../engine/safety/atomic-write-file.js';
|
|
27
27
|
import { recordForceUsage } from '../../storage/force-analytics-store.js';
|
|
28
28
|
import { maybeSafePushOnDone, queuePostCommitTasks, runCascadeForResponse, } from './side-effects.js';
|
|
29
29
|
import { formatKeyValue } from '../output-formatter.js';
|
|
30
30
|
import { checkCodeReality } from '../../engine/code-scanner/index.js';
|
|
31
|
-
import { join } from 'node:path';
|
|
31
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
32
|
+
import { readSpecContent } from '../../engine/spec-reader.js';
|
|
32
33
|
import { trackCost } from '../../engine/cost-tracking/operation-tracker.js';
|
|
33
34
|
import { resolveProjectIdOrAutoDetect } from '../resolve-project-id.js';
|
|
34
35
|
import { withToolTimeout } from '../safe-handler.js';
|
|
@@ -39,7 +40,79 @@ import { acquireLock, releaseLock, isLocked as isCrossProcessLocked, LockBusyErr
|
|
|
39
40
|
import { projectDataDir } from '../../storage/base-store.js';
|
|
40
41
|
import { assertExecutionCanCommit } from '../../engine/execution/context.js';
|
|
41
42
|
import { shouldSkipGateForExplicitLegacyHarness } from './gate-harness.js';
|
|
42
|
-
import { assertValidationFreshnessBarrier, } from '../../engine/validation/validation-freshness.js';
|
|
43
|
+
import { assertValidationFreshnessBarrier, closeValidationFreshnessLease, } from '../../engine/validation/validation-freshness.js';
|
|
44
|
+
import { currentReconciliationInvocationContext, reconcileImplementingSpec, } from '../../engine/lifecycle-reconciliation.js';
|
|
45
|
+
const IMPLEMENTATION_REVIEW_DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
46
|
+
function lifecycleAuthorityError(code, message, fixHint) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{ type: 'text', text: `${code}: ${message}\nFix: ${fixHint}` }],
|
|
49
|
+
isError: true,
|
|
50
|
+
structuredContent: { error: code, code: 422, fixHint },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Select the lifecycle path before any transition, artifact, receipt, or log mutation. */
|
|
54
|
+
export function classifyLifecycleAuthority(input, context, options = {}) {
|
|
55
|
+
const hasRequestId = input.reconciliationRequestId !== undefined;
|
|
56
|
+
const hasTransitionId = input.expectedImplementingTransitionId !== undefined;
|
|
57
|
+
const hasDigest = input.implementationReviewDigest !== undefined;
|
|
58
|
+
const hasAnyAuthority = hasRequestId || hasTransitionId || hasDigest;
|
|
59
|
+
if (input.status === 'done') {
|
|
60
|
+
if (hasRequestId || hasTransitionId) {
|
|
61
|
+
return {
|
|
62
|
+
route: 'error',
|
|
63
|
+
result: lifecycleAuthorityError('RECONCILIATION_TARGET_INVALID', 'Reconciliation authority is valid only for the review target.', 'Remove reconciliationRequestId and expectedImplementingTransitionId from the done request.'),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// SPEC-1356: implementationReviewDigest is optional. When the caller omits it,
|
|
67
|
+
// the done gate (checkDoneGates via dod-gates.deriveDoneReviewDigest) derives
|
|
68
|
+
// it automatically from the persisted validation-report.json — the caller no
|
|
69
|
+
// longer has to compute the sha256 by hand. `options.requireDoneDigest` is
|
|
70
|
+
// retained only so legacy/explicit-authority callers can still demand it.
|
|
71
|
+
if (options.requireDoneDigest === true && !hasDigest) {
|
|
72
|
+
return {
|
|
73
|
+
route: 'error',
|
|
74
|
+
result: lifecycleAuthorityError('DONE_REVIEW_DIGEST_REQUIRED', 'A done transition requires the implementation review digest.', 'Supply the sha256 digest of the exact persisted validation-report.json bytes.'),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (hasDigest &&
|
|
78
|
+
(typeof input.implementationReviewDigest !== 'string' ||
|
|
79
|
+
!IMPLEMENTATION_REVIEW_DIGEST.test(input.implementationReviewDigest))) {
|
|
80
|
+
return {
|
|
81
|
+
route: 'error',
|
|
82
|
+
result: lifecycleAuthorityError('DONE_REVIEW_DIGEST_INVALID', 'The implementation review digest must be sha256 followed by 64 lowercase hex characters.', 'Recompute the digest from the exact persisted validation-report.json bytes.'),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return { route: 'ordinary' };
|
|
86
|
+
}
|
|
87
|
+
if (input.status === 'review' && hasAnyAuthority) {
|
|
88
|
+
if (!hasRequestId || !hasTransitionId || !hasDigest) {
|
|
89
|
+
return {
|
|
90
|
+
route: 'error',
|
|
91
|
+
result: lifecycleAuthorityError('RECONCILIATION_TUPLE_INCOMPLETE', 'Review reconciliation requires the complete authority tuple.', 'Supply reconciliationRequestId, expectedImplementingTransitionId, implementationReviewDigest, and a reason of at least 100 characters.'),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (!input.reason || input.reason.trim().length < 100) {
|
|
95
|
+
return {
|
|
96
|
+
route: 'error',
|
|
97
|
+
result: lifecycleAuthorityError('RECONCILIATION_REASON_INVALID', 'Review reconciliation requires a non-blank reason of at least 100 characters.', 'Explain the reviewed implementation drift and required follow-up in at least 100 characters.'),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (context.surface !== 'local-mcp') {
|
|
101
|
+
return {
|
|
102
|
+
route: 'error',
|
|
103
|
+
result: lifecycleAuthorityError('RECONCILIATION_UNTRUSTED_SURFACE', 'Reconciliation is available only through trusted local MCP.', 'Retry the complete reconciliation request through the registered local MCP tool.'),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return { route: 'reconcile' };
|
|
107
|
+
}
|
|
108
|
+
if (hasAnyAuthority) {
|
|
109
|
+
return {
|
|
110
|
+
route: 'error',
|
|
111
|
+
result: lifecycleAuthorityError('RECONCILIATION_TARGET_INVALID', 'Lifecycle authority fields are invalid for this target.', 'Remove the authority fields or use implementationReviewDigest only with done.'),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return { route: 'ordinary' };
|
|
115
|
+
}
|
|
43
116
|
/**
|
|
44
117
|
* SPEC-280: Silently traverse intermediate states so callers can jump forward
|
|
45
118
|
* (e.g. draft → approved) without receiving an error.
|
|
@@ -52,6 +125,58 @@ function buildAutoAdvancePlan(currentStatus, targetStatus) {
|
|
|
52
125
|
stepsExecuted,
|
|
53
126
|
};
|
|
54
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* SPEC-1356 (T-A3): auto-generate traceability-matrix.json when it is missing,
|
|
130
|
+
* BEFORE the evidence gate reads it for a 'done' transition. Never overwrites an
|
|
131
|
+
* existing artifact. Tolerates every internal failure (falls through to the
|
|
132
|
+
* ordinary evidence gate, which will report the real missing-evidence error).
|
|
133
|
+
*
|
|
134
|
+
* When autofill actually writes a fresh file, the newly-created evidence has not
|
|
135
|
+
* been bound by validate's durable receipt yet, so this blocks once with a clear
|
|
136
|
+
* "re-run validate" message instead of letting a stale-relative-to-receipt
|
|
137
|
+
* traceability matrix slide through the done gates silently.
|
|
138
|
+
*/
|
|
139
|
+
async function runTraceabilityAutofillGate(args) {
|
|
140
|
+
try {
|
|
141
|
+
const specPath = isAbsolute(args.spec.specPath) || !args.projectPath
|
|
142
|
+
? args.spec.specPath
|
|
143
|
+
: join(args.projectPath, args.spec.specPath);
|
|
144
|
+
const specBody = await readSpecContent(specPath);
|
|
145
|
+
if (specBody === null) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const result = await autofillTraceabilityMatrix({
|
|
149
|
+
projectId: args.projectId,
|
|
150
|
+
specId: args.specId,
|
|
151
|
+
projectPath: args.projectPath,
|
|
152
|
+
specBody,
|
|
153
|
+
});
|
|
154
|
+
if (!result.written) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: 'text',
|
|
161
|
+
text: `traceability-matrix.json was auto-generated at ${result.path} (${String(result.rowCount)} rows). ` +
|
|
162
|
+
'Run validate to bind this evidence, then retry update_status(done).',
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
isError: true,
|
|
166
|
+
structuredContent: {
|
|
167
|
+
error: 'traceability_matrix_autofilled',
|
|
168
|
+
code: 422,
|
|
169
|
+
context: { specId: args.specId, path: result.path, rowCount: result.rowCount },
|
|
170
|
+
fixHint: 'Run validate so the new traceability-matrix.json is bound, then retry done.',
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
// reliability-optional: EVIDENCE_AUTOFILL_GATE — falls through to the ordinary evidence gate
|
|
176
|
+
reportClassifiedDegradation('EVIDENCE_AUTOFILL_GATE', error);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
55
180
|
/**
|
|
56
181
|
* SPEC-301: Check if spec is locked by another agent.
|
|
57
182
|
* Returns an error ToolResult if locked, or null to continue.
|
|
@@ -101,6 +226,27 @@ async function checkApprovalPolicyGate(projectId, specId, newStatus) {
|
|
|
101
226
|
}
|
|
102
227
|
return null;
|
|
103
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* SPEC-1356 (T-A2): a `done` transition no longer requires the caller to supply
|
|
231
|
+
* actuals. When the whole object is omitted, synthesize a minimal record instead
|
|
232
|
+
* of blocking the transition with `actuals_required`. Every caller-provided
|
|
233
|
+
* field — including a caller-provided partial object once the MCP input schema
|
|
234
|
+
* accepts one — is preserved verbatim; only genuinely unmeasured numeric fields
|
|
235
|
+
* default to 0, and `completedAt`/`notes` only default when absent.
|
|
236
|
+
*/
|
|
237
|
+
export function synthesizeActuals(actuals, transitionedAt) {
|
|
238
|
+
return {
|
|
239
|
+
devHours: actuals?.devHours ?? 0,
|
|
240
|
+
reviewHours: actuals?.reviewHours ?? 0,
|
|
241
|
+
tokensOpus: actuals?.tokensOpus ?? 0,
|
|
242
|
+
tokensSonnet: actuals?.tokensSonnet ?? 0,
|
|
243
|
+
apiCostUsd: actuals?.apiCostUsd ?? 0,
|
|
244
|
+
humanCostUsd: actuals?.humanCostUsd ?? 0,
|
|
245
|
+
totalCostUsd: actuals?.totalCostUsd ?? 0,
|
|
246
|
+
completedAt: actuals?.completedAt ?? transitionedAt,
|
|
247
|
+
notes: actuals?.notes ?? 'actuals unavailable — auto-filled by done gate',
|
|
248
|
+
};
|
|
249
|
+
}
|
|
104
250
|
/** Compute done metrics when transitioning to 'done' with actuals provided. */
|
|
105
251
|
function computeDoneMetrics(newStatus, spec, actuals) {
|
|
106
252
|
if (newStatus !== 'done' || actuals === undefined || spec === null) {
|
|
@@ -199,6 +345,18 @@ function planBackgroundActions(args) {
|
|
|
199
345
|
}
|
|
200
346
|
return actions;
|
|
201
347
|
}
|
|
348
|
+
/** Bind deferred lifecycle work to one normalized absolute root without persisting that path. */
|
|
349
|
+
export function computeTransitionRootBinding(projectPath) {
|
|
350
|
+
return createHash('sha256')
|
|
351
|
+
.update('planu:transition-root:v1\0')
|
|
352
|
+
.update(resolve(projectPath))
|
|
353
|
+
.digest('hex');
|
|
354
|
+
}
|
|
355
|
+
function receiptMatchesTransitionRoot(receipt, projectPath) {
|
|
356
|
+
return (projectPath !== undefined &&
|
|
357
|
+
receipt.transitionRootBinding !== undefined &&
|
|
358
|
+
receipt.transitionRootBinding === computeTransitionRootBinding(projectPath));
|
|
359
|
+
}
|
|
202
360
|
function buildPostCommitTasks(args) {
|
|
203
361
|
const enabled = new Set(args.actionNames);
|
|
204
362
|
const tasks = [];
|
|
@@ -212,13 +370,13 @@ function buildPostCommitTasks(args) {
|
|
|
212
370
|
if (enabled.has('implementing-side-effects')) {
|
|
213
371
|
tasks.push({
|
|
214
372
|
name: 'implementing-side-effects',
|
|
215
|
-
run: () => runImplementingSideEffects(args.projectId, args.spec.id),
|
|
373
|
+
run: () => runImplementingSideEffects(args.projectId, args.spec.id, args.effectiveProjectPath),
|
|
216
374
|
});
|
|
217
375
|
}
|
|
218
376
|
if (enabled.has('done-side-effects')) {
|
|
219
377
|
tasks.push({
|
|
220
378
|
name: 'done-side-effects',
|
|
221
|
-
run: () => runDoneSideEffects(args.projectId, args.spec.id, args.spec.gitBranch),
|
|
379
|
+
run: () => runDoneSideEffects(args.projectId, args.spec.id, args.spec.gitBranch, args.effectiveProjectPath),
|
|
222
380
|
});
|
|
223
381
|
}
|
|
224
382
|
if (enabled.has('reconcile')) {
|
|
@@ -231,7 +389,7 @@ function buildPostCommitTasks(args) {
|
|
|
231
389
|
if (enabled.has('record-done-metrics') && actuals) {
|
|
232
390
|
tasks.push({
|
|
233
391
|
name: 'record-done-metrics',
|
|
234
|
-
run: () => recordDoneMetrics(args.projectId, args.spec.id, args.spec, actuals, args.
|
|
392
|
+
run: () => recordDoneMetrics(args.projectId, args.spec.id, args.spec, actuals, args.effectiveProjectPath),
|
|
235
393
|
});
|
|
236
394
|
}
|
|
237
395
|
const effectiveProjectPath = args.effectiveProjectPath;
|
|
@@ -253,6 +411,7 @@ function buildPostCommitTasks(args) {
|
|
|
253
411
|
projectId: args.projectId,
|
|
254
412
|
spec: args.spec,
|
|
255
413
|
currentStatus: args.previousStatus,
|
|
414
|
+
projectPath: args.effectiveProjectPath,
|
|
256
415
|
}),
|
|
257
416
|
});
|
|
258
417
|
}
|
|
@@ -291,7 +450,10 @@ async function replayIdempotentPostCommitTasks(args) {
|
|
|
291
450
|
return;
|
|
292
451
|
}
|
|
293
452
|
const knowledge = await knowledgeStore.getKnowledge(args.projectId);
|
|
294
|
-
const effectiveProjectPath =
|
|
453
|
+
const effectiveProjectPath = args.projectPath ?? knowledge?.projectPath;
|
|
454
|
+
if (!receiptMatchesTransitionRoot(receipt, effectiveProjectPath)) {
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
295
457
|
const specAtTransition = { ...args.spec, status: receipt.status };
|
|
296
458
|
const tasks = buildPostCommitTasks({
|
|
297
459
|
actionNames: receipt.pendingBackgroundActions,
|
|
@@ -317,13 +479,17 @@ async function recoverPendingPostCommitTasks(projectId, projectPath) {
|
|
|
317
479
|
knowledgeStore.getKnowledge(projectId),
|
|
318
480
|
specStore.listSpecs(projectId),
|
|
319
481
|
]);
|
|
320
|
-
const effectiveProjectPath =
|
|
482
|
+
const effectiveProjectPath = projectPath ?? knowledge?.projectPath;
|
|
321
483
|
for (const spec of specs) {
|
|
322
|
-
const
|
|
323
|
-
|
|
484
|
+
const recoverableReceipts = (spec.statusHistory ?? []).filter((receipt) => receipt.transitionId &&
|
|
485
|
+
receipt.pendingBackgroundActions?.length &&
|
|
486
|
+
receiptMatchesTransitionRoot(receipt, effectiveProjectPath));
|
|
487
|
+
if (recoverableReceipts.length === 0 || !effectiveProjectPath) {
|
|
324
488
|
continue;
|
|
325
489
|
}
|
|
326
|
-
const recoveryLock = await acquireLock(effectiveProjectPath
|
|
490
|
+
const recoveryLock = await acquireLock(effectiveProjectPath, spec.id, {
|
|
491
|
+
reason: 'recover pending post-commit tasks',
|
|
492
|
+
});
|
|
327
493
|
if (recoveryLock === null) {
|
|
328
494
|
continue;
|
|
329
495
|
}
|
|
@@ -335,7 +501,9 @@ async function recoverPendingPostCommitTasks(projectId, projectPath) {
|
|
|
335
501
|
continue;
|
|
336
502
|
}
|
|
337
503
|
for (const receipt of currentSpec.statusHistory ?? []) {
|
|
338
|
-
if (!receipt.transitionId ||
|
|
504
|
+
if (!receipt.transitionId ||
|
|
505
|
+
!receipt.pendingBackgroundActions?.length ||
|
|
506
|
+
!receiptMatchesTransitionRoot(receipt, effectiveProjectPath)) {
|
|
339
507
|
continue;
|
|
340
508
|
}
|
|
341
509
|
const specAtTransition = { ...currentSpec, status: receipt.status };
|
|
@@ -383,6 +551,18 @@ export async function recoverPendingPostCommitTasksAtStartup() {
|
|
|
383
551
|
});
|
|
384
552
|
}
|
|
385
553
|
}
|
|
554
|
+
async function runTransitionMaintenance(transitionProjectPath) {
|
|
555
|
+
if (transitionProjectPath) {
|
|
556
|
+
try {
|
|
557
|
+
const { runStrictPlanuCleanup } = await import('../../engine/spec-migrator/index.js');
|
|
558
|
+
await runStrictPlanuCleanup(transitionProjectPath);
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
/* reliability-cleanup: PRE_TRANSITION_STRICT_CLEANUP — validate remains fail-closed */
|
|
562
|
+
reportClassifiedDegradation('PRE_TRANSITION_STRICT_CLEANUP', error);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
386
566
|
function buildPersistenceFailureResponse(args) {
|
|
387
567
|
const committed = args.recoveryRequired ? null : false;
|
|
388
568
|
const message = args.recoveryRequired
|
|
@@ -469,7 +649,7 @@ async function commitTransitionWithNarrowLock(args) {
|
|
|
469
649
|
expectedSpec: freshSpec,
|
|
470
650
|
});
|
|
471
651
|
const updatedSpec = transitionRecord.spec;
|
|
472
|
-
const syncResult = await syncSpecFiles(updatedSpec, args.originalStatus, args.newStatus, args.projectPath);
|
|
652
|
+
const syncResult = await syncSpecFiles(updatedSpec, args.originalStatus, args.newStatus, args.projectPath, args.qualityWarnings);
|
|
473
653
|
if (!syncResult.warning) {
|
|
474
654
|
return { ok: true, transitionRecord, updatedSpec };
|
|
475
655
|
}
|
|
@@ -571,6 +751,13 @@ async function resolveOrchestrationPlan(newStatus, specScope, specId, projectPat
|
|
|
571
751
|
export async function handleUpdateStatus(params, server) {
|
|
572
752
|
return trackCost(params.projectPath?.trim() ?? '', 'update_status', async () => {
|
|
573
753
|
const { specId, status: newStatus, actuals, reviewNotes } = params;
|
|
754
|
+
// SPEC-1356: implementationReviewDigest is derived automatically by the done
|
|
755
|
+
// gate when omitted (see dod-gates.deriveDoneReviewDigest), so it is never
|
|
756
|
+
// required here.
|
|
757
|
+
const authorityRoute = classifyLifecycleAuthority(params, currentReconciliationInvocationContext());
|
|
758
|
+
if (authorityRoute.route === 'error') {
|
|
759
|
+
return authorityRoute.result;
|
|
760
|
+
}
|
|
574
761
|
// SPEC-509: auto-detect project from git root when projectPath/projectId are omitted
|
|
575
762
|
const resolved = await resolveProjectIdOrAutoDetect({
|
|
576
763
|
projectId: params.projectId,
|
|
@@ -581,24 +768,27 @@ export async function handleUpdateStatus(params, server) {
|
|
|
581
768
|
}
|
|
582
769
|
const projectId = resolved.projectId;
|
|
583
770
|
const effectiveProjectPath = resolved.projectPath;
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
771
|
+
const knowledge = await knowledgeStore.getKnowledge(projectId);
|
|
772
|
+
const transitionProjectPath = (params.projectPath?.trim()
|
|
773
|
+
? effectiveProjectPath
|
|
774
|
+
: (knowledge?.projectPath ?? effectiveProjectPath)) || undefined;
|
|
775
|
+
if (authorityRoute.route === 'reconcile') {
|
|
776
|
+
return reconcileImplementingSpec({
|
|
777
|
+
input: params,
|
|
778
|
+
projectId,
|
|
779
|
+
projectPath: transitionProjectPath,
|
|
780
|
+
context: currentReconciliationInvocationContext(),
|
|
781
|
+
syncSpecFiles,
|
|
782
|
+
});
|
|
593
783
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
784
|
+
if (newStatus !== 'done') {
|
|
785
|
+
await runTransitionMaintenance(transitionProjectPath);
|
|
786
|
+
}
|
|
787
|
+
let doneFreshnessLease;
|
|
598
788
|
return (async () => {
|
|
599
789
|
try {
|
|
600
790
|
// SPEC-301: Reject status changes when spec is locked by another holder (legacy manual lock)
|
|
601
|
-
const lockError = await checkSpecLockGate(
|
|
791
|
+
const lockError = await checkSpecLockGate(transitionProjectPath, specId);
|
|
602
792
|
if (lockError) {
|
|
603
793
|
return lockError;
|
|
604
794
|
}
|
|
@@ -608,7 +798,7 @@ export async function handleUpdateStatus(params, server) {
|
|
|
608
798
|
if (isDryRun) {
|
|
609
799
|
// SPEC-731: dry_run checks lock availability without acquiring it.
|
|
610
800
|
// isLocked returns null when no lockfile exists (available), or a LockInfo when locked.
|
|
611
|
-
const lockInfo = await isCrossProcessLocked(
|
|
801
|
+
const lockInfo = await isCrossProcessLocked(transitionProjectPath ?? projectId, specId).catch((error) => {
|
|
612
802
|
// reliability-optional: DRY_RUN_LOCK_PROBE — null conservatively avoids stale lock claims
|
|
613
803
|
reportClassifiedDegradation('DRY_RUN_LOCK_PROBE', error);
|
|
614
804
|
return null;
|
|
@@ -647,29 +837,16 @@ export async function handleUpdateStatus(params, server) {
|
|
|
647
837
|
await replayIdempotentPostCommitTasks({
|
|
648
838
|
projectId,
|
|
649
839
|
spec,
|
|
650
|
-
projectPath:
|
|
840
|
+
projectPath: transitionProjectPath,
|
|
651
841
|
});
|
|
652
842
|
const idempotentResult = checkIdempotentOrTransition(projectId, spec, newStatus);
|
|
653
843
|
if (idempotentResult) {
|
|
654
844
|
return idempotentResult;
|
|
655
845
|
}
|
|
656
846
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
{
|
|
661
|
-
type: 'text',
|
|
662
|
-
text: 'actuals_required: provide measured values for done. Use zero only when a metric is explicitly unavailable; Planu does not estimate provider tokens or costs.',
|
|
663
|
-
},
|
|
664
|
-
],
|
|
665
|
-
isError: true,
|
|
666
|
-
structuredContent: {
|
|
667
|
-
error: 'actuals_required',
|
|
668
|
-
code: 422,
|
|
669
|
-
fixHint: 'Provide actuals with measured values or zero for unavailable metrics. Values are preserved without estimation.',
|
|
670
|
-
},
|
|
671
|
-
};
|
|
672
|
-
}
|
|
847
|
+
// SPEC-1356 (T-A2): actuals are no longer required to reach done. When
|
|
848
|
+
// omitted, synthesizeActuals() below fills in a defaulted record instead
|
|
849
|
+
// of blocking the transition — see synthesizeActuals doc comment.
|
|
673
850
|
// SPEC-280/SPEC-1122: plan every intermediate state in memory. No status is
|
|
674
851
|
// persisted until all gates for the complete path have passed.
|
|
675
852
|
const { plannedStatuses, stepsExecuted } = buildAutoAdvancePlan(spec.status, newStatus);
|
|
@@ -784,10 +961,8 @@ export async function handleUpdateStatus(params, server) {
|
|
|
784
961
|
if (elicitResult) {
|
|
785
962
|
return elicitResult;
|
|
786
963
|
}
|
|
787
|
-
// Load knowledge for project path (needed for DoD gates, validate trigger and HTML regen)
|
|
788
|
-
const knowledge = await knowledgeStore.getKnowledge(projectId);
|
|
789
964
|
// Resolve effective project path once — used across all gates below
|
|
790
|
-
const effectiveGatePath =
|
|
965
|
+
const effectiveGatePath = transitionProjectPath;
|
|
791
966
|
// SPEC-1044: SDD model-routing + context continuity hard gate.
|
|
792
967
|
const sddRoutingGate = {
|
|
793
968
|
blockResult: null,
|
|
@@ -812,6 +987,26 @@ export async function handleUpdateStatus(params, server) {
|
|
|
812
987
|
sddRoutingGate.gateResults.sddModelRouting = 'forced';
|
|
813
988
|
}
|
|
814
989
|
}
|
|
990
|
+
// SPEC-1356 (T-A3): auto-generate traceability-matrix.json before the
|
|
991
|
+
// evidence gate below reads it, so a spec never fails 'done' purely
|
|
992
|
+
// because nobody hand-wrote the traceability artifact. Never overwrites
|
|
993
|
+
// an existing file. Freshness caveat: when autofill actually writes a
|
|
994
|
+
// new file, its evidence has not yet been bound by validate's durable
|
|
995
|
+
// receipt, so we block once and ask the caller to re-run validate
|
|
996
|
+
// rather than risk certifying stale bindings. See evidence-autofill.ts
|
|
997
|
+
// for the full rationale.
|
|
998
|
+
if (plannedStatuses.includes('done') &&
|
|
999
|
+
!shouldSkipGateForExplicitLegacyHarness('evidence')) {
|
|
1000
|
+
const autofillGate = await runTraceabilityAutofillGate({
|
|
1001
|
+
spec,
|
|
1002
|
+
specId,
|
|
1003
|
+
projectId,
|
|
1004
|
+
projectPath: effectiveGatePath,
|
|
1005
|
+
});
|
|
1006
|
+
if (autofillGate) {
|
|
1007
|
+
return autofillGate;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
815
1010
|
// SPEC-1054: BDD/SDD evidence gates. Non-trivial specs must carry
|
|
816
1011
|
// Discovery before approval, task-plan before implementation, and
|
|
817
1012
|
// traceability/contract evidence before done.
|
|
@@ -830,6 +1025,21 @@ export async function handleUpdateStatus(params, server) {
|
|
|
830
1025
|
}
|
|
831
1026
|
}
|
|
832
1027
|
}
|
|
1028
|
+
// SPEC-1356 (T-A1): implementationReviewDigest is optional. When the
|
|
1029
|
+
// caller omits it, derive it automatically from the persisted
|
|
1030
|
+
// validation-report.json instead of requiring the caller to hand-compute
|
|
1031
|
+
// a sha256. Failure to derive it fails closed with a clear "run validate
|
|
1032
|
+
// first" message — identical strength to the explicit-digest path.
|
|
1033
|
+
let resolvedImplementationReviewDigest = params.implementationReviewDigest;
|
|
1034
|
+
if (newStatus === 'done' &&
|
|
1035
|
+
resolvedImplementationReviewDigest === undefined &&
|
|
1036
|
+
!shouldSkipGateForExplicitLegacyHarness('evidence')) {
|
|
1037
|
+
const derived = await deriveDoneReviewDigest(specId, projectId);
|
|
1038
|
+
if ('error' in derived) {
|
|
1039
|
+
return derived.error;
|
|
1040
|
+
}
|
|
1041
|
+
resolvedImplementationReviewDigest = derived.digest;
|
|
1042
|
+
}
|
|
833
1043
|
// ---------------------------------------------------------------------------
|
|
834
1044
|
// BATCH A (parallel): code-reality + done-gates — independent of each other
|
|
835
1045
|
// SPEC-441: Code reality check before transitioning to 'implementing'
|
|
@@ -839,7 +1049,6 @@ export async function handleUpdateStatus(params, server) {
|
|
|
839
1049
|
// security and repository scans belong to the durable validation worker.
|
|
840
1050
|
let codeRealityWarning = null;
|
|
841
1051
|
let forcedBypassWarning = null;
|
|
842
|
-
let doneFreshnessLease;
|
|
843
1052
|
const [codeRealityResult, doneGatesResult] = await Promise.all([
|
|
844
1053
|
// Code reality: only relevant for 'implementing'
|
|
845
1054
|
newStatus === 'implementing' && !reverseTransition && effectiveGatePath
|
|
@@ -851,7 +1060,7 @@ export async function handleUpdateStatus(params, server) {
|
|
|
851
1060
|
: Promise.resolve(null),
|
|
852
1061
|
// Done gates: only relevant for 'done'
|
|
853
1062
|
newStatus === 'done' && !shouldSkipGateForExplicitLegacyHarness('evidence')
|
|
854
|
-
? checkDoneGates(spec, specId, projectId, effectiveGatePath, params.force, params.forceStatusReason ?? params.reason ?? 'No force reason provided')
|
|
1063
|
+
? checkDoneGates(spec, specId, projectId, effectiveGatePath, params.force, params.forceStatusReason ?? params.reason ?? 'No force reason provided', undefined, resolvedImplementationReviewDigest)
|
|
855
1064
|
: Promise.resolve(null),
|
|
856
1065
|
]);
|
|
857
1066
|
// Process code reality result
|
|
@@ -933,15 +1142,23 @@ export async function handleUpdateStatus(params, server) {
|
|
|
933
1142
|
return specReviewWriteError;
|
|
934
1143
|
}
|
|
935
1144
|
}
|
|
936
|
-
// Explicit actuals are preserved verbatim. Provider usage and cost are
|
|
937
|
-
|
|
1145
|
+
// Explicit actuals are preserved verbatim. Provider usage and cost are
|
|
1146
|
+
// never inferred — synthesizeActuals only fills gaps the caller left
|
|
1147
|
+
// unmeasured (SPEC-1356 T-A2).
|
|
1148
|
+
const resolvedActuals = newStatus === 'done' || actuals !== undefined
|
|
1149
|
+
? synthesizeActuals(actuals, new Date().toISOString())
|
|
1150
|
+
: undefined;
|
|
938
1151
|
// Run only fast, read-only transition checks before persistence.
|
|
939
1152
|
const implActions = newStatus === 'implementing'
|
|
940
|
-
? await runImplementingActions(projectId, specId, {
|
|
1153
|
+
? await runImplementingActions(projectId, specId, {
|
|
1154
|
+
deferSideEffects: true,
|
|
1155
|
+
projectPath: effectiveGatePath,
|
|
1156
|
+
})
|
|
941
1157
|
: null;
|
|
942
1158
|
const doneActions = newStatus === 'done'
|
|
943
1159
|
? await runDoneActions(projectId, specId, spec.gitBranch, {
|
|
944
1160
|
deferSideEffects: true,
|
|
1161
|
+
projectPath: effectiveGatePath,
|
|
945
1162
|
})
|
|
946
1163
|
: null;
|
|
947
1164
|
const trigger = reverseTransition
|
|
@@ -1089,13 +1306,20 @@ export async function handleUpdateStatus(params, server) {
|
|
|
1089
1306
|
hasProjectPath: Boolean(effectiveGatePath),
|
|
1090
1307
|
shouldAutoStage,
|
|
1091
1308
|
});
|
|
1309
|
+
const transitionRootBinding = plannedBackgroundActions.length > 0 && effectiveGatePath
|
|
1310
|
+
? computeTransitionRootBinding(effectiveGatePath)
|
|
1311
|
+
: undefined;
|
|
1312
|
+
const qualityWarningsToWrite = newStatus === 'approved'
|
|
1313
|
+
? [...readinessGate.qualityWarnings, ...formatGate.qualityWarnings]
|
|
1314
|
+
: [];
|
|
1092
1315
|
const persistence = await commitTransitionWithNarrowLock({
|
|
1093
|
-
projectPath:
|
|
1316
|
+
projectPath: effectiveGatePath ?? projectId,
|
|
1094
1317
|
projectId,
|
|
1095
1318
|
specId,
|
|
1096
1319
|
newStatus,
|
|
1097
1320
|
originalStatus,
|
|
1098
1321
|
expectedSpec: spec,
|
|
1322
|
+
qualityWarnings: qualityWarningsToWrite,
|
|
1099
1323
|
options: {
|
|
1100
1324
|
trigger: trigger,
|
|
1101
1325
|
actor,
|
|
@@ -1103,6 +1327,7 @@ export async function handleUpdateStatus(params, server) {
|
|
|
1103
1327
|
reason: params.reason,
|
|
1104
1328
|
updates: nonStatusUpdates,
|
|
1105
1329
|
pendingBackgroundActions: plannedBackgroundActions,
|
|
1330
|
+
transitionRootBinding,
|
|
1106
1331
|
expectedSpec: spec,
|
|
1107
1332
|
},
|
|
1108
1333
|
...(doneFreshnessLease ? { freshnessLease: doneFreshnessLease } : {}),
|
|
@@ -1114,6 +1339,9 @@ export async function handleUpdateStatus(params, server) {
|
|
|
1114
1339
|
const frontmatterSyncWarnings = [];
|
|
1115
1340
|
const transitionId = transitionRecord.transitionId;
|
|
1116
1341
|
const committedAt = transitionRecord.timestamp;
|
|
1342
|
+
if (newStatus === 'done') {
|
|
1343
|
+
await runTransitionMaintenance(transitionProjectPath);
|
|
1344
|
+
}
|
|
1117
1345
|
await appendTransitionEvent({
|
|
1118
1346
|
projectId,
|
|
1119
1347
|
specId,
|
|
@@ -1145,29 +1373,6 @@ export async function handleUpdateStatus(params, server) {
|
|
|
1145
1373
|
reportClassifiedDegradation('REOPEN_EVENT_APPEND', err);
|
|
1146
1374
|
});
|
|
1147
1375
|
}
|
|
1148
|
-
// SPEC-769/SPEC-780: Write qualityWarnings to spec.md frontmatter when force-approved (best-effort)
|
|
1149
|
-
const qualityWarningsToWrite = [
|
|
1150
|
-
...readinessGate.qualityWarnings,
|
|
1151
|
-
...formatGate.qualityWarnings,
|
|
1152
|
-
];
|
|
1153
|
-
const specPathForWarnings = updatedSpec.specPath;
|
|
1154
|
-
if (newStatus === 'approved' &&
|
|
1155
|
-
qualityWarningsToWrite.length > 0 &&
|
|
1156
|
-
specPathForWarnings) {
|
|
1157
|
-
void (async () => {
|
|
1158
|
-
try {
|
|
1159
|
-
const { readFile: fsReadFile } = await import('node:fs/promises');
|
|
1160
|
-
const content = await fsReadFile(specPathForWarnings, 'utf-8');
|
|
1161
|
-
const warningsJson = JSON.stringify(qualityWarningsToWrite);
|
|
1162
|
-
const updated = updateFrontmatterField(content, 'qualityWarnings', warningsJson);
|
|
1163
|
-
await atomicWriteFile(specPathForWarnings, updated);
|
|
1164
|
-
}
|
|
1165
|
-
catch (error) {
|
|
1166
|
-
// reliability-optional: QUALITY_WARNING_FRONTMATTER — canonical receipt retains warnings
|
|
1167
|
-
reportClassifiedDegradation('QUALITY_WARNING_FRONTMATTER', error);
|
|
1168
|
-
}
|
|
1169
|
-
})();
|
|
1170
|
-
}
|
|
1171
1376
|
// SPEC-723: Record terminal transition in hash-chained log (best-effort, fire-and-forget)
|
|
1172
1377
|
// SPEC-734: Thread enriched payload — sessionId, modelId, gateResults
|
|
1173
1378
|
if (newStatus === 'done' || newStatus === 'discarded') {
|
|
@@ -1197,6 +1402,7 @@ export async function handleUpdateStatus(params, server) {
|
|
|
1197
1402
|
modelId: params.modelId,
|
|
1198
1403
|
gateResults: Object.keys(terminalGateResults).length > 0 ? terminalGateResults : undefined,
|
|
1199
1404
|
meta: transitionEvidenceMeta,
|
|
1405
|
+
projectPath: effectiveGatePath,
|
|
1200
1406
|
}).catch((error) => {
|
|
1201
1407
|
/* reliability-optional: TERMINAL_VALIDATION_REPORT — canonical receipt is authoritative */
|
|
1202
1408
|
reportClassifiedDegradation('TERMINAL_VALIDATION_REPORT', error);
|
|
@@ -1335,6 +1541,14 @@ export async function handleUpdateStatus(params, server) {
|
|
|
1335
1541
|
isError: true,
|
|
1336
1542
|
};
|
|
1337
1543
|
}
|
|
1544
|
+
finally {
|
|
1545
|
+
if (doneFreshnessLease) {
|
|
1546
|
+
await closeValidationFreshnessLease(doneFreshnessLease).catch((error) => {
|
|
1547
|
+
/* reliability-cleanup: freshness expiry remains the recovery boundary */
|
|
1548
|
+
reportClassifiedDegradation('VALIDATION_FRESHNESS_CHECK_FAILED', error);
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1338
1552
|
})();
|
|
1339
1553
|
}); // end trackCost
|
|
1340
1554
|
}
|