@matteeh/osq 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +234 -15
- package/dist/cli/approve.d.ts +7 -0
- package/dist/cli/approve.d.ts.map +1 -1
- package/dist/cli/approve.js +34 -0
- package/dist/cli/approve.js.map +1 -1
- package/dist/cli/bin.js +2 -3
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/check.d.ts +15 -0
- package/dist/cli/check.d.ts.map +1 -0
- package/dist/cli/check.js +65 -0
- package/dist/cli/check.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +10 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +6 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/lint.d.ts +3 -0
- package/dist/cli/lint.d.ts.map +1 -1
- package/dist/cli/lint.js +33 -14
- package/dist/cli/lint.js.map +1 -1
- package/dist/cli/plan-queue.d.ts +10 -4
- package/dist/cli/plan-queue.d.ts.map +1 -1
- package/dist/cli/plan-queue.js +20 -19
- package/dist/cli/plan-queue.js.map +1 -1
- package/dist/cli/plan-sections.d.ts +15 -0
- package/dist/cli/plan-sections.d.ts.map +1 -0
- package/dist/cli/plan-sections.js +47 -0
- package/dist/cli/plan-sections.js.map +1 -0
- package/dist/cli/plan.d.ts +1 -0
- package/dist/cli/plan.d.ts.map +1 -1
- package/dist/cli/plan.js +14 -4
- package/dist/cli/plan.js.map +1 -1
- package/dist/cli/report.d.ts.map +1 -1
- package/dist/cli/report.js +73 -1
- package/dist/cli/report.js.map +1 -1
- package/dist/cli/run.d.ts +7 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +22 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/verified.d.ts +15 -0
- package/dist/cli/verified.d.ts.map +1 -0
- package/dist/cli/verified.js +34 -0
- package/dist/cli/verified.js.map +1 -0
- package/dist/core/foundation/config-agents.d.ts +16 -0
- package/dist/core/foundation/config-agents.d.ts.map +1 -0
- package/dist/core/foundation/config-agents.js +2 -0
- package/dist/core/foundation/config-agents.js.map +1 -0
- package/dist/core/foundation/config-claude.d.ts +48 -0
- package/dist/core/foundation/config-claude.d.ts.map +1 -0
- package/dist/core/foundation/config-claude.js +106 -0
- package/dist/core/foundation/config-claude.js.map +1 -0
- package/dist/core/foundation/config-codex-resolve.d.ts +10 -0
- package/dist/core/foundation/config-codex-resolve.d.ts.map +1 -0
- package/dist/core/foundation/config-codex-resolve.js +24 -0
- package/dist/core/foundation/config-codex-resolve.js.map +1 -0
- package/dist/core/foundation/config-file.d.ts +18 -0
- package/dist/core/foundation/config-file.d.ts.map +1 -0
- package/dist/core/foundation/config-file.js +46 -0
- package/dist/core/foundation/config-file.js.map +1 -0
- package/dist/core/foundation/config-gates.d.ts +2 -0
- package/dist/core/foundation/config-gates.d.ts.map +1 -1
- package/dist/core/foundation/config-gates.js +8 -0
- package/dist/core/foundation/config-gates.js.map +1 -1
- package/dist/core/foundation/config-opencode.d.ts +25 -0
- package/dist/core/foundation/config-opencode.d.ts.map +1 -0
- package/dist/core/foundation/config-opencode.js +55 -0
- package/dist/core/foundation/config-opencode.js.map +1 -0
- package/dist/core/foundation/config-pi.d.ts +10 -0
- package/dist/core/foundation/config-pi.d.ts.map +1 -1
- package/dist/core/foundation/config-pi.js +13 -0
- package/dist/core/foundation/config-pi.js.map +1 -1
- package/dist/core/foundation/config-planning.d.ts +12 -1
- package/dist/core/foundation/config-planning.d.ts.map +1 -1
- package/dist/core/foundation/config-planning.js +29 -1
- package/dist/core/foundation/config-planning.js.map +1 -1
- package/dist/core/foundation/config-traceability.d.ts +28 -0
- package/dist/core/foundation/config-traceability.d.ts.map +1 -0
- package/dist/core/foundation/config-traceability.js +63 -0
- package/dist/core/foundation/config-traceability.js.map +1 -0
- package/dist/core/foundation/config-user.d.ts +32 -0
- package/dist/core/foundation/config-user.d.ts.map +1 -0
- package/dist/core/foundation/config-user.js +2 -0
- package/dist/core/foundation/config-user.js.map +1 -0
- package/dist/core/foundation/config-vcs.d.ts +29 -0
- package/dist/core/foundation/config-vcs.d.ts.map +1 -0
- package/dist/core/foundation/config-vcs.js +57 -0
- package/dist/core/foundation/config-vcs.js.map +1 -0
- package/dist/core/foundation/config.d.ts +23 -24
- package/dist/core/foundation/config.d.ts.map +1 -1
- package/dist/core/foundation/config.js +29 -36
- package/dist/core/foundation/config.js.map +1 -1
- package/dist/core/foundation/decisions-validate.d.ts +16 -0
- package/dist/core/foundation/decisions-validate.d.ts.map +1 -0
- package/dist/core/foundation/decisions-validate.js +74 -0
- package/dist/core/foundation/decisions-validate.js.map +1 -0
- package/dist/core/foundation/decisions.d.ts +34 -0
- package/dist/core/foundation/decisions.d.ts.map +1 -0
- package/dist/core/foundation/decisions.js +152 -0
- package/dist/core/foundation/decisions.js.map +1 -0
- package/dist/core/foundation/doctor-decisions.d.ts +9 -0
- package/dist/core/foundation/doctor-decisions.d.ts.map +1 -0
- package/dist/core/foundation/doctor-decisions.js +49 -0
- package/dist/core/foundation/doctor-decisions.js.map +1 -0
- package/dist/core/foundation/doctor-managed.d.ts.map +1 -1
- package/dist/core/foundation/doctor-managed.js +3 -0
- package/dist/core/foundation/doctor-managed.js.map +1 -1
- package/dist/core/foundation/doctor-prices.d.ts +8 -0
- package/dist/core/foundation/doctor-prices.d.ts.map +1 -0
- package/dist/core/foundation/doctor-prices.js +25 -0
- package/dist/core/foundation/doctor-prices.js.map +1 -0
- package/dist/core/foundation/doctor.d.ts.map +1 -1
- package/dist/core/foundation/doctor.js +20 -30
- package/dist/core/foundation/doctor.js.map +1 -1
- package/dist/core/foundation/harness-catalog.d.ts +7 -8
- package/dist/core/foundation/harness-catalog.d.ts.map +1 -1
- package/dist/core/foundation/harness-catalog.js +26 -41
- package/dist/core/foundation/harness-catalog.js.map +1 -1
- package/dist/core/foundation/init-blocks.d.ts +1 -1
- package/dist/core/foundation/init-blocks.d.ts.map +1 -1
- package/dist/core/foundation/init-blocks.js +51 -14
- package/dist/core/foundation/init-blocks.js.map +1 -1
- package/dist/core/foundation/init.d.ts +4 -0
- package/dist/core/foundation/init.d.ts.map +1 -1
- package/dist/core/foundation/init.js +7 -0
- package/dist/core/foundation/init.js.map +1 -1
- package/dist/core/foundation/new.d.ts +4 -1
- package/dist/core/foundation/new.d.ts.map +1 -1
- package/dist/core/foundation/new.js +18 -8
- package/dist/core/foundation/new.js.map +1 -1
- package/dist/core/foundation/package-root.d.ts +6 -0
- package/dist/core/foundation/package-root.d.ts.map +1 -1
- package/dist/core/foundation/package-root.js +6 -0
- package/dist/core/foundation/package-root.js.map +1 -1
- package/dist/core/foundation/rules-block.d.ts +11 -0
- package/dist/core/foundation/rules-block.d.ts.map +1 -0
- package/dist/core/foundation/rules-block.js +90 -0
- package/dist/core/foundation/rules-block.js.map +1 -0
- package/dist/core/foundation/traceability-block.d.ts +13 -0
- package/dist/core/foundation/traceability-block.d.ts.map +1 -0
- package/dist/core/foundation/traceability-block.js +124 -0
- package/dist/core/foundation/traceability-block.js.map +1 -0
- package/dist/core/lifecycle/done.d.ts.map +1 -1
- package/dist/core/lifecycle/done.js +2 -4
- package/dist/core/lifecycle/done.js.map +1 -1
- package/dist/core/lifecycle/recertify.d.ts +9 -0
- package/dist/core/lifecycle/recertify.d.ts.map +1 -0
- package/dist/core/lifecycle/recertify.js +27 -0
- package/dist/core/lifecycle/recertify.js.map +1 -0
- package/dist/core/lifecycle/reject.d.ts.map +1 -1
- package/dist/core/lifecycle/reject.js +3 -4
- package/dist/core/lifecycle/reject.js.map +1 -1
- package/dist/core/lifecycle/retry.d.ts.map +1 -1
- package/dist/core/lifecycle/retry.js +6 -20
- package/dist/core/lifecycle/retry.js.map +1 -1
- package/dist/core/lifecycle/verification-record.d.ts +42 -0
- package/dist/core/lifecycle/verification-record.d.ts.map +1 -0
- package/dist/core/lifecycle/verification-record.js +76 -0
- package/dist/core/lifecycle/verification-record.js.map +1 -0
- package/dist/core/report/approval-flags.d.ts +16 -3
- package/dist/core/report/approval-flags.d.ts.map +1 -1
- package/dist/core/report/approval-flags.js +59 -22
- package/dist/core/report/approval-flags.js.map +1 -1
- package/dist/core/report/brief-to-approval.d.ts +7 -0
- package/dist/core/report/brief-to-approval.d.ts.map +1 -0
- package/dist/core/report/brief-to-approval.js +43 -0
- package/dist/core/report/brief-to-approval.js.map +1 -0
- package/dist/core/report/planning-price-gaps.d.ts +10 -0
- package/dist/core/report/planning-price-gaps.d.ts.map +1 -0
- package/dist/core/report/planning-price-gaps.js +37 -0
- package/dist/core/report/planning-price-gaps.js.map +1 -0
- package/dist/core/report/planning-slice-lookup.d.ts.map +1 -1
- package/dist/core/report/planning-slice-lookup.js +3 -9
- package/dist/core/report/planning-slice-lookup.js.map +1 -1
- package/dist/core/report/recent-disclosures.d.ts +18 -0
- package/dist/core/report/recent-disclosures.d.ts.map +1 -0
- package/dist/core/report/recent-disclosures.js +105 -0
- package/dist/core/report/recent-disclosures.js.map +1 -0
- package/dist/core/report/record-disclosures.d.ts +14 -0
- package/dist/core/report/record-disclosures.d.ts.map +1 -0
- package/dist/core/report/record-disclosures.js +30 -0
- package/dist/core/report/record-disclosures.js.map +1 -0
- package/dist/core/report/record-estimates.d.ts +25 -0
- package/dist/core/report/record-estimates.d.ts.map +1 -0
- package/dist/core/report/record-estimates.js +83 -0
- package/dist/core/report/record-estimates.js.map +1 -0
- package/dist/core/report/record-rework.d.ts +19 -0
- package/dist/core/report/record-rework.d.ts.map +1 -0
- package/dist/core/report/record-rework.js +50 -0
- package/dist/core/report/record-rework.js.map +1 -0
- package/dist/core/report/record-verification.d.ts +16 -0
- package/dist/core/report/record-verification.d.ts.map +1 -0
- package/dist/core/report/record-verification.js +33 -0
- package/dist/core/report/record-verification.js.map +1 -0
- package/dist/core/report/report-dependencies.d.ts +29 -0
- package/dist/core/report/report-dependencies.d.ts.map +1 -0
- package/dist/core/report/report-dependencies.js +92 -0
- package/dist/core/report/report-dependencies.js.map +1 -0
- package/dist/core/report/report-events.d.ts +1 -0
- package/dist/core/report/report-events.d.ts.map +1 -1
- package/dist/core/report/report-events.js +8 -26
- package/dist/core/report/report-events.js.map +1 -1
- package/dist/core/report/report-mutation.d.ts +34 -0
- package/dist/core/report/report-mutation.d.ts.map +1 -0
- package/dist/core/report/report-mutation.js +191 -0
- package/dist/core/report/report-mutation.js.map +1 -0
- package/dist/core/report/report-scope.d.ts +21 -0
- package/dist/core/report/report-scope.d.ts.map +1 -0
- package/dist/core/report/report-scope.js +61 -0
- package/dist/core/report/report-scope.js.map +1 -0
- package/dist/core/report/report-traceability.d.ts +29 -0
- package/dist/core/report/report-traceability.d.ts.map +1 -0
- package/dist/core/report/report-traceability.js +94 -0
- package/dist/core/report/report-traceability.js.map +1 -0
- package/dist/core/report/report.d.ts +35 -1
- package/dist/core/report/report.d.ts.map +1 -1
- package/dist/core/report/report.js +130 -111
- package/dist/core/report/report.js.map +1 -1
- package/dist/core/report/result-sections.d.ts +30 -0
- package/dist/core/report/result-sections.d.ts.map +1 -0
- package/dist/core/report/result-sections.js +125 -0
- package/dist/core/report/result-sections.js.map +1 -0
- package/dist/core/report/scope-size.d.ts +7 -0
- package/dist/core/report/scope-size.d.ts.map +1 -0
- package/dist/core/report/scope-size.js +22 -0
- package/dist/core/report/scope-size.js.map +1 -0
- package/dist/core/run/commit-message.d.ts +24 -0
- package/dist/core/run/commit-message.d.ts.map +1 -0
- package/dist/core/run/commit-message.js +76 -0
- package/dist/core/run/commit-message.js.map +1 -0
- package/dist/core/run/dead-commit.d.ts +10 -0
- package/dist/core/run/dead-commit.d.ts.map +1 -0
- package/dist/core/run/dead-commit.js +70 -0
- package/dist/core/run/dead-commit.js.map +1 -0
- package/dist/core/run/focused-tests.d.ts +43 -0
- package/dist/core/run/focused-tests.d.ts.map +1 -0
- package/dist/core/run/focused-tests.js +116 -0
- package/dist/core/run/focused-tests.js.map +1 -0
- package/dist/core/run/manifest-approval.d.ts +7 -0
- package/dist/core/run/manifest-approval.d.ts.map +1 -0
- package/dist/core/run/manifest-approval.js +30 -0
- package/dist/core/run/manifest-approval.js.map +1 -0
- package/dist/core/run/manifest.d.ts +11 -1
- package/dist/core/run/manifest.d.ts.map +1 -1
- package/dist/core/run/manifest.js +52 -11
- package/dist/core/run/manifest.js.map +1 -1
- package/dist/core/run/mutation-report.d.ts +29 -0
- package/dist/core/run/mutation-report.d.ts.map +1 -0
- package/dist/core/run/mutation-report.js +107 -0
- package/dist/core/run/mutation-report.js.map +1 -0
- package/dist/core/run/mutation-run.d.ts +32 -0
- package/dist/core/run/mutation-run.d.ts.map +1 -0
- package/dist/core/run/mutation-run.js +80 -0
- package/dist/core/run/mutation-run.js.map +1 -0
- package/dist/core/run/verification.d.ts +12 -1
- package/dist/core/run/verification.d.ts.map +1 -1
- package/dist/core/run/verification.js +24 -2
- package/dist/core/run/verification.js.map +1 -1
- package/dist/core/spec/approve-worktree-shared.d.ts +33 -0
- package/dist/core/spec/approve-worktree-shared.d.ts.map +1 -0
- package/dist/core/spec/approve-worktree-shared.js +59 -0
- package/dist/core/spec/approve-worktree-shared.js.map +1 -0
- package/dist/core/spec/approve-worktree.d.ts +21 -0
- package/dist/core/spec/approve-worktree.d.ts.map +1 -0
- package/dist/core/spec/approve-worktree.js +153 -0
- package/dist/core/spec/approve-worktree.js.map +1 -0
- package/dist/core/spec/approve.d.ts +20 -10
- package/dist/core/spec/approve.d.ts.map +1 -1
- package/dist/core/spec/approve.js +57 -54
- package/dist/core/spec/approve.js.map +1 -1
- package/dist/core/spec/capability-impact.d.ts +43 -0
- package/dist/core/spec/capability-impact.d.ts.map +1 -0
- package/dist/core/spec/capability-impact.js +85 -0
- package/dist/core/spec/capability-impact.js.map +1 -0
- package/dist/core/spec/decisions-lint.d.ts +18 -0
- package/dist/core/spec/decisions-lint.d.ts.map +1 -0
- package/dist/core/spec/decisions-lint.js +127 -0
- package/dist/core/spec/decisions-lint.js.map +1 -0
- package/dist/core/spec/delta.d.ts +6 -0
- package/dist/core/spec/delta.d.ts.map +1 -1
- package/dist/core/spec/delta.js +73 -5
- package/dist/core/spec/delta.js.map +1 -1
- package/dist/core/spec/digest-decisions.d.ts +27 -0
- package/dist/core/spec/digest-decisions.d.ts.map +1 -0
- package/dist/core/spec/digest-decisions.js +86 -0
- package/dist/core/spec/digest-decisions.js.map +1 -0
- package/dist/core/spec/digest-format.d.ts +4 -0
- package/dist/core/spec/digest-format.d.ts.map +1 -0
- package/dist/core/spec/digest-format.js +44 -0
- package/dist/core/spec/digest-format.js.map +1 -0
- package/dist/core/spec/digest.d.ts +5 -3
- package/dist/core/spec/digest.d.ts.map +1 -1
- package/dist/core/spec/digest.js +14 -33
- package/dist/core/spec/digest.js.map +1 -1
- package/dist/core/spec/human-steps.d.ts +20 -0
- package/dist/core/spec/human-steps.d.ts.map +1 -0
- package/dist/core/spec/human-steps.js +52 -0
- package/dist/core/spec/human-steps.js.map +1 -0
- package/dist/core/spec/impact-lint.d.ts +24 -0
- package/dist/core/spec/impact-lint.d.ts.map +1 -0
- package/dist/core/spec/impact-lint.js +39 -0
- package/dist/core/spec/impact-lint.js.map +1 -0
- package/dist/core/spec/import-graph.d.ts +35 -0
- package/dist/core/spec/import-graph.d.ts.map +1 -0
- package/dist/core/spec/import-graph.js +155 -0
- package/dist/core/spec/import-graph.js.map +1 -0
- package/dist/core/spec/lint-findings.d.ts +44 -0
- package/dist/core/spec/lint-findings.d.ts.map +1 -0
- package/dist/core/spec/lint-findings.js +56 -0
- package/dist/core/spec/lint-findings.js.map +1 -0
- package/dist/core/spec/lint-output.d.ts +53 -0
- package/dist/core/spec/lint-output.d.ts.map +1 -0
- package/dist/core/spec/lint-output.js +87 -0
- package/dist/core/spec/lint-output.js.map +1 -0
- package/dist/core/spec/linter.d.ts +14 -4
- package/dist/core/spec/linter.d.ts.map +1 -1
- package/dist/core/spec/linter.js +271 -111
- package/dist/core/spec/linter.js.map +1 -1
- package/dist/core/spec/merged-spec-check.d.ts +24 -0
- package/dist/core/spec/merged-spec-check.d.ts.map +1 -0
- package/dist/core/spec/merged-spec-check.js +95 -0
- package/dist/core/spec/merged-spec-check.js.map +1 -0
- package/dist/core/spec/openspec-issues.d.ts +58 -0
- package/dist/core/spec/openspec-issues.d.ts.map +1 -0
- package/dist/core/spec/openspec-issues.js +166 -0
- package/dist/core/spec/openspec-issues.js.map +1 -0
- package/dist/core/spec/parser.d.ts +1 -0
- package/dist/core/spec/parser.d.ts.map +1 -1
- package/dist/core/spec/parser.js +16 -6
- package/dist/core/spec/parser.js.map +1 -1
- package/dist/core/spec/scenario-impact.d.ts +47 -0
- package/dist/core/spec/scenario-impact.d.ts.map +1 -0
- package/dist/core/spec/scenario-impact.js +130 -0
- package/dist/core/spec/scenario-impact.js.map +1 -0
- package/dist/core/spec/test-impact.d.ts +28 -0
- package/dist/core/spec/test-impact.d.ts.map +1 -0
- package/dist/core/spec/test-impact.js +119 -0
- package/dist/core/spec/test-impact.js.map +1 -0
- package/dist/core/spec/traceability-links.d.ts +50 -0
- package/dist/core/spec/traceability-links.d.ts.map +1 -0
- package/dist/core/spec/traceability-links.js +120 -0
- package/dist/core/spec/traceability-links.js.map +1 -0
- package/dist/core/spec/traceability-lint.d.ts +29 -0
- package/dist/core/spec/traceability-lint.d.ts.map +1 -0
- package/dist/core/spec/traceability-lint.js +143 -0
- package/dist/core/spec/traceability-lint.js.map +1 -0
- package/dist/core/status/blocked-item.d.ts +8 -0
- package/dist/core/status/blocked-item.d.ts.map +1 -0
- package/dist/core/status/blocked-item.js +45 -0
- package/dist/core/status/blocked-item.js.map +1 -0
- package/dist/core/status/change-locations.d.ts +44 -0
- package/dist/core/status/change-locations.d.ts.map +1 -0
- package/dist/core/status/change-locations.js +138 -0
- package/dist/core/status/change-locations.js.map +1 -0
- package/dist/core/status/dependency-readiness.d.ts +11 -0
- package/dist/core/status/dependency-readiness.d.ts.map +1 -0
- package/dist/core/status/dependency-readiness.js +39 -0
- package/dist/core/status/dependency-readiness.js.map +1 -0
- package/dist/core/status/inbox-projection.d.ts.map +1 -1
- package/dist/core/status/inbox-projection.js +23 -4
- package/dist/core/status/inbox-projection.js.map +1 -1
- package/dist/core/status/inbox-text.d.ts.map +1 -1
- package/dist/core/status/inbox-text.js +34 -3
- package/dist/core/status/inbox-text.js.map +1 -1
- package/dist/core/status/inbox.d.ts +10 -1
- package/dist/core/status/inbox.d.ts.map +1 -1
- package/dist/core/status/inbox.js +19 -1
- package/dist/core/status/inbox.js.map +1 -1
- package/dist/core/status/layout.d.ts +6 -0
- package/dist/core/status/layout.d.ts.map +1 -1
- package/dist/core/status/layout.js +8 -0
- package/dist/core/status/layout.js.map +1 -1
- package/dist/core/status/next-step.d.ts +17 -0
- package/dist/core/status/next-step.d.ts.map +1 -0
- package/dist/core/status/next-step.js +85 -0
- package/dist/core/status/next-step.js.map +1 -0
- package/dist/core/status/pre-spawn-words.d.ts +13 -0
- package/dist/core/status/pre-spawn-words.d.ts.map +1 -0
- package/dist/core/status/pre-spawn-words.js +26 -0
- package/dist/core/status/pre-spawn-words.js.map +1 -0
- package/dist/core/status/queue-parser.d.ts +1 -0
- package/dist/core/status/queue-parser.d.ts.map +1 -1
- package/dist/core/status/queue-parser.js +41 -2
- package/dist/core/status/queue-parser.js.map +1 -1
- package/dist/core/status/queue-planning.d.ts +1 -0
- package/dist/core/status/queue-planning.d.ts.map +1 -1
- package/dist/core/status/queue-planning.js +24 -12
- package/dist/core/status/queue-planning.js.map +1 -1
- package/dist/core/status/queue-report-detail.d.ts.map +1 -1
- package/dist/core/status/queue-report-detail.js +5 -3
- package/dist/core/status/queue-report-detail.js.map +1 -1
- package/dist/core/status/queue-state.d.ts +1 -1
- package/dist/core/status/queue-state.d.ts.map +1 -1
- package/dist/core/status/queue-state.js +27 -41
- package/dist/core/status/queue-state.js.map +1 -1
- package/dist/core/status/show.d.ts +25 -0
- package/dist/core/status/show.d.ts.map +1 -1
- package/dist/core/status/show.js +354 -91
- package/dist/core/status/show.js.map +1 -1
- package/dist/core/status/state.d.ts.map +1 -1
- package/dist/core/status/state.js +1 -23
- package/dist/core/status/state.js.map +1 -1
- package/dist/core/status/status.d.ts +18 -0
- package/dist/core/status/status.d.ts.map +1 -1
- package/dist/core/status/status.js +59 -80
- package/dist/core/status/status.js.map +1 -1
- package/dist/core/status/verification.d.ts +27 -0
- package/dist/core/status/verification.d.ts.map +1 -0
- package/dist/core/status/verification.js +111 -0
- package/dist/core/status/verification.js.map +1 -0
- package/dist/core/trace/function-ranges.d.ts +30 -0
- package/dist/core/trace/function-ranges.d.ts.map +1 -0
- package/dist/core/trace/function-ranges.js +250 -0
- package/dist/core/trace/function-ranges.js.map +1 -0
- package/dist/core/trace/mutation-pick.d.ts +55 -0
- package/dist/core/trace/mutation-pick.d.ts.map +1 -0
- package/dist/core/trace/mutation-pick.js +96 -0
- package/dist/core/trace/mutation-pick.js.map +1 -0
- package/dist/core/trace/scenario-index.d.ts +27 -0
- package/dist/core/trace/scenario-index.d.ts.map +1 -0
- package/dist/core/trace/scenario-index.js +76 -0
- package/dist/core/trace/scenario-index.js.map +1 -0
- package/dist/core/trace/scenario-lookup.d.ts +14 -0
- package/dist/core/trace/scenario-lookup.d.ts.map +1 -0
- package/dist/core/trace/scenario-lookup.js +179 -0
- package/dist/core/trace/scenario-lookup.js.map +1 -0
- package/dist/core/trace/tag-scan.d.ts +44 -0
- package/dist/core/trace/tag-scan.d.ts.map +1 -0
- package/dist/core/trace/tag-scan.js +200 -0
- package/dist/core/trace/tag-scan.js.map +1 -0
- package/dist/core/vcs/doctor-git.d.ts +13 -0
- package/dist/core/vcs/doctor-git.d.ts.map +1 -0
- package/dist/core/vcs/doctor-git.js +114 -0
- package/dist/core/vcs/doctor-git.js.map +1 -0
- package/dist/core/vcs/git-vcs-write.d.ts +47 -0
- package/dist/core/vcs/git-vcs-write.d.ts.map +1 -0
- package/dist/core/vcs/git-vcs-write.js +210 -0
- package/dist/core/vcs/git-vcs-write.js.map +1 -0
- package/dist/core/vcs/git-vcs.d.ts +40 -0
- package/dist/core/vcs/git-vcs.d.ts.map +1 -0
- package/dist/core/vcs/git-vcs.js +170 -0
- package/dist/core/vcs/git-vcs.js.map +1 -0
- package/dist/core/vcs/no-vcs.d.ts +26 -0
- package/dist/core/vcs/no-vcs.d.ts.map +1 -0
- package/dist/core/vcs/no-vcs.js +62 -0
- package/dist/core/vcs/no-vcs.js.map +1 -0
- package/dist/core/vcs/select.d.ts +11 -0
- package/dist/core/vcs/select.d.ts.map +1 -0
- package/dist/core/vcs/select.js +33 -0
- package/dist/core/vcs/select.js.map +1 -0
- package/dist/core/vcs/snapshot.d.ts +34 -0
- package/dist/core/vcs/snapshot.d.ts.map +1 -0
- package/dist/core/vcs/snapshot.js +71 -0
- package/dist/core/vcs/snapshot.js.map +1 -0
- package/dist/core/vcs/vcs.d.ts +55 -0
- package/dist/core/vcs/vcs.d.ts.map +1 -0
- package/dist/core/vcs/vcs.js +2 -0
- package/dist/core/vcs/vcs.js.map +1 -0
- package/dist/core/vcs/worktree.d.ts +10 -0
- package/dist/core/vcs/worktree.d.ts.map +1 -0
- package/dist/core/vcs/worktree.js +25 -0
- package/dist/core/vcs/worktree.js.map +1 -0
- package/dist/core/web/web-data-folders.d.ts.map +1 -1
- package/dist/core/web/web-data-folders.js +18 -33
- package/dist/core/web/web-data-folders.js.map +1 -1
- package/dist/core/web/web-data-lifecycle.d.ts.map +1 -1
- package/dist/core/web/web-data-lifecycle.js +2 -1
- package/dist/core/web/web-data-lifecycle.js.map +1 -1
- package/dist/core/web/web-events.d.ts +8 -6
- package/dist/core/web/web-events.d.ts.map +1 -1
- package/dist/core/web/web-events.js +7 -31
- package/dist/core/web/web-events.js.map +1 -1
- package/dist/core/web/web-server.d.ts.map +1 -1
- package/dist/core/web/web-server.js +3 -0
- package/dist/core/web/web-server.js.map +1 -1
- package/dist/core/web/web-trees.d.ts +27 -0
- package/dist/core/web/web-trees.d.ts.map +1 -0
- package/dist/core/web/web-trees.js +82 -0
- package/dist/core/web/web-trees.js.map +1 -0
- package/dist/harness/claude/claude-exec-args.d.ts +13 -0
- package/dist/harness/claude/claude-exec-args.d.ts.map +1 -0
- package/dist/harness/claude/claude-exec-args.js +61 -0
- package/dist/harness/claude/claude-exec-args.js.map +1 -0
- package/dist/harness/claude/claude-exec.d.ts +12 -0
- package/dist/harness/claude/claude-exec.d.ts.map +1 -0
- package/dist/harness/claude/claude-exec.js +79 -0
- package/dist/harness/claude/claude-exec.js.map +1 -0
- package/dist/harness/claude/claude-stream.d.ts +31 -0
- package/dist/harness/claude/claude-stream.d.ts.map +1 -0
- package/dist/harness/claude/claude-stream.js +150 -0
- package/dist/harness/claude/claude-stream.js.map +1 -0
- package/dist/harness/claude/claude-tools.d.ts +5 -0
- package/dist/harness/claude/claude-tools.d.ts.map +1 -0
- package/dist/harness/claude/claude-tools.js +30 -0
- package/dist/harness/claude/claude-tools.js.map +1 -0
- package/dist/harness/index.d.ts +1 -0
- package/dist/harness/index.d.ts.map +1 -1
- package/dist/harness/index.js +3 -0
- package/dist/harness/index.js.map +1 -1
- package/dist/harness/opencode/opencode-session.d.ts +42 -0
- package/dist/harness/opencode/opencode-session.d.ts.map +1 -0
- package/dist/harness/opencode/opencode-session.js +123 -0
- package/dist/harness/opencode/opencode-session.js.map +1 -0
- package/dist/harness/opencode/opencode.d.ts +2 -1
- package/dist/harness/opencode/opencode.d.ts.map +1 -1
- package/dist/harness/opencode/opencode.js +52 -28
- package/dist/harness/opencode/opencode.js.map +1 -1
- package/dist/harness/prompt.js +1 -1
- package/dist/harness/prompt.js.map +1 -1
- package/dist/harness/types.d.ts +112 -1
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +2 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/scenario.d.ts +29 -0
- package/dist/testing/scenario.d.ts.map +1 -0
- package/dist/testing/scenario.js +157 -0
- package/dist/testing/scenario.js.map +1 -0
- package/dist/watcher/archiver.d.ts +2 -1
- package/dist/watcher/archiver.d.ts.map +1 -1
- package/dist/watcher/archiver.js +27 -2
- package/dist/watcher/archiver.js.map +1 -1
- package/dist/watcher/auto-recertify.d.ts +27 -0
- package/dist/watcher/auto-recertify.d.ts.map +1 -0
- package/dist/watcher/auto-recertify.js +113 -0
- package/dist/watcher/auto-recertify.js.map +1 -0
- package/dist/watcher/auto-retry.d.ts +1 -1
- package/dist/watcher/auto-retry.d.ts.map +1 -1
- package/dist/watcher/auto-retry.js +3 -11
- package/dist/watcher/auto-retry.js.map +1 -1
- package/dist/watcher/baseline-key.d.ts +17 -0
- package/dist/watcher/baseline-key.d.ts.map +1 -0
- package/dist/watcher/baseline-key.js +31 -0
- package/dist/watcher/baseline-key.js.map +1 -0
- package/dist/watcher/baseline.d.ts +32 -0
- package/dist/watcher/baseline.d.ts.map +1 -0
- package/dist/watcher/baseline.js +166 -0
- package/dist/watcher/baseline.js.map +1 -0
- package/dist/watcher/blocked.d.ts +12 -0
- package/dist/watcher/blocked.d.ts.map +1 -0
- package/dist/watcher/blocked.js +24 -0
- package/dist/watcher/blocked.js.map +1 -0
- package/dist/watcher/dependencies.d.ts +28 -0
- package/dist/watcher/dependencies.d.ts.map +1 -0
- package/dist/watcher/dependencies.js +152 -0
- package/dist/watcher/dependencies.js.map +1 -0
- package/dist/watcher/done-metadata.d.ts +4 -0
- package/dist/watcher/done-metadata.d.ts.map +1 -0
- package/dist/watcher/done-metadata.js +20 -0
- package/dist/watcher/done-metadata.js.map +1 -0
- package/dist/watcher/failure-reason.d.ts +1 -1
- package/dist/watcher/failure-reason.d.ts.map +1 -1
- package/dist/watcher/focused-verify.d.ts +17 -0
- package/dist/watcher/focused-verify.d.ts.map +1 -0
- package/dist/watcher/focused-verify.js +54 -0
- package/dist/watcher/focused-verify.js.map +1 -0
- package/dist/watcher/git-guard.d.ts +24 -0
- package/dist/watcher/git-guard.d.ts.map +1 -0
- package/dist/watcher/git-guard.js +95 -0
- package/dist/watcher/git-guard.js.map +1 -0
- package/dist/watcher/instructions-drift.d.ts +21 -0
- package/dist/watcher/instructions-drift.d.ts.map +1 -0
- package/dist/watcher/instructions-drift.js +115 -0
- package/dist/watcher/instructions-drift.js.map +1 -0
- package/dist/watcher/loop.d.ts.map +1 -1
- package/dist/watcher/loop.js +36 -50
- package/dist/watcher/loop.js.map +1 -1
- package/dist/watcher/measures.d.ts.map +1 -1
- package/dist/watcher/measures.js +22 -39
- package/dist/watcher/measures.js.map +1 -1
- package/dist/watcher/mutation-check.d.ts +42 -0
- package/dist/watcher/mutation-check.d.ts.map +1 -0
- package/dist/watcher/mutation-check.js +147 -0
- package/dist/watcher/mutation-check.js.map +1 -0
- package/dist/watcher/regression.d.ts +10 -5
- package/dist/watcher/regression.d.ts.map +1 -1
- package/dist/watcher/regression.js +29 -22
- package/dist/watcher/regression.js.map +1 -1
- package/dist/watcher/runner.d.ts.map +1 -1
- package/dist/watcher/runner.js +12 -14
- package/dist/watcher/runner.js.map +1 -1
- package/dist/watcher/spawn.d.ts.map +1 -1
- package/dist/watcher/spawn.js +5 -0
- package/dist/watcher/spawn.js.map +1 -1
- package/dist/watcher/task-verify.d.ts +7 -2
- package/dist/watcher/task-verify.d.ts.map +1 -1
- package/dist/watcher/task-verify.js +33 -8
- package/dist/watcher/task-verify.js.map +1 -1
- package/dist/watcher/verify.d.ts +1 -5
- package/dist/watcher/verify.d.ts.map +1 -1
- package/dist/watcher/verify.js +2 -6
- package/dist/watcher/verify.js.map +1 -1
- package/package.json +5 -1
- package/templates/PLANNER.md +35 -9
- package/templates/openspec/config.yaml +1 -1
- package/templates/openspec/schemas/osq/schema.yaml +4 -2
- package/templates/openspec/schemas/osq/templates/proposal.md +9 -2
- package/templates/proposal.md +9 -2
- package/ui/dist/assets/{index-CZYeQ8la.js → index-D39uYbbL.js} +1 -1
- package/ui/dist/index.html +1 -1
package/dist/core/spec/linter.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import { execFile } from 'node:child_process';
|
|
2
1
|
import fs from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import { resolveScope } from '../run/scope.js';
|
|
5
4
|
import { getArchiveDir, getChangesDir, getRejectedDir } from '../status/layout.js';
|
|
6
5
|
import { compareNumericPrefix } from '../status/state.js';
|
|
6
|
+
import { collectDecisionsFindings } from './decisions-lint.js';
|
|
7
7
|
import { DeltaMergeError, mergeDelta, parseDelta } from './delta.js';
|
|
8
8
|
import { isExcludedChangePath } from './hasher.js';
|
|
9
|
+
import { collectImpactFindings } from './impact-lint.js';
|
|
10
|
+
import { buildImportGraph } from './import-graph.js';
|
|
11
|
+
import { LintFindingSet, makeFinding, } from './lint-findings.js';
|
|
12
|
+
import { validateMergedSpecs } from './merged-spec-check.js';
|
|
13
|
+
import { OPENSPEC_ERROR_PREFIX, appendUnsupportedNote, execFileCapture, scanOpenSpecIssues, } from './openspec-issues.js';
|
|
9
14
|
import { OPENSPEC_EXPECTED_VERSION, assessOpenSpecVersion, formatInRangeWarning, } from './openspec-version.js';
|
|
10
15
|
import { hasDeclaredWrites, parseFrontmatter, parseSpecMd, parseTaskMd, resolveChangeDoc, } from './parser.js';
|
|
16
|
+
import { collectTraceabilityFindings } from './traceability-lint.js';
|
|
11
17
|
import { listNamedPaths, missingNamedPaths, namesExistingRepositoryPath, tokenizeVerifyCommand, } from './verify-paths.js';
|
|
12
18
|
import { analyzeVerifyStarts, namedPathCoveredByScopes, } from './verify-starts.js';
|
|
13
|
-
export { OPENSPEC_EXPECTED_VERSION };
|
|
19
|
+
export { OPENSPEC_EXPECTED_VERSION, OPENSPEC_ERROR_PREFIX };
|
|
14
20
|
/** Remediation guidance citing ADR 004 for any validator pin violation. */
|
|
15
21
|
const OPENSPEC_PIN_REMEDIATION = 'Refer to ADR 004 (decisions/004-pinned-openspec-validator.md) and run: pnpm add -D @fission-ai/openspec@1.13.1';
|
|
16
|
-
/** Prefix applied to every finding surfaced by the OpenSpec validator. */
|
|
17
|
-
export const OPENSPEC_ERROR_PREFIX = 'openspec:';
|
|
18
22
|
/**
|
|
19
23
|
* ASCII control characters prohibited in authored change-folder files. Newline
|
|
20
24
|
* (`\n`, 0x0A) and tab (`\t`, 0x09) are permitted as structural Markdown
|
|
@@ -204,6 +208,13 @@ function missingPackageScriptError(label, script) {
|
|
|
204
208
|
function unresolvedVerifyWarning(label, command) {
|
|
205
209
|
return `${label} verify names neither an existing repository path nor a package script: "${command}"`;
|
|
206
210
|
}
|
|
211
|
+
function chainedVerifyError(label, command) {
|
|
212
|
+
return `Task in ${label} verify chains commands ("${command}"); move the chain into a package script and name that script, for example pnpm run <script>`;
|
|
213
|
+
}
|
|
214
|
+
/** Resolve an absolute path to its repository-relative POSIX form. */
|
|
215
|
+
function repositoryPath(projectRoot, absolutePath) {
|
|
216
|
+
return path.relative(projectRoot, absolutePath).split(path.sep).join('/');
|
|
217
|
+
}
|
|
207
218
|
/**
|
|
208
219
|
* Recursively list a change folder's authored files relative to `baseDir`,
|
|
209
220
|
* applying the shared root-relative exclusion predicate. `.run`, `.git`, and
|
|
@@ -235,21 +246,21 @@ async function collectArtifactFiles(dir, baseDir) {
|
|
|
235
246
|
return files;
|
|
236
247
|
}
|
|
237
248
|
/** Report the first prohibited control character found in `content`. */
|
|
238
|
-
function scanControlCharacters(relPath, content,
|
|
249
|
+
function scanControlCharacters(relPath, file, content, findings) {
|
|
239
250
|
const match = PROHIBITED_CONTROL_REGEX.exec(content);
|
|
240
251
|
if (!match) {
|
|
241
252
|
return;
|
|
242
253
|
}
|
|
243
254
|
const code = match[0].charCodeAt(0).toString(16).toUpperCase().padStart(2, '0');
|
|
244
|
-
|
|
255
|
+
findings.error({ file }, `File ${relPath} contains prohibited control character (0x${code})`);
|
|
245
256
|
}
|
|
246
257
|
/** Report any task line carrying two or more acceptance checkbox items. */
|
|
247
|
-
function scanFusedAcceptance(taskFile, content,
|
|
258
|
+
function scanFusedAcceptance(taskFile, file, content, findings) {
|
|
248
259
|
const lines = content.split('\n');
|
|
249
260
|
lines.forEach((line, index) => {
|
|
250
261
|
const count = line.match(ACCEPTANCE_CHECKBOX_REGEX)?.length ?? 0;
|
|
251
262
|
if (count > 1) {
|
|
252
|
-
|
|
263
|
+
findings.error({ file }, `Task in ${taskFile} contains fused acceptance lines on line ${index + 1}`);
|
|
253
264
|
}
|
|
254
265
|
});
|
|
255
266
|
}
|
|
@@ -397,18 +408,6 @@ export function parseOpenSpecFindings(output) {
|
|
|
397
408
|
visit(parsed);
|
|
398
409
|
return { errors, warnings };
|
|
399
410
|
}
|
|
400
|
-
function execFileCapture(bin, args, options) {
|
|
401
|
-
return new Promise((resolve) => {
|
|
402
|
-
execFile(bin, args, options, (error, stdout, stderr) => {
|
|
403
|
-
const code = error
|
|
404
|
-
? typeof error.code === 'number'
|
|
405
|
-
? error.code
|
|
406
|
-
: 1
|
|
407
|
-
: 0;
|
|
408
|
-
resolve({ stdout: stdout ?? '', stderr: stderr ?? '', code });
|
|
409
|
-
});
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
411
|
/**
|
|
413
412
|
* Maps a validator version assessment to the single finding it produces: none
|
|
414
413
|
* for the pin, one warning inside the declared peer range, and one error citing
|
|
@@ -425,6 +424,68 @@ function versionFinding(assessment) {
|
|
|
425
424
|
}
|
|
426
425
|
return {};
|
|
427
426
|
}
|
|
427
|
+
/** The proposal file an items-less OpenSpec finding is charged to. */
|
|
428
|
+
function legacyProposalFile(context) {
|
|
429
|
+
return context.changeFolder
|
|
430
|
+
? `${context.openspecRoot}/changes/${context.changeFolder}/proposal.md`
|
|
431
|
+
: 'proposal.md';
|
|
432
|
+
}
|
|
433
|
+
/** Charge an items-less OpenSpec output to the linted change. */
|
|
434
|
+
function addLegacyFindings(output, context, findings) {
|
|
435
|
+
const file = legacyProposalFile(context);
|
|
436
|
+
const legacy = parseOpenSpecFindings(output);
|
|
437
|
+
for (const message of legacy.errors) {
|
|
438
|
+
findings.error({ file }, `${OPENSPEC_ERROR_PREFIX} ${appendUnsupportedNote(message)}`);
|
|
439
|
+
}
|
|
440
|
+
for (const message of legacy.warnings) {
|
|
441
|
+
findings.warning({ file }, `${OPENSPEC_ERROR_PREFIX} ${appendUnsupportedNote(message)}`);
|
|
442
|
+
}
|
|
443
|
+
return legacy.errors.length + legacy.warnings.length;
|
|
444
|
+
}
|
|
445
|
+
/** Run one validator command and fold its issues into the finding collector. */
|
|
446
|
+
async function collectCommandFindings(bin, args, projectRoot, timeout, context, findings, repository) {
|
|
447
|
+
const outcome = await execFileCapture(bin, args, {
|
|
448
|
+
cwd: projectRoot,
|
|
449
|
+
env: { ...process.env, OPENSPEC_TELEMETRY: '0' },
|
|
450
|
+
timeout,
|
|
451
|
+
});
|
|
452
|
+
const scan = await scanOpenSpecIssues(outcome.stdout, context);
|
|
453
|
+
let count;
|
|
454
|
+
if (scan === null) {
|
|
455
|
+
count = addLegacyFindings(outcome.stdout, context, findings);
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
for (const finding of scan.findings) {
|
|
459
|
+
findings.addOwn(finding);
|
|
460
|
+
}
|
|
461
|
+
for (const finding of scan.repository) {
|
|
462
|
+
repository.push(finding);
|
|
463
|
+
}
|
|
464
|
+
count = scan.findings.length + scan.repository.length;
|
|
465
|
+
}
|
|
466
|
+
return {
|
|
467
|
+
code: outcome.code,
|
|
468
|
+
count,
|
|
469
|
+
detail: (outcome.stderr || outcome.stdout).trim().split('\n')[0],
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
/** Assess the installed validator and record the one version finding it yields. */
|
|
473
|
+
async function collectVersionFindings(projectRoot, logger, file, findings) {
|
|
474
|
+
const version = await resolveOpenSpecVersion(projectRoot);
|
|
475
|
+
if (!version) {
|
|
476
|
+
findings.error({ file }, `OpenSpec validator version could not be resolved from node_modules/@fission-ai/openspec/package.json. ${OPENSPEC_PIN_REMEDIATION}`);
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
logger?.info(`openspec version ${version}`);
|
|
480
|
+
const finding = versionFinding(await assessOpenSpecVersion(version));
|
|
481
|
+
if (finding.error !== undefined) {
|
|
482
|
+
findings.error({ file }, finding.error);
|
|
483
|
+
}
|
|
484
|
+
if (finding.warning !== undefined) {
|
|
485
|
+
findings.warning({ file }, finding.warning);
|
|
486
|
+
}
|
|
487
|
+
return version;
|
|
488
|
+
}
|
|
428
489
|
/**
|
|
429
490
|
* Runs the local OpenSpec validator for changes and specs. Validation failures
|
|
430
491
|
* are returned prefixed with `openspec:`. A missing binary or a version outside
|
|
@@ -433,59 +494,48 @@ function versionFinding(assessment) {
|
|
|
433
494
|
* ADR 005. Linting fails closed rather than silently degrading.
|
|
434
495
|
*/
|
|
435
496
|
export async function validateWithOpenSpec(projectRoot, config, options = {}) {
|
|
436
|
-
const
|
|
497
|
+
const file = 'package.json';
|
|
498
|
+
const findings = new LintFindingSet();
|
|
499
|
+
const repository = [];
|
|
437
500
|
const bin = await resolveOpenSpecBin(projectRoot, config);
|
|
438
501
|
if (!bin) {
|
|
502
|
+
findings.error({ file }, `OpenSpec validator binary is unavailable at node_modules/.bin/openspec. ${OPENSPEC_PIN_REMEDIATION}`);
|
|
439
503
|
return {
|
|
440
|
-
errors:
|
|
441
|
-
|
|
442
|
-
],
|
|
443
|
-
|
|
504
|
+
errors: findings.errors(),
|
|
505
|
+
warnings: findings.warnings(),
|
|
506
|
+
findings: [...findings.findings],
|
|
507
|
+
repository,
|
|
444
508
|
ran: false,
|
|
445
509
|
bin: null,
|
|
446
510
|
version: null,
|
|
447
511
|
};
|
|
448
512
|
}
|
|
449
|
-
const version = await
|
|
450
|
-
const errors = [];
|
|
451
|
-
const warnings = [];
|
|
452
|
-
if (!version) {
|
|
453
|
-
errors.push(`OpenSpec validator version could not be resolved from node_modules/@fission-ai/openspec/package.json. ${OPENSPEC_PIN_REMEDIATION}`);
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
logger?.info(`openspec version ${version}`);
|
|
457
|
-
const finding = versionFinding(await assessOpenSpecVersion(version));
|
|
458
|
-
if (finding.error !== undefined) {
|
|
459
|
-
errors.push(finding.error);
|
|
460
|
-
}
|
|
461
|
-
if (finding.warning !== undefined) {
|
|
462
|
-
warnings.push(finding.warning);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
513
|
+
const version = await collectVersionFindings(projectRoot, options.logger, file, findings);
|
|
465
514
|
const timeout = config.timeouts.verifyTimeoutSeconds * 1000;
|
|
515
|
+
const context = {
|
|
516
|
+
projectRoot,
|
|
517
|
+
openspecRoot: resolveOpenSpecRoot(config),
|
|
518
|
+
changeFolder: options.changeFolder ?? null,
|
|
519
|
+
};
|
|
466
520
|
const commands = [
|
|
467
521
|
['validate', '--changes', '--strict', '--json', '--no-interactive'],
|
|
468
522
|
['validate', '--specs', '--strict', '--json', '--no-interactive'],
|
|
469
523
|
];
|
|
470
524
|
for (const args of commands) {
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
timeout,
|
|
475
|
-
});
|
|
476
|
-
const findings = parseOpenSpecFindings(outcome.stdout);
|
|
477
|
-
for (const error of findings.errors) {
|
|
478
|
-
errors.push(`${OPENSPEC_ERROR_PREFIX} ${error}`);
|
|
479
|
-
}
|
|
480
|
-
for (const warning of findings.warnings) {
|
|
481
|
-
warnings.push(`${OPENSPEC_ERROR_PREFIX} ${warning}`);
|
|
482
|
-
}
|
|
483
|
-
if (outcome.code !== 0 && findings.errors.length === 0 && findings.warnings.length === 0) {
|
|
484
|
-
const detail = (outcome.stderr || outcome.stdout).trim().split('\n')[0];
|
|
485
|
-
errors.push(`${OPENSPEC_ERROR_PREFIX} ${args.join(' ')} exited with code ${outcome.code}${detail ? `: ${detail}` : ''}`);
|
|
525
|
+
const { code, count, detail } = await collectCommandFindings(bin, args, projectRoot, timeout, context, findings, repository);
|
|
526
|
+
if (code !== 0 && count === 0) {
|
|
527
|
+
findings.error({ file }, `${OPENSPEC_ERROR_PREFIX} ${args.join(' ')} exited with code ${code}${detail ? `: ${detail}` : ''}`);
|
|
486
528
|
}
|
|
487
529
|
}
|
|
488
|
-
return {
|
|
530
|
+
return {
|
|
531
|
+
errors: findings.errors(),
|
|
532
|
+
warnings: findings.warnings(),
|
|
533
|
+
findings: [...findings.findings],
|
|
534
|
+
repository,
|
|
535
|
+
ran: true,
|
|
536
|
+
bin,
|
|
537
|
+
version,
|
|
538
|
+
};
|
|
489
539
|
}
|
|
490
540
|
async function checkDependencyExists(projectRoot, depId, config) {
|
|
491
541
|
const dirsToCheck = [
|
|
@@ -522,13 +572,13 @@ function instructionVerbOf(name) {
|
|
|
522
572
|
* verb such as "update" or "document" are rejected with the capability and the
|
|
523
573
|
* offending requirement title.
|
|
524
574
|
*/
|
|
525
|
-
function scanInstructionShapedRequirements(capability, requirements,
|
|
575
|
+
function scanInstructionShapedRequirements(capability, deltaFile, requirements, findings) {
|
|
526
576
|
for (const requirement of requirements) {
|
|
527
577
|
const verb = instructionVerbOf(requirement.name);
|
|
528
578
|
if (!verb) {
|
|
529
579
|
continue;
|
|
530
580
|
}
|
|
531
|
-
|
|
581
|
+
findings.push(makeFinding('error', { file: deltaFile, requirement: requirement.name }, `${OPENSPEC_ERROR_PREFIX} ${capability}: requirement "${requirement.name}" is instruction-shaped ("${verb}"); delta specs must define living capability requirements, not instructions`));
|
|
532
582
|
}
|
|
533
583
|
}
|
|
534
584
|
/**
|
|
@@ -536,29 +586,31 @@ function scanInstructionShapedRequirements(capability, requirements, errors) {
|
|
|
536
586
|
* the living base spec at `openspec/specs/<capability>/spec.md`. Applying the
|
|
537
587
|
* delta through the deterministic merge engine reports unmatched MODIFIED,
|
|
538
588
|
* REMOVED, or RENAMED targets; ADDED-only deltas may create a new capability.
|
|
589
|
+
* Merged results are validated against their living spec's repository findings.
|
|
539
590
|
*/
|
|
540
|
-
export async function verifyDeltaTargets(projectRoot, folderPath, config) {
|
|
541
|
-
const
|
|
591
|
+
export async function verifyDeltaTargets(projectRoot, folderPath, config, repositoryFindings = []) {
|
|
592
|
+
const findings = [];
|
|
593
|
+
const mergedSpecs = [];
|
|
542
594
|
const deltasDir = path.join(folderPath, 'specs');
|
|
543
595
|
let entries = [];
|
|
544
596
|
try {
|
|
545
597
|
entries = await fs.readdir(deltasDir, { withFileTypes: true });
|
|
546
598
|
}
|
|
547
599
|
catch {
|
|
548
|
-
return
|
|
600
|
+
return findings;
|
|
549
601
|
}
|
|
550
602
|
const capabilities = entries
|
|
551
603
|
.filter((entry) => entry.isDirectory())
|
|
552
604
|
.map((entry) => entry.name)
|
|
553
605
|
.sort();
|
|
554
606
|
if (capabilities.length === 0) {
|
|
555
|
-
return
|
|
607
|
+
return findings;
|
|
556
608
|
}
|
|
557
609
|
const specsRoot = path.join(projectRoot, resolveOpenSpecRoot(config), 'specs');
|
|
558
610
|
for (const capability of capabilities) {
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
|
|
611
|
+
const deltaPath = path.join(deltasDir, capability, 'spec.md');
|
|
612
|
+
const deltaFile = repositoryPath(projectRoot, deltaPath);
|
|
613
|
+
const deltaContent = await fs.readFile(deltaPath, 'utf8').catch(() => null);
|
|
562
614
|
if (deltaContent === null) {
|
|
563
615
|
continue;
|
|
564
616
|
}
|
|
@@ -566,20 +618,32 @@ export async function verifyDeltaTargets(projectRoot, folderPath, config) {
|
|
|
566
618
|
.readFile(path.join(specsRoot, capability, 'spec.md'), 'utf8')
|
|
567
619
|
.catch(() => null);
|
|
568
620
|
const delta = parseDelta(deltaContent);
|
|
569
|
-
scanInstructionShapedRequirements(capability, [...delta.added, ...delta.modified, ...delta.removed],
|
|
621
|
+
scanInstructionShapedRequirements(capability, deltaFile, [...delta.added, ...delta.modified, ...delta.removed], findings);
|
|
570
622
|
try {
|
|
571
|
-
mergeDelta(baseContent, capability, delta);
|
|
623
|
+
const merged = mergeDelta(baseContent, capability, delta);
|
|
624
|
+
mergedSpecs.push({ capability, mergedContent: merged, deltaFile });
|
|
572
625
|
}
|
|
573
626
|
catch (error) {
|
|
574
627
|
if (error instanceof DeltaMergeError) {
|
|
575
|
-
|
|
628
|
+
findings.push(makeFinding('error', { file: deltaFile }, `${OPENSPEC_ERROR_PREFIX} ${capability}: ${error.message}`));
|
|
576
629
|
}
|
|
577
630
|
else {
|
|
578
631
|
throw error;
|
|
579
632
|
}
|
|
580
633
|
}
|
|
581
634
|
}
|
|
582
|
-
|
|
635
|
+
if (mergedSpecs.length > 0) {
|
|
636
|
+
findings.push(...(await collectMergedSpecFindings(projectRoot, config, mergedSpecs, repositoryFindings)));
|
|
637
|
+
}
|
|
638
|
+
return findings;
|
|
639
|
+
}
|
|
640
|
+
/** Validate every merged spec when the validator binary is available. */
|
|
641
|
+
async function collectMergedSpecFindings(projectRoot, config, mergedSpecs, repositoryFindings) {
|
|
642
|
+
const bin = await resolveOpenSpecBin(projectRoot, config);
|
|
643
|
+
if (!bin) {
|
|
644
|
+
return [];
|
|
645
|
+
}
|
|
646
|
+
return validateMergedSpecs(config, bin, resolveOpenSpecRoot(config), mergedSpecs, repositoryFindings);
|
|
583
647
|
}
|
|
584
648
|
/**
|
|
585
649
|
* Compares each pair of tasks' resolved existing files and returns one
|
|
@@ -600,7 +664,10 @@ function collectOverlapWarnings(scopes) {
|
|
|
600
664
|
.filter((relativePath) => other.has(relativePath))
|
|
601
665
|
.sort();
|
|
602
666
|
for (const relativePath of shared) {
|
|
603
|
-
warnings.push(
|
|
667
|
+
warnings.push({
|
|
668
|
+
file: relativePath,
|
|
669
|
+
message: `Tasks ${ordered[left].taskNumber} and ${ordered[right].taskNumber} share resolved scope file ${relativePath}`,
|
|
670
|
+
});
|
|
604
671
|
}
|
|
605
672
|
}
|
|
606
673
|
}
|
|
@@ -618,7 +685,10 @@ function collectHarnessFixtureWarnings(scopes) {
|
|
|
618
685
|
const hasHarness = scope.existingPaths.some((entry) => entry.startsWith('src/harness/'));
|
|
619
686
|
const hasFixtures = scope.existingPaths.some((entry) => entry === 'tests/fixtures/events' || entry.startsWith('tests/fixtures/events/'));
|
|
620
687
|
if (hasHarness && !hasFixtures) {
|
|
621
|
-
warnings.push(
|
|
688
|
+
warnings.push({
|
|
689
|
+
file: scope.taskPath,
|
|
690
|
+
message: `Task in ${scope.taskFile} resolves src/harness/ scope but no file under tests/fixtures/events/`,
|
|
691
|
+
});
|
|
622
692
|
}
|
|
623
693
|
}
|
|
624
694
|
return warnings;
|
|
@@ -650,85 +720,107 @@ function readTestsModifyDeclaration(content) {
|
|
|
650
720
|
return { present: false, valid: true };
|
|
651
721
|
}
|
|
652
722
|
export async function lintChangeFolder(projectRoot, folderPath, config, options = {}) {
|
|
653
|
-
const
|
|
654
|
-
const warnings = [];
|
|
723
|
+
const findings = new LintFindingSet();
|
|
655
724
|
const packageScripts = await readRootPackageScripts(projectRoot);
|
|
656
725
|
// Check: no authored change-folder file contains prohibited control characters
|
|
657
726
|
for (const relPath of await collectArtifactFiles(folderPath, folderPath)) {
|
|
658
727
|
const content = await fs.readFile(path.join(folderPath, relPath), 'utf8');
|
|
659
|
-
scanControlCharacters(relPath, content,
|
|
728
|
+
scanControlCharacters(relPath, repositoryPath(projectRoot, path.join(folderPath, relPath)), content, findings);
|
|
660
729
|
}
|
|
661
730
|
const resolvedDoc = await resolveChangeDoc(folderPath);
|
|
662
731
|
if (!resolvedDoc) {
|
|
663
|
-
|
|
664
|
-
return
|
|
732
|
+
findings.error({ file: repositoryPath(projectRoot, path.join(folderPath, 'proposal.md')) }, 'proposal.md (or legacy spec.md) not found in change folder');
|
|
733
|
+
return lintResult(findings);
|
|
665
734
|
}
|
|
735
|
+
const docRepoPath = repositoryPath(projectRoot, resolvedDoc.path);
|
|
666
736
|
const specContent = await fs.readFile(resolvedDoc.path, 'utf8');
|
|
667
737
|
const spec = parseSpecMd(specContent);
|
|
668
738
|
// Check: proposals must declare a change-level verify command
|
|
669
739
|
if (resolvedDoc.kind === 'proposal' && !spec.verify) {
|
|
670
|
-
|
|
740
|
+
findings.error({ file: docRepoPath }, 'proposal.md must declare a verify command in frontmatter');
|
|
671
741
|
}
|
|
672
742
|
// Check: proposals must declare a non-empty ## Surface section; legacy spec.md
|
|
673
743
|
// change documents are exempt exactly as they are for the verify check.
|
|
674
744
|
if (resolvedDoc.kind === 'proposal' &&
|
|
675
745
|
proposalSurfaceIsEmpty(parseFrontmatter(specContent).body)) {
|
|
676
|
-
|
|
746
|
+
findings.error({ file: docRepoPath, section: 'Surface' }, PROPOSAL_SURFACE_ERROR);
|
|
747
|
+
}
|
|
748
|
+
// Check: Decisions section and project rules block, when the project has ADRs.
|
|
749
|
+
// A legacy spec.md change document is exempt exactly as it is for Surface.
|
|
750
|
+
if (resolvedDoc.kind === 'proposal') {
|
|
751
|
+
for (const finding of await collectDecisionsFindings({
|
|
752
|
+
projectRoot,
|
|
753
|
+
folderPath,
|
|
754
|
+
proposalPath: docRepoPath,
|
|
755
|
+
proposalBody: parseFrontmatter(specContent).body,
|
|
756
|
+
config,
|
|
757
|
+
})) {
|
|
758
|
+
findings.addOwn(finding);
|
|
759
|
+
}
|
|
677
760
|
}
|
|
678
761
|
// Check: the change-level verify participates in the same trust analysis
|
|
679
762
|
if (spec.verify) {
|
|
680
763
|
const docLabel = path.basename(resolvedDoc.path);
|
|
681
764
|
const analysis = await analyzeVerifyCommand(projectRoot, spec.verify, packageScripts);
|
|
682
765
|
if (analysis.placeholder) {
|
|
683
|
-
|
|
766
|
+
findings.error({ file: docRepoPath }, placeholderVerifyError(docLabel));
|
|
684
767
|
}
|
|
685
768
|
else if (!chainsVerifyCommand(spec.verify)) {
|
|
686
769
|
if (analysis.missingScript !== null) {
|
|
687
|
-
|
|
770
|
+
findings.error({ file: docRepoPath }, missingPackageScriptError(docLabel, analysis.missingScript));
|
|
688
771
|
}
|
|
689
772
|
else if (analysis.unresolved) {
|
|
690
|
-
|
|
773
|
+
findings.warning({ file: docRepoPath }, unresolvedVerifyWarning(docLabel, spec.verify));
|
|
691
774
|
}
|
|
692
775
|
}
|
|
693
776
|
}
|
|
694
777
|
// Check: features.writes is retired; delta specs are the sole writes declaration
|
|
695
778
|
if (hasDeclaredWrites(parseFrontmatter(specContent).data)) {
|
|
696
|
-
|
|
779
|
+
findings.error({ file: docRepoPath }, 'features.writes is no longer supported in proposal frontmatter; write declarations are derived strictly from delta specs under specs/<capability>/spec.md');
|
|
697
780
|
}
|
|
698
781
|
// Check: contract tables count
|
|
699
782
|
if (spec.contractTablesCount > config.limits.maxContractTables) {
|
|
700
|
-
|
|
783
|
+
findings.error({ file: docRepoPath }, `Contract has ${spec.contractTablesCount} tables (max allowed is ${config.limits.maxContractTables})`);
|
|
701
784
|
}
|
|
702
785
|
// Check: depends_on exists
|
|
703
786
|
for (const dep of spec.dependsOn) {
|
|
704
787
|
const exists = await checkDependencyExists(projectRoot, dep, config);
|
|
705
788
|
if (!exists) {
|
|
706
|
-
|
|
789
|
+
findings.error({ file: docRepoPath }, `depends_on names missing change: ${dep}`);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
// Check: fixes names an existing active, archived, or rejected change
|
|
793
|
+
for (const fixed of spec.fixes) {
|
|
794
|
+
const exists = await checkDependencyExists(projectRoot, fixed, config);
|
|
795
|
+
if (!exists) {
|
|
796
|
+
findings.error({ file: docRepoPath }, `fixes names missing change: ${fixed}`);
|
|
707
797
|
}
|
|
708
798
|
}
|
|
709
799
|
// Tasks checks
|
|
710
800
|
const tasksDir = path.join(folderPath, 'tasks');
|
|
801
|
+
const tasksRepoPath = repositoryPath(projectRoot, tasksDir);
|
|
711
802
|
let taskEntries = [];
|
|
712
803
|
try {
|
|
713
804
|
taskEntries = (await fs.readdir(tasksDir)).filter((e) => e.endsWith('.md'));
|
|
714
805
|
}
|
|
715
806
|
catch {
|
|
716
|
-
|
|
807
|
+
findings.error({ file: tasksRepoPath }, 'tasks directory not found in change folder');
|
|
717
808
|
}
|
|
718
809
|
if (taskEntries.length === 0) {
|
|
719
|
-
|
|
810
|
+
findings.error({ file: tasksRepoPath }, 'No task files found under tasks/');
|
|
720
811
|
}
|
|
721
812
|
const resolvedTaskScopes = [];
|
|
722
813
|
for (const taskFile of taskEntries) {
|
|
723
814
|
const taskPath = path.join(tasksDir, taskFile);
|
|
815
|
+
const taskRepoPath = repositoryPath(projectRoot, taskPath);
|
|
724
816
|
const taskContent = await fs.readFile(taskPath, 'utf8');
|
|
725
817
|
const task = parseTaskMd(taskContent);
|
|
726
818
|
// Check: acceptance items must each occupy their own line
|
|
727
|
-
scanFusedAcceptance(taskFile, taskContent,
|
|
819
|
+
scanFusedAcceptance(taskFile, taskRepoPath, taskContent, findings);
|
|
728
820
|
// Check: tests.modify, when declared, must be a boolean
|
|
729
821
|
const testsModify = readTestsModifyDeclaration(taskContent);
|
|
730
822
|
if (testsModify.present && !testsModify.valid) {
|
|
731
|
-
|
|
823
|
+
findings.error({ file: taskRepoPath }, `Task in ${taskFile} tests.modify must be a boolean (got ${JSON.stringify(testsModify.value)})`);
|
|
732
824
|
}
|
|
733
825
|
// Resolve the declared scope once through the shared deterministic resolver.
|
|
734
826
|
const resolved = task.scope.length > 0 ? await resolveScope(projectRoot, task.scope) : [];
|
|
@@ -742,7 +834,9 @@ export async function lintChangeFolder(projectRoot, folderPath, config, options
|
|
|
742
834
|
resolvedTaskScopes.push({
|
|
743
835
|
taskFile,
|
|
744
836
|
taskNumber: taskFile.replace(/\.md$/, ''),
|
|
837
|
+
taskPath: taskRepoPath,
|
|
745
838
|
existingPaths,
|
|
839
|
+
resolvedPaths: resolved.map((entry) => entry.relativePath),
|
|
746
840
|
task,
|
|
747
841
|
verifyAnalysis: analysis,
|
|
748
842
|
});
|
|
@@ -750,35 +844,39 @@ export async function lintChangeFolder(projectRoot, folderPath, config, options
|
|
|
750
844
|
if (!task.testsModify && existingPaths.length > 0) {
|
|
751
845
|
const touched = existingPaths.filter(isTestFilePath).sort();
|
|
752
846
|
if (touched.length > 0) {
|
|
753
|
-
|
|
847
|
+
findings.error({ file: taskRepoPath }, `Task in ${taskFile} scope touches existing test files (${touched.join(', ')}) without tests.modify: true`);
|
|
754
848
|
}
|
|
755
849
|
}
|
|
756
850
|
// Check: verify command empty, placeholder, chained, or missing script
|
|
757
851
|
if (analysis === null) {
|
|
758
|
-
|
|
852
|
+
findings.error({ file: taskRepoPath }, `Task in ${taskFile} verify command is empty`);
|
|
759
853
|
}
|
|
760
854
|
else if (analysis.placeholder) {
|
|
761
|
-
|
|
855
|
+
findings.error({ file: taskRepoPath }, placeholderVerifyError(taskLabel));
|
|
762
856
|
}
|
|
763
857
|
else if (chainsVerifyCommand(task.verify)) {
|
|
764
|
-
|
|
858
|
+
findings.error({ file: taskRepoPath }, chainedVerifyError(taskFile, task.verify));
|
|
765
859
|
}
|
|
766
860
|
else if (analysis.missingScript !== null) {
|
|
767
|
-
|
|
861
|
+
findings.error({ file: taskRepoPath }, missingPackageScriptError(taskLabel, analysis.missingScript));
|
|
768
862
|
}
|
|
769
863
|
// Check: acceptance checklist length
|
|
770
864
|
if (task.acceptance.length > config.limits.maxAcceptanceLines) {
|
|
771
|
-
|
|
865
|
+
findings.error({ file: taskRepoPath }, `Task in ${taskFile} acceptance lines (${task.acceptance.length}) exceeds limit ${config.limits.maxAcceptanceLines}`);
|
|
772
866
|
}
|
|
773
867
|
// Check: scope expands to more than maxScopeFiles
|
|
774
868
|
if (task.scope.length > config.limits.maxScopeFiles) {
|
|
775
|
-
|
|
869
|
+
findings.error({ file: taskRepoPath }, `Task in ${taskFile} scope specifies ${task.scope.length} patterns, exceeding limit ${config.limits.maxScopeFiles}`);
|
|
776
870
|
}
|
|
777
871
|
}
|
|
778
872
|
// Check: harness scope should cover the event fixtures without failing lint
|
|
779
|
-
|
|
873
|
+
for (const entry of collectHarnessFixtureWarnings(resolvedTaskScopes)) {
|
|
874
|
+
findings.warning({ file: entry.file }, entry.message);
|
|
875
|
+
}
|
|
780
876
|
// Check: shared resolved files across tasks are reported as non-failing warnings
|
|
781
|
-
|
|
877
|
+
for (const entry of collectOverlapWarnings(resolvedTaskScopes)) {
|
|
878
|
+
findings.warning({ file: entry.file }, entry.message);
|
|
879
|
+
}
|
|
782
880
|
// Tasks are analyzed in numeric order so earlier scopes can cover a later
|
|
783
881
|
// task's new paths. The unresolved-target warning is suppressed when every
|
|
784
882
|
// missing named path is one the task's own or an earlier task's scope covers.
|
|
@@ -791,7 +889,7 @@ export async function lintChangeFolder(projectRoot, folderPath, config, options
|
|
|
791
889
|
const covered = namedPaths.length > 0 &&
|
|
792
890
|
missingPaths.every((namedPath) => namedPathCoveredByScopes(namedPath, [scope.task.scope, ...earlierScopes]));
|
|
793
891
|
if (!covered) {
|
|
794
|
-
|
|
892
|
+
findings.warning({ file: scope.taskPath }, unresolvedVerifyWarning(`Task in ${scope.taskFile}`, scope.task.verify));
|
|
795
893
|
}
|
|
796
894
|
}
|
|
797
895
|
earlierScopes.push([...scope.task.scope]);
|
|
@@ -806,21 +904,83 @@ export async function lintChangeFolder(projectRoot, folderPath, config, options
|
|
|
806
904
|
}));
|
|
807
905
|
const verifyStarts = await analyzeVerifyStarts(projectRoot, verifyStartTasks);
|
|
808
906
|
for (const contradiction of verifyStarts.contradictions) {
|
|
809
|
-
|
|
907
|
+
findings.warning({ file: taskRepoPathFor(projectRoot, tasksDir, contradiction.taskNumber) }, `Task in ${contradiction.taskNumber}.md declares verify_starts: ${contradiction.start} but its verify names ${contradiction.path}, which the task creates`);
|
|
810
908
|
}
|
|
811
909
|
for (const uncreatable of verifyStarts.uncreatable) {
|
|
812
|
-
|
|
813
|
-
}
|
|
814
|
-
// Check:
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
910
|
+
findings.warning({ file: taskRepoPathFor(projectRoot, tasksDir, uncreatable.taskNumber) }, `Task in ${uncreatable.taskNumber}.md verify names ${uncreatable.path}, which no task in the change can create`);
|
|
911
|
+
}
|
|
912
|
+
// Check: import-graph impact warnings never fail the change. The graph is
|
|
913
|
+
// built once per lint run by the CLI and passed through `LintOptions`.
|
|
914
|
+
const importGraph = options.importGraph ??
|
|
915
|
+
(await buildImportGraph(projectRoot, { skip: [config.paths.openspecRoot] }));
|
|
916
|
+
for (const finding of await collectImpactFindings({
|
|
917
|
+
projectRoot,
|
|
918
|
+
folderPath,
|
|
919
|
+
config,
|
|
920
|
+
proposalPath: docRepoPath,
|
|
921
|
+
reads: spec.features.reads,
|
|
922
|
+
tasks: resolvedTaskScopes.map((scope) => ({
|
|
923
|
+
taskNumber: scope.taskNumber,
|
|
924
|
+
taskPath: scope.taskPath,
|
|
925
|
+
existingPaths: scope.existingPaths,
|
|
926
|
+
testsModify: scope.task.testsModify,
|
|
927
|
+
verify: scope.task.verify,
|
|
928
|
+
})),
|
|
929
|
+
importGraph,
|
|
930
|
+
})) {
|
|
931
|
+
findings.addOwn(finding);
|
|
932
|
+
}
|
|
933
|
+
// Check: scenario traceability links and the blast radius of changed
|
|
934
|
+
// scenarios. Reuses the graph already built for impact lint and builds the
|
|
935
|
+
// scenario index once.
|
|
936
|
+
for (const finding of await collectTraceabilityFindings({
|
|
937
|
+
projectRoot,
|
|
938
|
+
folderPath,
|
|
939
|
+
config,
|
|
940
|
+
proposalPath: docRepoPath,
|
|
941
|
+
tasks: resolvedTaskScopes.map((scope) => ({
|
|
942
|
+
taskNumber: scope.taskNumber,
|
|
943
|
+
taskPath: scope.taskPath,
|
|
944
|
+
resolvedPaths: scope.resolvedPaths,
|
|
945
|
+
testsModify: scope.task.testsModify,
|
|
946
|
+
})),
|
|
947
|
+
importGraph,
|
|
948
|
+
})) {
|
|
949
|
+
findings.addOwn(finding);
|
|
950
|
+
}
|
|
951
|
+
// Check: pinned OpenSpec validator and strict validation. Runs before the
|
|
952
|
+
// delta check so living-spec repository findings can suppress inherited
|
|
953
|
+
// warnings in the merged specs.
|
|
954
|
+
const changeFolder = path.basename(folderPath);
|
|
955
|
+
const openSpec = await validateWithOpenSpec(projectRoot, config, {
|
|
956
|
+
...options,
|
|
957
|
+
changeFolder,
|
|
958
|
+
});
|
|
959
|
+
for (const finding of openSpec.findings) {
|
|
960
|
+
findings.addOwn(finding);
|
|
961
|
+
}
|
|
962
|
+
for (const finding of openSpec.repository) {
|
|
963
|
+
findings.addRepository(finding);
|
|
964
|
+
}
|
|
965
|
+
// Check: delta targets resolve against living base specs before approval and
|
|
966
|
+
// the living spec each delta will produce validates after archive.
|
|
967
|
+
for (const finding of await verifyDeltaTargets(projectRoot, folderPath, config, openSpec.repository)) {
|
|
968
|
+
findings.addOwn(finding);
|
|
969
|
+
}
|
|
970
|
+
return lintResult(findings);
|
|
971
|
+
}
|
|
972
|
+
/** Repository path of a task file named by its numeric task number. */
|
|
973
|
+
function taskRepoPathFor(projectRoot, tasksDir, taskNumber) {
|
|
974
|
+
return repositoryPath(projectRoot, path.join(tasksDir, `${taskNumber}.md`));
|
|
975
|
+
}
|
|
976
|
+
/** Assemble the public lint result from the accumulated findings. */
|
|
977
|
+
function lintResult(findings) {
|
|
820
978
|
return {
|
|
821
|
-
valid: errors.length === 0,
|
|
822
|
-
errors,
|
|
823
|
-
warnings,
|
|
979
|
+
valid: findings.errors().length === 0,
|
|
980
|
+
errors: findings.errors(),
|
|
981
|
+
warnings: findings.warnings(),
|
|
982
|
+
findings: [...findings.findings],
|
|
983
|
+
repository: [...findings.repository],
|
|
824
984
|
};
|
|
825
985
|
}
|
|
826
986
|
//# sourceMappingURL=linter.js.map
|