fix(kyberforge): harden Research doc and Basis parsing in the validator
Review of PR 139 found list-rejection and confinement holes that let the exact malformed entries the grammar forbids pass check 7. - Reject comma, space-separated and backticked path lists, so `a/sources.md (x), b/topic.md` no longer exits 0 unchecked. - FAIL absolute paths and any path whose realpath leaves the repo, for both `Research doc:` and `Basis:`. - Anchor `(removed in <sha>)` to the end of the value with a 7-40 hex sha. The sha is format-checked only, not resolved with git cat-file. - Read `* ` bullets and `- **X**` bullets correctly under a `**Basis:**` header, and strip backticks from Basis paths. - Stop the semicolon rule firing on annotation prose, and stop `none` matching `none/foo.md`. - Update the stale field messages to the new grammar and report an empty field as empty, not missing. - Skip a removed Basis silently when there is no repo root. Adds 40 tests. Each guarded line was mutated in place and every mutant is caught. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EGHFJextYtVQseaHPDDhxB
This commit is contained in:
@@ -1948,3 +1948,278 @@ EOF
|
||||
refute_output --partial "Research doc field missing"
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
# --- #121 review round: list detection, repo confinement, parser edge cases --
|
||||
|
||||
# Helper: one-line Research doc / Basis fixtures over make_entry_skill.
|
||||
rd_fixture() { make_entry_skill "$TMPDIR/fakerepo" "$1"; }
|
||||
basis_fixture() { make_entry_skill "$TMPDIR/fakerepo" "$(printf '%s\n%s' '- **Research doc:** none' "$1")"; }
|
||||
|
||||
@test "#121 FAIL: a brace-only Research doc (no comma) names more than one path" {
|
||||
rd_fixture '- **Research doc:** docs/research/{sources}.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a bare 'a.md; b.md' Research doc names more than one path" {
|
||||
rd_fixture '- **Research doc:** docs/research/sources.md; docs/other-basis.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: an annotated first path followed by ', second-path' is a list" {
|
||||
rd_fixture '- **Research doc:** docs/research/sources.md (x), docs/research/topic.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a space-separated pair of Research docs is a list" {
|
||||
rd_fixture '- **Research doc:** docs/research/sources.md docs/other-basis.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a space-separated pair of backticked Research docs is a list" {
|
||||
rd_fixture '- **Research doc:** `docs/research/sources.md` `docs/other-basis.md`'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 pass: a single backticked Research doc path is unwrapped before resolving" {
|
||||
rd_fixture '- **Research doc:** `docs/research/sources.md`'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 pass: a ';' inside an annotation that holds a path is prose (path part only is checked)" {
|
||||
rd_fixture '- **Research doc:** docs/research/sources.md (digested; docs/other-basis.md)'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a bare 'a.md; b.md' Basis names more than one path" {
|
||||
basis_fixture '- **Basis:** docs/basis.md; docs/other-basis.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis value names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a brace Basis names more than one path" {
|
||||
basis_fixture '- **Basis:** docs/{basis,other-basis}.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis value names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a brace-only Basis names more than one path" {
|
||||
basis_fixture '- **Basis:** docs/{basis}.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis value names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a space-separated Basis pair names more than one path" {
|
||||
basis_fixture '- **Basis:** docs/basis.md docs/other-basis.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis value names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 pass: a backticked Basis path is unwrapped before resolving" {
|
||||
basis_fixture '- **Basis:** `docs/basis.md`'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 FAIL: an absolute Research doc path is outside the repo" {
|
||||
rd_fixture '- **Research doc:** /etc/passwd'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "outside the repository"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a '..' Research doc escape is outside the repo" {
|
||||
rd_fixture '- **Research doc:** ../outside/sources.md'
|
||||
mkdir -p "$TMPDIR/outside"
|
||||
printf '# R\n\n## my-source\n' > "$TMPDIR/outside/sources.md"
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "outside the repository"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: an absolute Basis path is outside the repo" {
|
||||
basis_fixture '- **Basis:** /etc/passwd'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "outside the repository"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a '..' Basis escape is outside the repo even though the file exists" {
|
||||
basis_fixture '- **Basis:** ../outside.md'
|
||||
printf 'x\n' > "$TMPDIR/outside.md"
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "outside the repository"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: '(removed in abc)' is too short a sha to skip the check" {
|
||||
basis_fixture '- **Basis:** docs/deleted-adr.md (removed in abc)'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis path 'docs/deleted-adr.md' does not exist"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: '(removed in <sha>)' followed by more text is not the annotation" {
|
||||
basis_fixture '- **Basis:** docs/deleted-adr.md (removed in 5b80f30) but really still here'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis path 'docs/deleted-adr.md' does not exist"
|
||||
}
|
||||
|
||||
@test "#121 pass: '(removed in <sha>)' Basis with no repo root is 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.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** none
|
||||
- **Basis:** docs/gone.md (removed in 5b80f30)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_success
|
||||
refute_output --partial "Basis check skipped"
|
||||
}
|
||||
|
||||
@test "#121 parity: a '- **X**' bullet under a Basis header is a value, not the next field" {
|
||||
make_entry_skill "$TMPDIR/fakerepo" "$(printf '%s\n%s\n%s' '- **Research doc:** none' '**Basis:**' '- **docs/gone.md**')"
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "does not exist"
|
||||
refute_output --partial "Basis missing"
|
||||
}
|
||||
|
||||
@test "#121 parity: '* ' bullets under a Basis header are read" {
|
||||
make_entry_skill "$TMPDIR/fakerepo" "$(printf '%s\n%s\n%s\n%s' '- **Research doc:** none' '**Basis:**' '* docs/basis.md' '* docs/gone.md')"
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis path 'docs/gone.md' does not exist"
|
||||
}
|
||||
|
||||
@test "#121 'none/foo.md' is a path, not a 'none' declaration" {
|
||||
rd_fixture '- **Research doc:** none/foo.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
refute_output --partial "Basis missing"
|
||||
}
|
||||
|
||||
@test "#121 'none-of-these.md' is a path, not a 'none' declaration" {
|
||||
rd_fixture '- **Research doc:** none-of-these.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
refute_output --partial "Basis missing"
|
||||
}
|
||||
|
||||
@test "#121 pass: 'None' and 'NONE' are recognised case-insensitively" {
|
||||
local v
|
||||
for v in None NONE; do
|
||||
make_entry_skill "$TMPDIR/fakerepo" "$(printf '%s\n%s' "- **Research doc:** $v" '- **Basis:** docs/basis.md')"
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
done
|
||||
}
|
||||
|
||||
@test "#121 FAIL: an empty Basis value is empty, not missing" {
|
||||
basis_fixture '- **Basis:**'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis is empty or placeholder"
|
||||
refute_output --partial "Basis missing"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a 'FILL IN:' Basis is a placeholder" {
|
||||
basis_fixture '- **Basis:** FILL IN: repo path'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis is empty or placeholder"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: an empty inline Research doc says empty, not missing" {
|
||||
rd_fixture '- **Research doc:**'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc field is empty or placeholder"
|
||||
refute_output --partial "Research doc field missing"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a missing Research doc advises the new grammar, not '<path-or-(none)>'" {
|
||||
rd_fixture ''
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc field missing"
|
||||
refute_output --partial "path-or-(none)"
|
||||
assert_output --partial "Basis"
|
||||
}
|
||||
|
||||
@test "#121 parity: an inline Basis with no leading hyphen is read" {
|
||||
basis_fixture '**Basis:** docs/gone.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis path 'docs/gone.md' does not exist"
|
||||
refute_output --partial "Basis missing"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: 'a.md;b.md' with no space is a list, for Research doc" {
|
||||
rd_fixture '- **Research doc:** docs/research/sources.md;docs/basis.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: 'a.md;b.md' with no space is a list, for Basis" {
|
||||
basis_fixture '- **Basis:** docs/basis.md;docs/other-basis.md'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis value names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: an absolute Basis path is outside the repo even when it points inside the checkout" {
|
||||
make_entry_skill "$TMPDIR/fakerepo" "$(printf '%s\n%s' '- **Research doc:** none' "- **Basis:** $TMPDIR/fakerepo/docs/basis.md")"
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "outside the repository"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: an absolute Research doc path is outside the repo even when it points inside the checkout" {
|
||||
rd_fixture "- **Research doc:** $TMPDIR/fakerepo/docs/research/sources.md"
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "outside the repository"
|
||||
}
|
||||
|
||||
@test "#121 parity: a '* **Basis:**' bullet spelling is read" {
|
||||
make_entry_skill "$TMPDIR/fakerepo" "$(printf '%s\n%s' '- **Research doc:** none' '* **Basis:** docs/gone.md')"
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis path 'docs/gone.md' does not exist"
|
||||
}
|
||||
|
||||
@test "#121 pass: a comma inside a section-marker annotation is prose, not a list" {
|
||||
rd_fixture '- **Research doc:** docs/research/sources.md § "Foo, bar and baz"'
|
||||
run bash "$SCRIPT" "$TMPDIR/fakerepo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user