feat(kyberforge): make Research doc name one Research registry
validate-provenance.sh assumed `Research doc:` names a research sources.md whose H2 headings are the source slugs, but 29 corpus entries named topic docs and 6 values were not a single path, so checks 7 and 8 reported INFO for 36 entries and nothing ever failed. `Research doc:` now takes exactly one path. An entry with no registry writes `none` plus one `- **Basis:** <path>` bullet per path; each Basis path is existence-checked unless annotated `(removed in <sha>)`. - Check 7 FAILs when a resolved registry lacks the slug, when the value is a topic doc, or when it is a list. An unresolvable path stays INFO. - Check 8 is retired: one registry serves many skills, so requiring every registry slug in each skill's sources.md is unsatisfiable. - The Research doc and Basis parsers accept the inline, bullet and header-plus-bullets spellings, so a differently spelled field is no longer read as absent. Refs: #121 ADR: 0028 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EGHFJextYtVQseaHPDDhxB
This commit is contained in:
@@ -53,6 +53,10 @@ EOF
|
||||
local slug="${2:-my-source}"
|
||||
local contrib="${3:-SKILL.md}"
|
||||
local research="${4:-(none)}"
|
||||
# A 'none' Research doc must name its Basis (#121); SKILL.md exists in
|
||||
# every fixture skill and, once commit_as_base has run, is a repo path.
|
||||
local basis_line=""
|
||||
if [[ "$research" == *none* ]]; then basis_line="- **Basis:** SKILL.md"; fi
|
||||
mkdir -p "$dir/references"
|
||||
cat > "$dir/references/sources.md" <<EOF
|
||||
# Sources
|
||||
@@ -63,6 +67,7 @@ EOF
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** ${contrib}
|
||||
- **Research doc:** ${research}
|
||||
${basis_line}
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
}
|
||||
@@ -82,6 +87,7 @@ EOF
|
||||
no forge-specific content drawn directly from it beyond that.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** `extracted`
|
||||
EOF
|
||||
}
|
||||
@@ -106,7 +112,7 @@ EOF
|
||||
|
||||
# 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
|
||||
# slug points at the given Research doc value. Check 7 only runs
|
||||
# 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
|
||||
@@ -114,6 +120,8 @@ EOF
|
||||
make_upstream_skill() {
|
||||
local repo="$1"
|
||||
local research="${2:-docs/research/sources.md}"
|
||||
local basis_line=""
|
||||
if [[ "$research" == *none* ]]; then basis_line="- **Basis:** my-skill/SKILL.md"; fi
|
||||
local skill="$repo/my-skill"
|
||||
mkdir -p "$skill/references" "$repo/docs/research"
|
||||
cat > "$skill/SKILL.md" <<EOF
|
||||
@@ -138,6 +146,7 @@ EOF
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** ${research}
|
||||
${basis_line}
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
commit_as_base "$repo"
|
||||
@@ -199,6 +208,7 @@ teardown() {
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$skill"
|
||||
@@ -232,6 +242,7 @@ EOF
|
||||
- **Description:** A different source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$skill"
|
||||
@@ -335,6 +346,7 @@ EOF
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
# Now add my-source that references SKILL.md but SKILL.md doesn't back-reference it
|
||||
@@ -346,6 +358,7 @@ EOF
|
||||
- **Description:** Another source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$skill"
|
||||
@@ -407,7 +420,7 @@ EOF
|
||||
# Cycle 11 — Check 7: Upstream forward: slug in sources.md not in research doc → INFO
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "INFO: slug in sources.md not found in research doc → INFO, exits 0" {
|
||||
@test "FAIL: slug in sources.md not found in the research registry → FAIL (was INFO before #121)" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
|
||||
@@ -471,262 +484,9 @@ EOF
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill2"
|
||||
assert_success
|
||||
assert_output --partial "INFO"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 12 — Check 8: Upstream reverse: extracted slug in research doc not in sources.md → FAIL
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "FAIL: extracted non-(none) slug in research doc missing from skill sources.md" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
mkdir -p "$fake_repo"
|
||||
touch "$fake_repo/.git"
|
||||
|
||||
local skill="$fake_repo/my-skill"
|
||||
mkdir -p "$skill"
|
||||
cat > "$skill/SKILL.md" <<EOF
|
||||
---
|
||||
name: my-skill
|
||||
description: A valid skill description.
|
||||
metadata:
|
||||
source_keys:
|
||||
- my-source
|
||||
---
|
||||
|
||||
## Step 1
|
||||
|
||||
Do the thing.
|
||||
EOF
|
||||
|
||||
mkdir -p "$skill/references"
|
||||
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/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Contributing files:** some-skill/references/extra.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
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:** docs/research/sources.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_failure
|
||||
assert_output --partial "FAIL"
|
||||
}
|
||||
|
||||
@test "pass: extracted slug in research doc with (none) contributing files is not required in sources.md" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
mkdir -p "$fake_repo"
|
||||
touch "$fake_repo/.git"
|
||||
|
||||
local skill="$fake_repo/my-skill"
|
||||
mkdir -p "$skill"
|
||||
cat > "$skill/SKILL.md" <<EOF
|
||||
---
|
||||
name: my-skill
|
||||
description: A valid skill description.
|
||||
metadata:
|
||||
source_keys:
|
||||
- my-source
|
||||
---
|
||||
|
||||
## Step 1
|
||||
|
||||
Do the thing.
|
||||
EOF
|
||||
|
||||
mkdir -p "$skill/references"
|
||||
mkdir -p "$fake_repo/docs/research"
|
||||
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Contributing files:** (none — not relevant)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
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:** docs/research/sources.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 13 — parse_contributing_files: None ("could not parse") is NOT []
|
||||
# ("explicitly (none)")
|
||||
#
|
||||
# Check 8 reads [] as "the research doc deliberately records no contributing
|
||||
# files" and SKIPS the slug on that basis. A block the parser cannot read must
|
||||
# therefore return None, so the slug stays exposed to check 8. Each case below
|
||||
# asserts that CONSEQUENCE — check 8 firing on the unreadable entry — not the
|
||||
# parser's return value, because returning [] is exactly the shape that makes
|
||||
# the check silently do nothing while still exiting 0.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "check 8 runs: bullet form with '*' asterisk bullets is unparsable, not '(none)'" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## 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 "FAIL"
|
||||
assert_output --partial "extra-source"
|
||||
}
|
||||
|
||||
@test "check 8 runs: bullet form with a numbered list is unparsable, not '(none)'" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
**Contributing files:**
|
||||
1. some-skill/references/extra.md
|
||||
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "FAIL"
|
||||
assert_output --partial "extra-source"
|
||||
}
|
||||
|
||||
@test "check 8 runs: bullet form followed by prose is unparsable, not '(none)'" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
**Contributing files:**
|
||||
See the table below for the file list.
|
||||
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "FAIL"
|
||||
assert_output --partial "extra-source"
|
||||
}
|
||||
|
||||
@test "check 8 runs: bullet form heading with a blank line and nothing after is unparsable, not '(none)'" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
**Contributing files:**
|
||||
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "FAIL"
|
||||
assert_output --partial "extra-source"
|
||||
}
|
||||
|
||||
@test "check 8 runs: inline form whose whole value is a parenthetical is unparsable, not '(none)'" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Contributing files:** (see notes below)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "FAIL"
|
||||
assert_output --partial "extra-source"
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
}
|
||||
|
||||
@test "check 4 runs: bullet form with '-' hyphen bullets still parses each path" {
|
||||
@@ -746,6 +506,7 @@ EOF
|
||||
- references/nonexistent.md (why this one matters)
|
||||
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$skill"
|
||||
@@ -762,52 +523,6 @@ EOF
|
||||
assert_output --partial "Contributing file 'references/nonexistent.md' does not exist"
|
||||
}
|
||||
|
||||
@test "check 8 skips: bullet form '- (none)' is an explicit declaration" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
**Contributing files:**
|
||||
- (none — nothing was extracted from this section)
|
||||
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
}
|
||||
|
||||
@test "check 8 skips: inline bare '(none)' is an explicit declaration" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo"
|
||||
|
||||
cat > "$fake_repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** some-skill/SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## extra-source
|
||||
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 14 — Check 3 (#111): an explicit 'source_keys: []' is a house-authored
|
||||
# declaration, a bare 'source_keys:' is truncation
|
||||
@@ -894,7 +609,7 @@ EOF
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 15 — Checks 7 and 8: Research doc annotation stripping, and the INFO
|
||||
# Cycle 15 — Check 7: Research doc annotation stripping, and the INFO
|
||||
# that replaced the silent skip
|
||||
#
|
||||
# A Research doc value is very often a path PLUS a section annotation, and
|
||||
@@ -917,7 +632,7 @@ EOF
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_failure
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
refute_output --partial "§"
|
||||
}
|
||||
@@ -936,7 +651,7 @@ EOF
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_failure
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
refute_output --partial "→"
|
||||
}
|
||||
@@ -955,7 +670,7 @@ EOF
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_failure
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
refute_output --partial "whole-document reference"
|
||||
}
|
||||
@@ -978,7 +693,7 @@ EOF
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "checks 7 and 8 skipped silently: Research doc '(none)' is recognised before the strip" {
|
||||
@test "check 7 skipped silently: Research doc '(none)' is recognised before the strip" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo" "(none)"
|
||||
|
||||
@@ -987,7 +702,7 @@ EOF
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "checks 7 and 8 skipped silently: bare 'none — reason' is recognised as a declaration" {
|
||||
@test "check 7 skipped silently: bare 'none — reason' is recognised as a declaration" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo" "none — org convention, no upstream research doc"
|
||||
|
||||
@@ -1031,7 +746,7 @@ EOF
|
||||
# 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
|
||||
# Cycle 13 pinned the distinction for check 8 (since retired), which read 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
|
||||
@@ -1053,6 +768,7 @@ EOF
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
**Contributing files:**
|
||||
* references/ghost.md (asterisk bullets are not the bullet form)
|
||||
- **Status:** \`extracted\`
|
||||
@@ -1075,6 +791,7 @@ EOF
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$skill"
|
||||
@@ -1158,11 +875,11 @@ EOF
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 20 — F: checks 7 and 8 apply only to a research SOURCE INDEX, and
|
||||
# Cycle 20 — F: check 7 applies only to a Research registry, and
|
||||
# every skip announces itself
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "F: a topic-doc Research doc is reported as not applicable, not as a missing slug" {
|
||||
@test "F: a topic-doc Research doc is a FAIL naming the registry, not a missing slug (#121)" {
|
||||
local fake_repo="$TMPDIR/fakerepo"
|
||||
make_upstream_skill "$fake_repo" "docs/research/remotes.md"
|
||||
cat > "$fake_repo/docs/research/remotes.md" <<EOF
|
||||
@@ -1174,9 +891,8 @@ 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"
|
||||
assert_failure
|
||||
assert_output --partial "is a topic document, not a Research registry"
|
||||
refute_output --partial "not found as H2 in research doc"
|
||||
}
|
||||
|
||||
@@ -1193,102 +909,11 @@ EOF
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$fake_repo/my-skill"
|
||||
assert_success
|
||||
assert_failure
|
||||
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
|
||||
#
|
||||
@@ -1361,6 +986,7 @@ EOF
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** references/sources.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
@@ -1446,6 +1072,7 @@ EOF
|
||||
- **Description:** FILL IN:
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
@@ -1480,6 +1107,7 @@ EOF
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## my-source
|
||||
@@ -1488,6 +1116,7 @@ EOF
|
||||
- **Description:** The same slug a second time.
|
||||
- **Contributing files:** references/nonexistent.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
@@ -1497,7 +1126,7 @@ EOF
|
||||
assert_output --partial "only the first block is checked"
|
||||
}
|
||||
|
||||
@test "G7: a second '- **Research doc:**' line in one entry is announced" {
|
||||
@test "G7: a second '- **Research doc:**' line in one entry FAILs (Research doc is single-valued, #121)" {
|
||||
local skill="$TMPDIR/my-skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
mkdir -p "$skill/references"
|
||||
@@ -1510,12 +1139,13 @@ EOF
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** SKILL.md
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Research doc:** docs/research/sources.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_success
|
||||
assert_failure
|
||||
assert_output --partial "Multiple '- **Research doc:**' lines for 'my-source'"
|
||||
}
|
||||
|
||||
@@ -1664,6 +1294,7 @@ EOF
|
||||
- **Description:** A brand-new source, never committed.
|
||||
- **Contributing files:** (none)
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
|
||||
@@ -1852,6 +1483,7 @@ PY
|
||||
- references/other.md (the rubric)
|
||||
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** `extracted`
|
||||
EOF
|
||||
printf -- '---\nsource_keys:\n - my-source\n---\n\nnotes\n' > "$skill/references/other.md"
|
||||
@@ -1886,6 +1518,7 @@ EOF
|
||||
- references/other.md (the rubric)
|
||||
|
||||
- **Research doc:** (none)
|
||||
- **Basis:** SKILL.md
|
||||
- **Status:** `extracted`
|
||||
EOF
|
||||
printf -- '---\nsource_keys:\n - my-source\n---\n\nnotes\n' > "$skill/references/other.md"
|
||||
@@ -1924,6 +1557,7 @@ EOF
|
||||
mkdir -p "$skill"
|
||||
make_skill_with_source_keys "$skill"
|
||||
make_sources_md "$skill"
|
||||
sed -i 's#^- \*\*Basis:\*\* SKILL.md#- **Basis:** tracked-skill/SKILL.md#' "$skill/references/sources.md"
|
||||
commit_as_base "$repo"
|
||||
|
||||
# A copy of the same skill at a path git does not know — the everyday case
|
||||
@@ -2060,3 +1694,257 @@ EOF
|
||||
refute_output ""
|
||||
assert_output --partial "$dir/notes.txt"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cycle 28 — #121: `Research doc:` names exactly one Research registry;
|
||||
# an entry with no registry writes `none` plus `Basis:`
|
||||
#
|
||||
# Helper: a fake repo holding one skill whose single entry is written verbatim
|
||||
# from the fields passed in — make_upstream_skill only varies the Research doc
|
||||
# value, and the Basis cases need a second field. The registry lives at
|
||||
# docs/research/sources.md and a basis file at docs/basis.md.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
make_entry_skill() {
|
||||
local repo="$1"
|
||||
local fields="$2"
|
||||
local skill="$repo/my-skill"
|
||||
mkdir -p "$skill/references" "$repo/docs/research"
|
||||
cat > "$skill/SKILL.md" <<EOF
|
||||
---
|
||||
name: my-skill
|
||||
description: A valid skill description.
|
||||
metadata:
|
||||
source_keys:
|
||||
- my-source
|
||||
---
|
||||
|
||||
## Step 1
|
||||
|
||||
Do the thing.
|
||||
EOF
|
||||
printf '%s\n' "# Basis" > "$repo/docs/basis.md"
|
||||
printf '%s\n' "# Other" > "$repo/docs/other-basis.md"
|
||||
{
|
||||
printf '# Sources\n\n## my-source\n\n'
|
||||
printf '%s\n' '- **URL:** https://example.com/my-source'
|
||||
printf '%s\n' '- **Description:** A test source.'
|
||||
printf '%s\n' '- **Contributing files:** SKILL.md'
|
||||
printf '%s\n' "$fields"
|
||||
printf '%s\n' '- **Status:** `extracted`'
|
||||
} > "$skill/references/sources.md"
|
||||
cat > "$repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## my-source
|
||||
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
commit_as_base "$repo"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a brace-expansion Research doc names more than one path" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '- **Research doc:** docs/research/{sources,other}.md'
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a comma-separated Research doc names more than one path" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '- **Research doc:** docs/research/sources.md, docs/research/other.md'
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a semicolon-separated pair of annotated paths names more than one path" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '- **Research doc:** docs/research/sources.md (Releases section); docs/research/other.md (`delete_release` gotcha)'
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Research doc names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 pass: a comma or semicolon INSIDE the annotation is prose, not a list" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '- **Research doc:** docs/research/sources.md (cross-cutting; no dedicated section, see notes)'
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 FAIL: repeated Research doc lines are a list, not an INFO" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s' '- **Research doc:** docs/research/sources.md' '- **Research doc:** docs/research/other.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Multiple '- **Research doc:**' lines for 'my-source'"
|
||||
}
|
||||
|
||||
@test "#121 pass: 'none' with one Basis bullet resolves and is clean" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s' '- **Research doc:** none' '- **Basis:** docs/basis.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 pass: 'none — reason' carries a trailing annotation and still counts as none" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s' '- **Research doc:** none — org convention, not a research corpus entry' '- **Basis:** docs/basis.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 pass: repeated single-path Basis bullets are each checked" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s\n%s' '- **Research doc:** none' '- **Basis:** docs/basis.md' '- **Basis:** docs/other-basis.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a second Basis bullet naming a missing path fails even when the first resolves" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s\n%s' '- **Research doc:** none' '- **Basis:** docs/basis.md' '- **Basis:** docs/gone.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis path 'docs/gone.md' does not exist"
|
||||
}
|
||||
|
||||
@test "#121 pass: a Basis header followed by '- ' path bullets is read too" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s\n%s\n%s' '- **Research doc:** none' '**Basis:**' '- docs/basis.md' '- docs/other-basis.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 FAIL: 'none' with no Basis declares nothing" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '- **Research doc:** none'
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis missing for 'my-source'"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a Basis path that does not exist" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s' '- **Research doc:** none' '- **Basis:** docs/gone.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis path 'docs/gone.md' does not exist"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: one Basis value naming several paths is a list" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s' '- **Research doc:** none' '- **Basis:** docs/basis.md, docs/other-basis.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis value names more than one path"
|
||||
}
|
||||
|
||||
@test "#121 pass: a '(removed in <sha>)' Basis skips the existence check" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s\n%s' '- **Research doc:** none' '- **Basis:** docs/basis.md' '- **Basis:** docs/deleted-adr.md (removed in 5b80f30)')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a Basis annotated with something other than '(removed in <sha>)' is still existence-checked" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s' '- **Research doc:** none' '- **Basis:** docs/deleted-adr.md (removed later)')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "Basis path 'docs/deleted-adr.md' does not exist"
|
||||
}
|
||||
|
||||
@test "#121 pass: a Basis with a trailing annotation resolves its path" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s' '- **Research doc:** none' '- **Basis:** docs/basis.md (org convention file, not a corpus entry)')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 FAIL: check 7 — a slug missing from a resolved registry is a FAIL, not an INFO" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '- **Research doc:** docs/research/sources.md'
|
||||
cat > "$repo/docs/research/sources.md" <<EOF
|
||||
# Research
|
||||
|
||||
## different-slug
|
||||
|
||||
- **Contributing files:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "FAIL"
|
||||
assert_output --partial "Slug 'my-source' not found as H2 in research doc 'docs/research/sources.md'"
|
||||
}
|
||||
|
||||
@test "#121 FAIL: a Research doc that resolves to a topic document is wrong, not 'not applicable'" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '- **Research doc:** docs/research/remotes.md (whole-document reference)'
|
||||
printf '# Remotes\n\n## Core Philosophy\n\nProse.\n' > "$repo/docs/research/remotes.md"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_failure
|
||||
assert_output --partial "is a topic document, not a Research registry"
|
||||
refute_output --partial "not applicable"
|
||||
}
|
||||
|
||||
@test "#121 INFO: an unresolvable Research doc path stays INFO and exits 0" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '- **Research doc:** docs/research/gone/sources.md'
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
assert_output --partial "INFO"
|
||||
assert_output --partial "does not exist"
|
||||
}
|
||||
|
||||
@test "#121 INFO: a Basis cannot be existence-checked with no repo root, and says so" {
|
||||
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/basis.md
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$skill"
|
||||
assert_success
|
||||
assert_output --partial "Basis check skipped for 'my-source'"
|
||||
}
|
||||
|
||||
# --- Parser parity: the spelling of a field must not change what is read ----
|
||||
|
||||
@test "#121 parity: an inline Research doc with no leading hyphen is read, not reported missing" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" '**Research doc:** docs/research/sources.md'
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
refute_output --partial "Research doc field missing"
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "#121 parity: a Research doc header with one '- ' bullet is read" {
|
||||
local repo="$TMPDIR/fakerepo"
|
||||
make_entry_skill "$repo" "$(printf '%s\n%s' '**Research doc:**' '- docs/research/sources.md')"
|
||||
run bash "$SCRIPT" "$repo/my-skill"
|
||||
assert_success
|
||||
refute_output --partial "Research doc field missing"
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user