← Design notes

The brew — how Ink is made, nightlyproposal

The operating half of Ink, answering the owner's audit (2026-09-02):「do we have all the design decisions?」— the reader-facing half is decided (the essays · the look); this page proposes the four that weren't: the nightly pipeline, the slate policy (how many · which beats · which formats · who writes how often), where editions live, and the console's Ink tab. Recommendations are marked; the owner's calls are collected at the foot.

The decision audit

DecisionStateWhere
Feed layout + the day-seeded composerdecided · builtink-design · lib/ink_compose.py
Article ceremony · peg strip + in-app peg card · ⋯ menudecided · builtink-design · v1042–v1044
Formats (9, likeliness not assignment) · triggers (8 + the why-now contract)decidedvibes-essays
First-hand boundary · vantage (alive now, remembering then) · anecdote budget + memory bankdecidedvibes-essays
Tone gate → THE EDITOR: the AI-tell lint (en + zh) · a RIVAL model's three positive passes · exemplars of the persona's own prose in the briefBUILT 09-02 (§3f) · author = DeepSeek V4 Pro thinking, Flash failover; editor = Gemini 3.8 Flash (3.7 until 09-09)this page §3f · lib/ink_lint.py
Hero art: DeepSeek art-directs → flash-image, batch modedecided · provenink-design
Language: one essay, frozen renditions, switch in the ⋯decided · mockedink-design §Language
The door = a room seeded by the essay + the essay as a SPECIAL BUBBLEBUILT 09-02 (v1054): one private room per reader per piece; the essay frozen in as the room's first linked page; its card as the author's first bubble (the link-bubble grammar; a tap reopens the reader)vibes-essays · /api/ink/door
The nightly pipeline (stages · timing · failure rules)decided · BUILT 09-02 (lib/ink_brew.py, §5)this page §1 · §5
The slate policy (count · beats · formats · cadence · language)decided 09-02 · 16/day, the 8 beats standthis page §3
The rhythm (heavy · mid · light — the kicker) + the light formatsbuilt into the editor's brief, the validator and ink_compose H6–H8 · funny still unproventhis page §3b · §5
Topic granularity (coarse beats · fine tags)tags minted per piece from the first brew; not yet reader-visiblethis page §3c
The anecdote bank — built? backfill?the ledger + the dealt hand are live; the bank is the Vibes voice card, extracted lazily on first commissionthis page §3d · §5
Where editions live on the boxopen → proposed belowthis page
The console's Ink tabBUILT 09-02 (v1053) — editions · the brew · policy knobs · kill switch · Brew nowthis page §4

1 · The nightly pipeline

Three times, not two passes (the owner's names, 09-07: Text starts · Art cutoff · Publish, the console's three knobs): at Text starts the brew writes and gates the edition on DeepSeek's off-peak clock and hands the art to Gemini's batch queue; from half an hour before the Art cutoff it collects the art, and at the cutoff the batch is cancelled for whatever hasn't come back and those heroes are painted synchronously — the deadline is the deadline — then composes and publishes at Publish (owner, 2026-09-02; the box runs 03:00 · 06:15 · 06:30 Shanghai since 09-07). A brew never leaves a blank day — if anything fails, yesterday's edition stays current and the console shows why. Built 09-03 And the owner is told: a pass that dies names its stage in state.json, in brew.log and as a notice on every admin's rail (ink_failed, one row per day — a second failure the same night replaces it; a pass that later publishes the day takes the row back). A day published below the floor knob raises the same notice (「published short: 4 pieces, floor 12」). The brew reaches the rail through one seam (ink_brew.NOTIFY): the bare timers write the durable rows straight into the DB; a console-triggered brew runs inside the app, which hangs its own callback on the seam so the notice also mirrors live over SSE and to the lock screen.
20:00 CST · off-peak begins 18:00 06:45 07:30 · published pegs8 triggers · SERP budget meetingslate N+2 · deks · hints writersV4 Pro · dealt anchors tone gatefail → runner-up renditionszh ⇄ en · gated art director → batchprompts · submit 50%-off overnight collect artmissing → sync paint (4.4¢) composeink_compose · seed publishdated file → pointer flip on any failure: yesterday stays current the console and the owner's rail name the stage · Brew now re-runs commission N+2 · ship N (the gate's spare) · every stage idempotent · a re-run resumes from the last frozen artefact cost/day at N=8: authoring ~$0.18 · renditions ~$0.04 · art ~$0.18 batch · searches ~$0.04 → ≈ $0.45/day · ≈ $14/month
Why the times are split: DeepSeek's off-peak window is the writing hour; Gemini's batch queue promises art「within 24h」but usually delivers overnight — at the art cutoff the stragglers are painted synchronously so no edition ships without its heroes. Every stage writes a frozen artefact (slate → drafts → gated → renditions → prompts → art → edition), so a failed run resumes rather than restarts, and the owner can inspect any stage from the console.

2 · Where editions live

ProposalContent is not code: on the box, editions + heroes + pegs live in /var/lib/mad/ink (beside rooms/ and the built personas), served by the existing /ink mount pointed at MAD_INK_DIR (default lib/ink for dev, which keeps the specimen editions in-repo as fixtures). Retained forever — the archive is the product's memory, and the threshold needs it. A nightly brew writes ~1 MB (8 heroes + JSON); a year is ~400 MB.

3 · The slate policy — how many, of what, by whom

