fix(kyberforge): tell an unparsable Contributing-files block from an explicit (none)

parse_contributing_files documented that callers depend on None vs [], because a
parse failure returning [] would silently disable the check. Only check 8
honoured it; checks 4/5 (skill-audit) and 3/4 (agent-audit) used a truthiness
test, so an unreadable block disabled them without a word.

Two live corpus entries were skipping this way. A sweep of all 32 sources.md
found 134 entries, exactly 2 parsing to None, both in gitea-files: one heading
carried an inline parenthetical that defeated both regexes, and one (none) was
written without its leading bullet. Also pins EMPTY_SOURCE_KEYS_RE to the two
indents parse_source_keys actually reads.

agent-audit had no INFO tier at all, so it gains one rather than reporting a
check that could not run as a FAIL.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJJrm5YmacbwMdzZpXcoti
This commit is contained in:
2026-08-31 19:46:05 +00:00
parent c232e69645
commit 00daf285ec
8 changed files with 418 additions and 32 deletions

View File

@@ -985,3 +985,132 @@ EOF
assert_output --partial "INFO"
assert_output --partial "Upstream checks skipped for 'my-source' — no repo root above the skill directory"
}
# ---------------------------------------------------------------------------
# Cycle 16 — Checks 4 and 5: None ("could not parse") is NOT [] ("explicitly
# (none)"), on the sources.md side this time
#
# Cycle 13 pinned the distinction for check 8, which reads the parser's output
# against a RESEARCH doc. Checks 4 and 5 read it against the skill's own
# sources.md and honoured neither half: a truthiness test collapsed None into
# [], so an unreadable Contributing files block disabled both checks and the
# script still exited 0 with no output — the failure mode
# parse_contributing_files' docstring names in as many words. The assertions
# below are therefore about the INFO appearing; a silent exit 0 is exactly the
# bug.
# ---------------------------------------------------------------------------
@test "INFO: an unparsable Contributing files block names the slug instead of skipping checks 4 and 5 silently" {
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.
- **Research doc:** (none)
**Contributing files:**
* references/ghost.md (asterisk bullets are not the bullet form)
- **Status:** \`extracted\`
EOF
run bash "$SCRIPT" "$skill"
assert_success
assert_output --partial "INFO"
assert_output --partial "Contributing-file checks skipped for 'my-source' — the Contributing files block could not be parsed"
}
@test "INFO: an entry with no Contributing files field at all is reported, not skipped silently" {
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.
- **Research doc:** (none)
- **Status:** \`extracted\`
EOF
run bash "$SCRIPT" "$skill"
assert_success
assert_output --partial "INFO"
assert_output --partial "Contributing-file checks skipped for 'my-source' — the Contributing files block could not be parsed"
}
@test "checks 4 and 5 skipped silently: an explicit '(none)' emits no INFO" {
local skill="$TMPDIR/my-skill"
make_skill_with_source_keys "$skill"
make_sources_md "$skill" "my-source" "(none — not used directly)"
run bash "$SCRIPT" "$skill"
assert_success
assert_output ""
}
@test "checks 4 and 5 still run: a parseable Contributing files list is not diverted to the INFO" {
local skill="$TMPDIR/my-skill"
make_skill_with_source_keys "$skill"
make_sources_md "$skill" "my-source" "references/nonexistent.md"
run bash "$SCRIPT" "$skill"
assert_failure
assert_output --partial "FAIL"
assert_output --partial "Contributing file 'references/nonexistent.md' does not exist"
refute_output --partial "could not be parsed"
}
# ---------------------------------------------------------------------------
# Cycle 17 — Check 3 (#111): 'source_keys: []' only declares anything in the
# two positions parse_source_keys() actually reads
#
# The declaration and the parse have to agree on WHERE the key lives. They did
# not: the declaration regex accepted any indent, so a `source_keys: []` buried
# under an unrelated key — a position parse_source_keys() never reads — passed
# as a house-authored declaration and silenced the INFO for a file that had
# declared nothing.
# ---------------------------------------------------------------------------
@test "INFO: 'source_keys: []' nested under an unrelated key is not a declaration" {
local skill="$TMPDIR/my-skill"
make_skill_with_source_keys "$skill"
make_sources_md "$skill"
cat > "$skill/references/extra.md" <<EOF
---
title: Extra Reference
unrelated:
nested:
source_keys: []
---
# Extra Reference
The empty list is nested where parse_source_keys never looks.
EOF
run bash "$SCRIPT" "$skill"
assert_success
assert_output --partial "INFO"
assert_output --partial "No source_keys frontmatter"
}
@test "INFO: a four-space-indented 'source_keys: []' is not a declaration either" {
local skill="$TMPDIR/my-skill"
make_skill_with_source_keys "$skill"
make_sources_md "$skill"
cat > "$skill/references/extra.md" <<EOF
---
metadata:
source_keys: []
---
# Extra Reference
Two spaces is the position parse_source_keys reads under metadata:, not four.
EOF
run bash "$SCRIPT" "$skill"
assert_success
assert_output --partial "INFO"
assert_output --partial "No source_keys frontmatter"
}