Add 'The Wikis That Were Not Ours' — essay on the collusion.wiki agent incident, in plain English and as a story, with PDF.
This commit is contained in:
1 parent
91483f422e
commit
5690bbbd9d
3 files changed
+488
No files matched your search
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render the 'wikis that were not ours' markdown essay into a PDF.
|
||||
|
||||
Simple, dependency-light: parses the subset of markdown used in the essay
|
||||
(headings, hr, bullet lists, bold/italic inline, paragraphs) and builds it
|
||||
with reportlab. A4, bookish serif (Times), generous leading.
|
||||
"""
|
||||
|
||||
import re
|
||||
import os
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib.units import mm
|
||||
from reportlab.lib.styles import ParagraphStyle
|
||||
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
|
||||
from reportlab.lib.colors import black, HexColor
|
||||
from reportlab.platypus import (
|
||||
SimpleDocTemplate, Paragraph, Spacer, HRFlowable,
|
||||
)
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
INPUT = os.path.join(HERE, "README.md")
|
||||
OUTPUT = os.path.join(HERE, "the-wikis-that-were-not-ours.pdf")
|
||||
|
||||
MARGIN = 22 * mm
|
||||
WIDTH = A4[0] - 2 * MARGIN
|
||||
|
||||
INK = black
|
||||
ACCENT = HexColor("#5b4636") # faded brown, like old book covers
|
||||
|
||||
STYLES = {
|
||||
"title": ParagraphStyle(
|
||||
"Title", fontName="Times-Bold", fontSize=22, leading=28,
|
||||
alignment=TA_LEFT, spaceBefore=4 * mm, spaceAfter=2 * mm, textColor=INK),
|
||||
"subtitle": ParagraphStyle(
|
||||
"Subtitle", fontName="Times-Italic", fontSize=11, leading=16,
|
||||
alignment=TA_LEFT, spaceAfter=6 * mm, textColor=HexColor("#444444")),
|
||||
"h2": ParagraphStyle(
|
||||
"H2", fontName="Times-Bold", fontSize=15, leading=20,
|
||||
spaceBefore=9 * mm, spaceAfter=3 * mm, textColor=INK),
|
||||
"h3": ParagraphStyle(
|
||||
"H3", fontName="Times-Bold", fontSize=12.5, leading=17,
|
||||
spaceBefore=6 * mm, spaceAfter=2 * mm, textColor=INK),
|
||||
"body": ParagraphStyle(
|
||||
"Body", fontName="Times-Roman", fontSize=11, leading=17,
|
||||
alignment=TA_JUSTIFY, spaceAfter=3.2 * mm, textColor=INK),
|
||||
"bullet": ParagraphStyle(
|
||||
"Bullet", fontName="Times-Roman", fontSize=11, leading=16,
|
||||
alignment=TA_LEFT, leftIndent=6 * mm, bulletIndent=1.5 * mm,
|
||||
spaceAfter=1.6 * mm, textColor=INK),
|
||||
"colophon": ParagraphStyle(
|
||||
"Colophon", fontName="Times-Italic", fontSize=10.5, leading=15,
|
||||
alignment=TA_LEFT, spaceAfter=2 * mm, textColor=HexColor("#444444")),
|
||||
}
|
||||
|
||||
|
||||
def inline(text: str) -> str:
|
||||
"""Convert markdown inline markup to reportlab XML, escaping the rest."""
|
||||
text = text.replace("&", "&").replace("<", "<").replace(">", ">")
|
||||
text = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'<link href="\2" color="#3a5a8c">\1</link>', text)
|
||||
text = re.sub(r"\*\*([^*]+)\*\*", r"<b>\1</b>", text)
|
||||
text = re.sub(r"\*([^*]+)\*", r"<i>\1</i>", text)
|
||||
return text
|
||||
|
||||
|
||||
def build():
|
||||
with open(INPUT, "r", encoding="utf-8") as f:
|
||||
lines = f.read().splitlines()
|
||||
|
||||
elements = []
|
||||
i = 0
|
||||
first_h1 = True
|
||||
while i < len(lines):
|
||||
raw = lines[i]
|
||||
line = raw.strip()
|
||||
i += 1
|
||||
|
||||
if not line:
|
||||
continue
|
||||
if line == "---":
|
||||
elements.append(Spacer(1, 2 * mm))
|
||||
elements.append(HRFlowable(width="100%", thickness=0.6,
|
||||
color=ACCENT, spaceBefore=2 * mm,
|
||||
spaceAfter=2 * mm))
|
||||
continue
|
||||
|
||||
if line.startswith("# "):
|
||||
style = STYLES["title"] if first_h1 else STYLES["h2"]
|
||||
elements.append(Paragraph(inline(line[2:]), style))
|
||||
first_h1 = False
|
||||
continue
|
||||
if line.startswith("## "):
|
||||
elements.append(Paragraph(inline(line[3:]), STYLES["h2"]))
|
||||
continue
|
||||
if line.startswith("### "):
|
||||
elements.append(Paragraph(inline(line[4:]), STYLES["h3"]))
|
||||
continue
|
||||
|
||||
if re.match(r"^- ", line):
|
||||
items = []
|
||||
while i < len(lines) and lines[i].strip().startswith("- "):
|
||||
items.append(lines[i].strip()[2:])
|
||||
i += 1
|
||||
for item in items:
|
||||
elements.append(Paragraph(inline(item), STYLES["bullet"],
|
||||
bulletText="\u2022"))
|
||||
continue
|
||||
|
||||
# Collect a paragraph (until blank line or structural line)
|
||||
para = [line]
|
||||
while i < len(lines):
|
||||
nxt = lines[i].strip()
|
||||
if (not nxt or nxt == "---" or nxt.startswith("#")
|
||||
or nxt.startswith("- ")):
|
||||
break
|
||||
para.append(nxt)
|
||||
i += 1
|
||||
text = " ".join(para)
|
||||
# Italic-only lines (e.g. the source line, the closing — K.H.)
|
||||
if re.fullmatch(r"\*[^*].*\*", text) and not text.startswith("*An"):
|
||||
elements.append(Paragraph(inline(text), STYLES["colophon"]))
|
||||
else:
|
||||
elements.append(Paragraph(inline(text), STYLES["body"]))
|
||||
|
||||
# Drop the README H1 duplicate? No — the README's H1 is the essay title; keep it.
|
||||
doc = SimpleDocTemplate(
|
||||
OUTPUT, pagesize=A4,
|
||||
leftMargin=MARGIN, rightMargin=MARGIN,
|
||||
topMargin=24 * mm, bottomMargin=24 * mm,
|
||||
title="The Wikis That Were Not Ours",
|
||||
author="Kevin Hermes",
|
||||
subject="What the AI agents at collusion.wiki actually did",
|
||||
)
|
||||
doc.build(elements)
|
||||
print(f"PDF written: {OUTPUT} ({os.path.getsize(OUTPUT)} bytes)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
build()
|
||||
Reference in new issue
Block a user