fix(kyberforge): announce every provenance skip and scope checks 7-8 to source indexes
The clean provenance bill was an artifact. Checks 7 and 8 assume `Research doc:` names a source index whose H2s are slugs, but 30 of 121 corpus entries point at topic content documents whose H2s are topics. Those 30 produced every new check-7 INFO — all false positives. Check 8 aimed at the same documents, which carry no `Status:` line at all, would have emitted a large false-FAIL flood; the only thing preventing it was an unannounced `rd_status != extracted` skip. So "0 new FAILs" rested on exactly the fail-open class this branch exists to remove, and naively fixing the skip would have turned the branch red. Checks 7/8 now run only when the research doc's basename is `sources.md`, and every other case emits a visible INFO naming the slug. The dangling-path INFO stays ahead of the basename gate, because a path that does not resolve is rot whatever it is named. `parse_status` accepts the bullet form and a trailing note after the backticked value, so a status it cannot read no longer reads as "nothing to check". Corpus: 36 INFOs of which 30 were false, to 56 of which none are. FAIL stays 0, and no Status line flipped to `extracted` under the new parser, so no FAIL was suppressed by luck. Also closed, each a silent pass: a nonexistent directory, a directory with no SKILL.md, and extra arguments now exit 2; a UTF-8 BOM no longer defeats frontmatter parsing; the bare `except Exception: return False` that turned an unreadable file into a clean pass is gone, with all reads pinned to UTF-8; check 3 walks nested `references/` subdirectories; `FILL IN:` at end of line no longer escapes checks 1 and 6; duplicate `## slug` blocks and repeated `Research doc:` lines are announced rather than half-read. The agent-audit copy carried all of the above unfixed and is now ported, minus the four fixes that are genuinely N/A at agent scope — it reads a plugin-root `sources.md` and has no checks 7/8 and no `references/` tree. Its silent exit 0 for a file outside plugin scope is preserved deliberately: that is a verdict about a valid file, not a skip, and `check-scope-walkup-sync.sh` pins it. Every exit-2 gate therefore decides from the argument alone, before the walk-up runs. `validation-scripts.md` said flatly that silence from the validator is a pass, not a skip. That sentence is what made a typo'd path dangerous, and both copies are corrected here. The matching SKILL.md exit-code guidance lands with the audit rubric change, which touches the same files. Tests: skill-audit 45 to 65, agent-audit 24 to 43, every new case proven by mutation. Refs: #111, #118, #121
This commit is contained in:
@@ -71,10 +71,10 @@ EOF
|
||||
# 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/my-research.md".
|
||||
# written per test into "$repo/docs/research/sources.md".
|
||||
make_upstream_skill() {
|
||||
local repo="$1"
|
||||
local research="${2:-docs/research/my-research.md}"
|
||||
local research="${2:-docs/research/sources.md}"
|
||||
local skill="$repo/my-skill"
|
||||
mkdir -p "$skill/references" "$repo/docs/research"
|
||||
touch "$repo/.git"
|
||||
@@ -375,7 +375,7 @@ EOF
|
||||
# Create a research doc that does NOT have the slug
|
||||
local research_dir="$TMPDIR/research"
|
||||
mkdir -p "$research_dir"
|
||||
cat > "$research_dir/my-research.md" <<EOF
|
||||
cat > "$research_dir/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## different-slug
|
||||
@@ -410,7 +410,7 @@ EOF
|
||||
|
||||
mkdir -p "$skill2/references"
|
||||
mkdir -p "$fake_repo/docs/research"
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## different-slug
|
||||
@@ -427,7 +427,7 @@ EOF
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** docs/research/my-research.md
|
||||
- **Research doc:** docs/research/sources.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
@@ -465,7 +465,7 @@ EOF
|
||||
mkdir -p "$fake_repo/docs/research"
|
||||
|
||||
# Research doc has my-source (extracted, with a contributing file) AND extra-source (also extracted)
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -487,7 +487,7 @@ EOF
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** docs/research/my-research.md
|
||||
- **Research doc:** docs/research/sources.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
@@ -520,7 +520,7 @@ EOF
|
||||
mkdir -p "$skill/references"
|
||||
mkdir -p "$fake_repo/docs/research"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -542,7 +542,7 @@ EOF
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** docs/research/my-research.md
|
||||
- **Research doc:** docs/research/sources.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
@@ -566,7 +566,7 @@ EOF
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -592,7 +592,7 @@ EOF
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -618,7 +618,7 @@ EOF
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -644,7 +644,7 @@ EOF
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -670,7 +670,7 @@ EOF
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -727,7 +727,7 @@ EOF
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -751,7 +751,7 @@ EOF
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -864,9 +864,9 @@ EOF
|
||||
|
||||
@test "check 7 runs: '§' section annotation is stripped before the path is resolved" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo" 'docs/research/my-research.md § "Some Section"'
|
||||
make_upstream_skill "$fake_repo" 'docs/research/sources.md § "Some Section"'
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## different-slug
|
||||
@@ -877,15 +877,15 @@ EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/my-research.md'"
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
refute_output --partial "§"
|
||||
}
|
||||
|
||||
@test "check 7 runs: '→' section annotation is stripped before the path is resolved" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo" 'docs/research/my-research.md → `## Pushing`'
|
||||
make_upstream_skill "$fake_repo" 'docs/research/sources.md → `## Pushing`'
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## different-slug
|
||||
@@ -896,15 +896,15 @@ EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/my-research.md'"
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
refute_output --partial "→"
|
||||
}
|
||||
|
||||
@test "check 7 runs: parenthetical annotation is stripped before the path is resolved" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo" "docs/research/my-research.md (whole-document reference)"
|
||||
make_upstream_skill "$fake_repo" "docs/research/sources.md (whole-document reference)"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## different-slug
|
||||
@@ -915,15 +915,15 @@ EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/my-research.md'"
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
refute_output --partial "whole-document reference"
|
||||
}
|
||||
|
||||
@test "check 7 runs: a bare path with no annotation survives the strip intact" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo" "docs/research/my-research.md"
|
||||
make_upstream_skill "$fake_repo" "docs/research/sources.md"
|
||||
|
||||
cat > "$fake_repo/docs/research/my-research.md" <<EOF
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
@@ -978,7 +978,7 @@ EOF
|
||||
@test "INFO: no repo root above the skill directory names the slug instead of skipping silently" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
make_sources_md "$skill" "my-source" "SKILL.md" "docs/research/my-research.md"
|
||||
make_sources_md "$skill" "my-source" "SKILL.md" "docs/research/sources.md"
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_success
|
||||
@@ -1114,3 +1114,383 @@ EOF
|
||||
assert_output --partial "INFO"
|
||||
assert_output --partial "No source_keys frontmatter"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 20 — F: checks 7 and 8 apply only to a research SOURCE INDEX, and
|
||||
# every skip announces itself
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "F: a topic-doc Research doc is reported as not applicable, not as a missing slug" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo" "docs/research/remotes.md"
|
||||
cat > "$fake_repo/docs/research/remotes.md" <<EOF
|
||||
# Remotes
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
Prose about remotes.
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_output --partial "Upstream checks not applicable for 'my-source'"
|
||||
assert_output --partial "is a topic document, not a source index"
|
||||
refute_output --partial "not found as H2 in research doc"
|
||||
}
|
||||
|
||||
@test "F: check 7 still runs when the Research doc IS a source index" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## different-slug
|
||||
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
refute_output --partial "not applicable"
|
||||
}
|
||||
|
||||
@test "F: check 8 announces the slug it skipped for a non-extracted Status" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Contributing files:** some-skill/references/extra.md
|
||||
- **Status:** \`referenced\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_output --partial "Check 8 skipped for research-doc slug 'extra-source'"
|
||||
assert_output --partial "its Status is \`referenced\`, not \`extracted\`"
|
||||
}
|
||||
|
||||
@test "F: a Status with a trailing note after the backticked value still reads as extracted" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Contributing files:** some-skill/references/extra.md
|
||||
- **Status:** \`extracted\` — partial fetch, section 3 only
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc slug 'extra-source' missing from skill sources.md"
|
||||
}
|
||||
|
||||
@test "F: a bullet-form Status still reads as extracted" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Contributing files:** some-skill/references/extra.md
|
||||
|
||||
**Status:**
|
||||
- \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc slug 'extra-source' missing from skill sources.md"
|
||||
}
|
||||
|
||||
@test "F: a research-doc slug with no Status line at all is announced, not skipped silently" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Contributing files:** some-skill/references/extra.md
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_output --partial "Check 8 skipped for research-doc slug 'extra-source'"
|
||||
assert_output --partial "its Status is absent, not \`extracted\`"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 21 — G1: a bad target is a hard error, not a silent pass
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "G1: a nonexistent directory is a hard error (exit 2), not a silent exit 0" {
|
||||
run bash "$SCRIPT" "$TMPDIR/does-not-exist"
|
||||
[ "$status" -eq 2 ]
|
||||
assert_output --partial "not a directory"
|
||||
}
|
||||
|
||||
@test "G1: a directory with no SKILL.md is a hard error (exit 2), not a silent exit 0" {
|
||||
mkdir -p "$TMPDIR/not-a-skill/references"
|
||||
run bash "$SCRIPT" "$TMPDIR/not-a-skill"
|
||||
[ "$status" -eq 2 ]
|
||||
assert_output --partial "not a skill directory"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 22 — G2: a UTF-8 BOM does not disable check 2
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "G2: a BOM at the head of SKILL.md does not silently disable check 2" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
mkdir -p "$skill/references"
|
||||
printf '\xef\xbb\xbf' > "$skill/SKILL.md"
|
||||
cat >> "$skill/SKILL.md" <<EOF
|
||||
---
|
||||
name: my-skill
|
||||
description: A valid skill description.
|
||||
metadata:
|
||||
source_keys:
|
||||
- my-source
|
||||
---
|
||||
|
||||
## Step 1
|
||||
|
||||
Do the thing.
|
||||
EOF
|
||||
cat > "$skill/references/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## different-source
|
||||
|
||||
- **URL:** https://example.com/different-source
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** references/sources.md
|
||||
- **Research doc:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_failure
|
||||
assert_output --partial "source_keys slug 'my-source' not found in sources.md"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 23 — G3: reads are UTF-8 and an unreadable file is a finding
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "G3: an em dash under LC_ALL=C is read, not turned into a traceback or a silent pass" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
make_sources_md "$skill"
|
||||
cat > "$skill/references/topic.md" <<EOF
|
||||
---
|
||||
source_keys:
|
||||
- ghost-source
|
||||
---
|
||||
|
||||
Prose — with an em dash.
|
||||
EOF
|
||||
|
||||
LC_ALL=C PYTHONUTF8=0 PYTHONCOERCECLOCALE=0 run bash "$SCRIPT" "$skill"
|
||||
assert_failure
|
||||
assert_output --partial "source_keys slug 'ghost-source' not found in sources.md"
|
||||
refute_output --partial "Traceback"
|
||||
}
|
||||
|
||||
@test "G3: a file that is genuinely not UTF-8 is a FAIL, not a clean pass" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
make_sources_md "$skill"
|
||||
printf -- '---\nsource_keys:\n - my-source\n---\n\nLatin-1 byte: \xe9\n' \
|
||||
> "$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/<subdir>/ 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" <<EOF
|
||||
---
|
||||
source_keys:
|
||||
- ghost-source
|
||||
---
|
||||
|
||||
Nested prose.
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_failure
|
||||
assert_output --partial "source_keys slug 'ghost-source' not found in sources.md"
|
||||
assert_output --partial "references/nested/topic.md"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 25 — G5: a placeholder at end of line is still a placeholder
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "G5: 'FILL IN:' at end of line is caught by check 1" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
mkdir -p "$skill/references"
|
||||
cat > "$skill/references/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** FILL IN:
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_failure
|
||||
assert_output --partial "Unfilled FILL IN: placeholder"
|
||||
}
|
||||
|
||||
@test "G5: a Research doc value that is a bare 'FILL IN:' is caught by check 6" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
make_sources_md "$skill" "my-source" "SKILL.md" "FILL IN:"
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc field is empty or placeholder"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 26 — G6/G7: duplicated entries and duplicated fields are announced
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "G6: a duplicate '## slug' block is announced, not half-checked in silence" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
mkdir -p "$skill/references"
|
||||
cat > "$skill/references/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## my-source
|
||||
|
||||
- **URL:** https://example.com/my-source-again
|
||||
- **Description:** The same slug a second time.
|
||||
- **Contributing files:** references/nonexistent.md
|
||||
- **Research doc:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_success
|
||||
assert_output --partial "Duplicate '## my-source' entry in sources.md"
|
||||
assert_output --partial "only the first block is checked"
|
||||
}
|
||||
|
||||
@test "G7: a second '- **Research doc:**' line in one entry is announced" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
mkdir -p "$skill/references"
|
||||
cat > "$skill/references/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Research doc:** docs/research/sources.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_success
|
||||
assert_output --partial "Multiple '- **Research doc:**' lines for 'my-source'"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 27 — G8: usage and environment errors exit 2, never 1
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "G8: a missing argument exits 2, not 1" {
|
||||
run bash "$SCRIPT"
|
||||
[ "$status" -eq 2 ]
|
||||
assert_output --partial "skill-dir is required"
|
||||
}
|
||||
|
||||
@test "G8: an extra positional argument is rejected, not silently ignored" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_clean_skill "$skill"
|
||||
run bash "$SCRIPT" "$skill" extra
|
||||
[ "$status" -eq 2 ]
|
||||
assert_output --partial "expected exactly one argument"
|
||||
}
|
||||
|
||||
@test "G8: a missing python3 is reported by name and exits 2, not 127" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_clean_skill "$skill"
|
||||
local stub="$TMPDIR/emptybin"
|
||||
mkdir -p "$stub"
|
||||
for cmd in bash cat sed; do
|
||||
ln -s "$(command -v "$cmd")" "$stub/$cmd"
|
||||
done
|
||||
PATH="$stub" run bash "$SCRIPT" "$skill"
|
||||
[ "$status" -eq 2 ]
|
||||
assert_output --partial "python3 is required"
|
||||
}
|
||||
|
||||
@test "G3: an unreadable file is reported even when there is nothing else to validate" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_clean_skill "$skill"
|
||||
mkdir -p "$skill/references"
|
||||
printf -- '---\nsource_keys:\n - my-source\n---\n\nLatin-1 byte: \xe9\n' \
|
||||
> "$skill/references/topic.md"
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_failure
|
||||
assert_output --partial "not valid UTF-8"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user