mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 06:36:55 +01:00
Add knowledge-backed AL development
Add read-only planning and repository-changing development skills so BCQuality knowledge can guide features, bug fixes, refactors, upgrades, and maintenance before the existing AL review gate runs. Track Microsoft Learn ingestion and add development and BCApps-shaped guidance evaluation fixtures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 638b66d2-9f06-4f60-8781-808709e1485c
This commit is contained in:
parent
1a5afdc0eb
commit
56b80e6dcf
50 changed files with 11589 additions and 79 deletions
80
.github/scripts/validate_frontmatter.py
vendored
80
.github/scripts/validate_frontmatter.py
vendored
|
|
@ -39,6 +39,7 @@ ACTION_SKILL_REQUIRED_KEYS = {
|
|||
}
|
||||
ACTION_SKILL_OPTIONAL_KEYS = {
|
||||
"bc-version", "technologies", "countries", "application-area", "sub-skills",
|
||||
"quality-skill", "guidance-skill",
|
||||
}
|
||||
META_SKILL_REQUIRED_KEYS = {"kind", "id", "version", "title"}
|
||||
ENTRY_SKILL_REQUIRED_KEYS = {"kind", "id", "version", "title"}
|
||||
|
|
@ -46,8 +47,11 @@ HOST_SKILL_REQUIRED_KEYS = {"name", "description"}
|
|||
|
||||
STANDARD_INPUTS = {
|
||||
"pr-diff", "object-list", "file-path", "repository", "telemetry-query",
|
||||
"development-request", "development-plan",
|
||||
}
|
||||
ALLOWED_OUTPUTS = {
|
||||
"findings-report", "implementation-report", "development-guidance-report",
|
||||
}
|
||||
ALLOWED_OUTPUTS = {"findings-report"}
|
||||
VALID_SAMPLE_KINDS = {"good", "bad"}
|
||||
|
||||
ACTION_SKILL_SECTIONS = ["Source", "Relevance", "Worklist", "Action", "Output"]
|
||||
|
|
@ -340,9 +344,11 @@ def validate_action_skill(path: Path, parsed: Parsed, report: Report) -> None:
|
|||
if not is_non_empty_list_of_str(out):
|
||||
report.error(path, "R18", "outputs must be a non-empty list of strings", 1)
|
||||
else:
|
||||
if len(out) != 1:
|
||||
report.error(path, "R18", "outputs must contain exactly one output kind", 1)
|
||||
bad = [x for x in out if x not in ALLOWED_OUTPUTS]
|
||||
if bad:
|
||||
report.error(path, "R18", f"outputs contains non-allowed values {bad}; currently only {sorted(ALLOWED_OUTPUTS)} is defined", 1)
|
||||
report.error(path, "R18", f"outputs contains non-allowed values {bad}; allowed values are {sorted(ALLOWED_OUTPUTS)}", 1)
|
||||
|
||||
# R19 optional filter dimensions, if present
|
||||
if "bc-version" in fm:
|
||||
|
|
@ -382,6 +388,20 @@ def validate_action_skill(path: Path, parsed: Parsed, report: Report) -> None:
|
|||
if bad:
|
||||
report.error(path, "R20", f"sub-skills entries must end in '.md': {bad}", 1)
|
||||
|
||||
if "quality-skill" in fm:
|
||||
quality_skill = fm["quality-skill"]
|
||||
if not isinstance(quality_skill, str) or not quality_skill.endswith(".md"):
|
||||
report.error(path, "R31", "quality-skill must be one repo-relative .md path", 1)
|
||||
if fm.get("outputs") != ["implementation-report"]:
|
||||
report.error(path, "R31", "quality-skill is valid only with outputs: [implementation-report]", 1)
|
||||
|
||||
if "guidance-skill" in fm:
|
||||
guidance_skill = fm["guidance-skill"]
|
||||
if not isinstance(guidance_skill, str) or not guidance_skill.endswith(".md"):
|
||||
report.error(path, "R32", "guidance-skill must be one repo-relative .md path", 1)
|
||||
if fm.get("outputs") != ["implementation-report"]:
|
||||
report.error(path, "R32", "guidance-skill is valid only with outputs: [implementation-report]", 1)
|
||||
|
||||
# R21 five required sections, in order, each exactly once
|
||||
heads = [h for h, _ in headings_in_order(parsed.body)]
|
||||
indices: list[int] = []
|
||||
|
|
@ -603,6 +623,58 @@ def validate_sub_skills_registry(path: Path, fm: dict[str, Any], root: Path, rep
|
|||
report.error(path, "R26", f"leaf not registered in sub-skills: {leaf}", 1)
|
||||
|
||||
|
||||
def validate_quality_skill(path: Path, fm: dict[str, Any], root: Path, report: Report) -> None:
|
||||
"""R30: implementation quality-skill paths resolve to a findings producer."""
|
||||
quality_skill = fm.get("quality-skill")
|
||||
if not isinstance(quality_skill, str) or not quality_skill.endswith(".md"):
|
||||
return
|
||||
|
||||
normalized = quality_skill.lstrip("./")
|
||||
target = root / normalized
|
||||
if not target.is_file():
|
||||
report.error(path, "R30", f"quality-skill does not exist on disk: {normalized}", 1)
|
||||
return
|
||||
if target.resolve() == path.resolve():
|
||||
report.error(path, "R30", "quality-skill must not reference the implementation skill itself", 1)
|
||||
return
|
||||
|
||||
try:
|
||||
target_parsed = parse_markdown(target.read_text(encoding="utf-8"))
|
||||
except UnicodeDecodeError as e:
|
||||
report.error(path, "R30", f"quality-skill is not valid UTF-8: {e}", 1)
|
||||
return
|
||||
target_outputs = (target_parsed.frontmatter or {}).get("outputs")
|
||||
if target_outputs != ["findings-report"]:
|
||||
report.error(path, "R30", f"quality-skill must emit findings-report: {normalized}", 1)
|
||||
|
||||
|
||||
def validate_guidance_skill(path: Path, fm: dict[str, Any], root: Path, report: Report) -> None:
|
||||
"""R33: implementation guidance-skill paths resolve to a read-only planner."""
|
||||
guidance_skill = fm.get("guidance-skill")
|
||||
if not isinstance(guidance_skill, str) or not guidance_skill.endswith(".md"):
|
||||
return
|
||||
|
||||
normalized = guidance_skill.lstrip("./")
|
||||
target = root / normalized
|
||||
if not target.is_file():
|
||||
report.error(path, "R33", f"guidance-skill does not exist on disk: {normalized}", 1)
|
||||
return
|
||||
if target.resolve() == path.resolve():
|
||||
report.error(path, "R33", "guidance-skill must not reference the implementation skill itself", 1)
|
||||
return
|
||||
|
||||
try:
|
||||
target_parsed = parse_markdown(target.read_text(encoding="utf-8"))
|
||||
except UnicodeDecodeError as e:
|
||||
report.error(path, "R33", f"guidance-skill is not valid UTF-8: {e}", 1)
|
||||
return
|
||||
target_fm = target_parsed.frontmatter or {}
|
||||
if target_fm.get("outputs") != ["development-guidance-report"]:
|
||||
report.error(path, "R33", f"guidance-skill must emit development-guidance-report: {normalized}", 1)
|
||||
if "development-plan" not in (target_fm.get("inputs") or []):
|
||||
report.error(path, "R33", f"guidance-skill must accept development-plan: {normalized}", 1)
|
||||
|
||||
|
||||
def run(root: Path) -> Report:
|
||||
report = Report()
|
||||
skill_records: list[SkillRecord] = []
|
||||
|
|
@ -670,9 +742,11 @@ 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: cross-skill references
|
||||
for path, fm in action_skill_fms:
|
||||
validate_sub_skills_registry(path, fm, root, report)
|
||||
validate_quality_skill(path, fm, root, report)
|
||||
validate_guidance_skill(path, fm, root, report)
|
||||
|
||||
return report
|
||||
|
||||
|
|
|
|||
26
.github/workflows/development-coverage.yml
vendored
Normal file
26
.github/workflows/development-coverage.yml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
name: Validate development coverage
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
validate-development-coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Validate Microsoft Learn coverage ledger
|
||||
shell: pwsh
|
||||
run: ./tools/Test-LearnCoverage.ps1 -Root .
|
||||
|
||||
- name: Validate and prepare development fixtures
|
||||
shell: pwsh
|
||||
run: ./tools/Test-DevelopmentFixtures.ps1 -Root . -PrepareDirectory "$env:RUNNER_TEMP/bcquality-development-fixtures"
|
||||
|
||||
- name: Validate and prepare development-guidance fixtures
|
||||
shell: pwsh
|
||||
run: ./tools/Test-DevelopmentGuidanceFixtures.ps1 -Root . -PrepareDirectory "$env:RUNNER_TEMP/bcquality-development-guidance-fixtures"
|
||||
Loading…
Add table
Add a link
Reference in a new issue