From 58434f406068887291342dece24a55b0887dd86b Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 31 May 2026 11:04:15 -0500 Subject: 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. --- scripts/tests/workflow-integrity.bats | 106 ++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 scripts/tests/workflow-integrity.bats (limited to 'scripts/tests') 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"* ]] +} -- cgit v1.2.3