"""Extract checkable claims from the management repo's docs -> claims.tsv. Phase 1 only *extracts*; nothing here judges whether a claim is true. Verification happens in Phase 2. A script cannot recognise a claim semantically, so extraction is trigger based: a doc line is emitted when it matches at least one pattern that marks it as asserting something checkable about the world (a component, a count, a status, a version, a path, a date, the mirror topology, an issue). Recall is favoured over precision -- a false positive costs a Phase-2 glance, a false negative loses evidence. Only management-repo docs are scanned: the mandate scopes claims to the management repo's statements about its components. """ import re from common import DOC_SUFFIXES, MGMT_REPO, cell, days_since, last_commit, \ tracked_files, write_tsv COMPONENT_WORDS = ( r"ThreadNet[- ](?:Web|Call|Git|Operating|Server Suite)|threadnet-call|" r"thread-net-git|threadnet-operating|threadnet-web|axion1337\.chat-gitops|" r"game-operating|gameserver|management" ) TRIGGERS = [ ("component-ref", re.compile(COMPONENT_WORDS, re.I)), ("count", re.compile( r"\b(ein|zwei|drei|vier|fünf|sechs|sieben|acht|neun|zehn|\d+)\s+" r"(Repos?|Produkt-Repos?|Projekte?|Issues?|Commits?|Pipelines?|Themes?|" r"Hosts?|Mirrors?)\b", re.I)), ("status", re.compile( r"\b(erledigt|offen|live|aktiv|geschlossen|leer|umgezogen|entfallen|" r"abgelöst|veraltet|überholt|scharf|grün|rot|tabu|kanonisch|" r"zurückgestellt|verifiziert|bereinigt)\b", re.I)), ("version", re.compile(r"\bv?\d+\.\d+\.\d+\b|\b\d+\.\d+\.\d+-[\w.]+\b")), ("path-claim", re.compile(r"`[^`]*(?:/[^`]*|\.(?:md|ya?ml|json|py|ts|toml|crt))`")), ("date-claim", re.compile(r"\b(seit|Stand|bis|am|ab)\s+\d{4}-\d{2}-\d{2}\b", re.I)), ("mirror-topology", re.compile( r"\b(Mirror|Spiegel|gespiegelt|Gitea|rohana|Push-Mirror|kanonisch|Flux-Source)\b", re.I)), ("issue-ref", re.compile(r"(?:#\d+\b|/-/issues/\d+|\b[A-Z]{3,8}-\d{2}\b)")), ] FENCE_RE = re.compile(r"^\s*(```|~~~)") def main(): rows = [] docs = [f for f in tracked_files("management", MGMT_REPO) if any(f.endswith(s) for s in DOC_SUFFIXES)] for src in sorted(docs): iso, _, _ = last_commit("management", MGMT_REPO, src) text = (MGMT_REPO / src).read_text(encoding="utf-8") in_fence = False for lineno, line in enumerate(text.splitlines(), start=1): if FENCE_RE.match(line): in_fence = not in_fence continue stripped = line.strip() if len(stripped) < 12: continue hits = [name for name, rx in TRIGGERS if rx.search(stripped)] if not hits: continue rows.append([ src, lineno, ";".join(hits), "yes" if in_fence else "no", iso, days_since(iso), cell(stripped)[:400], ]) rows.sort(key=lambda r: (r[0], r[1])) write_tsv( "claims.tsv", ["path", "line", "triggers", "in_code_block", "doc_last_commit_date", "doc_days_since_change", "claim_text"], rows, ) if __name__ == "__main__": main()