@planu/cli 4.14.1 → 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 +35 -1
- package/LICENSE +55 -12
- package/README.md +10 -2
- package/dist/cli/commands/install.d.ts +1 -12
- package/dist/cli/commands/install.js +9 -65
- package/dist/cli/commands/job.js +4 -3
- package/dist/cli/commands/spec.js +2 -2
- package/dist/cli/commands/validate.js +2 -2
- package/dist/cli/router.js +1 -3
- package/dist/config/environment-schema.json +1 -3
- package/dist/config/official-sdd-tools.d.ts +4 -0
- package/dist/config/official-sdd-tools.js +35 -0
- package/dist/config/product-public.d.ts +139 -3
- package/dist/config/product-public.js +8 -1
- package/dist/config/product-public.json +7 -36
- package/dist/config/registries/hosts/codex.json +20 -363
- package/dist/config/release-policy.json +84 -2
- package/dist/config/runtime-policy.json +12 -1
- package/dist/config/skill-templates/planu-resume-work.md +2 -2
- package/dist/core/index.d.ts +1 -1
- package/dist/core/spec-validator.d.ts +4 -4
- package/dist/core/spec-validator.js +1 -1
- package/dist/engine/abortable-process-runner.d.ts +20 -2
- package/dist/engine/abortable-process-runner.js +243 -74
- package/dist/engine/ambiguity-scorer.js +4 -26
- package/dist/engine/auth/scope-mapper.js +1 -7
- package/dist/engine/autopilot/state-updater.js +4 -4
- package/dist/engine/cascade-hooks/hooks/review-enricher.hook.js +9 -6
- package/dist/engine/cascade-hooks/state-drift-detector.d.ts +3 -3
- package/dist/engine/cascade-hooks/state-drift-detector.js +1 -1
- package/dist/engine/ci-generator/planu-config.js +1 -7
- package/dist/engine/clarification-gate/gate.d.ts +3 -3
- package/dist/engine/code-scanner/layer-scanner.js +6 -22
- package/dist/engine/core-bridge.d.ts +3 -3
- package/dist/engine/cost-guardrails.d.ts +1 -1
- package/dist/engine/cost-guardrails.js +1 -1
- package/dist/engine/criterion-identity.d.ts +1 -0
- package/dist/engine/criterion-identity.js +7 -0
- package/dist/engine/dashboard/data-loader.js +2 -7
- package/dist/engine/dashboard/templates-layout.js +0 -3
- package/dist/engine/dashboard/templates-project.js +1 -13
- package/dist/engine/docs-site-generator/data-collector.d.ts +1 -1
- package/dist/engine/docs-site-generator/data-collector.js +7 -28
- package/dist/engine/docs-site-generator/tools-renderer.js +3 -10
- package/dist/engine/evidence-gates/artifact-reader.d.ts +10 -1
- package/dist/engine/evidence-gates/artifact-reader.js +115 -2
- package/dist/engine/evidence-gates/lifecycle-gate.js +4 -3
- package/dist/engine/evidence-gates/reconciliation-freshness.d.ts +11 -0
- package/dist/engine/evidence-gates/reconciliation-freshness.js +72 -0
- package/dist/engine/evidence-index/done-drift.d.ts +1 -1
- package/dist/engine/evidence-index/done-drift.js +40 -18
- package/dist/engine/execution/context.js +3 -0
- package/dist/engine/execution/durable-job-heartbeat-runtime.d.ts +4 -0
- package/dist/engine/execution/durable-job-heartbeat-runtime.js +400 -0
- package/dist/engine/execution/job-runtime.d.ts +30 -0
- package/dist/engine/execution/job-runtime.js +114 -6
- package/dist/engine/execution/public-job-sanitizer.d.ts +4 -0
- package/dist/engine/execution/public-job-sanitizer.js +31 -0
- package/dist/engine/execution/validate-job-executor.d.ts +12 -1
- package/dist/engine/execution/validate-job-executor.js +549 -86
- package/dist/engine/execution/validate-job-payload.js +107 -50
- package/dist/engine/execution/validate-job-recovery.js +1 -1
- package/dist/engine/execution/validate-worker-runtime.d.ts +5 -0
- package/dist/engine/execution/validate-worker-runtime.js +90 -2
- package/dist/engine/execution/validate-worker-thread.js +17 -24
- package/dist/engine/handoff-artifacts/schemas.d.ts +2 -1
- package/dist/engine/handoff-artifacts/schemas.js +33 -0
- package/dist/engine/handoff-packager.d.ts +19 -0
- package/dist/engine/handoff-packager.js +415 -13
- package/dist/engine/hooks/effort-configurator.js +1 -1
- package/dist/engine/impact-detector/test-break-predictor.js +3 -3
- package/dist/engine/impact-detector/tool-registration.js +1 -1
- package/dist/engine/implementation-contract/renderer.js +23 -8
- package/dist/engine/lifecycle-reconciliation-io.d.ts +11 -0
- package/dist/engine/lifecycle-reconciliation-io.js +180 -0
- package/dist/engine/lifecycle-reconciliation.d.ts +16 -0
- package/dist/engine/lifecycle-reconciliation.js +406 -0
- package/dist/engine/local-first/tool-classification.js +2 -0
- package/dist/engine/macos-keychain-native-port.d.ts +5 -0
- package/dist/engine/macos-keychain-native-port.js +19 -7
- package/dist/engine/migrations/remove-commercial-state.d.ts +3 -0
- package/dist/engine/migrations/remove-commercial-state.js +343 -0
- package/dist/engine/network-policy.js +1 -0
- package/dist/engine/planu-core.darwin-arm64.node.manifest.json +7 -7
- package/dist/engine/planu-core.darwin-arm64.node.sbom.json +10 -10
- package/dist/engine/planu-core.darwin-x64.node.manifest.json +7 -7
- package/dist/engine/planu-core.darwin-x64.node.sbom.json +10 -10
- package/dist/engine/planu-core.linux-arm64-gnu.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-arm64-gnu.node.sbom.json +10 -10
- package/dist/engine/planu-core.linux-arm64-musl.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-arm64-musl.node.sbom.json +10 -10
- package/dist/engine/planu-core.linux-x64-gnu.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-x64-gnu.node.sbom.json +10 -10
- package/dist/engine/planu-core.linux-x64-musl.node.manifest.json +7 -7
- package/dist/engine/planu-core.linux-x64-musl.node.sbom.json +10 -10
- package/dist/engine/planu-core.win32-arm64-msvc.node.manifest.json +7 -7
- package/dist/engine/planu-core.win32-arm64-msvc.node.sbom.json +10 -10
- package/dist/engine/planu-core.win32-x64-msvc.node.manifest.json +7 -7
- package/dist/engine/planu-core.win32-x64-msvc.node.sbom.json +10 -10
- package/dist/engine/readiness-checker.d.ts +1 -16
- package/dist/engine/readiness-checker.js +27 -88
- package/dist/engine/runtime-policy.d.ts +2 -0
- package/dist/engine/runtime-policy.js +42 -5
- package/dist/engine/runtime-security/rate-limiter.js +0 -1
- package/dist/engine/safety/contained-project-file.d.ts +3 -0
- package/dist/engine/safety/contained-project-file.js +23 -0
- package/dist/engine/scope-boundaries/contradiction-checker.js +63 -1
- package/dist/engine/scope-boundaries/scope-validator.js +45 -7
- package/dist/engine/session/checkpoint-writer.js +0 -9
- package/dist/engine/spec-format/acceptance-criteria.d.ts +6 -2
- package/dist/engine/spec-format/acceptance-criteria.js +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-migrator/canonical-deduplicator.d.ts +18 -0
- package/dist/engine/spec-migrator/canonical-deduplicator.js +479 -0
- package/dist/engine/spec-migrator/filesystem-import.d.ts +4 -8
- package/dist/engine/spec-migrator/filesystem-import.js +121 -14
- package/dist/engine/spec-migrator/frontmatter-parser.js +32 -15
- package/dist/engine/spec-migrator/index.d.ts +1 -0
- package/dist/engine/spec-migrator/index.js +1 -0
- package/dist/engine/spec-state-machine/transition-spec.d.ts +6 -1
- package/dist/engine/spec-state-machine/transition-spec.js +10 -1
- package/dist/engine/technical-enricher/index.js +16 -8
- package/dist/engine/technical-enricher/render-enriched.d.ts +1 -0
- package/dist/engine/technical-enricher/render-enriched.js +42 -14
- package/dist/engine/telemetry/health-checker.js +53 -17
- package/dist/engine/telemetry/telemetry-client.js +26 -8
- package/dist/engine/text-signal-boundaries.d.ts +2 -0
- package/dist/engine/text-signal-boundaries.js +25 -2
- package/dist/engine/time/relative-date.d.ts +3 -0
- package/dist/engine/time/relative-date.js +16 -0
- package/dist/engine/timing/budget.d.ts +11 -0
- package/dist/engine/timing/budget.js +27 -9
- package/dist/engine/token-optimizer/analytics.d.ts +1 -1
- package/dist/engine/token-optimizer/analytics.js +5 -6
- package/dist/engine/usage-tracker/core.d.ts +2 -2
- package/dist/engine/usage-tracker/core.js +1 -2
- package/dist/engine/usage-tracker/index.d.ts +0 -3
- package/dist/engine/usage-tracker/index.js +0 -3
- package/dist/engine/usage-tracker/stats.d.ts +2 -2
- package/dist/engine/usage-tracker/stats.js +1 -28
- package/dist/engine/validation/durable-validation.d.ts +1 -1
- package/dist/engine/validation/durable-validation.js +3 -82
- package/dist/engine/validation/validation-freshness.d.ts +11 -1
- package/dist/engine/validation/validation-freshness.js +231 -91
- package/dist/engine/validation/validation-receipt.d.ts +9 -6
- package/dist/engine/validation/validation-receipt.js +84 -24
- package/dist/engine/validation/validation-source-digest.d.ts +2 -0
- package/dist/engine/validation/validation-source-digest.js +86 -0
- package/dist/engine/validation/validation-submission.d.ts +5 -4
- package/dist/engine/validation/validation-submission.js +25 -11
- package/dist/engine/validation/validation-worktree.d.ts +21 -0
- package/dist/engine/validation/validation-worktree.js +260 -0
- package/dist/engine/validation-impact-planner.d.ts +14 -2
- package/dist/engine/validation-impact-planner.js +0 -3
- package/dist/engine/validator/dor-dod.d.ts +1 -0
- package/dist/engine/validator/dor-dod.js +5 -3
- package/dist/engine/validator/extractors.d.ts +1 -5
- package/dist/engine/validator/extractors.js +24 -69
- package/dist/engine/validator/spec-compliance-runner.d.ts +12 -24
- package/dist/engine/validator/spec-compliance-runner.js +429 -211
- package/dist/engine/validator.js +35 -22
- package/dist/index.js +4 -16
- package/dist/resources/usage-stats-resource.js +2 -18
- package/dist/security/adapters/linux-secret-service.js +7 -3
- package/dist/security/adapters/windows-credential-manager.js +6 -4
- package/dist/server/routes/specs.js +10 -0
- package/dist/storage/base-store.d.ts +1 -1
- package/dist/storage/base-store.js +1 -1
- package/dist/storage/feedback-remote.d.ts +2 -3
- package/dist/storage/feedback-remote.js +45 -31
- package/dist/storage/global-store.d.ts +1 -17
- package/dist/storage/global-store.js +1 -42
- package/dist/storage/index.d.ts +0 -2
- package/dist/storage/index.js +0 -2
- package/dist/storage/migrations/canonical-storage.js +124 -11
- package/dist/storage/runtime-db.d.ts +9 -0
- package/dist/storage/runtime-db.js +64 -8
- package/dist/storage/spec-store.d.ts +14 -2
- package/dist/storage/spec-store.js +104 -14
- package/dist/storage/status-store/atomic-status-write.d.ts +5 -0
- package/dist/storage/status-store/atomic-status-write.js +57 -0
- package/dist/storage/status-store/file-lock.js +30 -11
- package/dist/storage/status-store/self-healing.js +3 -3
- package/dist/storage/status-store/version-sync.d.ts +2 -1
- package/dist/storage/status-store/version-sync.js +35 -40
- package/dist/storage/usage-store.d.ts +1 -5
- package/dist/storage/usage-store.js +0 -35
- package/dist/tools/auto-update-handler.d.ts +3 -3
- package/dist/tools/auto-update-handler.js +3 -3
- package/dist/tools/challenge-spec/resilience-challenge-scenarios.d.ts +2 -1
- package/dist/tools/challenge-spec/resilience-challenge-scenarios.js +10 -7
- package/dist/tools/challenge-spec/scenario-collector.js +1 -1
- package/dist/tools/challenge-spec/scenarios-data.js +1 -1
- package/dist/tools/challenge-spec/scenarios-utils.d.ts +1 -1
- package/dist/tools/challenge-spec/scenarios-utils.js +35 -11
- package/dist/tools/challenge-spec.js +5 -5
- package/dist/tools/check-readiness.js +1 -1
- package/dist/tools/configure-telemetry.js +30 -2
- package/dist/tools/create-spec/spec-builder.js +0 -11
- package/dist/tools/create-spec.d.ts +1 -0
- package/dist/tools/create-spec.js +444 -239
- package/dist/tools/git/branch-ops.d.ts +1 -1
- package/dist/tools/git/branch-ops.js +3 -2
- package/dist/tools/init-project/handler.js +10 -12
- package/dist/tools/init-project/host-assets-writer.js +26 -1
- package/dist/tools/init-project/lint-scaffolder.d.ts +3 -3
- package/dist/tools/init-project/migration-runner.d.ts +2 -4
- package/dist/tools/init-project/migration-runner.js +3 -2
- package/dist/tools/init-project/portable-index-reconciler.d.ts +4 -0
- package/dist/tools/init-project/portable-index-reconciler.js +14 -0
- package/dist/tools/init-project/result-builder.js +1 -3
- package/dist/tools/jobs/handlers.js +38 -6
- package/dist/tools/list-specs.js +5 -26
- package/dist/tools/merge-risk-handler.js +2 -2
- package/dist/tools/package-handoff.js +2 -0
- package/dist/tools/reconcile-spec.js +0 -27
- package/dist/tools/register-agent-squad-tools.js +2 -2
- package/dist/tools/register-platform-tools/design-stack-tools.js +11 -11
- package/dist/tools/register-platform-tools/lifecycle-infra-tools.js +7 -7
- package/dist/tools/register-sdd-tools.d.ts +1 -1
- package/dist/tools/register-sdd-tools.js +2 -29
- package/dist/tools/register-spec-tools/analysis-tools.js +8 -8
- package/dist/tools/register-spec-tools/core-spec-tools.js +17 -8
- package/dist/tools/safe-handler.d.ts +5 -13
- package/dist/tools/safe-handler.js +7 -23
- package/dist/tools/schemas/index.d.ts +1 -1
- package/dist/tools/schemas/index.js +1 -1
- package/dist/tools/schemas/output-schemas.d.ts +0 -23
- package/dist/tools/schemas/output-schemas.js +0 -28
- package/dist/tools/schemas/spec.d.ts +4 -0
- package/dist/tools/schemas/spec.js +3 -0
- package/dist/tools/schemas/validate-output-schema.d.ts +11 -0
- package/dist/tools/schemas/validate-output-schema.js +7 -0
- package/dist/tools/spec-prompt-handler.js +3 -3
- package/dist/tools/sync-spec-state-handler.d.ts +1 -1
- package/dist/tools/sync-spec-state-handler.js +1 -1
- package/dist/tools/tool-entry.d.ts +4 -4
- package/dist/tools/tool-entry.js +3 -3
- package/dist/tools/tool-registry/core-tools.js +31 -15
- package/dist/tools/tool-registry/group-infra.js +27 -110
- package/dist/tools/tool-registry/group-quality-compliance.js +26 -26
- package/dist/tools/tool-registry/inventory.d.ts +2 -0
- package/dist/tools/tool-registry/inventory.js +2 -0
- package/dist/tools/tool-registry-helpers.d.ts +3 -3
- package/dist/tools/tool-registry-helpers.js +3 -3
- package/dist/tools/tool-usage-report/registered-tools.d.ts +1 -1
- package/dist/tools/tool-usage-report/registered-tools.js +3 -15
- package/dist/tools/update-status/approval-gates.js +3 -0
- package/dist/tools/update-status/done-receipt-verifier.js +40 -27
- package/dist/tools/update-status/evidence-gate.js +36 -1
- package/dist/tools/update-status/file-sync.d.ts +2 -1
- package/dist/tools/update-status/file-sync.js +57 -6
- package/dist/tools/update-status/index.d.ts +2 -0
- package/dist/tools/update-status/index.js +95 -58
- package/dist/tools/update-status/response-builder.js +3 -3
- package/dist/tools/update-status/side-effects.d.ts +2 -0
- package/dist/tools/update-status/side-effects.js +2 -2
- package/dist/tools/update-status/transition-guard.js +32 -15
- package/dist/tools/update-status-actions.d.ts +4 -2
- package/dist/tools/update-status-actions.js +16 -14
- package/dist/tools/usage-report.js +1 -16
- package/dist/tools/usage-stats.js +1 -18
- package/dist/tools/usage-tracking.d.ts +4 -0
- package/dist/tools/usage-tracking.js +20 -0
- package/dist/tools/validate-lint.d.ts +1 -0
- package/dist/tools/validate-lint.js +1 -1
- package/dist/tools/validate.d.ts +2 -1
- package/dist/tools/validate.js +114 -34
- package/dist/transports/http-transport.js +6 -8
- package/dist/transports/mcp-types.d.ts +0 -2
- package/dist/transports/session-context.js +1 -1
- package/dist/types/cascade-hooks.d.ts +1 -1
- package/dist/types/ci.d.ts +0 -4
- package/dist/types/clarification-token.d.ts +1 -1
- package/dist/types/claude-code-runtime.d.ts +0 -27
- package/dist/types/commercial-migration.d.ts +17 -0
- package/dist/types/commercial-migration.js +2 -0
- package/dist/types/common/primitives.d.ts +0 -2
- package/dist/types/core-bridge.d.ts +4 -10
- package/dist/types/dashboard.d.ts +3 -7
- package/dist/types/docs.d.ts +0 -1
- package/dist/types/durable-job.d.ts +4 -4
- package/dist/types/durable-validation.d.ts +100 -2
- package/dist/types/durable-validation.js +165 -1
- package/dist/types/errors/structured-error.d.ts +3 -3
- package/dist/types/estimation.d.ts +2 -0
- package/dist/types/evidence-gates.d.ts +9 -1
- package/dist/types/execution.d.ts +2 -0
- package/dist/types/gemini-integration.d.ts +0 -11
- package/dist/types/handoff-artifacts.d.ts +41 -0
- package/dist/types/hooks-advanced.d.ts +0 -13
- package/dist/types/impact-detection.d.ts +2 -2
- package/dist/types/index.d.ts +2 -6
- package/dist/types/index.js +1 -5
- package/dist/types/kiro-integration.d.ts +0 -6
- package/dist/types/network-policy.d.ts +1 -1
- package/dist/types/project/lint-scaffolder.d.ts +1 -1
- package/dist/types/runtime-policy.d.ts +11 -0
- package/dist/types/session-safeguard.d.ts +0 -31
- package/dist/types/spec/canonical-repair.d.ts +81 -0
- package/dist/types/spec/canonical-repair.js +2 -0
- package/dist/types/spec/core.d.ts +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 +29 -0
- package/dist/types/spec-format.d.ts +24 -0
- package/dist/types/telemetry.d.ts +4 -1
- package/dist/types/transport.d.ts +0 -2
- package/dist/types/usage.d.ts +1 -36
- package/dist/types/usage.js +1 -1
- package/dist/types/validate-worker.d.ts +6 -0
- package/dist/types/validation-receipt.d.ts +1 -1
- package/dist/types/validation.d.ts +2 -2
- package/package.json +16 -12
- package/planu-native.json +1 -1
- package/planu-plugin.json +23 -40
- package/scripts/lib/pending-release-file.mjs +93 -0
- package/src/i18n/messages/en.json +0 -41
- package/src/i18n/messages/es.json +0 -41
- package/src/i18n/messages/pt.json +0 -41
- package/dist/cli/commands/activate.d.ts +0 -14
- package/dist/cli/commands/activate.js +0 -174
- package/dist/config/license-plans.json +0 -251
- package/dist/config/product-public.ts +0 -148
- package/dist/config/server-instructions.ts +0 -121
- package/dist/config/version-defaults.ts +0 -76
- package/dist/config/version.ts +0 -21
- package/dist/engine/ai-integration/gemini/settings-generator.d.ts +0 -4
- package/dist/engine/ai-integration/gemini/settings-generator.js +0 -195
- package/dist/engine/ai-integration/kiro/hooks-generator.d.ts +0 -6
- package/dist/engine/ai-integration/kiro/hooks-generator.js +0 -75
- package/dist/engine/hooks/full-spectrum-generator.d.ts +0 -32
- package/dist/engine/hooks/full-spectrum-generator.js +0 -117
- package/dist/engine/license-validator/config-loader.d.ts +0 -5
- package/dist/engine/license-validator/config-loader.js +0 -16
- package/dist/engine/license-validator/core.d.ts +0 -15
- package/dist/engine/license-validator/core.js +0 -151
- package/dist/engine/license-validator/fingerprint.d.ts +0 -3
- package/dist/engine/license-validator/fingerprint.js +0 -17
- package/dist/engine/license-validator/lemon-squeezy.d.ts +0 -5
- package/dist/engine/license-validator/lemon-squeezy.js +0 -55
- package/dist/engine/license-validator.d.ts +0 -5
- package/dist/engine/license-validator.js +0 -6
- package/dist/engine/product-intelligence/index.d.ts +0 -7
- package/dist/engine/product-intelligence/index.js +0 -211
- package/dist/engine/session/auto-save.d.ts +0 -18
- package/dist/engine/session/auto-save.js +0 -98
- package/dist/engine/session/context-snapshot.d.ts +0 -8
- package/dist/engine/session/context-snapshot.js +0 -49
- package/dist/engine/session/memory-sync.d.ts +0 -12
- package/dist/engine/session/memory-sync.js +0 -34
- package/dist/engine/session/session-diff.d.ts +0 -11
- package/dist/engine/session/session-diff.js +0 -42
- package/dist/engine/session/session-merge.d.ts +0 -12
- package/dist/engine/session/session-merge.js +0 -55
- package/dist/engine/session-safeguard/checkpoint-runner.d.ts +0 -4
- package/dist/engine/session-safeguard/checkpoint-runner.js +0 -70
- package/dist/engine/session-safeguard/learnings-buffer.d.ts +0 -15
- package/dist/engine/session-safeguard/learnings-buffer.js +0 -91
- package/dist/engine/session-safeguard/precompaction-drain.d.ts +0 -10
- package/dist/engine/session-safeguard/precompaction-drain.js +0 -82
- package/dist/engine/session-safeguard/unpushed-detector.d.ts +0 -7
- package/dist/engine/session-safeguard/unpushed-detector.js +0 -59
- package/dist/engine/spec-changelog/core.d.ts +0 -16
- package/dist/engine/spec-changelog/core.js +0 -176
- package/dist/engine/spec-changelog/diff.d.ts +0 -18
- package/dist/engine/spec-changelog/diff.js +0 -121
- package/dist/engine/spec-changelog/index.d.ts +0 -3
- package/dist/engine/spec-changelog/index.js +0 -4
- package/dist/engine/trial-engine.d.ts +0 -5
- package/dist/engine/trial-engine.js +0 -14
- package/dist/engine/usage-tracker/rate-check.d.ts +0 -7
- package/dist/engine/usage-tracker/rate-check.js +0 -18
- package/dist/engine/usage-tracker/rate-limiter.d.ts +0 -14
- package/dist/engine/usage-tracker/rate-limiter.js +0 -73
- package/dist/engine/usage-tracker/trial.d.ts +0 -27
- package/dist/engine/usage-tracker/trial.js +0 -49
- package/dist/engine/webhook/event-handlers.d.ts +0 -25
- package/dist/engine/webhook/event-handlers.js +0 -89
- package/dist/engine/webhook/index.d.ts +0 -4
- package/dist/engine/webhook/index.js +0 -5
- package/dist/engine/webhook/server.d.ts +0 -12
- package/dist/engine/webhook/server.js +0 -249
- package/dist/engine/webhook/signature.d.ts +0 -16
- package/dist/engine/webhook/signature.js +0 -49
- package/dist/storage/license-store.d.ts +0 -6
- package/dist/storage/license-store.js +0 -63
- package/dist/storage/session-store.d.ts +0 -20
- package/dist/storage/session-store.js +0 -136
- package/dist/tools/activate-license.d.ts +0 -6
- package/dist/tools/activate-license.js +0 -132
- package/dist/tools/checkpoint-handler.d.ts +0 -3
- package/dist/tools/checkpoint-handler.js +0 -130
- package/dist/tools/export-session.d.ts +0 -3
- package/dist/tools/export-session.js +0 -90
- package/dist/tools/license-gate.d.ts +0 -18
- package/dist/tools/license-gate.js +0 -222
- package/dist/tools/license-status.d.ts +0 -3
- package/dist/tools/license-status.js +0 -93
- package/dist/tools/list-sessions.d.ts +0 -3
- package/dist/tools/list-sessions.js +0 -46
- package/dist/tools/planu-session-checkpoint.d.ts +0 -6
- package/dist/tools/planu-session-checkpoint.js +0 -42
- package/dist/tools/restore-session.d.ts +0 -3
- package/dist/tools/restore-session.js +0 -71
- package/dist/tools/spec-history.d.ts +0 -3
- package/dist/tools/spec-history.js +0 -59
- package/dist/tools/webhook.d.ts +0 -6
- package/dist/tools/webhook.js +0 -150
- package/dist/types/changelog.d.ts +0 -49
- package/dist/types/changelog.js +0 -3
- package/dist/types/licensing.d.ts +0 -109
- package/dist/types/licensing.js +0 -4
- package/dist/types/product-intelligence.d.ts +0 -60
- package/dist/types/product-intelligence.js +0 -3
- package/dist/types/session.d.ts +0 -84
- package/dist/types/session.js +0 -3
- package/dist/types/webhook.d.ts +0 -62
- package/dist/types/webhook.js +0 -4
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { lstat, readFile, readlink } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { isValidationSourcePath } from './validation-source-policy.js';
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
const SOURCE_DIGEST_MAX_CONCURRENCY = 8;
|
|
9
|
+
async function mapWithConcurrency(values, concurrency, worker) {
|
|
10
|
+
const results = new Array(values.length);
|
|
11
|
+
let cursor = 0;
|
|
12
|
+
const runners = Array.from({ length: Math.min(concurrency, values.length) }, async () => {
|
|
13
|
+
for (;;) {
|
|
14
|
+
const index = cursor;
|
|
15
|
+
cursor += 1;
|
|
16
|
+
if (index >= values.length) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
results[index] = await worker(values[index]);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
await Promise.all(runners);
|
|
23
|
+
return results;
|
|
24
|
+
}
|
|
25
|
+
export async function gitSourceTreeDigest(projectPath) {
|
|
26
|
+
try {
|
|
27
|
+
const gitOptions = { cwd: projectPath, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 };
|
|
28
|
+
const [trackedResult, untrackedResult] = await Promise.all([
|
|
29
|
+
execFileAsync('git', ['ls-files', '--cached', '-z'], gitOptions),
|
|
30
|
+
execFileAsync('git', ['ls-files', '--others', '--exclude-standard', '-z'], gitOptions),
|
|
31
|
+
]);
|
|
32
|
+
const sourceEntries = (stdout) => stdout
|
|
33
|
+
.split('\0')
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.map((sourcePath) => ({ sourcePath, canonicalPath: sourcePath.normalize('NFC') }))
|
|
36
|
+
.filter(({ canonicalPath }) => isValidationSourcePath(canonicalPath));
|
|
37
|
+
const entriesToRead = [
|
|
38
|
+
...sourceEntries(trackedResult.stdout),
|
|
39
|
+
...sourceEntries(untrackedResult.stdout),
|
|
40
|
+
].sort((left, right) => Buffer.compare(Buffer.from(left.canonicalPath), Buffer.from(right.canonicalPath)));
|
|
41
|
+
if (new Set(entriesToRead.map(({ canonicalPath }) => canonicalPath)).size !== entriesToRead.length) {
|
|
42
|
+
throw new Error('source paths collide after Unicode normalization');
|
|
43
|
+
}
|
|
44
|
+
const entries = await mapWithConcurrency(entriesToRead, SOURCE_DIGEST_MAX_CONCURRENCY, async (entry) => {
|
|
45
|
+
const { canonicalPath: path, sourcePath } = entry;
|
|
46
|
+
const absolutePath = join(projectPath, sourcePath);
|
|
47
|
+
try {
|
|
48
|
+
const stats = await lstat(absolutePath);
|
|
49
|
+
if (stats.isSymbolicLink()) {
|
|
50
|
+
const target = (await readlink(absolutePath)).normalize('NFC');
|
|
51
|
+
return { path, content: Buffer.from(`symlink\0${target}`, 'utf8') };
|
|
52
|
+
}
|
|
53
|
+
if (!stats.isFile()) {
|
|
54
|
+
throw new Error(`unsupported source entry type: ${path}`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
path,
|
|
58
|
+
content: Buffer.concat([Buffer.from('file\0'), await readFile(absolutePath)]),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
if (error.code === 'ENOENT') {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const hash = createHash('sha256');
|
|
69
|
+
hash.update('planu-source-v1\0');
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
if (!entry) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const pathBytes = Buffer.from(entry.path, 'utf8');
|
|
75
|
+
hash.update(`${pathBytes.length}:`);
|
|
76
|
+
hash.update(pathBytes);
|
|
77
|
+
hash.update(`${entry.content.length}:`);
|
|
78
|
+
hash.update(entry.content);
|
|
79
|
+
}
|
|
80
|
+
return `sha256:${hash.digest('hex')}`;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw new Error('[Planu] Unable to bind validation to the source tree', { cause: error });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=validation-source-digest.js.map
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { DurableJobRuntime } from '../execution/job-runtime.js';
|
|
2
2
|
import type { DurableJobRecord } from '../../storage/runtime-db.js';
|
|
3
3
|
import type { DurableValidationBindings } from '../../types/durable-validation.js';
|
|
4
|
-
import type { ValidationArtifactDigests } from '../../types/validation-receipt.js';
|
|
5
|
-
import type { ValidationFreshnessLease } from './validation-freshness.js';
|
|
4
|
+
import type { ValidationArtifactDigests, ValidationFreshnessLease } from '../../types/validation-receipt.js';
|
|
6
5
|
/** Stable identity for exactly one immutable validation input tuple. */
|
|
7
|
-
export declare function validationOperationId(bindings: DurableValidationBindings): string;
|
|
6
|
+
export declare function validationOperationId(bindings: DurableValidationBindings, validationRootId: string): string;
|
|
8
7
|
/** Stable identity used to acknowledge before repository-wide bindings are computed. */
|
|
9
|
-
export declare function validationSubmissionOperationId(projectId: string, specId: string): string;
|
|
8
|
+
export declare function validationSubmissionOperationId(projectId: string, specId: string, validationRootId: string): string;
|
|
10
9
|
/** Accept one pre-bound durable validation for internal and compatibility callers. */
|
|
11
10
|
export declare function createValidationSubmission(input: {
|
|
12
11
|
readonly runtime: DurableJobRuntime;
|
|
@@ -16,6 +15,7 @@ export declare function createValidationSubmission(input: {
|
|
|
16
15
|
readonly projectId?: string;
|
|
17
16
|
readonly specPath?: string;
|
|
18
17
|
readonly projectPath?: string;
|
|
18
|
+
readonly validationRootId: string;
|
|
19
19
|
};
|
|
20
20
|
readonly bindings: DurableValidationBindings;
|
|
21
21
|
readonly artifactDigests?: ValidationArtifactDigests;
|
|
@@ -30,6 +30,7 @@ export declare function createPendingValidationSubmission(input: {
|
|
|
30
30
|
readonly id: string;
|
|
31
31
|
readonly specPath: string;
|
|
32
32
|
readonly projectPath: string;
|
|
33
|
+
readonly validationRootId: string;
|
|
33
34
|
};
|
|
34
35
|
readonly deadlineAt: number;
|
|
35
36
|
}): DurableJobRecord;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { DIGEST, PROJECT_ID as CANONICAL_PROJECT_ID, SPEC_ID as CANONICAL_SPEC_ID, } from './validation-receipt-codec.js';
|
|
3
|
+
import { isValidationRootId } from './validation-worktree.js';
|
|
3
4
|
function assertBindings(bindings) {
|
|
4
5
|
if (!CANONICAL_PROJECT_ID.test(bindings.projectId)) {
|
|
5
6
|
throw new Error('[Planu] Durable validation requires a canonical projectId');
|
|
@@ -17,11 +18,15 @@ function assertBindings(bindings) {
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
/** Stable identity for exactly one immutable validation input tuple. */
|
|
20
|
-
export function validationOperationId(bindings) {
|
|
21
|
+
export function validationOperationId(bindings, validationRootId) {
|
|
21
22
|
assertBindings(bindings);
|
|
23
|
+
if (!isValidationRootId(validationRootId)) {
|
|
24
|
+
throw new Error('[Planu] Durable validation requires a valid validationRootId');
|
|
25
|
+
}
|
|
22
26
|
const operationDigest = createHash('sha256')
|
|
23
27
|
.update(JSON.stringify([
|
|
24
|
-
'planu-validation-
|
|
28
|
+
'planu-validation-v2',
|
|
29
|
+
validationRootId,
|
|
25
30
|
bindings.projectId,
|
|
26
31
|
bindings.specId,
|
|
27
32
|
bindings.sourceTreeHash,
|
|
@@ -33,12 +38,14 @@ export function validationOperationId(bindings) {
|
|
|
33
38
|
return `validation-${operationDigest}`;
|
|
34
39
|
}
|
|
35
40
|
/** Stable identity used to acknowledge before repository-wide bindings are computed. */
|
|
36
|
-
export function validationSubmissionOperationId(projectId, specId) {
|
|
37
|
-
if (!CANONICAL_PROJECT_ID.test(projectId) ||
|
|
41
|
+
export function validationSubmissionOperationId(projectId, specId, validationRootId) {
|
|
42
|
+
if (!CANONICAL_PROJECT_ID.test(projectId) ||
|
|
43
|
+
!CANONICAL_SPEC_ID.test(specId) ||
|
|
44
|
+
!isValidationRootId(validationRootId)) {
|
|
38
45
|
throw new Error('[Planu] Invalid durable validation submission identity');
|
|
39
46
|
}
|
|
40
47
|
const operationDigest = createHash('sha256')
|
|
41
|
-
.update(JSON.stringify(['planu-validation-submission-
|
|
48
|
+
.update(JSON.stringify(['planu-validation-submission-v2', projectId, specId, validationRootId]))
|
|
42
49
|
.digest('hex');
|
|
43
50
|
return `validation-${operationDigest}`;
|
|
44
51
|
}
|
|
@@ -70,6 +77,9 @@ function existingOrAccept(input) {
|
|
|
70
77
|
/** Accept one pre-bound durable validation for internal and compatibility callers. */
|
|
71
78
|
export function createValidationSubmission(input) {
|
|
72
79
|
assertBindings(input.bindings);
|
|
80
|
+
if (!isValidationRootId(input.spec.validationRootId) || !input.spec.specPath) {
|
|
81
|
+
throw new Error('[Planu] Durable validation requires a verified validation root and spec path');
|
|
82
|
+
}
|
|
73
83
|
if (input.canonicalProjectId !== input.bindings.projectId ||
|
|
74
84
|
input.spec.id !== input.bindings.specId) {
|
|
75
85
|
throw new Error('[Planu] Durable validation binding identity mismatch');
|
|
@@ -79,7 +89,8 @@ export function createValidationSubmission(input) {
|
|
|
79
89
|
}
|
|
80
90
|
const payload = {
|
|
81
91
|
kind: 'validate',
|
|
82
|
-
schemaVersion:
|
|
92
|
+
schemaVersion: 2,
|
|
93
|
+
validationRootId: input.spec.validationRootId,
|
|
83
94
|
input: {
|
|
84
95
|
specId: input.spec.id,
|
|
85
96
|
projectId: input.canonicalProjectId,
|
|
@@ -88,20 +99,22 @@ export function createValidationSubmission(input) {
|
|
|
88
99
|
bindings: input.bindings,
|
|
89
100
|
...(input.artifactDigests === undefined ? {} : { artifactDigests: input.artifactDigests }),
|
|
90
101
|
...(input.freshnessLease === undefined ? {} : { freshnessLease: input.freshnessLease }),
|
|
91
|
-
|
|
102
|
+
specPath: input.spec.specPath ?? '',
|
|
92
103
|
deadlineAt: input.deadlineAt,
|
|
93
104
|
};
|
|
94
105
|
return existingOrAccept({
|
|
95
106
|
runtime: input.runtime,
|
|
96
107
|
projectId: input.canonicalProjectId,
|
|
97
|
-
operationId: validationOperationId(input.bindings),
|
|
108
|
+
operationId: validationOperationId(input.bindings, input.spec.validationRootId),
|
|
98
109
|
payload,
|
|
99
110
|
});
|
|
100
111
|
}
|
|
101
112
|
/** Persist validation immediately; the worker computes and binds the source snapshot. */
|
|
102
113
|
export function createPendingValidationSubmission(input) {
|
|
103
114
|
if (!CANONICAL_PROJECT_ID.test(input.canonicalProjectId) ||
|
|
104
|
-
!CANONICAL_SPEC_ID.test(input.spec.id)
|
|
115
|
+
!CANONICAL_SPEC_ID.test(input.spec.id) ||
|
|
116
|
+
!isValidationRootId(input.spec.validationRootId) ||
|
|
117
|
+
!input.spec.specPath) {
|
|
105
118
|
throw new Error('[Planu] Invalid durable validation submission identity');
|
|
106
119
|
}
|
|
107
120
|
if (!Number.isSafeInteger(input.deadlineAt) || input.deadlineAt <= Date.now()) {
|
|
@@ -109,7 +122,8 @@ export function createPendingValidationSubmission(input) {
|
|
|
109
122
|
}
|
|
110
123
|
const payload = {
|
|
111
124
|
kind: 'validate',
|
|
112
|
-
schemaVersion:
|
|
125
|
+
schemaVersion: 2,
|
|
126
|
+
validationRootId: input.spec.validationRootId,
|
|
113
127
|
input: {
|
|
114
128
|
specId: input.spec.id,
|
|
115
129
|
projectId: input.canonicalProjectId,
|
|
@@ -121,7 +135,7 @@ export function createPendingValidationSubmission(input) {
|
|
|
121
135
|
return existingOrAccept({
|
|
122
136
|
runtime: input.runtime,
|
|
123
137
|
projectId: input.canonicalProjectId,
|
|
124
|
-
operationId: validationSubmissionOperationId(input.canonicalProjectId, input.spec.id),
|
|
138
|
+
operationId: validationSubmissionOperationId(input.canonicalProjectId, input.spec.id, input.spec.validationRootId),
|
|
125
139
|
payload,
|
|
126
140
|
});
|
|
127
141
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ResolvedValidationWorktree, ValidationWorktreeErrorCode } from '../../types/durable-validation.js';
|
|
2
|
+
export declare class ValidationWorktreeError extends Error {
|
|
3
|
+
readonly code: ValidationWorktreeErrorCode;
|
|
4
|
+
readonly specId: string;
|
|
5
|
+
readonly relativeSpecPath?: string;
|
|
6
|
+
constructor(code: ValidationWorktreeErrorCode, specId: string, relativeSpecPath?: string);
|
|
7
|
+
}
|
|
8
|
+
export declare function resolveValidationWorktree(input: {
|
|
9
|
+
requestedProjectPath: string;
|
|
10
|
+
canonicalProjectPath: string;
|
|
11
|
+
canonicalSpecPath: string;
|
|
12
|
+
specId: string;
|
|
13
|
+
}): Promise<ResolvedValidationWorktree>;
|
|
14
|
+
export declare function revalidateValidationWorktree(input: {
|
|
15
|
+
projectPath: string;
|
|
16
|
+
specPath: string;
|
|
17
|
+
specId: string;
|
|
18
|
+
validationRootId: string;
|
|
19
|
+
}): Promise<ResolvedValidationWorktree>;
|
|
20
|
+
export declare function isValidationRootId(value: unknown): value is string;
|
|
21
|
+
//# sourceMappingURL=validation-worktree.d.ts.map
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { lstat, readFile, realpath } from 'node:fs/promises';
|
|
4
|
+
import { basename, dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
5
|
+
import { promisify } from 'node:util';
|
|
6
|
+
import { reportClassifiedDegradation } from '../../errors/classified-degradation.js';
|
|
7
|
+
import { parseFrontmatter } from '../frontmatter-parser.js';
|
|
8
|
+
const runFile = promisify(execFile);
|
|
9
|
+
const ROOT_ID = /^[a-f0-9]{64}$/u;
|
|
10
|
+
const SAFE_SPEC_ID = /^SPEC-[1-9]\d*$/u;
|
|
11
|
+
const PORTABLE_SPEC = /^planu\/specs\/(SPEC-\d{3,}(?:-[^/]+)?)\/spec\.md$/u;
|
|
12
|
+
export class ValidationWorktreeError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
specId;
|
|
15
|
+
relativeSpecPath;
|
|
16
|
+
constructor(code, specId, relativeSpecPath) {
|
|
17
|
+
const safeSpecId = SAFE_SPEC_ID.test(specId) ? specId : 'SPEC-unknown';
|
|
18
|
+
const safeRelativeSpecPath = relativeSpecPath !== undefined && PORTABLE_SPEC.test(relativeSpecPath)
|
|
19
|
+
? relativeSpecPath
|
|
20
|
+
: undefined;
|
|
21
|
+
super(`[Planu] ${code} spec=${safeSpecId}${safeRelativeSpecPath ? ` path=${safeRelativeSpecPath}` : ''}`);
|
|
22
|
+
this.code = code;
|
|
23
|
+
this.name = 'ValidationWorktreeError';
|
|
24
|
+
this.specId = safeSpecId;
|
|
25
|
+
this.relativeSpecPath = safeRelativeSpecPath;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function fail(code, specId, relativeSpecPath) {
|
|
29
|
+
throw new ValidationWorktreeError(code, specId, relativeSpecPath);
|
|
30
|
+
}
|
|
31
|
+
function isEnoent(error) {
|
|
32
|
+
return error.code === 'ENOENT';
|
|
33
|
+
}
|
|
34
|
+
async function gitPath(projectPath, argument) {
|
|
35
|
+
try {
|
|
36
|
+
const result = await runFile('git', ['-C', projectPath, 'rev-parse', argument], {
|
|
37
|
+
encoding: 'utf8',
|
|
38
|
+
timeout: 10_000,
|
|
39
|
+
maxBuffer: 64 * 1024,
|
|
40
|
+
});
|
|
41
|
+
const output = result.stdout.trim();
|
|
42
|
+
return await realpath(isAbsolute(output) ? output : resolve(projectPath, output));
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
reportClassifiedDegradation('VALIDATION_GIT_PATH_PROBE', error);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function resolveGitMembership(projectPath, specId) {
|
|
50
|
+
let requestedRoot;
|
|
51
|
+
try {
|
|
52
|
+
requestedRoot = await realpath(projectPath);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
reportClassifiedDegradation('VALIDATION_ROOT_REALPATH', error);
|
|
56
|
+
}
|
|
57
|
+
if (requestedRoot === undefined) {
|
|
58
|
+
fail('VALIDATION_ROOT_NOT_GIT_WORKTREE', specId);
|
|
59
|
+
}
|
|
60
|
+
const [topLevel, commonDir] = await Promise.all([
|
|
61
|
+
gitPath(requestedRoot, '--show-toplevel'),
|
|
62
|
+
gitPath(requestedRoot, '--git-common-dir'),
|
|
63
|
+
]);
|
|
64
|
+
if (topLevel === null || commonDir === null) {
|
|
65
|
+
fail('VALIDATION_ROOT_NOT_GIT_WORKTREE', specId);
|
|
66
|
+
}
|
|
67
|
+
return { projectPath: topLevel, gitCommonDir: commonDir };
|
|
68
|
+
}
|
|
69
|
+
async function resolveGitRoot(projectPath, specId) {
|
|
70
|
+
let requestedRoot;
|
|
71
|
+
try {
|
|
72
|
+
requestedRoot = await realpath(projectPath);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
reportClassifiedDegradation('VALIDATION_ROOT_REALPATH', error);
|
|
76
|
+
}
|
|
77
|
+
if (requestedRoot === undefined) {
|
|
78
|
+
fail('VALIDATION_ROOT_NOT_GIT_WORKTREE', specId);
|
|
79
|
+
}
|
|
80
|
+
const membership = await resolveGitMembership(requestedRoot, specId);
|
|
81
|
+
if (membership.projectPath !== requestedRoot) {
|
|
82
|
+
fail('VALIDATION_ROOT_NOT_GIT_WORKTREE', specId);
|
|
83
|
+
}
|
|
84
|
+
return membership;
|
|
85
|
+
}
|
|
86
|
+
async function resolveSpecSourceRoot(input) {
|
|
87
|
+
const specPath = isAbsolute(input.canonicalSpecPath)
|
|
88
|
+
? input.canonicalSpecPath
|
|
89
|
+
: resolve(input.canonical.projectPath, input.canonicalSpecPath);
|
|
90
|
+
let source;
|
|
91
|
+
try {
|
|
92
|
+
source = await resolveGitMembership(dirname(specPath), input.specId);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
throw new ValidationWorktreeError('VALIDATION_SPEC_PATH_TRAVERSAL', input.specId);
|
|
96
|
+
}
|
|
97
|
+
if (source.gitCommonDir !== input.canonical.gitCommonDir) {
|
|
98
|
+
fail('VALIDATION_COMMON_DIR_MISMATCH', input.specId);
|
|
99
|
+
}
|
|
100
|
+
return { projectPath: source.projectPath, specPath };
|
|
101
|
+
}
|
|
102
|
+
async function portableSpecPath(projectPath, submittedSpecPath, specId) {
|
|
103
|
+
const candidate = isAbsolute(submittedSpecPath)
|
|
104
|
+
? submittedSpecPath
|
|
105
|
+
: resolve(projectPath, submittedSpecPath);
|
|
106
|
+
let stats;
|
|
107
|
+
try {
|
|
108
|
+
stats = await lstat(candidate);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (!isEnoent(error)) {
|
|
112
|
+
reportClassifiedDegradation('VALIDATION_SPEC_STAT_FAILED', error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (stats === undefined) {
|
|
116
|
+
const normalizedMissingPath = await resolveFromExistingAncestor(candidate);
|
|
117
|
+
if (!contained(projectPath, normalizedMissingPath)) {
|
|
118
|
+
fail('VALIDATION_SPEC_PATH_TRAVERSAL', specId);
|
|
119
|
+
}
|
|
120
|
+
fail('VALIDATION_SPEC_NOT_FOUND', specId);
|
|
121
|
+
}
|
|
122
|
+
if (stats.isSymbolicLink()) {
|
|
123
|
+
fail('VALIDATION_SPEC_PATH_SYMLINK', specId);
|
|
124
|
+
}
|
|
125
|
+
let canonicalPath;
|
|
126
|
+
try {
|
|
127
|
+
canonicalPath = await realpath(candidate);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
reportClassifiedDegradation('VALIDATION_SPEC_REALPATH_FAILED', error);
|
|
131
|
+
}
|
|
132
|
+
if (canonicalPath === undefined || !contained(projectPath, canonicalPath)) {
|
|
133
|
+
fail('VALIDATION_SPEC_PATH_TRAVERSAL', specId);
|
|
134
|
+
}
|
|
135
|
+
const portablePath = relative(projectPath, canonicalPath).replaceAll('\\', '/');
|
|
136
|
+
const match = PORTABLE_SPEC.exec(portablePath);
|
|
137
|
+
if (!match?.[1] || (match[1] !== specId && !match[1].startsWith(`${specId}-`))) {
|
|
138
|
+
fail('VALIDATION_SPEC_PATH_TRAVERSAL', specId);
|
|
139
|
+
}
|
|
140
|
+
return portablePath;
|
|
141
|
+
}
|
|
142
|
+
function contained(root, candidate) {
|
|
143
|
+
const path = relative(root, candidate);
|
|
144
|
+
return path.length > 0 && path !== '..' && !path.startsWith(`..${sep}`) && !isAbsolute(path);
|
|
145
|
+
}
|
|
146
|
+
async function resolveFromExistingAncestor(candidate) {
|
|
147
|
+
const suffix = [];
|
|
148
|
+
let cursor = candidate;
|
|
149
|
+
for (;;) {
|
|
150
|
+
let resolvedAncestor;
|
|
151
|
+
try {
|
|
152
|
+
resolvedAncestor = await realpath(cursor);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (!isEnoent(error)) {
|
|
156
|
+
reportClassifiedDegradation('VALIDATION_ANCESTOR_REALPATH_FAILED', error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (resolvedAncestor !== undefined) {
|
|
160
|
+
return resolve(resolvedAncestor, ...suffix.reverse());
|
|
161
|
+
}
|
|
162
|
+
const parent = dirname(cursor);
|
|
163
|
+
if (parent === cursor) {
|
|
164
|
+
return candidate;
|
|
165
|
+
}
|
|
166
|
+
suffix.push(basename(cursor));
|
|
167
|
+
cursor = parent;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function resolveSpec(projectPath, relativeSpecPath, specId) {
|
|
171
|
+
const candidate = resolve(projectPath, relativeSpecPath);
|
|
172
|
+
if (!contained(projectPath, candidate)) {
|
|
173
|
+
fail('VALIDATION_SPEC_PATH_TRAVERSAL', specId, relativeSpecPath);
|
|
174
|
+
}
|
|
175
|
+
let stats;
|
|
176
|
+
try {
|
|
177
|
+
stats = await lstat(candidate);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
if (!isEnoent(error)) {
|
|
181
|
+
reportClassifiedDegradation('VALIDATION_SPEC_STAT_FAILED', error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (stats === undefined) {
|
|
185
|
+
fail('VALIDATION_SPEC_NOT_FOUND', specId, relativeSpecPath);
|
|
186
|
+
}
|
|
187
|
+
if (stats.isSymbolicLink()) {
|
|
188
|
+
fail('VALIDATION_SPEC_PATH_SYMLINK', specId, relativeSpecPath);
|
|
189
|
+
}
|
|
190
|
+
if (!stats.isFile()) {
|
|
191
|
+
fail('VALIDATION_SPEC_NOT_FOUND', specId, relativeSpecPath);
|
|
192
|
+
}
|
|
193
|
+
let specPath;
|
|
194
|
+
try {
|
|
195
|
+
specPath = await realpath(candidate);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
reportClassifiedDegradation('VALIDATION_SPEC_REALPATH_FAILED', error);
|
|
199
|
+
}
|
|
200
|
+
if (specPath === undefined || !contained(projectPath, specPath)) {
|
|
201
|
+
fail('VALIDATION_SPEC_PATH_TRAVERSAL', specId, relativeSpecPath);
|
|
202
|
+
}
|
|
203
|
+
let content;
|
|
204
|
+
try {
|
|
205
|
+
content = await readFile(specPath, 'utf8');
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
if (!isEnoent(error)) {
|
|
209
|
+
reportClassifiedDegradation('VALIDATION_SPEC_READ_FAILED', error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const parsedId = content === undefined ? undefined : parseFrontmatter(content).metadata.id;
|
|
213
|
+
if (parsedId !== specId) {
|
|
214
|
+
fail('VALIDATION_SPEC_ID_MISMATCH', specId, relativeSpecPath);
|
|
215
|
+
}
|
|
216
|
+
return { specPath, relativeSpecPath };
|
|
217
|
+
}
|
|
218
|
+
function buildValidationRootId(projectPath, gitCommonDir) {
|
|
219
|
+
return createHash('sha256')
|
|
220
|
+
.update(`validation-root/v1\0${projectPath}\0${gitCommonDir}`)
|
|
221
|
+
.digest('hex');
|
|
222
|
+
}
|
|
223
|
+
export async function resolveValidationWorktree(input) {
|
|
224
|
+
const [requested, canonical] = await Promise.all([
|
|
225
|
+
resolveGitRoot(input.requestedProjectPath, input.specId),
|
|
226
|
+
resolveGitRoot(input.canonicalProjectPath, input.specId),
|
|
227
|
+
]);
|
|
228
|
+
if (requested.gitCommonDir !== canonical.gitCommonDir) {
|
|
229
|
+
fail('VALIDATION_COMMON_DIR_MISMATCH', input.specId);
|
|
230
|
+
}
|
|
231
|
+
const source = await resolveSpecSourceRoot({
|
|
232
|
+
canonical,
|
|
233
|
+
canonicalSpecPath: input.canonicalSpecPath,
|
|
234
|
+
specId: input.specId,
|
|
235
|
+
});
|
|
236
|
+
const relativeSpecPath = await portableSpecPath(source.projectPath, source.specPath, input.specId);
|
|
237
|
+
const spec = await resolveSpec(requested.projectPath, relativeSpecPath, input.specId);
|
|
238
|
+
return {
|
|
239
|
+
...requested,
|
|
240
|
+
...spec,
|
|
241
|
+
validationRootId: buildValidationRootId(requested.projectPath, requested.gitCommonDir),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
export async function revalidateValidationWorktree(input) {
|
|
245
|
+
if (!ROOT_ID.test(input.validationRootId)) {
|
|
246
|
+
fail('VALIDATION_ROOT_ID_MALFORMED', input.specId);
|
|
247
|
+
}
|
|
248
|
+
const root = await resolveGitRoot(input.projectPath, input.specId);
|
|
249
|
+
const relativeSpecPath = await portableSpecPath(root.projectPath, input.specPath, input.specId);
|
|
250
|
+
const spec = await resolveSpec(root.projectPath, relativeSpecPath, input.specId);
|
|
251
|
+
const observedRootId = buildValidationRootId(root.projectPath, root.gitCommonDir);
|
|
252
|
+
if (observedRootId !== input.validationRootId) {
|
|
253
|
+
fail('VALIDATION_ROOT_ID_MISMATCH', input.specId, spec.relativeSpecPath);
|
|
254
|
+
}
|
|
255
|
+
return { ...root, ...spec, validationRootId: observedRootId };
|
|
256
|
+
}
|
|
257
|
+
export function isValidationRootId(value) {
|
|
258
|
+
return typeof value === 'string' && ROOT_ID.test(value);
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=validation-worktree.js.map
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const VALIDATION_COMMANDS:
|
|
1
|
+
import type { ValidationImpactPlan } from '../types/validation-evidence.js';
|
|
2
|
+
export declare const VALIDATION_COMMANDS: {
|
|
3
|
+
readonly typecheck: 'pnpm typecheck';
|
|
4
|
+
readonly lint: 'pnpm lint';
|
|
5
|
+
readonly 'test-related': 'pnpm test --run --changed';
|
|
6
|
+
readonly 'test-full': 'pnpm test';
|
|
7
|
+
readonly contract: 'pnpm test:contracts';
|
|
8
|
+
readonly coverage: 'pnpm test:coverage';
|
|
9
|
+
readonly migration: 'pnpm test:migrations';
|
|
10
|
+
readonly 'fault-injection': 'pnpm test:fault-injection';
|
|
11
|
+
readonly security: 'pnpm check:deps:fresh';
|
|
12
|
+
readonly native: 'pnpm native:project-graph:e2e';
|
|
13
|
+
readonly website: 'pnpm --dir website build';
|
|
14
|
+
};
|
|
3
15
|
export declare function planValidationImpact(paths: readonly string[]): ValidationImpactPlan;
|
|
4
16
|
//# sourceMappingURL=validation-impact-planner.d.ts.map
|
|
@@ -10,7 +10,6 @@ export const VALIDATION_COMMANDS = {
|
|
|
10
10
|
security: 'pnpm check:deps:fresh',
|
|
11
11
|
native: 'pnpm native:project-graph:e2e',
|
|
12
12
|
website: 'pnpm --dir website build',
|
|
13
|
-
reproducibility: 'bash scripts/check-reproducibility.sh',
|
|
14
13
|
};
|
|
15
14
|
const LIFECYCLE_RE = /^(?:planu\/|docs\/|README(?:\.|$)|CHANGELOG\.md$|[^/]+\.md$)/;
|
|
16
15
|
const SOURCE_RE = /^(?:src\/|tests\/)/;
|
|
@@ -44,7 +43,6 @@ export function planValidationImpact(paths) {
|
|
|
44
43
|
else if (NATIVE_RE.test(path)) {
|
|
45
44
|
kind = 'native';
|
|
46
45
|
gates.add('native');
|
|
47
|
-
gates.add('reproducibility');
|
|
48
46
|
gates.add('test-full');
|
|
49
47
|
}
|
|
50
48
|
else if (DEPENDENCY_RE.test(path)) {
|
|
@@ -71,7 +69,6 @@ function fullPlan(reason, changedPaths) {
|
|
|
71
69
|
'migration',
|
|
72
70
|
'fault-injection',
|
|
73
71
|
'native',
|
|
74
|
-
'reproducibility',
|
|
75
72
|
];
|
|
76
73
|
if (changedPaths.some((path) => path.startsWith('website/'))) {
|
|
77
74
|
gates.push('website');
|
|
@@ -5,6 +5,9 @@ import { runAllGates } from './gates/index.js';
|
|
|
5
5
|
/** Minimum test file count that auto-passes test gates (batch-implementation heuristic). */
|
|
6
6
|
const BATCH_TEST_THRESHOLD = 5;
|
|
7
7
|
import { parseProgressActuals } from '../actuals/progress-parser.js';
|
|
8
|
+
export function isBlockingQualitySeverity(severity) {
|
|
9
|
+
return severity !== 'info' && severity !== 'warning';
|
|
10
|
+
}
|
|
8
11
|
function buildDoRSpecItems(spec) {
|
|
9
12
|
// Adapt description to actual file name (modern: spec.md, legacy: HU.md)
|
|
10
13
|
const specFileName = spec.specPath ? basename(spec.specPath) : 'spec.md';
|
|
@@ -320,9 +323,8 @@ function gateCategory(gate) {
|
|
|
320
323
|
*/
|
|
321
324
|
export async function generateDoD(spec, validationResult, codeState, projectPath) {
|
|
322
325
|
const score = validationResult?.score ?? 0;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const hasBlockingQualityIssues = validationResult?.qualityIssues.some((i) => blockingSeverities.includes(i.severity)) ?? false;
|
|
326
|
+
const hasBlockingQualityIssues = validationResult?.qualityIssues.some((issue) => isBlockingQualitySeverity(issue.severity)) ??
|
|
327
|
+
false;
|
|
326
328
|
const hasTestFiles = await detectTestFiles(spec, codeState);
|
|
327
329
|
const items = buildDoDItems(spec, score, hasBlockingQualityIssues, hasTestFiles);
|
|
328
330
|
// Override actuals check with progress.md parsing
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Spec, VerifyBlock } from '../../types/index.js';
|
|
2
|
+
export { extractListItems, extractSection } from '../spec-format/markdown-sections.js';
|
|
2
3
|
/**
|
|
3
4
|
* Extract all acceptance criteria from a spec's source files.
|
|
4
5
|
*/
|
|
@@ -13,9 +14,4 @@ export declare function extractVerifyBlocks(content: string): Map<string, Verify
|
|
|
13
14
|
/**
|
|
14
15
|
* Extract the text content under a markdown heading that matches any of the given keywords.
|
|
15
16
|
*/
|
|
16
|
-
export declare function extractSection(content: string, ...headingKeywords: string[]): string | null;
|
|
17
|
-
/**
|
|
18
|
-
* Extract bullet/numbered list items from a markdown text block.
|
|
19
|
-
*/
|
|
20
|
-
export declare function extractListItems(content: string): string[];
|
|
21
17
|
//# sourceMappingURL=extractors.d.ts.map
|