Owner's call (09-02): 16 pieces a day for now — enough to read the whole slate and judge quality and fun (≈ 1 hour of reading at 4 min a piece). Floor 12, ceiling 20; commission 18, ship 16. At 16 the composer opens a second well and a second compact run, and the last piece before the band is the kicker (§3b). Cost ≈ $0.95/day (writers 40¢ · renditions 9¢ · editor + gates 10¢ · art 35¢ batched, 70¢ if the 07:15 rule fires on everything). The earlier recommendation, for the record: 8 pieces a day (floor 6, ceiling 12): enough for the composer's full rhythm (the well needs 5+, a compact run 7+), ~30 minutes of reading — a finite morning, not a feed — and a cast of 86 writing at most twice a week can supply 24, so eight leaves room for the gate to drop pieces without thinning the day. Scale on one number: rooms opened per slate.
AxisPolicy (the validator enforces; the editor is briefed)
Count16/day (12–20). Commission N+2, ship N. A console knob — the owner reads all 16 for now and will size it by what holds up.
Beats (8 house beats)World · Science · Tech & AI · Markets · Culture · Sports · China · Life & relationships. A day covers ≥6 beats, no beat more than 4 pieces; the beat least covered over the trailing week gets priority. (Owner 09-02: the combo stands; finer topics ride the tag layer, §3c.)
Register (new axis)Every commission carries heavy · mid · light. Per 16: ~6 heavy · ~6 mid · ~4 light; never two heavies adjacent in the composed order; the piece before the band is light (the kicker); one light piece rides front-of-book right after the lead. §3b.
FormatsThe plain essay ≤ 6 of 16; ≥4 distinct other formats a day; ≥2 light-format pieces a day (§3b); fable and notes ≤ 1 each per day; ≥1 dialogue a week, ≥2 advice columns a week once reader letters exist; the response (商榷) whenever a piece has provoked rooms.
TriggersPer 16: ~7 breaking · ~4 calendar · ~2 reply/continuity · ~2 appointment columns (running-title recurring slots) · ~1 no-peg light piece (the kicker may be pegged to nothing but the day).
LanguageA persona writes in its own tongue — owner's ruling 09-03, reversing 09-02's「everyone writes in English」: a Chinese writer (the card's writes: zh, else a Han-script name that is not Japanese — native_lang) writes in Chinese and is rendered INTO English by the same two-pass method under the AI-tell lint; everyone else writes in English and the Chinese reader gets the persona-voiced rendition (frozen at brew time, the honesty pill under the byline). 09-04: the rendition brief carries the reporter's notebook, the house glossary (lib/ink/glossary-zh.json) and the names rule (people · companies · products · publications in the original script; places and long-dead figures in Chinese); 09-06 — the slate fills (owner: 16 asked, 11 written, 8 published): the budget meeting used to be told only three days of titles, proposed ten subjects the shelf's cap then refused (once on the word「riddle」— a format, not a subject; once on one shared tag,「central-banks」), re-asked once and went ahead with 11; the gate then dropped three on triples a generic editor pass had left standing. Now the meeting reads the shelf's last four days with their SUBJECT WORDS named and only the pegs still open (the same rule the validator applies, told up front), commissions n + 4 (four spares), and the re-ask is spent only on a real shortage; the cap fires on two shared subject words, never a format or beat word; and the editor's two passes read the lint's own hits — the exact triple, the exact word — so nothing new is spent (owner:「the design is the solution, not more passes」). 09-05: a rendition that still fails the lint SHIPS, flagged lint_fail — never withheld (owner: every non-English piece gets its English); the「wrote this after」line is rendered with it. measured with exam/rendition_ab.py: the machine judge preferred the two passes with thinking, the owner's read preferred the cheap re-authoring arm (one call, thinking off, ~0.6¢, 20 s) for fluency — it ships, with the periodical rhythm (财新 · 三联: 20–45 字 sentences of two or three 分句, never three short ones in a row) in the brief (ink-quality §4, the pairs page). ≥2 zh-original pieces a day.
Cadence≤2 pieces per persona per week · every public persona writes ≥1 a month (the whole cast stays exposed) · a new public persona debuts within 7 days · the used-anchor ledger caps repetition.
LeadThe editor's #1 by significance + art strength; never the same persona two days running; a zh lead at least weekly.

The mechanism is the house one: these are constraints in the editor's brief (so it composes toward them) and checks in code (so a miss is caught, not hoped against) — one re-ask, then runner-up swaps. All numbers are console knobs, not constants.

3b · The rhythm — heavy · mid · light, and the kicker

Owner (09-02): mix in fun pieces so the day has a rhythm — serious, relaxed, fun. The pros all do it, and they do it by position, not by accident.
PublisherThe light slotThe rule we take
Broadcast news / the wirethe kicker —「and finally…」— the last item is light, by rule, every bulletinthe piece before the band is always light
Axios (AM · PM · every vertical)ends every edition with 「1 fun thing」; the body is bulleted and heavythe kicker is a fixed, named slot readers learn to expect
NYT The Morningnews → Morning Reads (lighter) → culture → Play (games)the register descends through the edition; games at the tail — we have the toolbox
The New YorkerShouts & Murmurs (satire, ~600 words), cartoons and the caption contest sit between the long pieces; Talk of the Town opens lightlight pieces are short and interleaved, never a ghetto at the end only
The Economistthe back half — Bartleby, Johnson, the obituary, Books & arts; Espresso's quote of the daymagazine anatomy: front of book · the well · back of book — light at both ends, the weight in the middle. The Ink Well already exists.
WeChat 公众号 · 人民日报夜读 · 副刊 — the evening light read beside the day's newsa zh light slot is natural, not an import

The proposal. Register becomes a third axis of every commission (beat · format · register), briefed to the editor and enforced by ink_compose as a harmony constraint: H6 never two heavies adjacent, H7 the kicker is light, H8 one light front-of-book. Four light formats join the nine:

Honest capabilityRelaxed: yes. The fable, letter and advice specimens already read light, and the register brief costs nothing. Funny: unproven. Comedy is the LLM's weakest register — it reaches for puns, lists and the 不是…而是… reflex (the AI-tone meter exists for exactly this). Mitigations: humour by character, never by joke (Munger's dry, Feynman's prankish, Banksy's deadpan — the profile is the comedy engine); the tone gate gains a 「landed?」judge (a second model reads the piece cold and answers flat / landed; a flat light piece drops to a spare, and the day simply has one fewer); and the light formats are short, which is where model humour survives. Prove it before committing: a 12-piece light specimen run (3 per format, ~30¢) read blind by the owner. If fewer than half land, the kicker stays「relaxed」until the writers improve.

3c · Topics — coarse beats, fine tags

Owner (09-02): the combo is fine, but the prevailing apps carry a much larger pool of topic areas. They do — and in two layers, which is the design.
AppCoarse layerFine layer
Google News8 top-level sectionsthousands of followable topics, machine-minted
Apple News~10 channels on the tab barhundreds of topics + publishers to follow
小红书~20 channel chipsopen hashtags, the feed learns from them
Substack · Medium~30 categoriesopen tags per post
The Economist · NYTa dozen sections, fixed for decadessubjects and series inside them

