Files
command-center/scripts/kb-coverage.py
T
Frank 4849f46eb2 feat(kb-gates): one work log per bundle, and the quarantine is not bundle content
The bundle had two work logs. `core/log.md` was created "for tracking core directory
modifications" and holds two entries of pure migration bookkeeping (file moved,
directory created, log created) — every one recoverable from `git log` — plus a date
for the OKF conversion (2026-07-15) that matches neither the bundle log (2026-01-15)
nor git history, where the OKF rebuild lands 2026-08-28. `core/index.md` was still
linking it, which is exactly how a stale page stays in circulation. kb-lint now reports
any `log.md` that is not the bundle root's; the page moved to omp-docs/core/.AGENTTRASH/
per the trash convention, and the core index points one level up.

Both gates also walked `.AGENTTRASH/`, so trashing a page turned them red: kb-lint
demanded frontmatter from a quarantined page and kb-coverage demanded an index for the
trash directory. A gate that only goes green once the quarantined copy is deleted is a
gate that deletes history, so both skip any path containing `.AGENTTRASH` — pinned by a
test in each suite, and kb-coverage finally gets its own test file.
2026-10-20 21:49:15 -07:00

53 lines
2.1 KiB
Python
Executable File

#!/usr/bin/env python3
"""Doctrine check: every dir has index.md; index links every sibling file/dir."""
import re, sys
from pathlib import Path
kb = Path(sys.argv[1])
issues = []
def kept(p):
# `.AGENTTRASH/` is quarantine, not bundle content: a gate that flags it pushes an
# agent to delete a page that is only waiting for a pruning session with the operator.
return ".AGENTTRASH" not in p.parts
dirs = sorted({p.parent for p in kb.rglob("*.md") if kept(p)})
for d in dirs:
idx = d / "index.md"
if not idx.is_file() and d != kb:
issues.append(f"{d.relative_to(kb)}: no index.md")
continue
if not idx.is_file():
continue
text = idx.read_text(errors="replace")
linked = set()
for m in re.finditer(r"\]\((?!https?:|#)([^)#\s]+?)(?:#[^)]*)?\)", text):
t = m.group(1)
target = (d / t).resolve() if not t.startswith("/") else (kb / t.lstrip("/")).resolve()
linked.add(target)
# wikilinks too
wl_names = {m.group(1).strip().rsplit("/", 1)[-1] for m in re.finditer(r"\[\[([^\]|#]+)", text)}
for sib in sorted(d.iterdir()):
if sib.name == ".AGENTTRASH":
continue
if sib.suffix != ".md" or sib.name == "index.md":
continue
if sib.resolve() not in linked and sib.stem not in wl_names:
issues.append(f"{d.relative_to(kb)}/index.md: does not link {sib.name}")
# subdirs of d linked as their index?
for sub in sorted(d.iterdir()):
if sub.name == ".AGENTTRASH":
continue
if sub.is_dir() and (sub / "index.md").is_file():
subidx = (sub / "index.md").resolve()
dirref = sub.resolve()
if subidx not in linked and dirref not in linked:
# also accept link to sub/ or sub/index.md or wikilink to dir name
if not re.search(re.escape(sub.name) + r"[/)]", text):
issues.append(f"{d.relative_to(kb)}/index.md: does not link subdir {sub.name}/")
for i in issues:
print(i)
print(f"index-coverage: {len(issues)} issue(s)")
sys.exit(1 if issues else 0)