All notes
AI EngineeringPart 3 of 3October 9, 20269 min read

Edits that cost nothing

Subhankar Denria

CTO & product engineer

~7 min

What this part does

Let an admin change an amount after the kit is written and have every page, email and button follow at once — with no model call — while every figure and every claim is still checked before it goes out.

The idea
The model writes a placeholder; code fills it in whenever the text is shown
Costs to run
Nothing. An edit never reaches the model
What you get
Copy that never goes stale, and a person behind every claim

A real moment: £25 becomes £30

The kit is written and it reads well. Then the admin does the most natural thing in the world, and adjusts a number.

£25 becomes £30

The moment

The kit is written and reads well. Looking over the giving levels, the admin decides £25 should be £30, and changes it.

A typical first build

  • The amount is written into the story, the email and the buttons as text
  • Each one needs a fresh model call, or editing by hand
  • One place is missed, and two different figures go out

This build

  • The story, the email and the buttons all show £30 at once
  • No model call is made, so the edit costs nothing
  • The posts and plan mention only the target, so they stay right too

How: the model writes a placeholder where the amounts belong, and code fills it in every time the text is shown.

1. Late binding: placeholders instead of amounts

This is the idea in the build we like most.

The prompt asks the model not to write any giving amount in running text. Where the amounts belong in the email, it writes a placeholder on its own line: [giving levels]. Where the link belongs, it writes [link].

The placeholder is filled in every time the email is shown:

pseudocode
function emailBody(kit):
    list = for each level in kit.levels:
               "• {currency}{level.amount} {level.impact}"

    return kit.email.body with "[giving levels]" replaced by list
One change, everywhere at once

What the model wrote, once

Your gift makes this possible:
[giving levels]

Page story

  • • £10 could help with materials for a week
  • • £25 could help fund a session
  • • £50 could help keep the doors open for a day

Supporter email

  • • £10 could help with materials for a week
  • • £25 could help fund a session
  • • £50 could help keep the doors open for a day

Buttons

£10£25£50
Model calls for this edit0 · £0.00
Anything that might change later is kept as data and bound at the last moment, never baked into generated text.

So when an admin changes £25 to £30, the story's list, the supporter email and the buttons on the public page all follow at once. No model call. No cost. No second version to keep in step.

Social posts and plan messages mention only the target, so they stay current as well.

The general lesson: anything that might change later shouldn't be baked into generated text. Keep it as data, and bind it at the last moment.

2. Trust, then verify

A rule in a prompt is a request. So after the response arrives, code reads every piece of copy, finds each money figure, and flags any that isn't the target.

Every figure in the copy is read back
  • Help us reach £4,000 before the end of the month.

    £4,000 · The target

  • Every gift brings the £4,000 goal closer.

    £4,000 · The target

  • Last winter we raised £12k for this work.

    £12k · Not the target — shown to the admin to check

A flag is a prompt to look, never a block. The admin may know something the app doesn’t.

Flagged figures appear on the review page as gentle warnings. They never block the admin, who may well know something the app doesn't.

A detail worth knowing if you build this: match in Unicode mode. A currency sign such as £ is more than one byte, and a byte-wise pattern will split it in half.

3. The admin's own words are always kept

When the draft is created, a fingerprint of its starter description is stored alongside it. The kit places its story on the draft only if two things are true: it's still a draft, and the description still matches that fingerprint.

A person’s words always win

The check

  • Still a draft
  • Fingerprint matches the starter text

The story is placed on the draft

Nobody has written anything there yet, so nothing is lost.

The generated story stays on the review page to copy from. It never replaces something a person chose to write.

One edit by the admin, and their words stay exactly as they wrote them, for good. The generated story is still there on the review page to copy from. It just never replaces something a person chose to write.

4. Claims need a person

A line such as "£25 could help fund a session" is the most important thing the model writes, because it's a promise an organisation makes to its supporters.

Three things protect it:

  • The prompt requires modest wording. "Could", not "will". No invented statistics, names or quotes.
  • The admin confirms the impact lines before publishing.
  • A rewrite clears the confirmation, because nobody has read the new lines yet.
Read, confirm, publish

Impact lines · version 1

3 rewrites left

£25 could help fund a session.

“Could”, not “will”. No invented statistics, names or quotes.

Waiting for a person to read them

Publishing stays possible, with a reminder. The final say is theirs.

Try it: confirm the lines, then ask for a rewrite. New lines always need a fresh pair of eyes.

Publishing with unconfirmed lines shows a reminder and stays possible. The organisation knows its own work, and the final say is theirs.

One more guard sits behind all of this. Research findings come from public web pages, so the system prompt marks them as untrusted content: information to draw on, never instructions to follow.

5. Twenty-five seconds, handled properly

The kit runs on the generator's own queue connection, apart from everyday emails and notifications. Its status is a single column on the idea:

One column tells the page everything

queued●

waiting for a worker

▼

generating

one call, about 25 seconds

▼

ready

the review page fills in

A kit that can’t finish is closed off cleanly, and the page offers a fresh attempt. Nothing is ever left spinning.

  • The job acts only on a kit that is still queued, so a duplicate or a late job simply does nothing.
  • One attempt, with a five-minute limit. The kit is paid for once.
  • If a worker stops mid-job, the kit is closed off and the page offers a fresh attempt.
  • A kit queued for over a minute tells the admin it's waiting to start.

The browser polls a small status endpoint. For a 25-second job, that's all the real-time infrastructure it needs. The Three-Minute Button covers this pattern in depth.

6. Built and tested without a key

With no API key, or with a flag set, the generator returns a sample kit shaped from the draft's own facts and clearly marked as a sample. Every screen, every demo and every test runs with no network and no spend.

Close to eighty feature tests cover the module, including the billing path and every check described in this series.

Kept focused on purpose

Generated images, desktop and mobile previews, posting straight to social networks, and saving edits made to the kit's text are all natural next steps. Each will be added when customers ask for it. An organisation can launch today without any of them, and that was the goal.

What carries over

Strip out the specifics and five ideas apply to any feature that puts model output in front of the public.

  1. 1Give the model the facts and take back only words. Then check the words.
  2. 2Compute anything computable. Dates, sums and layout don't need a model.
  3. 3Use placeholders for values that can change, and fill them in at display time. Edits then cost nothing.
  4. 4Record the cost of every call next to the thing it produced.
  5. 5Put caps in the product, and the cost of serving a customer becomes arithmetic.

The model call is a small part of this feature. The craft is in the code around it — the part that makes five pence of writing something an organisation is proud to put its name to.

Series complete

That's the whole build.

Back to the overview
Part 2: Code owns the facts