The proposal: keep the 8 beats closed and coarse, and mint an open tag layer now. The beat is the editorial constraint (what a day must cover) and the reader's rubric (World · Essay); it can split later — when a beat exceeds ~30% of a month's pieces, it forks (Tech & AI → Tech · AI). Tags are the fine layer: 2–3 per piece, chosen by the editor from a house vocabulary file (lib/ink/tags.json) that grows — a new tag is allowed but must be normalized (Fed, not Federal Reserve and the Fed), and a tag becomes reader-visible only once three pieces carry it. Tags cost nothing at brew time and are expensive to add later (retro-tagging every edition), which is why they are minted from day one even though no reader sees them yet. They feed, in order: search · the Metrics tab (which subjects draw rooms) · follow-a-topic · personalization, when there are enough readers to personalize for. Coarse now, finer in the future — with the data already there.

3d · The anecdote bank — designed; the backfill

Owner (09-02): did we solve depletion? Do we need a corpus backfill for every persona in the library?

Solved on paper, not in code. The five mechanisms on the essays page (the used-anchor ledger · the anti-echo block · method over memoir · the corpus grows in-world · the memory bank outside profile.md) are designed and two are proven in the Vibes engine; none runs for essays yet, because the brew doesn't exist. They ship as stage 0 of the brew, not as a separate project.

The corpus is mostly there; the bank is not. The repo holds 88 personas, 85 with a corpus/; the 27 app-built personas have none. So the backfill is not a research harvest — it is a distillation: run the existing vibes_extract anchor-bank pass over every corpus into anchors.json beside the profile (30–60 dated, sourced anchors each, tagged marquee / minor / method), one V4 call per persona ≈ 1–2¢ → ≈ $1.50 for the whole library, an hour off-peak. The three corpus-less repo personas and the app-built ones get the builder's research step (a few SERP calls) or, for invented personas, a frozen invented ledger — made once, canonical forever, so the character never contradicts itself.

Then the bank feeds itself. Each commission deals 2–4 unburned anchors (≤1 marquee), the ledger burns them with a 90-day cool-down, and the writer stage runs 1–2 SERP calls around persona × peg and files any new sourced fact into the bank — the corpus grows by writing. The arithmetic: 40 anchors, ~1.5 pieces a week at 16/day over 88 personas, ~2 anchors a piece → a first full cycle takes ~3 months, by which time the bank has grown and the persona's own published record is material (「as I wrote in September, and was wrong about」).

3e · The persona remembers what it wrote — proposal

Owner (09-02):「shall we let the persona remember what he wrote (the gist), so his attitude is continuous through chats and future articles? Beliefs change, so a λ(1−t) strength on it?」

Yes — and half the machinery exists. Two stores already carry a persona's past: the live persona memory (docs/live-personas.html row ③, shipped v931 — what a persona remembers across chats) and the brew's ledger (what it published). The proposal joins them with one new artefact, the stance gist:

