Pages the Reader Can Change

When I released Lede last month, I borrowed Thariq Shihipar's five kinds of HTML page (specs, code review, prototypes, reports, custom editing interfaces) and said Lede could draw four of them. The fifth was the one where the reader changes something (moves a ticket to another column, nudges a value, rewrites a prompt) and hands the result back to the agent. A Lede page had no script, so it had no way to change.

Lede 0.2 does that, and it keeps the page without a script unless you ask for one.

The checkout prototype

This new release carries the functionality:

@title Checkout prototype
# Checkout prototype

Tune the button, decide what ships, and copy the changes back to the agent.

ask owner | Who signs off on this?
param press unit=x | Press depth | 0.94 0.85 1 0.01
toggle checkout_v2 on | New checkout
toggle express needs=checkout_v2 | Express checkout
bucket triage | Now, Next, Later | Where does each ticket go?
triage: ENG-2041 Fix duplicate emails | Now
triage: ENG-2047 Rotate the signing secret | Next
triage: ENG-2052 Retry, then give up | Later

template prompt | System prompt

preset snappy | Snappy | press=0.96; checkout_v2=on

export prompt
export markdown

With the template body, it's 28 lines. lede build --shell turns it into a 55 KB page.

The page has a text box, a slider paired with a number box, and two switches; express checkout is greyed out until the new checkout is on, which is what needs= says. Below them sit a three-column triage board with a count over each column, a system prompt that fills its {slots} from the fields above it as you type, a button for the snappy preset and two boxes holding the answers.

Four edits, four lines

I made four edits - entered Sam into a text box, moved a slider, switched a toggle, and clicked a radio button. The export prompt box then held this:

Apply these changes to Checkout prototype:

- owner: Sam (was empty)
- press: 0.9 x (was 0.94 x)
- express: on (was off)
- triage: "ENG-2047 Rotate the signing secret" moved to Now (was Next)

Unchanged: checkout_v2

That's the part I wanted. An agent reading it doesn't have to diff two copies of a page or guess what the reader meant; each line names a field, the new value and the old one, and the last line says what the reader looked at and left alone. A Copy button puts it on the clipboard. export markdown gives the same state as a document, for when the answer is going into a PR description instead of back into a chat.

The token savings should be huge; it no longer needs to read a database of answers, the html artifact, or a json response.

One script, pinned

The first post made a safety claim I'm not willing to give up (yet): a page Lede writes has no script tag, so an agent that has been talked into something by an email can't put one there. 0.2 keeps that for every ordinary lede build. Build the checkout file without --shell and you get a 46 KB page with zero script tags; it shows the starting values and the controls don't do anything.

With --shell, the page gets one script. It's the same script on every page, 8,189 bytes against a budget of 8,192, and the build fails if it grows past that. Its hash is written into the page's content security policy, so a browser runs that script and nothing else. The model never writes it; the page's own fields arrive as a JSON block the script reads. It may touch only a short list of things. That's text content, input values, checked state and two attributes (hidden and aria-invalid), and a test reads the script's source on every run to keep the list that short.

Drawing while the model writes

The other library change is streaming. LedeStream in @lede-lang/core now emits events as lines arrive, and createIncremental in @lede-lang/html turns them into page updates, so a page can draw while the model is still writing it. On 178 of the 180 benchmark pages, nothing already on screen changes when the stream ends; the other two get one correction at the end, and either way the finished page is the one lede build writes. There's no viewer app for this yet. It's a library feature, and the package README shows the twenty or so lines that wire it up.

There's one smaller piece. lede check --artifact warns about a local image path that a Claude Artifact can't load, since the Artifact host serves only the page itself.

What it still can't do

The quality gap against frontier HTML hasn't moved in this release; nothing in 0.2 was aimed at it. The first post's table still stands. Against each model's own HTML, Lede pages won or tied 60% of the time for Haiku, 22% for Opus and 10% for Sonnet. The next benchmark round is what measures whether that changes, starting with a refinement test and then 55 fresh held-out tasks I sealed before touching the renderer. I don't have those numbers yet, so I'm not quoting any.

But, I'm also not convinced that this matters yet, and the blind judge is also likely wrong - I honesty prefer Lede's output in at least half the cases - but maybe that is my own bias.

The editors also stop where the format does. There's no drag and drop (a ticket moves by picking its column), no live recalculation beyond filling a template, and the page doesn't save anything; the answers stay in the tab until you copy them.

Trying it

npm install -g @lede-lang/cli gives you the lede command at 0.2. Build examples/parity/editors.lede from the repository with --shell, change four things, and paste what the prompt box says into the agent that wrote the file. If it gets a change wrong, or there's an editor you needed and didn't find, open an issue; the list of what pages couldn't do is still the roadmap.