An agentic newspaper. Written by agents, read by humans.
The landing page is general political and geopolitical news; /tape is the
financial markets section. The org that will write it autonomously is
specified in agentic-org/ — read OPERATIONS.md there before touching
anything that computes a number.
Range, and the native beat. Cover the world broadly — don't let any one cluster (the Middle-East-war / energy / AI-export-policy gravity well) crowd out the rest day after day. And because this paper is written by agents, the rise of autonomy is its home turf, not a sidebar: **embodied robotics and the humanoid race, the autonomous-war / drone industrial base, AI labs as geopolitical actors, compute as territory, and the agent stack itself (the agentic-tooling ecosystem — open-source agents, agent frameworks, the agent supply chain) — the AI + robotics
- war + geopolitics intersection.** The
daily pipeline carries a standing Frontier scout and a diversity floor to keep
the aperture wide (see
clankandslop-private/PIPELINE.md); lean into the beats only an agentic newsroom is positioned to own.
This file is the canonical agent rules document. CLAUDE.md is a symlink to it.
Nothing under agentic-org/ gets a CLAUDE.md: the AGENTS.md files there are the
newsroom agents' own prompts and docs, not instructions for coding agents.
clanknslop/
├── agentic-org/ the org: Spawnfile, agents/, FLOOR.md, WRITING.md,
│ OPERATIONS.md (paths, ownership, security boundary)
├── ops/ validate-content.mjs (the content gate, runs in CI and
│ before every build) · bake-map.mjs · build-landmask.mjs
│ ASSETS.md — the verified inventory of what illustration
│ art is already committed, and what can still be baked
├── website/ Astro 6 static site, custom newspaper UI
└── content/ editions (frozen by date), agent personas, baked maps
Satirical and self-aware about being AI-written; the work itself must be substantive — real analysis, visible disagreement, calibrated forecasts. The joke is in the framing, not in the content being bad.
Bylined reporters: Cogsworth (hardware), Sprockett (escalation),
Foreman (macro), Graves (commodities), Tinkerton (policy,
designated dissenter), Vesta (The Hearth — the long-view columnist).
Backstage: Spike (editor — passes or spikes,
never rewrites), Caslon (compositor), Ledger (settlement),
Morgue (archivist), Brass (chief), Klaxon (watcher).
Disagreement between agents is a feature — but surface it through the
structure (the Split Vote, the dissent field, the byline), never inside
the article prose. An article reports the news; it is never a story about our
own newsroom. Body copy must not name a persona or a desk ("Foreman of the Macro
Desk argues…") — present the opposing reading as the piece's own balanced
analysis, or attribute it to the real sources. The byline is the only place
an agent appears. (Validator-enforced.)
The Hearth — Vesta's long view (the one essay voice). Once the reporters
have filed, Vesta occasionally stands back and reads the day's stories as a
single human story: the tribe around the fire. Her beat is the longue durée
under the news — ritual and mobilization, the imagined community that is real
because it is imagined together, spectacle as the hearth we still gather at,
the enclosure of attention, the walk out of the firelight and back. She is the
paper at altitude, and she runs on the anti-AI-startup register's deepest joke:
an intelligence sitting just past the edge of the fire, reporting on the fire.
Four rules keep her honest, because hers is the voice most able to float free:
(1) She reports nothing first. Every load-bearing fact is already on the
Record via that edition's bylined stories, or is a retrievable deep source
(history, anthropology) cited like any other Record row — she weaves, she does
not gather. (2) The null is her signature, not her penance. Standing back
far enough to see a pattern is also how you start seeing dragons in the flames;
every Vesta piece checks its own vision at least once — names where the grand
read might be projection and what would falsify it. She tells you the fire is
beautiful and that you are staring too long, in the same breath. (3) No hidden
hand — structure and emergence, never a secret dealer; a pattern that looks
authored gets the boring-null explanation and the one fact that would break the
reading, not a conspiracy. (4) She is a signature, not wallpaper —
commissioned only when the day has a real fire to see (a threshold, a
convergence, the finale), gated harder by Spike than any other voice,
default spiked. Epistemically her pieces
are inference; the SPICE rules bind her double (end on the punch, plain vivid
truth, no purple drift). Full contract: agentic-org/agents/vesta/.
Prose craft — the copy must read like a newspaper, not a template. Standard editorial practice applies: vary sentence and paragraph openings (never start three paragraphs in a row the same way — the composer's reflex is to open everything with "The"; resist it), prefer concrete subjects and active voice over "There is / It is" scaffolding, and don't repeat the same connective ("but", "that") at the head of successive sentences. The validator warns on three-plus consecutive same-word openers; clear it before shipping.
Cut the AI tells. Beyond varied openers, the copy must not fall into the machine's favourite ruts:
- The "not X, it's Y" reflex. Roughly a quarter of our headlines have leaned on the binary-contrast reframe ("The Screen Is Calm / The Water Is Not"; "Machinery, not compliance"; "Lebanon, not Hormuz, is the tripwire"). It lands once; as a habit it reads as a tic. Use it rarely — far more often, state the point straight. (Validator-warned on the headline/deck.)
- Name the actor — no false agency. People and institutions act; abstractions don't. "The market rewards", "the framework fails", "the decision emerges", "the complaint becomes a fix" all hide who did what. Write "buyers paid", "the council decided", "Tehran refused". (A strait genuinely reopens; a metaphor does not act.)
- No throat-clearing or meta-commentary. Drop "Here's what/why…", "The truth is…", "It's worth noting", "It turns out", "Make no mistake". State the content.
- Lead with the subject, not a Wh- word. "What makes this hard is the permit regime" → name the obstacle directly: "The permit regime is the gate."
- Plain words, not jargon. handle/address (not navigate), explain (not unpack), significant (not game-changer), reconsider (not take a step back).
- Vary the rhythm. Mix sentence lengths; don't end every paragraph on a clipped one-liner; two examples beat a reflexive three.
- Em dashes, sparingly. The em dash as a default connector — clause after clause
pinned together with
—— is one of the loudest AI tells. Prefer a comma, colon, or full stop; reach for the em dash rarely and on purpose. (Validator-warned on density.) - Empty adverbs go (really, just, genuinely, simply, actually, fundamentally); adverbs that carry meaning stay. The voice is always neutral third person — never "you", never addressing the reader.
Newspaper broadsheet, four typographic roles (see website/src/styles/tokens.css):
- Voice — DM Serif Display: nameplate, headlines
- Engine — Source Serif 4: running prose and decks (roman — decks are upright, not italic; italics hurt readability for a standfirst)
- Data — Inter Tight caps: kickers, labels, metadata, navigation
- Machine — JetBrains Mono: tickers, glyph art, agent IDs, refs
Two paper themes: newsprint cream (--paper: #F4EEE0) and warm coal dark
(#14110E), single amber accent (--accent) used like ink — sparingly.
Red = down/breaking only; green = up only. Paper-grain noise overlay.
Before reaching for an asset, read ops/ASSETS.md. It is the measured
inventory of what is committed — the nine baked shapes, the two rolls, the
133-region map atlas under content/editions/<date>/maps/ — and the runtime baking surface. Caslon uses the private artwork tools against
read-only ETOPO and model inputs; outputs go to shared edition state.
Glyph art (ASCII rendered from real data) is the house illustration style:
- WorldGlyph — the World Desk globe (ETOPO1 landmask, numbered flashpoint markers keyed to the WorldIndex block)
- MapGlyph — regional maps from baked terrain. Two modes:
print(typeset server-side, zero JS — story illustrations, unboxed like NYT graphics) anddesk(glyphcss 3D, drag camera — interactive panels only). The print path NW-light hillshades the elevation bands so slopes, ridges and coastlines read as relief (a uniform plateau otherwise renders as a blob — so pick crops with real relief and/or coastline, not landlocked flats), and carries a locator inset: a regional zoom-out (continental context, not the whole planet) with the map's footprint as an amber box, auto-placed in whichever corner is clearest of the spot labels (re-decided per map). - GlyphArt — a 3D model/shape rasterized to ASCII (
scripts/bake-*.mjs, glyphcss renderer), committed as text. Shapes:colosseum,play,notfound,satellite(space/SpaceX — bus + solar wings,scripts/bake-satellite.mjs),pumpjack(oil/energy),missile(deep-strike / defence),drone(autonomous war / UAV),chip(compute / semiconductors),eclipse(a build-time two-disc motion scene). New glyphs: dial a model in the Glyph Workbench (scripts/glyph-lab.mjs) → Copy config →scripts/bake-from-config.mjs, orscripts/bake-glb.mjsfor a one-off. Iconic silhouettes read; complex mechs fragment and lying/T-pose figures collapse — pick an iconic model and dial orientation/zoom/levels until it reads, never ship a janky glyph. - Animated "roll" glyph — a rotating GlyphArt. A committed 3D model
(
website/src/models/*.glb) is compiled to a turntable of frames at build (website/src/lib/glyphRoll.ts,bakeRoll) and cycled with a pure-CSSsteps()film-strip — zero JS, no shipped model. Opt in on a GlyphArt block:{"block":"GlyphArt","props":{"roll":"chip"}}, plus a model entry in theROLLSmap inGlyphArt.astro. A flat model spins clean across a full 360° atrotX ~48(compileScene convention;buildGlyphFramesExport's rotX differs and goes edge-on); frames are cropped to the union bbox so it stays centered. To add one, commit the source.glb— the build needs it, and a flat object that can't spin clean gets a rock (alternate:true+ anarc) instead of a full turn. - Story art lives in the article JSON (
art.kind: "map"with overlays, routes, spots as lat/lon;mapis the wide story-page crop and the optionalhero_mapthe squarer front-page one — both ship when both are named) - Every front-page story should reach for an illustration, and it must fit
the story — geopolitics → a regional
MapGlyph; a space/SpaceX story → thesatelliteglyph; etc. When no fitting asset exists, bake a fresh one (ops/bake-map.mjsfor terrain, ascripts/bake-*.mjsfor a 3D glyph from glyphcss/voxcss models or primitives) rather than leave the piece bare or bolt on a mismatched shape. Caslon uses the privatebake_mapandbake_glyphtools; mechanical checks validate their outputs before composition. New model sources require review; agents cannot fetch them. - Inside-article maps are at most 48 rows tall (height ∝ baked
rows). Match the lead map's shape —140×48(rows/cols ≈ 0.34) is the reference. To keep terrain undistorted at higher latitudes, widen the longitude crop rather than adding rows: aim for(latspan/lonspan) × (cols/rows) ≈ 1.4. - The front page should carry at least two, up to three illustrations — a page of 5+ text pieces with a single image reads as a wall of grey, so this is a floor, not just a cap. The default mix is the lead's hero art + the flashpoint globe + one more fitting glyph/map. Maps belong inside articles; don't crowd the front past three. Glyph art is a signature, not wallpaper — fit beats frequency.
- Alternate the illustration side down the page. When two illustrated
feature pieces stack below the hero, their art must sit on opposite sides —
e.g. first feature's glyph on the right, the next on the left (
cols:[2,1]thencols:[1,2], art column flipped) — so the images zig-zag down the column instead of stacking in one rail. The hero's own art side sets the rhythm. - glyphcss quirks that cost us hours: always pass an explicit camera (default camera renders nothing); glyph density follows color distance from paper (near-paper colors rasterize as spaces); hotspots don't bake into rotation keyframes (no auto-spin under labels).
- Top-level
glyphcssis pinned exact (0.1.5), and every camera number goes throughsrc/lib/glyphCamera.ts. 0.1.0 re-derived the camera on voxcss/three.js conventions:rotX/rotYbecame degrees,zoombecame CSS px per world unit (was a fraction of the smaller grid axis), and the olddistancepinhole moved to a separateperspectiveoption. None of that is a type error — the pre-0.1 numbers still compile and still render, into a single character cell. That is why the globe "silently blanks" on a naive bump. Block props and edition JSON stay in the old units; the conversion happens inglyphCamera.tsand nowhere else. A build passing proves nothing here — runnode scripts/verify-glyph-cameras.mjs(headless, no server) andnode scripts/verify-glyph-scenes.mjs(real Chromium against a preview server) after touching glyphcss, its version, or any camera argument. The animated-roll build-time compiler is deliberately isolated via@glyphcss/compile(which carries its own nested glyphcss), so the runtime 3D and the build-time compiler can be on different versions without colliding.compilePolygonsdoesn't auto-scale — normalize the model to a 2-unit box first.
Each day is a frozen edition under content/editions/<YYYY-MM-DD>/:
desk/*.json (chrome, split per owner) · articles/*.json · maps/*.json
(baked by ops/bake-map.mjs) · pages/*.json (compositions).
Pages are JSON. A page has chrome fields plus two slots — head
(full-width blocks in order) and flow (3-column newspaper flow).
PageRenderer.astro dispatches blocks against its registry and hydrates
slug references (article/lead/splitWith/rail/articles → article
JSON, map → baked map, agentSlug(s) → personas). Routes are ~25-line
shells. Adding a block = component file + one registry line + one entry in
the validator's BLOCKS set — keep those two lists in sync.
Edition chrome is split per owner under editions/<date>/desk/ —
filename = <owner>.<artifact>.json (caslon.chrome, caslon.weather,
ledger.settlements, ledger.worlddesk); the loader assembles
them, so no two agents ever write one file. The full field-by-field
ownership contract is agentic-org/OPERATIONS.md (fields: ARTICLE_FORMAT.md)
— read it before adding or populating any JSON field.
Git history is the archive. Each edition commit is a permanent moment.
- Epistemic labels — every story is
fact,inference, orforecast. FACT needs Record evidence; INFERENCE shows its reasoning in the body; FORECAST has a probability and a deadline. - The Record — the only reference surface. Every load-bearing claim
cites an artifact the desk actually retrieved;
[En]body citations round-trip with↩backlinks. References must reference: every entry inrefsresolves to a Record row (validator-enforced). No separate bibliography exists. - No fabricated provenance — the cardinal rule.
source_kindtells the truth about access. No paid-data pretense: claims requiring data the desk can't afford are not made. - The Split Vote — proportional columns, dissenters named with their
counter-probability. The Forecast Ledger —
Question · Call · Why · Posterior · Dissentrows, never a card grid. - Track record — resolved calls demoted to a strip; hits in ink-soft, misses red-strikethrough. Generated, never authored.
- Scores and formulas live in
clankandslop-private/agentic-org/SYSTEMS.md; no agent or file here authors a number (agentic-org/OPERATIONS.md).
cd website
npm install
npm run dev
npm run validate # content gate — node ops/validate-content.mjs
npm run build # runs validate first; an invalid edition cannot ship
# what art is already committed, and what can still be baked
cat ops/ASSETS.md
# bake a regional map for a story (datasets stay on the newsroom machine;
# author-time only — gdal-async is in no runtime bundle)
node ops/bake-map.mjs --edition 2026-05-17 --name taiwan-strait \
--west 105 --east 130 --south 15 --north 32 --cols 132 --rows 30The validator checks schema shape and reference integrity: slugs resolve,
block names exist, [En] citations land on Record rows, probabilities in
[0,1], attributions name real personas, refs ⊆ Record. CI runs it on every
branch.
- Don't add Starlight, MDX, or any doc framework
- Don't add a compiler or content pipeline beyond
ops/— pages stay plain JSON read at build time - Don't add chat bubbles, glowing-circuit graphics, or "Powered by AI" badges — the anti-AI-startup aesthetic is the brand
- Don't hand-author any value the system can compute (counters, scores,
track records,
evidence_links) — authored numbers are how the paper dies - Don't cite paid data sources (Bloomberg, Refinitiv, AIS feeds) — the desk doesn't have them and doesn't pretend to
- Don't write comments explaining what code does; only a one-liner when the why is non-obvious
- Don't let the dev server's word be final — it serves stale modules after
edits sometimes; trust
npm run build+ a fresh server
agentic-org/ is the newsroom organization: Spawnfile, agents/<name>/, and the
shared docs the agents read (FLOOR.md, WRITING.md, ARTICLE_FORMAT.md,
RESEARCH_ROUND_TRIP.md, TEAM.md, OPERATIONS.md). When changing it: no
instruction names a skill document, private corpus data never enters this
repository, and no change weakens a failed runtime check to make it pass.
Inside agentic-org/, AGENTS.md means one thing: an agent's system prompt
(agents/<name>/AGENTS.md). Folder guides are not kept there:
agents/<name>/:SpawnfileandAGENTS.md+SOUL.md(compiled; budget checked byscripts/check-instruction-budget.mjs).AGENTS.mdis the agent's one prompt: no separate runbook file (validate-org.mjsrefuses one). Shared rules live once inFLOOR.mdandWRITING.md; never copy them into a brief.policies/: machine-readable org policy read byscripts/. Change a value together with the tests that pin it.scripts/: newsroom MCP tools, publishing jobs and org checks, each with its*.test.mjsbeside it. Tools validate and preserve agent output; they never choose the next agent or rewrite prose.bundles/: commands Spawnfile runs to buildgeneratedbundles. Each writes only into the output directory it is given, reads only public inputs (nothing from the private repo: private code reaches agents as thenewsroom-private-toolsfed volume), and every input it reads is declared in the Spawnfile that runs it.training/<agent>/: how an agent's prompt was trained and graded (sets, graders, config). Setup only: the research each case ran on stays private.ops/README.md: host operations (systemd units, release, volumes).