`reach wiki stats` and `reach wiki gttr-hook` replace tooling/db/wiki_sync.py and populate_gttr_hook.py. Both are output-identical to the originals: `stats` byte-for-byte, and all 301 extracted GTTR hooks line-for-line. wiki_sync.py moved whole, but generate_wiki() and import_from_wiki() are NOT verbs. Before porting, the old `--generate` was run against a clean tree to get a parity baseline. It changed all 301 system pages, +940 / -10,761, and was reverted at once. It deletes the Celestial Bodies / Stations blocks (owned by the Rust atlas sync, which it does not know about), deletes the Industries / Exports / Imports rows (nothing writes those any more), and rewrites star types where systems.db and the pages disagree. D-262, CLAUDE.md and the wiki skill all described it as the routine, prose-preserving render. CLAUDE.md and the skill now say not to run it; D-262 needs amending — T-1292. Provenance moves to tooling/archive/, with a README naming what each script did and why it is not run: - pql-migrate/ (the T-1271 ruling) - wiki-bootstrap/: assign-astro-ids + its catalog, migrate-s-to-gj, patch-core-sector (hardcodes a dead path), fill-missing-globes, generate-stubs and find-stubs (finds 0 stubs — Phase 1 is done), backfill_cultural_corridor (a raw systems.db patch script, outside D-262), and process-wiki-system-changes, whose last step is the destructive render Also: - stats() printed "run import first" and exited 0 when a table was missing; it now fails with a remedy. generate_wiki() counted created pages after writing them, so `created` was always 0. - tooling/godot-cold-parse and godot-parse-sweep were never retired after T-1283, and the pr-process skill still told agents to run them. Removed; the skill and parse_sweep.gd now name the reach verbs. - systems.db re-stamped: schema comments changed, and the stamp records the schema file's SHA for tamper detection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
2.8 KiB
Python
77 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Add decision_refs provenance frontmatter to workshop outcomes (pql migration, Phase 5).
|
|
|
|
Workshop -> decision provenance was prose-only. This injects a `decision_refs:` YAML
|
|
frontmatter list (the confirmed D-records each workshop-outcomes.md touches) so pql can
|
|
answer "which workshop produced D-NNN" via
|
|
pql query "SELECT name, fm.workshop WHERE fm.decision_refs CONTAINS 'D-238'"
|
|
|
|
decision_refs = distinct valid confirmed-D ids mentioned in the file body (filtered
|
|
against the governance decision set, so typos/multi-refs/stale ids are dropped). Files
|
|
without frontmatter get a minimal block. Idempotent: re-running rewrites the same line.
|
|
|
|
Default is a dry run. Pass --apply to write.
|
|
"""
|
|
import glob
|
|
import os
|
|
import re
|
|
import sqlite3
|
|
import subprocess
|
|
import sys
|
|
|
|
APPLY = "--apply" in sys.argv
|
|
# Derive the repo root from git so the script targets the checkout it's run from.
|
|
REPO = subprocess.check_output(["git", "rev-parse", "--show-toplevel"], text=True).strip()
|
|
DB = f"{REPO}/.pql/pql.db"
|
|
|
|
D_IDS = set(r[0] for r in sqlite3.connect(DB).execute(
|
|
"SELECT id FROM decisions WHERE type='confirmed'"))
|
|
|
|
D_REF = re.compile(r"\bD-\d{3}\b")
|
|
FILES = sorted(glob.glob(f"{REPO}/docs/workshops/*/workshop-outcomes.md"))
|
|
|
|
|
|
def split_frontmatter(text):
|
|
"""Return (fm_lines, body) where fm_lines excludes the --- fences; fm is None if absent."""
|
|
if text.startswith("---\n"):
|
|
end = text.find("\n---\n", 4)
|
|
if end != -1:
|
|
return text[4:end].splitlines(), text[end + 5:]
|
|
return None, text
|
|
|
|
|
|
def refs_in(body):
|
|
return sorted({m for m in D_REF.findall(body) if m in D_IDS}, key=lambda d: int(d[2:]))
|
|
|
|
|
|
def main():
|
|
print(f"=== add_workshop_provenance.py ({'APPLY' if APPLY else 'DRY-RUN'}) ===\n")
|
|
for f in FILES:
|
|
text = open(f, encoding="utf-8").read()
|
|
fm, body = split_frontmatter(text)
|
|
refs = refs_in(body if fm is not None else text)
|
|
rel = os.path.relpath(f, REPO)
|
|
wname = os.path.basename(os.path.dirname(f))
|
|
refs_yaml = "[" + ", ".join(refs) + "]"
|
|
|
|
if fm is not None:
|
|
fm = [ln for ln in fm if not ln.startswith("decision_refs:")]
|
|
fm.append(f"decision_refs: {refs_yaml}")
|
|
new = "---\n" + "\n".join(fm) + "\n---\n" + body
|
|
action = f"inject decision_refs ({len(refs)})"
|
|
else:
|
|
new = (f"---\ntitle: \"Workshop Outcomes: {wname}\"\ntype: workshop\n"
|
|
f"workshop: {wname}\nstatus: archived\ndecision_refs: {refs_yaml}\n---\n\n" + text)
|
|
action = f"ADD frontmatter + decision_refs ({len(refs)})"
|
|
|
|
print(f"{rel}: {action}")
|
|
if refs:
|
|
print(f" {refs_yaml}")
|
|
if APPLY:
|
|
open(f, "w", encoding="utf-8").write(new)
|
|
print("\nDone." + ("" if APPLY else " (dry run — pass --apply)"))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|