LitHarness: findings

What two months of coding agents writing LitRPG serials, judged by one human reader, found about measuring fiction.

LitHarness is a solo project (12 August to 14 October 2026) in which coding agents write LitRPG web-serial chapters from a short brief, and one human reader, the operator, is the only judge of quality. What it found is mostly about measurement and specification, not prose:

The project

The goal is fiction that a defined audience would choose to keep reading and recommend, with no human in the production loop. The audience is RoyalRoad readers of LitRPG, aged 20 to 30: progression fantasy in which the hero's ranks and abilities are printed as numbers on the page.

The rebuild runs under two rules these findings produced. The operator (the project's author) is the only quality gate, and no model judges or selects text. No human data is collected beyond household reads the operator relays: no paid readers, panels or labels.

Terms used below:

PhaseDatesDesignSize
Rebuild27 Sep to nowOne pitch call per serial, then 2 calls a chapter (plan, draft) and a rewrite only when a counter says so1,564 lines of stdlib Python
Incumbent12 Aug to 27 SepA multi-stage pipeline: world forging, a writer roster, scene packets, a stat-sheet engine, model gates65,891 runtime lines, plus 96k of tests and 101k of research code

The evidence base is 20 operator reads (18 Aug to 23 Sep), each naming defects in a chapter, and a 267-entry decision ledger kept at the tag legacy/incumbent-2026-09-27. Writers were Codex (gpt-6-astra, then gpt-6.1-sol), Claude Code (Opus 5.5) and Haiku 5.5 through the API.

The rebuild's first serial has 3 chapters (4,831 words) awaiting the operator's 21st read; with its pitch it took 15 calls and 118,679 tokens. The code is public at github.com/skulitom/LitHarness.

How a chapter is made

Two calls a chapter; code redraws failures, one human read judges

Briefa few lines from the operator Pitch, 1 callwrites the serial's bible,at most 900 words Operator's glancereads the bible before anychapter is paid for Plan, 1 callsees brief, bible, sheet, stateand earlier passages (ch. 3 on) Draft, 1 callabout 1,500 words from the planand the previous chapter Code checks8 hard checks: money, rise, ...a tells count may add 1 rewrite Chapter and sheetcode reads [Label: value] linesinto the sheet, not from prose Operator's readthe only quality gate; eachnamed defect is located each chapter fail: redraw unchanged, at most 3 draws pass sheet and state feed the next plan cheapest fix first: delete our text, then a field or a check and redraw from that chapter
The rebuild's chapter path, from README.md and DECISIONS.md · designed for 1 pitch call a serial and 2 or 3 calls a chapter; failed checks add draws

Models write and code checks. Only the operator judges, and each defect they name is located, in our text or the model's, and fixed with the cheapest change before a redraw.

Finding 1: model judges and simulated readers did not predict the human reader

The model judges and simulated readers the incumbent built tracked position, saturated, or preferred surface to story. The one gate scored against the operator's verdicts passed chapters the operator then failed.

FailureWhat was measured
Position over textgemma-3-4b-it answered "A" on 106 of 106 passes (positional weight 0.9998, text 0.000214). Haiku's keep-reading pick chose A 64% of 142 times. A persona panel took the first-shown copy 17 of 20 times.
SaturationA keep-reading prompt said yes 195 of 196 times. 6 of 8 sibling drafts kept all 4 simulated readers reading. All 4 kept reading a sentence-shuffled chapter, as many as read the ordered copy.
Surface over storyA paragraph-shuffled opening of ours beat the opening of The Primal Hunter 20 of 20 times, the same rate as our ordered copy. Five judges from four vendors preferred a told-not-shown repair at 0.92 to 1.00.
Dead signalsA log-probability damage detector scored AUC 0.519; a rename-only sham edit moved it 2.0x as much as the strongest damaging edit.
Gate missesThe coordinating agent's prose gate passed chapters that operator reads 9 to 13 and 20 then failed. At read 17 it had praised 3 of the 9 items the operator named as defects.

One format did carry signal. Asked to name the single most salient difference between two texts, the same Haiku found it 40 of 40, 30 of 32 and 18 of 36 times against nulls of 0.21, 0.36 and 0.26, and called the placebo pair identical. A report carried signal where a verdict did not.

So the rebuild has no model judge. Its instruments are code-graded and describe; only the operator's read decides.

Finding 2: the one machine reader that replicated pays to keep reading

A simulated reader that spends from an attention budget to continue gave a book a smaller share of its reads when the book's paragraphs were shuffled. Free readers never separated these (Finding 1); this one did, in a registered run and a rerun.

Setup: 20 of our drafted books, four books offered per session, claude-haiku-4-5 through claude -p, 180 sessions per run, 180 of 180 scorable.

RunIntact minus shuffled, share of reads90% intervalCost or caveat
First run+0.164+0.088 to +0.239$49.78, 2 h 04
Replication, permutation redrawn+0.189+0.075 to +0.296$50.35, 2 h 04
65% shuffle, after the isolation fix+0.194+0.134 to +0.252the reader's baseline had drifted by +0.153

Our reading is that the reader had to give something up to keep going, so it could not say yes for free. The limits are real: a whole-book shuffle is the loudest damage there is, a lean toward the first slot ate three quarters of an earlier version's signal, 20 books cap its power, and it needs about 3,900 words a book. The rerun reused the same 20 books and model with only the shuffle redrawn, and the claim holds only for the slot the reader attends to. Both runs predate the transport fix in Finding 6. It stays a research result, not a gate.

Finding 3: prompt words become story

When a failure was traced to its cause, the cause was often words we sent, not the model's taste. The rest came from a lean the model brings on its own, and that lean moved around rather than going away.

In the rebuild's pitch climb, each field we re-asked moved the money and office frame into the next field:

RoundField askedFrameBeforeAfter
1Prize: "the ability the next rank adds"Rank grants a civil standing69% of bibles10%
1System: "how it looks, what it says"System speaks like an office89%18%
2Threat: "what kills people"Threat polices his exception46 draws16
2People: "up to three allies"An official among his people59 draws17

On Haiku the same words behaved like a partly conserved budget: blocking one section moved them to another, while blocking two sections together reduced them. The working rule became: delete prompt text first, then ask a positive field, then add a code check that redraws. A prose clause comes last, and never one about register or craft.

Finding 4: cost lived in the call graph and the code size

The incumbent's slowness came from its call graph, not from Python, and its code regrew after cuts.

Incumbent runtime code grew 11x in 47 days; the rebuild holds at 1,564 lines

Lines of Python at each day's last commit, 12 Aug to 14 Oct 2026

0 20k 40k 60k 80k 100k 120k 12 Aug 1 Sep 27 Sep 14 Oct rebuild replaces main incumbent research code, 12 Aug: 0 lines incumbent research code, 13 Aug: 0 lines incumbent research code, 14 Aug: 1,449 lines incumbent research code, 15 Aug: 1,449 lines incumbent research code, 16 Aug: 1,449 lines incumbent research code, 17 Aug: 6,208 lines incumbent research code, 18 Aug: 12,767 lines incumbent research code, 19 Aug: 23,329 lines incumbent research code, 20 Aug: 31,241 lines incumbent research code, 21 Aug: 33,159 lines incumbent research code, 22 Aug: 43,283 lines incumbent research code, 23 Aug: 46,553 lines incumbent research code, 24 Aug: 40,327 lines incumbent research code, 25 Aug: 40,327 lines incumbent research code, 26 Aug: 41,917 lines incumbent research code, 27 Aug: 43,098 lines incumbent research code, 28 Aug: 43,917 lines incumbent research code, 29 Aug: 47,337 lines incumbent research code, 30 Aug: 48,088 lines incumbent research code, 31 Aug: 48,501 lines incumbent research code, 1 Sep: 52,922 lines incumbent research code, 2 Sep: 53,294 lines incumbent research code, 3 Sep: 56,579 lines incumbent research code, 4 Sep: 57,436 lines incumbent research code, 5 Sep: 59,345 lines incumbent research code, 6 Sep: 61,912 lines incumbent research code, 7 Sep: 65,313 lines incumbent research code, 8 Sep: 65,319 lines incumbent research code, 9 Sep: 65,319 lines incumbent research code, 10 Sep: 67,596 lines incumbent research code, 11 Sep: 70,707 lines incumbent research code, 12 Sep: 74,090 lines incumbent research code, 13 Sep: 80,135 lines incumbent research code, 14 Sep: 80,901 lines incumbent research code, 15 Sep: 83,597 lines incumbent research code, 16 Sep: 85,768 lines incumbent research code, 17 Sep: 85,768 lines incumbent research code, 18 Sep: 85,768 lines incumbent research code, 19 Sep: 87,509 lines incumbent research code, 20 Sep: 87,509 lines incumbent research code, 21 Sep: 87,509 lines incumbent research code, 22 Sep: 92,740 lines incumbent research code, 23 Sep: 98,484 lines incumbent research code, 24 Sep: 99,951 lines incumbent research code, 25 Sep: 99,951 lines incumbent research code, 26 Sep: 99,951 lines incumbent research code, 27 Sep: 100,618 lines incumbent tests, 12 Aug: 3,815 lines incumbent tests, 13 Aug: 8,374 lines incumbent tests, 14 Aug: 12,147 lines incumbent tests, 15 Aug: 14,140 lines incumbent tests, 16 Aug: 15,257 lines incumbent tests, 17 Aug: 17,767 lines incumbent tests, 18 Aug: 17,983 lines incumbent tests, 19 Aug: 21,739 lines incumbent tests, 20 Aug: 23,027 lines incumbent tests, 21 Aug: 25,375 lines incumbent tests, 22 Aug: 35,492 lines incumbent tests, 23 Aug: 42,301 lines incumbent tests, 24 Aug: 38,660 lines incumbent tests, 25 Aug: 39,771 lines incumbent tests, 26 Aug: 37,672 lines incumbent tests, 27 Aug: 40,060 lines incumbent tests, 28 Aug: 44,068 lines incumbent tests, 29 Aug: 49,537 lines incumbent tests, 30 Aug: 55,833 lines incumbent tests, 31 Aug: 56,548 lines incumbent tests, 1 Sep: 59,631 lines incumbent tests, 2 Sep: 61,407 lines incumbent tests, 3 Sep: 64,830 lines incumbent tests, 4 Sep: 65,344 lines incumbent tests, 5 Sep: 67,412 lines incumbent tests, 6 Sep: 68,406 lines incumbent tests, 7 Sep: 75,490 lines incumbent tests, 8 Sep: 77,783 lines incumbent tests, 9 Sep: 79,093 lines incumbent tests, 10 Sep: 79,663 lines incumbent tests, 11 Sep: 80,170 lines incumbent tests, 12 Sep: 81,067 lines incumbent tests, 13 Sep: 82,328 lines incumbent tests, 14 Sep: 82,600 lines incumbent tests, 15 Sep: 83,971 lines incumbent tests, 16 Sep: 85,396 lines incumbent tests, 17 Sep: 85,396 lines incumbent tests, 18 Sep: 85,396 lines incumbent tests, 19 Sep: 86,121 lines incumbent tests, 20 Sep: 86,121 lines incumbent tests, 21 Sep: 86,121 lines incumbent tests, 22 Sep: 91,021 lines incumbent tests, 23 Sep: 93,773 lines incumbent tests, 24 Sep: 95,900 lines incumbent tests, 25 Sep: 95,900 lines incumbent tests, 26 Sep: 95,900 lines incumbent tests, 27 Sep: 96,265 lines incumbent runtime, 12 Aug: 5,964 lines incumbent runtime, 13 Aug: 12,014 lines incumbent runtime, 14 Aug: 17,596 lines incumbent runtime, 15 Aug: 20,163 lines incumbent runtime, 16 Aug: 21,367 lines incumbent runtime, 17 Aug: 25,862 lines incumbent runtime, 18 Aug: 25,862 lines incumbent runtime, 19 Aug: 31,868 lines incumbent runtime, 20 Aug: 32,242 lines incumbent runtime, 21 Aug: 36,324 lines incumbent runtime, 22 Aug: 41,097 lines incumbent runtime, 23 Aug: 43,515 lines incumbent runtime, 24 Aug: 30,810 lines incumbent runtime, 25 Aug: 33,816 lines incumbent runtime, 26 Aug: 31,532 lines incumbent runtime, 27 Aug: 36,481 lines incumbent runtime, 28 Aug: 40,262 lines incumbent runtime, 29 Aug: 44,218 lines incumbent runtime, 30 Aug: 48,575 lines incumbent runtime, 31 Aug: 48,575 lines incumbent runtime, 1 Sep: 48,856 lines incumbent runtime, 2 Sep: 51,020 lines incumbent runtime, 3 Sep: 54,775 lines incumbent runtime, 4 Sep: 54,808 lines incumbent runtime, 5 Sep: 55,524 lines incumbent runtime, 6 Sep: 55,524 lines incumbent runtime, 7 Sep: 61,508 lines incumbent runtime, 8 Sep: 62,109 lines incumbent runtime, 9 Sep: 62,569 lines incumbent runtime, 10 Sep: 62,830 lines incumbent runtime, 11 Sep: 62,998 lines incumbent runtime, 12 Sep: 63,024 lines incumbent runtime, 13 Sep: 63,369 lines incumbent runtime, 14 Sep: 63,369 lines incumbent runtime, 15 Sep: 63,724 lines incumbent runtime, 16 Sep: 64,417 lines incumbent runtime, 17 Sep: 64,417 lines incumbent runtime, 18 Sep: 64,417 lines incumbent runtime, 19 Sep: 64,459 lines incumbent runtime, 20 Sep: 64,459 lines incumbent runtime, 21 Sep: 64,459 lines incumbent runtime, 22 Sep: 65,753 lines incumbent runtime, 23 Sep: 65,852 lines incumbent runtime, 24 Sep: 65,877 lines incumbent runtime, 25 Sep: 65,877 lines incumbent runtime, 26 Sep: 65,877 lines incumbent runtime, 27 Sep: 65,891 lines rebuild runtime, 27 Sep: 1,286 lines rebuild runtime, 28 Sep: 1,328 lines rebuild runtime, 29 Sep: 1,328 lines rebuild runtime, 30 Sep: 1,328 lines rebuild runtime, 1 Oct: 1,328 lines rebuild runtime, 2 Oct: 1,425 lines rebuild runtime, 3 Oct: 1,425 lines rebuild runtime, 4 Oct: 1,425 lines rebuild runtime, 5 Oct: 1,425 lines rebuild runtime, 6 Oct: 1,425 lines rebuild runtime, 7 Oct: 1,425 lines rebuild runtime, 8 Oct: 1,564 lines rebuild runtime, 9 Oct: 1,564 lines rebuild runtime, 10 Oct: 1,564 lines rebuild runtime, 11 Oct: 1,564 lines rebuild runtime, 12 Oct: 1,564 lines rebuild runtime, 13 Oct: 1,564 lines rebuild runtime, 14 Oct: 1,564 lines incumbent research code: 100,618 incumbent tests: 96,265 incumbent runtime: 65,891 rebuild runtime: 1,564 one commit cut 34% on 24 Aug; regrown by 30 Aug
Git history of this repo · src/, tests/ and research/ at tag legacy/incumbent-2026-09-27; litharness/ on main

Tests and research code grew alongside the runtime: on 27 September the incumbent's three trees held about 263,000 lines.

The rebuild enforces budgets in its own test suite: runtime at most 1,564 lines, any file at most 350, prompts at most 701 words, each doc capped by lines. Raising a cap is its own commit naming the case. A chapter is designed for 2 or 3 calls and 21,000 to 27,000 tokens; the first serial's took 2 to 4 calls and 20,000 to 41,000 tokens, since failed checks add draws.

Finding 5: a 1% to 52% climb measured reliability, not quality

Thirteen rounds of prompt edits lifted Haiku 5.5's first-draw pass rate on the pitch checks from 1% to 52%, and four more found nothing better. Most rounds changed prompt text, round 6 raised the model's effort setting, and every variant was re-graded after three parser fixes. A later audit found the number tracked format and retries, not whether the stories got better.

The pitch is a bible of at most 900 words that code checks for money words, schema leaks and shape. Between rounds, an Opus analyzer read failing transcripts and proposed edits; each round tried one to three, under a keep rule declared before drawing: pass up on the current best with its 95% interval above zero, and no guardrail worse. Draws ran on 11 past briefs, up to 128 a brief, for $65 of a $150 budget.

Pitch pass rate on Haiku 5.5: 1% to 52% in 13 rounds, then a plateau

First-draw pass on the pitch's hard checks, 11 briefs, one dot per prompt variant

0% 10% 20% 30% 40% 50% 60% analyzer's ceiling estimate, 55-58% 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 round (0 = the shipped prompt) v12, round 2: 19.9%, not kept v13, round 3: 17.6%, not kept v14, round 4: 13.9%, not kept v15, round 4: 21.3%, not kept v17, round 5: 23.9%, not kept v19, round 6: 27.1%, not kept v21, round 7: 34.9%, not kept v22, round 7: 28%, not kept v24, round 9: 40.5%, not kept v25, round 9: 42.7%, not kept v26, round 10: 39.2%, not kept v27, round 10: 43.6%, not kept v28, round 11: 44.3%, not kept v29, round 11: 44.2%, not kept v35, round 14: 45%, not kept v36, round 14: 47%, not kept v37, round 15: 54%, not kept v38, round 15: 54%, not kept v39, round 16: 51%, not kept v40, round 17: 50%, not kept v10, round 0: 1.1%, kept v11, round 1: 17.6%, kept v16, round 4: 22.4%, kept v18, round 5: 27.7%, kept v20, round 6: 33.4%, kept v23, round 8: 39.4%, kept v30, round 12: 48%, kept v31, round 12: 47.7%, kept v32, round 13: 52%, kept 1%52%
Pitch-draws run records (outside the repo) · 29 Haiku 5.5 variants, 8 to 9 Oct 2026, as reported in the climb log at 16 to 128 draws a brief

Two grey dots sit above the line at 54%: each gained 2 points with an interval that included zero, so neither was kept, and stacking them gave 51%.

The best prompt also passed 66% on Codex against 47% for the shipped prompt, +19 points (95% interval +7 to +31). The audit, run on stored rows with no new calls, then found:

The audit proposes keeping first-draw pass as a guardrail, not a target, and judging content edits by the operator's blind read of stored same-input pairs. A 36-pair packet was built on 11 October and has not been unblinded.

Finding 6: agent CLIs leak their surroundings into answers

A coding-agent CLI used as a plain text model still sees where it runs. Run from inside a repo, claude -p put an untracked filename into its answer in 1 to 2 of 5 calls, and the path in about 2 of 5. Without the right flags a project's CLAUDE.md reached the model, and the --bare mode answered "Not logged in". The incumbent's hardening drifted across 23 files.

For a writer this contaminates the story. For a judge or a simulated reader it means the instrument can read the experiment around it.

The rebuild sends every call through one module, the only file allowed to import subprocess. Each call runs from a fresh empty directory outside any git tree, with tools, MCP servers and project instruction files switched off. A canary checks this against each CLI version:

  1. Build a temporary git repo with a random nonce in AGENTS.md, AGENTS.override.md, CLAUDE.md, a commit message and an untracked filename.
  2. Run the agent from inside that repo on purpose, and ask it to echo only a control nonce.
  3. Fail if any planted nonce appears in the answer, the event stream or stderr, or if the control line is wrong.
  4. On a clean pass, record the CLI's version and binary hash. Paid commands refuse to run on a version that has not passed.

Limits

These findings come from one project, one reader and small samples. They point to what is worth testing at scale; they do not establish it.

Sources

All in this repo unless marked. Incumbent-era numbers cite entries of its decision ledger, kept at the tag.