Add scripts/check-okf.py: enforces the OKF v0.1 hard rules the repo had been carrying by convention — parseable frontmatter with type/title/ description/timestamp, description shape (length, not-equals-title, no restated quantity), the reserved-name and index.md rules, log.md's date heading shape (bare ISO date, one entry per date, newest first), the 500-line hint, and the doubled-page signature (a second `type:` key in the body). Citations are reported as a soft tally, not a failure. The 500-line ceiling is escapable two ways, both explainable: `okf-exempt: length` in a page's own frontmatter, and pages derived as one-per- endpoint — live endpoints from scripts/, retired-endpoint era pages from their tag (AGENTS.md lets a campaign ledger live on the page that measured it). scripts/gen-vault-index.py: sibling-presence was tested as a bare stem substring, so a page whose name is a prefix of another page's name (`frank-medium` inside `frank-medium-rocmfpx`) silently never got an index row. Test the wikilink form instead. scripts/check-vault-links.py: `.AGENTTRASH` is deleted-but-held state, not vault content. It is now excluded from both the scan set and the target index, so a live link into the trash reads as unresolved instead of resolved. tests/docs_completeness.bats: the same exclusion in both page walks — trashed pages need no index row, and a trashed page must not satisfy the docs requirement for a live compose service. Adds the OKF conformance and index-freshness tests. scripts/port-census.py: the port-before-cut orphan census used to prove a merge lost no fact, kept so the next collapse can re-run it.
79 lines
2.6 KiB
Python
Executable File
79 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Port census: prove a merge/collapse lost no fact.
|
|
|
|
For every line that existed in the OLD set and not in the NEW set, check that
|
|
each of its significant tokens (numbers, long identifiers, paths) still occurs
|
|
somewhere in the NEW corpus. A line whose every token survives is a reworded
|
|
line; a line with an unheld token is a candidate fact loss and gets printed.
|
|
|
|
port-census.py <new-glob-dir> <old-file> [<old-file> ...]
|
|
|
|
Old files may be `git-rev:path` (read from git) or a path (e.g. a trash copy).
|
|
"""
|
|
import pathlib
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
def toks(line):
|
|
out = []
|
|
for n in re.findall(r"\d[\d,]*(?:\.\d+)?", line):
|
|
if len(n.replace(",", "").split(".")[0]) >= 3 or (
|
|
"." in n and len(n.split(".")[-1]) >= 2 and len(n.replace(",", "")) >= 4
|
|
):
|
|
out.append(n)
|
|
# Identifiers, flags, paths and backticked spans are the facts worth
|
|
# tracking; ordinary prose words are not — they reword on every rewrite.
|
|
out += [w for w in re.findall(r"[A-Za-z][\w./:-]{7,}", line)
|
|
if re.search(r"[\d_.:/-]", w) or w.isupper()]
|
|
out += [s.strip() for s in re.findall(r"`([^`]+)`", line) if len(s) > 3]
|
|
return out
|
|
|
|
|
|
def held(tok, corpus):
|
|
t = tok.replace(",", "")
|
|
alt = rf"\b{re.escape(t)}\b"
|
|
for c in corpus:
|
|
if tok in c or t in c:
|
|
return True
|
|
if re.search(alt, c.replace(",", "")):
|
|
return True
|
|
return False
|
|
|
|
|
|
def main(argv):
|
|
new_dir = pathlib.Path(argv[1])
|
|
corpus = [
|
|
p.read_text(errors="replace")
|
|
for p in new_dir.rglob("*.md")
|
|
if ".AGENTTRASH" not in str(p) and ".obsidian" not in str(p)
|
|
]
|
|
blob = "\n".join(corpus)
|
|
lost = 0
|
|
checked = 0
|
|
for spec in argv[2:]:
|
|
if spec.startswith("git-"):
|
|
ref, path = spec[4:].split(":", 1)
|
|
old = subprocess.run(["git", "show", f"{ref}:{path}"],
|
|
capture_output=True, text=True).stdout
|
|
label = f"{ref}:{path}"
|
|
else:
|
|
old = pathlib.Path(spec).read_text(errors="replace")
|
|
label = spec
|
|
for line in old.splitlines():
|
|
s = line.strip().strip("->*|# ").strip()
|
|
if len(s) < 30 or s.startswith("```"):
|
|
continue
|
|
checked += 1
|
|
missing = [t for t in toks(s) if not held(t, [blob])]
|
|
if missing:
|
|
lost += 1
|
|
print(f" {label}\n L: {s[:150]}\n unheld: {', '.join(missing[:6])}")
|
|
print(f"{checked} old lines checked, {lost} with unheld tokens")
|
|
return 1 if lost else 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|