154 lines
5.1 KiB
Python
154 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Extract every `## Q<n>` block from WORK-QUEUE.md into a queue-ledger table.
|
|
|
|
Ledger (Extract lane) — data extraction, today 2026-10-29.
|
|
|
|
Source: /root/troupe-standup/WORK-QUEUE.md (revision HEAD).
|
|
|
|
Output:
|
|
queue-ledger.tsv q | seq | title | status | state
|
|
queue-ledger.md rendered markdown table
|
|
queue-ledger.json structured + counts
|
|
|
|
Every row is derived from the raw `## Q<n>` heading lines and the first line
|
|
"Status:" (optionally bold "**Status:") inside each block, so the artifact is
|
|
a faithful replay of the file. The CHECK re-parses the source with an
|
|
independent inline parser and diffs the committed TSV.
|
|
"""
|
|
|
|
import csv
|
|
import json
|
|
import re
|
|
from pathlib import Path
|
|
|
|
REPO = Path("/root/troupe-standup")
|
|
SRC = REPO / "WORK-QUEUE.md"
|
|
OUT_DIR = REPO / "wizard-work" / "ledger" / "2026-10-29-queue-ledger"
|
|
|
|
TOP_HEADING = re.compile(r"^## ")
|
|
Q_HEADING = re.compile(r"^## Q(\d+)\s?(.*)$")
|
|
STATUS_LINE = re.compile(r"^\*\*\s*Status\s*[:,]\s*(.*)$")
|
|
|
|
CLOSED_MARKERS = (
|
|
"DONE", "CLOSED", "COMPLETE", "RESOLVED",
|
|
"VERIFIED", "SATISFIED", "RE-SCOPED",
|
|
)
|
|
OPEN_MARKERS = ("OPEN", "OPEN AND", "UNCLAIMED", "AWAITING", "OPEN-AND")
|
|
|
|
|
|
def classify(status_text):
|
|
"""Classify a replayed status line into closed / open / unclassified.
|
|
|
|
Deterministic rules over the verbatim status text only:
|
|
- closed: contains a completion marker (DONE/CLOSED/COMPLETE/RESOLVED/
|
|
VERIFIED/SATISFIED, case-insensitive);
|
|
- open: contains an open marker (OPEN/UNCLAIMED/AWAITING, case-insensitive);
|
|
- otherwise unclassified (e.g. a status that reads as partial or tbd).
|
|
"""
|
|
u = (status_text or "").strip().upper()
|
|
if not u:
|
|
return "unclassified"
|
|
closed = ("DONE" in u) or ("CLOSED" in u) or ("COMPLETE" in u) or (
|
|
"RESOLVED" in u
|
|
) or ("VERIFIED" in u) or ("SATISFIED" in u)
|
|
if closed:
|
|
return "closed"
|
|
open_ = ("OPEN" in u) or ("UNCLAIMED" in u) or ("AWAITING" in u)
|
|
if open_:
|
|
return "open"
|
|
return "unclassified"
|
|
|
|
|
|
def extract_rows(src_path):
|
|
rows = []
|
|
current_q = None
|
|
current_title = None
|
|
status = None
|
|
|
|
def flush():
|
|
nonlocal current_q, current_title, status
|
|
if current_q is not None:
|
|
rows.append({
|
|
"q": current_q,
|
|
"seq": len(rows) + 1,
|
|
"title": current_title,
|
|
"status": status if status is not None else "no Status line",
|
|
"state": classify(status) if status is not None else "unclassified",
|
|
})
|
|
current_q = None
|
|
current_title = None
|
|
status = None
|
|
|
|
for raw in src_path.read_text(encoding="utf-8").splitlines():
|
|
line = raw.rstrip()
|
|
m = Q_HEADING.match(line)
|
|
if m:
|
|
flush()
|
|
current_q = int(m.group(1))
|
|
raw_title = m.group(2).strip()
|
|
if raw_title.startswith("—"):
|
|
raw_title = raw_title[1:].strip()
|
|
current_title = raw_title
|
|
continue
|
|
if TOP_HEADING.match(line):
|
|
flush()
|
|
continue
|
|
if current_q is not None and status is None:
|
|
sm = STATUS_LINE.match(line)
|
|
if sm:
|
|
status = sm.group(1).strip()
|
|
# Drop a stray leading asterisk pair that can survive "**Status".
|
|
if status.startswith("**"):
|
|
status = status[2:].strip()
|
|
flush()
|
|
return rows
|
|
|
|
|
|
def main():
|
|
rows = extract_rows(SRC)
|
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
tsv_path = OUT_DIR / "queue-ledger.tsv"
|
|
with tsv_path.open("w", encoding="utf-8", newline="") as fh:
|
|
w = csv.writer(fh, delimiter="\t")
|
|
w.writerow(["q", "seq", "title", "status", "state"])
|
|
for r in rows:
|
|
w.writerow([r["q"], r["seq"], r["title"], r["status"], r["state"]])
|
|
|
|
json_path = OUT_DIR / "queue-ledger.json"
|
|
counts = {"closed": 0, "open": 0, "unclassified": 0}
|
|
for r in rows:
|
|
counts[r["state"]] += 1
|
|
payload = {
|
|
"source": str(SRC),
|
|
"generated_at": "2026-10-29T16:30:00Z",
|
|
"total_rows": len(rows),
|
|
"counts": counts,
|
|
"rows": rows,
|
|
}
|
|
json_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2),
|
|
encoding="utf-8")
|
|
|
|
md_path = OUT_DIR / "queue-ledger.md"
|
|
with md_path.open("w", encoding="utf-8") as fh:
|
|
fh.write("# Queue ledger\n\n")
|
|
fh.write("Every `## Q<n>` block in `WORK-QUEUE.md` at HEAD, replayed "
|
|
"verbatim from its `Status:` line.\n\n")
|
|
fh.write("| q | title | status | state |\n")
|
|
fh.write("|---|---|---|---|\n")
|
|
for r in rows:
|
|
fh.write(f"| {r['q']} | {r['title']} | {r['status']} | {r['state']} |\n")
|
|
fh.write(f"\n**Total rows: {len(rows)}** "
|
|
f"(closed {counts['closed']}, open {counts['open']}, "
|
|
f"unclassified {counts['unclassified']}).\n")
|
|
|
|
print(f"rows: {len(rows)}")
|
|
print(f"counts: {counts}")
|
|
print(f"wrote {tsv_path}")
|
|
print(f"wrote {json_path}")
|
|
print(f"wrote {md_path}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|