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:
@@ -2,13 +2,13 @@
|
||||
|
||||
## gitea-mcp-repo
|
||||
|
||||
**Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags.
|
||||
**Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags. Tool parameters and SHA/concurrency behavior were cross-checked live against the deployed MCP tool schemas via `ToolSearch`, per this repo's process for resolving schema-vs-docs drift, rather than copied from the derived research doc.
|
||||
|
||||
**Source:** https://gitea.com/gitea/gitea-mcp
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:** (tool parameters and SHA/concurrency behavior cross-checked live against the deployed MCP tool schemas via ToolSearch)
|
||||
**Contributing files:**
|
||||
- SKILL.md (Gotchas — cross-flow parameter and encoding traps; Dispatch)
|
||||
- references/reading.md (read-tool parameters, `ref`/`tree_sha` selection, tree pagination)
|
||||
- references/writing.md (write-tool parameters, SHA/concurrency behavior, canonical call sequences, failed-write triage)
|
||||
@@ -45,4 +45,4 @@
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:** (none)
|
||||
- **Contributing files:** (none)
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
## gitea-mcp-repo
|
||||
|
||||
**Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags.
|
||||
**Description:** Official gitea-mcp repository (v1.3.0); operation/*.go source files documenting all 55 MCP tools, their parameters, and CLI flags. Tool parameters and SHA/concurrency behavior were cross-checked live against the deployed MCP tool schemas via `ToolSearch`, per this repo's process for resolving schema-vs-docs drift, rather than copied from the derived research doc.
|
||||
|
||||
**Source:** https://gitea.com/gitea/gitea-mcp
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:** (tool parameters and SHA/concurrency behavior cross-checked live against the deployed MCP tool schemas via ToolSearch)
|
||||
**Contributing files:**
|
||||
- SKILL.md (Gotchas — cross-flow parameter and encoding traps; Dispatch)
|
||||
- references/reading.md (read-tool parameters, `ref`/`tree_sha` selection, tree pagination)
|
||||
- references/writing.md (write-tool parameters, SHA/concurrency behavior, canonical call sequences, failed-write triage)
|
||||
@@ -45,4 +45,4 @@
|
||||
|
||||
- **Research doc:** plugins/gitea/docs/research/docs/gitea/sources.md
|
||||
|
||||
**Contributing files:** (none)
|
||||
- **Contributing files:** (none)
|
||||
|
||||
@@ -22,7 +22,10 @@ Checks performed:
|
||||
0 source_keys present in agent pair but sources.md absent
|
||||
1 FILL IN: placeholders in sources.md
|
||||
2 source_keys in agent files → slug exists in sources.md
|
||||
3 Contributing files listed in sources.md exist on disk (plugin-root relative)
|
||||
3 Contributing files listed in sources.md exist on disk (plugin-root
|
||||
relative). An explicit '(none)' skips silently; a Contributing files block
|
||||
this parser cannot read is reported as an INFO saying checks 3 and 4 did
|
||||
not run, never skipped silently.
|
||||
4 Contributing files back-reference the parent slug in their source_keys
|
||||
5 Research doc field present and not placeholder
|
||||
EOF
|
||||
@@ -244,14 +247,31 @@ has_fail = False
|
||||
def emit_fail(desc, fpath, why, fix):
|
||||
global has_fail
|
||||
has_fail = True
|
||||
findings.append(("FAIL", desc, fpath, why, fix))
|
||||
findings.append(("FAIL", desc, fpath, why, fix, None))
|
||||
|
||||
# INFO does not set has_fail and does not change the exit code. It is for a
|
||||
# check that could not RUN — an unverified entry, not a broken one — and it
|
||||
# exists so that "did not run" is never spelled the same way as "passed".
|
||||
def emit_info(desc, fpath, note):
|
||||
findings.append(("INFO", desc, fpath, None, None, note))
|
||||
|
||||
def print_findings():
|
||||
for kind, desc, fpath, why, fix in findings:
|
||||
for entry in findings:
|
||||
kind = entry[0]
|
||||
desc = entry[1]
|
||||
fpath = entry[2]
|
||||
why = entry[3]
|
||||
fix = entry[4]
|
||||
note = entry[5]
|
||||
if kind == "FAIL":
|
||||
print(f"FAIL {desc} — {fpath}")
|
||||
print(f" Why: {why}")
|
||||
print(f" Fix: {fix}")
|
||||
print()
|
||||
else:
|
||||
print(f"INFO {desc} — {fpath}")
|
||||
print(f" Note: {note}")
|
||||
print()
|
||||
|
||||
# --- Collect source_keys from agent pair ---
|
||||
def get_source_keys_from_file(fpath):
|
||||
@@ -321,9 +341,24 @@ for fpath, keys in [(agent_file, given_keys)]:
|
||||
|
||||
# --- Checks 3, 4, 5: Per-slug checks in sources.md ---
|
||||
for slug in parse_h2_slugs(sources_content):
|
||||
# Check 3: Contributing files exist (paths relative to plugin root)
|
||||
# Checks 3 and 4: Contributing files exist (paths relative to plugin root),
|
||||
# and back-reference the slug. `[]` and None are NOT the same answer here.
|
||||
# `[]` is the author writing "(none)" — there is nothing to check and the
|
||||
# skip is correct. None is a Contributing-files block this parser cannot
|
||||
# read, and skipping THAT silently disables both checks on the one entry
|
||||
# least likely to be right, which is the failure mode
|
||||
# parse_contributing_files' own docstring warns about. Say so out loud.
|
||||
cf_files = parse_contributing_files(sources_content, slug)
|
||||
if cf_files:
|
||||
if cf_files is None:
|
||||
emit_info(
|
||||
f"Contributing-file checks skipped for '{slug}' — the Contributing files block could not be parsed",
|
||||
f"sources.md (## {slug})",
|
||||
f"The '## {slug}' entry has no Contributing files list this parser can read — a missing field, a bare heading, '*' bullets, a numbered list, or prose all read as unparsable rather than as an empty declaration. "
|
||||
f"Checks 3 and 4 did not run for this slug, so nothing verified that its contributing files exist or name it back. "
|
||||
f"Write the value as '- **Contributing files:** <comma-separated paths>', or as a '**Contributing files:**' heading followed by '- ' bullets — "
|
||||
f"or record '(none)' if this source contributed no files."
|
||||
)
|
||||
elif cf_files:
|
||||
for cf_rel in cf_files:
|
||||
cf_abs = os.path.join(plugin_root, cf_rel)
|
||||
if not os.path.isfile(cf_abs):
|
||||
|
||||
@@ -433,3 +433,140 @@ EOF
|
||||
run bash "$SCRIPT" "$root/.apm/agents/my-agent.agent.md"
|
||||
assert_success
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Checks 3 and 4: None ("could not parse") is NOT [] ("explicitly (none)")
|
||||
#
|
||||
# parse_contributing_files returns three distinguishable answers and checks 3
|
||||
# and 4 have to honour all three. `[]` is the author writing "(none)" — the
|
||||
# skip is correct and silent. None is a Contributing files block the parser
|
||||
# cannot read, and skipping THAT silently disables both checks on the one entry
|
||||
# least likely to be right, which is the failure mode the parser's own
|
||||
# docstring warns about. 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 3 and 4 silently" {
|
||||
local root="$TMPDIR/package"
|
||||
make_package "$root"
|
||||
make_agent_with_source_keys "$root"
|
||||
cat > "$root/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Research doc:** (none)
|
||||
**Contributing files:**
|
||||
* .apm/agents/ghost.agent.md (asterisk bullets are not the bullet form)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$root/.apm/agents/my-agent.agent.md"
|
||||
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 root="$TMPDIR/package"
|
||||
make_package "$root"
|
||||
make_agent_with_source_keys "$root"
|
||||
cat > "$root/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" "$root/.apm/agents/my-agent.agent.md"
|
||||
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 3 and 4 skipped silently: an explicit '(none)' emits no INFO" {
|
||||
local root="$TMPDIR/package"
|
||||
make_package "$root"
|
||||
make_agent_with_source_keys "$root"
|
||||
make_sources_md "$root" "my-source" "(none — not used directly)"
|
||||
run bash "$SCRIPT" "$root/.apm/agents/my-agent.agent.md"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "checks 3 and 4 still run: a parseable Contributing files list is not diverted to the INFO" {
|
||||
local root="$TMPDIR/package"
|
||||
make_package "$root"
|
||||
make_agent_with_source_keys "$root"
|
||||
make_sources_md "$root" "my-source" ".apm/agents/nonexistent.agent.md"
|
||||
run bash "$SCRIPT" "$root/.apm/agents/my-agent.agent.md"
|
||||
assert_failure
|
||||
assert_output --partial "FAIL"
|
||||
assert_output --partial "Contributing file '.apm/agents/nonexistent.agent.md' does not exist"
|
||||
refute_output --partial "could not be parsed"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The INFO tier itself: kind-aware printing and a kind-aware exit code
|
||||
#
|
||||
# INFO is new here — before it, findings was a 5-tuple and print_findings
|
||||
# stamped every entry FAIL. The two cases below pin the tier rather than any
|
||||
# one check: an INFO must print under the INFO prefix and leave the exit code
|
||||
# at 0, and a real FAIL must keep printing under the FAIL prefix and still exit
|
||||
# non-zero even when an INFO is sitting in the same findings list.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@test "INFO tier: an INFO alone prints as INFO with a Note and does not set a failing exit code" {
|
||||
local root="$TMPDIR/package"
|
||||
make_package "$root"
|
||||
make_agent_with_source_keys "$root"
|
||||
cat > "$root/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" "$root/.apm/agents/my-agent.agent.md"
|
||||
assert_success
|
||||
assert_output --partial "INFO Contributing-file checks skipped for 'my-source'"
|
||||
assert_output --partial "Note:"
|
||||
refute_output --partial "FAIL"
|
||||
}
|
||||
|
||||
@test "FAIL tier: a genuine FAIL alongside an INFO still prints as FAIL and exits non-zero" {
|
||||
local root="$TMPDIR/package"
|
||||
make_package "$root"
|
||||
make_agent_with_source_keys "$root"
|
||||
cat > "$root/sources.md" <<EOF
|
||||
# Sources
|
||||
|
||||
## my-source
|
||||
|
||||
- **URL:** https://example.com/my-source
|
||||
- **Description:** A test source.
|
||||
- **Contributing files:** .apm/agents/nonexistent.agent.md
|
||||
- **Research doc:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
|
||||
## ghost-source
|
||||
|
||||
- **URL:** https://example.com/ghost-source
|
||||
- **Description:** Another test source.
|
||||
- **Research doc:** (none)
|
||||
- **Status:** \`extracted\`
|
||||
EOF
|
||||
run bash "$SCRIPT" "$root/.apm/agents/my-agent.agent.md"
|
||||
assert_failure
|
||||
assert_output --partial "FAIL Contributing file '.apm/agents/nonexistent.agent.md' does not exist"
|
||||
assert_output --partial "Why:"
|
||||
assert_output --partial "INFO Contributing-file checks skipped for 'ghost-source'"
|
||||
assert_output --partial "Note:"
|
||||
}
|
||||
|
||||
@@ -21,7 +21,10 @@ Checks performed:
|
||||
3 source_keys in references/*.md → slug exists in sources.md (INFO if no
|
||||
source_keys; an explicit 'source_keys: []' declares the file house-authored
|
||||
and passes silently)
|
||||
4 Contributing files listed in sources.md exist on disk
|
||||
4 Contributing files listed in sources.md exist on disk. An explicit
|
||||
'(none)' skips silently; a Contributing files block this parser cannot
|
||||
read is reported as an INFO saying checks 4 and 5 did not run, never
|
||||
skipped silently.
|
||||
5 Contributing files back-reference the parent slug in their source_keys
|
||||
6 Research doc field present and not placeholder
|
||||
7 Slug in sources.md present in upstream research doc (INFO only). A section
|
||||
@@ -104,7 +107,13 @@ def parse_source_keys(fm):
|
||||
# claims that then have to be maintained in sources.md as well. A bare
|
||||
# `source_keys:` with nothing after it is NOT accepted here — that reads as a
|
||||
# truncated or half-written entry, not a decision.
|
||||
EMPTY_SOURCE_KEYS_RE = re.compile(r'^\s*source_keys:\s*\[\s*\]\s*$')
|
||||
#
|
||||
# The indent is pinned to the two positions parse_source_keys() actually reads
|
||||
# — column 0, or two spaces under `metadata:`. A permissive `^\s*` matched a
|
||||
# `source_keys: []` nested at ANY depth under an unrelated key, which
|
||||
# parse_source_keys() never reads, so a stray nested key silenced the check-3
|
||||
# INFO for a file that had declared nothing.
|
||||
EMPTY_SOURCE_KEYS_RE = re.compile(r'^(?: )?source_keys:\s*\[\s*\]\s*$')
|
||||
|
||||
def declares_empty_source_keys(fm):
|
||||
"""True when frontmatter carries an explicit, empty `source_keys: []`."""
|
||||
@@ -436,9 +445,25 @@ repo_root = find_repo_root(skill_dir)
|
||||
research_docs_seen = {} # abs_path → set of slugs in sources.md that reference it
|
||||
|
||||
for slug in parse_h2_slugs(sources_content):
|
||||
# Check 4: Contributing files exist
|
||||
# Checks 4 and 5: Contributing files exist, and back-reference the slug.
|
||||
# `[]` and None are NOT the same answer here. `[]` is the author writing
|
||||
# "(none)" — there is nothing to check and the skip is correct. None is a
|
||||
# Contributing-files block this parser cannot read, and skipping THAT
|
||||
# silently disables both checks on the one entry least likely to be right,
|
||||
# which is the failure mode parse_contributing_files' own docstring warns
|
||||
# about. Say so out loud instead, the same way an unresolvable Research doc
|
||||
# value does.
|
||||
cf_files = parse_contributing_files(sources_content, slug)
|
||||
if cf_files:
|
||||
if cf_files is None:
|
||||
emit_info(
|
||||
f"Contributing-file checks skipped for '{slug}' — the Contributing files block could not be parsed",
|
||||
f"references/sources.md (## {slug})",
|
||||
f"The '## {slug}' entry has no Contributing files list this parser can read — a missing field, a bare heading, '*' bullets, a numbered list, or prose all read as unparsable rather than as an empty declaration. "
|
||||
f"Checks 4 and 5 did not run for this slug, so nothing verified that its contributing files exist or name it back. "
|
||||
f"Write the value as '- **Contributing files:** <comma-separated paths>', or as a '**Contributing files:**' heading followed by '- ' bullets — "
|
||||
f"or record '(none)' if this source contributed no files."
|
||||
)
|
||||
elif cf_files:
|
||||
for cf_rel in cf_files:
|
||||
cf_abs = os.path.join(skill_dir, cf_rel)
|
||||
if not os.path.isfile(cf_abs):
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@ Checks performed:
|
||||
0 source_keys present in agent pair but sources.md absent
|
||||
1 FILL IN: placeholders in sources.md
|
||||
2 source_keys in agent files → slug exists in sources.md
|
||||
3 Contributing files listed in sources.md exist on disk (plugin-root relative)
|
||||
3 Contributing files listed in sources.md exist on disk (plugin-root
|
||||
relative). An explicit '(none)' skips silently; a Contributing files block
|
||||
this parser cannot read is reported as an INFO saying checks 3 and 4 did
|
||||
not run, never skipped silently.
|
||||
4 Contributing files back-reference the parent slug in their source_keys
|
||||
5 Research doc field present and not placeholder
|
||||
EOF
|
||||
@@ -244,14 +247,31 @@ has_fail = False
|
||||
def emit_fail(desc, fpath, why, fix):
|
||||
global has_fail
|
||||
has_fail = True
|
||||
findings.append(("FAIL", desc, fpath, why, fix))
|
||||
findings.append(("FAIL", desc, fpath, why, fix, None))
|
||||
|
||||
# INFO does not set has_fail and does not change the exit code. It is for a
|
||||
# check that could not RUN — an unverified entry, not a broken one — and it
|
||||
# exists so that "did not run" is never spelled the same way as "passed".
|
||||
def emit_info(desc, fpath, note):
|
||||
findings.append(("INFO", desc, fpath, None, None, note))
|
||||
|
||||
def print_findings():
|
||||
for kind, desc, fpath, why, fix in findings:
|
||||
for entry in findings:
|
||||
kind = entry[0]
|
||||
desc = entry[1]
|
||||
fpath = entry[2]
|
||||
why = entry[3]
|
||||
fix = entry[4]
|
||||
note = entry[5]
|
||||
if kind == "FAIL":
|
||||
print(f"FAIL {desc} — {fpath}")
|
||||
print(f" Why: {why}")
|
||||
print(f" Fix: {fix}")
|
||||
print()
|
||||
else:
|
||||
print(f"INFO {desc} — {fpath}")
|
||||
print(f" Note: {note}")
|
||||
print()
|
||||
|
||||
# --- Collect source_keys from agent pair ---
|
||||
def get_source_keys_from_file(fpath):
|
||||
@@ -321,9 +341,24 @@ for fpath, keys in [(agent_file, given_keys)]:
|
||||
|
||||
# --- Checks 3, 4, 5: Per-slug checks in sources.md ---
|
||||
for slug in parse_h2_slugs(sources_content):
|
||||
# Check 3: Contributing files exist (paths relative to plugin root)
|
||||
# Checks 3 and 4: Contributing files exist (paths relative to plugin root),
|
||||
# and back-reference the slug. `[]` and None are NOT the same answer here.
|
||||
# `[]` is the author writing "(none)" — there is nothing to check and the
|
||||
# skip is correct. None is a Contributing-files block this parser cannot
|
||||
# read, and skipping THAT silently disables both checks on the one entry
|
||||
# least likely to be right, which is the failure mode
|
||||
# parse_contributing_files' own docstring warns about. Say so out loud.
|
||||
cf_files = parse_contributing_files(sources_content, slug)
|
||||
if cf_files:
|
||||
if cf_files is None:
|
||||
emit_info(
|
||||
f"Contributing-file checks skipped for '{slug}' — the Contributing files block could not be parsed",
|
||||
f"sources.md (## {slug})",
|
||||
f"The '## {slug}' entry has no Contributing files list this parser can read — a missing field, a bare heading, '*' bullets, a numbered list, or prose all read as unparsable rather than as an empty declaration. "
|
||||
f"Checks 3 and 4 did not run for this slug, so nothing verified that its contributing files exist or name it back. "
|
||||
f"Write the value as '- **Contributing files:** <comma-separated paths>', or as a '**Contributing files:**' heading followed by '- ' bullets — "
|
||||
f"or record '(none)' if this source contributed no files."
|
||||
)
|
||||
elif cf_files:
|
||||
for cf_rel in cf_files:
|
||||
cf_abs = os.path.join(plugin_root, cf_rel)
|
||||
if not os.path.isfile(cf_abs):
|
||||
|
||||
@@ -21,7 +21,10 @@ Checks performed:
|
||||
3 source_keys in references/*.md → slug exists in sources.md (INFO if no
|
||||
source_keys; an explicit 'source_keys: []' declares the file house-authored
|
||||
and passes silently)
|
||||
4 Contributing files listed in sources.md exist on disk
|
||||
4 Contributing files listed in sources.md exist on disk. An explicit
|
||||
'(none)' skips silently; a Contributing files block this parser cannot
|
||||
read is reported as an INFO saying checks 4 and 5 did not run, never
|
||||
skipped silently.
|
||||
5 Contributing files back-reference the parent slug in their source_keys
|
||||
6 Research doc field present and not placeholder
|
||||
7 Slug in sources.md present in upstream research doc (INFO only). A section
|
||||
@@ -104,7 +107,13 @@ def parse_source_keys(fm):
|
||||
# claims that then have to be maintained in sources.md as well. A bare
|
||||
# `source_keys:` with nothing after it is NOT accepted here — that reads as a
|
||||
# truncated or half-written entry, not a decision.
|
||||
EMPTY_SOURCE_KEYS_RE = re.compile(r'^\s*source_keys:\s*\[\s*\]\s*$')
|
||||
#
|
||||
# The indent is pinned to the two positions parse_source_keys() actually reads
|
||||
# — column 0, or two spaces under `metadata:`. A permissive `^\s*` matched a
|
||||
# `source_keys: []` nested at ANY depth under an unrelated key, which
|
||||
# parse_source_keys() never reads, so a stray nested key silenced the check-3
|
||||
# INFO for a file that had declared nothing.
|
||||
EMPTY_SOURCE_KEYS_RE = re.compile(r'^(?: )?source_keys:\s*\[\s*\]\s*$')
|
||||
|
||||
def declares_empty_source_keys(fm):
|
||||
"""True when frontmatter carries an explicit, empty `source_keys: []`."""
|
||||
@@ -436,9 +445,25 @@ repo_root = find_repo_root(skill_dir)
|
||||
research_docs_seen = {} # abs_path → set of slugs in sources.md that reference it
|
||||
|
||||
for slug in parse_h2_slugs(sources_content):
|
||||
# Check 4: Contributing files exist
|
||||
# Checks 4 and 5: Contributing files exist, and back-reference the slug.
|
||||
# `[]` and None are NOT the same answer here. `[]` is the author writing
|
||||
# "(none)" — there is nothing to check and the skip is correct. None is a
|
||||
# Contributing-files block this parser cannot read, and skipping THAT
|
||||
# silently disables both checks on the one entry least likely to be right,
|
||||
# which is the failure mode parse_contributing_files' own docstring warns
|
||||
# about. Say so out loud instead, the same way an unresolvable Research doc
|
||||
# value does.
|
||||
cf_files = parse_contributing_files(sources_content, slug)
|
||||
if cf_files:
|
||||
if cf_files is None:
|
||||
emit_info(
|
||||
f"Contributing-file checks skipped for '{slug}' — the Contributing files block could not be parsed",
|
||||
f"references/sources.md (## {slug})",
|
||||
f"The '## {slug}' entry has no Contributing files list this parser can read — a missing field, a bare heading, '*' bullets, a numbered list, or prose all read as unparsable rather than as an empty declaration. "
|
||||
f"Checks 4 and 5 did not run for this slug, so nothing verified that its contributing files exist or name it back. "
|
||||
f"Write the value as '- **Contributing files:** <comma-separated paths>', or as a '**Contributing files:**' heading followed by '- ' bullets — "
|
||||
f"or record '(none)' if this source contributed no files."
|
||||
)
|
||||
elif cf_files:
|
||||
for cf_rel in cf_files:
|
||||
cf_abs = os.path.join(skill_dir, cf_rel)
|
||||
if not os.path.isfile(cf_abs):
|
||||
|
||||
Reference in New Issue
Block a user