"""Check an IA/sitemap doc for findability hygiene.
Usage: check_ia.py <ia-doc.md>
Flags any nav level with more than 9 items (Miller's 7±2) unless the doc
documents an explicit exception nearby, and flags any page/section named in
one list (sitemap vs labeling table) but missing from the other.
def bullet_groups(text: str):
for line in text.splitlines():
if re.match(r"^\s*[-*]\s+", line):
current.append(line.strip())
print("Usage: check_ia.py <ia-doc.md>", file=sys.stderr)
text = open(sys.argv[1], encoding="utf-8").read()
groups = bullet_groups(text)
over_limit = [g for g in groups if len(g) > MAX_ITEMS]
has_exception = re.search(r"exception|documented reason|see note", text, re.IGNORECASE)
print(f"⚠️ a nav level has {len(g)} items (>{MAX_ITEMS}) — doc mentions an exception, confirm it covers this level")
print(f"❌ a nav level has {len(g)} items (>{MAX_ITEMS}, Miller's 7±2) with no documented exception")
print(f"✅ no nav level exceeds {MAX_ITEMS} items")
sitemap_match = re.search(r"#+\s*Sitemap.*?\n(.*?)(?:\n#+\s|\Z)", text, re.IGNORECASE | re.DOTALL)
labels_match = re.search(r"#+\s*Label(?:ing)?.*?\n(.*?)(?:\n#+\s|\Z)", text, re.IGNORECASE | re.DOTALL)
if sitemap_match and labels_match:
sitemap_items = set(re.findall(r"[-*]\s+([A-Za-z][\w /-]+)", sitemap_match.group(1)))
label_items = set(re.findall(r"[-*]\s+([A-Za-z][\w /-]+)", labels_match.group(1)))
orphans = sitemap_items - label_items
print(f"❌ page(s) in the sitemap with no labeling-table entry: {sorted(orphans)}")
print("✅ every sitemap entry has a matching labeling-table entry")
print("⚠️ no distinct Sitemap/Labeling sections found — skipped orphan check")
if __name__ == "__main__":