aboutsummaryrefslogtreecommitdiff
path: root/scripts/tests/workflow-integrity.bats
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-31 11:04:15 -0500
committerCraig Jennings <c@cjennings.net>2026-05-31 11:04:15 -0500
commit58434f406068887291342dece24a55b0887dd86b (patch)
treed1f380a704ede831823b44ed0dbe2ae08019c9b7 /scripts/tests/workflow-integrity.bats
parent09896988c1147a196b72a9782b5cc4ba1ede27a4 (diff)
downloadrulesets-58434f406068887291342dece24a55b0887dd86b.tar.gz
rulesets-58434f406068887291342dece24a55b0887dd86b.zip
feat(scripts): add workflow-integrity checker + tests
Startup's drift check catches index-vs-directory mismatches. This goes deeper: scripts/workflow-integrity.py runs six checks over the canonical .ai/workflows/: each file is indexed-or-a-plugin-of-an-indexed-engine, each index entry resolves to a file, each .ai/scripts/ reference resolves, each plugin maps to an indexed parent, each non-plugin workflow has an orientation section, and no trigger phrase is claimed by two workflows. Exit 1 on any finding. scripts/tests/workflow-integrity.bats covers the clean canonical state plus a fixture per breakage class. make test already globs scripts/tests/*.bats, so it's wired in. I calibrated against the 38 current workflows (clean). The orientation check accepts the real heading variety (Overview / Purpose / When to Use|Run / Status) and exempts plugins.
Diffstat (limited to 'scripts/tests/workflow-integrity.bats')
-rw-r--r--scripts/tests/workflow-integrity.bats106
1 files changed, 106 insertions, 0 deletions
diff --git a/scripts/tests/workflow-integrity.bats b/scripts/tests/workflow-integrity.bats
new file mode 100644
index 0000000..9152f89
--- /dev/null
+++ b/scripts/tests/workflow-integrity.bats
@@ -0,0 +1,106 @@
+#!/usr/bin/env bats
+# Tests for scripts/workflow-integrity.py: clean on the real canonical
+# workflows, and exit 1 with the right finding for each breakage class.
+
+setup() {
+ CHECKER="$(cd "$(dirname "$BATS_TEST_FILENAME")/../.." && pwd)/scripts/workflow-integrity.py"
+ TMP="$(mktemp -d)"
+ W="$TMP/.ai/workflows"
+ S="$TMP/.ai/scripts"
+ mkdir -p "$W" "$S"
+ touch "$S/helper.sh"
+ cat > "$W/INDEX.org" <<'EOF'
+* Catalog
+- =alpha.org= — the alpha workflow.
+ - Triggers: "do alpha"
+- =engine.org= — an engine with plugins.
+ - Triggers: "run engine"
+EOF
+ cat > "$W/alpha.org" <<'EOF'
+* Overview
+Alpha workflow. Uses .ai/scripts/helper.sh.
+EOF
+ cat > "$W/engine.org" <<'EOF'
+* Overview
+The engine.
+EOF
+ cat > "$W/engine.foo.org" <<'EOF'
+* Adapter
+The foo plugin of engine.
+EOF
+}
+
+teardown() {
+ rm -rf "$TMP"
+}
+
+@test "workflow-integrity: real canonical workflows are clean" {
+ run python3 "$CHECKER"
+ [ "$status" -eq 0 ]
+ [[ "$output" == *"OK"* ]]
+}
+
+@test "workflow-integrity: a valid fixture passes" {
+ run python3 "$CHECKER" "$W"
+ [ "$status" -eq 0 ]
+}
+
+@test "workflow-integrity: an un-indexed non-plugin file is an orphan" {
+ cat > "$W/stray.org" <<'EOF'
+* Overview
+Not indexed, not a plugin.
+EOF
+ run python3 "$CHECKER" "$W"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"orphan"* ]]
+ [[ "$output" == *"stray.org"* ]]
+}
+
+@test "workflow-integrity: an index entry with no file is stale" {
+ echo '- =ghost.org= — a workflow that was deleted.' >> "$W/INDEX.org"
+ run python3 "$CHECKER" "$W"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"stale-index"* ]]
+ [[ "$output" == *"ghost.org"* ]]
+}
+
+@test "workflow-integrity: a reference to a missing script fails" {
+ cat >> "$W/alpha.org" <<'EOF'
+Also calls .ai/scripts/missing-tool.py which is gone.
+EOF
+ run python3 "$CHECKER" "$W"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"bad-ref"* ]]
+ [[ "$output" == *"missing-tool.py"* ]]
+}
+
+@test "workflow-integrity: a plugin whose engine is not indexed is flagged" {
+ cat > "$W/lonely.plugin.org" <<'EOF'
+* Adapter
+A plugin with no indexed parent engine.
+EOF
+ run python3 "$CHECKER" "$W"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"orphan-plugin"* ]]
+}
+
+@test "workflow-integrity: an indexed workflow with no orientation section is flagged" {
+ echo '- =bare.org= — a bare workflow.' >> "$W/INDEX.org"
+ cat > "$W/bare.org" <<'EOF'
+* Steps
+Jumps straight in with no orientation section.
+EOF
+ run python3 "$CHECKER" "$W"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"missing-section"* ]]
+ [[ "$output" == *"bare.org"* ]]
+}
+
+@test "workflow-integrity: a trigger phrase claimed by two workflows is flagged" {
+ # Make engine also claim alpha's trigger.
+ sed -i 's/ - Triggers: "run engine"/ - Triggers: "run engine", "do alpha"/' "$W/INDEX.org"
+ run python3 "$CHECKER" "$W"
+ [ "$status" -eq 1 ]
+ [[ "$output" == *"dup-trigger"* ]]
+ [[ "$output" == *"do alpha"* ]]
+}