2026-07-18·2 min read

Pulling the prose engine out of Perpetūra

field-notesperpeturaprose-engineclaude-code

I pointed the linter at a story it had never seen. Not another chapter of Perpetūra, but a contemporary-noir scrap with no love interests, no branches, none of the romance texture the rules were built around. First run, the trunk chapter threw false staccato warnings and a low-mean-sentence flag. Both wrong. The noir’s rhythm was fine for noir; the thresholds catching it were still Perpetūra’s romance defaults, copied over wholesale.

That was the actual test. Not “does it run on another story”, because it’s a linter and of course it runs. The question was whether fixing those false positives meant touching engine code. If it did, the extraction had failed and I’d built a Perpetūra-shaped tool that only works on Perpetūra. Finding that out was the whole point of prose-engine-poc.

It didn’t. I opened profiles/noir.json, tuned the rhythm and register bands, and every false positive cleared. The one chapter with a real problem still fired. I never touched a line of prose-check.js between the two runs.

Everything genre-specific is data

The engine took none of Perpetūra’s story format with it. In its place is load-story.js, about thirty lines that read a directory of chapter files into { id, paragraphs }. Markdown, plain text, whatever an editor exports later, it plugs in behind that one adapter and the engine never learns the difference.

Everything that knows about genre lives in the profile JSON instead. Word lists, the rhythm bands, the register bands, even the single most house-specific rule I own. British spelling collapsed to one boolean, spelling.enforce, off for the noir, so “gray” and “harbor” passed silently. The engine doesn’t know what a love interest is. It reads paragraphs.

The profiler was the cheap win

The best thing that came out of this wasn’t the linter. It was the register-drift profiler, and it almost didn’t get built: in Perpetūra it was never a script, just a one-liner buried in a style-guide doc. Calibrate it to a trunk chapter and it reports four metrics per chapter, then flags drift. Pointed at the noir, a later chapter’s dialogue share collapsed and its sentences bloated well past the trunk’s range, the same mechanical decay I’d caught before in Perpetūra’s own prose, now showing up in a genre with nothing in common with romance. Lifting that one-liner into a real script was the cheapest, highest-value hour of the weekend.

What it still can’t do

The engine catches mechanical tells: em-dashes, banned words, rhythm outside declared bands, register drift against a trunk. It doesn’t know why Hyunjae’s prose lands and a flat draft doesn’t. That’s judgment, and judgment isn’t in the tooling. It’s the thing the tooling frees me to spend more attention on, not a thing it replaces.

There’s a real product question hiding in the profiler. Most writers can’t name their own target numbers for sentence rhythm or dialogue share; I couldn’t, before I built this. But you don’t need to. Profile three chapters of a book you admire, and those numbers become your bands.