# -*- coding: utf-8 -*-
"""
build_governance.py — projections of the Governance Standard itself.
GOV-F5.6 / F5.7 : the Standard is BOTH a deliverable and a file the AI loads every session.
The deliverable lives in 05_Outputs. Its always-loaded operating SUBSET is a
DERIVED artefact and lives in 01_System.
GOV-F9.8 : a source of truth held in a binary format MUST also exist as a CURRENT
plain-text projection inside the project. An operator with no Office suite must
still be able to read every rule.
GOV-A1.6 : the CORE that must be loaded at the start of every session is Part A, B2, B3,
C1, C3.2, D2, E2, E3, E5, F3, F8 and H1. That is what the core file holds.
Both outputs are GENERATED. Edit neither; replace the .docx and re-run.
"""
import os, re
from docx import Document
from docx.table import Table
from docx.text.paragraph import Paragraph
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
SRC = os.path.join(ROOT, "05_Outputs", "AI_Project_Governance_Standard_v3.7.docx")
TXT = os.path.join(ROOT, "05_Outputs", "AI_Project_Governance_Standard_v3.7.txt")
CORE = os.path.join(ROOT, "01_System", "governance_core.md")
BANNER = (
"GENERATED PROJECTION of 05_Outputs/AI_Project_Governance_Standard_v3.7.docx\n"
"OWNER: 01_System/build_governance.py\n"
"REVIEW: regenerate whenever the .docx changes. Do not hand-edit — replace the .docx and re-run.\n"
"Where this text and the .docx disagree, THE .docx WINS and this projection is a defect (GOV-D4.11).\n"
)
# GOV-A1.6 CORE: the parts that must be loaded at the start of every session.
CORE_PARTS = [
("PART A", "the whole of Part A — application, tailoring, and THE INVIOLABLE RULES"),
("B2.", "problem framing"), ("B3.", "requirements engineering"),
("C1.", "the Master Brain charter"), ("C3.", "mandatory roles and separation of duties"),
("D2.", "the change impact classification"), ("E2.", "independent verification"),
("E3.", "Definition of Done"), ("E5.", "security and safety"),
("F3.", "storage and file discipline"), ("F8.", "standing behavioural rules"),
("H1.", "follow-up is the Master Brain's standing duty"),
]
def extract():
"""Read the .docx in document order, keeping tables as pipe-delimited rows."""
d = Document(SRC)
out, ti = [], 0
for child in d.element.body.iterchildren():
tag = child.tag.split("}")[1]
if tag == "p":
p = Paragraph(child, d)
t = p.text.strip()
if not t:
continue
try:
style = p.style.name or "Normal"
except Exception:
style = "Normal"
if style.startswith("Heading"):
lvl = "#" * (int(style[-1]) if style[-1].isdigit() else 2)
out.append(("H", lvl, t))
else:
out.append(("P", style, t))
elif tag == "tbl":
tb = Table(child, d)
ti += 1
rows = [" | ".join(c.text.strip().replace("\n", " ") for c in r.cells) for r in tb.rows]
out.append(("T", str(ti), rows))
return out
def as_text(items):
lines = [BANNER, "=" * 100, ""]
for kind, meta, body in items:
if kind == "H":
lines += ["", "=" * 100, body.upper(), "=" * 100]
elif kind == "P":
lines.append(body)
else:
lines += ["", " --- TABLE %s ---" % meta]
lines += [" " + r for r in body]
lines += [" --- END TABLE %s ---" % meta, ""]
return "\n".join(lines) + "\n"
def as_core(items):
"""The always-loaded subset. Derived, never hand-maintained.
Two-level state machine: an H1 sets the current PART, an H2 sets the current SECTION.
A block is kept if the current PART is Part A (all of it, including A1/A2/A3), or if the
current SECTION starts with one of the CORE_PARTS prefixes.
"""
core_prefixes = tuple(p for p, _ in CORE_PARTS if p != "PART A")
keep, part, section, on = [], "", "", False
def in_core():
if part.upper().startswith("PART A"):
return True
return section.replace(" ", "").upper().startswith(
tuple(x.replace(" ", "").upper() for x in core_prefixes))
for kind, meta, body in items:
if kind == "H":
head = body.strip()
if meta == "#" or head.upper().startswith("PART "):
part, section = head, ""
else:
section = head
on = in_core()
if on:
keep.append(("H", meta, head))
continue
if on:
keep.append((kind, meta, body))
n_rules = sum(1 for k, _, b in keep if k == "P" and str(b).startswith("GOV-"))
out = [
"# GOVERNANCE CORE — the subset that must be loaded at the start of EVERY session",
"",
"OWNER: `01_System/build_governance.py` ",
"REVIEW: regenerate whenever the Standard changes. Generated file — do not hand-edit (GOV-D4.11).",
"",
"GENERATED from `05_Outputs/AI_Project_Governance_Standard_v3.7.docx` by "
"`01_System/build_governance.py`. Do not hand-edit.",
"",
"GOV-A1.6 requires the CORE to be loaded at the start of every session and confirmed in one line, "
"with the remaining Parts loaded ON DEMAND — Part D in full before changing a baselined item, "
"Part G before building documentation, Part I at delivery. It also forbids relying on having read "
"a Part earlier in a long session: re-read it before acting on it.",
"",
"**The full Standard is the authority. This file is a projection of part of it. "
"Where the two disagree, the .docx wins and this file is a defect.** The complete plain-text "
"projection is at `05_Outputs/AI_Project_Governance_Standard_v3.7.txt`.",
"",
"Rules carried here: **%d**. Parts included: %s" % (
n_rules, "; ".join("%s (%s)" % (p, why) for p, why in CORE_PARTS)),
"",
"---",
"",
]
for kind, meta, body in keep:
if kind == "H":
out += ["", ("## " if meta == "#" else "### ") + body, ""]
elif kind == "P":
out += [body, ""]
else:
out.append("")
for i, r in enumerate(body):
out.append("| " + r + " |")
if i == 0:
out.append("|" + "---|" * (r.count("|") + 1))
out.append("")
return "\n".join(out) + "\n"
if __name__ == "__main__":
if not os.path.exists(SRC):
raise SystemExit("MISSING: %s — the Standard is CI-001 and must be filed in 05_Outputs "
"(GOV-F5.6, GOV-F5.7)." % SRC)
items = extract()
open(TXT, "w", encoding="utf-8", newline="\n").write(as_text(items))
print("wrote", TXT, "(%d KB)" % (os.path.getsize(TXT) // 1024))
open(CORE, "w", encoding="utf-8", newline="\n").write(as_core(items))
print("wrote", CORE, "(%d KB)" % (os.path.getsize(CORE) // 1024))