feat: harden OpenSpec SDD workflow
This commit is contained in:
Executable
+128
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)
|
||||
workspace=$(cd "$script_dir/../.." && pwd -P)
|
||||
verifier="$script_dir/verify-openspec-planning.sh"
|
||||
source_change="$workspace/specs/openspec/changes/harden-openspec-sdd-quality"
|
||||
changes_root="$workspace/specs/openspec/changes"
|
||||
prefix="planning-fixture-$$"
|
||||
created_changes=('')
|
||||
|
||||
cleanup() {
|
||||
local change
|
||||
for change in "${created_changes[@]}"; do
|
||||
[[ -n "$change" ]] || continue
|
||||
rm -rf "$changes_root/$change"
|
||||
done
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ ! -x "$verifier" ]]; then
|
||||
printf 'expected executable planning verifier at %s\n' "$verifier" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
make_change() {
|
||||
local suffix=$1
|
||||
fixture_change="$prefix-$suffix"
|
||||
cp -R "$source_change" "$changes_root/$fixture_change"
|
||||
created_changes+=("$fixture_change")
|
||||
}
|
||||
|
||||
run_verifier() {
|
||||
bash "$verifier" "$1"
|
||||
}
|
||||
|
||||
expect_failure() {
|
||||
local change=$1
|
||||
local expected=$2
|
||||
local output
|
||||
|
||||
if output=$(run_verifier "$change" 2>&1); then
|
||||
printf 'expected planning verification to fail for %s\n' "$change" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$output" != *"$expected"* ]]; then
|
||||
printf 'expected failure containing %q, got:\n%s\n' "$expected" "$output" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
make_change proposal
|
||||
proposal_change=$fixture_change
|
||||
rm -rf "$changes_root/$proposal_change/specs"
|
||||
rm -f "$changes_root/$proposal_change/design.md" "$changes_root/$proposal_change/tasks.md"
|
||||
proposal_output=$(run_verifier "$proposal_change")
|
||||
[[ "$proposal_output" == *'Planning stage verified: proposal'* ]]
|
||||
[[ "$proposal_output" == *'Full change validation pending: specs, design, tasks'* ]]
|
||||
|
||||
make_change skip-conflict
|
||||
skip_conflict=$fixture_change
|
||||
rm -rf "$changes_root/$skip_conflict/specs"
|
||||
rm -f "$changes_root/$skip_conflict/design.md" "$changes_root/$skip_conflict/tasks.md"
|
||||
printf 'skip_specs: true\n' >> "$changes_root/$skip_conflict/.openspec.yaml"
|
||||
expect_failure "$skip_conflict" 'skip_specs: true cannot be combined with declared capabilities'
|
||||
|
||||
make_change specs
|
||||
specs_change=$fixture_change
|
||||
rm -f "$changes_root/$specs_change/design.md" "$changes_root/$specs_change/tasks.md"
|
||||
specs_output=$(run_verifier "$specs_change")
|
||||
[[ "$specs_output" == *'Planning stage verified: specs'* ]]
|
||||
[[ "$specs_output" == *'Full change validation pending: design, tasks'* ]]
|
||||
|
||||
make_change capability-mismatch
|
||||
capability_mismatch=$fixture_change
|
||||
mv "$changes_root/$capability_mismatch/specs/audit-remediation-workflow" "$changes_root/$capability_mismatch/specs/not-declared"
|
||||
expect_failure "$capability_mismatch" 'delta capability is absent from Proposal: not-declared'
|
||||
|
||||
make_change design
|
||||
design_change=$fixture_change
|
||||
rm -f "$changes_root/$design_change/tasks.md"
|
||||
design_output=$(run_verifier "$design_change")
|
||||
[[ "$design_output" == *'Planning stage verified: design'* ]]
|
||||
[[ "$design_output" == *'Full change validation pending: tasks'* ]]
|
||||
|
||||
make_change required-rollback
|
||||
required_rollback=$fixture_change
|
||||
rm -f "$changes_root/$required_rollback/tasks.md"
|
||||
perl -0pi -e 's/\| Status \| `N\/A` \|/| Status | `required` |/; s/(\| Verification \|)/| Trigger | Deployment fails the projection gate. |\n| Actions | Restore the previous projection. |\n| Data safety | No application data changes. |\n$1/' "$changes_root/$required_rollback/design.md"
|
||||
required_output=$(run_verifier "$required_rollback")
|
||||
[[ "$required_output" == *'Planning stage verified: design'* ]]
|
||||
|
||||
perl -0pi -e 's/^\| Data safety \|.*\n//m' "$changes_root/$required_rollback/design.md"
|
||||
expect_failure "$required_rollback" 'Rollback Contract required status needs exactly one Data safety row'
|
||||
|
||||
make_change tasks
|
||||
tasks_change=$fixture_change
|
||||
tasks_output=$(run_verifier "$tasks_change")
|
||||
[[ "$tasks_output" == *'Planning stage verified: tasks'* ]]
|
||||
[[ "$tasks_output" == *'Delta requirement coverage verified: 9/9'* ]]
|
||||
[[ "$tasks_output" == *'Full planning structure verified; semantic approval remains human.'* ]]
|
||||
|
||||
make_change invalid-area
|
||||
invalid_area=$fixture_change
|
||||
perl -0pi -e 's/\[specs\]/[frontend]/' "$changes_root/$invalid_area/tasks.md"
|
||||
expect_failure "$invalid_area" 'task 1.1 uses frontend, but Delivery Map marks it N/A'
|
||||
|
||||
make_change unknown-reference
|
||||
unknown_reference=$fixture_change
|
||||
perl -0pi -e 's/`Planning artifact проверяется до запроса approval`/`Несуществующее требование`/' "$changes_root/$unknown_reference/tasks.md"
|
||||
expect_failure "$unknown_reference" 'unknown requirement reference'
|
||||
|
||||
make_change uncovered-requirement
|
||||
uncovered_requirement=$fixture_change
|
||||
sed -i '' '/`audit-remediation-workflow` — `Каждое audit claim получает доказательный disposition`/s/`audit-remediation-workflow` — `Каждое audit claim получает доказательный disposition`/`audit-remediation-workflow` — `Реестр remediation отделён от снимка аудита`/' "$changes_root/$uncovered_requirement/tasks.md"
|
||||
expect_failure "$uncovered_requirement" 'uncovered Requirement: audit-remediation-workflow — Каждое audit claim получает доказательный disposition'
|
||||
|
||||
make_change missing-metadata
|
||||
missing_metadata=$fixture_change
|
||||
perl -0pi -e 's/^ - \*\*Verification:\*\*.*\n//m' "$changes_root/$missing_metadata/tasks.md"
|
||||
expect_failure "$missing_metadata" 'task 1.1 must contain exactly one Verification line'
|
||||
|
||||
make_change malformed-rollback
|
||||
malformed_rollback=$fixture_change
|
||||
sed -i '' '/| Status | `N\/A` |/d' "$changes_root/$malformed_rollback/design.md"
|
||||
expect_failure "$malformed_rollback" 'Rollback Contract must contain exactly one Status row'
|
||||
|
||||
printf 'verify-openspec-planning tests passed\n'
|
||||
Reference in New Issue
Block a user