#!/usr/bin/env bats setup() { REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/../../../../../../" && pwd)" load "$REPO_ROOT/tests/test_helper/bats-support/load" load "$REPO_ROOT/tests/test_helper/bats-assert/load" SCRIPT="$(cd "$BATS_TEST_DIRNAME/../scripts" && pwd)/validate-provenance.sh" TMPDIR="$(mktemp -d)" # Helper: create a minimal skill directory with no sources.md and no source_keys make_clean_skill() { local dir="$1" local name name="$(basename "$dir")" mkdir -p "$dir" cat > "$dir/SKILL.md" < "$dir/SKILL.md" < "$dir/references/sources.md" </dev/null 2>&1 git -C "$dir" -c user.email=test@example.com -c user.name=test add -A >/dev/null 2>&1 git -C "$dir" -c user.email=test@example.com -c user.name=test commit -q -m base >/dev/null 2>&1 git -C "$dir" update-ref refs/remotes/origin/main HEAD >/dev/null 2>&1 } # Helper: create a fake repo (a real git repo, one commit, makes # find_repo_root stop there) holding one skill whose single sources.md # slug points at the given Research doc value. Checks 7 and 8 only run # for a skill inside a checkout, so every upstream case needs this shape; # the research doc itself is written per test into # "$repo/docs/research/sources.md" — which check 9 does not examine, so # a test overwriting it after this helper runs does not disturb check 9. make_upstream_skill() { local repo="$1" local research="${2:-docs/research/sources.md}" local skill="$repo/my-skill" mkdir -p "$skill/references" "$repo/docs/research" cat > "$skill/SKILL.md" < "$skill/references/sources.md" < "$skill/references/sources.md" <> "$skill/references/sources.md" run bash "$SCRIPT" "$skill" assert_success } # --------------------------------------------------------------------------- # Cycle 5 — Check 2: source_keys slug missing from sources.md → FAIL # --------------------------------------------------------------------------- @test "FAIL: source_keys slug in SKILL.md not present as H2 in sources.md" { local skill="$TMPDIR/my-skill" make_skill_with_source_keys "$skill" mkdir -p "$skill/references" cat > "$skill/references/sources.md" < "$skill/references/sources.md" < "$skill/references/sources.md" < "$skill/SKILL.md" < "$skill/references/sources.md" <> "$skill/references/sources.md" < "$skill/references/extra.md" < "$skill/references/extra.md" < "$research_dir/sources.md" < "$skill2/SKILL.md" < "$fake_repo/docs/research/sources.md" < "$skill2/references/sources.md" < "$skill/SKILL.md" < "$fake_repo/docs/research/sources.md" < "$skill/references/sources.md" < "$skill/SKILL.md" < "$fake_repo/docs/research/sources.md" < "$skill/references/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$skill/references/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$skill/references/extra.md" < "$skill/references/extra.md" < "$skill/references/extra.md" < "$skill/references/extra.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$skill/references/sources.md" < "$skill/references/sources.md" < "$skill/references/extra.md" < "$skill/references/extra.md" < "$fake_repo/docs/research/remotes.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$fake_repo/docs/research/sources.md" < "$skill/SKILL.md" cat >> "$skill/SKILL.md" < "$skill/references/sources.md" < "$skill/references/topic.md" < "$skill/references/topic.md" run bash "$SCRIPT" "$skill" assert_failure assert_output --partial "not valid UTF-8" assert_output --partial "references/topic.md" } # --------------------------------------------------------------------------- # Cycle 24 — G4: check 3 walks references/ recursively # --------------------------------------------------------------------------- @test "G4: a source_keys file in references// is validated, not skipped" { local skill="$TMPDIR/my-skill" make_skill_with_source_keys "$skill" make_sources_md "$skill" mkdir -p "$skill/references/nested" cat > "$skill/references/nested/topic.md" < "$skill/references/sources.md" < "$skill/references/sources.md" < "$skill/references/sources.md" < "$skill/references/topic.md" run bash "$SCRIPT" "$skill" assert_failure assert_output --partial "not valid UTF-8" } # --------------------------------------------------------------------------- # Cycle 28 — Check 9: a changed Description or Contributing files claim is an # INFO, never a FAIL — the script can tell the text changed, not whether the # (possibly stronger) new wording is still true. # --------------------------------------------------------------------------- @test "check 9: Description text changed since base ref fires an INFO naming the slug and field" { local skill="$TMPDIR/my-skill" make_skill_with_source_keys "$skill" make_sources_md "$skill" commit_as_base "$skill" # Rewritten in the working tree only, never committed — exactly the # shape of the bug check 9 exists to flag: a hedge upgraded to a # confident claim with nothing else in the entry touched. sed -i 's/^- \*\*Description:\*\* A test source\.$/- **Description:** A test source that Grounds the dispatch table directly./' \ "$skill/references/sources.md" run bash "$SCRIPT" "$skill" assert_success assert_output --partial "INFO" assert_output --partial "'Description' changed for 'my-source'" } @test "check 9: Contributing files text changed since base ref fires an INFO naming the slug and field" { local skill="$TMPDIR/my-skill" make_skill_with_source_keys "$skill" make_sources_md "$skill" commit_as_base "$skill" # Only an annotation is added. strip_note() in parse_contributing_files() # removes it before checks 4 and 5 compare paths, so those stay clean — # check 9 compares the raw field text, not the parsed path list, and # this is still a real wording change worth a human re-reading it. sed -i 's/^- \*\*Contributing files:\*\* SKILL\.md$/- **Contributing files:** SKILL.md (the dispatch table)/' \ "$skill/references/sources.md" run bash "$SCRIPT" "$skill" assert_success assert_output --partial "INFO" assert_output --partial "'Contributing files' changed for 'my-source'" refute_output --partial "does not exist" refute_output --partial "does not list" } @test "check 9: an entry unchanged since base ref produces no check-9 finding" { local skill="$TMPDIR/my-skill" make_skill_with_source_keys "$skill" make_sources_md "$skill" commit_as_base "$skill" run bash "$SCRIPT" "$skill" assert_success assert_output "" } @test "check 9: a brand-new entry absent at the base ref is a creation, not a change" { local skill="$TMPDIR/my-skill" make_skill_with_source_keys "$skill" make_sources_md "$skill" commit_as_base "$skill" # Added in the working tree only, after the commit above. It has no # earlier revision to diff against, so check 9 must stay silent about # it — a brand-new entry is a creation, not a rewrite of an existing # claim, and flagging it would be exactly the false-positive shape the # rejected literal-text approaches produced. cat >> "$skill/references/sources.md" </dev/null 2>&1 run bash "$SCRIPT" "$skill" assert_success assert_output --partial "INFO" assert_output --partial "Check 9 skipped — no base ref could be resolved" } @test "check 9: --base-ref overrides the default origin/main resolution" { local skill="$TMPDIR/my-skill" make_skill_with_source_keys "$skill" make_sources_md "$skill" commit_as_base "$skill" local base_sha base_sha="$(git -C "$skill" rev-parse HEAD)" git -C "$skill" update-ref -d refs/remotes/origin/main >/dev/null 2>&1 sed -i 's/^- \*\*Description:\*\* A test source\.$/- **Description:** A rewritten claim./' \ "$skill/references/sources.md" run bash "$SCRIPT" "$skill" "--base-ref=$base_sha" assert_success assert_output --partial "'Description' changed for 'my-source'" } @test "check 9: an invalid --base-ref value is reported as unresolvable, not a crash" { local skill="$TMPDIR/my-skill" make_skill_with_source_keys "$skill" make_sources_md "$skill" commit_as_base "$skill" run bash "$SCRIPT" "$skill" "--base-ref=not-a-real-ref" assert_success assert_output --partial "Check 9 skipped — no base ref could be resolved" assert_output --partial "not-a-real-ref" }