devrites 3.0.7 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +67 -47
- package/SECURITY.md +31 -29
- package/docs/adr/0001-go-engine-as-control-plane.md +1 -1
- package/docs/adr/0002-dual-host-harness.md +1 -1
- package/docs/adr/0004-state-schema-phases-sections.md +1 -2
- package/docs/adr/0006-clock-seam-and-engine-ci-gates.md +3 -4
- package/docs/adr/0009-prebuild-decision-coverage-and-readiness.md +67 -0
- package/docs/adr/0010-agent-first-fresh-context-orchestration.md +71 -0
- package/docs/adr/0011-define-plan-transition-rights.md +28 -0
- package/docs/adr/README.md +3 -0
- package/docs/agents/triage-labels.md +7 -7
- package/docs/architecture.md +157 -140
- package/docs/cli.md +41 -12
- package/docs/command-map.md +49 -35
- package/docs/engine/agent-contract.md +92 -15
- package/docs/engine/commands.md +242 -72
- package/docs/engine/state-schema.md +52 -23
- package/docs/engine/workspace-schema.md +94 -15
- package/docs/extensions.md +1 -1
- package/docs/flow.md +80 -50
- package/docs/harness-compliance.md +29 -5
- package/docs/orchestration.md +107 -79
- package/docs/quick-reference.md +7 -3
- package/docs/release.md +4 -3
- package/docs/skills.md +64 -38
- package/docs/usage.md +57 -38
- package/engine/cmd/releasepack/main.go +219 -0
- package/engine/cmd/releasepack/main_test.go +170 -0
- package/engine/commands.go +170 -23
- package/engine/git_guard.go +187 -0
- package/engine/git_guard_test.go +283 -0
- package/engine/hookpolicy.go +53 -55
- package/engine/hookpolicy_test.go +91 -1
- package/engine/hooks.go +296 -75
- package/engine/hooks_events_test.go +276 -6
- package/engine/hooks_workspace.go +640 -159
- package/engine/internal/devritespaths/paths.go +65 -10
- package/engine/internal/devritespaths/paths_test.go +110 -0
- package/engine/internal/doctor/doctor.go +153 -23
- package/engine/internal/doctor/doctor_test.go +74 -0
- package/engine/internal/forge/forge.go +940 -0
- package/engine/internal/forge/forge_test.go +576 -0
- package/engine/internal/forge/git.go +245 -0
- package/engine/internal/forge/liveness_unix.go +48 -0
- package/engine/internal/forge/liveness_windows.go +67 -0
- package/engine/internal/forge/manifest.go +402 -0
- package/engine/internal/gate/gate.go +90 -71
- package/engine/internal/gate/gate_test.go +71 -2
- package/engine/internal/harness/compliance.go +18 -24
- package/engine/internal/harness/harness.go +37 -44
- package/engine/internal/harness/harness_test.go +21 -5
- package/engine/internal/install/install.go +486 -38
- package/engine/internal/install/install_test.go +383 -10
- package/engine/internal/iohooks/iohooks.go +350 -59
- package/engine/internal/iohooks/iohooks_test.go +421 -1
- package/engine/internal/lib/buildreadiness.go +35 -18
- package/engine/internal/lib/clarifyreturn.go +91 -0
- package/engine/internal/lib/clarifyreturn_test.go +123 -0
- package/engine/internal/lib/context.go +54 -15
- package/engine/internal/lib/cursor_compat_test.go +8 -0
- package/engine/internal/lib/extensions.go +2 -3
- package/engine/internal/lib/gitauthority.go +601 -0
- package/engine/internal/lib/gitauthority_test.go +346 -0
- package/engine/internal/lib/jsonout.go +25 -11
- package/engine/internal/lib/jsonout_test.go +16 -0
- package/engine/internal/lib/lanes.go +14 -8
- package/engine/internal/lib/observability_test.go +358 -0
- package/engine/internal/lib/packageexistence.go +151 -35
- package/engine/internal/lib/packageexistence_test.go +163 -0
- package/engine/internal/lib/progress.go +11 -9
- package/engine/internal/lib/provenance.go +462 -0
- package/engine/internal/lib/provenance_test.go +154 -0
- package/engine/internal/lib/readiness_contract.json +111 -0
- package/engine/internal/lib/readinessartifact.go +510 -0
- package/engine/internal/lib/readinessartifact_test.go +321 -0
- package/engine/internal/lib/reconcile.go +712 -88
- package/engine/internal/lib/reconcile_test.go +335 -16
- package/engine/internal/lib/recoveryattempts.go +298 -0
- package/engine/internal/lib/recoveryattempts_test.go +215 -0
- package/engine/internal/lib/resolve.go +23 -19
- package/engine/internal/lib/runbook_context_test.go +20 -0
- package/engine/internal/lib/session.go +701 -9
- package/engine/internal/lib/session_test.go +80 -13
- package/engine/internal/lib/testintegrity.go +33 -37
- package/engine/internal/lib/testintegrity_test.go +63 -1
- package/engine/internal/migrate/migrate.go +81 -1
- package/engine/internal/migrate/migrate_test.go +22 -1
- package/engine/internal/reason/reason.go +180 -0
- package/engine/internal/reason/reason_test.go +35 -0
- package/engine/internal/rootfacts/facts.go +466 -0
- package/engine/internal/rootfacts/facts_test.go +306 -0
- package/engine/internal/safepath/safepath.go +55 -0
- package/engine/internal/safepath/safepath_test.go +69 -0
- package/engine/internal/safepath/safepath_windows_test.go +26 -0
- package/engine/internal/state/clarify_transition.go +165 -0
- package/engine/internal/state/clarify_transition_test.go +130 -0
- package/engine/internal/state/cmd/workflowmanifest/main.go +12 -8
- package/engine/internal/state/cursor.go +58 -0
- package/engine/internal/state/cursor_test.go +42 -1
- package/engine/internal/state/feature.go +35 -48
- package/engine/internal/state/schema.go +90 -37
- package/engine/internal/state/snapshot.go +18 -2
- package/engine/internal/state/state_test.go +187 -7
- package/engine/internal/state/status.go +48 -11
- package/engine/internal/state/workflow_manifest.json +231 -50
- package/engine/internal/toolpolicy/classifier.go +533 -0
- package/engine/internal/toolpolicy/classifier_test.go +424 -0
- package/engine/internal/toolpolicy/git.go +616 -0
- package/engine/internal/toolpolicy/scanner.go +382 -0
- package/engine/main.go +160 -77
- package/engine/observability_cli_test.go +52 -0
- package/engine/root_routing_test.go +277 -0
- package/engine/testdata/golden/TestParityBuildReadiness/arg=approved.golden +1 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=emptystatus.golden +1 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=noclarify.golden +1 -0
- package/engine/testdata/golden/TestParityBuildReadiness/arg=novet.golden +1 -0
- package/engine/testdata/golden/TestParityBuildReadiness/arg=stalevet.golden +1 -0
- package/engine/testdata/golden/TestParityBuildReadiness/arg=trailhash.golden +1 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=trailpipe.golden +1 -1
- package/engine/testdata/golden/TestParityBuildReadiness/arg=vetnotready.golden +1 -0
- package/engine/testdata/golden/TestParityProgress/arg=allbuilt.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=done.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=mid.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=nophase.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=noslice.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=plan.golden +1 -1
- package/engine/testdata/golden/TestParityProgress/arg=seal.golden +1 -1
- package/engine/testdata/golden/TestParityReconcile/check-clean.golden +1 -1
- package/engine/testdata/golden/TestParityReconcile/inline-fallback.golden +1 -0
- package/engine/testdata/golden/TestParityReconcile/snapshot-no-allowlist.golden +1 -0
- package/engine/testdata/golden/TestParityWrightScope/devrites-edit-denied.golden +2 -0
- package/engine/testdata/golden/TestParityWrightScope/out-of-scope-enforce-denies.golden +1 -1
- package/engine/tests/adr_0011_define_plan_test.go +30 -0
- package/engine/tests/budget_test.go +19 -8
- package/engine/tests/concurrency_cli_test.go +7 -1
- package/engine/tests/doctor_cli_test.go +148 -2
- package/engine/tests/forge_cli_test.go +463 -0
- package/engine/tests/gate_test.go +63 -8
- package/engine/tests/hook_test.go +260 -8
- package/engine/tests/hooks_io_test.go +94 -3
- package/engine/tests/json_contract_test.go +127 -2
- package/engine/tests/migrate_cli_test.go +2 -2
- package/engine/tests/parity_buildreadiness_test.go +141 -10
- package/engine/tests/parity_learnings_test.go +16 -17
- package/engine/tests/parity_reconcile_test.go +20 -17
- package/engine/tests/parity_resolve_test.go +12 -11
- package/engine/tests/parity_test.go +21 -17
- package/pack/.claude/agents/devrites-code-reviewer.md +62 -48
- package/pack/.claude/agents/devrites-devex-reviewer.md +69 -53
- package/pack/.claude/agents/devrites-doubt-reviewer.md +29 -22
- package/pack/.claude/agents/devrites-evidence-scout.md +69 -0
- package/pack/.claude/agents/devrites-forge-judge.md +74 -61
- package/pack/.claude/agents/devrites-frontend-reviewer.md +48 -39
- package/pack/.claude/agents/devrites-performance-reviewer.md +49 -40
- package/pack/.claude/agents/devrites-plan-drafter.md +71 -0
- package/pack/.claude/agents/devrites-plan-reviewer.md +80 -47
- package/pack/.claude/agents/devrites-proof-runner.md +74 -0
- package/pack/.claude/agents/devrites-retrospector.md +48 -45
- package/pack/.claude/agents/devrites-security-auditor.md +46 -36
- package/pack/.claude/agents/devrites-simplifier-reviewer.md +50 -46
- package/pack/.claude/agents/devrites-slice-wright.md +153 -165
- package/pack/.claude/agents/devrites-spec-reviewer.md +34 -32
- package/pack/.claude/agents/devrites-strategy-reviewer.md +63 -34
- package/pack/.claude/agents/devrites-test-analyst.md +40 -30
- package/pack/.claude/settings.json +2 -1
- package/pack/.claude/skills/devrites-audit/SKILL.md +40 -61
- package/pack/.claude/skills/devrites-debug-recovery/SKILL.md +24 -13
- package/pack/.claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
- package/pack/.claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
- package/pack/.claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
- package/pack/.claude/skills/devrites-doubt/SKILL.md +25 -17
- package/pack/.claude/skills/devrites-interview/SKILL.md +53 -52
- package/pack/.claude/skills/devrites-lib/SKILL.md +11 -9
- package/pack/.claude/skills/devrites-lib/reference/intent-map.md +3 -2
- package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
- package/pack/.claude/skills/devrites-lib/reference/reply-contract.md +8 -1
- package/pack/.claude/skills/devrites-lib/reference/standards/README.md +38 -55
- package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
- package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +198 -190
- package/pack/.claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
- package/pack/.claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
- package/pack/.claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
- package/pack/.claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
- package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
- package/pack/.claude/skills/devrites-lib/reference/standards/core.md +23 -18
- package/pack/.claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
- package/pack/.claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
- package/pack/.claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
- package/pack/.claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
- package/pack/.claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
- package/pack/.claude/skills/devrites-lib/reference/standards/performance.md +0 -9
- package/pack/.claude/skills/devrites-lib/reference/standards/principles.md +1 -3
- package/pack/.claude/skills/devrites-lib/reference/standards/security.md +17 -7
- package/pack/.claude/skills/devrites-lib/reference/standards/testing.md +1 -27
- package/pack/.claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
- package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
- package/pack/.claude/skills/devrites-source-driven/SKILL.md +23 -22
- package/pack/.claude/skills/rite/SKILL.md +8 -6
- package/pack/.claude/skills/rite/reference/menu.md +8 -6
- package/pack/.claude/skills/rite-adopt/SKILL.md +33 -37
- package/pack/.claude/skills/rite-autocomplete/SKILL.md +33 -28
- package/pack/.claude/skills/rite-autocomplete/reference/loop.md +21 -21
- package/pack/.claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
- package/pack/.claude/skills/rite-build/SKILL.md +35 -30
- package/pack/.claude/skills/rite-build/reference/afk-discipline.md +38 -38
- package/pack/.claude/skills/rite-build/reference/evidence-standard.md +10 -0
- package/pack/.claude/skills/rite-build/reference/forge.md +186 -156
- package/pack/.claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
- package/pack/.claude/skills/rite-build/reference/phase-contract.md +96 -175
- package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +129 -134
- package/pack/.claude/skills/rite-clarify/SKILL.md +89 -0
- package/pack/.claude/skills/rite-clarify/reference/decision-coverage.md +59 -0
- package/pack/.claude/skills/rite-converge/SKILL.md +35 -25
- package/pack/.claude/skills/rite-define/SKILL.md +57 -32
- package/pack/.claude/skills/rite-define/reference/gates.md +16 -15
- package/pack/.claude/skills/rite-define/reference/plan-template.md +16 -8
- package/pack/.claude/skills/rite-frame/reference/failure-modes.md +22 -24
- package/pack/.claude/skills/rite-plan/SKILL.md +44 -16
- package/pack/.claude/skills/rite-plan/reference/task-breakdown.md +4 -0
- package/pack/.claude/skills/rite-polish/SKILL.md +27 -23
- package/pack/.claude/skills/rite-prototype/SKILL.md +25 -26
- package/pack/.claude/skills/rite-prove/SKILL.md +27 -17
- package/pack/.claude/skills/rite-resolve/SKILL.md +12 -11
- package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
- package/pack/.claude/skills/rite-review/SKILL.md +37 -28
- package/pack/.claude/skills/rite-seal/reference/phase-contract.md +27 -92
- package/pack/.claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
- package/pack/.claude/skills/rite-ship/reference/design-memory.md +31 -36
- package/pack/.claude/skills/rite-spec/SKILL.md +84 -135
- package/pack/.claude/skills/rite-spec/reference/investigation.md +37 -33
- package/pack/.claude/skills/rite-spec/reference/question-protocol.md +6 -2
- package/pack/.claude/skills/rite-spec/reference/spec-checklists.md +25 -24
- package/pack/.claude/skills/rite-spec/reference/spec-template.md +9 -2
- package/pack/.claude/skills/rite-spec/reference/state-workspace.md +13 -3
- package/pack/.claude/skills/rite-temper/SKILL.md +62 -47
- package/pack/.claude/skills/rite-temper/reference/review-dimensions.md +24 -22
- package/pack/.claude/skills/rite-vet/SKILL.md +110 -113
- package/pack/.claude/skills/rite-vet/reference/artifacts.md +42 -9
- package/pack/.claude/skills/rite-vet/reference/review-axes.md +39 -37
- package/pack/generated/claude/agents/devrites-code-reviewer.md +62 -48
- package/pack/generated/claude/agents/devrites-devex-reviewer.md +69 -53
- package/pack/generated/claude/agents/devrites-doubt-reviewer.md +29 -22
- package/pack/generated/claude/agents/devrites-evidence-scout.md +69 -0
- package/pack/generated/claude/agents/devrites-forge-judge.md +74 -61
- package/pack/generated/claude/agents/devrites-frontend-reviewer.md +48 -39
- package/pack/generated/claude/agents/devrites-performance-reviewer.md +49 -40
- package/pack/generated/claude/agents/devrites-plan-drafter.md +71 -0
- package/pack/generated/claude/agents/devrites-plan-reviewer.md +80 -47
- package/pack/generated/claude/agents/devrites-proof-runner.md +74 -0
- package/pack/generated/claude/agents/devrites-retrospector.md +48 -45
- package/pack/generated/claude/agents/devrites-security-auditor.md +46 -36
- package/pack/generated/claude/agents/devrites-simplifier-reviewer.md +50 -46
- package/pack/generated/claude/agents/devrites-slice-wright.md +153 -165
- package/pack/generated/claude/agents/devrites-spec-reviewer.md +34 -32
- package/pack/generated/claude/agents/devrites-strategy-reviewer.md +63 -34
- package/pack/generated/claude/agents/devrites-test-analyst.md +40 -30
- package/pack/generated/claude/settings.json +2 -1
- package/pack/generated/claude/skills/devrites-audit/SKILL.md +40 -61
- package/pack/generated/claude/skills/devrites-debug-recovery/SKILL.md +24 -13
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
- package/pack/generated/claude/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
- package/pack/generated/claude/skills/devrites-doubt/SKILL.md +25 -17
- package/pack/generated/claude/skills/devrites-interview/SKILL.md +53 -52
- package/pack/generated/claude/skills/devrites-lib/SKILL.md +11 -9
- package/pack/generated/claude/skills/devrites-lib/reference/intent-map.md +3 -2
- package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
- package/pack/generated/claude/skills/devrites-lib/reference/reply-contract.md +8 -1
- package/pack/generated/claude/skills/devrites-lib/reference/standards/README.md +38 -55
- package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
- package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +198 -190
- package/pack/generated/claude/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
- package/pack/generated/claude/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
- package/pack/generated/claude/skills/devrites-lib/reference/standards/code-review.md +16 -52
- package/pack/generated/claude/skills/devrites-lib/reference/standards/coding-style.md +2 -10
- package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
- package/pack/generated/claude/skills/devrites-lib/reference/standards/core.md +23 -18
- package/pack/generated/claude/skills/devrites-lib/reference/standards/deprecation.md +17 -57
- package/pack/generated/claude/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
- package/pack/generated/claude/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
- package/pack/generated/claude/skills/devrites-lib/reference/standards/hooks.md +3 -14
- package/pack/generated/claude/skills/devrites-lib/reference/standards/patterns.md +9 -25
- package/pack/generated/claude/skills/devrites-lib/reference/standards/performance.md +0 -9
- package/pack/generated/claude/skills/devrites-lib/reference/standards/principles.md +1 -3
- package/pack/generated/claude/skills/devrites-lib/reference/standards/security.md +17 -7
- package/pack/generated/claude/skills/devrites-lib/reference/standards/testing.md +1 -27
- package/pack/generated/claude/skills/devrites-lib/reference/standards/tooling.md +46 -50
- package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
- package/pack/generated/claude/skills/devrites-source-driven/SKILL.md +23 -22
- package/pack/generated/claude/skills/rite/SKILL.md +8 -6
- package/pack/generated/claude/skills/rite/reference/menu.md +8 -6
- package/pack/generated/claude/skills/rite-adopt/SKILL.md +33 -37
- package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +33 -28
- package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +21 -21
- package/pack/generated/claude/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
- package/pack/generated/claude/skills/rite-build/SKILL.md +35 -30
- package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +38 -38
- package/pack/generated/claude/skills/rite-build/reference/evidence-standard.md +10 -0
- package/pack/generated/claude/skills/rite-build/reference/forge.md +186 -156
- package/pack/generated/claude/skills/rite-build/reference/one-slice-cycle.md +4 -3
- package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +96 -175
- package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +129 -134
- package/pack/generated/claude/skills/rite-clarify/SKILL.md +89 -0
- package/pack/generated/claude/skills/rite-clarify/reference/decision-coverage.md +59 -0
- package/pack/generated/claude/skills/rite-converge/SKILL.md +35 -25
- package/pack/generated/claude/skills/rite-define/SKILL.md +57 -32
- package/pack/generated/claude/skills/rite-define/reference/gates.md +16 -15
- package/pack/generated/claude/skills/rite-define/reference/plan-template.md +16 -8
- package/pack/generated/claude/skills/rite-frame/reference/failure-modes.md +22 -24
- package/pack/generated/claude/skills/rite-plan/SKILL.md +44 -16
- package/pack/generated/claude/skills/rite-plan/reference/task-breakdown.md +4 -0
- package/pack/generated/claude/skills/rite-polish/SKILL.md +27 -23
- package/pack/generated/claude/skills/rite-prototype/SKILL.md +25 -26
- package/pack/generated/claude/skills/rite-prove/SKILL.md +27 -17
- package/pack/generated/claude/skills/rite-resolve/SKILL.md +12 -11
- package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +3 -0
- package/pack/generated/claude/skills/rite-review/SKILL.md +37 -28
- package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +27 -92
- package/pack/generated/claude/skills/rite-seal/reference/risk-and-rollback.md +13 -0
- package/pack/generated/claude/skills/rite-ship/reference/design-memory.md +31 -36
- package/pack/generated/claude/skills/rite-spec/SKILL.md +84 -135
- package/pack/generated/claude/skills/rite-spec/reference/investigation.md +37 -33
- package/pack/generated/claude/skills/rite-spec/reference/question-protocol.md +6 -2
- package/pack/generated/claude/skills/rite-spec/reference/spec-checklists.md +25 -24
- package/pack/generated/claude/skills/rite-spec/reference/spec-template.md +9 -2
- package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +13 -3
- package/pack/generated/claude/skills/rite-temper/SKILL.md +62 -47
- package/pack/generated/claude/skills/rite-temper/reference/review-dimensions.md +24 -22
- package/pack/generated/claude/skills/rite-vet/SKILL.md +110 -113
- package/pack/generated/claude/skills/rite-vet/reference/artifacts.md +42 -9
- package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +39 -37
- package/pack/generated/codex/AGENTS.md +5 -2
- package/pack/generated/codex/agents/devrites-code-reviewer.toml +69 -47
- package/pack/generated/codex/agents/devrites-devex-reviewer.toml +75 -51
- package/pack/generated/codex/agents/devrites-doubt-reviewer.toml +35 -20
- package/pack/generated/codex/agents/devrites-evidence-scout.toml +75 -0
- package/pack/generated/codex/agents/devrites-forge-judge.toml +80 -59
- package/pack/generated/codex/agents/devrites-frontend-reviewer.toml +54 -37
- package/pack/generated/codex/agents/devrites-performance-reviewer.toml +55 -38
- package/pack/generated/codex/agents/devrites-plan-drafter.toml +77 -0
- package/pack/generated/codex/agents/devrites-plan-reviewer.toml +82 -47
- package/pack/generated/codex/agents/devrites-proof-runner.toml +80 -0
- package/pack/generated/codex/agents/devrites-retrospector.toml +54 -43
- package/pack/generated/codex/agents/devrites-security-auditor.toml +53 -35
- package/pack/generated/codex/agents/devrites-simplifier-reviewer.toml +56 -44
- package/pack/generated/codex/agents/devrites-slice-wright.toml +159 -163
- package/pack/generated/codex/agents/devrites-spec-reviewer.toml +40 -30
- package/pack/generated/codex/agents/devrites-strategy-reviewer.toml +65 -34
- package/pack/generated/codex/agents/devrites-test-analyst.toml +46 -28
- package/pack/generated/codex/hooks.json +4 -14
- package/pack/generated/codex/skills/devrites-api-interface/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-audit/SKILL.md +47 -64
- package/pack/generated/codex/skills/devrites-browser-proof/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-debug-recovery/SKILL.md +31 -16
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/build-the-loop.md +24 -21
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/cleanup-and-classify.md +34 -6
- package/pack/generated/codex/skills/devrites-debug-recovery/reference/instrumentation.md +2 -2
- package/pack/generated/codex/skills/devrites-doubt/SKILL.md +32 -20
- package/pack/generated/codex/skills/devrites-frontend-craft/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-interview/SKILL.md +60 -55
- package/pack/generated/codex/skills/devrites-lib/SKILL.md +18 -12
- package/pack/generated/codex/skills/devrites-lib/reference/intent-map.md +3 -2
- package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +82 -137
- package/pack/generated/codex/skills/devrites-lib/reference/reply-contract.md +8 -1
- package/pack/generated/codex/skills/devrites-lib/reference/standards/README.md +38 -55
- package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +53 -27
- package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +198 -190
- package/pack/generated/codex/skills/devrites-lib/reference/standards/anti-patterns.md +5 -15
- package/pack/generated/codex/skills/devrites-lib/reference/standards/ci-cd.md +27 -58
- package/pack/generated/codex/skills/devrites-lib/reference/standards/code-review.md +16 -52
- package/pack/generated/codex/skills/devrites-lib/reference/standards/coding-style.md +2 -10
- package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +18 -61
- package/pack/generated/codex/skills/devrites-lib/reference/standards/core.md +23 -18
- package/pack/generated/codex/skills/devrites-lib/reference/standards/deprecation.md +17 -57
- package/pack/generated/codex/skills/devrites-lib/reference/standards/development-workflow.md +4 -33
- package/pack/generated/codex/skills/devrites-lib/reference/standards/git-workflow.md +4 -20
- package/pack/generated/codex/skills/devrites-lib/reference/standards/hooks.md +3 -14
- package/pack/generated/codex/skills/devrites-lib/reference/standards/patterns.md +9 -25
- package/pack/generated/codex/skills/devrites-lib/reference/standards/performance.md +0 -9
- package/pack/generated/codex/skills/devrites-lib/reference/standards/principles.md +1 -3
- package/pack/generated/codex/skills/devrites-lib/reference/standards/security.md +17 -7
- package/pack/generated/codex/skills/devrites-lib/reference/standards/testing.md +1 -27
- package/pack/generated/codex/skills/devrites-lib/reference/standards/tooling.md +46 -50
- package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +23 -10
- package/pack/generated/codex/skills/devrites-prose-craft/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-refresh-indexes/SKILL.md +7 -3
- package/pack/generated/codex/skills/devrites-source-driven/SKILL.md +29 -24
- package/pack/generated/codex/skills/devrites-ux-shape/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite/SKILL.md +19 -13
- package/pack/generated/codex/skills/rite/reference/menu.md +8 -6
- package/pack/generated/codex/skills/rite-adopt/SKILL.md +40 -40
- package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +40 -31
- package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +21 -21
- package/pack/generated/codex/skills/rite-autocomplete/reference/stop-conditions.md +15 -6
- package/pack/generated/codex/skills/rite-build/SKILL.md +42 -33
- package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +38 -38
- package/pack/generated/codex/skills/rite-build/reference/evidence-standard.md +10 -0
- package/pack/generated/codex/skills/rite-build/reference/forge.md +186 -156
- package/pack/generated/codex/skills/rite-build/reference/one-slice-cycle.md +4 -3
- package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +96 -175
- package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +129 -134
- package/pack/generated/codex/skills/rite-clarify/SKILL.md +105 -0
- package/pack/generated/codex/skills/rite-clarify/reference/decision-coverage.md +59 -0
- package/pack/generated/codex/skills/rite-converge/SKILL.md +42 -28
- package/pack/generated/codex/skills/rite-customize/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-define/SKILL.md +64 -35
- package/pack/generated/codex/skills/rite-define/reference/gates.md +16 -15
- package/pack/generated/codex/skills/rite-define/reference/plan-template.md +16 -8
- package/pack/generated/codex/skills/rite-doctor/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-dogfood/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-explain/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-frame/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-frame/reference/failure-modes.md +22 -24
- package/pack/generated/codex/skills/rite-handoff/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-learn/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-plan/SKILL.md +51 -19
- package/pack/generated/codex/skills/rite-plan/reference/task-breakdown.md +5 -1
- package/pack/generated/codex/skills/rite-polish/SKILL.md +34 -26
- package/pack/generated/codex/skills/rite-pov/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-pr-feedback/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-pressure-test/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-prototype/SKILL.md +32 -29
- package/pack/generated/codex/skills/rite-prove/SKILL.md +34 -20
- package/pack/generated/codex/skills/rite-quick/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-resolve/SKILL.md +19 -14
- package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +3 -0
- package/pack/generated/codex/skills/rite-review/SKILL.md +44 -31
- package/pack/generated/codex/skills/rite-seal/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +27 -92
- package/pack/generated/codex/skills/rite-seal/reference/risk-and-rollback.md +13 -0
- package/pack/generated/codex/skills/rite-ship/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-ship/reference/design-memory.md +31 -36
- package/pack/generated/codex/skills/rite-spec/SKILL.md +91 -138
- package/pack/generated/codex/skills/rite-spec/reference/investigation.md +37 -33
- package/pack/generated/codex/skills/rite-spec/reference/question-protocol.md +6 -2
- package/pack/generated/codex/skills/rite-spec/reference/spec-checklists.md +25 -24
- package/pack/generated/codex/skills/rite-spec/reference/spec-template.md +9 -2
- package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +13 -3
- package/pack/generated/codex/skills/rite-status/SKILL.md +7 -3
- package/pack/generated/codex/skills/rite-temper/SKILL.md +69 -50
- package/pack/generated/codex/skills/rite-temper/reference/review-dimensions.md +24 -22
- package/pack/generated/codex/skills/rite-vet/SKILL.md +117 -116
- package/pack/generated/codex/skills/rite-vet/reference/artifacts.md +42 -9
- package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +40 -38
- package/pack/generated/codex/skills/rite-zoom-out/SKILL.md +7 -3
- package/package.json +1 -1
- package/scripts/build-release-tarball.sh +32 -15
- package/scripts/check-authority-drift.py +125 -0
- package/scripts/check-instruction-size-baseline.mjs +19 -11
- package/scripts/check-invocation-integrity.py +2 -0
- package/scripts/codex-generate.sh +69 -33
- package/scripts/grade-feature.sh +121 -40
- package/scripts/live-hosts/agent-result.schema.json +230 -0
- package/scripts/live-hosts/claude.sh +87 -0
- package/scripts/live-hosts/codex.sh +81 -0
- package/scripts/live-hosts/common.sh +113 -0
- package/scripts/live-hosts/fake-host.py +264 -0
- package/scripts/live-hosts/host-transport.py +287 -0
- package/scripts/release-check.sh +5 -1
- package/scripts/run-agent-contract-evals.py +1380 -0
- package/scripts/run-behavioral-evals.sh +24 -30
- package/scripts/run-evals.sh +1 -5
- package/scripts/run-live-behavioral-evals.py +1274 -144
- package/scripts/run-outcome-evals.sh +414 -88
- package/scripts/run-tests.mjs +30 -2
- package/scripts/skills-inventory.mjs +1 -1
- package/scripts/validate-workflow-security.py +39 -20
- package/scripts/validate-workspace-schema.py +362 -10
- package/scripts/validate.sh +21 -15
- package/engine/testdata/golden/TestParityWrightScope/devrites-edit-allowed.golden +0 -1
- /package/engine/testdata/golden/{TestParityReconcile/check-no-claimed.golden → TestParityBuildReadiness/arg=clarifyopen.golden} +0 -0
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Scan GitHub Actions workflows for supply-chain
|
|
2
|
+
"""Scan GitHub Actions workflows for supply-chain and permission risks.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Release and auto-merge workflows are especially sensitive, but these rules
|
|
5
|
+
apply to every workflow. The check rejects workflows that:
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
tag
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
14
|
-
workflow invalid YAML and prevents every job from starting.
|
|
7
|
+
- use a non-local action without a full 40-character commit SHA, because a
|
|
8
|
+
moving tag can change upstream code;
|
|
9
|
+
- omit an explicit `permissions:` scope or use `permissions: write-all`;
|
|
10
|
+
- use `pull_request_target` outside a Dependabot-only workflow that does not
|
|
11
|
+
check out pull-request code;
|
|
12
|
+
- interpolate workflow_dispatch inputs directly into a shell `run:` block;
|
|
13
|
+
- leave `: ` unquoted inside a workflow or step name, producing invalid YAML.
|
|
15
14
|
|
|
16
15
|
Usage: validate-workflow-security.py [DIR] (default: .github/workflows)
|
|
17
16
|
Exit: 0 clean; 1 on any finding.
|
|
@@ -29,6 +28,8 @@ DEPENDABOT_ONLY_RE = re.compile(
|
|
|
29
28
|
re.MULTILINE,
|
|
30
29
|
)
|
|
31
30
|
UNQUOTED_NAME_COLON_RE = re.compile(r"^\s*(?:-\s*)?name:\s+[^'\"].*:\s+\S")
|
|
31
|
+
RUN_RE = re.compile(r"^(\s*)(?:-\s*)?run:\s*(.*)$")
|
|
32
|
+
DISPATCH_EXPRESSION_RE = re.compile(r"\$\{\{[^}]*\binputs\b", re.IGNORECASE)
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
def safe_dependabot_target(text):
|
|
@@ -46,18 +47,18 @@ def scan_text(path, text):
|
|
|
46
47
|
lines = text.splitlines()
|
|
47
48
|
dependabot_target_is_safe = safe_dependabot_target(text)
|
|
48
49
|
if not re.search(r"^\s*permissions:", text, re.MULTILINE):
|
|
49
|
-
findings.append("%s: no permissions
|
|
50
|
-
"add an explicit least-privilege
|
|
50
|
+
findings.append("%s: no permissions block. The default GITHUB_TOKEN is broad; "
|
|
51
|
+
"add an explicit least-privilege block" % path)
|
|
51
52
|
for i, line in enumerate(lines, 1):
|
|
52
53
|
if UNQUOTED_NAME_COLON_RE.match(line):
|
|
53
|
-
findings.append("%s:%d: name
|
|
54
|
+
findings.append("%s:%d: name has an unquoted colon. Quote the complete "
|
|
54
55
|
"name so GitHub can parse the workflow" % (path, i))
|
|
55
56
|
if "write-all" in line:
|
|
56
|
-
findings.append("%s:%d: permissions: write-all
|
|
57
|
-
"
|
|
57
|
+
findings.append("%s:%d: permissions: write-all grants too much access. "
|
|
58
|
+
"Limit it to the permissions this workflow needs" % (path, i))
|
|
58
59
|
if "pull_request_target" in line and not dependabot_target_is_safe:
|
|
59
|
-
findings.append("%s:%d: pull_request_target
|
|
60
|
-
"code
|
|
60
|
+
findings.append("%s:%d: pull_request_target exposes secrets to untrusted PR "
|
|
61
|
+
"code. Only a Dependabot-only workflow without checkout is allowed"
|
|
61
62
|
% (path, i))
|
|
62
63
|
m = USES_RE.match(line)
|
|
63
64
|
if not m:
|
|
@@ -68,9 +69,27 @@ def scan_text(path, text):
|
|
|
68
69
|
at = ref.rsplit("@", 1)
|
|
69
70
|
pin = at[1] if len(at) == 2 else ""
|
|
70
71
|
if not SHA_RE.match(pin):
|
|
71
|
-
findings.append("%s:%d: action '%s' not pinned to a full commit "
|
|
72
|
-
"SHA
|
|
72
|
+
findings.append("%s:%d: action '%s' is not pinned to a full commit "
|
|
73
|
+
"SHA. Pin it because a moving tag is a supply-chain risk"
|
|
73
74
|
% (path, i, ref))
|
|
75
|
+
for i, line in enumerate(lines):
|
|
76
|
+
match = RUN_RE.match(line)
|
|
77
|
+
if not match:
|
|
78
|
+
continue
|
|
79
|
+
run_indent = len(match.group(1))
|
|
80
|
+
run_lines = [(i + 1, match.group(2))]
|
|
81
|
+
for j in range(i + 1, len(lines)):
|
|
82
|
+
candidate = lines[j]
|
|
83
|
+
if candidate.strip() and len(candidate) - len(candidate.lstrip()) <= run_indent:
|
|
84
|
+
break
|
|
85
|
+
run_lines.append((j + 1, candidate))
|
|
86
|
+
for line_number, command in run_lines:
|
|
87
|
+
if DISPATCH_EXPRESSION_RE.search(command):
|
|
88
|
+
findings.append(
|
|
89
|
+
"%s:%d: workflow_dispatch input appears directly in run. "
|
|
90
|
+
"Pass it through env and quote the shell variable"
|
|
91
|
+
% (path, line_number)
|
|
92
|
+
)
|
|
74
93
|
return findings
|
|
75
94
|
|
|
76
95
|
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""Validate generated DevRites workspace artifacts.
|
|
2
|
+
"""Validate generated DevRites workspace artifacts against the Markdown contract.
|
|
3
3
|
|
|
4
|
-
The validator
|
|
5
|
-
|
|
4
|
+
The validator uses only the standard library, so it does not add a runtime
|
|
5
|
+
parser dependency.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
11
13
|
import re
|
|
12
14
|
import sys
|
|
13
15
|
from pathlib import Path
|
|
@@ -15,6 +17,21 @@ from pathlib import Path
|
|
|
15
17
|
from workflow_schema import PHASES, cursor_field
|
|
16
18
|
|
|
17
19
|
|
|
20
|
+
READINESS_CONTRACT_PATH = (
|
|
21
|
+
Path(__file__).resolve().parents[1]
|
|
22
|
+
/ "engine"
|
|
23
|
+
/ "internal"
|
|
24
|
+
/ "lib"
|
|
25
|
+
/ "readiness_contract.json"
|
|
26
|
+
)
|
|
27
|
+
READINESS_CONTRACT = json.loads(READINESS_CONTRACT_PATH.read_text(encoding="utf-8"))
|
|
28
|
+
READINESS_PLACEHOLDER_RE = re.compile(
|
|
29
|
+
r"<[^>\n]+>|\b(?:TODO|TBD|FIXME|UNKNOWN)\b", re.IGNORECASE
|
|
30
|
+
)
|
|
31
|
+
READINESS_DIGEST_RE = re.compile(r"^[0-9a-f]{64}$")
|
|
32
|
+
READINESS_AC_RE = re.compile(r"\bAC-?\d+\b", re.IGNORECASE)
|
|
33
|
+
|
|
34
|
+
|
|
18
35
|
ID_PATTERNS = {
|
|
19
36
|
"AC": re.compile(r"\bAC-\d{3}\b"),
|
|
20
37
|
"REQ": re.compile(r"\bREQ-\d{3}\b"),
|
|
@@ -49,10 +66,13 @@ HIGH_TRAFFIC_BUDGETS = {
|
|
|
49
66
|
"feature.md": 120,
|
|
50
67
|
"brief.md": 80,
|
|
51
68
|
"spec.md": 260,
|
|
69
|
+
"decision-coverage.md": 200,
|
|
52
70
|
"ai-spec.md": 160,
|
|
53
71
|
"plan.md": 220,
|
|
54
72
|
"tasks.md": 280,
|
|
55
73
|
"traceability.md": 220,
|
|
74
|
+
"eng-review.md": 240,
|
|
75
|
+
"test-plan.md": 260,
|
|
56
76
|
"state.md": 120,
|
|
57
77
|
"handoff.md": 120,
|
|
58
78
|
"evidence.md": 280,
|
|
@@ -77,6 +97,13 @@ REQUIRED_HEADINGS = {
|
|
|
77
97
|
"Measurable success",
|
|
78
98
|
"Scope boundaries",
|
|
79
99
|
],
|
|
100
|
+
"decision-coverage.md": [
|
|
101
|
+
"Topology",
|
|
102
|
+
"Coverage matrix",
|
|
103
|
+
"Assumption audit",
|
|
104
|
+
"Residual uncertainty",
|
|
105
|
+
"Readiness verdict",
|
|
106
|
+
],
|
|
80
107
|
"architecture.md": [
|
|
81
108
|
"Owning module / layer",
|
|
82
109
|
"Integration points",
|
|
@@ -106,6 +133,9 @@ WORKSPACE_MAP_FIELDS = ("phase", "status", "next_action", "last_updated")
|
|
|
106
133
|
SLICE_REQUIRED_FIELDS = (
|
|
107
134
|
"Goal",
|
|
108
135
|
"Satisfies",
|
|
136
|
+
"Forge",
|
|
137
|
+
"Forge strategies",
|
|
138
|
+
"Forge scorecard",
|
|
109
139
|
"Files likely touched",
|
|
110
140
|
"Tests/proof",
|
|
111
141
|
"Mode",
|
|
@@ -169,10 +199,9 @@ def existing_or_alias(workspace: Path, name: str) -> bool:
|
|
|
169
199
|
|
|
170
200
|
|
|
171
201
|
def find_local_workspaces(root: Path) -> list[Path]:
|
|
172
|
-
#
|
|
173
|
-
#
|
|
174
|
-
#
|
|
175
|
-
# scan below (return [root] before the loop ever runs).
|
|
202
|
+
# Detect workspaces by DevRites artifacts, not README.md. Most repository
|
|
203
|
+
# roots have a README, which would misclassify the root and stop the
|
|
204
|
+
# .devrites/work scan before it starts.
|
|
176
205
|
if root.is_dir() and any((root / name).exists() for name in ("spec.md", "state.md", "feature.md")):
|
|
177
206
|
return [root]
|
|
178
207
|
out: list[Path] = []
|
|
@@ -257,6 +286,56 @@ def validate_tasks(path: Path, text: str, errors: list[str]) -> None:
|
|
|
257
286
|
if not re.search(rf"(?im)^{re.escape(field)}\s*:\s*\S+", body):
|
|
258
287
|
errors.append(f"{path}: {slice_id} missing field '{field}:'")
|
|
259
288
|
|
|
289
|
+
def field(name: str) -> str:
|
|
290
|
+
match = re.search(rf"(?im)^{re.escape(name)}\s*:\s*(.+?)\s*$", body)
|
|
291
|
+
return match.group(1).strip() if match else ""
|
|
292
|
+
|
|
293
|
+
forge = field("Forge")
|
|
294
|
+
strategies = field("Forge strategies")
|
|
295
|
+
scorecard = field("Forge scorecard")
|
|
296
|
+
if not forge:
|
|
297
|
+
continue
|
|
298
|
+
if re.fullmatch(r"(?i)no", forge):
|
|
299
|
+
if strategies.lower() != "none" or scorecard.lower() != "none":
|
|
300
|
+
errors.append(
|
|
301
|
+
f"{path}: {slice_id} Forge:no requires strategies and scorecard 'none'"
|
|
302
|
+
)
|
|
303
|
+
continue
|
|
304
|
+
if not re.fullmatch(r"(?i)yes\s+[—-]\s+\S.*", forge):
|
|
305
|
+
errors.append(f"{path}: {slice_id} Forge must be 'no' or 'yes — <reason>'")
|
|
306
|
+
continue
|
|
307
|
+
|
|
308
|
+
parsed: list[tuple[str, str]] = []
|
|
309
|
+
for part in strategies.split("|"):
|
|
310
|
+
match = re.fullmatch(r"\s*([ABC])\s*=\s*(\S.*?)\s*", part)
|
|
311
|
+
if not match:
|
|
312
|
+
parsed = []
|
|
313
|
+
break
|
|
314
|
+
parsed.append((match.group(1), " ".join(match.group(2).split())))
|
|
315
|
+
ids = [item[0] for item in parsed]
|
|
316
|
+
descriptions = [item[1].casefold() for item in parsed]
|
|
317
|
+
if ids not in (["A", "B"], ["A", "B", "C"]) or len(set(descriptions)) != len(
|
|
318
|
+
descriptions
|
|
319
|
+
):
|
|
320
|
+
errors.append(
|
|
321
|
+
f"{path}: {slice_id} Forge strategies must be 2-3 distinct contiguous A-C entries"
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
acceptance = re.search(r"(?i)\bacceptance\s*=\s*[^;\n]*", scorecard)
|
|
325
|
+
satisfies_ids = set(ID_PATTERNS["AC"].findall(field("Satisfies")))
|
|
326
|
+
scorecard_ids = set(ID_PATTERNS["AC"].findall(acceptance.group(0))) if acceptance else set()
|
|
327
|
+
test_plan = re.search(
|
|
328
|
+
r"(?i)\btest-plan\s*=\s*(?=\S)(?!none\b).*test-plan\.md", scorecard
|
|
329
|
+
)
|
|
330
|
+
if (
|
|
331
|
+
not satisfies_ids
|
|
332
|
+
or not satisfies_ids.issubset(scorecard_ids)
|
|
333
|
+
or not test_plan
|
|
334
|
+
):
|
|
335
|
+
errors.append(
|
|
336
|
+
f"{path}: {slice_id} Forge scorecard must bind every Satisfies AC ID and exact test-plan.md rows or commands"
|
|
337
|
+
)
|
|
338
|
+
|
|
260
339
|
|
|
261
340
|
def validate_id_register(path: Path, text: str, kind: str, errors: list[str]) -> None:
|
|
262
341
|
if text and not ID_PATTERNS[kind].search(text):
|
|
@@ -299,9 +378,273 @@ def blocking_questions(questions: str) -> list[str]:
|
|
|
299
378
|
qid = (ID_PATTERNS["Q"].search(chunk) or re.search(r"\bq-\d{4}-\d{2}-\d{2}-\d{3}\b", chunk))
|
|
300
379
|
status = re.search(r"(?im)^status:\s*(\w+)", chunk)
|
|
301
380
|
gate = re.search(r"(?im)^gate:\s*(\w+)", chunk)
|
|
302
|
-
if status and status.group(1).lower() == "open" and gate and gate.group(1).lower() in {"blocking", "escalating"}:
|
|
381
|
+
if status and status.group(1).lower() == "open" and gate and gate.group(1).lower() in {"blocking", "validating", "escalating"}:
|
|
303
382
|
out.append(qid.group(0) if qid else "<unknown>")
|
|
304
|
-
|
|
383
|
+
|
|
384
|
+
status_index = gate_index = qid_index = -1
|
|
385
|
+
for raw in questions.splitlines():
|
|
386
|
+
line = raw.strip()
|
|
387
|
+
if not (line.startswith("|") and line.endswith("|")):
|
|
388
|
+
status_index = gate_index = qid_index = -1
|
|
389
|
+
continue
|
|
390
|
+
cells = [cell.strip() for cell in line[1:-1].split("|")]
|
|
391
|
+
lowered = [cell.lower() for cell in cells]
|
|
392
|
+
if status_index < 0:
|
|
393
|
+
status_index = lowered.index("status") if "status" in lowered else -1
|
|
394
|
+
gate_index = lowered.index("gate") if "gate" in lowered else -1
|
|
395
|
+
for label in ("question id", "question", "id"):
|
|
396
|
+
if label in lowered:
|
|
397
|
+
qid_index = lowered.index(label)
|
|
398
|
+
break
|
|
399
|
+
if status_index < 0 or gate_index < 0:
|
|
400
|
+
status_index = gate_index = qid_index = -1
|
|
401
|
+
continue
|
|
402
|
+
if status_index >= len(cells) or gate_index >= len(cells):
|
|
403
|
+
continue
|
|
404
|
+
if cells[status_index].lower() != "open" or cells[gate_index].lower() not in {
|
|
405
|
+
"blocking",
|
|
406
|
+
"validating",
|
|
407
|
+
"escalating",
|
|
408
|
+
}:
|
|
409
|
+
continue
|
|
410
|
+
qid = cells[qid_index] if 0 <= qid_index < len(cells) else "<unknown>"
|
|
411
|
+
out.append(qid or "<unknown>")
|
|
412
|
+
return list(dict.fromkeys(out))
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def readiness_digest(workspace: Path, names: list[str]) -> str:
|
|
416
|
+
digest = hashlib.sha256()
|
|
417
|
+
for name in names:
|
|
418
|
+
path = workspace / name
|
|
419
|
+
data = path.read_bytes()
|
|
420
|
+
if not data.strip():
|
|
421
|
+
raise ValueError(f"{name} is empty")
|
|
422
|
+
digest.update(name.encode())
|
|
423
|
+
digest.update(b"\0")
|
|
424
|
+
digest.update(str(len(data)).encode())
|
|
425
|
+
digest.update(b"\0")
|
|
426
|
+
digest.update(data)
|
|
427
|
+
digest.update(b"\0")
|
|
428
|
+
return digest.hexdigest()
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def cursor_values(text: str, key: str) -> list[str]:
|
|
432
|
+
want = key.strip().lower().replace("_", " ")
|
|
433
|
+
values: list[str] = []
|
|
434
|
+
for raw in text.splitlines():
|
|
435
|
+
line = raw.strip()
|
|
436
|
+
if line.startswith("|") and line.endswith("|"):
|
|
437
|
+
cells = [cell.strip() for cell in line[1:-1].split("|")]
|
|
438
|
+
if len(cells) >= 2 and cells[0].lower().replace("_", " ") == want:
|
|
439
|
+
values.append(cells[1])
|
|
440
|
+
continue
|
|
441
|
+
line = line.lstrip("-*+ \t")
|
|
442
|
+
if ":" not in line:
|
|
443
|
+
continue
|
|
444
|
+
found, value = line.split(":", 1)
|
|
445
|
+
if found.strip().lower().replace("_", " ") == want:
|
|
446
|
+
values.append(value.strip())
|
|
447
|
+
return values
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def markdown_section(text: str, wanted: str) -> str | None:
|
|
451
|
+
lines = text.splitlines()
|
|
452
|
+
start = -1
|
|
453
|
+
level = 0
|
|
454
|
+
for index, line in enumerate(lines):
|
|
455
|
+
match = re.match(r"^(#{1,6})\s+(.+?)\s*#*\s*$", line.strip())
|
|
456
|
+
if not match:
|
|
457
|
+
continue
|
|
458
|
+
if start < 0:
|
|
459
|
+
if match.group(2).strip().lower() == wanted.lower():
|
|
460
|
+
start = index + 1
|
|
461
|
+
level = len(match.group(1))
|
|
462
|
+
continue
|
|
463
|
+
if len(match.group(1)) <= level:
|
|
464
|
+
return "\n".join(lines[start:index])
|
|
465
|
+
return "\n".join(lines[start:]) if start >= 0 else None
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def markdown_table(text: str, heading: str) -> list[list[str]]:
|
|
469
|
+
section = markdown_section(text, heading)
|
|
470
|
+
if section is None:
|
|
471
|
+
return []
|
|
472
|
+
rows: list[list[str]] = []
|
|
473
|
+
header_seen = False
|
|
474
|
+
for raw in section.splitlines():
|
|
475
|
+
line = raw.strip()
|
|
476
|
+
if not (line.startswith("|") and line.endswith("|")):
|
|
477
|
+
continue
|
|
478
|
+
cells = [cell.strip() for cell in line[1:-1].split("|")]
|
|
479
|
+
if all(not cell.strip(" :-") for cell in cells):
|
|
480
|
+
continue
|
|
481
|
+
if not header_seen:
|
|
482
|
+
header_seen = True
|
|
483
|
+
continue
|
|
484
|
+
rows.append(cells)
|
|
485
|
+
return rows
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def empty_or_na(value: str) -> bool:
|
|
489
|
+
return value.strip().lower() in {"", "-", "n/a", "none"}
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def substantive_cells(row: list[str], *indexes: int) -> bool:
|
|
493
|
+
return all(index < len(row) and not empty_or_na(row[index]) for index in indexes)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def none_row(value: str) -> bool:
|
|
497
|
+
return value.strip().lower() in {
|
|
498
|
+
"none",
|
|
499
|
+
"no material assumptions",
|
|
500
|
+
"no residual uncertainty",
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def validate_readiness_base(
|
|
505
|
+
workspace: Path, contract: dict[str, object], errors: list[str]
|
|
506
|
+
) -> str | None:
|
|
507
|
+
path = workspace / str(contract["artifact"])
|
|
508
|
+
if not path.exists():
|
|
509
|
+
errors.append(f"{path}: missing readiness artifact")
|
|
510
|
+
return None
|
|
511
|
+
text = read(path)
|
|
512
|
+
if not text.strip():
|
|
513
|
+
errors.append(f"{path}: empty readiness artifact")
|
|
514
|
+
return None
|
|
515
|
+
if READINESS_PLACEHOLDER_RE.search(text):
|
|
516
|
+
errors.append(f"{path}: contains an unresolved placeholder")
|
|
517
|
+
for heading in contract.get("requiredHeadings", []):
|
|
518
|
+
if markdown_section(text, str(heading)) is None:
|
|
519
|
+
errors.append(f"{path}: missing heading '{heading}'")
|
|
520
|
+
for heading in contract.get("requiredTables", []):
|
|
521
|
+
if not markdown_table(text, str(heading)):
|
|
522
|
+
errors.append(f"{path}: heading '{heading}' has no table data rows")
|
|
523
|
+
|
|
524
|
+
verdicts = cursor_values(text, str(contract["verdictField"]))
|
|
525
|
+
if len(verdicts) != 1 or verdicts[0].upper() != str(contract["readyValue"]).upper():
|
|
526
|
+
errors.append(
|
|
527
|
+
f"{path}: must contain exactly one "
|
|
528
|
+
f"{contract['verdictField']}: {contract['readyValue']}"
|
|
529
|
+
)
|
|
530
|
+
digests = cursor_values(text, str(contract["digestField"]))
|
|
531
|
+
if len(digests) != 1 or not READINESS_DIGEST_RE.fullmatch(digests[0].lower()):
|
|
532
|
+
errors.append(f"{path}: must contain exactly one valid {contract['digestField']}")
|
|
533
|
+
else:
|
|
534
|
+
try:
|
|
535
|
+
expected = readiness_digest(workspace, list(contract["inputs"]))
|
|
536
|
+
except (OSError, ValueError) as exc:
|
|
537
|
+
errors.append(f"{path}: cannot compute readiness digest: {exc}")
|
|
538
|
+
else:
|
|
539
|
+
if digests[0].lower() != expected:
|
|
540
|
+
errors.append(f"{path}: input digest is stale")
|
|
541
|
+
return text
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
def validate_decision_coverage(workspace: Path, errors: list[str]) -> None:
|
|
545
|
+
contract = READINESS_CONTRACT["coverage"]
|
|
546
|
+
text = validate_readiness_base(workspace, contract, errors)
|
|
547
|
+
if text is None:
|
|
548
|
+
return
|
|
549
|
+
path = workspace / "decision-coverage.md"
|
|
550
|
+
for index, row in enumerate(markdown_table(text, "Topology"), 1):
|
|
551
|
+
if len(row) < 4 or not substantive_cells(row, 0, 1, 3):
|
|
552
|
+
errors.append(f"{path}: topology row {index} is not evidence-backed")
|
|
553
|
+
rows = markdown_table(text, "Coverage matrix")
|
|
554
|
+
allowed = {"closed", "agent-owned", "not-applicable", "deferred-nonblocking"}
|
|
555
|
+
for index, row in enumerate(rows, 1):
|
|
556
|
+
if len(row) < 6:
|
|
557
|
+
errors.append(f"{path}: coverage row {index} has fewer than 6 cells")
|
|
558
|
+
continue
|
|
559
|
+
status = row[2].strip().lower()
|
|
560
|
+
if not substantive_cells(row, 0, 1, 5):
|
|
561
|
+
errors.append(f"{path}: coverage row {index} is incomplete")
|
|
562
|
+
if status not in allowed:
|
|
563
|
+
errors.append(
|
|
564
|
+
f"{path}: coverage row {index} has unresolved status {row[2]!r}"
|
|
565
|
+
)
|
|
566
|
+
if status == "closed" and not substantive_cells(row, 3):
|
|
567
|
+
errors.append(f"{path}: coverage row {index} is closed without a canonical reference")
|
|
568
|
+
if status in {"agent-owned", "deferred-nonblocking"} and empty_or_na(row[4]):
|
|
569
|
+
errors.append(
|
|
570
|
+
f"{path}: coverage row {index} has no owner/validation gate"
|
|
571
|
+
)
|
|
572
|
+
for index, row in enumerate(markdown_table(text, "Assumption audit"), 1):
|
|
573
|
+
if len(row) < 6:
|
|
574
|
+
errors.append(f"{path}: assumption row {index} has fewer than 6 cells")
|
|
575
|
+
elif not none_row(row[0]) and not substantive_cells(row, 0, 1, 2, 3, 4, 5):
|
|
576
|
+
errors.append(f"{path}: assumption row {index} is unowned or unverifiable")
|
|
577
|
+
for index, row in enumerate(markdown_table(text, "Residual uncertainty"), 1):
|
|
578
|
+
if len(row) < 4:
|
|
579
|
+
errors.append(f"{path}: residual row {index} has fewer than 4 cells")
|
|
580
|
+
elif not none_row(row[0]) and not substantive_cells(row, 0, 1, 2, 3):
|
|
581
|
+
errors.append(f"{path}: residual row {index} is unowned or unverifiable")
|
|
582
|
+
questions_path = workspace / "questions.md"
|
|
583
|
+
if questions_path.exists():
|
|
584
|
+
open_gates = blocking_questions(read(questions_path))
|
|
585
|
+
if open_gates:
|
|
586
|
+
errors.append(
|
|
587
|
+
f"{questions_path}: open blocking/validating/escalating questions: "
|
|
588
|
+
+ ", ".join(open_gates)
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
|
|
592
|
+
def validate_engineering_readiness(workspace: Path, errors: list[str]) -> None:
|
|
593
|
+
contract = READINESS_CONTRACT["engineering"]
|
|
594
|
+
text = validate_readiness_base(workspace, contract, errors)
|
|
595
|
+
if text is None:
|
|
596
|
+
return
|
|
597
|
+
review_path = workspace / "eng-review.md"
|
|
598
|
+
for index, row in enumerate(markdown_table(text, "2a. Build-entry preflight"), 1):
|
|
599
|
+
if len(row) < 7:
|
|
600
|
+
errors.append(f"{review_path}: preflight row {index} has fewer than 7 cells")
|
|
601
|
+
continue
|
|
602
|
+
verdict = row[6].strip().lower()
|
|
603
|
+
if verdict not in {"pass", "n/a"}:
|
|
604
|
+
errors.append(f"{review_path}: preflight row {index} is not passing")
|
|
605
|
+
if not substantive_cells(row, 0) or (
|
|
606
|
+
verdict == "pass" and not substantive_cells(row, 1, 2, 4, 5)
|
|
607
|
+
):
|
|
608
|
+
errors.append(f"{review_path}: preflight row {index} lacks executable provenance")
|
|
609
|
+
for index, row in enumerate(markdown_table(text, "2b. Implementation readiness"), 1):
|
|
610
|
+
if (
|
|
611
|
+
len(row) < 6
|
|
612
|
+
or row[5].strip().lower() != "ready"
|
|
613
|
+
or not substantive_cells(row, 0, 1, 2, 3, 4)
|
|
614
|
+
):
|
|
615
|
+
errors.append(f"{review_path}: readiness row {index} is not ready")
|
|
616
|
+
|
|
617
|
+
test_contract = READINESS_CONTRACT["testPlan"]
|
|
618
|
+
test_path = workspace / str(test_contract["artifact"])
|
|
619
|
+
if not test_path.exists():
|
|
620
|
+
errors.append(f"{test_path}: missing test plan")
|
|
621
|
+
return
|
|
622
|
+
test_text = read(test_path)
|
|
623
|
+
if not test_text.strip() or READINESS_PLACEHOLDER_RE.search(test_text):
|
|
624
|
+
errors.append(f"{test_path}: empty or contains an unresolved placeholder")
|
|
625
|
+
return
|
|
626
|
+
for heading in test_contract.get("requiredHeadings", []):
|
|
627
|
+
if markdown_section(test_text, str(heading)) is None:
|
|
628
|
+
errors.append(f"{test_path}: missing heading '{heading}'")
|
|
629
|
+
for heading in test_contract.get("requiredTables", []):
|
|
630
|
+
rows = markdown_table(test_text, str(heading))
|
|
631
|
+
if not rows:
|
|
632
|
+
errors.append(f"{test_path}: heading '{heading}' has no table data rows")
|
|
633
|
+
continue
|
|
634
|
+
required_cells = (0, 1, 2, 3, 5)
|
|
635
|
+
if heading == "Per-gap test requirements":
|
|
636
|
+
required_cells = (0, 1, 2, 3, 4, 5, 6)
|
|
637
|
+
for index, row in enumerate(rows, 1):
|
|
638
|
+
if not substantive_cells(row, *required_cells):
|
|
639
|
+
errors.append(f"{test_path}: heading '{heading}' row {index} is incomplete")
|
|
640
|
+
mapping = markdown_section(test_text, "Acceptance → test map") or ""
|
|
641
|
+
if "→" not in mapping and "->" not in mapping:
|
|
642
|
+
errors.append(f"{test_path}: acceptance map has no mappings")
|
|
643
|
+
spec_path = workspace / "spec.md"
|
|
644
|
+
if spec_path.exists():
|
|
645
|
+
for acceptance_id in sorted(set(READINESS_AC_RE.findall(read(spec_path)))):
|
|
646
|
+
if acceptance_id.upper() not in mapping.upper():
|
|
647
|
+
errors.append(f"{test_path}: acceptance map does not map {acceptance_id}")
|
|
305
648
|
|
|
306
649
|
|
|
307
650
|
def validate_workspace(workspace: Path) -> list[str]:
|
|
@@ -316,6 +659,10 @@ def validate_workspace(workspace: Path) -> list[str]:
|
|
|
316
659
|
for name in required:
|
|
317
660
|
if not existing_or_alias(workspace, name):
|
|
318
661
|
errors.append(f"{workspace}: phase {phase} requires {name}")
|
|
662
|
+
if "decision-coverage.md" in required:
|
|
663
|
+
validate_decision_coverage(workspace, errors)
|
|
664
|
+
if "eng-review.md" in required:
|
|
665
|
+
validate_engineering_readiness(workspace, errors)
|
|
319
666
|
|
|
320
667
|
for path in sorted(workspace.glob("*.md")):
|
|
321
668
|
text = read(path)
|
|
@@ -419,7 +766,12 @@ def validate_workspace(workspace: Path) -> list[str]:
|
|
|
419
766
|
|
|
420
767
|
def main(argv: list[str]) -> int:
|
|
421
768
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
422
|
-
parser.add_argument(
|
|
769
|
+
parser.add_argument(
|
|
770
|
+
"paths",
|
|
771
|
+
nargs="*",
|
|
772
|
+
default=["."],
|
|
773
|
+
help="workspace directories or roots containing .devrites/work|features|archive",
|
|
774
|
+
)
|
|
423
775
|
args = parser.parse_args(argv)
|
|
424
776
|
|
|
425
777
|
workspaces: list[Path] = []
|
package/scripts/validate.sh
CHANGED
|
@@ -230,6 +230,13 @@ else
|
|
|
230
230
|
echo "skip: go not found; workflow manifest freshness not checked"
|
|
231
231
|
fi
|
|
232
232
|
if command -v python3 >/dev/null 2>&1; then
|
|
233
|
+
if python3 "$ROOT/scripts/check-authority-drift.py" >/tmp/dr_authority_drift 2>&1; then
|
|
234
|
+
cat /tmp/dr_authority_drift
|
|
235
|
+
good "authority-derived docs are current"
|
|
236
|
+
else
|
|
237
|
+
cat /tmp/dr_authority_drift
|
|
238
|
+
bad "authority-derived docs drifted"
|
|
239
|
+
fi
|
|
233
240
|
if python3 "$ROOT/scripts/validate-workspace-schema.py" "$ROOT/tests/fixtures/workspace-schema" >/tmp/dr_workspace_schema 2>&1; then
|
|
234
241
|
cat /tmp/dr_workspace_schema
|
|
235
242
|
good "workspace artifact schema fixtures valid"
|
|
@@ -252,13 +259,13 @@ else
|
|
|
252
259
|
fi
|
|
253
260
|
|
|
254
261
|
# ---- 12. no runtime-broken pack/.claude/ path in installed prose ---------
|
|
255
|
-
#
|
|
256
|
-
#
|
|
257
|
-
#
|
|
262
|
+
# Installed paths omit the leading pack/. A literal pack/.claude/skills/ path
|
|
263
|
+
# in shipped skill or reference prose will not resolve. Repository README and
|
|
264
|
+
# documentation links are GitHub links, so this check ignores them.
|
|
258
265
|
section "no literal pack/.claude/ paths in shipped skill prose"
|
|
259
|
-
#
|
|
260
|
-
#
|
|
261
|
-
#
|
|
266
|
+
# Keep the resolution-snippet fallback (`... || P=pack/.claude/...`). It checks
|
|
267
|
+
# the installed `.claude/` path first, then `${CLAUDE_SKILL_DIR}` as a
|
|
268
|
+
# best-effort plugin path, and finally the repository path during development.
|
|
262
269
|
PACKPATH_HITS="$(grep -rnI -e 'pack/\.claude/skills/devrites-lib/reference/standards/' -e 'pack/\.claude/skills/' "$SKILLS" 2>/dev/null | grep -vE '\|\| [A-Z]+=pack/\.claude/skills/' || true)"
|
|
263
270
|
if [ -n "$PACKPATH_HITS" ]; then
|
|
264
271
|
bad "literal pack/.claude/ path in shipped skill prose (strips to .claude/ on install):"
|
|
@@ -268,8 +275,9 @@ else
|
|
|
268
275
|
fi
|
|
269
276
|
|
|
270
277
|
# ---- 14. no false session-start autoload claim ---------------------------
|
|
271
|
-
# DevRites
|
|
272
|
-
#
|
|
278
|
+
# DevRites has no autoload wiring. Skills read
|
|
279
|
+
# .claude/skills/devrites-lib/reference/standards/core.md at step 0. Reject any
|
|
280
|
+
# shipped skill or document that claims native session-start autoloading.
|
|
273
281
|
section "no false session-start autoload claim"
|
|
274
282
|
AUTOLOAD_HITS="$(grep -rl 'autoloaded by Claude Code' "$ROOT/pack" "$ROOT/docs" "$ROOT/README.md" 2>/dev/null || true)"
|
|
275
283
|
if [ -n "$AUTOLOAD_HITS" ]; then
|
|
@@ -280,9 +288,8 @@ else
|
|
|
280
288
|
fi
|
|
281
289
|
|
|
282
290
|
# ---- 14b. no deleted shell-helper guidance -------------------------------
|
|
283
|
-
#
|
|
284
|
-
#
|
|
285
|
-
# guidance must not direct users or agents back to deleted helper files.
|
|
291
|
+
# Public docs and generated installer guidance must use the installed
|
|
292
|
+
# `devrites-engine` binary, not the retired devrites-lib/*.sh helpers.
|
|
286
293
|
section "no deleted shell-helper guidance"
|
|
287
294
|
DELETED_HELPER_HITS="$(grep -rnI \
|
|
288
295
|
-e 'analyze\.sh' \
|
|
@@ -325,15 +332,14 @@ else
|
|
|
325
332
|
fi
|
|
326
333
|
|
|
327
334
|
# ---- 15. shellcheck (error = blocking, warning = advisory) ---------------
|
|
328
|
-
# CI runners
|
|
329
|
-
#
|
|
330
|
-
# where the tool isn't installed: never silently downgraded where it is).
|
|
335
|
+
# CI runners include shellcheck and enforce the error-level gate on every PR.
|
|
336
|
+
# Local validation skips this gate only when shellcheck is not installed.
|
|
331
337
|
section "shellcheck (-S error blocking · -S warning advisory)"
|
|
332
338
|
if command -v shellcheck >/dev/null 2>&1; then
|
|
333
339
|
for f in $SH_LIST; do
|
|
334
340
|
if shellcheck -S error "$f"; then good "shellcheck ${f#"$ROOT"/}"; else bad "shellcheck (error) ${f#"$ROOT"/}"; fi
|
|
335
341
|
done
|
|
336
|
-
#
|
|
342
|
+
# Warnings are advisory. Print them per file without failing the build.
|
|
337
343
|
for f in $SH_LIST; do
|
|
338
344
|
shellcheck -S warning "$f" >/dev/null 2>&1 || echo " advisory (warning-level): ${f#"$ROOT"/}"
|
|
339
345
|
done
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
exit 0
|