#!/usr/bin/env python3 from __future__ import annotations import argparse import csv import datetime as _dt import hashlib import json import os import re import subprocess import textwrap import uuid from dataclasses import dataclass from pathlib import Path from urllib.parse import urlparse @dataclass(frozen=True) class DayConfig: day: str edition: str hashtag: str source_url: str def _repo_root() -> Path: return Path(__file__).resolve().parents[2] def _run(cmd: list[str], *, cwd: Path | None = None, env: dict[str, str] | None = None) -> subprocess.CompletedProcess[str]: return subprocess.run( cmd, cwd=str(cwd) if cwd else None, env=env, check=True, capture_output=True, text=True, ) def _sha256_file(path: Path) -> str: h = hashlib.sha256() with path.open("rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest() def _sha256_text(text: str) -> str: return hashlib.sha256(text.encode("utf-8", errors="replace")).hexdigest() def _write_sha256_sidecar(path: Path) -> None: path.with_suffix(path.suffix + ".sha256").write_text(_sha256_file(path) + "\n", encoding="utf-8") def _utc_now() -> str: return _dt.datetime.now(tz=_dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z") def _download(url: str, dest: Path) -> None: dest.parent.mkdir(parents=True, exist_ok=True) _run( [ "curl", "-fsSL", "-L", "--retry", "3", "--retry-delay", "1", "-o", str(dest), url, ] ) def _guess_ext(url: str, default: str = ".pdf") -> str: try: path = urlparse(url).path or "" except Exception: path = "" ext = Path(path).suffix.lower() if ext in {".pdf", ".md", ".txt"}: return ext return default def _revoice_env() -> dict[str, str]: repo_root = _repo_root() env = dict(os.environ) env["PYTHONPATH"] = str(repo_root / "src") return env def _revoice_extract(*, input_path: Path, output_txt: Path) -> None: repo_root = _repo_root() _run( ["python3", "-m", "revoice", "extract", "--input", str(input_path), "--output", str(output_txt)], cwd=repo_root, env=_revoice_env(), ) def _revoice_generate(*, style: str, input_path: Path, output_md: Path) -> None: repo_root = _repo_root() _run( ["python3", "-m", "revoice", "generate", "--style", style, "--input", str(input_path), "--output", str(output_md)], cwd=repo_root, env=_revoice_env(), ) def _revoice_preflight(*, style: str, md_path: Path, source_path: Path) -> str: repo_root = _repo_root() proc = subprocess.run( ["python3", "-m", "revoice", "preflight", "--style", style, "--input", str(md_path), "--source", str(source_path)], cwd=str(repo_root), env=_revoice_env(), capture_output=True, text=True, ) if proc.returncode == 0: return "" if proc.returncode == 2: return (proc.stderr or proc.stdout or "").strip() raise RuntimeError(f"revoice preflight failed (code {proc.returncode}): {(proc.stderr or proc.stdout or '').strip()}") def _extract_first_claim(md: str) -> str: for line in md.splitlines(): m = re.match(r"^- The source claims: [“\"](?P.+?)[”\"]\s*$", line.strip()) if m: claim = m.group("q").strip() if len(claim) > 160: return claim[:157].rstrip() + "…" return claim return "" def _extract_first_dave_factor(md: str) -> str: for line in md.splitlines(): m = re.match(r"^>\s*\*\*The Dave Factor:\*\*\s*(?P.+?)\s*$", line.strip()) if m: text = m.group("t").strip() if len(text) > 180: return text[:177].rstrip() + "…" return text return "" def _sponsor_bumper(day_key: str) -> str: variants = [ "This episode brought to you by the exception half-life: temporary becomes permanent without automated expiry.", "Underwritten by the laws of incentives: dashboards observe, gates enforce. See verifiable traces at https://infrafabric.io", "Sponsored by operational realism: the roadmap is not the territory.", "A message from the gating problem: visibility without stop conditions is theater.", "This critique made possible by InfraFabric Red Team — publishing the gates your org must own. https://infrafabric.io", ] digest = hashlib.sha256(day_key.encode("utf-8", errors="replace")).digest() return variants[int.from_bytes(digest[:2], "big") % len(variants)] def _write_marketing( *, out_path: Path, day: DayConfig, next_day: DayConfig | None, base_url: str, source_url: str, dossier_md: str, stamp_square_url: str, hero_url: str, ) -> None: day_upper = day.day.upper() next_label = f"{next_day.day.upper()} — {next_day.edition} {next_day.hashtag}" if next_day else "Next week: new drops." dave_factor = _extract_first_dave_factor(dossier_md) or "The control drifts into a status update, and the status update becomes the control." claim = _extract_first_claim(dossier_md) or "(no short claim extracted)" lines = [ f"# Thread Pack — {day_upper} ({day.edition} Edition)", "", f"- Hashtag: {day.hashtag}", "- Schedule: 6:45 AM EST main drop (promo 6:00 AM; next-on 8:00 PM prior)", "", "## Post 0 — Next On (previous evening, 8:00 PM EST)", "", f"Tomorrow: {next_label}", "", "## Post 1 — Pre-Show Promo (6:00 AM EST)", "", f"{day.hashtag} EYES ONLY // DAVE", "", dave_factor, "", f"Stamp: {stamp_square_url}", f"Hero: {hero_url}", "", "## Post 2 — Main Thread (6:45 AM EST)", "", f"Shadow Dossier — {day.edition} Edition {day.hashtag}", "", f"Source: {source_url}", f"Full pack: {base_url}/{day.day}.pack.md", "", "## Post 3 — The Source Claims (quote-budget)", "", f"- The source claims: “{claim}”", "", "## Post 4 — Sponsor Bumper (mid-thread)", "", _sponsor_bumper(day.day), "", "## Post 5 — The Gate (Action Pack tease)", "", "Gate: Governance", 'Stop condition: No "rolled out" without an owner, a gate, and an expiry.', "", "## Post 6 — Trace + Download", "", f"Trace: {base_url}/{day.day}.trace.json", f"Shadow (md): {base_url}/{day.day}.shadow.md", "", "## Post 7 — Next Day Tease (end of thread)", "", f"Tomorrow 6:45 AM: {next_label}" if next_day else "Next: new drops.", "", ] out_path.write_text("\n".join(lines).strip() + "\n", encoding="utf-8") def _write_pack( *, out_path: Path, day: DayConfig, next_day: DayConfig | None, base_url: str, source_url: str, shadow_url: str, trace_url: str, marketing_url: str, trace_json: dict, marketing_md: str, shadow_md: str, ) -> None: next_link = f"{base_url}/{next_day.day}.pack.md" if next_day else "" lines = [ f"# InfraFabric External Review Pack — {day.day.upper()} ({day.edition} Edition)", "", "This is a single-file bundle intended for review environments that cannot reliably fetch multiple URLs.", "", "## Links", "", f"- Source: {source_url}", "", f"- Shadow dossier (download Markdown): {shadow_url}", "", f"- Trace (JSON): {trace_url}", "", f"- Marketing thread pack: {marketing_url}", "", f"- Pack (this file): {base_url}/{day.day}.pack.md", ] if next_link: lines.extend(["", f"- Coming next: {next_link}", ""]) else: lines.append("") lines.extend( [ "", "## Review instructions (portable)", "", "Hard rules:", "", "1) 100% factual: tag every non-trivial claim as [SOURCE]/[DOSSIER]/[TRACE]/[INFERENCE].", "", "2) Vendor-neutral: critique deployment conditions + org behaviors, not vendor intent/competence.", "", "3) Mirror discipline: follow the dossier’s section order; do not invent a new outline.", "", "", "Deliverables:", "", "A) 5–10 bullets: what works / what doesn’t (tag each)", "", "B) Scorecard (0–5): mirror integrity, layout fidelity, humor discipline, mermaid value, trace/demo value, CTA stealth", "", "C) Section-by-section critique (mirror headings): what’s mirrored, what’s missing, what feels templated/repeated", "", "D) Vendor-safe conclusion rewrite: success conditions / traps / questions-to-ask-vendor", "", "E) Patch suggestions (actionable): unified diffs preferred against bible + generator", "", "", "## Trace", "", "```json", "", json.dumps(trace_json, indent=2, sort_keys=False), "", "```", "", "", "## Marketing thread pack", "", "```markdown", "", marketing_md.strip(), "", "```", "", "", "## Shadow dossier (Markdown)", "", "```markdown", "", shadow_md.strip(), "", "```", "", ] ) out_path.write_text("\n".join(lines).strip() + "\n", encoding="utf-8") def _write_week_index(*, out_path: Path, week_title: str, base_url: str, days: list[DayConfig], source_links: dict[str, str]) -> None: lines = [ f"# InfraFabric External Review Pack — Week ({week_title})", "", f"This is the week bundle for **IF.DAVE.BIBLE {week_title}**. Review one day at a time.", "", f"Base: {base_url}/", "", "", "## Days", "", "| Day | Edition | Pack | Marketing | Shadow | Trace | Source |", "| --- | --- | --- | --- | --- | --- | --- |", ] for d in days: day_upper = d.day.upper() lines.append( "| " + " | ".join( [ day_upper, d.edition, f"{base_url}/{d.day}.pack.md", f"{base_url}/{d.day}.marketing.md", f"{base_url}/{d.day}.shadow.md", f"{base_url}/{d.day}.trace.json", source_links.get(d.day, d.source_url), ] ) + " |" ) lines.extend( [ "", "## Full offline week bundle", "", f"- Full week single-file pack: {base_url}/week.pack.md", "", ] ) out_path.write_text("\n".join(lines).strip() + "\n", encoding="utf-8") def _render_recap_source(*, base_url: str, days: list[DayConfig], highlight_by_day: dict[str, str]) -> str: rows = [] for d in days: if d.day in {"sat", "sun"}: continue rows.append(f"| {d.day.title()} | {d.edition} | {base_url}/{d.day}.pack.md | {base_url}/{d.day}.trace.json |") highlights = [] for d in days: if d.day in {"sat", "sun"}: continue sting = highlight_by_day.get(d.day, "") if sting: highlights.append(f"- **{d.edition}:** {sting}") if not highlights: highlights.append("- (highlights unavailable)") return "\n".join( [ "# Shadow Dossier — Weekly Recap Edition", "", "This recap aggregates the week’s drops (Mon–Fri) into one “what mattered / what broke / what to steal for Monday” artifact.", "", "## Week lineup (links)", "", "| Day | Edition | Pack | Trace |", "| --- | --- | --- | --- |", *rows, "", "## Highlights (one-line stings)", "", *highlights, "", "## What to steal (portable)", "", "1. Replace manual evidence with machine-verifiable signals (event type + emitter + freshness window).", "2. Treat exceptions as architecture unless auto-expiry is enforced.", "3. Never accept “rolled out” without opt-in/opt-out + stop conditions.", "", "## Poll (optional)", "", "Which failure mode hurts most in your org?", "- A) Evidence theater (screenshots/certs)", "- B) Exception creep", "- C) Dashboard storytelling", "- D) “Pilot” that never ends", "", ] ).strip() def _read_days_tsv(path: Path) -> list[DayConfig]: rows: list[DayConfig] = [] with path.open("r", encoding="utf-8") as f: reader = csv.DictReader(f, delimiter="\t") for row in reader: day = (row.get("day") or "").strip().lower() edition = (row.get("edition") or "").strip() hashtag = (row.get("hashtag") or "").strip() source_url = (row.get("source_url") or "").strip() if not day or not edition or not hashtag or not source_url: raise ValueError(f"Invalid row in {path}: {row}") rows.append(DayConfig(day=day, edition=edition, hashtag=hashtag, source_url=source_url)) return rows def main() -> int: p = argparse.ArgumentParser() p.add_argument("--days", required=True, help="TSV with columns: day, edition, hashtag, source_url") p.add_argument("--out", required=True, help="Output directory (build artifacts)") p.add_argument("--style", default="if.dave.v1.9", help="Dave style id (default: if.dave.v1.9)") p.add_argument("--base-url", required=True, help="Published base URL for week packs (no trailing slash)") p.add_argument("--source-prefix", default="https://infrafabric.io/static/source/", help="Where sources will be hosted") p.add_argument( "--note", default="static_week_v19", help="Trace note field (default: static_week_v19)", ) p.add_argument( "--stamp-square-url", default="https://infrafabric.io/static/hosted/review/assets/eyes-only/red-ream-600-600.png", help="Canonical square stamp image URL", ) p.add_argument( "--hero-url", default="https://infrafabric.io/static/hosted/review/assets/eyes-only/red-team-doc-1024-559.jpg", help="Canonical hero image URL", ) args = p.parse_args() out_dir = Path(args.out).resolve() sources_dir = out_dir / "sources" build_dir = out_dir / "build" build_dir.mkdir(parents=True, exist_ok=True) sources_dir.mkdir(parents=True, exist_ok=True) days = _read_days_tsv(Path(args.days)) by_day = {d.day: d for d in days} ordered = [by_day[k] for k in ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] if k in by_day] if len(ordered) != 7: raise SystemExit("Expected 7 days (mon..sun) in TSV") source_links: dict[str, str] = {} highlight_by_day: dict[str, str] = {} # First pass: download/generate sources (except recap), create shadow, trace, marketing, pack. for idx, day in enumerate(ordered): next_day = ordered[idx + 1] if idx + 1 < len(ordered) else None if day.source_url.upper() == "GENERATE": continue ext = _guess_ext(day.source_url, default=".pdf") src_path = sources_dir / f"{day.day}{ext}" _download(day.source_url, src_path) src_sha = _sha256_file(src_path) source_links[day.day] = f"{args.source_prefix}{src_sha}{ext}" # Keep extracted text for debugging (PDF only). if ext == ".pdf": _revoice_extract(input_path=src_path, output_txt=sources_dir / f"{day.day}.txt") shadow_path = build_dir / f"{day.day}.shadow.md" _revoice_generate(style=args.style, input_path=src_path, output_md=shadow_path) warnings = _revoice_preflight(style=args.style, md_path=shadow_path, source_path=src_path) out_sha = _sha256_file(shadow_path) trace = { "id": str(uuid.uuid4()), "status": "done", "createdAt": _utc_now(), "day": day.day, "edition": day.edition, "hashtag": day.hashtag, "style": args.style, "sourceSha256": src_sha, "outputSha256": out_sha, "warnings": warnings, "note": args.note, } trace_path = build_dir / f"{day.day}.trace.json" trace_path.write_text(json.dumps(trace, indent=2, sort_keys=False) + "\n", encoding="utf-8") shadow_md = shadow_path.read_text(encoding="utf-8", errors="replace") if day.day in {"mon", "tue", "wed", "thu", "fri"}: highlight_by_day[day.day] = _extract_first_dave_factor(shadow_md) marketing_path = build_dir / f"{day.day}.marketing.md" _write_marketing( out_path=marketing_path, day=day, next_day=next_day, base_url=args.base_url, source_url=source_links[day.day], dossier_md=shadow_md, stamp_square_url=args.stamp_square_url, hero_url=args.hero_url, ) pack_path = build_dir / f"{day.day}.pack.md" _write_pack( out_path=pack_path, day=day, next_day=next_day, base_url=args.base_url, source_url=source_links[day.day], shadow_url=f"{args.base_url}/{day.day}.shadow.md", trace_url=f"{args.base_url}/{day.day}.trace.json", marketing_url=f"{args.base_url}/{day.day}.marketing.md", trace_json=trace, marketing_md=marketing_path.read_text(encoding="utf-8"), shadow_md=shadow_md, ) # Build recap source for SAT, then run it through the same pipeline. recap = by_day.get("sat") if recap: recap_src = sources_dir / "sat.md" recap_src.write_text( _render_recap_source(base_url=args.base_url, days=ordered, highlight_by_day=highlight_by_day) + "\n", encoding="utf-8" ) recap_sha = _sha256_file(recap_src) source_links["sat"] = f"{args.source_prefix}{recap_sha}.md" shadow_path = build_dir / "sat.shadow.md" _revoice_generate(style=args.style, input_path=recap_src, output_md=shadow_path) warnings = _revoice_preflight(style=args.style, md_path=shadow_path, source_path=recap_src) out_sha = _sha256_file(shadow_path) trace = { "id": str(uuid.uuid4()), "status": "done", "createdAt": _utc_now(), "day": "sat", "edition": recap.edition, "hashtag": recap.hashtag, "style": args.style, "sourceSha256": recap_sha, "outputSha256": out_sha, "warnings": warnings, "note": args.note, } trace_path = build_dir / "sat.trace.json" trace_path.write_text(json.dumps(trace, indent=2, sort_keys=False) + "\n", encoding="utf-8") shadow_md = shadow_path.read_text(encoding="utf-8", errors="replace") marketing_path = build_dir / "sat.marketing.md" _write_marketing( out_path=marketing_path, day=recap, next_day=by_day.get("sun"), base_url=args.base_url, source_url=source_links["sat"], dossier_md=shadow_md, stamp_square_url=args.stamp_square_url, hero_url=args.hero_url, ) pack_path = build_dir / "sat.pack.md" _write_pack( out_path=pack_path, day=recap, next_day=by_day.get("sun"), base_url=args.base_url, source_url=source_links["sat"], shadow_url=f"{args.base_url}/sat.shadow.md", trace_url=f"{args.base_url}/sat.trace.json", marketing_url=f"{args.base_url}/sat.marketing.md", trace_json=trace, marketing_md=marketing_path.read_text(encoding="utf-8"), shadow_md=shadow_md, ) # Week index + full pack. m = re.search(r"(v\\d+(?:\\.\\d+)*)", args.style) week_title = m.group(1) if m else args.style index_path = build_dir / "index.md" _write_week_index(out_path=index_path, week_title=week_title, base_url=args.base_url, days=ordered, source_links=source_links) week_pack_path = build_dir / "week.pack.md" body_parts = [ "# InfraFabric External Review Pack — Full Week (v1.9)", "", "This file embeds all daily packs for sandboxed review environments. Review one day at a time.", "", f"Index: {args.base_url}/index.md", "", "---", "", ] for d in ordered: pack_file = build_dir / f"{d.day}.pack.md" if not pack_file.exists(): continue body_parts.append(f"## {d.day.upper()} ({d.edition} Edition)") body_parts.append("") body_parts.append(pack_file.read_text(encoding="utf-8", errors="replace").strip()) body_parts.append("") body_parts.append("---") body_parts.append("") week_pack_path.write_text("\n".join(body_parts).strip() + "\n", encoding="utf-8") # Hash sidecars for everything in build dir. for pth in sorted(build_dir.iterdir()): if pth.is_file() and not pth.name.endswith(".sha256"): _write_sha256_sidecar(pth) # Write resolved source manifest for publishing. manifest = out_dir / "source_manifest.json" manifest.write_text(json.dumps({"sources": source_links}, indent=2, sort_keys=True) + "\n", encoding="utf-8") return 0 if __name__ == "__main__": raise SystemExit(main())