atelier. Built in a day

Built in a day Β· section 4

The AI flow

The AI flow

πŸ€– One Textarea, Zero Guesswork

Phase 3 is where the app earns the "AI-assisted" in its description β€” and where the design philosophy faces its sternest test. Because nothing erodes trust in a carefully groomed, hand-validated corpus faster than a language model with creative opinions about your metadata.

The flow itself is disarmingly simple. One textarea. Type anything: "fourth wing rebecca yarros", a pasted blurb, an ISBN, "the next three Malazan books". Press one button. Receive one or more proposal cards β€” author, title, publisher, year, pages or duration, categories, tags, series and series number β€” every field editable, nothing accepted until a human says so.

But the simplicity is a stage performance, and this is where the magic happens. The machinery behind the curtain has three acts. Let's unpack them.

Act one: structure, not prose. The app makes a single OpenAI chat-completions call β€” the model is gpt-5.5 β€” with a strict JSON schema. No free-text parsing, no "hopefully the model formats it right". The schema is the contract. And in a detail that does a surprising amount of heavy lifting, the site's closed list of categories travels along in the prompt, so the model picks from what exists instead of inventing taxonomy. The model also writes the one-line Dutch note that becomes the post's body.

Act two: reconcile before showing. Everything the model returns is checked against the repository before the user sees it. Authors are fuzzy-matched on name parts in any order β€” "Rebecca Yarros" resolves to the existing yarros-rebecca β€” and unknown authors are simply flagged as "new, will be created". Tags are de-duplicated against near-misses. Series are matched against the site's existing series pages. And a genuinely new category β€” the one place where the bar stays deliberately high β€” requires an explicit confirmation checkbox. The model proposes. The repository disposes. It's not just validation β€” it's a philosophy.

Act three: plain forms, all the way down. Each proposal card is an ordinary bound Django form β€” several coexist, prefixed β€” which means edited values are re-validated by the same rules automatically, and accepting a card runs the exact same pull β†’ write β†’ lint β†’ commit pipeline as every other write in the app. The AI flow gets no special powers. It fills in forms, like everybody else. Democracy, but for software components.

A blueprint-blue alchemical assembly line: handwritten scrolls feed an ornate boiler labelled 'AI Furnace' with glowing AI chips, which pours molten light onto a golden cassette labelled 'Structured Book Data'; pipes lead to a mirror labelled 'Server Validator' with a holographic shield, then to a porthole labelled 'User Review' showing a book stamped 'Published', beside a wax-sealed scroll reading 'Approved draft for publishing' and a small brass robot.
Figure 7 From free text to published book: the intake pipeline, end to end. The AI proposes; the server validates; the human decides.

Verified with a real API call, the first live test read: "The Tainted Cup van Robert Jackson Bennett, en Fourth Wing van Rebecca Yarros als audioboek". Two cards came back. Bennett matched his existing author slug; the audiobook arrived with a duration of 20u 43m and two narrators as new authors with stem (voice) roles; both books matched existing series pages. Accepting produced exactly one commit: the post, two author pages, two tag pages β€” linted first, naturally. The results speak for themselves.

πŸͺ The Ringworld Problem

Then came feedback round two, and with it the most delicious modelling problem of the day. The request: series input should be recognised and expanded β€” "commonwealth saga" should become Pandora's Star and Judas Unchained, its main constituent books. Simple enough, right?

Wrong. Because, as the feedback itself pointed out: a name like "Ringworld" means both the first book and the whole series. Welcome to natural language, where nothing is ever simple and everything is a special case.

The resolution is a small masterpiece of prompt jurisprudence, with the ambiguity rule spelled out explicitly:

  • Explicit series wording β€” or a name that only exists as a series β€” expands to one entry per main book, in reading order, novellas and side stories excluded.
  • A name that can be both book one and the series, like "Ringworld", without clear series intent β†’ just that one book.
  • An unknown series is never invented. One honest entry with the given title and null fields beats three confidently imagined books every single time.

And the live verification reads like a set of unit tests written by a librarian: "commonwealth saga" β†’ two correct entries. "Ringworld" β†’ one book. "de hele Ringworld-reeks" β†’ all four main books, in order. "Confessions of a trash droid series" β€” unknown to the model β€” β†’ one honest null-entry, exactly as designed. Four inputs. Four correct behaviours. Zero invented books. As a bycatch, the model's occasional comma-style "Hamilton, Peter F." taught the author matcher to be punctuation- and comma-insensitive, both forms now resolving to the site's legacy slug.

A baroque clockwork triptych titled 'Series Decision': three porthole dials wreathed in purple smoke, labelled A 'Whole series' (a shelf of matched volumes), B 'First book only' (an open book with a gear stamped 1), and C 'Unknown to AI' (a locked tome), with piping below leading to indicators reading 'Single entry log' and 'Manual review queue'.
Figure 8 The series decision, demystified: whole series, first book only, or unknown to AI β€” and never, ever an invented book.

πŸ—£οΈ The Tone Audit

The same feedback round contained a second, subtler correction β€” about words, not features. Out went the chatty spinner line ("Even kijken wat dit voor boeken zijn…"), the explanatory labels, anything cutesy β€” and, in a phrasing that deserves preservation, anything "that sounds like it's someone from Holland speaking". A single-user app needs no small talk with its only user. "Hoe ver zit je?" became "Voortgang". A jokey validation quip became "Meer dan 100%." The analyse button, which had given no feedback at all when tapped, now disables itself and reads "Bezig…" until the model answers.

The takeaway? Interface copy is a feature like any other: it ships, it gets feedback, it gets fixed. The best products sweat these details β€” and this one sweats them in two languages.

With all three flows from the brief now live, the app was functional. But functional and finished are two very different words, and the difference β€” covers, home-screen installation, and one very red badge β€” is the subject of the polish.