mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 06:36:55 +01:00
Add machine-readable review contracts (#182)
Generate a deterministic action-skill index from frontmatter, publish structural schemas for orchestration and findings, and validate flat review composition in CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: dayland <dayland@microsoft.com> Copilot-Session: 76eb42c4-2acd-4f9c-a898-f4a44f9d46f7
This commit is contained in:
parent
852a676285
commit
b74967bc5b
8 changed files with 803 additions and 3 deletions
41
.github/scripts/validate_frontmatter.py
vendored
41
.github/scripts/validate_frontmatter.py
vendored
|
|
@ -381,6 +381,20 @@ def validate_action_skill(path: Path, parsed: Parsed, report: Report) -> None:
|
|||
bad = [x for x in ss if not x.endswith(".md")]
|
||||
if bad:
|
||||
report.error(path, "R20", f"sub-skills entries must end in '.md': {bad}", 1)
|
||||
non_canonical = [
|
||||
x for x in ss
|
||||
if "\\" in x or x.startswith("/") or ".." in Path(x).parts or x.startswith("./")
|
||||
]
|
||||
if non_canonical:
|
||||
report.error(
|
||||
path,
|
||||
"R20",
|
||||
f"sub-skills entries must be canonical repo-relative paths: {non_canonical}",
|
||||
1,
|
||||
)
|
||||
duplicates = sorted({x for x in ss if ss.count(x) > 1})
|
||||
if duplicates:
|
||||
report.error(path, "R20", f"sub-skills contains duplicate paths: {duplicates}", 1)
|
||||
|
||||
# R21 five required sections, in order, each exactly once
|
||||
heads = [h for h, _ in headings_in_order(parsed.body)]
|
||||
|
|
@ -565,7 +579,13 @@ class SkillRecord:
|
|||
skill_id: str | None
|
||||
|
||||
|
||||
def validate_sub_skills_registry(path: Path, fm: dict[str, Any], root: Path, report: Report) -> None:
|
||||
def validate_sub_skills_registry(
|
||||
path: Path,
|
||||
fm: dict[str, Any],
|
||||
root: Path,
|
||||
action_skills_by_path: dict[str, dict[str, Any]],
|
||||
report: Report,
|
||||
) -> None:
|
||||
"""R26: a super-skill's declared `sub-skills` must exactly match the
|
||||
`al-*-review.md` leaf files present in the same directory (set equality,
|
||||
ordering-agnostic). This keeps the registered leaf list the single source
|
||||
|
|
@ -598,6 +618,17 @@ def validate_sub_skills_registry(path: Path, fm: dict[str, Any], root: Path, rep
|
|||
f"sub-skills entry is not a sibling 'al-*-review.md' leaf: {entry}", 1,
|
||||
)
|
||||
|
||||
for entry in ss:
|
||||
leaf = action_skills_by_path.get(entry)
|
||||
if leaf is None:
|
||||
if (root / entry).exists():
|
||||
report.error(path, "R26", f"sub-skills entry is not an action skill: {entry}", 1)
|
||||
continue
|
||||
if is_non_empty_list_of_str(leaf.get("sub-skills")):
|
||||
report.error(path, "R26", f"nested super-skill is not permitted in v1 composition: {entry}", 1)
|
||||
if leaf.get("outputs") != ["findings-report"]:
|
||||
report.error(path, "R26", f"sub-skill must produce findings-report: {entry}", 1)
|
||||
|
||||
# Sibling leaves on disk that were never registered ('forgot to wire it up').
|
||||
for leaf in sorted(leaves - declared):
|
||||
report.error(path, "R26", f"leaf not registered in sub-skills: {leaf}", 1)
|
||||
|
|
@ -670,9 +701,13 @@ def run(root: Path) -> Report:
|
|||
others = [q.relative_to(root).as_posix() for q in paths if q != p]
|
||||
report.error(p, "R24", f"skill id '{sid}' ({kind}) is not unique; also defined in: {others}")
|
||||
|
||||
# Fourth pass: R26 sub-skills registry matches leaf files on disk
|
||||
# Fourth pass: R26 sub-skills registry matches compatible leaf files on disk
|
||||
action_skills_by_path = {
|
||||
path.relative_to(root).as_posix(): fm
|
||||
for path, fm in action_skill_fms
|
||||
}
|
||||
for path, fm in action_skill_fms:
|
||||
validate_sub_skills_registry(path, fm, root, report)
|
||||
validate_sub_skills_registry(path, fm, root, action_skills_by_path, report)
|
||||
|
||||
return report
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue