From 484357a3b9d49f5be6b8b186b055b4cd2fbd19f9 Mon Sep 17 00:00:00 2001 From: Defame1297 Date: Sun, 30 Aug 2026 20:51:56 +0000 Subject: [PATCH] fix(gates): parse the bullet form of Contributing files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit validate-provenance.sh matched Contributing files only as a single inline line beginning "- **Contributing files:**". Seven skills write it as a bare "**Contributing files:**" heading above a bullet list, so parse_contributing_files returned None and checks 4 (contributing file exists) and 5 (bidirectional source_keys) silently verified nothing on git-branches, git-remotes, git-submodules, git-workflow, git-worktrees, gitea-files and gitea-releases. Those are among the skills this branch changed most — git-branches alone gained five reference files — and the retrofit's mandatory sources.md collateral went in unchecked. Demonstrated rather than argued: planting a nonexistent contributing path in git-remotes yields 0 findings under the old parser and 1 FAIL under the new one. Both forms are now accepted. The bullet form is parsed per bullet rather than by splitting a joined value, because its per-file notes contain commas that would otherwise be read as path separators. The return type becomes a list of note-stripped paths, with "(none)" as an empty list and an absent entry as None, so the two callers no longer re-split a string. Applied to agent-audit's copy as well. No agent ships a sources.md today, so it is latent there, but it is the same defect. This is a third gate blind spot alongside #117 and #118, and was unfiled. One real defect surfaced immediately and is fixed separately. Refs #99 --- .../scripts/validate-provenance.sh | 42 +++++++++++-- .../scripts/validate-provenance.sh | 62 ++++++++++++++++--- .../scripts/validate-provenance.sh | 42 +++++++++++-- .../scripts/validate-provenance.sh | 62 ++++++++++++++++--- 4 files changed, 182 insertions(+), 26 deletions(-) diff --git a/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate-provenance.sh b/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate-provenance.sh index 20052bd..82395c3 100755 --- a/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate-provenance.sh +++ b/plugins/kyberforge/.apm/skills/agent-audit/scripts/validate-provenance.sh @@ -131,6 +131,15 @@ def parse_h2_slugs(content): return re.findall(r'^## (.+)$', content, re.MULTILINE) def parse_contributing_files(content, slug): + """Find the Contributing files for a given slug H2 in content. + + Accepts the inline form and the bullet form; recognising only the + inline one silently skips the contributing-file checks on every + sources.md written the other way. Returns a list of paths with any + trailing parenthetical note stripped; "(none)" returns an empty list + and a slug with no entry returns None. Kept behaviourally identical to + skill-audit's copy, which is where the bug was found. + """ pattern = re.compile( r'^## ' + re.escape(slug) + r'\s*\n(.*?)(?=^## |\Z)', re.MULTILINE | re.DOTALL @@ -139,10 +148,36 @@ def parse_contributing_files(content, slug): if not m: return None block = m.group(1) + + def strip_note(entry): + return re.sub(r'\s*\(.*$', '', entry).strip() + cf_m = re.search(r'^\- \*\*Contributing files:\*\* (.+)$', block, re.MULTILINE) + if cf_m: + value = cf_m.group(1).strip() + if value.startswith("(none"): + return [] + return [p for p in (strip_note(x) for x in value.split(",")) if p] + + cf_m = re.search(r'^\*\*Contributing files:\*\*\s*$', block, re.MULTILINE) if not cf_m: return None - return cf_m.group(1).strip() + files = [] + for line in block[cf_m.end():].splitlines(): + line = line.strip() + if not line: + if files: + break + continue + if not line.startswith("- "): + break + entry = line[2:].strip() + if entry.startswith("(none"): + return [] + entry = strip_note(entry) + if entry: + files.append(entry) + return files def parse_research_doc(content, slug): pattern = re.compile( @@ -242,9 +277,8 @@ 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) - cf_value = parse_contributing_files(sources_content, slug) - if cf_value and not cf_value.startswith("(none"): - cf_files = [p.strip() for p in cf_value.split(",") if p.strip()] + cf_files = parse_contributing_files(sources_content, slug) + if cf_files: for cf_rel in cf_files: cf_abs = os.path.join(plugin_root, cf_rel) if not os.path.isfile(cf_abs): diff --git a/plugins/kyberforge/.apm/skills/skill-audit/scripts/validate-provenance.sh b/plugins/kyberforge/.apm/skills/skill-audit/scripts/validate-provenance.sh index edfe211..619ac47 100755 --- a/plugins/kyberforge/.apm/skills/skill-audit/scripts/validate-provenance.sh +++ b/plugins/kyberforge/.apm/skills/skill-audit/scripts/validate-provenance.sh @@ -94,8 +94,24 @@ def parse_h2_slugs(content): return re.findall(r'^## (.+)$', content, re.MULTILINE) def parse_contributing_files(content, slug): - """Find the Contributing files value for a given slug H2 in content.""" - # Find the H2 block for slug, then look for Contributing files line + """Find the Contributing files for a given slug H2 in content. + + Both authored forms are accepted, because both are in use across the + corpus and only recognising the first silently skipped checks 4 and 5 + on every skill using the second: + + - **Contributing files:** SKILL.md, references/a.md + + **Contributing files:** + - SKILL.md (what this source contributed) + - references/a.md (what this source contributed) + + Returns a list of paths with any trailing parenthetical note stripped. + A "(none)" value returns an empty list; a slug with no Contributing + files entry at all returns None. Note the bullet form's notes may + themselves contain commas, so the list is built per bullet rather than + by splitting the joined value. + """ pattern = re.compile( r'^## ' + re.escape(slug) + r'\s*\n(.*?)(?=^## |\Z)', re.MULTILINE | re.DOTALL @@ -104,10 +120,40 @@ def parse_contributing_files(content, slug): if not m: return None block = m.group(1) + + def strip_note(entry): + # "references/a.md (why)" -> "references/a.md" + return re.sub(r'\s*\(.*$', '', entry).strip() + + # Inline form: value on the same line, comma-separated, no notes. cf_m = re.search(r'^\- \*\*Contributing files:\*\* (.+)$', block, re.MULTILINE) + if cf_m: + value = cf_m.group(1).strip() + if value.startswith("(none"): + return [] + return [p for p in (strip_note(x) for x in value.split(",")) if p] + + # Bullet form: heading on its own line, one file per following bullet. + cf_m = re.search(r'^\*\*Contributing files:\*\*\s*$', block, re.MULTILINE) if not cf_m: return None - return cf_m.group(1).strip() + rest = block[cf_m.end():] + files = [] + for line in rest.splitlines(): + line = line.strip() + if not line: + if files: + break + continue + if not line.startswith("- "): + break + entry = line[2:].strip() + if entry.startswith("(none"): + return [] + entry = strip_note(entry) + if entry: + files.append(entry) + return files def parse_research_doc(content, slug): """Find the Research doc value for a given slug H2 in content.""" @@ -300,10 +346,8 @@ research_docs_seen = {} # abs_path → set of slugs in sources.md that referenc for slug in parse_h2_slugs(sources_content): # Check 4: Contributing files exist - cf_value = parse_contributing_files(sources_content, slug) - if cf_value and not cf_value.startswith("(none"): - # Split by comma - cf_files = [p.strip() for p in cf_value.split(",") if p.strip()] + cf_files = parse_contributing_files(sources_content, slug) + if cf_files: for cf_rel in cf_files: cf_abs = os.path.join(skill_dir, cf_rel) if not os.path.isfile(cf_abs): @@ -374,8 +418,8 @@ for rd_abs, (rd_rel, known_slugs) in research_docs_seen.items(): # Parse this slug's Contributing files and Status in the research doc rd_cf = parse_contributing_files(rd_content, rd_slug) rd_status = parse_status(rd_content, rd_slug) - # Skip if contributing files start with (none - if rd_cf and rd_cf.startswith("(none"): + # Skip if the research doc explicitly records no contributing files + if rd_cf == []: continue # Skip if status is not `extracted` if rd_status != "`extracted`": diff --git a/plugins/kyberforge/skills/agent-audit/scripts/validate-provenance.sh b/plugins/kyberforge/skills/agent-audit/scripts/validate-provenance.sh index 20052bd..82395c3 100755 --- a/plugins/kyberforge/skills/agent-audit/scripts/validate-provenance.sh +++ b/plugins/kyberforge/skills/agent-audit/scripts/validate-provenance.sh @@ -131,6 +131,15 @@ def parse_h2_slugs(content): return re.findall(r'^## (.+)$', content, re.MULTILINE) def parse_contributing_files(content, slug): + """Find the Contributing files for a given slug H2 in content. + + Accepts the inline form and the bullet form; recognising only the + inline one silently skips the contributing-file checks on every + sources.md written the other way. Returns a list of paths with any + trailing parenthetical note stripped; "(none)" returns an empty list + and a slug with no entry returns None. Kept behaviourally identical to + skill-audit's copy, which is where the bug was found. + """ pattern = re.compile( r'^## ' + re.escape(slug) + r'\s*\n(.*?)(?=^## |\Z)', re.MULTILINE | re.DOTALL @@ -139,10 +148,36 @@ def parse_contributing_files(content, slug): if not m: return None block = m.group(1) + + def strip_note(entry): + return re.sub(r'\s*\(.*$', '', entry).strip() + cf_m = re.search(r'^\- \*\*Contributing files:\*\* (.+)$', block, re.MULTILINE) + if cf_m: + value = cf_m.group(1).strip() + if value.startswith("(none"): + return [] + return [p for p in (strip_note(x) for x in value.split(",")) if p] + + cf_m = re.search(r'^\*\*Contributing files:\*\*\s*$', block, re.MULTILINE) if not cf_m: return None - return cf_m.group(1).strip() + files = [] + for line in block[cf_m.end():].splitlines(): + line = line.strip() + if not line: + if files: + break + continue + if not line.startswith("- "): + break + entry = line[2:].strip() + if entry.startswith("(none"): + return [] + entry = strip_note(entry) + if entry: + files.append(entry) + return files def parse_research_doc(content, slug): pattern = re.compile( @@ -242,9 +277,8 @@ 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) - cf_value = parse_contributing_files(sources_content, slug) - if cf_value and not cf_value.startswith("(none"): - cf_files = [p.strip() for p in cf_value.split(",") if p.strip()] + cf_files = parse_contributing_files(sources_content, slug) + if cf_files: for cf_rel in cf_files: cf_abs = os.path.join(plugin_root, cf_rel) if not os.path.isfile(cf_abs): diff --git a/plugins/kyberforge/skills/skill-audit/scripts/validate-provenance.sh b/plugins/kyberforge/skills/skill-audit/scripts/validate-provenance.sh index edfe211..619ac47 100755 --- a/plugins/kyberforge/skills/skill-audit/scripts/validate-provenance.sh +++ b/plugins/kyberforge/skills/skill-audit/scripts/validate-provenance.sh @@ -94,8 +94,24 @@ def parse_h2_slugs(content): return re.findall(r'^## (.+)$', content, re.MULTILINE) def parse_contributing_files(content, slug): - """Find the Contributing files value for a given slug H2 in content.""" - # Find the H2 block for slug, then look for Contributing files line + """Find the Contributing files for a given slug H2 in content. + + Both authored forms are accepted, because both are in use across the + corpus and only recognising the first silently skipped checks 4 and 5 + on every skill using the second: + + - **Contributing files:** SKILL.md, references/a.md + + **Contributing files:** + - SKILL.md (what this source contributed) + - references/a.md (what this source contributed) + + Returns a list of paths with any trailing parenthetical note stripped. + A "(none)" value returns an empty list; a slug with no Contributing + files entry at all returns None. Note the bullet form's notes may + themselves contain commas, so the list is built per bullet rather than + by splitting the joined value. + """ pattern = re.compile( r'^## ' + re.escape(slug) + r'\s*\n(.*?)(?=^## |\Z)', re.MULTILINE | re.DOTALL @@ -104,10 +120,40 @@ def parse_contributing_files(content, slug): if not m: return None block = m.group(1) + + def strip_note(entry): + # "references/a.md (why)" -> "references/a.md" + return re.sub(r'\s*\(.*$', '', entry).strip() + + # Inline form: value on the same line, comma-separated, no notes. cf_m = re.search(r'^\- \*\*Contributing files:\*\* (.+)$', block, re.MULTILINE) + if cf_m: + value = cf_m.group(1).strip() + if value.startswith("(none"): + return [] + return [p for p in (strip_note(x) for x in value.split(",")) if p] + + # Bullet form: heading on its own line, one file per following bullet. + cf_m = re.search(r'^\*\*Contributing files:\*\*\s*$', block, re.MULTILINE) if not cf_m: return None - return cf_m.group(1).strip() + rest = block[cf_m.end():] + files = [] + for line in rest.splitlines(): + line = line.strip() + if not line: + if files: + break + continue + if not line.startswith("- "): + break + entry = line[2:].strip() + if entry.startswith("(none"): + return [] + entry = strip_note(entry) + if entry: + files.append(entry) + return files def parse_research_doc(content, slug): """Find the Research doc value for a given slug H2 in content.""" @@ -300,10 +346,8 @@ research_docs_seen = {} # abs_path → set of slugs in sources.md that referenc for slug in parse_h2_slugs(sources_content): # Check 4: Contributing files exist - cf_value = parse_contributing_files(sources_content, slug) - if cf_value and not cf_value.startswith("(none"): - # Split by comma - cf_files = [p.strip() for p in cf_value.split(",") if p.strip()] + cf_files = parse_contributing_files(sources_content, slug) + if cf_files: for cf_rel in cf_files: cf_abs = os.path.join(skill_dir, cf_rel) if not os.path.isfile(cf_abs): @@ -374,8 +418,8 @@ for rd_abs, (rd_rel, known_slugs) in research_docs_seen.items(): # Parse this slug's Contributing files and Status in the research doc rd_cf = parse_contributing_files(rd_content, rd_slug) rd_status = parse_status(rd_content, rd_slug) - # Skip if contributing files start with (none - if rd_cf and rd_cf.startswith("(none"): + # Skip if the research doc explicitly records no contributing files + if rd_cf == []: continue # Skip if status is not `extracted` if rd_status != "`extracted`":