@planu/cli 4.13.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -1
- package/LICENSE +55 -12
- package/README.md +33 -25
- package/dist/cli/commands/inspect.d.ts +4 -0
- package/dist/cli/commands/inspect.js +144 -0
- package/dist/cli/commands/install.d.ts +19 -12
- package/dist/cli/commands/install.js +148 -102
- package/dist/cli/commands/job.d.ts +3 -0
- package/dist/cli/commands/job.js +120 -0
- package/dist/cli/commands/offline.d.ts +15 -0
- package/dist/cli/commands/offline.js +132 -0
- package/dist/cli/commands/serve.js +4 -0
- package/dist/cli/commands/spec.js +1 -1
- package/dist/cli/commands/storage.d.ts +3 -0
- package/dist/cli/commands/storage.js +102 -0
- package/dist/cli/commands/uninstall.d.ts +10 -0
- package/dist/cli/commands/uninstall.js +93 -7
- package/dist/cli/commands/validate.js +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/router.js +9 -3
- package/dist/config/crash-shield-framework-rules.json +93 -0
- package/dist/config/environment-schema.json +147 -0
- package/dist/config/hardcode-exceptions.json +4 -0
- package/dist/config/native-release-public-key.pem +3 -0
- package/dist/config/native-targets.json +87 -0
- 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 +225 -0
- package/dist/config/product-public.js +147 -0
- package/dist/config/product-public.json +283 -0
- package/dist/config/registries/hosts/codex.json +20 -363
- package/dist/config/release-compatibility.json +13 -0
- package/dist/config/release-policy.json +108 -0
- package/dist/config/runtime-policy.json +55 -0
- package/dist/config/skill-templates/planu-resume-work.md +2 -2
- package/dist/config/skill-templates/planu-validate.md +7 -4
- package/dist/config/subagent-templates/planu-spec-implementer.md +7 -3
- package/dist/config/subagent-templates/planu-validator.md +10 -5
- package/dist/config/technology-registry.json +120 -0
- package/dist/config/version.js +7 -8
- package/dist/core/index.d.ts +1 -1
- package/dist/core/spec-validator.d.ts +4 -4
- package/dist/core/spec-validator.js +1 -1
- package/dist/engine/abortable-process-runner.d.ts +20 -2
- package/dist/engine/abortable-process-runner.js +267 -67
- package/dist/engine/advanced-testing/accessibility-checker.js +12 -14
- package/dist/engine/advanced-testing/mutation-advisor.js +3 -4
- package/dist/engine/advanced-testing/property-based.js +2 -3
- package/dist/engine/advanced-testing/test-data-advisor.js +2 -3
- package/dist/engine/agent-generator.js +2 -1
- package/dist/engine/agent-handoff-store.js +2 -1
- package/dist/engine/agent-prompt-generator.js +7 -5
- package/dist/engine/agent-telemetry.js +2 -1
- package/dist/engine/ai-integration/gemini/extensions-advisor.js +41 -9
- package/dist/engine/ambiguity-detector.js +12 -11
- package/dist/engine/ambiguity-scorer.js +4 -26
- package/dist/engine/analyzer/db-engine.js +2 -3
- package/dist/engine/analyzer/detectors.js +13 -14
- package/dist/engine/analyzer/env-setup.js +9 -10
- package/dist/engine/analyzer-env-scanner/env-example.js +7 -8
- package/dist/engine/api-spec-generator/graphql-parser.js +2 -2
- package/dist/engine/api-validation/graphql-schema-validator.js +2 -1
- package/dist/engine/api-validation/openapi-impl-validator.js +6 -7
- package/dist/engine/architecture-detector.js +10 -8
- package/dist/engine/auth/config.d.ts +1 -1
- package/dist/engine/auth/config.js +4 -3
- package/dist/engine/auth/scope-mapper.js +1 -7
- package/dist/engine/auto-updater/atomic-upgrade.d.ts +8 -0
- package/dist/engine/auto-updater/atomic-upgrade.js +381 -0
- package/dist/engine/auto-updater/config-detector.js +9 -3
- package/dist/engine/auto-updater/config-patcher.d.ts +0 -4
- package/dist/engine/auto-updater/config-patcher.js +10 -15
- package/dist/engine/auto-updater/index.d.ts +3 -1
- package/dist/engine/auto-updater/index.js +44 -30
- package/dist/engine/auto-updater/npm-registry.js +10 -2
- package/dist/engine/auto-updater/release-verifier.d.ts +3 -0
- package/dist/engine/auto-updater/release-verifier.js +112 -0
- package/dist/engine/auto-updater/upgrade-lock.d.ts +6 -0
- package/dist/engine/auto-updater/upgrade-lock.js +28 -0
- package/dist/engine/auto-updater/upgrade-trust-state.d.ts +7 -0
- package/dist/engine/auto-updater/upgrade-trust-state.js +59 -0
- package/dist/engine/autopilot/audit-logger.js +2 -5
- package/dist/engine/autopilot/state-updater.js +4 -4
- package/dist/engine/best-practices-library.js +2 -1
- package/dist/engine/browser-validator.js +2 -1
- package/dist/engine/build-tool-detector.js +3 -2
- 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/context-builders.js +7 -5
- package/dist/engine/ci-generator/planu-config.js +1 -7
- package/dist/engine/ci-generator/stack-detector.js +0 -2
- package/dist/engine/ci-generator/yaml-builder.js +0 -2
- package/dist/engine/clarification-gate/gate.d.ts +3 -3
- package/dist/engine/claude-config-auditor/rules-auditor.js +4 -3
- package/dist/engine/code-scanner/layer-scanner.js +6 -22
- package/dist/engine/code-transforms/typescript/ast-utils.js +2 -3
- package/dist/engine/complexity-budget/trivial-detector.js +2 -1
- package/dist/engine/compliance-test-generator/index.js +7 -2
- package/dist/engine/compliance-test-generator/test-formatter.js +2 -2
- package/dist/engine/config-health/mcp-config-checker.js +2 -1
- package/dist/engine/config-health/ts-checker-scripts.js +4 -3
- package/dist/engine/config-health/ts-checker.js +3 -2
- package/dist/engine/convention-injector.js +5 -7
- package/dist/engine/convention-scanner/convention-parser.js +3 -2
- package/dist/engine/core-bridge-duplicate-blocks.d.ts +3 -0
- package/dist/engine/core-bridge-duplicate-blocks.js +57 -0
- package/dist/engine/core-bridge-gaps.d.ts +4 -0
- package/dist/engine/core-bridge-gaps.js +75 -0
- package/dist/engine/core-bridge.d.ts +10 -10
- package/dist/engine/core-bridge.js +196 -283
- package/dist/engine/cost-guardrails.d.ts +1 -1
- package/dist/engine/cost-guardrails.js +1 -1
- package/dist/engine/cost-tracking/ledger.js +2 -5
- package/dist/engine/coverage-gap-analyzer.js +10 -1
- package/dist/engine/crash-shield/detectors/framework-detector.d.ts +2 -0
- package/dist/engine/crash-shield/detectors/framework-detector.js +110 -192
- package/dist/engine/crash-shield/detectors/typescript-detector.js +2 -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 +4 -8
- package/dist/engine/dashboard/templates-layout.js +0 -3
- package/dist/engine/dashboard/templates-project.js +1 -13
- package/dist/engine/data-projects-gc/gc-runner.d.ts +7 -0
- package/dist/engine/data-projects-gc/gc-runner.js +113 -3
- package/dist/engine/data-projects-gc/scanner.js +5 -1
- package/dist/engine/decision-store.js +11 -14
- package/dist/engine/deleter/core.js +5 -5
- package/dist/engine/dep-auditor/abandonment-data.js +2 -1
- package/dist/engine/dep-auditor/abandonment-registry.js +3 -1
- package/dist/engine/dep-auditor/cve-fetcher.js +3 -0
- package/dist/engine/dep-auditor/dep-duplicates-detector.js +36 -4
- package/dist/engine/dep-auditor/license-registry.js +2 -0
- package/dist/engine/dependency-evaluator.d.ts +14 -0
- package/dist/engine/dependency-evaluator.js +81 -0
- package/dist/engine/deploy/providers/fly.js +7 -7
- package/dist/engine/deploy/providers/netlify.js +7 -7
- package/dist/engine/deploy/providers/railway.js +10 -12
- package/dist/engine/deploy/providers/vercel.js +7 -9
- package/dist/engine/deprecation-scanner/pattern-loader.js +0 -2
- package/dist/engine/desktop-detector.js +2 -3
- package/dist/engine/detect-duplication.js +2 -1
- package/dist/engine/detectors/advanced-framework-detector/css-state-detector.js +2 -1
- package/dist/engine/detectors/advanced-framework-detector/legacy-detector.js +2 -3
- package/dist/engine/detectors/advanced-framework-detector/modern-detector.js +3 -4
- package/dist/engine/detectors/agent-framework-detector.js +3 -2
- package/dist/engine/detectors/ai-native-detector.js +4 -6
- package/dist/engine/detectors/cache-db-detector.js +4 -3
- package/dist/engine/detectors/cicd-detector.js +4 -3
- package/dist/engine/detectors/deployment-detector.js +11 -12
- package/dist/engine/detectors/frontend-perf-detector.js +14 -9
- package/dist/engine/detectors/library-detector-multilang-a.js +16 -3
- package/dist/engine/detectors/library-detector-node.js +14 -4
- package/dist/engine/detectors/library-detector-python.js +7 -1
- package/dist/engine/detectors/library-detector.js +0 -2
- package/dist/engine/detectors/llm-detector/provider-maps.js +3 -2
- package/dist/engine/detectors/mcp-candidate-detector.js +12 -16
- package/dist/engine/detectors/mfe-detector.js +6 -7
- package/dist/engine/detectors/monitoring-detector.js +4 -3
- package/dist/engine/detectors/otel-detector.js +3 -2
- package/dist/engine/detectors/otel-ecosystem-detectors.d.ts +2 -2
- package/dist/engine/detectors/otel-ecosystem-detectors.js +4 -3
- package/dist/engine/detectors/platform-specialty-detector.js +3 -5
- package/dist/engine/detectors/template-engine-detector.js +3 -4
- package/dist/engine/doc-generator/builders.js +2 -3
- package/dist/engine/doc-generator/mobile-distribution-guide.js +13 -17
- package/dist/engine/doc-generator/portal/dirty-flag-map.d.ts +2 -2
- package/dist/engine/doc-generator/portal/portal-regenerator.js +3 -47
- 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/domain-detector.js +3 -6
- package/dist/engine/dynamic-knowledge/knowledge-cache.js +3 -4
- package/dist/engine/dynamic-knowledge/knowledge-merger.js +17 -2
- package/dist/engine/dynamic-knowledge/web-researcher.js +15 -14
- package/dist/engine/dynamic-migration-detector/manifest-readers-extended.js +0 -2
- package/dist/engine/e2e-test-generator.js +4 -3
- package/dist/engine/ecosystem-absorber/knowledge-cache.js +2 -1
- package/dist/engine/ecosystem-absorber/platform-crawler.js +2 -2
- package/dist/engine/escalator/retry-counter.js +2 -1
- package/dist/engine/evidence-gates/artifact-reader.d.ts +16 -1
- package/dist/engine/evidence-gates/artifact-reader.js +220 -23
- package/dist/engine/evidence-gates/lifecycle-gate.d.ts +2 -1
- package/dist/engine/evidence-gates/lifecycle-gate.js +58 -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.d.ts +19 -0
- package/dist/engine/execution/context.js +130 -0
- package/dist/engine/execution/deadline-io.d.ts +21 -0
- package/dist/engine/execution/deadline-io.js +45 -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-executor.d.ts +16 -0
- package/dist/engine/execution/job-executor.js +140 -0
- package/dist/engine/execution/job-runtime.d.ts +108 -0
- package/dist/engine/execution/job-runtime.js +328 -0
- package/dist/engine/execution/native-relevance-scorer.d.ts +6 -0
- package/dist/engine/execution/native-relevance-scorer.js +64 -0
- package/dist/engine/execution/native-worker-dispatch.d.ts +3 -0
- package/dist/engine/execution/native-worker-dispatch.js +46 -0
- package/dist/engine/execution/native-worker-protocol.d.ts +6 -0
- package/dist/engine/execution/native-worker-protocol.js +33 -0
- package/dist/engine/execution/native-worker-runtime.d.ts +4 -0
- package/dist/engine/execution/native-worker-runtime.js +177 -0
- package/dist/engine/execution/native-worker-thread.d.ts +2 -0
- package/dist/engine/execution/native-worker-thread.js +66 -0
- package/dist/engine/execution/operation-journal.d.ts +66 -0
- package/dist/engine/execution/operation-journal.js +260 -0
- package/dist/engine/execution/outbox-worker.d.ts +31 -0
- package/dist/engine/execution/outbox-worker.js +192 -0
- 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 +35 -0
- package/dist/engine/execution/validate-job-executor.js +805 -0
- package/dist/engine/execution/validate-job-payload.d.ts +5 -0
- package/dist/engine/execution/validate-job-payload.js +144 -0
- package/dist/engine/execution/validate-job-recovery.d.ts +4 -0
- package/dist/engine/execution/validate-job-recovery.js +47 -0
- package/dist/engine/execution/validate-worker-runtime.d.ts +10 -0
- package/dist/engine/execution/validate-worker-runtime.js +188 -0
- package/dist/engine/execution/validate-worker-thread.d.ts +2 -0
- package/dist/engine/execution/validate-worker-thread.js +64 -0
- package/dist/engine/execution-plan/desktop-distribution.js +3 -7
- package/dist/engine/execution-plan/mobile-distribution.js +4 -9
- package/dist/engine/execution-plan/phases.js +4 -5
- package/dist/engine/execution-plan/plan-utils.js +8 -5
- package/dist/engine/facilitate/context-loader.js +3 -9
- package/dist/engine/figma/code-connect-fetcher.js +9 -8
- package/dist/engine/filesystem-watcher/index.d.ts +3 -3
- package/dist/engine/filesystem-watcher/index.js +5 -5
- package/dist/engine/framework-detector.js +14 -15
- package/dist/engine/git/planu-autocommit.js +8 -7
- package/dist/engine/git/repository-probe.d.ts +3 -0
- package/dist/engine/git/repository-probe.js +13 -0
- package/dist/engine/global-rules-manager.js +2 -2
- package/dist/engine/handoff-artifacts/io.js +2 -3
- package/dist/engine/handoff-artifacts/schemas/red-tests-handoff.js +8 -1
- package/dist/engine/handoff-artifacts/schemas.d.ts +6 -1
- package/dist/engine/handoff-artifacts/schemas.js +38 -0
- package/dist/engine/handoff-packager.d.ts +19 -0
- package/dist/engine/handoff-packager.js +415 -13
- package/dist/engine/hook-generator/stack-hook-templates.js +4 -4
- package/dist/engine/hooks/config-loader.js +1 -1
- package/dist/engine/hooks/context-injector.js +4 -7
- package/dist/engine/hooks/effort-configurator.js +1 -1
- package/dist/engine/hooks/file-watcher.js +1 -14
- package/dist/engine/hooks-reconciler.js +6 -4
- 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/infrastructure/component-mapper.js +5 -7
- package/dist/engine/infrastructure/docker-compose-generator.js +8 -10
- package/dist/engine/infrastructure/index.js +6 -3
- package/dist/engine/infrastructure/kubernetes-generator.js +2 -2
- package/dist/engine/infrastructure/railway-generator.js +10 -8
- package/dist/engine/infrastructure/signal-detector.js +6 -8
- package/dist/engine/infrastructure/terraform-generator.js +6 -8
- package/dist/engine/legal-compliance/detector.js +3 -2
- 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/storage-advisor.js +5 -6
- package/dist/engine/local-first/tool-classification.js +2 -0
- package/dist/engine/macos-keychain-native-port.d.ts +10 -0
- package/dist/engine/macos-keychain-native-port.js +38 -0
- package/dist/engine/marketplace-fetcher/cache.js +3 -1
- package/dist/engine/mcp-config/mcp-config-writer.d.ts +2 -2
- package/dist/engine/mcp-config/mcp-config-writer.js +10 -8
- package/dist/engine/migration/db-migrator.js +2 -5
- package/dist/engine/migration/equivalence-mapper.js +12 -13
- package/dist/engine/migration/service-catalogs.js +6 -7
- package/dist/engine/migration/service-migrator-catalogs.js +6 -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/mobile-detector-ios.js +2 -3
- package/dist/engine/mobile-detector.js +16 -4
- package/dist/engine/model-router/complexity-analyzer.js +4 -7
- package/dist/engine/multi-agent-review/security-analyzer.js +3 -1
- package/dist/engine/multi-app-detector/app-type.js +26 -21
- package/dist/engine/multi-app-detector/core.js +3 -2
- package/dist/engine/multi-teammate-review/disagreement-counter.js +2 -1
- package/dist/engine/multi-teammate-review/panel-orchestrator.js +0 -2
- package/dist/engine/mutation-config-generator.js +10 -7
- package/dist/engine/native-capability-policy.d.ts +4 -0
- package/dist/engine/native-capability-policy.js +24 -0
- package/dist/engine/native-platform.d.ts +3 -0
- package/dist/engine/native-platform.js +44 -0
- package/dist/engine/native-runtime-handshake.d.ts +20 -0
- package/dist/engine/native-runtime-handshake.js +230 -0
- package/dist/engine/network-policy.d.ts +27 -0
- package/dist/engine/network-policy.js +175 -0
- package/dist/engine/next-spec-resolver/dependency-filter.js +2 -22
- package/dist/engine/nfr-profile-detector.js +12 -16
- package/dist/engine/nli-keywords.js +8 -9
- package/dist/engine/oauth/config-guard.js +4 -5
- package/dist/engine/observability/local-execution-spans.d.ts +15 -0
- package/dist/engine/observability/local-execution-spans.js +63 -0
- package/dist/engine/orchestrator/runtime.js +0 -1
- package/dist/engine/package-registry.js +2 -6
- package/dist/engine/performance-analyzer.js +2 -5
- package/dist/engine/permissions-merger/stack-detectors.js +5 -5
- package/dist/engine/planu-core.darwin-arm64.node.manifest.json +39 -0
- package/dist/engine/planu-core.darwin-arm64.node.sbom.json +1310 -0
- package/dist/engine/planu-core.darwin-x64.node.manifest.json +39 -0
- package/dist/engine/planu-core.darwin-x64.node.sbom.json +1310 -0
- package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +38 -0
- package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +1310 -0
- package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +38 -0
- package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +1310 -0
- package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +38 -0
- package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +1310 -0
- package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +38 -0
- package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +1310 -0
- package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +38 -0
- package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +1310 -0
- package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +38 -0
- package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +1310 -0
- package/dist/engine/platform-detector/desktop.js +15 -2
- package/dist/engine/platform-detector/extensions.js +2 -1
- package/dist/engine/platform-detector/games.js +2 -1
- package/dist/engine/platform-detector.js +3 -2
- package/dist/engine/platform-engineering/idp-detector.js +2 -1
- package/dist/engine/plugin-installer/stack-matcher.js +9 -17
- package/dist/engine/plugins/lifecycle.js +6 -2
- package/dist/engine/prior-decisions/contradiction-detector.js +60 -30
- package/dist/engine/project-dna/stack-detector.js +15 -17
- package/dist/engine/project-graph/builder.js +11 -0
- package/dist/engine/project-graph/cache.js +1 -1
- package/dist/engine/project-graph/freshness-projection.d.ts +16 -0
- package/dist/engine/project-graph/freshness-projection.js +95 -0
- package/dist/engine/project-graph/index.d.ts +1 -0
- package/dist/engine/project-graph/index.js +1 -0
- package/dist/engine/project-graph/query.js +15 -25
- package/dist/engine/project-health-checker.js +3 -4
- package/dist/engine/project-scanner/stack-detector.js +17 -17
- package/dist/engine/property-test-generator.js +2 -3
- package/dist/engine/qa-gate.d.ts +1 -0
- package/dist/engine/qa-gate.js +6 -4
- package/dist/engine/quality-gates/gate-catalog.js +47 -46
- package/dist/engine/quality-gates/gate-injector.js +9 -4
- package/dist/engine/readiness-checker.d.ts +1 -16
- package/dist/engine/readiness-checker.js +27 -88
- package/dist/engine/registry/core.js +0 -2
- package/dist/engine/registry/loader.js +9 -12
- package/dist/engine/registry/matcher.js +129 -24
- package/dist/engine/resilience-detector/concurrency-detector.js +4 -4
- package/dist/engine/resilience-detector/middleware-detector.js +11 -13
- package/dist/engine/resilience-detector/validation-detector.js +17 -4
- package/dist/engine/reverse-engineer/config-analyzer.js +9 -8
- package/dist/engine/reverse-engineer/orchestrator.js +5 -9
- package/dist/engine/reverse-engineer/test-file-parser.js +6 -3
- package/dist/engine/rules-generator/index.js +6 -5
- package/dist/engine/runtime-policy.d.ts +9 -0
- package/dist/engine/runtime-policy.js +138 -0
- package/dist/engine/runtime-security/checkers/content-security.js +21 -6
- package/dist/engine/runtime-security/checkers/file-permissions.js +0 -2
- package/dist/engine/runtime-security/rate-limiter.js +0 -1
- package/dist/engine/safety/atomic-write-file.js +20 -9
- package/dist/engine/safety/atomic-writer.js +10 -3
- package/dist/engine/safety/contained-project-file.d.ts +3 -0
- package/dist/engine/safety/contained-project-file.js +23 -0
- package/dist/engine/safety/cross-process-lock.d.ts +3 -40
- package/dist/engine/safety/cross-process-lock.js +268 -272
- package/dist/engine/safety/file-mutex.d.ts +2 -9
- package/dist/engine/safety/file-mutex.js +155 -87
- package/dist/engine/safety/health-monitor.js +15 -6
- package/dist/engine/safety/lock-lease-store.d.ts +79 -0
- package/dist/engine/safety/lock-lease-store.js +372 -0
- package/dist/engine/safety/lock-scavenger.d.ts +2 -5
- package/dist/engine/safety/lock-scavenger.js +158 -76
- package/dist/engine/safety/lock-staleness.js +4 -1
- package/dist/engine/safety/obsolete-lock-guard.d.ts +8 -0
- package/dist/engine/safety/obsolete-lock-guard.js +31 -0
- package/dist/engine/safety/transaction.js +3 -3
- package/dist/engine/sandbox/index.js +2 -1
- package/dist/engine/sandbox/runner-detect.js +3 -2
- package/dist/engine/sandbox/runner-docker.js +4 -3
- package/dist/engine/scan-project/module-discoverer.js +3 -2
- package/dist/engine/schema-generator.js +16 -9
- package/dist/engine/scope-boundaries/contradiction-checker.js +63 -1
- package/dist/engine/scope-boundaries/scope-validator.js +45 -7
- package/dist/engine/security-analyzer/criteria-generator.js +2 -3
- package/dist/engine/self-healing/healer.js +4 -4
- package/dist/engine/session/checkpoint-writer.js +0 -9
- package/dist/engine/shared-agent-memory.js +66 -74
- package/dist/engine/skill-adapter/command-rewriter.js +3 -2
- package/dist/engine/skill-adapter/index.js +3 -3
- package/dist/engine/skill-adapter/placeholder-map.js +3 -2
- package/dist/engine/skill-bootstrap/stack-matcher.js +47 -48
- package/dist/engine/skill-generation/stack-content-generator.js +0 -2
- package/dist/engine/skill-generator/workflow-skill-generator.js +2 -1
- package/dist/engine/skill-registry/ttl-refresh.js +3 -1
- package/dist/engine/skills/skills-fetcher.js +3 -6
- package/dist/engine/skills-reconciler.js +19 -39
- package/dist/engine/spec-coverage/test-finder.js +7 -6
- package/dist/engine/spec-diff-engine.js +3 -3
- package/dist/engine/spec-format/acceptance-criteria.d.ts +6 -2
- package/dist/engine/spec-format/acceptance-criteria.js +77 -18
- 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/unified-spec-builder.d.ts +7 -0
- package/dist/engine/spec-format/unified-spec-builder.js +202 -0
- package/dist/engine/spec-generator/api-key-resolver.d.ts +1 -1
- package/dist/engine/spec-generator/api-key-resolver.js +3 -24
- package/dist/engine/spec-generators/issue-extractor.js +9 -3
- 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/drift-detector.js +5 -13
- package/dist/engine/spec-migrator/filesystem-import.d.ts +4 -8
- package/dist/engine/spec-migrator/filesystem-import.js +134 -23
- 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-migrator/planu-canonical-policy.js +10 -1
- package/dist/engine/spec-migrator/strict-planu-cleanup.js +24 -25
- package/dist/engine/spec-registry/adapter.d.ts +0 -5
- package/dist/engine/spec-registry/adapter.js +45 -46
- package/dist/engine/spec-registry/client.js +2 -0
- package/dist/engine/spec-source-sync.js +3 -2
- 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-templates/custom-loader.js +2 -3
- package/dist/engine/spec-templates/templates-domain-specific.js +8 -5
- package/dist/engine/spec-templates/templates-perf-integration.js +2 -3
- package/dist/engine/stack-auditor/manifest-reader.js +0 -2
- package/dist/engine/staleness/stale-implementing.js +7 -7
- package/dist/engine/tdd-scaffold-generator.js +24 -11
- package/dist/engine/teammate-router/green-phase-verifier.js +2 -1
- package/dist/engine/teammate-router/red-phase-verifier.js +2 -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/technology-registry.d.ts +11 -0
- package/dist/engine/technology-registry.js +142 -0
- package/dist/engine/telemetry/error-reporter.d.ts +1 -2
- package/dist/engine/telemetry/error-reporter.js +11 -18
- package/dist/engine/telemetry/health-checker.js +58 -31
- package/dist/engine/telemetry/index.d.ts +0 -1
- package/dist/engine/telemetry/index.js +0 -1
- package/dist/engine/telemetry/stance.js +3 -2
- package/dist/engine/telemetry/telemetry-client.d.ts +1 -5
- package/dist/engine/telemetry/telemetry-client.js +69 -35
- package/dist/engine/telemetry/telemetry-store.d.ts +3 -3
- package/dist/engine/telemetry/telemetry-store.js +30 -23
- package/dist/engine/test-framework-detector/detectors.js +8 -8
- package/dist/engine/test-framework-detector/strategy.js +8 -9
- package/dist/engine/test-generators/agent-test-templates.js +2 -3
- package/dist/engine/test-generators/contract-test-generator.d.ts +1 -1
- package/dist/engine/test-generators/contract-test-generator.js +9 -202
- package/dist/engine/test-generators/platform-test-templates-b.js +2 -3
- package/dist/engine/test-generators/platform-test-templates.js +2 -5
- package/dist/engine/test-mocks-generator.js +4 -3
- package/dist/engine/test-plan-generator.js +30 -12
- package/dist/engine/test-scaffold-generator/advanced-scaffold.js +4 -5
- package/dist/engine/test-scaffold-generator/unit-scaffold.js +5 -6
- 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 +17 -17
- package/dist/engine/timing/budget.js +98 -64
- package/dist/engine/token-optimizer/analytics.d.ts +1 -1
- package/dist/engine/token-optimizer/analytics.js +5 -6
- package/dist/engine/tool-groups/group-manager.js +3 -3
- package/dist/engine/universal-rules/installer.js +1 -1
- package/dist/engine/universal-rules/rules/planu-modes.js +2 -0
- package/dist/engine/universal-rules/rules/planu-release-policy.js +1 -0
- package/dist/engine/update-notifier.js +8 -5
- 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/approved-spec-portability.d.ts +12 -0
- package/dist/engine/validation/approved-spec-portability.js +107 -0
- package/dist/engine/validation/durable-validation.d.ts +36 -0
- package/dist/engine/validation/durable-validation.js +256 -0
- package/dist/engine/validation/validation-freshness.d.ts +46 -0
- package/dist/engine/validation/validation-freshness.js +567 -0
- package/dist/engine/validation/validation-receipt-codec.d.ts +13 -0
- package/dist/engine/validation/validation-receipt-codec.js +182 -0
- package/dist/engine/validation/validation-receipt.d.ts +62 -0
- package/dist/engine/validation/validation-receipt.js +342 -0
- 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-source-policy.d.ts +4 -0
- package/dist/engine/validation/validation-source-policy.js +16 -0
- package/dist/engine/validation/validation-submission.d.ts +37 -0
- package/dist/engine/validation/validation-submission.js +142 -0
- package/dist/engine/validation/validation-worktree.d.ts +21 -0
- package/dist/engine/validation/validation-worktree.js +260 -0
- package/dist/engine/validation-evidence-ledger.js +41 -0
- package/dist/engine/validation-impact-planner.d.ts +14 -2
- package/dist/engine/validation-impact-planner.js +23 -10
- 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 +13 -25
- package/dist/engine/validator/spec-compliance-runner.js +442 -215
- package/dist/engine/validator/validation-report-writer.js +1 -1
- package/dist/engine/validator.js +37 -23
- package/dist/engine/verify-integrations/index.js +1 -1
- package/dist/engine/version-resolver.js +5 -6
- package/dist/engine/web-fetcher/stack-advisor.js +156 -92
- package/dist/engine/web-fetcher/stack-detector.js +114 -54
- package/dist/engine/workspace-manager.js +3 -2
- package/dist/errors/classified-degradation.d.ts +3 -0
- package/dist/errors/classified-degradation.js +10 -0
- package/dist/errors/degradation-sink.d.ts +3 -0
- package/dist/errors/degradation-sink.js +5 -0
- package/dist/errors/error-taxonomy.d.ts +12 -0
- package/dist/errors/error-taxonomy.js +62 -0
- package/dist/hosts/claude-code/ux/mcp-prompts.js +10 -4
- package/dist/hosts/claude-code/ux/mcp-resources.js +4 -2
- package/dist/hosts/codex/config-scaffold.js +4 -3
- package/dist/hosts/gemini/config-scaffold.js +2 -1
- package/dist/hosts/gemini/multimodal-spec-flow.js +7 -4
- package/dist/index.js +23 -20
- package/dist/resources/patterns.js +3 -1
- package/dist/resources/specs.js +3 -1
- package/dist/resources/templates.js +3 -1
- package/dist/resources/usage-stats-resource.js +2 -18
- package/dist/security/adapters/environment.d.ts +12 -0
- package/dist/security/adapters/environment.js +25 -0
- package/dist/security/adapters/linux-secret-service.d.ts +8 -0
- package/dist/security/adapters/linux-secret-service.js +64 -0
- package/dist/security/adapters/macos-keychain.d.ts +18 -0
- package/dist/security/adapters/macos-keychain.js +90 -0
- package/dist/security/adapters/windows-credential-manager.d.ts +8 -0
- package/dist/security/adapters/windows-credential-manager.js +60 -0
- package/dist/security/redactor.d.ts +5 -0
- package/dist/security/redactor.js +58 -0
- package/dist/security/secret-field.d.ts +5 -0
- package/dist/security/secret-field.js +17 -0
- package/dist/security/secret-migration.d.ts +3 -0
- package/dist/security/secret-migration.js +123 -0
- package/dist/security/secret-provider.d.ts +23 -0
- package/dist/security/secret-provider.js +145 -0
- package/dist/security/secure-json.d.ts +3 -0
- package/dist/security/secure-json.js +29 -0
- package/dist/server/routes/specs.js +10 -0
- package/dist/storage/api-surfaces-store.js +2 -2
- package/dist/storage/approval-operation-lock.d.ts +1 -1
- package/dist/storage/approval-operation-lock.js +5 -3
- package/dist/storage/audit-trail-store.d.ts +1 -1
- package/dist/storage/audit-trail-store.js +48 -62
- package/dist/storage/autopilot-log-store.js +12 -5
- package/dist/storage/base-store.d.ts +9 -9
- package/dist/storage/base-store.js +58 -21
- package/dist/storage/checkpoint-store.js +5 -2
- package/dist/storage/comments-store.js +2 -2
- package/dist/storage/compliance-audit-store.js +5 -2
- package/dist/storage/compliance-gate-config-store.js +7 -4
- package/dist/storage/compliance-store.js +2 -2
- package/dist/storage/context-profile-store.js +2 -2
- package/dist/storage/crash-shield-store.js +8 -4
- package/dist/storage/feedback-remote.d.ts +2 -3
- package/dist/storage/feedback-remote.js +45 -31
- package/dist/storage/feedback-store.js +10 -6
- package/dist/storage/figma-store.js +8 -4
- package/dist/storage/file-mutex.js +9 -1
- package/dist/storage/gaps-log.js +7 -4
- package/dist/storage/global-projects-store.js +4 -2
- package/dist/storage/global-store.d.ts +1 -17
- package/dist/storage/global-store.js +1 -42
- package/dist/storage/ideas-store.js +2 -2
- package/dist/storage/index.d.ts +5 -2
- package/dist/storage/index.js +5 -2
- package/dist/storage/knowledge-store/knowledge.js +27 -33
- package/dist/storage/lessons-store.js +2 -2
- package/dist/storage/migrations/canonical-storage.d.ts +15 -0
- package/dist/storage/migrations/canonical-storage.js +712 -0
- package/dist/storage/oauth-store.js +36 -8
- package/dist/storage/path-resolver.js +16 -3
- package/dist/storage/pr-store.js +2 -2
- package/dist/storage/project-drift-store.js +20 -8
- package/dist/storage/project-identity.d.ts +32 -0
- package/dist/storage/project-identity.js +194 -0
- package/dist/storage/project-resolver.js +14 -7
- package/dist/storage/release-notes-store.js +2 -6
- package/dist/storage/runtime-db.d.ts +195 -0
- package/dist/storage/runtime-db.js +1110 -0
- package/dist/storage/schema-registry.d.ts +5 -0
- package/dist/storage/schema-registry.js +74 -0
- package/dist/storage/sentry-store.js +20 -9
- package/dist/storage/session-state-store.js +12 -5
- package/dist/storage/skill-registry-storage.d.ts +1 -1
- package/dist/storage/skill-registry-storage.js +13 -11
- package/dist/storage/slack-store.js +10 -5
- package/dist/storage/spec-store.d.ts +14 -2
- package/dist/storage/spec-store.js +134 -37
- 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 +61 -20
- package/dist/storage/status-store/self-healing.js +13 -8
- package/dist/storage/status-store/version-sync.d.ts +2 -1
- package/dist/storage/status-store/version-sync.js +36 -39
- package/dist/storage/steering-store.js +2 -2
- package/dist/storage/storage-bundle.d.ts +5 -0
- package/dist/storage/storage-bundle.js +117 -0
- package/dist/storage/storage-catalog-validation.d.ts +3 -0
- package/dist/storage/storage-catalog-validation.js +20 -0
- package/dist/storage/storage-catalog.d.ts +25 -0
- package/dist/storage/storage-catalog.js +390 -0
- package/dist/storage/storage-layout.d.ts +24 -0
- package/dist/storage/storage-layout.js +101 -0
- package/dist/storage/storybook-store.js +2 -2
- package/dist/storage/supabase-store.js +24 -6
- package/dist/storage/tdd-policy-store.js +8 -6
- package/dist/storage/tech-debt-store.js +3 -3
- package/dist/storage/technology-selection-store.d.ts +1 -1
- package/dist/storage/technology-selection-store.js +6 -3
- package/dist/storage/token-cache-store.js +3 -3
- package/dist/storage/token-ledger-store.js +13 -8
- package/dist/storage/transition-log.js +7 -4
- package/dist/storage/trash-store.js +15 -6
- package/dist/storage/usage-store.d.ts +1 -5
- package/dist/storage/usage-store.js +0 -35
- package/dist/storage/vector-store/migrator.js +7 -2
- package/dist/storage/vector-store/sqlite-adapter.js +4 -1
- package/dist/storage/webhook-server-store.js +2 -2
- package/dist/tools/api-spec-generator-handler.js +3 -2
- package/dist/tools/auto-update-handler.d.ts +4 -4
- package/dist/tools/auto-update-handler.js +4 -4
- package/dist/tools/browser-validate-handler.js +2 -1
- package/dist/tools/challenge-spec/platform-challenge-scenarios-b.js +8 -5
- 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-desktop.js +8 -3
- package/dist/tools/challenge-spec/scenarios-failure.js +7 -2
- 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/security-challenge-scenarios.js +3 -3
- package/dist/tools/challenge-spec.js +5 -5
- package/dist/tools/check-readiness.js +1 -1
- package/dist/tools/clarify-requirements/multiple-choice.js +53 -41
- package/dist/tools/comments-handler.js +2 -3
- package/dist/tools/compliance-test-handler.js +2 -2
- package/dist/tools/configure-telemetry.js +41 -13
- package/dist/tools/create-spec/auto-pipeline.js +13 -1
- package/dist/tools/create-spec/autopilot-analyzer.d.ts +1 -1
- package/dist/tools/create-spec/autopilot-analyzer.js +2 -4
- package/dist/tools/create-spec/mcp-tool-contract.d.ts +10 -4
- package/dist/tools/create-spec/mcp-tool-contract.js +7 -36
- package/dist/tools/create-spec/post-creation.d.ts +2 -1
- package/dist/tools/create-spec/post-creation.js +56 -54
- package/dist/tools/create-spec/relevance-scorer.d.ts +1 -1
- package/dist/tools/create-spec/relevance-scorer.js +17 -1
- package/dist/tools/create-spec/spec-builder.js +2 -23
- package/dist/tools/create-spec/spec-created-outbox-consumer.d.ts +14 -0
- package/dist/tools/create-spec/spec-created-outbox-consumer.js +176 -0
- package/dist/tools/create-spec.d.ts +1 -0
- package/dist/tools/create-spec.js +802 -732
- package/dist/tools/define-ui-contract/agent-interaction-contract.js +2 -4
- package/dist/tools/define-ui-contract-desktop/desktop-contract.js +12 -4
- package/dist/tools/define-ui-contract-desktop/native-contract.js +7 -3
- package/dist/tools/define-ui-contract-extensions.js +2 -3
- package/dist/tools/define-ui-contract-mobile.js +5 -9
- package/dist/tools/define-ui-contract-web/component-tree.js +4 -3
- package/dist/tools/define-ui-contract.js +7 -4
- package/dist/tools/deploy/deploy-spec.js +2 -4
- package/dist/tools/deploy/deploy-status.js +2 -4
- package/dist/tools/design-schema/nosql-category-schemas.js +2 -3
- package/dist/tools/design-schema-sql/migrations.js +3 -2
- package/dist/tools/design-schema-sql/tables.js +4 -1
- package/dist/tools/design-schema.js +4 -3
- package/dist/tools/feedback-handler.js +2 -4
- package/dist/tools/gc-data-projects.js +5 -5
- package/dist/tools/generate-execution-plan.js +3 -4
- package/dist/tools/generate-sub-agent.js +2 -1
- package/dist/tools/generate-tests/generators/a11y-tests-generator.js +5 -4
- package/dist/tools/generate-tests/generators/advanced-testing-generator.js +5 -4
- package/dist/tools/generate-tests/generators/concurrency-test-generator/js-templates.js +9 -4
- package/dist/tools/generate-tests/generators/concurrency-test-generator.js +8 -4
- package/dist/tools/generate-tests/generators/database/detectors.js +9 -3
- package/dist/tools/generate-tests/generators/database/index.js +2 -4
- package/dist/tools/generate-tests/generators/event-test-generator.js +5 -4
- package/dist/tools/generate-tests/generators/game-tests-generator.js +2 -1
- package/dist/tools/generate-tests/generators/graphql-test-generator.js +11 -5
- package/dist/tools/generate-tests/generators/grpc-test-generator.js +2 -3
- package/dist/tools/generate-tests/generators/llm-security-test-generator.js +4 -3
- package/dist/tools/generate-tests/generators/local-first-test-generator.js +4 -3
- package/dist/tools/generate-tests/generators/mcp-tests-generator.js +11 -9
- package/dist/tools/generate-tests/generators/microservices/index.js +2 -3
- package/dist/tools/generate-tests/generators/otel-test-generator.js +6 -3
- package/dist/tools/generate-tests/generators/security/detectors.js +2 -5
- package/dist/tools/generate-tests/generators/security/index.js +2 -4
- package/dist/tools/generate-tests/generators/supply-chain-test-generator.js +2 -3
- package/dist/tools/generate-tests/generators/visual-regression-generator.js +14 -17
- package/dist/tools/generate-tests/generators/websocket-test-generator.js +2 -3
- package/dist/tools/generate-tests/test-helpers.js +11 -12
- package/dist/tools/generate-tests-content.js +4 -3
- package/dist/tools/generate-tests.js +7 -4
- package/dist/tools/git/branch-ops.d.ts +1 -1
- package/dist/tools/git/branch-ops.js +3 -2
- package/dist/tools/git/git-helpers.d.ts +0 -2
- package/dist/tools/git/git-helpers.js +7 -7
- package/dist/tools/github-pr-handler.js +10 -3
- package/dist/tools/heal-spec-docs.js +6 -53
- package/dist/tools/hook-generator-handler.js +6 -6
- package/dist/tools/init-constitution/platform-principles.js +4 -2
- package/dist/tools/init-project/agents-md-writer.js +14 -5
- package/dist/tools/init-project/claude-md-generator.js +19 -12
- package/dist/tools/init-project/handler.js +165 -75
- package/dist/tools/init-project/host-assets-writer.d.ts +1 -1
- package/dist/tools/init-project/host-assets-writer.js +30 -3
- package/dist/tools/init-project/lifecycle-helpers.js +3 -5
- package/dist/tools/init-project/lint-scaffolder.d.ts +3 -3
- package/dist/tools/init-project/lint-scaffolder.js +6 -2
- package/dist/tools/init-project/migration-runner.d.ts +8 -1
- package/dist/tools/init-project/migration-runner.js +68 -2
- package/dist/tools/init-project/per-client-files-writer.js +14 -4
- 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/init-project/rules-generator.js +10 -2
- package/dist/tools/init-project/scaffold-writer.d.ts +1 -1
- package/dist/tools/init-project/scaffold-writer.js +17 -15
- package/dist/tools/jobs/handlers.d.ts +15 -0
- package/dist/tools/jobs/handlers.js +158 -0
- package/dist/tools/list-specs.js +39 -59
- package/dist/tools/manage-plugins-handler.js +17 -9
- package/dist/tools/merge-risk-handler.js +2 -2
- package/dist/tools/migrate-tech/advanced-handlers.js +3 -2
- package/dist/tools/migrate-tech/build-tool-handlers.js +3 -2
- package/dist/tools/migrate-tech/core-handlers.js +5 -2
- package/dist/tools/migrate-tech/data-handlers.js +6 -3
- package/dist/tools/migrate-tech-advanced/rollback-handlers.js +3 -2
- package/dist/tools/migrate-tech-advanced/service-handlers.js +5 -2
- package/dist/tools/oauth-handler.js +7 -1
- 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 -24
- package/dist/tools/register-spec-tools/analysis-tools.js +8 -8
- package/dist/tools/register-spec-tools/core-spec-tools.js +48 -8
- package/dist/tools/registry/auth.js +13 -5
- package/dist/tools/registry/publish.js +7 -4
- package/dist/tools/resolve-project-path.d.ts +2 -2
- package/dist/tools/resolve-project-path.js +7 -7
- package/dist/tools/reverse-engineer/analyzer.js +14 -14
- package/dist/tools/rollback-release.js +3 -2
- package/dist/tools/safe-handler.d.ts +5 -13
- package/dist/tools/safe-handler.js +302 -194
- package/dist/tools/schema-generator-handler.js +2 -1
- 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 +88 -105
- package/dist/tools/schemas/output-schemas.js +12 -122
- package/dist/tools/schemas/plugins-schemas.js +0 -2
- package/dist/tools/schemas/registry.js +0 -2
- package/dist/tools/schemas/skill-registry-schemas.js +0 -2
- 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 +267 -0
- package/dist/tools/schemas/validate-output-schema.js +219 -0
- package/dist/tools/sentry-handler.js +6 -1
- package/dist/tools/skill-bootstrap-handler.js +7 -4
- package/dist/tools/spec-prompt-handler.js +3 -3
- package/dist/tools/status-handler.js +4 -0
- package/dist/tools/storage-bundle-handler.d.ts +9 -0
- package/dist/tools/storage-bundle-handler.js +29 -0
- package/dist/tools/storage-paths-handler.d.ts +9 -0
- package/dist/tools/storage-paths-handler.js +17 -0
- package/dist/tools/suggest-mcp-server.js +11 -5
- package/dist/tools/suggest-mcps.js +29 -9
- package/dist/tools/suggest-tooling/advanced-testing-catalog.js +4 -5
- package/dist/tools/suggest-tooling/build-tool-catalog.js +2 -1
- package/dist/tools/suggest-tooling/css-framework-catalog.js +13 -7
- package/dist/tools/suggest-tooling/framework-catalog.js +3 -5
- package/dist/tools/suggest-tooling/frontend-perf-catalog.js +2 -6
- package/dist/tools/suggest-tooling/handler.js +10 -4
- package/dist/tools/suggest-tooling/hypermedia-catalog.js +11 -3
- package/dist/tools/suggest-tooling/linting-catalog.js +4 -7
- package/dist/tools/suggest-tooling/llm-security-catalog.js +2 -1
- package/dist/tools/suggest-tooling/local-first-catalog.js +7 -5
- package/dist/tools/suggest-tooling/observability-catalog.js +3 -4
- package/dist/tools/suggest-tooling/otel-catalog.js +4 -1
- package/dist/tools/suggest-tooling/resilience-catalog.js +16 -5
- package/dist/tools/suggest-tooling/skills-catalog.js +10 -9
- package/dist/tools/suggest-tooling/supply-chain-catalog.js +2 -3
- package/dist/tools/supabase-handler.js +2 -1
- package/dist/tools/sync-spec-state-handler.d.ts +1 -1
- package/dist/tools/sync-spec-state-handler.js +1 -1
- package/dist/tools/token-intelligence-handler.js +2 -2
- package/dist/tools/token-recording.js +2 -2
- package/dist/tools/token-savings-handler.js +2 -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 +162 -15
- package/dist/tools/tool-registry/group-infra.js +37 -122
- package/dist/tools/tool-registry/group-quality-compliance.js +31 -38
- 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.d.ts +13 -0
- package/dist/tools/update-status/approval-gates.js +84 -0
- package/dist/tools/update-status/batch.js +4 -2
- package/dist/tools/update-status/dod-gates.d.ts +6 -24
- package/dist/tools/update-status/dod-gates.js +60 -135
- package/dist/tools/update-status/done-receipt-verifier.d.ts +15 -0
- package/dist/tools/update-status/done-receipt-verifier.js +158 -0
- package/dist/tools/update-status/evidence-gate.js +190 -3
- package/dist/tools/update-status/file-sync.d.ts +2 -1
- package/dist/tools/update-status/file-sync.js +80 -18
- package/dist/tools/update-status/gate-harness.d.ts +7 -0
- package/dist/tools/update-status/gate-harness.js +8 -0
- package/dist/tools/update-status/index.d.ts +2 -0
- package/dist/tools/update-status/index.js +307 -367
- package/dist/tools/update-status/qa-gate.d.ts +1 -1
- package/dist/tools/update-status/qa-gate.js +2 -2
- 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 +32 -39
- package/dist/tools/update-status/transition-guard.d.ts +4 -2
- package/dist/tools/update-status/transition-guard.js +89 -25
- package/dist/tools/update-status-actions.d.ts +4 -2
- package/dist/tools/update-status-actions.js +16 -14
- package/dist/tools/update-status-convention-gate.js +24 -23
- package/dist/tools/update-status-reconcile.js +2 -2
- 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-api-contract-handler.js +3 -3
- package/dist/tools/validate-lint.d.ts +1 -0
- package/dist/tools/validate-lint.js +21 -14
- package/dist/tools/validate.d.ts +4 -1
- package/dist/tools/validate.js +319 -31
- package/dist/transports/http-transport.js +87 -66
- package/dist/transports/mcp-types.d.ts +0 -2
- package/dist/transports/middleware/with-telemetry.js +3 -2
- package/dist/transports/session-context.js +1 -1
- package/dist/types/abortable-process.d.ts +2 -0
- package/dist/types/advanced-framework.js +0 -2
- package/dist/types/atomic-upgrade.d.ts +71 -0
- package/dist/types/atomic-upgrade.js +2 -0
- package/dist/types/cascade-hooks.d.ts +1 -1
- package/dist/types/ci.d.ts +0 -4
- package/dist/types/ci.js +0 -2
- 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 +81 -10
- package/dist/types/crash-shield-framework-rules.d.ts +24 -0
- package/dist/types/crash-shield-framework-rules.js +2 -0
- package/dist/types/dashboard.d.ts +3 -7
- package/dist/types/data-gc.d.ts +16 -0
- package/dist/types/deploy.js +0 -2
- package/dist/types/docs.d.ts +0 -1
- package/dist/types/durable-job.d.ts +78 -0
- package/dist/types/durable-job.js +2 -0
- package/dist/types/durable-validation.d.ts +158 -0
- package/dist/types/durable-validation.js +166 -0
- package/dist/types/errors/structured-error.d.ts +3 -3
- package/dist/types/estimation.d.ts +4 -0
- package/dist/types/evidence-gates.d.ts +17 -1
- package/dist/types/execution.d.ts +45 -0
- package/dist/types/gemini-integration.d.ts +0 -11
- package/dist/types/handoff-artifacts.d.ts +43 -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 +4 -6
- package/dist/types/index.js +3 -5
- package/dist/types/infrastructure.js +0 -2
- package/dist/types/kiro-integration.d.ts +0 -6
- package/dist/types/lifecycle.js +0 -2
- package/dist/types/migration/canonical-storage.d.ts +37 -0
- package/dist/types/migration/canonical-storage.js +2 -0
- package/dist/types/migration/index.d.ts +1 -1
- package/dist/types/native-worker.d.ts +48 -0
- package/dist/types/native-worker.js +2 -0
- package/dist/types/network-policy.d.ts +12 -0
- package/dist/types/network-policy.js +2 -0
- package/dist/types/oauth.js +0 -2
- package/dist/types/observability.d.ts +32 -0
- package/dist/types/outbox-worker.d.ts +35 -0
- package/dist/types/outbox-worker.js +11 -0
- package/dist/types/permissions-config.d.ts +0 -27
- package/dist/types/permissions-config.js +0 -2
- package/dist/types/plugin-install.d.ts +0 -11
- package/dist/types/plugin-install.js +0 -2
- package/dist/types/portal.d.ts +1 -0
- package/dist/types/product-public.d.ts +75 -0
- package/dist/types/product-public.js +2 -0
- package/dist/types/project/inputs.d.ts +2 -0
- package/dist/types/project/lint-scaffolder.d.ts +1 -1
- package/dist/types/project/mfe-detection.js +0 -2
- package/dist/types/project/platform-mobile.js +0 -2
- package/dist/types/project/platform-specialty.js +0 -2
- package/dist/types/project-knowledge-graph.d.ts +7 -0
- package/dist/types/runtime-database.d.ts +10 -0
- package/dist/types/runtime-database.js +2 -0
- package/dist/types/runtime-policy.d.ts +51 -0
- package/dist/types/runtime-policy.js +2 -0
- package/dist/types/safety.d.ts +4 -5
- package/dist/types/schema-registry.d.ts +12 -0
- package/dist/types/schema-registry.js +2 -0
- package/dist/types/security.d.ts +52 -0
- package/dist/types/session-safeguard.d.ts +0 -31
- package/dist/types/skill-registry.js +0 -2
- 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 +14 -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 +31 -0
- package/dist/types/spec-format.d.ts +24 -0
- package/dist/types/spec-lock-v2.d.ts +13 -7
- package/dist/types/spec-lock-v2.js +1 -1
- package/dist/types/spec-registry.js +0 -2
- package/dist/types/storage-bundle.d.ts +13 -0
- package/dist/types/storage-bundle.js +2 -0
- package/dist/types/storage-catalog.d.ts +18 -0
- package/dist/types/storage-catalog.js +2 -0
- package/dist/types/team-planner.js +0 -2
- package/dist/types/technology-registry.d.ts +35 -0
- package/dist/types/technology-registry.js +2 -0
- package/dist/types/technology-symbols.generated.d.ts +117 -0
- package/dist/types/technology-symbols.generated.js +2 -0
- package/dist/types/telemetry.d.ts +7 -4
- package/dist/types/transport.d.ts +2 -2
- package/dist/types/usage.d.ts +1 -36
- package/dist/types/usage.js +1 -1
- package/dist/types/validate-worker.d.ts +50 -0
- package/dist/types/validate-worker.js +2 -0
- package/dist/types/validation-evidence.d.ts +1 -1
- package/dist/types/validation-receipt.d.ts +104 -0
- package/dist/types/validation-receipt.js +2 -0
- package/dist/types/validation.d.ts +2 -2
- package/package.json +36 -21
- 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 -244
- package/dist/config/server-instructions.ts +0 -121
- package/dist/config/version-defaults.ts +0 -76
- package/dist/config/version.ts +0 -18
- 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 -16
- package/dist/engine/session-safeguard/learnings-buffer.js +0 -114
- 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/telemetry/geo-resolver.d.ts +0 -9
- package/dist/engine/telemetry/geo-resolver.js +0 -43
- 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/data-dir-discovery.d.ts +0 -31
- package/dist/storage/data-dir-discovery.js +0 -140
- package/dist/storage/license-store.d.ts +0 -6
- package/dist/storage/license-store.js +0 -91
- package/dist/storage/migrations/cwd-to-absolute.d.ts +0 -25
- package/dist/storage/migrations/cwd-to-absolute.js +0 -281
- package/dist/storage/session-store.d.ts +0 -20
- package/dist/storage/session-store.js +0 -128
- 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/create-spec/adapters/prior-decisions-hint.d.ts +0 -14
- package/dist/tools/create-spec/adapters/prior-decisions-hint.js +0 -30
- package/dist/tools/export-session.d.ts +0 -3
- package/dist/tools/export-session.js +0 -90
- package/dist/tools/init-project/ancestor-data-detector.d.ts +0 -22
- package/dist/tools/init-project/ancestor-data-detector.js +0 -81
- 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/update-status/force-done-audit.d.ts +0 -2
- package/dist/tools/update-status/force-done-audit.js +0 -23
- 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/migration/cwd-to-absolute.d.ts +0 -8
- package/dist/types/migration/cwd-to-absolute.js +0 -3
- 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
|
@@ -3,41 +3,42 @@ import { upsertToken, hashQuestions } from '../engine/clarification-gate/token-s
|
|
|
3
3
|
import { ti } from '../i18n/index.js';
|
|
4
4
|
import { knowledgeStore, specStore } from '../storage/index.js';
|
|
5
5
|
import { readTechnologySelectionContract } from '../storage/technology-selection-store.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { toolResult, interactiveResult } from './response-helpers.js';
|
|
7
|
+
import { readFile, stat as fsStat } from 'node:fs/promises';
|
|
8
8
|
import { createHash, randomUUID } from 'node:crypto';
|
|
9
9
|
import { dirname as pathDirname, isAbsolute as pathIsAbsolute, join as pathJoin, relative as pathRelative, sep as pathSeparator, } from 'node:path';
|
|
10
|
-
import { checkSpecReadiness } from '../engine/readiness-checker.js';
|
|
11
10
|
import { buildSpecContext, buildSplitResult } from './create-spec/spec-builder.js';
|
|
12
11
|
import { validateConstitution } from './create-spec/constitution-validator.js';
|
|
13
|
-
import {
|
|
14
|
-
import { notifyStoreChange } from '../engine/doc-generator/portal/regen-hook.js';
|
|
15
|
-
import { compactObj } from '../engine/compact-obj.js';
|
|
16
|
-
import { runAutoPostCreatePipeline } from './create-spec/auto-pipeline.js';
|
|
12
|
+
import { getAsyncAnalysisPath } from './create-spec/post-creation.js';
|
|
17
13
|
import { extractCriteria, generateLeanSpecContent, } from '../engine/spec-format/lean-spec-generator.js';
|
|
18
14
|
import { generateLeanTechnicalContent, } from '../engine/spec-format/lean-technical-generator.js';
|
|
19
15
|
import { extractFilesFromSpecBody } from '../engine/spec-format/technical-md-populator.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
16
|
+
import { buildCanonicalUnifiedSpecContent, validateUnifiedSpecCandidate, } from '../engine/spec-format/unified-spec-builder.js';
|
|
17
|
+
import { buildImplementationContractSection } from '../engine/implementation-contract/index.js';
|
|
22
18
|
import { validateEnglishOnlySpecText } from '../engine/spec-language/english-only.js';
|
|
23
19
|
import { FallbackGenerator } from '../engine/spec-generator/index.js';
|
|
24
20
|
import { analyzeProjectForSpec, getEmptyAutopilotResult, } from './create-spec/autopilot-analyzer.js';
|
|
25
|
-
import { AutopilotSummaryCollector } from '../engine/autopilot/summary-collector.js';
|
|
26
21
|
import { trackCost } from '../engine/cost-tracking/operation-tracker.js';
|
|
27
22
|
import { analyzeSimplicity } from '../engine/simplicity-detector.js';
|
|
28
23
|
import { withBudget, unwrapBudget, withTotalBudget } from '../engine/timing/budget.js';
|
|
29
24
|
import { measureStep } from '../engine/timing/structured-log.js';
|
|
30
25
|
import { resolveProjectIdOrAutoDetect } from './resolve-project-id.js';
|
|
31
26
|
import { hashProjectPath } from '../storage/base-store.js';
|
|
32
|
-
import { findSimilarSpecs } from '../engine/spec-searcher.js';
|
|
33
|
-
import { scoreSpecQuality } from '../engine/spec-quality-scorer.js';
|
|
34
|
-
import { runPriorDecisionsHint } from './create-spec/adapters/prior-decisions-hint.js';
|
|
35
|
-
import { adviseSimilarSpecs } from '../engine/complexity-budget/index.js';
|
|
36
27
|
import { issuePlannerToken } from '../engine/reviewer-tokens/issuer.js';
|
|
28
|
+
import { getRuntimePolicy } from '../engine/runtime-policy.js';
|
|
37
29
|
import { generateInteractiveQuestions } from './create-spec/question-generator.js';
|
|
38
30
|
import { buildCriterionGroundingRecords, filterGroundedCriteria, getAdvisoryCriteria, getContractCriteria, } from '../engine/spec-grounding/contract.js';
|
|
39
31
|
import { checkGenericSpecOutput } from '../engine/spec-quality/generic-output-gate.js';
|
|
40
32
|
import { calculateActionableSpecMetrics, shouldExposeMetric, } from '../engine/spec-metrics/actionable-metrics.js';
|
|
33
|
+
import { RuntimeDatabase } from '../storage/runtime-db.js';
|
|
34
|
+
import { BoundaryFailure } from '../errors/error-taxonomy.js';
|
|
35
|
+
import { SpecAcFormatEnum, SpecScopeEnum, SpecTargetEnum, SpecTypeEnum } from './schemas/spec.js';
|
|
36
|
+
import { SPEC_CREATED_POST_COMMIT_TASKS } from '../types/outbox-worker.js';
|
|
37
|
+
import { resolveStorageLayout } from '../storage/storage-layout.js';
|
|
38
|
+
import { OperationJournal, recoverOperationJournal, } from '../engine/execution/operation-journal.js';
|
|
39
|
+
import { assertExecutionCanCommit } from '../engine/execution/context.js';
|
|
40
|
+
import { cleanupExecutionArtifact, executionLink, executionMkdir, executionRemove, executionRename, executionWriteFileExclusive, } from '../engine/execution/deadline-io.js';
|
|
41
|
+
import { atomicWriteFile } from '../engine/safety/atomic-write-file.js';
|
|
41
42
|
/** SPEC-584: Persist a clarification token when interactive questions are emitted. Best-effort. */
|
|
42
43
|
async function persistClarificationToken(earlyReturn, projectId, toolName) {
|
|
43
44
|
try {
|
|
@@ -88,31 +89,13 @@ function handleClarification(_server, description, knowledge, autopilot, params)
|
|
|
88
89
|
function hasFileEntries(files) {
|
|
89
90
|
return files.create.length + files.modify.length + files.test.length > 0;
|
|
90
91
|
}
|
|
91
|
-
function mergeTechnicalFiles(primary, fallback) {
|
|
92
|
-
return {
|
|
93
|
-
create: mergeFileEntries(primary.create, fallback.create),
|
|
94
|
-
modify: mergeFileEntries(primary.modify, fallback.modify),
|
|
95
|
-
test: mergeFileEntries(primary.test, fallback.test),
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
function mergeFileEntries(primary, fallback) {
|
|
99
|
-
const byPath = new Map();
|
|
100
|
-
for (const file of [...primary, ...fallback]) {
|
|
101
|
-
if (!byPath.has(file.path)) {
|
|
102
|
-
byPath.set(file.path, file);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return [...byPath.values()];
|
|
106
|
-
}
|
|
107
92
|
async function resolveTechnicalFiles(input) {
|
|
108
93
|
const generatedSource = [input.generatedTechnicalSection, input.generatedSpecBody]
|
|
109
94
|
.filter((part) => part.trim().length > 0)
|
|
110
95
|
.join('\n\n');
|
|
111
96
|
const extracted = await extractFilesFromSpecBody(generatedSource, input.projectPath);
|
|
112
97
|
if (extracted !== null && hasFileEntries(extracted)) {
|
|
113
|
-
return
|
|
114
|
-
? mergeTechnicalFiles(extracted, input.autopilot.suggestedFiles)
|
|
115
|
-
: extracted;
|
|
98
|
+
return extracted;
|
|
116
99
|
}
|
|
117
100
|
return { create: [], modify: [], test: [] };
|
|
118
101
|
}
|
|
@@ -185,23 +168,6 @@ const HIGH_RISK_WARNING = 'High-risk spec — consider: ' +
|
|
|
185
168
|
'1) Is the approach technically feasible within the project constraints? ' +
|
|
186
169
|
'2) What edge cases (network failure, concurrent writes, empty state) could break this? ' +
|
|
187
170
|
'3) Which existing specs or external services does this depend on?';
|
|
188
|
-
async function runQualityScore(spec) {
|
|
189
|
-
try {
|
|
190
|
-
const report = await scoreSpecQuality(spec);
|
|
191
|
-
return {
|
|
192
|
-
total: report.score.total,
|
|
193
|
-
grade: report.score.grade,
|
|
194
|
-
completeness: report.score.completeness,
|
|
195
|
-
testability: report.score.testability,
|
|
196
|
-
ambiguity: report.score.ambiguity,
|
|
197
|
-
risk: report.score.risk,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
catch {
|
|
201
|
-
/* best-effort — never block spec creation */
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
171
|
function runSimplicityCheck(text, hours) {
|
|
206
172
|
try {
|
|
207
173
|
return analyzeSimplicity(text, hours);
|
|
@@ -221,37 +187,15 @@ function buildAdvisoryCompat() {
|
|
|
221
187
|
};
|
|
222
188
|
}
|
|
223
189
|
/** Persist only out-of-scope items supplied explicitly by the caller. */
|
|
224
|
-
function resolveOutOfScope(provided) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
async function runComplexityAdvice(projectId, currentSpecId, tags, targetCriteriaCount) {
|
|
229
|
-
try {
|
|
230
|
-
const allSpecs = await specStore.listSpecs(projectId);
|
|
231
|
-
const historicals = allSpecs
|
|
232
|
-
.filter((s) => s.id !== currentSpecId)
|
|
233
|
-
.slice(-200)
|
|
234
|
-
.map((s) => ({
|
|
235
|
-
specId: s.id,
|
|
236
|
-
criteriaCount: s.reviewNotes?.length ?? 0,
|
|
237
|
-
filesCount: s.impactAnalysis?.affectedFiles.length ?? 0,
|
|
238
|
-
estimatedHours: s.estimation.devHours,
|
|
239
|
-
actualHours: s.actuals?.devHours,
|
|
240
|
-
tags: s.tags,
|
|
241
|
-
}));
|
|
242
|
-
const advice = adviseSimilarSpecs(targetCriteriaCount, tags, historicals);
|
|
243
|
-
if (advice.similarSpecsCount === 0) {
|
|
244
|
-
return null;
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
suggestedCategory: advice.suggestedCategory,
|
|
248
|
-
reasoning: advice.reasoning,
|
|
249
|
-
similarSpecsCount: advice.similarSpecsCount,
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
catch {
|
|
253
|
-
return null;
|
|
190
|
+
function resolveOutOfScope(provided, source = '') {
|
|
191
|
+
const explicit = provided?.filter((item) => item.trim().length > 0) ?? [];
|
|
192
|
+
if (explicit.length > 0) {
|
|
193
|
+
return explicit;
|
|
254
194
|
}
|
|
195
|
+
const section = /(?:^|\n)##\s+Out of scope\s*\n([\s\S]*?)(?=\n##\s|$)/i.exec(source)?.[1];
|
|
196
|
+
return section
|
|
197
|
+
? Array.from(section.matchAll(/^[-*]\s+(.+)$/gm), (match) => match[1]?.trim() ?? '').filter(Boolean)
|
|
198
|
+
: [];
|
|
255
199
|
}
|
|
256
200
|
/** SPEC-783: Synthesize agent team findings into unified spec.md ## Technical section. */
|
|
257
201
|
async function handleAgentTeamSynthesis(specId, projectPath, findings) {
|
|
@@ -296,7 +240,7 @@ async function handleAgentTeamSynthesis(specId, projectPath, findings) {
|
|
|
296
240
|
const synthesized = synthesizeFindings('', findings);
|
|
297
241
|
updatedContent = `${specContent.trimEnd()}\n\n## Technical\n\n${synthesized}\n`;
|
|
298
242
|
}
|
|
299
|
-
await
|
|
243
|
+
await atomicWriteFile(specPath, updatedContent, { encoding: 'utf8' });
|
|
300
244
|
return {
|
|
301
245
|
content: [
|
|
302
246
|
{
|
|
@@ -311,14 +255,139 @@ async function handleAgentTeamSynthesis(specId, projectPath, findings) {
|
|
|
311
255
|
return { content: [{ type: 'text', text: `Synthesis failed: ${msg}` }], isError: true };
|
|
312
256
|
}
|
|
313
257
|
}
|
|
314
|
-
/**
|
|
315
|
-
function computeIdempotencyKey(title, projectPath) {
|
|
316
|
-
|
|
258
|
+
/** Derive a bounded storage key from an explicit caller key or the legacy request identity. */
|
|
259
|
+
function computeIdempotencyKey(title, projectPath, explicitKey) {
|
|
260
|
+
const identity = explicitKey ? `explicit::${explicitKey}` : `${title}::${projectPath}`;
|
|
261
|
+
return createHash('sha256').update(identity).digest('hex');
|
|
262
|
+
}
|
|
263
|
+
function createSpecRequestDigest(params, projectPath) {
|
|
264
|
+
return createHash('sha256')
|
|
265
|
+
.update(JSON.stringify({ ...params, projectPath, idempotencyKey: undefined }))
|
|
266
|
+
.digest('hex');
|
|
267
|
+
}
|
|
268
|
+
function computeOperationKey(params, projectPath, dedupeKey) {
|
|
269
|
+
const identity = params.idempotencyKey
|
|
270
|
+
? `explicit::${params.idempotencyKey}`
|
|
271
|
+
: `implicit::${dedupeKey}::${params.description}::${randomUUID()}`;
|
|
272
|
+
return createHash('sha256').update(`${identity}::${projectPath}`).digest('hex');
|
|
317
273
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
274
|
+
function finishRecoveredOperation(journal, key, result) {
|
|
275
|
+
let entry = journal.get('create_spec', key);
|
|
276
|
+
if (entry?.state === 'intent') {
|
|
277
|
+
entry = journal.prepared('create_spec', key);
|
|
278
|
+
}
|
|
279
|
+
if (entry?.state === 'prepared') {
|
|
280
|
+
entry = journal.committed('create_spec', key, result);
|
|
281
|
+
}
|
|
282
|
+
if (entry?.state === 'committed') {
|
|
283
|
+
entry = journal.acknowledged('create_spec', key);
|
|
284
|
+
}
|
|
285
|
+
return entry?.result ?? result;
|
|
286
|
+
}
|
|
287
|
+
function buildCommittedCreateResult(input) {
|
|
288
|
+
const advisorySignals = [];
|
|
289
|
+
if (input.advisoryCriteria.length > 0) {
|
|
290
|
+
advisorySignals.push(makeAdvisorySignal({
|
|
291
|
+
key: 'ungrounded-contract-items',
|
|
292
|
+
kind: 'quality',
|
|
293
|
+
message: `${String(input.advisoryCriteria.length)} ungrounded contract item(s) kept advisory-only.`,
|
|
294
|
+
source: 'validator',
|
|
295
|
+
evidence: input.advisoryCriteria.slice(0, 10),
|
|
296
|
+
confidence: 0.8,
|
|
297
|
+
surface: 'structuredContent',
|
|
298
|
+
value: input.advisoryCriteria,
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
const splitResult = buildSplitResult(input.splitSuggestion, input.experienceLevel);
|
|
302
|
+
if (splitResult) {
|
|
303
|
+
advisorySignals.push(makeAdvisorySignal({
|
|
304
|
+
key: 'split-suggestion',
|
|
305
|
+
kind: 'complexity',
|
|
306
|
+
message: 'Spec may benefit from splitting.',
|
|
307
|
+
source: 'heuristic',
|
|
308
|
+
evidence: ['spec-splitter heuristic'],
|
|
309
|
+
confidence: 0.5,
|
|
310
|
+
surface: 'structuredContent',
|
|
311
|
+
value: splitResult,
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
if (input.simplicityResult) {
|
|
315
|
+
advisorySignals.push(makeAdvisorySignal({
|
|
316
|
+
key: 'simplicity-check',
|
|
317
|
+
kind: 'simplicity',
|
|
318
|
+
message: `Simplicity recommendation: ${input.simplicityResult.recommendation}.`,
|
|
319
|
+
source: 'heuristic',
|
|
320
|
+
evidence: input.simplicityResult.signals.map((signal) => signal.type),
|
|
321
|
+
confidence: 0.55,
|
|
322
|
+
surface: 'structuredContent',
|
|
323
|
+
value: input.simplicityResult,
|
|
324
|
+
deprecatedAlias: 'simplicityCheck',
|
|
325
|
+
}));
|
|
326
|
+
}
|
|
327
|
+
const spec = input.spec;
|
|
328
|
+
const riskWarning = spec.risk === 'high' || spec.difficulty >= 4 ? HIGH_RISK_WARNING : undefined;
|
|
329
|
+
if (riskWarning) {
|
|
330
|
+
advisorySignals.push(makeAdvisorySignal({
|
|
331
|
+
key: 'risk-warning',
|
|
332
|
+
kind: 'challenge',
|
|
333
|
+
message: 'High-risk heuristic warning generated.',
|
|
334
|
+
source: 'heuristic',
|
|
335
|
+
evidence: [`risk:${spec.risk}`, `difficulty:${String(spec.difficulty)}`],
|
|
336
|
+
confidence: 0.6,
|
|
337
|
+
surface: 'structuredContent',
|
|
338
|
+
value: riskWarning,
|
|
339
|
+
deprecatedAlias: 'riskWarning',
|
|
340
|
+
}));
|
|
341
|
+
}
|
|
342
|
+
return toolResult(`Spec ${spec.id} created and persisted.`, {
|
|
343
|
+
operationId: input.operationId,
|
|
344
|
+
/** @deprecated Operation-journal durability; this is not Git evidence. */
|
|
345
|
+
committed: true,
|
|
346
|
+
persisted: true,
|
|
347
|
+
gitCommitPerformed: false,
|
|
348
|
+
pendingAnalysis: true,
|
|
349
|
+
specId: spec.id,
|
|
350
|
+
title: spec.title,
|
|
351
|
+
slug: spec.slug,
|
|
352
|
+
type: spec.type,
|
|
353
|
+
scope: spec.scope,
|
|
354
|
+
difficulty: spec.difficulty,
|
|
355
|
+
risk: spec.risk,
|
|
356
|
+
tags: spec.tags,
|
|
357
|
+
target: spec.target,
|
|
358
|
+
status: spec.status,
|
|
359
|
+
gitBranch: spec.gitBranch,
|
|
360
|
+
specPath: spec.specPath,
|
|
361
|
+
...(input.duplicate
|
|
362
|
+
? { duplicateWarning: ti('spec.duplicateTitle', { title: spec.title, slug: spec.slug }) }
|
|
363
|
+
: {}),
|
|
364
|
+
...(input.constitutionWarnings.length > 0
|
|
365
|
+
? { constitutionWarnings: input.constitutionWarnings }
|
|
366
|
+
: {}),
|
|
367
|
+
...(input.clarificationSession
|
|
368
|
+
? {
|
|
369
|
+
linkedClarificationSession: input.clarificationSession.id,
|
|
370
|
+
clarificationTopic: input.clarificationSession.topic,
|
|
371
|
+
clarificationAnswersUsed: Object.keys(input.clarificationSession.answers).length,
|
|
372
|
+
}
|
|
373
|
+
: {
|
|
374
|
+
clarificationNote: 'Spec created directly from the supplied description; no clarification session was linked.',
|
|
375
|
+
}),
|
|
376
|
+
...(input.contradictionHint ? { contradictionHint: input.contradictionHint } : {}),
|
|
377
|
+
...(input.actionableMetrics.length > 0 ? { actionableMetrics: input.actionableMetrics } : {}),
|
|
378
|
+
...(input.simplicityResult ? { simplicityCheck: input.simplicityResult } : {}),
|
|
379
|
+
...(riskWarning ? { riskWarning } : {}),
|
|
380
|
+
...(input.plannerToken ? { plannerToken: input.plannerToken } : {}),
|
|
381
|
+
...(advisorySignals.length > 0 ? { advisorySignals } : {}),
|
|
382
|
+
compat: buildAdvisoryCompat(),
|
|
383
|
+
message: ti('tools.create_spec.success', { id: spec.id, title: spec.title }),
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
const CREATE_SPEC_POLICY = getRuntimePolicy().createSpec;
|
|
387
|
+
const IDEMPOTENCY_CLAIM_STALE_MS = CREATE_SPEC_POLICY.idempotencyClaimStaleMs;
|
|
388
|
+
const IDEMPOTENCY_CLAIM_WAIT_MS = CREATE_SPEC_POLICY.idempotencyClaimWaitMs;
|
|
389
|
+
const IDEMPOTENCY_CLAIM_POLL_MS = CREATE_SPEC_POLICY.idempotencyClaimPollMs;
|
|
390
|
+
const IDEMPOTENCY_MATCH_WINDOW_MS = CREATE_SPEC_POLICY.idempotencyMatchWindowMs;
|
|
322
391
|
function getIdempotencyEvidencePath(projectPath, key) {
|
|
323
392
|
const analysisPath = getAsyncAnalysisPath(projectPath, 'SPEC-000');
|
|
324
393
|
const projectDataPath = pathDirname(pathDirname(analysisPath));
|
|
@@ -438,9 +507,8 @@ async function commitIdempotencyEvidence(projectPath, key, claim, spec, specPath
|
|
|
438
507
|
committedAt,
|
|
439
508
|
};
|
|
440
509
|
try {
|
|
441
|
-
await
|
|
510
|
+
await executionWriteFileExclusive(evidencePath, JSON.stringify(evidence, null, 2), {
|
|
442
511
|
encoding: 'utf-8',
|
|
443
|
-
flag: 'wx',
|
|
444
512
|
});
|
|
445
513
|
}
|
|
446
514
|
catch (error) {
|
|
@@ -464,10 +532,66 @@ async function releaseIdempotencyClaim(projectPath, key, ownerId) {
|
|
|
464
532
|
await quarantineAndDeleteIdempotencyClaim(claimPath, raw);
|
|
465
533
|
}
|
|
466
534
|
}
|
|
535
|
+
async function decideCreateSpecRecovery(entry) {
|
|
536
|
+
const payload = entry.payload;
|
|
537
|
+
if (entry.payloadVersion !== 1 || payload?.operationVersion !== 1) {
|
|
538
|
+
return { action: 'quarantine', reason: 'Unsupported create_spec recovery payload' };
|
|
539
|
+
}
|
|
540
|
+
if (entry.state === 'intent') {
|
|
541
|
+
if (payload.ownerId) {
|
|
542
|
+
await releaseIdempotencyClaim(payload.projectPath, payload.idempotencyKey, payload.ownerId);
|
|
543
|
+
}
|
|
544
|
+
return { action: 'rollback', reason: 'Intent has no prepared durable side effects' };
|
|
545
|
+
}
|
|
546
|
+
if (!payload.projectId ||
|
|
547
|
+
!payload.specId ||
|
|
548
|
+
!payload.specPath ||
|
|
549
|
+
!payload.specContentDigest ||
|
|
550
|
+
!payload.result) {
|
|
551
|
+
return { action: 'quarantine', reason: 'Prepared payload is incomplete' };
|
|
552
|
+
}
|
|
553
|
+
const [content, storedSpec, evidenceRaw] = await Promise.all([
|
|
554
|
+
readFile(payload.specPath, 'utf8').catch(() => ''),
|
|
555
|
+
specStore.getSpec(payload.projectId, payload.specId).catch(() => null),
|
|
556
|
+
readFile(getIdempotencyEvidencePath(payload.projectPath, payload.idempotencyKey), 'utf8').catch(() => ''),
|
|
557
|
+
]);
|
|
558
|
+
const evidence = parseIdempotencyEvidence(evidenceRaw);
|
|
559
|
+
const isCommitted = createHash('sha256').update(content).digest('hex') === payload.specContentDigest &&
|
|
560
|
+
storedSpec?.id === payload.specId &&
|
|
561
|
+
evidence?.specId === payload.specId &&
|
|
562
|
+
(!payload.ownerId || !evidence.ownerId || evidence.ownerId === payload.ownerId);
|
|
563
|
+
if (isCommitted) {
|
|
564
|
+
return {
|
|
565
|
+
action: 'commit',
|
|
566
|
+
result: payload.result,
|
|
567
|
+
outbox: {
|
|
568
|
+
topic: 'spec.created',
|
|
569
|
+
payload: {
|
|
570
|
+
schemaVersion: 1,
|
|
571
|
+
specId: payload.specId,
|
|
572
|
+
projectId: payload.projectId,
|
|
573
|
+
projectPath: payload.projectPath,
|
|
574
|
+
postCommitTasks: SPEC_CREATED_POST_COMMIT_TASKS,
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
const noDurableArtifacts = content.length === 0 && storedSpec === null && evidence === undefined;
|
|
580
|
+
if (noDurableArtifacts) {
|
|
581
|
+
if (payload.ownerId) {
|
|
582
|
+
await releaseIdempotencyClaim(payload.projectPath, payload.idempotencyKey, payload.ownerId);
|
|
583
|
+
}
|
|
584
|
+
return { action: 'rollback', reason: 'Prepared mutation has no durable artifacts' };
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
action: 'quarantine',
|
|
588
|
+
reason: 'Prepared create_spec artifacts are partial or inconsistent',
|
|
589
|
+
};
|
|
590
|
+
}
|
|
467
591
|
async function quarantineAndDeleteIdempotencyClaim(claimPath, expectedRaw) {
|
|
468
592
|
const quarantinePath = `${claimPath}.${randomUUID()}.quarantine`;
|
|
469
593
|
try {
|
|
470
|
-
await
|
|
594
|
+
await executionRename(claimPath, quarantinePath);
|
|
471
595
|
}
|
|
472
596
|
catch (error) {
|
|
473
597
|
if (error.code === 'ENOENT') {
|
|
@@ -477,18 +601,18 @@ async function quarantineAndDeleteIdempotencyClaim(claimPath, expectedRaw) {
|
|
|
477
601
|
}
|
|
478
602
|
const quarantinedRaw = await readFile(quarantinePath, 'utf-8').catch(() => '');
|
|
479
603
|
if (quarantinedRaw === expectedRaw) {
|
|
480
|
-
await
|
|
604
|
+
await executionRemove(quarantinePath, { force: true });
|
|
481
605
|
return true;
|
|
482
606
|
}
|
|
483
607
|
try {
|
|
484
|
-
await
|
|
608
|
+
await executionLink(quarantinePath, claimPath);
|
|
485
609
|
}
|
|
486
610
|
catch (error) {
|
|
487
611
|
if (error.code !== 'EEXIST') {
|
|
488
612
|
throw error;
|
|
489
613
|
}
|
|
490
614
|
}
|
|
491
|
-
await
|
|
615
|
+
await executionRemove(quarantinePath, { force: true });
|
|
492
616
|
return false;
|
|
493
617
|
}
|
|
494
618
|
async function waitForIdempotencyOwner(projectPath, key, claim) {
|
|
@@ -514,7 +638,7 @@ async function waitForIdempotencyOwner(projectPath, key, claim) {
|
|
|
514
638
|
}
|
|
515
639
|
async function acquireIdempotencyClaim(projectPath, key, title) {
|
|
516
640
|
const claimPath = getIdempotencyClaimPath(projectPath, key);
|
|
517
|
-
await
|
|
641
|
+
await executionMkdir(pathDirname(claimPath), { recursive: true });
|
|
518
642
|
for (let attempt = 0; attempt < 4; attempt += 1) {
|
|
519
643
|
const claim = {
|
|
520
644
|
version: 2,
|
|
@@ -525,9 +649,8 @@ async function acquireIdempotencyClaim(projectPath, key, title) {
|
|
|
525
649
|
claimedAt: new Date().toISOString(),
|
|
526
650
|
};
|
|
527
651
|
try {
|
|
528
|
-
await
|
|
652
|
+
await executionWriteFileExclusive(claimPath, JSON.stringify(claim, null, 2), {
|
|
529
653
|
encoding: 'utf-8',
|
|
530
|
-
flag: 'wx',
|
|
531
654
|
});
|
|
532
655
|
const racedCommit = await findByIdempotencyEvidence(projectPath, key, Date.now() - IDEMPOTENCY_MATCH_WINDOW_MS);
|
|
533
656
|
if (racedCommit !== undefined) {
|
|
@@ -536,7 +659,7 @@ async function acquireIdempotencyClaim(projectPath, key, title) {
|
|
|
536
659
|
}
|
|
537
660
|
const expiredEvidencePath = getIdempotencyEvidencePath(projectPath, key);
|
|
538
661
|
if ((await fsStat(expiredEvidencePath).catch(() => null)) !== null) {
|
|
539
|
-
await
|
|
662
|
+
await executionRemove(expiredEvidencePath, { force: true });
|
|
540
663
|
}
|
|
541
664
|
return { kind: 'owner', claim };
|
|
542
665
|
}
|
|
@@ -600,28 +723,27 @@ async function buildIdempotencyMatchResult(projectPath, existing) {
|
|
|
600
723
|
};
|
|
601
724
|
}
|
|
602
725
|
const FRONTMATTER_ID_RE = /^id:\s*(SPEC-\d+)/m;
|
|
603
|
-
const FRONTMATTER_TITLE_RE = /^title:\s*"?([^"\n]+?)"?\s*$/m;
|
|
604
726
|
const FRONTMATTER_STATUS_RE = /^status:\s*(\S+)/m;
|
|
605
|
-
|
|
606
|
-
async function findByIdempotencyKey(projectPath, key, projectId, windowMs = IDEMPOTENCY_MATCH_WINDOW_MS) {
|
|
607
|
-
const cutoff = Date.now() - windowMs;
|
|
608
|
-
// Fast path: check store (always authoritative for successfully created specs)
|
|
727
|
+
async function findStoreByIdempotencyKey(projectId, key, cutoff) {
|
|
609
728
|
try {
|
|
610
729
|
const existing = await specStore.listSpecs(projectId);
|
|
611
|
-
|
|
612
|
-
if (match !== undefined) {
|
|
613
|
-
return match;
|
|
614
|
-
}
|
|
730
|
+
return existing.find((s) => s.idempotencyKey === key && new Date(s.createdAt).getTime() > cutoff);
|
|
615
731
|
}
|
|
616
732
|
catch {
|
|
617
|
-
|
|
733
|
+
return undefined;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
/** SPEC-770: Scan store and filesystem for a spec with matching idempotency key within windowMs. */
|
|
737
|
+
async function findByIdempotencyKey(projectPath, key, projectId, windowMs = IDEMPOTENCY_MATCH_WINDOW_MS) {
|
|
738
|
+
const cutoff = Date.now() - windowMs;
|
|
739
|
+
const storeMatch = await findStoreByIdempotencyKey(projectId, key, cutoff);
|
|
740
|
+
if (storeMatch) {
|
|
741
|
+
return storeMatch;
|
|
618
742
|
}
|
|
619
|
-
// Transactional fallback: the request key stays in external project data, not spec.md.
|
|
620
743
|
const evidenceMatch = await findByIdempotencyEvidence(projectPath, key, cutoff);
|
|
621
744
|
if (evidenceMatch !== undefined) {
|
|
622
745
|
return evidenceMatch;
|
|
623
746
|
}
|
|
624
|
-
// Compatibility fallback for specs created before external evidence was introduced.
|
|
625
747
|
try {
|
|
626
748
|
const { glob } = await import('glob');
|
|
627
749
|
const { join: joinPath } = await import('node:path');
|
|
@@ -651,7 +773,7 @@ async function findByIdempotencyKey(projectPath, key, projectId, windowMs = IDEM
|
|
|
651
773
|
return undefined;
|
|
652
774
|
}
|
|
653
775
|
/** SPEC-770: Scan for recently written spec.md files (for possibleDuplicates in timeout response). */
|
|
654
|
-
async function findRecentlyWrittenSpecs(projectPath, windowMs) {
|
|
776
|
+
async function findRecentlyWrittenSpecs(projectPath, windowMs, requestedTitle) {
|
|
655
777
|
try {
|
|
656
778
|
const { glob } = await import('glob');
|
|
657
779
|
const { join: joinPath } = await import('node:path');
|
|
@@ -665,9 +787,11 @@ async function findRecentlyWrittenSpecs(projectPath, windowMs) {
|
|
|
665
787
|
}
|
|
666
788
|
const content = await readFile(specFile, 'utf-8').catch(() => '');
|
|
667
789
|
const idMatch = FRONTMATTER_ID_RE.exec(content);
|
|
668
|
-
const
|
|
669
|
-
if (idMatch?.[1] !== undefined
|
|
670
|
-
|
|
790
|
+
const title = extractFrontmatterTitle(content);
|
|
791
|
+
if (idMatch?.[1] !== undefined &&
|
|
792
|
+
normalizeDuplicateTitle(title) !== '' &&
|
|
793
|
+
normalizeDuplicateTitle(title) === normalizeDuplicateTitle(requestedTitle)) {
|
|
794
|
+
results.push({ id: idMatch[1], title });
|
|
671
795
|
}
|
|
672
796
|
}
|
|
673
797
|
return results;
|
|
@@ -676,6 +800,69 @@ async function findRecentlyWrittenSpecs(projectPath, windowMs) {
|
|
|
676
800
|
return [];
|
|
677
801
|
}
|
|
678
802
|
}
|
|
803
|
+
function extractFrontmatterTitle(content) {
|
|
804
|
+
const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(content)?.[1];
|
|
805
|
+
const rawTitle = frontmatter ? /^title:\s*(.*?)\s*$/m.exec(frontmatter)?.[1] : undefined;
|
|
806
|
+
if (!rawTitle) {
|
|
807
|
+
return '';
|
|
808
|
+
}
|
|
809
|
+
if (rawTitle.startsWith('"') && rawTitle.endsWith('"')) {
|
|
810
|
+
try {
|
|
811
|
+
const parsed = JSON.parse(rawTitle);
|
|
812
|
+
return typeof parsed === 'string' ? parsed : '';
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
return '';
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (rawTitle.startsWith("'") && rawTitle.endsWith("'")) {
|
|
819
|
+
return rawTitle.slice(1, -1).replace(/''/g, "'");
|
|
820
|
+
}
|
|
821
|
+
return rawTitle;
|
|
822
|
+
}
|
|
823
|
+
function normalizeDuplicateTitle(value) {
|
|
824
|
+
return value
|
|
825
|
+
.normalize('NFKC')
|
|
826
|
+
.toLocaleLowerCase('en-US')
|
|
827
|
+
.replace(/[\p{P}]+/gu, ' ')
|
|
828
|
+
.replace(/\s+/g, ' ')
|
|
829
|
+
.trim();
|
|
830
|
+
}
|
|
831
|
+
const CREATE_SPEC_ENUMS = {
|
|
832
|
+
type: SpecTypeEnum,
|
|
833
|
+
scope: SpecScopeEnum,
|
|
834
|
+
target: SpecTargetEnum,
|
|
835
|
+
acFormat: SpecAcFormatEnum,
|
|
836
|
+
};
|
|
837
|
+
function validateCreateSpecEnums(input) {
|
|
838
|
+
for (const [field, schema] of Object.entries(CREATE_SPEC_ENUMS)) {
|
|
839
|
+
const received = input[field];
|
|
840
|
+
if (received === undefined || schema.safeParse(received).success) {
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
const boundedReceived = received === null || ['string', 'number', 'boolean'].includes(typeof received)
|
|
844
|
+
? received
|
|
845
|
+
: typeof received;
|
|
846
|
+
return {
|
|
847
|
+
content: [
|
|
848
|
+
{
|
|
849
|
+
type: 'text',
|
|
850
|
+
text: `${field} must be one of: ${schema.options.join(', ')}.`,
|
|
851
|
+
},
|
|
852
|
+
],
|
|
853
|
+
isError: true,
|
|
854
|
+
structuredContent: {
|
|
855
|
+
error: 'INVALID_INPUT',
|
|
856
|
+
code: 'INVALID_INPUT',
|
|
857
|
+
status: 422,
|
|
858
|
+
field,
|
|
859
|
+
received: boundedReceived,
|
|
860
|
+
allowed: schema.options,
|
|
861
|
+
},
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
679
866
|
const DESCRIPTION_MAX_CHARS = 10_000;
|
|
680
867
|
const DESCRIPTION_EXCEED_MSG = 'Description exceeds 10000 chars. Create with a summary (<3k) and use reconcile_spec to add the rest.';
|
|
681
868
|
function checkEnglishOnlyInput(params) {
|
|
@@ -700,8 +887,273 @@ function checkEnglishOnlyInput(params) {
|
|
|
700
887
|
},
|
|
701
888
|
};
|
|
702
889
|
}
|
|
890
|
+
function quotePosixShellArgument(value) {
|
|
891
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
892
|
+
}
|
|
893
|
+
function earlyPreparation(result, clarificationProjectId) {
|
|
894
|
+
return {
|
|
895
|
+
kind: 'early',
|
|
896
|
+
result,
|
|
897
|
+
...(clarificationProjectId ? { clarificationProjectId } : {}),
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
export async function resolveGroundedVerificationCommands(projectPath, testPaths) {
|
|
901
|
+
if (testPaths.length === 0) {
|
|
902
|
+
return [];
|
|
903
|
+
}
|
|
904
|
+
const packageJsonRaw = await readFile(pathJoin(projectPath, 'package.json'), 'utf8').catch(() => '');
|
|
905
|
+
if (!packageJsonRaw) {
|
|
906
|
+
return [];
|
|
907
|
+
}
|
|
908
|
+
try {
|
|
909
|
+
const manifest = JSON.parse(packageJsonRaw);
|
|
910
|
+
if (typeof manifest.packageManager !== 'string') {
|
|
911
|
+
return [];
|
|
912
|
+
}
|
|
913
|
+
const packageManager = /^(pnpm|npm|yarn|bun)@\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.exec(manifest.packageManager)?.[1];
|
|
914
|
+
if (!packageManager) {
|
|
915
|
+
return [];
|
|
916
|
+
}
|
|
917
|
+
if (typeof manifest.scripts?.test !== 'string' || manifest.scripts.test.trim() === '') {
|
|
918
|
+
return [];
|
|
919
|
+
}
|
|
920
|
+
return [`${packageManager} run test -- ${testPaths.map(quotePosixShellArgument).join(' ')}`];
|
|
921
|
+
}
|
|
922
|
+
catch {
|
|
923
|
+
return [];
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
async function prepareCreateSpecCandidate(initialParams, server) {
|
|
927
|
+
let params = initialParams;
|
|
928
|
+
const buildResult = await measureStep('buildSpecContext', () => buildSpecContext(params));
|
|
929
|
+
if (!buildResult.ok) {
|
|
930
|
+
return earlyPreparation({
|
|
931
|
+
content: [{ type: 'text', text: buildResult.errorMessage }],
|
|
932
|
+
isError: true,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
const context = buildResult.context;
|
|
936
|
+
const { spec, estimation, projectId } = context;
|
|
937
|
+
const knowledge = await measureStep('loadKnowledge', () => knowledgeStore.getKnowledge(projectId));
|
|
938
|
+
const clarificationSession = params.clarificationSessionId
|
|
939
|
+
? await knowledgeStore.getClarification(projectId, params.clarificationSessionId)
|
|
940
|
+
: null;
|
|
941
|
+
const constitutionCheck = await measureStep('validateConstitution', () => validateConstitution(projectId, spec.title, spec.tags));
|
|
942
|
+
if (constitutionCheck.blocked && constitutionCheck.errorResult) {
|
|
943
|
+
return earlyPreparation(constitutionCheck.errorResult);
|
|
944
|
+
}
|
|
945
|
+
const inlineAnalysis = await withBudget('create-spec-inline-analysis', getRuntimePolicy().createSpec.inlineAnalysisBudgetMs, (signal) => analyzeProjectForSpec(params.projectPath ?? '', params.description, spec.title, knowledge, signal));
|
|
946
|
+
const autopilot = unwrapBudget(inlineAnalysis, getEmptyAutopilotResult());
|
|
947
|
+
if (autopilot.isIdea) {
|
|
948
|
+
return earlyPreparation({
|
|
949
|
+
content: [
|
|
950
|
+
{
|
|
951
|
+
type: 'text',
|
|
952
|
+
text: `💡 **Idea captured**: "${params.description}"\n\nThe description is too brief for a full spec. Use \`capture_idea\` to save it to the backlog, or provide more detail (>10 words) to create a spec.`,
|
|
953
|
+
},
|
|
954
|
+
],
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
const clarificationResult = handleClarification(server, params.description, knowledge, autopilot, params);
|
|
958
|
+
if (clarificationResult !== null) {
|
|
959
|
+
if ('earlyReturn' in clarificationResult) {
|
|
960
|
+
return earlyPreparation(clarificationResult.earlyReturn, projectId);
|
|
961
|
+
}
|
|
962
|
+
params = clarificationResult.params;
|
|
963
|
+
}
|
|
964
|
+
const filteredCriteria = filterGroundedCriteria(autopilot.suggestedCriteria);
|
|
965
|
+
const technologyContract = await readTechnologySelectionContract(params.projectPath ?? '');
|
|
966
|
+
const contractNote = technologyContract
|
|
967
|
+
? [
|
|
968
|
+
'',
|
|
969
|
+
'Technology Contract:',
|
|
970
|
+
`- Mode: ${technologyContract.mode}`,
|
|
971
|
+
technologyContract.language ? `- Language: ${technologyContract.language}` : '',
|
|
972
|
+
technologyContract.framework
|
|
973
|
+
? `- Framework: ${technologyContract.framework}`
|
|
974
|
+
: '- Framework: none or not selected',
|
|
975
|
+
technologyContract.platform ? `- Platform: ${technologyContract.platform}` : '',
|
|
976
|
+
technologyContract.projectType ? `- Project type: ${technologyContract.projectType}` : '',
|
|
977
|
+
'- Agents must not choose a different stack unless the user approves a new contract.',
|
|
978
|
+
]
|
|
979
|
+
.filter(Boolean)
|
|
980
|
+
.join('\n')
|
|
981
|
+
: '';
|
|
982
|
+
const generatedSpec = await measureStep('generateSpecBody', () => new FallbackGenerator().generate({
|
|
983
|
+
title: spec.title,
|
|
984
|
+
description: `${params.description}${contractNote}`,
|
|
985
|
+
type: spec.type,
|
|
986
|
+
scope: spec.scope,
|
|
987
|
+
target: spec.target,
|
|
988
|
+
acFormat: params.acFormat,
|
|
989
|
+
projectContext: {
|
|
990
|
+
language: technologyContract?.language ?? knowledge?.language ?? undefined,
|
|
991
|
+
framework: technologyContract?.framework ?? knowledge?.framework ?? undefined,
|
|
992
|
+
architecture: knowledge?.architecture.primary ?? undefined,
|
|
993
|
+
},
|
|
994
|
+
}));
|
|
995
|
+
spec.generation = generatedSpec.generation;
|
|
996
|
+
spec.qualityWarnings =
|
|
997
|
+
generatedSpec.qualityWarnings.length > 0 ? generatedSpec.qualityWarnings : undefined;
|
|
998
|
+
const baseCriteria = extractCriteria(generatedSpec.specBody).map((criterion) => criterion.text);
|
|
999
|
+
const groundingCriteria = buildCriterionGroundingRecords({
|
|
1000
|
+
criteria: [...baseCriteria, ...filteredCriteria],
|
|
1001
|
+
userInput: params.description,
|
|
1002
|
+
generatedEvidence: [generatedSpec.generation.modelId ?? generatedSpec.generation.method],
|
|
1003
|
+
});
|
|
1004
|
+
const contractCriteria = getContractCriteria(groundingCriteria);
|
|
1005
|
+
const advisoryCriteria = getAdvisoryCriteria(groundingCriteria).map((record) => record.text);
|
|
1006
|
+
if (contractCriteria.length === 0) {
|
|
1007
|
+
return earlyPreparation({
|
|
1008
|
+
content: [
|
|
1009
|
+
{
|
|
1010
|
+
type: 'text',
|
|
1011
|
+
text: 'create_spec did not persist a spec because no acceptance criterion was grounded in the user request. ' +
|
|
1012
|
+
'Needs decision: provide at least one explicit, verifiable criterion as a checkbox or Given/When/Then outcome.',
|
|
1013
|
+
},
|
|
1014
|
+
],
|
|
1015
|
+
isError: true,
|
|
1016
|
+
structuredContent: {
|
|
1017
|
+
error: 'MISSING_GROUNDED_ACCEPTANCE_CRITERIA',
|
|
1018
|
+
missingDecision: 'Provide at least one explicit, verifiable acceptance criterion.',
|
|
1019
|
+
persisted: false,
|
|
1020
|
+
},
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
const actionableMetrics = calculateActionableSpecMetrics({
|
|
1024
|
+
criteria: [...baseCriteria, ...filteredCriteria],
|
|
1025
|
+
groundingRecords: groundingCriteria,
|
|
1026
|
+
}).filter(shouldExposeMetric);
|
|
1027
|
+
const technicalFiles = await measureStep('resolveTechnicalFiles', () => resolveTechnicalFiles({
|
|
1028
|
+
generatedSpecBody: generatedSpec.specBody,
|
|
1029
|
+
generatedTechnicalSection: generatedSpec.technicalSection,
|
|
1030
|
+
projectPath: params.projectPath ?? '',
|
|
1031
|
+
}));
|
|
1032
|
+
const groundedTechnical = await measureStep('groundTechnicalFiles', () => groundTechnicalFiles({
|
|
1033
|
+
files: technicalFiles,
|
|
1034
|
+
projectPath: params.projectPath ?? '',
|
|
1035
|
+
userInput: params.description,
|
|
1036
|
+
autopilot,
|
|
1037
|
+
}));
|
|
1038
|
+
const outOfScope = resolveOutOfScope(params.outOfScope, params.description);
|
|
1039
|
+
const scenarioTestPaths = groundedTechnical.files.test.map((file) => file.path);
|
|
1040
|
+
const criteria = contractCriteria.map((record) => ({ text: record.text, done: false }));
|
|
1041
|
+
const leanSpec = generateLeanSpecContent({
|
|
1042
|
+
spec,
|
|
1043
|
+
description: generatedSpec.specBody,
|
|
1044
|
+
estimation,
|
|
1045
|
+
criteriaOverride: criteria,
|
|
1046
|
+
groundingCriteria: contractCriteria,
|
|
1047
|
+
groundingTechnicalReferences: groundedTechnical.records,
|
|
1048
|
+
acFormat: params.acFormat,
|
|
1049
|
+
scenarioTestPaths,
|
|
1050
|
+
});
|
|
1051
|
+
const leanTechnical = generateLeanTechnicalContent({
|
|
1052
|
+
specId: spec.id,
|
|
1053
|
+
filesToCreate: groundedTechnical.files.create,
|
|
1054
|
+
filesToModify: groundedTechnical.files.modify,
|
|
1055
|
+
filesToTest: groundedTechnical.files.test,
|
|
1056
|
+
includeDecisionRequiredPlaceholder: spec.scope !== 'trivial',
|
|
1057
|
+
});
|
|
1058
|
+
const verificationCommands = await resolveGroundedVerificationCommands(params.projectPath ?? '', scenarioTestPaths);
|
|
1059
|
+
const implementationContract = buildImplementationContractSection({
|
|
1060
|
+
description: generatedSpec.specBody,
|
|
1061
|
+
criteria,
|
|
1062
|
+
files: groundedTechnical.files,
|
|
1063
|
+
outOfScope,
|
|
1064
|
+
verificationCommands,
|
|
1065
|
+
});
|
|
1066
|
+
const unifiedSpec = buildCanonicalUnifiedSpecContent({
|
|
1067
|
+
leanSpecBody: leanSpec,
|
|
1068
|
+
sourceDescription: params.description,
|
|
1069
|
+
technicalBody: leanTechnical,
|
|
1070
|
+
implementationContract,
|
|
1071
|
+
criteria,
|
|
1072
|
+
files: groundedTechnical.files,
|
|
1073
|
+
outOfScope,
|
|
1074
|
+
});
|
|
1075
|
+
const candidateValidation = validateUnifiedSpecCandidate(unifiedSpec, {
|
|
1076
|
+
groundedFilePaths: groundedTechnical.records.map((record) => record.path),
|
|
1077
|
+
});
|
|
1078
|
+
if (!candidateValidation.valid) {
|
|
1079
|
+
return earlyPreparation({
|
|
1080
|
+
content: [
|
|
1081
|
+
{
|
|
1082
|
+
type: 'text',
|
|
1083
|
+
text: `Unified spec candidate is invalid: ${candidateValidation.issues
|
|
1084
|
+
.slice(0, 3)
|
|
1085
|
+
.map((issue) => issue.message)
|
|
1086
|
+
.join('; ')}`,
|
|
1087
|
+
},
|
|
1088
|
+
],
|
|
1089
|
+
isError: true,
|
|
1090
|
+
structuredContent: {
|
|
1091
|
+
error: 'SPEC_FORMAT_INVALID',
|
|
1092
|
+
code: 422,
|
|
1093
|
+
persisted: false,
|
|
1094
|
+
issues: candidateValidation.issues,
|
|
1095
|
+
},
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
const genericOutputGate = checkGenericSpecOutput(unifiedSpec);
|
|
1099
|
+
if (!genericOutputGate.passed) {
|
|
1100
|
+
return earlyPreparation({
|
|
1101
|
+
content: [
|
|
1102
|
+
{
|
|
1103
|
+
type: 'text',
|
|
1104
|
+
text: 'Spec quality gate blocked generic output before persistence. ' +
|
|
1105
|
+
genericOutputGate.issues
|
|
1106
|
+
.slice(0, 3)
|
|
1107
|
+
.map((issue) => `${issue.phrase}: ${issue.reason}`)
|
|
1108
|
+
.join('; '),
|
|
1109
|
+
},
|
|
1110
|
+
],
|
|
1111
|
+
isError: true,
|
|
1112
|
+
structuredContent: {
|
|
1113
|
+
error: 'GENERIC_SPEC_OUTPUT_BLOCKED',
|
|
1114
|
+
sddConstitutionRuleId: 'sdd.no-generic-output',
|
|
1115
|
+
issues: genericOutputGate.issues,
|
|
1116
|
+
fixHint: 'Replace generic criteria or placeholder references with grounded, testable behavior.',
|
|
1117
|
+
},
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
if (outOfScope.length > 0) {
|
|
1121
|
+
spec.outOfScope = outOfScope;
|
|
1122
|
+
}
|
|
1123
|
+
return {
|
|
1124
|
+
kind: 'ready',
|
|
1125
|
+
prepared: {
|
|
1126
|
+
params,
|
|
1127
|
+
context,
|
|
1128
|
+
knowledge,
|
|
1129
|
+
clarificationSession,
|
|
1130
|
+
constitutionWarnings: constitutionCheck.warnings,
|
|
1131
|
+
advisoryCriteria,
|
|
1132
|
+
actionableMetrics,
|
|
1133
|
+
unifiedSpec,
|
|
1134
|
+
simplicityResult: runSimplicityCheck([params.description, ...filteredCriteria].join('\n'), estimation.devHours),
|
|
1135
|
+
},
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
703
1138
|
// eslint-disable-next-line max-lines-per-function
|
|
704
1139
|
export async function handleCreateSpec(inputParams, server) {
|
|
1140
|
+
const enumValidation = validateCreateSpecEnums(inputParams);
|
|
1141
|
+
if (enumValidation) {
|
|
1142
|
+
return enumValidation;
|
|
1143
|
+
}
|
|
1144
|
+
if (inputParams.idempotencyKey !== undefined &&
|
|
1145
|
+
!/^[A-Za-z0-9._:-]{8,128}$/.test(inputParams.idempotencyKey)) {
|
|
1146
|
+
return {
|
|
1147
|
+
content: [
|
|
1148
|
+
{
|
|
1149
|
+
type: 'text',
|
|
1150
|
+
text: 'idempotencyKey must contain 8-128 letters, digits, dots, underscores, colons or hyphens.',
|
|
1151
|
+
},
|
|
1152
|
+
],
|
|
1153
|
+
isError: true,
|
|
1154
|
+
structuredContent: { error: 'INVALID_IDEMPOTENCY_KEY', code: 422 },
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
705
1157
|
// SPEC-781: Friendly error for description exceeding 10000 chars (replaces cryptic Zod error)
|
|
706
1158
|
if (inputParams.description && inputParams.description.length > DESCRIPTION_MAX_CHARS) {
|
|
707
1159
|
return {
|
|
@@ -746,28 +1198,157 @@ export async function handleCreateSpec(inputParams, server) {
|
|
|
746
1198
|
if (languageGate) {
|
|
747
1199
|
return languageGate;
|
|
748
1200
|
}
|
|
1201
|
+
// SPEC-584: Gate check — block if pending clarification token exists and no answers provided
|
|
1202
|
+
const gateError = await runClarificationGate(resolvedPath, 'create_spec', inputParams.clarificationAnswers);
|
|
1203
|
+
if (gateError) {
|
|
1204
|
+
return gateError;
|
|
1205
|
+
}
|
|
1206
|
+
const idempotencyKey = computeIdempotencyKey(inputParams.title, resolvedPath, inputParams.idempotencyKey);
|
|
1207
|
+
const cutoff = Date.now() - IDEMPOTENCY_MATCH_WINDOW_MS;
|
|
1208
|
+
const storeOnlyCandidate = await findStoreByIdempotencyKey(hashProjectPath(resolvedPath), idempotencyKey, cutoff);
|
|
1209
|
+
let preparation;
|
|
1210
|
+
try {
|
|
1211
|
+
preparation = await prepareCreateSpecCandidate(resolvedInputParams, server);
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1215
|
+
return {
|
|
1216
|
+
content: [{ type: 'text', text: ti('errors.internalError', { message }) }],
|
|
1217
|
+
isError: true,
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
if (preparation.kind === 'early') {
|
|
1221
|
+
if (preparation.clarificationProjectId) {
|
|
1222
|
+
await persistClarificationToken(preparation.result, preparation.clarificationProjectId, 'create_spec');
|
|
1223
|
+
}
|
|
1224
|
+
return preparation.result;
|
|
1225
|
+
}
|
|
1226
|
+
const prepared = preparation.prepared;
|
|
1227
|
+
prepared.context.spec.idempotencyKey = idempotencyKey;
|
|
1228
|
+
if (storeOnlyCandidate) {
|
|
1229
|
+
const committedEvidence = await findByIdempotencyEvidence(resolvedPath, idempotencyKey, cutoff);
|
|
1230
|
+
if (!committedEvidence) {
|
|
1231
|
+
return {
|
|
1232
|
+
content: [
|
|
1233
|
+
{
|
|
1234
|
+
type: 'text',
|
|
1235
|
+
text: `A partial create_spec transaction exists for ${storeOnlyCandidate.id}: the store row has no committed idempotency evidence. Recovery is required before retrying.`,
|
|
1236
|
+
},
|
|
1237
|
+
],
|
|
1238
|
+
isError: true,
|
|
1239
|
+
structuredContent: {
|
|
1240
|
+
error: 'PARTIAL_IDEMPOTENCY_STATE',
|
|
1241
|
+
code: 409,
|
|
1242
|
+
persisted: false,
|
|
1243
|
+
specId: storeOnlyCandidate.id,
|
|
1244
|
+
},
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
749
1248
|
// SPEC-665: Fire-and-forget TTL refresh for conventions.json (never blocks create_spec)
|
|
750
1249
|
void import('../engine/conventions-ttl.js').then(({ triggerConventionsTtlRefresh }) => {
|
|
751
1250
|
void triggerConventionsTtlRefresh(resolvedPath).catch(() => {
|
|
752
1251
|
/* best-effort */
|
|
753
1252
|
});
|
|
754
1253
|
});
|
|
755
|
-
// SPEC-584: Gate check — block if pending clarification token exists and no answers provided
|
|
756
|
-
const gateError = await runClarificationGate(resolvedPath, 'create_spec', inputParams.clarificationAnswers);
|
|
757
|
-
if (gateError) {
|
|
758
|
-
return gateError;
|
|
759
|
-
}
|
|
760
1254
|
// SPEC-770: Idempotency check — return existing spec if same title+projectPath within 10 minutes
|
|
761
|
-
const
|
|
1255
|
+
const operationKey = computeOperationKey(resolvedInputParams, resolvedPath, idempotencyKey);
|
|
1256
|
+
const runtimeDatabase = new RuntimeDatabase({ path: resolveStorageLayout().runtimeDatabase });
|
|
1257
|
+
const operationJournal = new OperationJournal(runtimeDatabase, createHash('sha256').update(hashProjectPath(resolvedPath)).digest('hex').slice(0, 32));
|
|
1258
|
+
let journalEntry;
|
|
1259
|
+
try {
|
|
1260
|
+
await recoverOperationJournal({
|
|
1261
|
+
journal: operationJournal,
|
|
1262
|
+
operation: 'create_spec',
|
|
1263
|
+
staleBefore: new Date(Date.now() - IDEMPOTENCY_CLAIM_STALE_MS).toISOString(),
|
|
1264
|
+
decide: decideCreateSpecRecovery,
|
|
1265
|
+
});
|
|
1266
|
+
journalEntry = operationJournal.begin('create_spec', operationKey, inputParams.idempotencyKey
|
|
1267
|
+
? createSpecRequestDigest(resolvedInputParams, resolvedPath)
|
|
1268
|
+
: operationKey, {
|
|
1269
|
+
payloadVersion: 1,
|
|
1270
|
+
payload: {
|
|
1271
|
+
operationVersion: 1,
|
|
1272
|
+
projectPath: resolvedPath,
|
|
1273
|
+
idempotencyKey,
|
|
1274
|
+
},
|
|
1275
|
+
});
|
|
1276
|
+
if (journalEntry.state === 'rolled-back') {
|
|
1277
|
+
journalEntry = operationJournal.restartRolledBack('create_spec', operationKey);
|
|
1278
|
+
journalEntry = operationJournal.setRecoveryPayload('create_spec', operationKey, 1, {
|
|
1279
|
+
operationVersion: 1,
|
|
1280
|
+
projectPath: resolvedPath,
|
|
1281
|
+
idempotencyKey,
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
catch (error) {
|
|
1286
|
+
runtimeDatabase.close();
|
|
1287
|
+
if (error instanceof BoundaryFailure &&
|
|
1288
|
+
error.kind === 'Conflict' &&
|
|
1289
|
+
error.operation === 'operation begin') {
|
|
1290
|
+
return {
|
|
1291
|
+
content: [
|
|
1292
|
+
{
|
|
1293
|
+
type: 'text',
|
|
1294
|
+
text: 'The idempotency key is already bound to a different create_spec request.',
|
|
1295
|
+
},
|
|
1296
|
+
],
|
|
1297
|
+
isError: true,
|
|
1298
|
+
structuredContent: {
|
|
1299
|
+
error: 'IDEMPOTENCY_CONFLICT',
|
|
1300
|
+
code: 'IDEMPOTENCY_CONFLICT',
|
|
1301
|
+
status: 409,
|
|
1302
|
+
persisted: false,
|
|
1303
|
+
},
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
throw error;
|
|
1307
|
+
}
|
|
1308
|
+
if ((journalEntry.state === 'committed' || journalEntry.state === 'acknowledged') &&
|
|
1309
|
+
journalEntry.result) {
|
|
1310
|
+
if (journalEntry.state === 'committed') {
|
|
1311
|
+
operationJournal.acknowledged('create_spec', operationKey);
|
|
1312
|
+
}
|
|
1313
|
+
runtimeDatabase.close();
|
|
1314
|
+
return journalEntry.result;
|
|
1315
|
+
}
|
|
1316
|
+
if (journalEntry.state === 'quarantined') {
|
|
1317
|
+
runtimeDatabase.close();
|
|
1318
|
+
return {
|
|
1319
|
+
content: [
|
|
1320
|
+
{
|
|
1321
|
+
type: 'text',
|
|
1322
|
+
text: `The previous create_spec attempt is quarantined: ${journalEntry.recoveryReason ?? 'artifact mismatch'}.`,
|
|
1323
|
+
},
|
|
1324
|
+
],
|
|
1325
|
+
isError: true,
|
|
1326
|
+
structuredContent: { recoveryQuarantined: true, operationId: operationKey },
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
762
1329
|
const existingByKey = await findByIdempotencyKey(resolvedPath, idempotencyKey, hashProjectPath(resolvedPath));
|
|
763
1330
|
if (existingByKey) {
|
|
764
|
-
|
|
1331
|
+
const result = await buildIdempotencyMatchResult(resolvedPath, existingByKey);
|
|
1332
|
+
finishRecoveredOperation(operationJournal, operationKey, result);
|
|
1333
|
+
runtimeDatabase.close();
|
|
1334
|
+
return result;
|
|
1335
|
+
}
|
|
1336
|
+
let claimResolution;
|
|
1337
|
+
try {
|
|
1338
|
+
claimResolution = await acquireIdempotencyClaim(resolvedPath, idempotencyKey, inputParams.title);
|
|
1339
|
+
}
|
|
1340
|
+
catch (error) {
|
|
1341
|
+
runtimeDatabase.close();
|
|
1342
|
+
throw error;
|
|
765
1343
|
}
|
|
766
|
-
const claimResolution = await acquireIdempotencyClaim(resolvedPath, idempotencyKey, inputParams.title);
|
|
767
1344
|
if (claimResolution.kind === 'existing') {
|
|
768
|
-
|
|
1345
|
+
const result = await buildIdempotencyMatchResult(resolvedPath, claimResolution.spec);
|
|
1346
|
+
finishRecoveredOperation(operationJournal, operationKey, result);
|
|
1347
|
+
runtimeDatabase.close();
|
|
1348
|
+
return result;
|
|
769
1349
|
}
|
|
770
1350
|
if (claimResolution.kind === 'pending') {
|
|
1351
|
+
runtimeDatabase.close();
|
|
771
1352
|
return {
|
|
772
1353
|
content: [
|
|
773
1354
|
{
|
|
@@ -785,254 +1366,101 @@ export async function handleCreateSpec(inputParams, server) {
|
|
|
785
1366
|
retainForInFlightWork: false,
|
|
786
1367
|
};
|
|
787
1368
|
try {
|
|
788
|
-
|
|
1369
|
+
operationJournal.setRecoveryPayload('create_spec', operationKey, 1, {
|
|
1370
|
+
operationVersion: 1,
|
|
1371
|
+
projectPath: resolvedPath,
|
|
1372
|
+
idempotencyKey,
|
|
1373
|
+
ownerId: idempotencyClaim.ownerId,
|
|
1374
|
+
});
|
|
789
1375
|
return await trackCost(resolvedInputParams.projectPath ?? '', 'create_spec', async () => {
|
|
790
|
-
// Allow internal re-enrichment without mutating function parameter
|
|
791
|
-
let params = resolvedInputParams;
|
|
792
|
-
const { description, clarificationSessionId } = params;
|
|
793
1376
|
try {
|
|
794
1377
|
// SPEC-713: Phase 1 — Critical path: build + persist spec within 25s hard ceiling.
|
|
795
1378
|
// Only the steps required to produce spec.md on disk are inside this budget.
|
|
796
|
-
//
|
|
797
|
-
const criticalResult = await withTotalBudget('create_spec-critical',
|
|
798
|
-
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
earlyReturn: {
|
|
804
|
-
content: [{ type: 'text', text: buildResult.errorMessage }],
|
|
805
|
-
isError: true,
|
|
806
|
-
},
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
const { spec, specDir, specPath, technicalPath, estimation, splitSuggestion, duplicate, projectId, agentTeamPlan, } = buildResult.context;
|
|
810
|
-
// Kept in external store/evidence for retries; the value-only serializer omits it.
|
|
811
|
-
spec.idempotencyKey = idempotencyKey;
|
|
812
|
-
// Load project knowledge and clarification session
|
|
813
|
-
const knowledge = await measureStep('loadKnowledge', () => knowledgeStore.getKnowledge(projectId));
|
|
814
|
-
const clarificationSession = clarificationSessionId
|
|
815
|
-
? await knowledgeStore.getClarification(projectId, clarificationSessionId)
|
|
816
|
-
: null;
|
|
817
|
-
// Validate against Constitution
|
|
818
|
-
const constitutionCheck = await measureStep('validateConstitution', () => validateConstitution(projectId, spec.title, spec.tags));
|
|
819
|
-
if (constitutionCheck.blocked && constitutionCheck.errorResult) {
|
|
820
|
-
return { ok: false, earlyReturn: constitutionCheck.errorResult };
|
|
821
|
-
}
|
|
822
|
-
// SPEC-461 Phase 3: Autopilot — analyze project to enrich spec (best-effort)
|
|
823
|
-
// SPEC-560: Wrapped with 5s timeout to prevent hangs on large projects (2000+ files)
|
|
824
|
-
// SPEC-713: measureStep for Phase 0 diagnostic; withBudget enforces 5s ceiling
|
|
825
|
-
const autopilotResult = await withBudget('autopilot-analyzer', 5_000, () => measureStep('autopilot-analyzer', () => analyzeProjectForSpec(params.projectPath ?? '', description, spec.title, knowledge)));
|
|
826
|
-
const autopilot = unwrapBudget(autopilotResult, getEmptyAutopilotResult());
|
|
827
|
-
// If very vague (<10 words), capture as idea instead of spec
|
|
828
|
-
if (autopilot.isIdea) {
|
|
829
|
-
return {
|
|
830
|
-
ok: false,
|
|
831
|
-
earlyReturn: {
|
|
832
|
-
content: [
|
|
833
|
-
{
|
|
834
|
-
type: 'text',
|
|
835
|
-
text: `💡 **Idea captured**: "${description}"\n\nThe description is too brief for a full spec. Use \`capture_idea\` to save it to the backlog, or provide more detail (>10 words) to create a spec.`,
|
|
836
|
-
},
|
|
837
|
-
],
|
|
838
|
-
},
|
|
839
|
-
};
|
|
1379
|
+
// Durable post-commit work is delegated to the spec.created outbox consumer.
|
|
1380
|
+
const criticalResult = await withTotalBudget('create_spec-critical', getRuntimePolicy().createSpec.criticalTimeoutMs, async (criticalSignal) => {
|
|
1381
|
+
const { params, context, knowledge, clarificationSession, constitutionWarnings, advisoryCriteria, actionableMetrics, unifiedSpec, simplicityResult: committedSimplicity, } = prepared;
|
|
1382
|
+
const { spec, specDir, specPath, splitSuggestion, duplicate, projectId } = context;
|
|
1383
|
+
let committedPlannerToken;
|
|
1384
|
+
try {
|
|
1385
|
+
committedPlannerToken = await issuePlannerToken(resolvedPath, spec.id, params.sessionId ?? 'unknown-session', params.modelId ?? 'unknown-model', params.host ?? 'unknown-host');
|
|
840
1386
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (clarificationResult !== null) {
|
|
844
|
-
if ('earlyReturn' in clarificationResult) {
|
|
845
|
-
// SPEC-584: Persist token so the gate blocks retries without answers
|
|
846
|
-
await persistClarificationToken(clarificationResult.earlyReturn, projectId, 'create_spec');
|
|
847
|
-
return { ok: false, earlyReturn: clarificationResult.earlyReturn };
|
|
848
|
-
}
|
|
849
|
-
params = clarificationResult.params;
|
|
1387
|
+
catch {
|
|
1388
|
+
// Best-effort evidence must not block durable spec creation.
|
|
850
1389
|
}
|
|
851
|
-
const
|
|
852
|
-
const technologyContract = await readTechnologySelectionContract(params.projectPath ?? '');
|
|
853
|
-
const contractNote = technologyContract
|
|
854
|
-
? [
|
|
855
|
-
'',
|
|
856
|
-
'Technology Contract:',
|
|
857
|
-
`- Mode: ${technologyContract.mode}`,
|
|
858
|
-
technologyContract.language ? `- Language: ${technologyContract.language}` : '',
|
|
859
|
-
technologyContract.framework
|
|
860
|
-
? `- Framework: ${technologyContract.framework}`
|
|
861
|
-
: '- Framework: none or not selected',
|
|
862
|
-
technologyContract.platform ? `- Platform: ${technologyContract.platform}` : '',
|
|
863
|
-
technologyContract.projectType
|
|
864
|
-
? `- Project type: ${technologyContract.projectType}`
|
|
865
|
-
: '',
|
|
866
|
-
'- Agents must not choose a different stack unless the user approves a new contract.',
|
|
867
|
-
]
|
|
868
|
-
.filter(Boolean)
|
|
869
|
-
.join('\n')
|
|
870
|
-
: '';
|
|
871
|
-
const specGenerator = new FallbackGenerator();
|
|
872
|
-
const generatedSpec = await measureStep('generateSpecBody', () => specGenerator.generate({
|
|
873
|
-
title: spec.title,
|
|
874
|
-
description: `${description}${contractNote}`,
|
|
875
|
-
type: spec.type,
|
|
876
|
-
scope: spec.scope,
|
|
877
|
-
target: spec.target,
|
|
878
|
-
acFormat: params.acFormat,
|
|
879
|
-
projectContext: {
|
|
880
|
-
language: technologyContract?.language ?? knowledge?.language ?? undefined,
|
|
881
|
-
framework: technologyContract?.framework ?? knowledge?.framework ?? undefined,
|
|
882
|
-
architecture: knowledge?.architecture.primary ?? undefined,
|
|
883
|
-
},
|
|
884
|
-
}));
|
|
885
|
-
spec.generation = generatedSpec.generation;
|
|
886
|
-
spec.qualityWarnings =
|
|
887
|
-
generatedSpec.qualityWarnings.length > 0 ? generatedSpec.qualityWarnings : undefined;
|
|
888
|
-
const baseCriteria = extractCriteria(generatedSpec.specBody).map((criterion) => criterion.text);
|
|
889
|
-
const groundingCriteria = buildCriterionGroundingRecords({
|
|
890
|
-
criteria: [...baseCriteria, ...filteredCriteria],
|
|
891
|
-
userInput: description,
|
|
892
|
-
generatedEvidence: [
|
|
893
|
-
generatedSpec.generation.modelId ?? generatedSpec.generation.method,
|
|
894
|
-
],
|
|
895
|
-
});
|
|
896
|
-
const contractCriteria = getContractCriteria(groundingCriteria);
|
|
897
|
-
const advisoryCriteria = getAdvisoryCriteria(groundingCriteria).map((record) => record.text);
|
|
898
|
-
if (contractCriteria.length === 0) {
|
|
899
|
-
return {
|
|
900
|
-
ok: false,
|
|
901
|
-
earlyReturn: {
|
|
902
|
-
content: [
|
|
903
|
-
{
|
|
904
|
-
type: 'text',
|
|
905
|
-
text: 'create_spec did not persist a spec because no acceptance criterion was grounded in the user request. ' +
|
|
906
|
-
'Needs decision: provide at least one explicit, verifiable criterion as a checkbox or Given/When/Then outcome.',
|
|
907
|
-
},
|
|
908
|
-
],
|
|
909
|
-
isError: true,
|
|
910
|
-
structuredContent: {
|
|
911
|
-
error: 'MISSING_GROUNDED_ACCEPTANCE_CRITERIA',
|
|
912
|
-
missingDecision: 'Provide at least one explicit, verifiable acceptance criterion.',
|
|
913
|
-
persisted: false,
|
|
914
|
-
},
|
|
915
|
-
},
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
const actionableMetrics = calculateActionableSpecMetrics({
|
|
919
|
-
criteria: [...baseCriteria, ...filteredCriteria],
|
|
920
|
-
groundingRecords: groundingCriteria,
|
|
921
|
-
}).filter(shouldExposeMetric);
|
|
922
|
-
const technicalFiles = await measureStep('resolveTechnicalFiles', () => resolveTechnicalFiles({
|
|
923
|
-
generatedSpecBody: generatedSpec.specBody,
|
|
924
|
-
generatedTechnicalSection: generatedSpec.technicalSection,
|
|
925
|
-
projectPath: params.projectPath ?? '',
|
|
926
|
-
autopilot,
|
|
927
|
-
fallbackReason: generatedSpec.fallbackReason,
|
|
928
|
-
}));
|
|
929
|
-
const groundedTechnical = await measureStep('groundTechnicalFiles', () => groundTechnicalFiles({
|
|
930
|
-
files: technicalFiles,
|
|
931
|
-
projectPath: params.projectPath ?? '',
|
|
932
|
-
userInput: description,
|
|
933
|
-
autopilot,
|
|
934
|
-
}));
|
|
935
|
-
const outOfScope = resolveOutOfScope(params.outOfScope);
|
|
936
|
-
const scenarioTestPaths = groundedTechnical.files.test.map((file) => file.path);
|
|
937
|
-
const leanSpec = generateLeanSpecContent({
|
|
1390
|
+
const committedResult = buildCommittedCreateResult({
|
|
938
1391
|
spec,
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
const leanTechnical = generateLeanTechnicalContent({
|
|
951
|
-
specId: spec.id,
|
|
952
|
-
filesToCreate: groundedTechnical.files.create,
|
|
953
|
-
filesToModify: groundedTechnical.files.modify,
|
|
954
|
-
filesToTest: groundedTechnical.files.test,
|
|
955
|
-
includeDecisionRequiredPlaceholder: spec.scope !== 'trivial',
|
|
956
|
-
});
|
|
957
|
-
// SPEC-709: write unified spec.md from origin — no separate technical.md.
|
|
958
|
-
// The legacy two-file output is preserved by appending the technical body
|
|
959
|
-
// as a `## Technical` section inside spec.md.
|
|
960
|
-
const unifiedWithoutContract = buildUnifiedSpecContent(leanSpec, leanTechnical);
|
|
961
|
-
const unifiedSpec = appendImplementationContractIfMissing(unifiedWithoutContract, {
|
|
962
|
-
description: generatedSpec.specBody,
|
|
963
|
-
criteria: contractCriteria.map((record) => ({ text: record.text, done: false })),
|
|
964
|
-
files: groundedTechnical.files,
|
|
965
|
-
outOfScope,
|
|
966
|
-
verificationCommands: [],
|
|
1392
|
+
operationId: operationKey,
|
|
1393
|
+
duplicate,
|
|
1394
|
+
constitutionWarnings,
|
|
1395
|
+
clarificationSession,
|
|
1396
|
+
advisoryCriteria,
|
|
1397
|
+
actionableMetrics,
|
|
1398
|
+
splitSuggestion,
|
|
1399
|
+
experienceLevel: knowledge?.experienceLevel,
|
|
1400
|
+
contradictionHint: undefined,
|
|
1401
|
+
simplicityResult: committedSimplicity,
|
|
1402
|
+
plannerToken: committedPlannerToken,
|
|
967
1403
|
});
|
|
968
|
-
|
|
969
|
-
if (!genericOutputGate.passed) {
|
|
970
|
-
return {
|
|
971
|
-
ok: false,
|
|
972
|
-
earlyReturn: {
|
|
973
|
-
content: [
|
|
974
|
-
{
|
|
975
|
-
type: 'text',
|
|
976
|
-
text: 'Spec quality gate blocked generic output before persistence. ' +
|
|
977
|
-
genericOutputGate.issues
|
|
978
|
-
.slice(0, 3)
|
|
979
|
-
.map((issue) => `${issue.phrase}: ${issue.reason}`)
|
|
980
|
-
.join('; '),
|
|
981
|
-
},
|
|
982
|
-
],
|
|
983
|
-
isError: true,
|
|
984
|
-
structuredContent: {
|
|
985
|
-
error: 'GENERIC_SPEC_OUTPUT_BLOCKED',
|
|
986
|
-
sddConstitutionRuleId: 'sdd.no-generic-output',
|
|
987
|
-
issues: genericOutputGate.issues,
|
|
988
|
-
fixHint: 'Replace generic criteria or placeholder references with grounded, testable behavior.',
|
|
989
|
-
},
|
|
990
|
-
},
|
|
991
|
-
};
|
|
992
|
-
}
|
|
1404
|
+
let storeCreated = false;
|
|
993
1405
|
try {
|
|
994
|
-
|
|
1406
|
+
assertExecutionCanCommit(criticalSignal);
|
|
1407
|
+
operationJournal.setRecoveryPayload('create_spec', operationKey, 1, {
|
|
1408
|
+
operationVersion: 1,
|
|
1409
|
+
projectPath: resolvedPath,
|
|
1410
|
+
idempotencyKey,
|
|
1411
|
+
ownerId: idempotencyClaim.ownerId,
|
|
1412
|
+
projectId,
|
|
1413
|
+
specId: spec.id,
|
|
1414
|
+
specPath,
|
|
1415
|
+
specContentDigest: createHash('sha256').update(unifiedSpec).digest('hex'),
|
|
1416
|
+
result: committedResult,
|
|
1417
|
+
});
|
|
1418
|
+
operationJournal.prepared('create_spec', operationKey);
|
|
1419
|
+
await measureStep('mkdir-specDir', () => executionMkdir(specDir, { recursive: true }));
|
|
995
1420
|
// SPEC-713: measure file write — this is the critical persistence step.
|
|
996
|
-
|
|
1421
|
+
assertExecutionCanCommit(criticalSignal);
|
|
1422
|
+
await measureStep('writeFile-specPath', () => atomicWriteFile(specPath, unifiedSpec, { encoding: 'utf-8' }));
|
|
1423
|
+
assertExecutionCanCommit(criticalSignal);
|
|
1424
|
+
await measureStep('specStore-createSpec', () => specStore.createSpec(projectId, spec));
|
|
1425
|
+
storeCreated = true;
|
|
1426
|
+
assertExecutionCanCommit(criticalSignal);
|
|
997
1427
|
await measureStep('commit-idempotency-evidence', () => commitIdempotencyEvidence(resolvedPath, idempotencyKey, idempotencyClaim, spec, specPath));
|
|
1428
|
+
operationJournal.commitWithOutbox('create_spec', operationKey, committedResult, {
|
|
1429
|
+
topic: 'spec.created',
|
|
1430
|
+
payload: {
|
|
1431
|
+
schemaVersion: 1,
|
|
1432
|
+
specId: spec.id,
|
|
1433
|
+
projectId,
|
|
1434
|
+
projectPath: resolvedPath,
|
|
1435
|
+
postCommitTasks: SPEC_CREATED_POST_COMMIT_TASKS,
|
|
1436
|
+
},
|
|
1437
|
+
});
|
|
998
1438
|
claimLifecycle.committed = true;
|
|
999
|
-
// SPEC-709:
|
|
1000
|
-
// SPEC-461: No progress.md, no HTML reports.
|
|
1439
|
+
// SPEC-709/SPEC-461: unified spec.md; no progress or HTML artifacts.
|
|
1001
1440
|
}
|
|
1002
1441
|
catch (writeErr) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1442
|
+
const storeRolledBack = storeCreated
|
|
1443
|
+
? await specStore.deleteSpec(projectId, spec.id).catch(() => false)
|
|
1444
|
+
: true;
|
|
1445
|
+
await cleanupExecutionArtifact(getIdempotencyEvidencePath(resolvedPath, idempotencyKey), { force: true });
|
|
1446
|
+
await cleanupExecutionArtifact(specDir, { recursive: true, force: true });
|
|
1447
|
+
try {
|
|
1448
|
+
operationJournal.resolveRecovery('create_spec', operationKey, {
|
|
1449
|
+
action: storeRolledBack ? 'rollback' : 'quarantine',
|
|
1450
|
+
reason: storeRolledBack
|
|
1451
|
+
? 'create_spec persistence failed and compensating cleanup completed'
|
|
1452
|
+
: 'create_spec persistence failed and the spec store rollback was incomplete',
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
catch {
|
|
1456
|
+
// The original persistence failure remains the primary error.
|
|
1457
|
+
}
|
|
1005
1458
|
throw writeErr;
|
|
1006
1459
|
}
|
|
1007
|
-
// Persist only explicit out-of-scope input.
|
|
1008
|
-
if (outOfScope.length > 0) {
|
|
1009
|
-
spec.outOfScope = outOfScope;
|
|
1010
|
-
}
|
|
1011
|
-
// Persist spec in storage
|
|
1012
|
-
// SPEC-713: measure specStore.createSpec — file lock + JSON rewrite
|
|
1013
|
-
await measureStep('specStore-createSpec', () => specStore.createSpec(projectId, spec));
|
|
1014
1460
|
return {
|
|
1015
1461
|
ok: true,
|
|
1016
1462
|
data: {
|
|
1017
|
-
|
|
1018
|
-
specDir,
|
|
1019
|
-
specPath,
|
|
1020
|
-
technicalPath,
|
|
1021
|
-
estimation,
|
|
1022
|
-
splitSuggestion,
|
|
1023
|
-
duplicate,
|
|
1024
|
-
projectId,
|
|
1025
|
-
agentTeamPlan,
|
|
1026
|
-
knowledge,
|
|
1027
|
-
clarificationSession,
|
|
1028
|
-
constitutionCheck,
|
|
1029
|
-
autopilot,
|
|
1030
|
-
filteredCriteria,
|
|
1031
|
-
advisoryCriteria: [
|
|
1032
|
-
...advisoryCriteria,
|
|
1033
|
-
...groundedTechnical.advisoryFiles.map((file) => file.path),
|
|
1034
|
-
],
|
|
1035
|
-
actionableMetrics,
|
|
1463
|
+
committedResult,
|
|
1036
1464
|
},
|
|
1037
1465
|
};
|
|
1038
1466
|
}); // end withTotalBudget critical path
|
|
@@ -1041,7 +1469,7 @@ export async function handleCreateSpec(inputParams, server) {
|
|
|
1041
1469
|
claimLifecycle.retainForInFlightWork = criticalResult.warning.includes('exceeded');
|
|
1042
1470
|
// SPEC-770: Wait briefly for any in-flight spec write to complete, then scan for duplicates
|
|
1043
1471
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
1044
|
-
const recentSpecs = await findRecentlyWrittenSpecs(resolvedPath, 2 * 60 * 1000);
|
|
1472
|
+
const recentSpecs = await findRecentlyWrittenSpecs(resolvedPath, 2 * 60 * 1000, resolvedInputParams.title);
|
|
1045
1473
|
const possibleDuplicates = recentSpecs.length > 0 ? recentSpecs : undefined;
|
|
1046
1474
|
const dupNote = possibleDuplicates
|
|
1047
1475
|
? `\n\nA spec may have been created server-side: ${possibleDuplicates.map((s) => s.id).join(', ')}. Retry with the same title to return it instead of creating a duplicate.`
|
|
@@ -1050,401 +1478,42 @@ export async function handleCreateSpec(inputParams, server) {
|
|
|
1050
1478
|
content: [
|
|
1051
1479
|
{
|
|
1052
1480
|
type: 'text',
|
|
1053
|
-
text: `❌ create_spec exceeded
|
|
1481
|
+
text: `❌ create_spec exceeded its ${String(criticalResult.ceilingMs)}ms internal budget.\n\n${criticalResult.warning}${dupNote}`,
|
|
1054
1482
|
},
|
|
1055
1483
|
],
|
|
1056
1484
|
isError: true,
|
|
1057
1485
|
...(possibleDuplicates ? { structuredContent: { possibleDuplicates } } : {}),
|
|
1058
1486
|
};
|
|
1059
1487
|
}
|
|
1060
|
-
if (
|
|
1061
|
-
return
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
// -----------------------------------------------------------------------
|
|
1070
|
-
// Auto-setup git branch (non-blocking)
|
|
1071
|
-
// SPEC-713: withBudget 3s — git subprocess can be slow on large repos
|
|
1072
|
-
const gitBudgetResult = await withBudget('setupGitBranch', 3_000, () => measureStep('setupGitBranch', () => setupGitBranch(projectId, spec.id)));
|
|
1073
|
-
const gitSetupResult = unwrapBudget(gitBudgetResult, undefined);
|
|
1074
|
-
/* v8 ignore start -- requires real git repo */
|
|
1075
|
-
if (gitSetupResult) {
|
|
1076
|
-
spec.gitBranch = gitSetupResult.branch;
|
|
1077
|
-
}
|
|
1078
|
-
/* v8 ignore stop */
|
|
1079
|
-
// Brief contradiction check (non-blocking)
|
|
1080
|
-
// SPEC-713: withBudget 2s — scans all specs for contradiction keywords
|
|
1081
|
-
const contradictionResult = await withBudget('checkContradictions', 2_000, () => measureStep('checkContradictions', () => checkContradictions(projectId, spec.id, description)));
|
|
1082
|
-
const contradictionHint = unwrapBudget(contradictionResult, undefined);
|
|
1083
|
-
// Build result (SPEC-461: lean — no progress, HTML, diagrams, scope filters)
|
|
1084
|
-
const result = {
|
|
1085
|
-
// SPEC-781: async analysis running in background (external project data)
|
|
1086
|
-
pendingAnalysis: true,
|
|
1087
|
-
specId: spec.id,
|
|
1088
|
-
title: spec.title,
|
|
1089
|
-
slug: spec.slug,
|
|
1090
|
-
type: spec.type,
|
|
1091
|
-
scope: spec.scope,
|
|
1092
|
-
difficulty: spec.difficulty,
|
|
1093
|
-
risk: spec.risk,
|
|
1094
|
-
tags: spec.tags,
|
|
1095
|
-
target: spec.target,
|
|
1096
|
-
status: spec.status,
|
|
1097
|
-
gitBranch: spec.gitBranch,
|
|
1098
|
-
specPath,
|
|
1099
|
-
// SPEC-1010 Bug A — `technicalPath` no longer leaked in the response.
|
|
1100
|
-
// SSR back-migration (SPEC-752) folded technical.md into spec.md as
|
|
1101
|
-
// ## Technical; the file is never written and the field would point to
|
|
1102
|
-
// a non-existent path. Internal Spec record still carries it for
|
|
1103
|
-
// backwards-compat with stored data — that field is removed in PR-C.
|
|
1104
|
-
duplicateWarning: duplicate
|
|
1105
|
-
? ti('spec.duplicateTitle', { title: spec.title, slug: spec.slug })
|
|
1106
|
-
: undefined,
|
|
1107
|
-
...(constitutionCheck.warnings.length > 0
|
|
1108
|
-
? { constitutionWarnings: constitutionCheck.warnings }
|
|
1109
|
-
: {}),
|
|
1110
|
-
...(clarificationSession
|
|
1111
|
-
? {
|
|
1112
|
-
linkedClarificationSession: clarificationSession.id,
|
|
1113
|
-
clarificationTopic: clarificationSession.topic,
|
|
1114
|
-
clarificationAnswersUsed: Object.keys(clarificationSession.answers).length,
|
|
1115
|
-
}
|
|
1116
|
-
: {
|
|
1117
|
-
clarificationNote: 'Spec created directly from the supplied description; no clarification session was linked.',
|
|
1118
|
-
}),
|
|
1119
|
-
message: ti('tools.create_spec.success', { id: spec.id, title: spec.title }),
|
|
1120
|
-
...(contradictionHint ? { contradictionHint } : {}),
|
|
1121
|
-
/* v8 ignore next -- requires real git repo */
|
|
1122
|
-
...(gitSetupResult ? { gitAutoSetup: gitSetupResult.data } : {}),
|
|
1123
|
-
};
|
|
1124
|
-
const advisorySignals = [];
|
|
1125
|
-
if (actionableMetrics.length > 0) {
|
|
1126
|
-
result.actionableMetrics = actionableMetrics;
|
|
1127
|
-
}
|
|
1128
|
-
if (advisoryCriteria.length > 0) {
|
|
1129
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1130
|
-
key: 'ungrounded-contract-items',
|
|
1131
|
-
kind: 'quality',
|
|
1132
|
-
message: `${String(advisoryCriteria.length)} ungrounded contract item(s) kept advisory-only.`,
|
|
1133
|
-
source: 'validator',
|
|
1134
|
-
evidence: advisoryCriteria.slice(0, 10),
|
|
1135
|
-
confidence: 0.8,
|
|
1136
|
-
surface: 'structuredContent',
|
|
1137
|
-
value: advisoryCriteria,
|
|
1138
|
-
}));
|
|
1139
|
-
}
|
|
1140
|
-
const splitResult = buildSplitResult(splitSuggestion, knowledge?.experienceLevel);
|
|
1141
|
-
if (splitResult) {
|
|
1142
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1143
|
-
key: 'split-suggestion',
|
|
1144
|
-
kind: 'complexity',
|
|
1145
|
-
message: 'Spec may benefit from splitting.',
|
|
1146
|
-
source: 'heuristic',
|
|
1147
|
-
evidence: ['spec-splitter heuristic'],
|
|
1148
|
-
confidence: 0.5,
|
|
1149
|
-
surface: 'structuredContent',
|
|
1150
|
-
value: splitResult,
|
|
1151
|
-
}));
|
|
1152
|
-
}
|
|
1153
|
-
// Dispatch hook event (fire-and-forget)
|
|
1154
|
-
fireSpecCreatedHook(projectId, spec, params.projectPath ?? '');
|
|
1155
|
-
// SPEC-781: Fire-and-forget async analysis (external project data)
|
|
1156
|
-
runAutopilotAsync(spec.id, params.projectPath ?? '', description);
|
|
1157
|
-
// SPEC-713: Track warnings from budgeted post-creation steps
|
|
1158
|
-
const budgetWarnings = [];
|
|
1159
|
-
// SPEC-461: No per-spec HTML reports — lean format
|
|
1160
|
-
// SPEC-713: Run post-creation checks in parallel to reduce total wall-clock time.
|
|
1161
|
-
// Each has an individual budget; results are merged into `result` after settlement.
|
|
1162
|
-
const [readinessResult, qualityResult, duplicatesResult, complexityResult, priorLinksResult, nextStepsResult,] = await Promise.all([
|
|
1163
|
-
// Auto-readiness check (best-effort) — SPEC-713: 3s budget
|
|
1164
|
-
withBudget('auto-readiness', 3_000, () => measureStep('auto-readiness', () => checkSpecReadiness(spec, 'lenient'))),
|
|
1165
|
-
// SPEC-492: Spec quality score (0-100) — SPEC-713: 2s budget
|
|
1166
|
-
withBudget('quality-score', 2_000, () => measureStep('quality-score', () => runQualityScore(spec).then((r) => r ?? null))),
|
|
1167
|
-
// SPEC-514: Semantic duplicate detection — SPEC-713: 2s budget
|
|
1168
|
-
withBudget('duplicate-detection', 2_000, () => measureStep('duplicate-detection', async () => {
|
|
1169
|
-
const allSpecs = await specStore.listSpecs(projectId);
|
|
1170
|
-
const recentSpecs = allSpecs.slice(-200).filter((s) => s.id !== spec.id);
|
|
1171
|
-
return findSimilarSpecs(spec.title, recentSpecs, { threshold: 0.3, topN: 3 });
|
|
1172
|
-
})),
|
|
1173
|
-
// SPEC-614 AC4: complexity-category hint — SPEC-713: 2s budget
|
|
1174
|
-
withBudget('complexity-advice', 2_000, () => measureStep('complexity-advice', () => runComplexityAdvice(projectId, spec.id, spec.tags, filteredCriteria.length))),
|
|
1175
|
-
// SPEC-615: prior-decision links — SPEC-713: 2s budget
|
|
1176
|
-
withBudget('prior-decisions', 2_000, () => measureStep('prior-decisions', () => runPriorDecisionsHint(projectId, description, spec.tags))),
|
|
1177
|
-
// Post-creation suggestions — SPEC-713: 3s budget
|
|
1178
|
-
withBudget('post-creation-suggestions', 3_000, () => measureStep('post-creation-suggestions', () => generatePostCreationSuggestions(params.projectPath ?? '', description, knowledge ?? undefined))),
|
|
1179
|
-
]);
|
|
1180
|
-
// Merge parallel results into `result`
|
|
1181
|
-
if (!readinessResult.exceeded) {
|
|
1182
|
-
const readiness = readinessResult.value;
|
|
1183
|
-
result.autoReadiness = { score: readiness.score, isReady: readiness.ready };
|
|
1184
|
-
}
|
|
1185
|
-
else {
|
|
1186
|
-
budgetWarnings.push(readinessResult.warning);
|
|
1187
|
-
}
|
|
1188
|
-
const qualityValue = unwrapBudget(qualityResult, null, budgetWarnings);
|
|
1189
|
-
if (qualityValue !== null) {
|
|
1190
|
-
result.qualityScore = qualityValue;
|
|
1191
|
-
}
|
|
1192
|
-
// SPEC-485: Simplicity autopilot — detect over-engineering signals (best-effort, sync)
|
|
1193
|
-
const simplicityResult = runSimplicityCheck([params.description, ...filteredCriteria].join('\n'), estimation.devHours);
|
|
1194
|
-
if (simplicityResult) {
|
|
1195
|
-
result.simplicityCheck = simplicityResult;
|
|
1196
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1197
|
-
key: 'simplicity-check',
|
|
1198
|
-
kind: 'simplicity',
|
|
1199
|
-
message: `Simplicity recommendation: ${simplicityResult.recommendation}.`,
|
|
1200
|
-
source: 'heuristic',
|
|
1201
|
-
evidence: simplicityResult.signals.map((signal) => signal.type),
|
|
1202
|
-
confidence: 0.55,
|
|
1203
|
-
surface: 'structuredContent',
|
|
1204
|
-
value: simplicityResult,
|
|
1205
|
-
deprecatedAlias: 'simplicityCheck',
|
|
1206
|
-
}));
|
|
1207
|
-
}
|
|
1208
|
-
// SPEC-514: duplicate results
|
|
1209
|
-
const possibleDuplicates = unwrapBudget(duplicatesResult, [], budgetWarnings);
|
|
1210
|
-
if (possibleDuplicates.length > 0) {
|
|
1211
|
-
result.possibleDuplicates = possibleDuplicates;
|
|
1212
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1213
|
-
key: 'possible-duplicates',
|
|
1214
|
-
kind: 'duplicate',
|
|
1215
|
-
message: `${String(possibleDuplicates.length)} possible duplicate spec(s) detected.`,
|
|
1216
|
-
source: 'heuristic',
|
|
1217
|
-
evidence: possibleDuplicates.map((dup) => dup.specId),
|
|
1218
|
-
confidence: 0.5,
|
|
1219
|
-
surface: 'structuredContent',
|
|
1220
|
-
value: possibleDuplicates,
|
|
1221
|
-
deprecatedAlias: 'possibleDuplicates',
|
|
1222
|
-
}));
|
|
1223
|
-
}
|
|
1224
|
-
// SPEC-222 Trigger 2: Auto-challenge hint for high-risk specs
|
|
1225
|
-
if (spec.risk === 'high' || spec.difficulty >= 4) {
|
|
1226
|
-
result.riskWarning = HIGH_RISK_WARNING;
|
|
1227
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1228
|
-
key: 'risk-warning',
|
|
1229
|
-
kind: 'challenge',
|
|
1230
|
-
message: 'High-risk heuristic warning generated.',
|
|
1231
|
-
source: 'heuristic',
|
|
1232
|
-
evidence: [`risk:${spec.risk}`, `difficulty:${String(spec.difficulty)}`],
|
|
1233
|
-
confidence: 0.6,
|
|
1234
|
-
surface: 'structuredContent',
|
|
1235
|
-
value: HIGH_RISK_WARNING,
|
|
1236
|
-
deprecatedAlias: 'riskWarning',
|
|
1237
|
-
}));
|
|
1238
|
-
}
|
|
1239
|
-
// SPEC-614 AC4: complexity advice
|
|
1240
|
-
const cAdvice = unwrapBudget(complexityResult, null, budgetWarnings);
|
|
1241
|
-
if (cAdvice) {
|
|
1242
|
-
result.complexityAdvice = cAdvice;
|
|
1243
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1244
|
-
key: 'complexity-advice',
|
|
1245
|
-
kind: 'complexity',
|
|
1246
|
-
message: cAdvice.reasoning,
|
|
1247
|
-
source: 'history',
|
|
1248
|
-
evidence: [`similarSpecs:${String(cAdvice.similarSpecsCount)}`],
|
|
1249
|
-
confidence: 0.6,
|
|
1250
|
-
surface: 'structuredContent',
|
|
1251
|
-
value: cAdvice,
|
|
1252
|
-
deprecatedAlias: 'complexityAdvice',
|
|
1253
|
-
}));
|
|
1254
|
-
}
|
|
1255
|
-
// SPEC-615: prior decisions
|
|
1256
|
-
const priorLinks = unwrapBudget(priorLinksResult, [], budgetWarnings);
|
|
1257
|
-
if (priorLinks.length > 0) {
|
|
1258
|
-
result.priorDecisions = priorLinks.map((l) => l.decisionId);
|
|
1259
|
-
spec.priorDecisions = result.priorDecisions;
|
|
1260
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1261
|
-
key: 'prior-decisions',
|
|
1262
|
-
kind: 'prior-decision',
|
|
1263
|
-
message: `${String(priorLinks.length)} prior decision link(s) found.`,
|
|
1264
|
-
source: 'history',
|
|
1265
|
-
evidence: priorLinks.map((link) => link.decisionId),
|
|
1266
|
-
confidence: 0.65,
|
|
1267
|
-
surface: 'structuredContent',
|
|
1268
|
-
value: result.priorDecisions,
|
|
1269
|
-
deprecatedAlias: 'priorDecisions',
|
|
1270
|
-
}));
|
|
1271
|
-
}
|
|
1272
|
-
// Post-creation suggestions
|
|
1273
|
-
const nextSteps = unwrapBudget(nextStepsResult, [], budgetWarnings);
|
|
1274
|
-
if (nextSteps.length > 0) {
|
|
1275
|
-
result.nextSteps = nextSteps;
|
|
1276
|
-
}
|
|
1277
|
-
// SPEC-713: Surface budget warnings in result when steps exceeded their budget
|
|
1278
|
-
if (budgetWarnings.length > 0) {
|
|
1279
|
-
result.budgetWarnings = budgetWarnings;
|
|
1280
|
-
}
|
|
1281
|
-
// SPEC-1017: No auto-regeneration of project-tree dashboard HTML.
|
|
1282
|
-
if (params.projectPath) {
|
|
1283
|
-
notifyStoreChange(params.projectPath, 'specs');
|
|
1284
|
-
}
|
|
1285
|
-
// SPEC-644: Refresh model mapping TTL in background (fire-and-forget)
|
|
1286
|
-
void import('../engine/model-tier-resolver.js')
|
|
1287
|
-
.then(({ triggerModelMappingRefresh }) => {
|
|
1288
|
-
triggerModelMappingRefresh(knowledge?.projectPath ?? params.projectPath ?? '');
|
|
1289
|
-
})
|
|
1290
|
-
.catch(() => {
|
|
1291
|
-
/* best-effort */
|
|
1292
|
-
});
|
|
1293
|
-
// Auto post-creation pipeline: challenge + readiness (SPEC-445).
|
|
1294
|
-
// Keep it observable and bounded so users see the actual result instead of
|
|
1295
|
-
// background work that may finish invisibly after the tool already returned.
|
|
1296
|
-
const pipelineResult = await runAutoPostCreatePipeline(spec.id, projectId, knowledge?.projectPath ?? params.projectPath ?? '');
|
|
1297
|
-
// Build markdown response
|
|
1298
|
-
const lines = [
|
|
1299
|
-
`**SPEC-${spec.id}** — ${spec.title}`,
|
|
1300
|
-
'',
|
|
1301
|
-
`| Field | Value |`,
|
|
1302
|
-
`|-------|-------|`,
|
|
1303
|
-
`| Type | ${spec.type} |`,
|
|
1304
|
-
`| Scope | ${spec.scope} |`,
|
|
1305
|
-
`| Difficulty | ${String(spec.difficulty)}/5 |`,
|
|
1306
|
-
`| Risk | ${spec.risk} |`,
|
|
1307
|
-
`| Target | ${spec.target} |`,
|
|
1308
|
-
`| Status | ${spec.status} |`,
|
|
1309
|
-
`| Tags | ${spec.tags.join(', ')} |`,
|
|
1310
|
-
];
|
|
1311
|
-
if (spec.gitBranch) {
|
|
1312
|
-
lines.push(`| Branch | \`${spec.gitBranch}\` |`);
|
|
1313
|
-
}
|
|
1314
|
-
if (duplicate) {
|
|
1315
|
-
lines.push('', `⚠️ ${ti('spec.duplicateTitle', { title: spec.title, slug: spec.slug })}`);
|
|
1316
|
-
}
|
|
1317
|
-
if (constitutionCheck.warnings.length > 0) {
|
|
1318
|
-
lines.push('', '⚠️ **Constitution Warnings**');
|
|
1319
|
-
constitutionCheck.warnings.forEach((w) => lines.push(`- ${w.description}`));
|
|
1320
|
-
}
|
|
1321
|
-
if (contradictionHint) {
|
|
1322
|
-
lines.push('', `⚠️ ${contradictionHint}`);
|
|
1323
|
-
}
|
|
1324
|
-
if (possibleDuplicates.length > 0 ||
|
|
1325
|
-
result.riskWarning ||
|
|
1326
|
-
splitResult ||
|
|
1327
|
-
result.qualityScore ||
|
|
1328
|
-
simplicityResult) {
|
|
1329
|
-
lines.push('', 'Advisory signals were computed and are available in structuredContent.advisorySignals.');
|
|
1330
|
-
}
|
|
1331
|
-
const allNextSteps = (result.nextSteps ?? []).map((step) => (typeof step === 'string' ? step : formatPostCreationSuggestion(step)));
|
|
1332
|
-
const markdownText = allNextSteps.length > 0
|
|
1333
|
-
? addNextSteps(formatSuccess(ti('tools.create_spec.success', { id: spec.id, title: spec.title }), lines.join('\n')), allNextSteps)
|
|
1334
|
-
: formatSuccess(ti('tools.create_spec.success', { id: spec.id, title: spec.title }), lines.join('\n'));
|
|
1335
|
-
// SPEC-469: Build autopilot summary from analyzer results
|
|
1336
|
-
const collector = new AutopilotSummaryCollector();
|
|
1337
|
-
if (autopilot.detectedPatterns.length > 0) {
|
|
1338
|
-
collector.pushOk('pattern-detection', `Detected patterns: ${autopilot.detectedPatterns.join(', ')}`);
|
|
1339
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1340
|
-
key: 'detected-patterns',
|
|
1341
|
-
kind: 'domain',
|
|
1342
|
-
message: `Detected patterns: ${autopilot.detectedPatterns.join(', ')}`,
|
|
1343
|
-
source: 'heuristic',
|
|
1344
|
-
evidence: autopilot.detectedPatterns,
|
|
1345
|
-
confidence: 0.5,
|
|
1346
|
-
surface: 'structuredContent',
|
|
1347
|
-
value: autopilot.detectedPatterns,
|
|
1348
|
-
}));
|
|
1349
|
-
}
|
|
1350
|
-
const totalSuggestedFiles = autopilot.suggestedFiles.create.length +
|
|
1351
|
-
autopilot.suggestedFiles.modify.length +
|
|
1352
|
-
autopilot.suggestedFiles.test.length;
|
|
1353
|
-
if (totalSuggestedFiles > 0) {
|
|
1354
|
-
collector.pushOk('file-analysis', `Suggested ${String(totalSuggestedFiles)} files (${String(autopilot.suggestedFiles.create.length)} create, ${String(autopilot.suggestedFiles.modify.length)} modify, ${String(autopilot.suggestedFiles.test.length)} test)`);
|
|
1355
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1356
|
-
key: 'suggested-files',
|
|
1357
|
-
kind: 'file',
|
|
1358
|
-
message: `${String(totalSuggestedFiles)} possible related file(s) detected.`,
|
|
1359
|
-
source: 'heuristic',
|
|
1360
|
-
evidence: [
|
|
1361
|
-
...autopilot.suggestedFiles.create.map((f) => f.path),
|
|
1362
|
-
...autopilot.suggestedFiles.modify.map((f) => f.path),
|
|
1363
|
-
...autopilot.suggestedFiles.test.map((f) => f.path),
|
|
1364
|
-
],
|
|
1365
|
-
confidence: 0.45,
|
|
1366
|
-
surface: 'structuredContent',
|
|
1367
|
-
value: {
|
|
1368
|
-
create: autopilot.suggestedFiles.create.map((f) => f.path),
|
|
1369
|
-
modify: autopilot.suggestedFiles.modify.map((f) => f.path),
|
|
1370
|
-
test: autopilot.suggestedFiles.test.map((f) => f.path),
|
|
1488
|
+
if (criticalResult.status === 'failed') {
|
|
1489
|
+
return {
|
|
1490
|
+
content: [{ type: 'text', text: criticalResult.warning }],
|
|
1491
|
+
isError: true,
|
|
1492
|
+
structuredContent: {
|
|
1493
|
+
error: 'OPERATION_FAILED',
|
|
1494
|
+
category: criticalResult.category,
|
|
1495
|
+
elapsedMs: criticalResult.elapsedMs,
|
|
1496
|
+
persisted: false,
|
|
1371
1497
|
},
|
|
1372
|
-
|
|
1373
|
-
}));
|
|
1374
|
-
}
|
|
1375
|
-
if (filteredCriteria.length > 0) {
|
|
1376
|
-
collector.pushOk('criteria-enrichment', `Evaluated ${String(filteredCriteria.length)} proposed criteria; only directly grounded items entered the contract`);
|
|
1377
|
-
}
|
|
1378
|
-
if (pipelineResult.challengeSummary) {
|
|
1379
|
-
collector.pushOk('challenge', `Challenge analysis complete: ${pipelineResult.challengeSummary}`);
|
|
1380
|
-
}
|
|
1381
|
-
if (pipelineResult.readinessScore !== null) {
|
|
1382
|
-
collector.pushOk('readiness', `Readiness score: ${String(pipelineResult.readinessScore)}/100`);
|
|
1383
|
-
advisorySignals.push(makeAdvisorySignal({
|
|
1384
|
-
key: 'readiness-score',
|
|
1385
|
-
kind: 'readiness',
|
|
1386
|
-
message: `Readiness score: ${String(pipelineResult.readinessScore)}/100.`,
|
|
1387
|
-
source: 'validator',
|
|
1388
|
-
evidence: ['auto post-creation pipeline'],
|
|
1389
|
-
confidence: 0.6,
|
|
1390
|
-
surface: 'structuredContent',
|
|
1391
|
-
value: pipelineResult.readinessScore,
|
|
1392
|
-
}));
|
|
1498
|
+
};
|
|
1393
1499
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
result.compat = buildAdvisoryCompat();
|
|
1397
|
-
const compactResult = compactObj(result);
|
|
1398
|
-
// SPEC-722: Issue a planner token for this spec creation.
|
|
1399
|
-
// Best-effort — token failure never blocks spec creation.
|
|
1400
|
-
let plannerToken;
|
|
1401
|
-
try {
|
|
1402
|
-
const resolvedProjectPath = resolvedInputParams.projectPath ?? '';
|
|
1403
|
-
if (resolvedProjectPath.length > 0) {
|
|
1404
|
-
const sessionId = resolvedInputParams.sessionId ?? 'unknown-session';
|
|
1405
|
-
const modelId = resolvedInputParams.modelId ?? 'unknown-model';
|
|
1406
|
-
const host = resolvedInputParams.host ?? 'unknown-host';
|
|
1407
|
-
plannerToken = await issuePlannerToken(resolvedProjectPath, spec.id, sessionId, modelId, host);
|
|
1408
|
-
}
|
|
1500
|
+
if (!criticalResult.value.ok) {
|
|
1501
|
+
return criticalResult.value.earlyReturn;
|
|
1409
1502
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1503
|
+
if (!claimLifecycle.committed) {
|
|
1504
|
+
throw new Error('create_spec critical path completed without a durable commit');
|
|
1412
1505
|
}
|
|
1413
|
-
|
|
1414
|
-
// Those paths are also written into ## Files only when used as technical evidence.
|
|
1415
|
-
const suggestedFilesPayload = autopilot.suggestedFiles.create.length +
|
|
1416
|
-
autopilot.suggestedFiles.modify.length +
|
|
1417
|
-
autopilot.suggestedFiles.test.length >
|
|
1418
|
-
0
|
|
1419
|
-
? {
|
|
1420
|
-
create: autopilot.suggestedFiles.create.map((f) => f.path),
|
|
1421
|
-
modify: autopilot.suggestedFiles.modify.map((f) => f.path),
|
|
1422
|
-
test: autopilot.suggestedFiles.test.map((f) => f.path),
|
|
1423
|
-
}
|
|
1424
|
-
: undefined;
|
|
1425
|
-
const baseResult = toolResult(markdownText, {
|
|
1426
|
-
...compactResult,
|
|
1427
|
-
advisorySignals,
|
|
1428
|
-
compat: buildAdvisoryCompat(),
|
|
1429
|
-
humanSummary,
|
|
1430
|
-
...(collector.hasEntries() ? { autopilotSummary: collector.getMessages() } : {}),
|
|
1431
|
-
...(suggestedFilesPayload !== undefined
|
|
1432
|
-
? { 'autopilotSummary.suggestedFiles': suggestedFilesPayload }
|
|
1433
|
-
: {}),
|
|
1434
|
-
...(agentTeamPlan && agentTeamPlan.roles.length > 0 ? { agentTeamPlan } : {}),
|
|
1435
|
-
...(plannerToken !== undefined ? { plannerToken } : {}),
|
|
1436
|
-
});
|
|
1437
|
-
return {
|
|
1438
|
-
...baseResult,
|
|
1439
|
-
content: [...baseResult.content, { type: 'text', text: humanSummary }],
|
|
1440
|
-
};
|
|
1506
|
+
return finishRecoveredOperation(operationJournal, operationKey, criticalResult.value.data.committedResult);
|
|
1441
1507
|
}
|
|
1442
1508
|
catch (error) {
|
|
1443
1509
|
const message = error instanceof Error ? error.message : String(error);
|
|
1444
|
-
|
|
1510
|
+
const errorResult = {
|
|
1445
1511
|
content: [{ type: 'text', text: ti('errors.internalError', { message }) }],
|
|
1446
1512
|
isError: true,
|
|
1447
1513
|
};
|
|
1514
|
+
return claimLifecycle.committed
|
|
1515
|
+
? finishRecoveredOperation(operationJournal, operationKey, errorResult)
|
|
1516
|
+
: errorResult;
|
|
1448
1517
|
}
|
|
1449
1518
|
}); // end trackCost
|
|
1450
1519
|
}
|
|
@@ -1452,6 +1521,7 @@ export async function handleCreateSpec(inputParams, server) {
|
|
|
1452
1521
|
if (!claimLifecycle.committed && !claimLifecycle.retainForInFlightWork) {
|
|
1453
1522
|
await releaseIdempotencyClaim(resolvedPath, idempotencyKey, idempotencyClaim.ownerId);
|
|
1454
1523
|
}
|
|
1524
|
+
runtimeDatabase.close();
|
|
1455
1525
|
}
|
|
1456
1526
|
}
|
|
1457
1527
|
//# sourceMappingURL=create-spec.js.map
|