create-quiver 0.10.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BACKLOG.md +16 -17
- package/CHANGELOG.md +78 -0
- package/README.md +208 -41
- package/README_FOR_AI.md +50 -24
- package/ROADMAP.md +34 -11
- package/docs/AI_CONTEXT.md.template +2 -0
- package/docs/AI_ONBOARDING_PROMPT.md.template +31 -18
- package/docs/COMMANDS.md.template +90 -16
- package/docs/CONTEXTO.md.template +2 -0
- package/docs/DECISIONS.md.template +1 -0
- package/docs/INDEX.md.template +20 -18
- package/docs/STATUS.md.template +6 -1
- package/docs/SUPPORT_MATRIX.md.template +2 -2
- package/docs/TROUBLESHOOTING.md.template +50 -0
- package/docs/WORKFLOW.md.template +27 -17
- package/package.json +27 -4
- package/package.template.json +13 -1
- package/scripts/init-docs.sh +11 -4
- package/scripts/package-quiver.sh +18 -2
- package/specs/quiver-v22-guided-ai-workflow/EVIDENCE_REPORT.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/EXECUTION_PLAN.md +88 -0
- package/specs/quiver-v22-guided-ai-workflow/SPEC.md +228 -0
- package/specs/quiver-v22-guided-ai-workflow/STATUS.md +42 -0
- package/specs/quiver-v22-guided-ai-workflow/pr.md +104 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-01-docs-source-of-truth-sync/slice.json +55 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-02-prepare-command-diagnostics/slice.json +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-03-context-doc-refresh/slice.json +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-04-planner-approval-state/slice.json +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-05-spec-worktree-lifecycle/slice.json +54 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-06-executor-commit-recovery/slice.json +57 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-07-execution-waves-delegation/slice.json +55 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-08-pr-create-gh-ssh/slice.json +53 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/EXECUTION_BRIEF.md +59 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-09-post-merge-cleanup-release-safety/slice.json +59 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +58 -0
- package/specs/quiver-v22-guided-ai-workflow/slices/slice-10-docs-smokes-release-readiness/slice.json +60 -0
- package/specs/quiver-v23-guided-flow-productization/EVIDENCE_REPORT.md +80 -0
- package/specs/quiver-v23-guided-flow-productization/EXECUTION_PLAN.md +80 -0
- package/specs/quiver-v23-guided-flow-productization/SPEC.md +203 -0
- package/specs/quiver-v23-guided-flow-productization/STATUS.md +39 -0
- package/specs/quiver-v23-guided-flow-productization/pr.md +119 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +61 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/EXECUTION_BRIEF.md +35 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-01-short-command-and-flow-entrypoint/slice.json +56 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-02-flow-status-wizard/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-03-agent-profiles/slice.json +54 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-04-context-preparation-onboarding/slice.json +59 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/EXECUTION_BRIEF.md +29 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-05-planner-iteration-history/slice.json +53 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-06-production-plan-review/slice.json +54 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-07-spec-create-experience/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/EXECUTION_BRIEF.md +30 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-08-executor-prompt-generation/slice.json +55 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/EXECUTION_BRIEF.md +34 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-09-delegated-slice-execution/slice.json +57 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +32 -0
- package/specs/quiver-v23-guided-flow-productization/slices/slice-10-docs-smokes-release-readiness/slice.json +63 -0
- package/specs/quiver-v24-dx-onboarding-hardening/EVIDENCE_REPORT.md +55 -0
- package/specs/quiver-v24-dx-onboarding-hardening/EXECUTION_PLAN.md +43 -0
- package/specs/quiver-v24-dx-onboarding-hardening/SPEC.md +149 -0
- package/specs/quiver-v24-dx-onboarding-hardening/STATUS.md +31 -0
- package/specs/quiver-v24-dx-onboarding-hardening/pr.md +76 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +31 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-00-spec-foundation/slice.json +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-01-init-template-hygiene/slice.json +55 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-02-cli-command-routing-version-errors/slice.json +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-03-doctor-fix-doc-link-checks/slice.json +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-04-prepare-output-ai-context-drafts/slice.json +70 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/EXECUTION_BRIEF.md +49 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-05-local-slice-validation-base-guidance/slice.json +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-06-plan-graph-next-history-views/slice.json +60 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/CLOSURE_BRIEF.md +32 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/EXECUTION_BRIEF.md +50 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-07-analyzer-command-map-hardening/slice.json +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-08-evidence-run-command/slice.json +54 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-09-spec-viewer-demo-scaffolding/slice.json +59 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v24-dx-onboarding-hardening/slices/slice-10-docs-smokes-release-readiness/slice.json +76 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EVIDENCE_REPORT.md +293 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/EXECUTION_PLAN.md +58 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/SPEC.md +242 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/STATUS.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/pr.md +77 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-00-spec-foundation/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-01-cli-contract-compatibility/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/CLOSURE_BRIEF.md +43 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-02-run-state-phase-locks/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-03-safe-ai-onboarding-docs/slice.json +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-04-agent-profiles-adapters/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-05-approval-gates/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-06-spec-slice-generator/slice.json +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/CLOSURE_BRIEF.md +33 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-07-slice-execution-planner/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/EXECUTION_BRIEF.md +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-08-controlled-slice-execution/slice.json +53 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/EXECUTION_BRIEF.md +57 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-09-git-worktree-pr-lifecycle/slice.json +52 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/CLOSURE_BRIEF.md +39 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-10-validation-errors-fixtures/slice.json +56 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/CLOSURE_BRIEF.md +36 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v25-ai-first-lifecycle-orchestrator/slices/slice-11-export-dashboard-migration/slice.json +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/EVIDENCE_REPORT.md +208 -0
- package/specs/quiver-v26-0121-smoke-hardening/EXECUTION_PLAN.md +57 -0
- package/specs/quiver-v26-0121-smoke-hardening/SPEC.md +137 -0
- package/specs/quiver-v26-0121-smoke-hardening/STATUS.md +32 -0
- package/specs/quiver-v26-0121-smoke-hardening/pr.md +96 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-00-docs-foundation/slice.json +73 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/CLOSURE_BRIEF.md +38 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/EXECUTION_BRIEF.md +51 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-01-cli-help-version-contract/slice.json +76 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-02-init-doc-links-and-flow-guidance/slice.json +75 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/CLOSURE_BRIEF.md +37 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-03-ai-approval-review-consistency/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/EXECUTION_BRIEF.md +52 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-04-local-validation-brief-contracts/slice.json +77 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/CLOSURE_BRIEF.md +34 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/EXECUTION_BRIEF.md +54 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-05-demo-scaffold-readiness/slice.json +84 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/EXECUTION_BRIEF.md +53 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-06-plan-graph-scope-performance/slice.json +82 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/CLOSURE_BRIEF.md +35 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/EXECUTION_BRIEF.md +55 -0
- package/specs/quiver-v26-0121-smoke-hardening/slices/slice-07-smoke-release-readiness/slice.json +92 -0
- package/src/create-quiver/commands/ai.js +1060 -37
- package/src/create-quiver/commands/demo.js +22 -0
- package/src/create-quiver/commands/evidence.js +37 -0
- package/src/create-quiver/commands/flow.js +562 -0
- package/src/create-quiver/commands/graph.js +19 -4
- package/src/create-quiver/commands/next.js +28 -0
- package/src/create-quiver/commands/plan.js +9 -6
- package/src/create-quiver/commands/prepare.js +236 -0
- package/src/create-quiver/commands/spec.js +133 -0
- package/src/create-quiver/index.js +1010 -31
- package/src/create-quiver/lib/actionable-error.js +27 -0
- package/src/create-quiver/lib/agent-profiles.js +148 -0
- package/src/create-quiver/lib/ai/context-packs.js +16 -0
- package/src/create-quiver/lib/ai/execution-plan.js +377 -11
- package/src/create-quiver/lib/ai/executor.js +633 -24
- package/src/create-quiver/lib/ai/export-state.js +534 -0
- package/src/create-quiver/lib/ai/github.js +279 -0
- package/src/create-quiver/lib/ai/onboarding-template.js +578 -0
- package/src/create-quiver/lib/ai/plan-review.js +286 -0
- package/src/create-quiver/lib/ai/providers.js +5 -3
- package/src/create-quiver/lib/ai/run-state.js +414 -0
- package/src/create-quiver/lib/ai/safety.js +5 -0
- package/src/create-quiver/lib/ai/spec-generator.js +12 -0
- package/src/create-quiver/lib/ai/spec-templates.js +80 -11
- package/src/create-quiver/lib/approvals.js +369 -0
- package/src/create-quiver/lib/demo.js +832 -0
- package/src/create-quiver/lib/doctor.js +309 -0
- package/src/create-quiver/lib/evidence.js +115 -0
- package/src/create-quiver/lib/handoff.js +81 -12
- package/src/create-quiver/lib/init-docs.js +302 -17
- package/src/create-quiver/lib/init-layout.js +34 -1
- package/src/create-quiver/lib/json.js +53 -3
- package/src/create-quiver/lib/lifecycle.js +6 -0
- package/src/create-quiver/lib/package-safety.js +117 -0
- package/src/create-quiver/lib/readiness.js +103 -21
- package/src/create-quiver/lib/scope.js +50 -7
- package/src/create-quiver/lib/slice-graph.js +138 -37
- package/src/create-quiver/lib/slice.js +14 -9
- package/src/create-quiver/lib/spec-worktrees.js +363 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const PACKAGE_PREFIX = 'package/';
|
|
2
|
+
|
|
3
|
+
const SAFETY_RULES = [
|
|
4
|
+
{
|
|
5
|
+
code: 'env-file',
|
|
6
|
+
match(relativePath) {
|
|
7
|
+
return /(^|\/)\.env($|[./])/.test(relativePath);
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
code: 'npm-credentials',
|
|
12
|
+
match(relativePath) {
|
|
13
|
+
return /(^|\/)\.npmrc$/.test(relativePath) || /(^|\/)\.npm(\/|$)/.test(relativePath);
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
code: 'ai-tool-state',
|
|
18
|
+
match(relativePath) {
|
|
19
|
+
return /(^|\/)\.(claude|codex|quiver)(\/|$)/.test(relativePath);
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
code: 'worktree-state',
|
|
24
|
+
match(relativePath) {
|
|
25
|
+
return /(^|\/)\.worktrees(\/|$)/.test(relativePath);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
code: 'worktree-context',
|
|
30
|
+
match(relativePath) {
|
|
31
|
+
return /(^|\/)WORKTREE_CONTEXT\.md$/.test(relativePath);
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
code: 'demo-output',
|
|
36
|
+
match(relativePath) {
|
|
37
|
+
return relativePath === 'quiver-spec-viewer' || relativePath.startsWith('quiver-spec-viewer/');
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
function normalizeTarballPath(inputPath) {
|
|
43
|
+
return String(inputPath || '')
|
|
44
|
+
.trim()
|
|
45
|
+
.replace(/\\/g, '/')
|
|
46
|
+
.replace(/^\.\//, '')
|
|
47
|
+
.replace(/\/{2,}/g, '/');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function stripPackagePrefix(inputPath) {
|
|
51
|
+
const normalizedPath = normalizeTarballPath(inputPath);
|
|
52
|
+
|
|
53
|
+
if (normalizedPath.startsWith(PACKAGE_PREFIX)) {
|
|
54
|
+
return normalizedPath.slice(PACKAGE_PREFIX.length);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return normalizedPath;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function collectPackageSafetyViolations(paths) {
|
|
61
|
+
const violations = [];
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
|
|
64
|
+
for (const rawPath of paths || []) {
|
|
65
|
+
const normalizedPath = normalizeTarballPath(rawPath);
|
|
66
|
+
const relativePath = stripPackagePrefix(normalizedPath);
|
|
67
|
+
|
|
68
|
+
for (const rule of SAFETY_RULES) {
|
|
69
|
+
if (!rule.match(relativePath)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const key = `${rule.code}:${normalizedPath}`;
|
|
74
|
+
if (seen.has(key)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
seen.add(key);
|
|
79
|
+
violations.push({
|
|
80
|
+
code: rule.code,
|
|
81
|
+
path: normalizedPath,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return violations;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function formatPackageSafetyViolations(violations) {
|
|
90
|
+
return violations
|
|
91
|
+
.map((violation) => `${violation.path} [${violation.code}]`)
|
|
92
|
+
.join(', ');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function assertPackageSafety(paths) {
|
|
96
|
+
const violations = collectPackageSafetyViolations(paths);
|
|
97
|
+
|
|
98
|
+
if (violations.length === 0) {
|
|
99
|
+
return {
|
|
100
|
+
ok: true,
|
|
101
|
+
violations,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const error = new Error(`PACKAGE_SAFETY_FAILED: unsafe tarball contents detected: ${formatPackageSafetyViolations(violations)}`);
|
|
106
|
+
error.code = 'PACKAGE_SAFETY_FAILED';
|
|
107
|
+
error.violations = violations;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = {
|
|
112
|
+
assertPackageSafety,
|
|
113
|
+
collectPackageSafetyViolations,
|
|
114
|
+
formatPackageSafetyViolations,
|
|
115
|
+
normalizeTarballPath,
|
|
116
|
+
stripPackagePrefix,
|
|
117
|
+
};
|
|
@@ -2,7 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { catFileExists, currentBranch, hasLocalBranch, hasRemoteBranch, mergeBaseIsAncestor, revListCount, runGit, statusPorcelain, worktreeList } = require('./git');
|
|
4
4
|
const { parseJsonWithComments } = require('./json');
|
|
5
|
-
const { buildGraph, readAllSlices, SliceGraphError, topoSort } = require('./slice-graph');
|
|
5
|
+
const { buildGraph, normalizeDeclaredDependencies, readAllSlices, SliceGraphError, topoSort } = require('./slice-graph');
|
|
6
6
|
const { resolveSliceContext, toAlias } = require('./slice');
|
|
7
7
|
|
|
8
8
|
function ensureExists(filePath, message) {
|
|
@@ -56,7 +56,7 @@ function parseWorktrees(text) {
|
|
|
56
56
|
return entries;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function collectOverlapWarnings(repoRoot, currentBranchName, currentFiles) {
|
|
59
|
+
function collectOverlapWarnings(repoRoot, currentBranchName, currentFiles, baseRef = 'origin/develop') {
|
|
60
60
|
const sliceMap = new Map();
|
|
61
61
|
walkSlices(path.join(repoRoot, 'specs'), sliceMap, repoRoot);
|
|
62
62
|
walkSlices(path.join(repoRoot, 'specs-fix'), sliceMap, repoRoot);
|
|
@@ -79,7 +79,7 @@ function collectOverlapWarnings(repoRoot, currentBranchName, currentFiles) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const dirty = statusPorcelain(worktreePath) !== '';
|
|
82
|
-
const aheadCount = revListCount(worktreePath,
|
|
82
|
+
const aheadCount = revListCount(worktreePath, `${baseRef}..HEAD`);
|
|
83
83
|
const active = dirty || aheadCount > 0;
|
|
84
84
|
|
|
85
85
|
if (!active) {
|
|
@@ -95,6 +95,76 @@ function collectOverlapWarnings(repoRoot, currentBranchName, currentFiles) {
|
|
|
95
95
|
return warnings;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
function validateLocalSliceArtifacts(repoRoot, slice) {
|
|
99
|
+
const sliceDir = path.dirname(slice.sliceAbs);
|
|
100
|
+
for (const file of ['EXECUTION_BRIEF.md', 'CLOSURE_BRIEF.md']) {
|
|
101
|
+
ensureExists(path.join(sliceDir, file), `create-quiver: falta '${path.posix.join(path.dirname(slice.sliceRel), file)}'.`);
|
|
102
|
+
}
|
|
103
|
+
console.log('PASS: El slice local tiene EXECUTION_BRIEF.md y CLOSURE_BRIEF.md.');
|
|
104
|
+
|
|
105
|
+
if (!Array.isArray(slice.json.files) || slice.json.files.length === 0) {
|
|
106
|
+
throw new Error('create-quiver: slice.json debe declarar al menos un archivo en files para validacion local.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const invalidFiles = slice.json.files.filter((file) => typeof file !== 'string' || file.trim().length === 0);
|
|
110
|
+
if (invalidFiles.length > 0) {
|
|
111
|
+
throw new Error('create-quiver: slice.json.files contiene entradas invalidas.');
|
|
112
|
+
}
|
|
113
|
+
console.log('PASS: slice.json declara archivos de alcance.');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function baseRecoveryMessage(remote, baseBranch) {
|
|
117
|
+
return `No se encontro la base '${baseBranch}' como rama local ni como '${remote}/${baseBranch}'. Para validacion estructural usa --local; para validacion contra otra base usa --base <branch>; o configura/fetchea el remoto '${remote}'.`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function resolveReadinessRoot(localMode) {
|
|
121
|
+
try {
|
|
122
|
+
return runGit(['rev-parse', '--show-toplevel'], process.cwd());
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (localMode) {
|
|
125
|
+
return process.cwd();
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function validateSliceDocumentedOnBase(repoRoot, slice, options = {}) {
|
|
132
|
+
const gate = options.gate || 'execution';
|
|
133
|
+
const remote = options.remote || 'origin';
|
|
134
|
+
const baseBranch = options.baseBranch || slice.baseBranch || 'develop';
|
|
135
|
+
const remoteRef = `${remote}/${baseBranch}`;
|
|
136
|
+
const hasRemoteBase = hasRemoteBranch(repoRoot, baseBranch, remote);
|
|
137
|
+
const hasLocalBase = hasLocalBranch(repoRoot, baseBranch);
|
|
138
|
+
|
|
139
|
+
if (hasRemoteBase && catFileExists(repoRoot, `${remoteRef}:${slice.sliceRel}`)) {
|
|
140
|
+
console.log(`PASS: El slice ya existe en ${remoteRef} (PR base documental mergeado).`);
|
|
141
|
+
return remoteRef;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (hasLocalBase && catFileExists(repoRoot, `${baseBranch}:${slice.sliceRel}`)) {
|
|
145
|
+
console.log(`PASS: El slice ya existe en ${baseBranch} local (modo sin remote).`);
|
|
146
|
+
return baseBranch;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (!hasRemoteBase && !hasLocalBase) {
|
|
150
|
+
const guidance = baseRecoveryMessage(remote, baseBranch);
|
|
151
|
+
if (gate === 'validation') {
|
|
152
|
+
console.log(`WARN: ${guidance}`);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
throw new Error(`create-quiver: ${guidance}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const expectedBase = hasRemoteBase ? remoteRef : baseBranch;
|
|
160
|
+
if (gate === 'validation') {
|
|
161
|
+
console.log(`WARN: El slice no existe todavia en ${expectedBase}. El PR base documental sigue pendiente de merge. Podes abrir el PR del slice igual si el humano mergea en orden.`);
|
|
162
|
+
return expectedBase;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
throw new Error(`create-quiver: el slice no existe en ${expectedBase}. Mergea primero el PR base documental o usa --local para validar solo estructura local.`);
|
|
166
|
+
}
|
|
167
|
+
|
|
98
168
|
function validateDeclaredDependencyContract(repoRoot, slice) {
|
|
99
169
|
const declaredDependsOn = Array.isArray(slice.json.depends_on) ? slice.json.depends_on : null;
|
|
100
170
|
const declaredParallelSafe = typeof slice.json.parallel_safe === 'string' ? slice.json.parallel_safe.trim() : '';
|
|
@@ -119,8 +189,12 @@ function validateDeclaredDependencyContract(repoRoot, slice) {
|
|
|
119
189
|
throw new Error(`create-quiver: depends_on contiene referencias duplicadas en ${currentRef}.`);
|
|
120
190
|
}
|
|
121
191
|
|
|
192
|
+
const normalizedDeclared = normalizeDeclaredDependencies(currentNode, declared);
|
|
193
|
+
if (normalizedDeclared.length !== new Set(normalizedDeclared).size) {
|
|
194
|
+
throw new Error(`create-quiver: depends_on contiene referencias duplicadas en ${currentRef}.`);
|
|
195
|
+
}
|
|
122
196
|
const currentSet = new Set(currentNode.depends_on || []);
|
|
123
|
-
for (const dep of
|
|
197
|
+
for (const dep of normalizedDeclared) {
|
|
124
198
|
if (!currentSet.has(dep)) {
|
|
125
199
|
throw new Error(`create-quiver: depends_on apunta a una referencia inexistente o invalida: ${dep}`);
|
|
126
200
|
}
|
|
@@ -147,35 +221,43 @@ function validateDeclaredDependencyContract(repoRoot, slice) {
|
|
|
147
221
|
|
|
148
222
|
function checkSliceReadiness(sliceInput, options = {}) {
|
|
149
223
|
const gate = options.gate || 'execution';
|
|
224
|
+
const localMode = options.local === true;
|
|
150
225
|
const strictOverlap = options.strictOverlap === true;
|
|
151
|
-
const
|
|
226
|
+
const remote = options.remote || 'origin';
|
|
227
|
+
const repoRoot = resolveReadinessRoot(localMode);
|
|
152
228
|
const slice = resolveSliceContext(repoRoot, sliceInput);
|
|
229
|
+
const baseBranch = options.baseBranch || slice.baseBranch || 'develop';
|
|
153
230
|
|
|
154
231
|
for (const specFile of ['SPEC.md', 'STATUS.md', 'EVIDENCE_REPORT.md']) {
|
|
155
232
|
ensureExists(path.join(repoRoot, slice.specDirRel, specFile), `create-quiver: falta '${slice.specDirRel}/${specFile}'.`);
|
|
156
233
|
}
|
|
157
234
|
console.log('PASS: El spec local tiene SPEC.md, STATUS.md y EVIDENCE_REPORT.md.');
|
|
158
235
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
console.log(
|
|
163
|
-
|
|
164
|
-
console.log('WARN: El slice no existe todavia en origin/develop. El PR base documental sigue pendiente de merge. Podes abrir el PR del slice igual — el humano mergea en orden.');
|
|
236
|
+
let baseRef = null;
|
|
237
|
+
if (localMode) {
|
|
238
|
+
validateLocalSliceArtifacts(repoRoot, slice);
|
|
239
|
+
console.log(`INFO: Modo local: se omite validacion de existencia del slice en ${remote}/${baseBranch} o ${baseBranch}.`);
|
|
240
|
+
console.log('INFO: Modo local: se omite validacion de overlap contra worktrees activos basada en rama remota/base.');
|
|
165
241
|
} else {
|
|
166
|
-
|
|
242
|
+
baseRef = validateSliceDocumentedOnBase(repoRoot, slice, {
|
|
243
|
+
baseBranch,
|
|
244
|
+
gate,
|
|
245
|
+
remote,
|
|
246
|
+
});
|
|
167
247
|
}
|
|
168
248
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
249
|
+
if (!localMode) {
|
|
250
|
+
const overlapWarnings = collectOverlapWarnings(repoRoot, currentBranch(repoRoot), slice.files, baseRef || `${remote}/${baseBranch}`);
|
|
251
|
+
if (overlapWarnings.length === 0) {
|
|
252
|
+
console.log('PASS: No se detecto overlap con worktrees activos.');
|
|
253
|
+
} else {
|
|
254
|
+
for (const warning of overlapWarnings) {
|
|
255
|
+
const [overlapBranch, overlapFiles] = warning.split('|');
|
|
256
|
+
if (strictOverlap) {
|
|
257
|
+
throw new Error(`create-quiver: Overlap con worktree activo '${overlapBranch}': ${overlapFiles}`);
|
|
258
|
+
}
|
|
259
|
+
console.log(`WARN: Overlap con worktree activo '${overlapBranch}': ${overlapFiles}`);
|
|
177
260
|
}
|
|
178
|
-
console.log(`WARN: Overlap con worktree activo '${overlapBranch}': ${overlapFiles}`);
|
|
179
261
|
}
|
|
180
262
|
}
|
|
181
263
|
|
|
@@ -19,6 +19,48 @@ function normalizeScopePath(filePath) {
|
|
|
19
19
|
return normalizeContextPath(filePath);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function globToRegExp(pattern) {
|
|
23
|
+
const source = String(pattern || '')
|
|
24
|
+
.split('')
|
|
25
|
+
.map((char, index, chars) => {
|
|
26
|
+
if (char === '*') {
|
|
27
|
+
return chars[index + 1] === '*' ? '\0' : '[^/]*';
|
|
28
|
+
}
|
|
29
|
+
if (char === '\0') {
|
|
30
|
+
return '.*';
|
|
31
|
+
}
|
|
32
|
+
return /[\\^$+?.()|[\]{}]/.test(char) ? `\\${char}` : char;
|
|
33
|
+
})
|
|
34
|
+
.join('')
|
|
35
|
+
.replace(/\0\[\^\/\]\*/g, '.*');
|
|
36
|
+
|
|
37
|
+
return new RegExp(`^${source}$`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function allowedPathMatches(filePath, allowedPath) {
|
|
41
|
+
const file = normalizeScopePath(filePath);
|
|
42
|
+
const allowed = normalizeScopePath(allowedPath);
|
|
43
|
+
|
|
44
|
+
if (!file || !allowed) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (file === allowed) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (allowed.endsWith('/**')) {
|
|
53
|
+
const prefix = allowed.slice(0, -3);
|
|
54
|
+
return file === prefix || file.startsWith(`${prefix}/`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (allowed.includes('*')) {
|
|
58
|
+
return globToRegExp(allowed).test(file);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
22
64
|
function parseStatusPorcelain(text) {
|
|
23
65
|
if (!text) {
|
|
24
66
|
return [];
|
|
@@ -74,30 +116,30 @@ function diffWorktreeSnapshots(beforeSnapshot, afterSnapshot) {
|
|
|
74
116
|
}
|
|
75
117
|
|
|
76
118
|
function validateScopeSnapshot({ allowedFiles = [], beforeSnapshot, afterSnapshot, strict = true } = {}) {
|
|
77
|
-
const normalizedAllowedFiles = new Set(
|
|
119
|
+
const normalizedAllowedFiles = Array.from(new Set(
|
|
78
120
|
Array.isArray(allowedFiles)
|
|
79
121
|
? allowedFiles.map(normalizeScopePath).filter(Boolean)
|
|
80
122
|
: [],
|
|
81
|
-
);
|
|
123
|
+
));
|
|
82
124
|
const changedFiles = diffWorktreeSnapshots(beforeSnapshot, afterSnapshot);
|
|
83
|
-
const outOfScopeFiles = changedFiles.filter((file) => !normalizedAllowedFiles.
|
|
125
|
+
const outOfScopeFiles = changedFiles.filter((file) => !normalizedAllowedFiles.some((allowedFile) => allowedPathMatches(file, allowedFile)));
|
|
84
126
|
|
|
85
127
|
if (outOfScopeFiles.length === 0) {
|
|
86
128
|
return {
|
|
87
129
|
ok: true,
|
|
88
130
|
changedFiles,
|
|
89
131
|
outOfScopeFiles,
|
|
90
|
-
allowedFiles:
|
|
132
|
+
allowedFiles: normalizedAllowedFiles,
|
|
91
133
|
beforeSnapshot,
|
|
92
134
|
afterSnapshot,
|
|
93
135
|
};
|
|
94
136
|
}
|
|
95
137
|
|
|
96
138
|
const message = formatError(
|
|
97
|
-
`scope violation detected: changed files outside slice
|
|
139
|
+
`scope violation detected: changed files outside declared slice scope: ${outOfScopeFiles.join(', ')}`,
|
|
98
140
|
);
|
|
99
141
|
const error = new ScopeValidationError('SCOPE_VIOLATION', message, {
|
|
100
|
-
allowedFiles:
|
|
142
|
+
allowedFiles: normalizedAllowedFiles,
|
|
101
143
|
beforeSnapshot,
|
|
102
144
|
afterSnapshot,
|
|
103
145
|
changedFiles,
|
|
@@ -112,7 +154,7 @@ function validateScopeSnapshot({ allowedFiles = [], beforeSnapshot, afterSnapsho
|
|
|
112
154
|
ok: false,
|
|
113
155
|
changedFiles,
|
|
114
156
|
outOfScopeFiles,
|
|
115
|
-
allowedFiles:
|
|
157
|
+
allowedFiles: normalizedAllowedFiles,
|
|
116
158
|
beforeSnapshot,
|
|
117
159
|
afterSnapshot,
|
|
118
160
|
error,
|
|
@@ -121,6 +163,7 @@ function validateScopeSnapshot({ allowedFiles = [], beforeSnapshot, afterSnapsho
|
|
|
121
163
|
|
|
122
164
|
module.exports = {
|
|
123
165
|
ScopeValidationError,
|
|
166
|
+
allowedPathMatches,
|
|
124
167
|
captureWorktreeSnapshot,
|
|
125
168
|
diffWorktreeSnapshots,
|
|
126
169
|
checkScope,
|
|
@@ -66,6 +66,15 @@ function sortFileList(files) {
|
|
|
66
66
|
return Array.from(new Set((Array.isArray(files) ? files : []).map((file) => String(file)).filter(Boolean))).sort((a, b) => a.localeCompare(b));
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
function resolveWriteScope(json) {
|
|
70
|
+
const allowedWritePaths = sortFileList(json?.allowed_write_paths);
|
|
71
|
+
if (allowedWritePaths.length > 0) {
|
|
72
|
+
return allowedWritePaths;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return sortFileList(json?.files);
|
|
76
|
+
}
|
|
77
|
+
|
|
69
78
|
function normalizeDependencyRef(slice, dependency) {
|
|
70
79
|
const dep = String(dependency || '').trim();
|
|
71
80
|
if (!dep) {
|
|
@@ -95,6 +104,61 @@ function normalizeDependencyRef(slice, dependency) {
|
|
|
95
104
|
return `${slice.specSlug}/${dep}`;
|
|
96
105
|
}
|
|
97
106
|
|
|
107
|
+
function readSliceFile(rootDir, rootName, specSlug, sliceDirName) {
|
|
108
|
+
const sliceDir = path.join(rootDir, rootName, specSlug, 'slices', sliceDirName);
|
|
109
|
+
const slicePath = path.join(sliceDir, 'slice.json');
|
|
110
|
+
if (!fs.existsSync(slicePath)) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const json = parseJsonWithComments(fs.readFileSync(slicePath, 'utf8'));
|
|
115
|
+
const sliceId = String(json.slice_id || sliceDirName).trim();
|
|
116
|
+
const ref = `${specSlug}/${sliceId}`;
|
|
117
|
+
return {
|
|
118
|
+
ref,
|
|
119
|
+
specFamily: rootName,
|
|
120
|
+
specSlug,
|
|
121
|
+
sliceId,
|
|
122
|
+
slicePath,
|
|
123
|
+
sliceDir,
|
|
124
|
+
files: resolveWriteScope(json),
|
|
125
|
+
expected_read_paths: sortFileList(json.expected_read_paths),
|
|
126
|
+
allowed_write_paths: sortFileList(json.allowed_write_paths),
|
|
127
|
+
validation_hints: sortFileList(json.validation_hints),
|
|
128
|
+
dependencies: Array.isArray(json.dependencies) ? json.dependencies.map((item) => String(item).trim()).filter(Boolean) : [],
|
|
129
|
+
depends_on: Array.isArray(json.depends_on) ? json.depends_on.map((item) => String(item).trim()).filter(Boolean) : [],
|
|
130
|
+
parallel_safe: typeof json.parallel_safe === 'string' ? json.parallel_safe : null,
|
|
131
|
+
parallel_safe_reason: typeof json.parallel_safe_reason === 'string' ? json.parallel_safe_reason : null,
|
|
132
|
+
status: typeof json.status === 'string' ? json.status : 'draft',
|
|
133
|
+
ticket: typeof json.ticket === 'string' ? json.ticket : '',
|
|
134
|
+
title: typeof json.title === 'string' ? json.title : sliceId,
|
|
135
|
+
json,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function readSpecSlices(rootDir, rootName, specSlug) {
|
|
140
|
+
const slicesDir = path.join(rootDir, rootName, specSlug, 'slices');
|
|
141
|
+
if (!fs.existsSync(slicesDir)) {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const slices = [];
|
|
146
|
+
|
|
147
|
+
for (const sliceEntry of fs.readdirSync(slicesDir, { withFileTypes: true })) {
|
|
148
|
+
if (!sliceEntry.isDirectory() || isPlaceholderSliceDir(sliceEntry.name)) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const slice = readSliceFile(rootDir, rootName, specSlug, sliceEntry.name);
|
|
153
|
+
if (slice) {
|
|
154
|
+
slices.push(slice);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
slices.sort((left, right) => compareSliceRefs(left.ref, right.ref));
|
|
159
|
+
return slices;
|
|
160
|
+
}
|
|
161
|
+
|
|
98
162
|
function readAllSlices(rootDir) {
|
|
99
163
|
const roots = ['specs', 'specs-fix'];
|
|
100
164
|
const slices = [];
|
|
@@ -110,43 +174,7 @@ function readAllSlices(rootDir) {
|
|
|
110
174
|
continue;
|
|
111
175
|
}
|
|
112
176
|
|
|
113
|
-
|
|
114
|
-
const slicesDir = path.join(rootPath, specSlug, 'slices');
|
|
115
|
-
if (!fs.existsSync(slicesDir)) {
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
for (const sliceEntry of fs.readdirSync(slicesDir, { withFileTypes: true })) {
|
|
120
|
-
if (!sliceEntry.isDirectory() || isPlaceholderSliceDir(sliceEntry.name)) {
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const sliceDir = path.join(slicesDir, sliceEntry.name);
|
|
125
|
-
const slicePath = path.join(sliceDir, 'slice.json');
|
|
126
|
-
if (!fs.existsSync(slicePath)) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const json = parseJsonWithComments(fs.readFileSync(slicePath, 'utf8'));
|
|
131
|
-
const sliceId = String(json.slice_id || sliceEntry.name).trim();
|
|
132
|
-
const ref = `${specSlug}/${sliceId}`;
|
|
133
|
-
slices.push({
|
|
134
|
-
ref,
|
|
135
|
-
specFamily: rootName,
|
|
136
|
-
specSlug,
|
|
137
|
-
sliceId,
|
|
138
|
-
slicePath,
|
|
139
|
-
sliceDir,
|
|
140
|
-
files: sortFileList(json.files),
|
|
141
|
-
dependencies: Array.isArray(json.dependencies) ? json.dependencies.map((item) => String(item).trim()).filter(Boolean) : [],
|
|
142
|
-
depends_on: Array.isArray(json.depends_on) ? json.depends_on.map((item) => String(item).trim()).filter(Boolean) : [],
|
|
143
|
-
parallel_safe: typeof json.parallel_safe === 'string' ? json.parallel_safe : null,
|
|
144
|
-
parallel_safe_reason: typeof json.parallel_safe_reason === 'string' ? json.parallel_safe_reason : null,
|
|
145
|
-
status: typeof json.status === 'string' ? json.status : 'draft',
|
|
146
|
-
title: typeof json.title === 'string' ? json.title : sliceId,
|
|
147
|
-
json,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
177
|
+
slices.push(...readSpecSlices(rootDir, rootName, specEntry.name));
|
|
150
178
|
}
|
|
151
179
|
}
|
|
152
180
|
|
|
@@ -170,6 +198,75 @@ function declaredDependenciesForSlice(slice) {
|
|
|
170
198
|
return null;
|
|
171
199
|
}
|
|
172
200
|
|
|
201
|
+
function readSliceByRef(rootDir, ref) {
|
|
202
|
+
const value = String(ref || '').trim();
|
|
203
|
+
const slashIndex = value.indexOf('/');
|
|
204
|
+
if (slashIndex === -1) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const specSlug = value.slice(0, slashIndex);
|
|
209
|
+
const sliceId = value.slice(slashIndex + 1);
|
|
210
|
+
if (!specSlug || !sliceId) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const rootName of ['specs', 'specs-fix']) {
|
|
215
|
+
const direct = readSliceFile(rootDir, rootName, specSlug, sliceId);
|
|
216
|
+
if (direct) {
|
|
217
|
+
return direct;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
for (const slice of readSpecSlices(rootDir, rootName, specSlug)) {
|
|
221
|
+
if (slice.ref === value) {
|
|
222
|
+
return slice;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function readSlicesForSpec(rootDir, specSlug) {
|
|
231
|
+
const targetSpec = String(specSlug || '').trim();
|
|
232
|
+
if (!targetSpec) {
|
|
233
|
+
return readAllSlices(rootDir);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const byRef = new Map();
|
|
237
|
+
const queue = [];
|
|
238
|
+
|
|
239
|
+
function addSlice(slice) {
|
|
240
|
+
if (!slice || byRef.has(slice.ref)) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
byRef.set(slice.ref, slice);
|
|
244
|
+
queue.push(slice);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (const rootName of ['specs', 'specs-fix']) {
|
|
248
|
+
for (const slice of readSpecSlices(rootDir, rootName, targetSpec)) {
|
|
249
|
+
addSlice(slice);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
while (queue.length > 0) {
|
|
254
|
+
const slice = queue.shift();
|
|
255
|
+
for (const dep of declaredDependenciesForSlice(slice) || []) {
|
|
256
|
+
if (byRef.has(dep)) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const dependencySlice = readSliceByRef(rootDir, dep);
|
|
261
|
+
if (dependencySlice) {
|
|
262
|
+
addSlice(dependencySlice);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return Array.from(byRef.values()).sort((left, right) => compareSliceRefs(left.ref, right.ref));
|
|
268
|
+
}
|
|
269
|
+
|
|
173
270
|
function inferDependencies(slices) {
|
|
174
271
|
const bySpec = new Map();
|
|
175
272
|
const normalized = slices.map((slice) => ({
|
|
@@ -453,7 +550,11 @@ module.exports = {
|
|
|
453
550
|
detectFileConflicts,
|
|
454
551
|
inferDependencies,
|
|
455
552
|
isFoundationSliceId,
|
|
553
|
+
normalizeDeclaredDependencies,
|
|
554
|
+
normalizeDependencyRef,
|
|
456
555
|
readAllSlices,
|
|
556
|
+
readSlicesForSpec,
|
|
457
557
|
naturalNumberFromSliceId,
|
|
558
|
+
resolveWriteScope,
|
|
458
559
|
topoSort,
|
|
459
560
|
};
|
|
@@ -42,14 +42,18 @@ function readSliceMeta(slicePath) {
|
|
|
42
42
|
const branchName = typeof git.branch_name === 'string' ? git.branch_name.trim() : '';
|
|
43
43
|
const sliceId = typeof json.slice_id === 'string' ? json.slice_id.trim() : '';
|
|
44
44
|
const status = String(json.status || 'draft').trim() || 'draft';
|
|
45
|
+
const allowedWritePaths = Array.isArray(json.allowed_write_paths) ? json.allowed_write_paths.map((item) => String(item)) : [];
|
|
46
|
+
const legacyFiles = Array.isArray(json.files) ? json.files.map((item) => String(item)) : [];
|
|
45
47
|
|
|
46
48
|
return {
|
|
47
49
|
acceptance: Array.isArray(json.acceptance) ? json.acceptance : [],
|
|
50
|
+
allowedWritePaths,
|
|
48
51
|
baseBranch,
|
|
49
52
|
branchName,
|
|
50
53
|
branchSlug,
|
|
51
54
|
branchType,
|
|
52
|
-
|
|
55
|
+
expectedReadPaths: Array.isArray(json.expected_read_paths) ? json.expected_read_paths.map((item) => String(item)) : [],
|
|
56
|
+
files: allowedWritePaths.length > 0 ? allowedWritePaths : legacyFiles,
|
|
53
57
|
git,
|
|
54
58
|
isBaseline: sliceId.startsWith('slice-00'),
|
|
55
59
|
json,
|
|
@@ -60,6 +64,7 @@ function readSliceMeta(slicePath) {
|
|
|
60
64
|
status,
|
|
61
65
|
tests: Array.isArray(json.tests) ? json.tests : [],
|
|
62
66
|
ticket,
|
|
67
|
+
validationHints: Array.isArray(json.validation_hints) ? json.validation_hints.map((item) => String(item)) : [],
|
|
63
68
|
};
|
|
64
69
|
}
|
|
65
70
|
|
|
@@ -74,19 +79,19 @@ function validateSliceMetaForStart(slice) {
|
|
|
74
79
|
throw new Error('create-quiver: el bloque "git" debe incluir "branch_type", "base_branch", "branch_slug" y "branch_name".');
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
const
|
|
78
|
-
feature: 'develop',
|
|
79
|
-
bugfix: 'develop',
|
|
80
|
-
hotfix: 'main',
|
|
82
|
+
const allowedBaseByType = {
|
|
83
|
+
feature: ['main', 'develop'],
|
|
84
|
+
bugfix: ['main', 'develop'],
|
|
85
|
+
hotfix: ['main'],
|
|
81
86
|
};
|
|
82
87
|
|
|
83
|
-
if (!
|
|
88
|
+
if (!allowedBaseByType[slice.branchType]) {
|
|
84
89
|
throw new Error(`create-quiver: git.branch_type invalido: "${slice.branchType}". Usa "feature", "bugfix" o "hotfix".`);
|
|
85
90
|
}
|
|
86
91
|
|
|
87
|
-
const
|
|
88
|
-
if (slice.baseBranch
|
|
89
|
-
throw new Error(`create-quiver: git.base_branch invalido para ${slice.branchType}.
|
|
92
|
+
const allowedBaseBranches = allowedBaseByType[slice.branchType];
|
|
93
|
+
if (!allowedBaseBranches.includes(slice.baseBranch)) {
|
|
94
|
+
throw new Error(`create-quiver: git.base_branch invalido para ${slice.branchType}. Usa "${allowedBaseBranches.join('" o "')}".`);
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
const expectedBranchName = `${slice.branchType}/${slice.ticket}-${slice.branchSlug}`;
|