mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
Per the 2026-09-19 adversarial pre-ship review (findings.md M-192): - d318watchdog.c: one-shot per OCCURRENCE, not per process -- re-arm after 600 ticks of signature absence so a Facility replay in the same session (AllUnlocked) is still covered. - model.c: RULE-2-SIGNOFF records for the retained M-183/M-185 and D311 defensive clamps (user-approved retention as the stability-maximizing option; stale 'Threshold: 50.0' comment corrected to 10.0f; both marked post-release removal candidates pending full cutscene re-verification). - reference/mouse-injector/: removed from the public repo (GPLv2 source + prebuilt discord-rpc.lib in an MIT project; never compiled here). Provenance stub README remains; path gitignored for local re-vendoring. AGENTS.md / GEPD-INPUT-PLAN.md / optionsoverlay.c refs updated. - scratch/: untracked + gitignored (19 session logs/probe sources were committed in the release window). - findings-index.csv: D317 (and 5 other stale rows) now CLOSED -- added CLOSED to gen_findings_index.py's verdict vocabulary. - docs: D310 perf gauge (no change warranted), GE_DEBUG_UNLOCKALL noted in security-and-fidelity-status.md, release notes gain the missing D318 watchdog bullet + FOV-clamp clarification. Build verified: ./build-pc.sh ntsc-final clean; gen_findings_index.py --check and gen_env_probes.py --check pass.
171 lines
5.5 KiB
Python
171 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
||
"""Generate docs/dev/findings-index.csv from the §F/§H tables in findings.md.
|
||
|
||
Agentic speed-ups plan Step 4 / R8: findings.md is ~200 KB. Agents should grep
|
||
this CSV (label, status, one-liner) to decide *which* Dxx entries to actually
|
||
read, instead of linear-reading the log.
|
||
|
||
Heuristic parser: every markdown table row whose first cell matches a Dxx
|
||
label (`D12`, `D88.4`, `D75 · D77`, `D24-D30`, ...). The topic cell is
|
||
truncated to a one-liner; the status cell is normalised to a short bucket
|
||
(RESOLVED / OPEN / FIXED / PARTIAL / PROPOSED / ?) with the raw text kept.
|
||
|
||
Usage:
|
||
python tools_pc/gen_findings_index.py # write the CSV
|
||
python tools_pc/gen_findings_index.py --check # exit 1 if stale
|
||
python tools_pc/gen_findings_index.py --stdout # print, don't write
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import csv
|
||
import io
|
||
import re
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
SRC = ROOT / "docs" / "dev" / "findings.md"
|
||
OUT = ROOT / "docs" / "dev" / "findings-index.csv"
|
||
|
||
LABEL_RE = re.compile(r"^D\d+(?:\.\d+)?(?:[-–]D\d+)?(?:\s*[·,]\s*D\d+(?:\.\d+)?)*\+?$")
|
||
|
||
STATUS_BUCKETS = [
|
||
("FIXED", re.compile(r"\bfixed\b", re.I)),
|
||
("RESOLVED", re.compile(r"\bresolved\b", re.I)),
|
||
("PARTIAL", re.compile(r"\bpartial|clamp landed|landed\b|safety-net", re.I)),
|
||
("PROPOSED", re.compile(r"\bproposed|not applied|design\b", re.I)),
|
||
("OPEN", re.compile(r"\bopen\b|not started|awaiting|owed|pending", re.I)),
|
||
]
|
||
|
||
|
||
def one_liner(cell: str, limit: int = 160) -> str:
|
||
"""Strip markdown emphasis/code and collapse to a single short line."""
|
||
txt = cell.replace("**", "").replace("`", "")
|
||
txt = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", txt) # [t](u) -> t
|
||
txt = re.sub(r"\s+", " ", txt).strip()
|
||
# first sentence-ish
|
||
m = re.match(r"(.{20,}?[.—:])\s", txt)
|
||
if m and len(m.group(1)) <= limit:
|
||
txt = m.group(1)
|
||
if len(txt) > limit:
|
||
txt = txt[: limit - 1].rstrip() + "…"
|
||
return txt
|
||
|
||
|
||
LEAD_RE = re.compile(
|
||
r"^\**\s*(OPEN|RESOLVED|FIXED|PROPOSED|PARTIAL|LANDED|CLAMP|DISPROVEN"
|
||
r"|ROOT-CAUSED|GUARDED|ANALYZED|DIAGNOSTIC SHIPPED|NOT A BUG|CLOSED)",
|
||
re.I,
|
||
)
|
||
|
||
|
||
def bucket(status_cell: str) -> str:
|
||
# An explicit leading verdict token wins over prose keywords deeper in the
|
||
# cell (e.g. "OPEN (parked). ... D168 fixed ..." is OPEN, not FIXED).
|
||
lead = LEAD_RE.match(status_cell.strip())
|
||
if lead:
|
||
tok = lead.group(1).upper()
|
||
return {
|
||
"LANDED": "PARTIAL",
|
||
"CLAMP": "PARTIAL",
|
||
"DISPROVEN": "RESOLVED",
|
||
"ROOT-CAUSED": "RESOLVED",
|
||
"GUARDED": "PARTIAL",
|
||
"ANALYZED": "PARTIAL",
|
||
"DIAGNOSTIC SHIPPED": "PARTIAL",
|
||
"NOT A BUG": "RESOLVED",
|
||
}.get(tok, tok)
|
||
for name, rx in STATUS_BUCKETS:
|
||
if rx.search(status_cell):
|
||
return name
|
||
return "?"
|
||
|
||
|
||
def split_row(line: str) -> list[str] | None:
|
||
line = line.strip()
|
||
if not line.startswith("|"):
|
||
return None
|
||
# split on '|' that is not inside backticks
|
||
cells, buf, tick = [], [], False
|
||
for ch in line:
|
||
if ch == "`":
|
||
tick = not tick
|
||
buf.append(ch)
|
||
elif ch == "|" and not tick:
|
||
cells.append("".join(buf))
|
||
buf = []
|
||
else:
|
||
buf.append(ch)
|
||
cells.append("".join(buf))
|
||
cells = [c.strip() for c in cells]
|
||
# leading/trailing empty from outer pipes
|
||
if cells and cells[0] == "":
|
||
cells = cells[1:]
|
||
if cells and cells[-1] == "":
|
||
cells = cells[:-1]
|
||
return cells or None
|
||
|
||
|
||
def rows():
|
||
section = None
|
||
for raw in SRC.read_text(encoding="utf-8").splitlines():
|
||
h = re.match(r"^#+\s+([A-Z])\.\s", raw) or re.match(r"^#+\s.*§?([FH])\b", raw)
|
||
if raw.startswith("### ") and re.search(r"\b([FH])\.", raw):
|
||
section = re.search(r"\b([FH])\.", raw).group(1)
|
||
cells = split_row(raw)
|
||
if not cells or len(cells) < 2:
|
||
continue
|
||
label = cells[0].replace("–", "-")
|
||
if not LABEL_RE.match(cells[0]):
|
||
continue
|
||
topic = cells[1]
|
||
status = cells[2] if len(cells) > 2 else ""
|
||
yield {
|
||
"label": label,
|
||
"bucket": bucket(status),
|
||
"one_liner": one_liner(topic),
|
||
"status_raw": one_liner(status, 200) if status else "",
|
||
}
|
||
|
||
|
||
def build_csv() -> str:
|
||
buf = io.StringIO()
|
||
w = csv.DictWriter(
|
||
buf, fieldnames=["label", "bucket", "one_liner", "status_raw"], lineterminator="\n"
|
||
)
|
||
w.writeheader()
|
||
seen = set()
|
||
for r in rows():
|
||
key = r["label"]
|
||
if key in seen:
|
||
continue
|
||
seen.add(key)
|
||
w.writerow(r)
|
||
return buf.getvalue()
|
||
|
||
|
||
def main() -> int:
|
||
args = set(sys.argv[1:])
|
||
text = (
|
||
"# GENERATED by tools_pc/gen_findings_index.py -- do not hand-edit.\n"
|
||
"# Grep this to pick which findings.md entries to read; it is not authoritative.\n"
|
||
+ build_csv()
|
||
)
|
||
if "--stdout" in args:
|
||
sys.stdout.write(text)
|
||
return 0
|
||
want = text.encode("utf-8")
|
||
if "--check" in args:
|
||
cur = OUT.read_bytes() if OUT.exists() else b""
|
||
if cur != want:
|
||
sys.stderr.write("findings-index.csv is stale: re-run gen_findings_index.py\n")
|
||
return 1
|
||
return 0
|
||
OUT.write_bytes(want)
|
||
print(f"wrote {OUT.relative_to(ROOT)} ({text.count(chr(10))} lines)")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|