Skip to content

Latest commit

 

History

History
330 lines (292 loc) · 19.8 KB

AGENTS.md

File metadata and controls

330 lines (292 loc) · 19.8 KB

Clank & Slop

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.

Project Shape

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

Editorial Voice

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.

Visual System

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) and desk (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, or scripts/bake-glb.mjs for 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-CSS steps() film-strip — zero JS, no shipped model. Opt in on a GlyphArt block: {"block":"GlyphArt","props":{"roll":"chip"}}, plus a model entry in the ROLLS map in GlyphArt.astro. A flat model spins clean across a full 360° at rotX ~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 + an arc) instead of a full turn.
  • Story art lives in the article JSON (art.kind: "map" with overlays, routes, spots as lat/lon; map is the wide story-page crop and the optional hero_map the 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 → the satellite glyph; etc. When no fitting asset exists, bake a fresh one (ops/bake-map.mjs for terrain, a scripts/bake-*.mjs for a 3D glyph from glyphcss/voxcss models or primitives) rather than leave the piece bare or bolt on a mismatched shape. Caslon uses the private bake_map and bake_glyph tools; 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] then cols:[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 glyphcss is pinned exact (0.1.5), and every camera number goes through src/lib/glyphCamera.ts. 0.1.0 re-derived the camera on voxcss/three.js conventions: rotX/rotY became degrees, zoom became CSS px per world unit (was a fraction of the smaller grid axis), and the old distance pinhole moved to a separate perspective option. 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 in glyphCamera.ts and nowhere else. A build passing proves nothing here — run node scripts/verify-glyph-cameras.mjs (headless, no server) and node 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. compilePolygons doesn't auto-scale — normalize the model to a 2-unit box first.

Content Model

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.

Editorial Conventions

  • Epistemic labels — every story is fact, inference, or forecast. 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 in refs resolves to a Record row (validator-enforced). No separate bibliography exists.
  • No fabricated provenance — the cardinal rule. source_kind tells 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 · Dissent rows, 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).

Commands

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 30

The 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.

What Not To Do

  • 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/

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>/: Spawnfile and AGENTS.md + SOUL.md (compiled; budget checked by scripts/check-instruction-budget.mjs). AGENTS.md is the agent's one prompt: no separate runbook file (validate-org.mjs refuses one). Shared rules live once in FLOOR.md and WRITING.md; never copy them into a brief.
  • policies/: machine-readable org policy read by scripts/. Change a value together with the tests that pin it.
  • scripts/: newsroom MCP tools, publishing jobs and org checks, each with its *.test.mjs beside it. Tools validate and preserve agent output; they never choose the next agent or rewrite prose.
  • bundles/: commands Spawnfile runs to build generated bundles. Each writes only into the output directory it is given, reads only public inputs (nothing from the private repo: private code reaches agents as the newsroom-private-tools fed 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).