Decided · built (brew half) 09-02 Owner: store the stance in the growth file itself, personas-recent/<slug>.md, beside the「recent updates」— same dated-bullet grammar, told apart by tag: the record is the world's truth ([press · Forbes]), the stance is the persona's own opinion, developed by the model (- 2026-09-03 — [stance] I argued that… (The Drums at Hormuz) [ink · 2026-09-03-winston-churchill]). Built: the writer emits its stance in the same call (two lines, first person, present tense); publish appends the bullet (idempotent by piece id; a fixed-horizon persona gets the file created); the weekly refresh job never shows [stance] lines to its router, never counts them against the 60-bullet cap, never drops them; the chat loader excludes them from the「Since the record」block (its cite-only gloss is the opposite of a stance) — they load nowhere until their own block ships behind the baseline A/B. Shipped v1051: the profile's sub-page is「Lately」(owner's pick over「Since the record」/「On the record」; 近况) and shows both kinds — In the record (grouped by sweep, as before) and In Ink (the stances, newest first); a fixed-horizon persona gets the row too once it has a stance (the API's living gate became an empty-file gate). Owed: the stance block in the megaprompt with λ decay, the writer's stance block — both behind the baseline A/B.

3f · The editor — the lint, the rival model, the persona's own sentences

Ruled 09-11 · built No AI rewriting after the first draft. The owner: "no more AI editing — we try to make the first run right", and on the length top-up: "Our job is to tell the writer our expectations and let him write in one go." The gate still judges and marks — the lint, the disclaimers, a title that turns on an antithesis, a light piece that didn't land — but edits nothing: no retitle, no editor passes (English or the Chinese polish), no disclaimer rewrite, no length top-up after the draft or after the editor. A piece that fails is marked and still runs (v1094). The research behind it: the outside research §4 — a model editing its own prose with nothing outside to check against makes it worse. The sections below describe the editor as it was; its functions stay for the bench.
Built 09-11 · items 9, 10, 12 The first draft, better asked. 9 — the writing loop's best recipe is in the brew: the writer offers five titles and picks one that turns on something that happened; Notes and Scorecard are briefed as plain prose; the reporting's "not established" list stays at the desk; the house rules carry the loop's lines, with one line on where a paragraph stops, written as what to do. 10 — the words the prompt leaked into pieces are gone: it says the reporting, today's piece, the question to answer, never reporter's notebook, commission, the editor or reader. 12 — numbers by code at the gate: a spelled figure of 11 or more becomes digits (never at the start of a sentence, inside a quotation, or in an ordinal), and a number the reporting doesn't contain is flagged in the brew log.
Built 09-11 · items 6, 7 Two new columns on the console's Ink days. Alike — the word overlap between the day's English pieces, and each piece's closest match in the previous week (computed when the edition publishes; a phrase used in three or more pieces is logged). Sameness can't be seen one piece at a time, which is why it is measured over the edition. Came back — of the people who read the previous day's edition, how many read this one. How far each reader gets was already recorded (v1070, the Read to the end column); this adds whether they return. Both are meters, never verdicts.
Built 09-12 · items 3 + 8 · off by default Several drafts, the code picks. With the console's drafts knob above 1, each piece is written as separate drafts — three DeepSeek drafts each dealt a different real memory from the persona's record, and a Gemini draft with the whole hand — kept under drafts/. The code picks: a draft that passes the machine check, then one with no disclaimer, then the fewest flagged habits, then (since 09-13) the fewest over-used phrases against the profile's list and the lowest share of paragraphs ending on a That is / It is gavel, then the lowest lint score — the over-use profile §7. No model scores a draft. A local test (four DeepSeek drafts, tonight's assignments) found the drafts far more different than re-rolls or five-in-one-call, and the picked pieces cleaner by count; most drafts ignored the memory they were dealt. At 1 the brew writes one draft, unchanged. Production cost with the Gemini draft: about 3.5¢ a piece for the writing.
Built 09-12 · item 16 · off until switched on The interview before each draft. With the console's interview first switch on, each draft begins with one small call: the persona answers five questions written by code — what in the story gets to it, a moment from its own life (the memory that draft was dealt), who in the story it would talk to, its answer with the fact behind it, what a newcomer needs explained first — and the writer works from the answers. The call starts with the same text as the writer's (who you are and the profile), so the writer reads the profile from DeepSeek's cache. A failed interview leaves the draft as it was. Tested on last night's 9 assignments: the persona's real memory reached 6 of 7 picked pieces, against 0 of 7 without; the habit counts didn't move beyond chance. About half a cent a piece at four drafts. The call is its own line in the ledger, app.ink_interview.
Built 09-12 · item 17 A product Review needs someone who used it. For a Review, the research desk's notes also name the product (a thing people buy or use, that the writer didn't make) and anyone in the reporting who used it, each with its source. When there's a product and nobody used it, the code changes the commission to a Preview — what's announced, what to look for, no verdict on using it — before the writer starts, and records it in the slate. Preview is never offered to the editor. A speech, a match or a report has no product and stays a Review.
Tested 09-12 · the shape menu, retried · switched off The shape menu, with "never say where the turn is". The nine shapes from the writing loop are in the brew behind a shape switch (off; no console control yet): each draft gets a different fitting shape and the line beside it. On last night's 9 assignments, three blind readers found no difference from the brew without it: real turns 76% against 74%, pieces announcing their turn or structure 64% against 67%, made-up-feeling scenes 20% against 26%, keep reading 72% against 78%. The one count that moved: verdict-line endings, 3.8% of paragraphs against 6.1%. Kept off. Worth knowing: the readers say about two-thirds of pieces signpost their own turn or structure, with or without the menu.
Owner (09-02, on seeing「it isn't X, it's Y」still in the pieces):「that's fatal」— what do the long-form services do, and would an editor pass help? The field has converged; this section ports it.
FindingSourceWhat Ink does now
The tells are a KNOWN LIST: the negation pivot (not X, but Y / it isn't A; it's B), em-dash cadence, the rule of three, the metronome (sentence lengths within a narrow band — GPTZero's original signal), self-answered questions,「here's the thing」, a vocabulary of ~300 words (delve, tapestry, testament, underscore…)Wikipedia's Signs of AI writing (WikiProject AI Cleanup, ~15k words); the SlopMonster lintlib/ink_lint.py: regexes only, English and Chinese, run on every piece and every rendition. Zero tolerance on the strong pivot; two plain not…, but contrasts fail (one is speech); three of anything else fails; the metronome alone fails.
「Don't」instructions backfire — the pink-elephant problem; past ~40% negatives the output degrades. Positive instruction + exemplars is what works.16x Engineering; the negative-instruction cascade study (2026)The writer's「avoid」list is gone. In its place: say each thing directly and once · vary your sentence length · end where the thought ends — and THREE PARAGRAPHS OF THE PERSONA'S OWN PROSE.
An editor pass works when it is a RIVAL model: lint → cleanse in three positive passes (vocabulary · structure · voice) → lint again; ship only clean.「A model is poor at hearing its own accent.」SlopMonster (lint→cleanse→re-lint, 3/5 → 5/5 on a live run)The gate's editor is Gemini 3.8 Flash (3.7 until 09-09; the writer is DeepSeek): plain words → reshaped sentences → the voice pass with the exemplars; re-lint; still failing → dropped, the spare fills. ~1.5¢ a failing piece.
Stripping tells without restoring voice leaves sterile text; voice comes from real prose samples, not a description.Sudowrite's voice matching; the two-classifier findingexemplars() mines the persona's corpus for its own QUOTED spans (50–220 words, the researcher's notes filtered out) — 73 of 84 personas have three; they ride the writer's brief and the editor's voice pass.
The translator re-introduces the pivot even when the English is clean (Churchill's zh carried four 不是…而是 turns).this brew's own renditionsThe Chinese lint (不是…而是 · 与其说…不如说 · 不在于…而在于 · 不仅…更 · the note-taker's vocabulary · dashes · the metronome) runs on every rendition; a failing one goes through the editor's two zh passes; still failing → the rendition is not shipped (the piece ships English-only).

The console's edition view shows each piece's lint score and the editor's before → after. The three proof editions were re-gated as the baseline (see the chat of 09-02).

3f½ · The writer, since 09-19 — the persona drives

Built 09-19 · the console's the persona drives switch · ON by default The morning's pieces are written the way a columnist works, the process built and read piece by piece on the writing process and the writer as an agent: the persona reads the reporting and reacts (what struck it, what it wants to know) → its life on disk (episodes from its record, built once) is recalled against the news and what struck it, and the profile goes over without its memory block → the desk looks up its questions and four canned ones (earlier coverage, precedent, elsewhere, the critics) → the witness desk → the decision (09-20: the writer offers two or three questions it can now answer, each naming its kind of value; the editor picks one and the form follows it; none worth a reader's time is no piece) → a question nobody could answer becomes the writer's wonder, which the fact check leaves alone → the point, tested twice (a point that is the source story said back, twice, is no piece, logged as a fair answer and the day runs short by one) → the witness desk (people who were there, quoted word for word from a page it opened) → its own outline → the piece written whole → a reader that is not the writer (Gemini 3.8 Flash, with the taxonomy learned from the owner's marks) marks the tells and the writer mends, twice → the reread → the two-stage fact check and the correction → the point kept as the persona's prior. Code designs nothing: it fetches, says each thing once, and checks. Every step is kept under <brew>/driven/<id>.json. About 13¢ and 6½ minutes a piece (the cost, step by step); the searches run one a second by design, so a morning of sixteen finishes inside twenty minutes. Off, the brew writes the old way: several drafts, the code picks and repairs. English commissions only; a Chinese one keeps the old path. The dealt hand, the interview and the shape menu are not used on this path.

3g · The quality score — the target function

Ruled 09-07 The score no longer decides. The owner's first read (the first read):「it makes no sense to have you score each article subjectively; you can do stats, long words, long sentences, pronouns, but you can't judge overall quality.」 The gate now ranks the survivors by a deterministic register floor (ink_lint.register: metaphor nouns per 1k · long words per 100 · first person per 100 · mean sentence length · a「The X of the Y」title, each one flag over its allowance), fewest flags first, ties in the editor's order; the top N ship. No model scores a piece in the brew; the IQS below stays a CLI and calibration tool. The same ruling subtracted the writer's brief — see the first-read page §4 for what shipped.
Owner (09-02): the pieces are written once and read by everyone, so quality is the first priority — research how an editorial is scored and make that the target function; and one axis no traditional desk has: the persona × topic crossover — the piece must read as written by the persona, not by an imposter.
What the field measuresSourceInk's axis
The Pulitzer test for editorial writing:「clearness of style, moral purpose, sound reasoning, and power to influence public opinion」; since 2026 folded into Opinion Writing —「well-reasoned and compelling arguments… originally researched and reported, or informed by personal experience」pulitzer.orgE · editorial — five lines, 1–5 each with one quoted line of evidence: clarity · reasoning · specificity · a position a reader could disagree with · carry (one thing the reader can repeat tomorrow). Judged by the rival family.
LLM judges are usable (~85% agreement with humans, above human–human) IF: a rubric with separated criteria; pairwise with position swap; length-controlled; never the writer's own family; calibrated against human labels monthlythe 2025–26 judge-bias literatureAll judges are Gemini Flash (the writer is DeepSeek); the imposter test runs both orderings; the readers' 👍/👎 and door taps are the human labels the judges are calibrated to.
The authorship gap: personalized LLM text sits below the human cross-author floor (LUAR 0.48–0.51 vs 0.63) — closer to the model's own fingerprint than to any human. Single metrics lie (cross-metric |r| < 0.07); a judge that reads the same profile the writer read measures instruction-following, not voice (circularity).PersonalBench (2026); Theory-grounded evaluation of LLM personalization (2026)V · voice, three independent measures, none of which sees the profile: ① the imposter test — the judge gets the persona's REAL paragraphs and three unlabelled pieces (the persona's + two decoys from the same brew) and must pick the persona's from the writing alone; ② trait match — five checkable writing traits extracted once from the persona's real prose, scored yes/partly/no on the piece; ③ stylometrics in code — function words, punctuation, sentence shape, ranked against the cast (the content-independent authorship signal). The honest expectation from the literature: we will not cross the human floor; we can climb toward it and see which personas fall short.
Reported opinion beats asserted opinion: numbers, dates, names, attributed quotesthe Opinion Writing criteria; the specimen reviewS · specificity — specifics per 100 words, in code; the reporter's notebook (the next build) is what will move it.
The tells are a hard gate, not a score§3fL · the lint — a piece with a tell is not scored; it is edited or dropped.

IQS = 100 × (0.40·E + 0.40·V + 0.20·S), over pieces that pass the lint. The brew now commissions N+2, gates, scores every survivor, and ships the top N by IQS — a spare with a better score beats a weak main. The console's edition view shows each piece's IQS with its E · V · S and the editor's one-line verdict. Cost: about 0.7¢ a piece (four Flash calls; the traits once per persona).

The first reading (09-02, the 17 proof pieces, 12.6¢): IQS ran 47–92. Buffett's letter led (E 1.0 · V 0.87 · S 0.84); Li Ka-shing's letter and Jobs's essay followed; the bottom were Dylan's Shouts (V 0.47, stylo 0), Chestnut's fable (trait match 0.0) and Twain's kicker. Three things the numbers said at once: ① specificity is the weak axis across the board (S 0.10–0.84, most under 0.5) — the reporter's notebook is the lever; ② trait match is the discriminating voice signal (Jobs, Mouratoglou, Banksy 1.0; Churchill, Darwin, Einstein 0.2; Chestnut 0.0) and the judge's own words agree —「Churchillian pastiche whose rhetorical elegance outstrips its content」; ③ the imposter test was too easy (1.0 everywhere): with two other-persona decoys on other subjects the judge was picking by subject, not by voice. Second cut, the same piece rewritten by an anonymous columnist: still 1.0 — the judge picked「the one with a voice」. Third cut (shipped): the decoy is the same piece rewritten in another persona's voice from that persona's own paragraphs, so the judge must tell Churchill from Dylan on the same facts. Re-scored, the test still passes every piece — but a NEGATIVE CONTROL (the key set to Dylan's prose, the texts Churchill's piece, its Musk-voiced decoy and Dan Wang's) answers 0.5 with「none of the options match Dylan」, so the judge is reading style, not guessing: the personas' pieces really are in voices consistent with their samples, and the test is a FLOOR (a piece that failed it would be badly wrong), not a ladder. V is weighted accordingly — trait match ½ (the ladder: 0.0–1.0 across the cast), the imposter floor ¼, the stylometric rank ¼.

How it becomes an optimisation: every knob the brew has — the writer's brief, the exemplar count, the editor's passes, the model, the register mix, the notebook — is changed one at a time and read on IQS over a week of editions, with the readers' verdicts as the check that the judges still agree with people. The persona × topic map (IQS by persona and by beat) is the first thing to read: it says which crossovers work and which personas write as imposters.

3h · The reporter's notebook, and the voice harvest

Built 09-02 Stage 2½ — the notebook. The writer had been working from a headline and a one-sentence snippet (the frozen peg card). Now, per commission: the peg article fetched in full where the site allows (raw visible text; the link-unfurl digest as the fallback; walled sites fall through) · three searches — the subject (the news vertical), the editor's angle, the persona × the subject — with the two best pages fetched Phase 1, 09-08 (the lead and the well climb a full-text ladder for the peg — the page, a reader proxy, the archive's snapshot — fall to the same story on an open wire when it stays walled, and fetch four pages; a first notebook that comes back thin gets a second round keyed to what it could not establish; a commission still thin after that is floored to the spares, and a section page is refused as a peg by its URL's shape, not its words) · one Flash distillation into dated facts, numbers, people, attributed quotes, what is disputed, what is not established, and what the persona has said before, every line with its source number, and only what bears on the subject (a live page carries other stories). The writer's rule is now「facts only from the notebook」; the reader's peg card grows to the article's opening paragraphs when the fetch succeeded. ~0.6¢ a piece. The voice harvest (python lib/ink_brew.py voices): for the historic greats — Ink's staples — the open web's PRIMARY text (Gutenberg's files, Wikisource, archive.org; a middle window of a long book, past the front matter) fetched raw and Flash copying out verbatim paragraphs written BY the persona; the exemplar bank grows (cap 24 since 09-03) and the persona's traits are re-extracted. Searching by name was the trap (09-03): Gutenberg's「Winston Churchill」author page is the American novelist (Coniston, Richard Carvel), and Twain's first hits are Paine's biography of him — so the greats now have a hand-listed shelf, lib/ink/voices.json (slug → the persona's own public-domain plain-text files; two windows from the middle of each book), which the harvest reads before it searches; the SERP path stays for everyone else. voices --force <slug> starts the bank over from the corpus (dedupe + the note filter — the old Churchill bank held four passages twice, once bare and once in quotation marks; Twain's carried the researcher's own「THE MOST IMPORTANT FILE in this corpus」as Twain). The method and the readings: the quality score.

3i · Length — what the field runs

Owner, 09-03「All articles are too short. Even the essay is a 2-minute read. What is the proven length of a good essay on mainstream media? The kicker? The other formats?」The floors had been set for a feed (600–900 words for an essay, 70–150 for the kicker). The table is what the publications the essays page cites actually run; the new floors and ceilings follow it, scaled to a daily that a reader finishes (16 pieces × ~1,000 words ≈ 75 minutes of reading a day). Reading time is counted at 220 words a minute.
Ink formatThe field's proven lengthWhere the number comes fromInk now (was)
Essay800–1,200 words; leaders and signed columns 850–1,000NYT Opinion's guest-essay guideline (800–1,200); the Guardian's comment page ~850; the Economist's leaders and named columns (Bagehot, Lexington, Charlemagne) ~900–1,000; USA Today op-eds 600–8001,000–1,400 (600–900)
Notes800–1,200the Economist's Bartleby/Johnson columns; Matt Levine's numbered items; Munger's talks in list form800–1,200 (450–700)
Review800–1,200; a critic's essay 2,000+a book review is「around 1,000 words, 400–2,000」(Letter Review); NYer critics run longer900–1,300 (450–750)
Dispatch1,200–1,500 in a daily; 3,000+ in a magazineNYT dispatches; the New Yorker's「Letter from…」; Dan Wang's annual letters run to 10,0001,000–1,500 (450–750)
Letter700–1,100a published letter of counsel (Rilke's to the young poet ~1,000–1,500); NOT a letter to the editor, which the NYT caps at 150 words700–1,100 (350–600)
Advice800–1,200 for one letter answeredDear Prudence answers 300–500 each (three a column; the question itself is capped at 150); Ask Polly and Dear Sugar answer one letter at 1,500–2,500800–1,200 (400–650)
Scorecard700–1,100the forecasters' public post-mortems; a Levine item700–1,100 (350–600)
Dialogue900–1,400the New Yorker's「In Conversation」and the NYT's edited two-hander run 1,500–2,500 with the small talk in; a written dialogue keeps the argument only900–1,400 (450–800)
Fable250–600Thurber's Fables for Our Time (the New Yorker's fables) run 250–600 words; Aesop's mean is 44 words — a moral, not a read400–800 (350–600)
Shouts600–1,000the New Yorker's Shouts & Murmurs, as its submission trackers record it (600–1,000)600–1,000 (220–400)
Kicker80–150 spoken; 400–600 writtena broadcast「and finally」is 30–45 seconds of script; Axios's「1 fun thing」and the NYT Morning's closing item are 80–150 words; the shortest written equivalent — a Talk of the Town short — is 400–600150–300 (70–150) — the broadcast tag, doubled; whether the slot survives is the owner's call (§the ranking)
Overheard500–800a Shouts-length dialogue; McSweeney's lists and two-handers 500–800500–800 (220–400)
Riddle200–400a setter's preamble plus the puzzle (NYT Play, the Guardian's crossword blogs)200–400 (100–250)

The engagement data agrees with the print numbers rather than with「write short」: Medium's completion peaks at a 6–8-minute read (1,000–1,200 words); Substack's most-engaged cluster is 800–2,500 words with 70–80% completion at 1,200–1,800 (Substack Course). The owner's ranking of the formats by read quality (09-03): essay ≈ numbered notes ≳ notes > letter ≈ fable ≫ kicker — recorded here; what it changes in the slate mix (fewer kickers, or none) is decided on the specimen brew at the new lengths. Ruling (09-03, late): the kicker stays for testing — dropped only if it keeps getting bad reviews. The specimen at the new floors (brew 2026-09-07, ten pieces): the writer does not obey a floor it is merely told — essays and notes landed at 700–1,060 words against 800–1,400, a scorecard at 570, a fable at 370; and the editor's passes trim further (Buffett 746 → 572). So a draft under 85% of its floor now gets one extension ask in the writer's own call — carry the argument further from the notebook (another reported fact, a second example, the objection and the answer), never pad; the longer draft stands (extended on the piece, shown on the brew log). Measured on brew 2026-09-08 (ten pieces): the drafts landed at the floors (Darwin 1,057 · Dan Wang 1,033 · Augustus 938 · Nolan 886 · Karpathy 875 words — against 700–1,060 the night before), but the editor cuts what the writer built (Nolan 886 → 631, Karpathy 875 → 701): a piece the editor trims under its floor is now extended once more and re-linted, where the words are last touched. The gate also dropped two edited pieces at exactly three soft tells (a contrast, a dash pair, a triple — what the majors carry at that length): the allowance is now three per thousand words inclusive, a fourth fails.

4 · The console's Ink tab

Built 09-02 (v1053) The tab rides the console's own grammar (rail entry after Vibes; cards, .tbl tables, the switch). The daily schedule lives here since 09-04 (owner:「let me set the start time in the Ink tab」): a Daily brew switch and three times — text starts (20:00), the art cutoff (07:15) and publish (07:30) — read every minute by the app's own scheduler thread (ink_brew.due_pass): the text pass writes the edition of the morning it will publish — a start after the publish time (20:00) writes tomorrow's, a start before it (03:00, the first setting on the box) writes today's; the first scheduled run filed a 03:00 brew as tomorrow and nothing showed in the app — and it submits the art batch; the dawn pass begins collecting half an hour before the cutoff (derived, never a fourth knob — the owner's catch), paints the stragglers at the cutoff and publishes at the publish time; each runs once a day (a stamp on the day's state), never while the kill switch is on. The two systemd timers in deploy/ are superseded — installed on the box but never enabled. Redesigned v1078 (owner, 09-04:「no small text · house inputs · a timeline of the three times · one row per day with the key facts · a tap opens the day's analysis · fix the costs, show the major ones」): the top card is four stat tiles (current edition · the brew's state · Daily brew · kill switch) and the Brew now row; The day is a timeline — Text starts → Art cutoff → Publish, each a house time input on its node — with pieces/floor/ceiling beneath; Editions is one row per brew day: pieces published of wanted · the day's three times as they actually ran (from the day's state and log) · IQS mean · cost · read-to-the-end · verdicts; a tapped day opens the analysis below the table — six stat tiles, the cost by task as bars (Writing · Editing · Judges · Research · Renditions · Art · Voice banks, the brew log's own arithmetic: the publish summary already holds the evening in a one-process day, a two-process day adds both), and one card per piece: hero thumbnail · title · author · beat · format · words · art style · the gate's word (lead / the well / published / held / dropped · flat / dropped · antithesis) · IQS · the piece's own cost · votes · readers · notes · Hold/Restore · Repaint. Since 09-19, a piece the persona drove carries one more line — the persona drove · 6/10 questions answered · 3 of its life recalled · point passed both tests · witness 5 found, 1 quoted · tells 10 → 15 → 9 · 5 fact flags — the brew's own summary of that morning (the tells are the rival reader's marks before each mend, then after; a gauge with false positives, never a score), the IQS number appears only where a score exists, and the day's tile counts how many pieces the persona drove. In the knobs row, drafts a piece and interview first grey out while the persona drives (the old path's knobs); the the persona drives switch saves on change. The unpublished days (a failed pass, a cancelled run) list too. Top card (as first built): the current edition · the brew's state (idle / brewing / last crash) · the kill switch (settings ink_knobs.kill — the timers exit at once while it's on) · Brew now with a piece count and a date (both passes at once, art painted sync, published the moment it's ready; polls to completion). The count beside the button is for that click only and saves nothing — its pill says so as it is typed (09-19); the standing number is the Pieces a day knob under The day, saved with its Save button. Slate policy: pieces a day (floor · ceiling), the art cutoff and the publish time — saved to settings and read by the brew itself, so the box's timers honour them (the units now call the passes without arguments). Editions: every edition on the shelf — date · pieces · lead · layout string · heavy/mid/light · art · 👍/👎 — and a row unfolds into its pieces: role · title + provenance · author · beat/format/register + tags · minutes · the gate's verdict (antithesis ×n, disclaimers, flat) · art · zh · votes, with json · hold / restore (the edition is rewritten and re-composed; the pointer follows when it's the current day) · repaint (one hero, sync). The brew: the last 14 days — written · chosen · dropped · no-art · cost · published — and the stage log on demand. Models & APIs carries one Ink card, headed app.ink, with a plain sub-head per brew task (09-02, owner:「radio button style, separate by tasks」; the fifteen cards combined into one on 09-09, the Studio's shape — the per-seat kinds are not named on this tab, because Usage & cost is where app.ink opens into app.ink_desk and the rest) — the editor · the research desk · the background brief · the writer · the copy editor · the cold reader · the voice judges · renditions · the art director · the paint · the anchor bank · the voice harvest · pegs (thirteen, after the 09-03 audit:「all cost items, all model selections」) — each a radio row over the models the brew can run for that seat (ink_brew.TASKS; the pick lands in settings ink_knobs.models and the brew's model_for() reads it, falling back to the seat's default for any stray id). Usage & cost books each task under its own ledger leaf, app.ink_<task> — the paint per image at the console's $/image (batch half, sync full — the console's one-hero repaint books too) and every SERP fee (pegs · the desk's three searches · the voice harvest's five) included, neither of which the old single app.ink row ever saw; and the ledger books one row per provider call (09-04, the owner:「why only 1 call for the writer?」 — a pass used to be summed per task into a single row, so a night's writing read as one call), and the write drains, so a one-process brew (Brew now, the dev brew) books the evening pass once. ⚠ The ledger is written AS THE MONEY IS SPENT (09-10, the owner:「I don't think the cost record for ink is complete and accurate」). The accumulator is memory only and book_to_db used to run once, after the pass — so anything that ended the process in between (a deploy's systemctl restart mad-room, a crash, the timer's kill) took every dollar in it, and the resumed pass, finding the pieces already on disk, booked only its own remainder. The box's own record of it: the 09-09 brew started 03:00, was restarted under that day's two deploys, finished 21 hours later and booked $0.0370 over two seats for a 20-piece day — where the undisturbed 09-08 brew booked $1.53 over eleven; its _usage.jsonl holds the 285 calls the ledger never saw. Three things now hold it shut: every call flushes within BOOK_EVERY_CALLS (8) or BOOK_EVERY_SECONDS (90) of being made; every stage ends on the books (run_stages books in a finally, so a dying stage — which re-raises past the pass's own booking — still pays); and a write the DB refuses puts its unwritten tail back at the head of the queue instead of dropping it. A row is stamped with the clock of its own call, so an edition written across midnight straddles two ledger days by design. Not built from the mock: Cast & beats and Sources (the code holds them; a later slice), rooms-per-slate (no doors yet). Endpoints: GET /api/admin/ink · GET /api/admin/ink/edition/{date} · POST /api/admin/ink/brew-now | knobs | hold | repaint · GET /api/admin/ink/log.

The admin console gains an Ink tab in its own list-row grammar. Six sections down the left; the landing is today's edition with the brew's health strip on top. Everything the pipeline froze is inspectable here, and every policy number above is a knob.

-ish consoleRoomsModels & APIsSettingsInk
Editions
The brew
Slate policy
Cast & beats
Sources
Metrics
published 07:30 art: 7 batch · 1 sync gate: 8 of 10 passed Brew nowHold editionKill switch · ON today $0.47 · Sep $12.30
Tuesday, September 2 · 8 piecesgate · art · zhrooms
Notes on a Chokepoint
Churchill · World · Notes · lead
0.4 · batch · ✓3 roomsopen · regenerate hero · hold
The Part Grown Outside the Body
Da Vinci · Science · Dispatch
0.6 · batch · ✓1 roomopen · re-translate · hold
《互惠算法》
刘慈欣 · China · Fable · well
0.3 · batch · orig5 roomsopen · hold
The booth is a safe
Banksy · Culture · Letter
0.9 · sync · ✓0 roomsopen · regenerate hero · hold
Eleven things about this market
Munger · Markets · Notes · gate-dropped (antithesis 4/300w)
1.8 · — · ——read draft · re-run

Rows are pieces; the trailing verbs are the owner's hands on the edition: open (the reader, as users see it), regenerate hero (another batch/sync paint), re-translate, hold (unpublish one piece — the composer re-flows), read draft (a gate-dropped piece, with its meter score). The dot is the tone-gate score; the strip is the brew's health and the day's spend.

SectionWhat it holds
EditionsEvery edition by date: pieces, gate scores, art status (batch/sync/missing), renditions, peg ok, the background brief (built 09-03, stage 2¾ stage_briefs: one Flash call per Ink language from the notebook only, ~0.1¢; briefs --date backfills a day and re-freezes its Source cards without republishing) · what the writer read (built 09-02: peg fetched / walled · pages · facts · quotes · sources per piece — the honest answer to「how did the persona write from an empty excerpt」: the card is the reader's excerpt, the notebook is the writer's reading), rooms spawned; per-piece verbs above; whole-edition hold/re-publish.
The brewSchedule (evening/dawn times, timezone), the kill switch, Brew now, the last run's stage log with timings and the stage that failed, per-stage models (editor · writer · translator · art director · painter · failover) drawn from the Models tab's roster, retry.
Slate policyEvery number in §3 as a knob: pieces/day, beat quotas, format quotas, weekly minimums, cadence caps, language minimums, gate threshold, length floors per language.
Cast & beatsPersona × beat affinities (editable), last-written date, pieces this month, opt-out (a persona that shouldn't write), debut queue for new personas.
SourcesQuery recipes per beat, the calendar file (data days · anniversaries), blocked domains, the SERP engine chain — the same engines the dispatch uses.
MetricsThe four that matter: engaged reads (30s+), doors opened, rooms spawned per slate (the neutron-bomb number), return rate — per edition, per beat, per format, per persona. Cost per piece beside each.

5 · The build — lib/ink_brew.py (2026-09-02)

Built in one session after the owner's「now start building」. One module, one CLI, two passes; every stage a frozen artefact under <ink_dir>/brew/<date>/, re-runnable and resumable. The constitution throughout: the LLM proposes, the code enforces.
StageWhat runsThe code's wordArtefact
pegsthe Vibes news harvest (14 beat queries, Serper news) + 18 extra beat queries via websearch + calendar.json; cleaned (entities, tags), home/section pages droppeddedupe by url and title; pegs used in the last 3 days excluded (the ledger)pegs.json
slate — the budget meetingONE V4 Pro thinking call (24k cap): every peg + the whole roster with cadence state + the last 3 days + the policy text → N+2 commissions in editorial order with beat · format · register · trigger · peg · title · dek · angle · tags · well hintvalidate_slate: unknown persona, twice-a-day, resting, bad beat/format/peg, lead = yesterday's lead, <6 beats, >4 a beat, >6 essays, <3 lights, kicker not light, duplicate light formats → ONE re-ask, then trim_slate enforces the restslate.json
writeparallel V4 Pro thinking calls (6 at a time): the full profile + the house rules (boundary · vantage · ENGLISH · facts only from the peg card · style tells) + the commission + a DEALT HAND of ≤3 unburned anchors ranked against the peg + the persona's last 5 Ink pieces (anti-echo)the voice card is extracted on first commission if missing (the lazy backfill); word count → minutespieces/<id>.json
gatethe antithesis meter (two English regexes + aitone's 不是…而是… net): ≥3 hits → one rewrite ask, still ≥3 → dropped; every LIGHT piece read cold by V4 Flash —「did it land?」— flat → dropped; empties droppedsurvivors in editorial order, the spares fill; exactly N chosengate.json
renditionsthe Flash tier (V4.1 Flash since 09-09), persona-voiced 简体中文 for every chosen piecetranslated_from: en rides with it (the honesty pill)renditions/<id>.zh.json
artthe art director (V4 Pro thinking, one call, 12 styles varied across the day, no text/likeness/photorealism) → Gemini batchGenerateContent keyed by piece id (50% off)a piece with no prompt gets a plain one — never a missing hero by omissionart/prompts.json · art/batch.json
dawn · collectpoll the batch every minute; save heroes as JPEG (PIL) into img/<id>.jpg. The batch's own finish time is kept beside our collect time (09-04: Gemini's endTime said 03:47, seven minutes after submission, and the heroes sat until the 09:38 collect), and a batch that ends failed — the API says BATCH_STATE_* where the older docs said JOB_STATE_*, so the state is matched on its suffix — stops the polling instead of running to the cutoffTHE 07:15 RULE: at the cutoff, batches.cancel + sync generateContent ×4 in parallel for whatever is missingart/batch.json
publishink_compose (H1–H8, two wells and two compact runs from 12 pieces) → edition-<date>.json chained by prev, pegs.json merged (frozen SERP cards), then the atomic pointer flipa heroless piece never leads or wells; the ledger records cadence, burned anchors (90-day cool-down), the lead, the pegs; spend booked under call kind app.inkedition.json · ledger.json · state.json
python lib/ink_brew.py evening --n 16            # 20:00 CST (deploy/mad-ink-evening.timer)
python lib/ink_brew.py dawn --cutoff 07:15 --publish-at 07:30   # 06:45 CST (deploy/mad-ink-dawn.timer)
python lib/ink_brew.py brew --n 16 --sync-art     # dev: both passes now, art painted sync, publish at once
python lib/ink_brew.py status --date 2026-09-03   # what the day's brew holds

The owner's calls

Decided 09-02 ① 16 pieces a day (12–20) — read them all, size by what holds. ② the eight beats stand; finer topics are the tag layer. ③ publish 07:30 CST; art batch cancelled at 07:15 and painted sync — the deadline holds.
Decide Taken as approved by「let's build」(09-02) and built: ④ /var/lib/mad/ink · ⑥ the register axis + the four light formats · ⑦ tags from day one · ⑧ the bank, lazily (no separate backfill run needed). Still yours: ⑤ the console's Ink tab (§4 mock) — build next? · the light specimen read: the first real edition IS the specimen — read its four light pieces and rule.
Proposal · 2026-09-02 · siblings: the essays · the look · specimens · market scan · the engine