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:
2026-09-21 19:40:28 +00:00
parent 2bde9a6a82
commit 2c4b6d2615
2 changed files with 359 additions and 19 deletions

View File

@@ -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 ""
}