Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 78 additions & 6 deletions spec/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,9 @@ def extract_section(text, heading):
# Carried files scanned for a coordination reference (GOVERNANCE.md "Documentation Style Conventions").
TEMPLATE_REF_SCANNED = ("AGENTS.md", "GOVERNANCE.md", ".github/copilot-instructions.md")

# Carried files scanned for an undeclared H2 heading (spec/section-model.md).
UNDECLARED_HEADING_SCANNED = ("AGENTS.md", "GOVERNANCE.md", ".github/copilot-instructions.md")
Comment thread
qodo-code-review[bot] marked this conversation as resolved.


def strip_sections(text, names):
"""`text` with each named `## <heading>` region removed, located by position rather than by content.
Expand All @@ -444,6 +447,20 @@ def strip_sections(text, names):
return "\n".join(out)


def undeclared_h2_headings(text, declared):
"""Level-two headings in `text` that `declared` does not name, sorted.

`declared` is normalized here (stripped, lowercased) rather than trusted pre-normalized, so the
contract holds for any caller regardless of how its own section names are cased or spaced.
Scoped to `## ` only: the section model's unit is the H2, and an H1 title or a nested H3 is not itself a
section this check judges. Fence-aware via unfenced_text, so a `## ` line inside a fenced code sample
(documenting the heading syntax itself) or a `##`-prefixed shell comment is not misread as a real
heading. Per unfenced_text's own docstring, a checker left fence-blind is a document read two ways.
"""
h2s = {ln[3:].strip().lower() for ln in unfenced_text(text).split("\n") if ln.startswith("## ")}
return sorted(h2s - {d.strip().lower() for d in declared})


def template_ref_outside_verbatim(text, verbatim_names, hub_name):
"""True when `hub_name` appears in `text` outside every one of its verbatim sections.

Expand Down Expand Up @@ -1869,15 +1886,12 @@ def audit_repo(entry, spec, branch=None):
)
# The undeclared-section advisory, per spec/section-model.md, treats an H2 the manifest does not declare as a candidate duplicate of a verbatim section, or as repo-specific content to relocate.
# It is advisory only, since a repo may legitimately carry its own project-specific sections, which the AGENTS.md preamble allows, so it points at the reconciliation and never fails.
# It covers AGENTS.md and GOVERNANCE.md only, the two files whose section structure is governed by section-model.md.
# It covers UNDECLARED_HEADING_SCANNED, not only AGENTS.md and GOVERNANCE.md, and never names which destination file an undeclared heading belongs in.
# Skip the hub itself, since its copies are the source and legitimately hold hub-only sections, Repository Onboarding and Conformance being one, that are deliberately not carried.
# A downstream repo carrying such a section is still flagged, which is the point.
if path in ("AGENTS.md", "GOVERNANCE.md") and entry.get("name") != HUB_NAME:
if path in UNDECLARED_HEADING_SCANNED and entry.get("name") != HUB_NAME:
declared = {n.strip().lower() for n in (needed | verbatim_needed)}
h2s = {
ln[3:].strip().lower() for ln in text.splitlines() if ln.startswith("## ")
}
for h in sorted(h2s - declared):
for h in undeclared_h2_headings(text, declared):
findings.append(
(
"DRIFT",
Expand Down Expand Up @@ -2865,6 +2879,64 @@ def _selftest():
f" ok template-ref: {len(tref)} cases, verbatim regions excised before the hub-name scan"
)

# Undeclared-heading advisory: an H2 the manifest does not declare, scoped to AGENTS.md, GOVERNANCE.md, and .github/copilot-instructions.md, fence-aware so a documented heading syntax or a shell comment inside a code sample is not misread as a real section.
uh = [
(
"a declared H2 is not flagged",
"# AGENTS\n\n## Fleet Bootstrap\n\nText.\n",
{"fleet bootstrap"},
[],
),
(
"an undeclared H2 is flagged",
"# AGENTS\n\n## Fleet Bootstrap\n\nText.\n\n## Local Notes\n\nRepo-specific.\n",
{"fleet bootstrap"},
["local notes"],
),
(
"declared-name match is case-insensitive",
"# AGENTS\n\n## fleet BOOTSTRAP\n\nText.\n",
{"fleet bootstrap"},
[],
),
(
"an un-normalized declared set (mixed case, untrimmed) is normalized here, not trusted",
"# AGENTS\n\n## Fleet Bootstrap\n\nText.\n",
{" Fleet Bootstrap "},
[],
),
(
"an H1 title and a nested H3 are not judged, only H2",
"# Local Notes\n\n## Fleet Bootstrap\n\n### Local Notes\n\nText.\n",
{"fleet bootstrap"},
[],
),
(
"a fenced sample showing heading syntax is not a real heading",
"# AGENTS\n\n## Fleet Bootstrap\n\n```\n## Local Notes\n```\n",
{"fleet bootstrap"},
[],
),
(
"a repo's own local content, undeclared, is flagged even in a file with its own declared sections",
(
"# Copilot Instructions\n\n## GitHub Copilot Review Runbook\n\nText.\n\n"
"## Development Workflow\n\nLocal build and test steps.\n\n"
"## Command Line Usage\n\nLocal CLI reference.\n"
),
{"github copilot review runbook"},
["command line usage", "development workflow"],
),
]
uh_ok = True
for label, doc, declared, want in uh:
got = undeclared_h2_headings(doc, declared)
if got != want:
ok = uh_ok = False
print(f" FAIL undeclared-heading: {label} (expected {want}, got {got})")
if uh_ok:
print(f" ok undeclared-heading: {len(uh)} cases, H2-only, case-insensitive, fence-aware")

# Issue generator: findings land in the right buckets and the title carries the count.
fe = {"name": "Widget", "types": ["python"]}
it, ib = render_issue(
Expand Down
2 changes: 2 additions & 0 deletions spec/section-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ A repo that carried its governance inside `AGENTS.md` before the router split ho

`files.json` declares each section's fidelity. [validate.py][validate] proves every declared section resolves to a real level-two heading in the hub's own copy of the file that declares it, so a renamed or mistyped section cannot silently stop being checked. [audit.py][audit] checks each repo's copy (presence for `intent`, byte-match for `verbatim`) and classifies a mismatch as stale (re-vendor) or modified (review).

The undeclared-heading advisory above also runs against `.github/copilot-instructions.md`, not only `AGENTS.md` and `GOVERNANCE.md`, since that file has its own declared sections in `files.json` and is where repo-specific content has accumulated undetected before. It names the heading as undeclared and points at this doc's destinations, and it does not attempt to name which destination a given heading belongs in, since neither `OPERATIONS.md`'s six headings nor `ARCHITECTURE.md`'s are declared anywhere as data, and matching by heading name would miss content filed under a differently worded heading regardless.
Comment thread
ptr727 marked this conversation as resolved.

<!-- Internal -->

[agents]: ../AGENTS.md
Expand Down