The Solid Convergence, March 20, 2026 (in which two December solutions are exhumed, one is found to have become a foundation and the other a cautionary tale, the laws of software development are found to agree with each other for the first time in recorded history, and a form field discovers it has two audiences)
Previously on The Solid Convergence…
The Poor Man’s Signals had shipped on December 15. Six lines of JavaScript. A for-loop. Headers as state. There was no step 3.
The Window That Opened Both Ways had shipped on January 12, building on ideas from December 16. Markdown as the universal interface. The spec that renders is the spec that informs. The answer was markdown all along.
Two solutions. Same season. Same problem: how does the AI know what the user is looking at?
One was a lizard. One was a squirrel. They were born six days apart.
Three months later, the laws chose.
December 15: The Valve
The problem was sidebar state. Compact mode vs. detailed mode. The server needed to know which to render. The client needed to tell it.
The Squirrel reached for signals.
The Lizard said: “Put it on the body.”
document.body.addEventListener('htmx:configRequest', (e) => {
for (const [key, value] of Object.entries(document.body.dataset)) {
if (key.startsWith('solid')) e.detail.headers[key] = value;
}
});
Six lines. A for-loop that forwards any data-solid-* attribute as an HTTP header. A middleware that collects them. Two specific values needed. A generic pipeline built, because a for-loop is shorter than two if-statements.
The Squirrel stared. “That’s just headers.”
“Poor man’s signals.”
“It’s not reactive.”
“It’s reactive enough.”
The Squirrel filed a mental objection and moved on.
December 16: The Cathedral
The next day, a revelation.
“The markdown,” riclib said. “It’s not just for rendering.”
The whiteboard filled with diagrams. Three readers — renderer, human, AI — all reading the same widget spec. No translation layer. The spec IS the description. The spec IS the state. The spec IS the context. One format flows everywhere.
The Squirrel’s eyes went wide. “The UI IS the prompt.”
The Lizard watched from his rock. He did not blink, which meant either agreement or sleep.
READER 1: The Renderer
Input: widget spec
Output: HTML (cards, tables, charts)
READER 2: The Human
Input: widget spec
Output: Understanding ("ah, it queries stores by status")
READER 3: The AI
Input: widget spec
Output: Context ("user is looking at store health")
Same input. Three outputs. No translation layer.
“This solves JARVIS,” Claude said. “The UI-as-context problem.”
“Solved by accident,” riclib said. “By making widgets markdown.”
A scroll descended. Ornate. Longer than usual.
THE ANSWER WAS MARKDOWN
ALL ALONG
It was a beautiful idea. It was also, quietly, a cathedral.
The Cathedral’s Promise
The vision expanded, as visions do.
If widgets are markdown specs that the AI can read and write, then everything could be markdown specs. Forms. Configs. The entire admin UI. One format, both directions, three readers.
“The AI can write widgets,” riclib said.
“The AI can write forms,” the Squirrel added, leaning forward.
“The AI can write configs,” the Squirrel continued, leaning further.
“The AI can write the whole—”
The Lizard opened one eye.
The Squirrel sat back down.
The Three Laws
Three months passed. The RecordEditor migration arrived. Eleven domains needed new forms. Complex ones. A query provider with dynamic table lists, nested connection types, conditional credential pickers, five type sections that show and hide. A job scheduler with step pipelines, cron builders, add/remove patterns.
The question returned: how does the AI know what the user is editing? And can the AI help edit it?
The markdown-as-UI approach said: model the form as a spec. The AI reads the spec. The AI writes the spec. The renderer renders the spec.
Three laws raised their hands.
Gall’s Law spoke first. “A complex system designed from scratch never works. The markdown form spec would need to handle text inputs, selects, toggles, type switching, dynamic lists, conditional sections, password masking, credential pickers, validation, error display, and the seventeen things you haven’t thought of. You are designing a form DSL. You have zero working implementations. Show me the two simple tools you extracted it from.”
There were no simple tools.
YAGNI spoke second. “You are building a form abstraction to save time writing forms. You will spend more time managing the abstraction than writing the forms. The abstraction handles the easy cases — text input, select, toggle. The hard cases — dynamic query tables with imported columns, step pipelines with add/remove — will fight it. You will extend the DSL. You will extend it again. You will extend it until it is a worse version of HTML.”
The Squirrel’s clipboard trembled.
Zawinski’s Law spoke last. “Every DSL expands until it can describe forms. And those DSLs that cannot so expand are replaced by ones that can. Your markdown spec started as widget definitions. It is now attempting to become a form language. Given enough time, it will attempt to read mail.”
The Valve, Revisited
While the cathedral sank under the weight of its own ambition, the valve sat on its stone pedestal. Dusty. Unused. The sidebar resize modes it was built for had been deleted a month earlier. The feature was dead. The for-loop remained.
Then someone needed to tell the AI which editor panel was open.
“We have something like that already,” riclib said.
Six words. The machine found the valve. The valve had been waiting.
EditorPanel("skill", editorID) stamped two attributes on the body. The existing for-loop forwarded them. The existing middleware collected them. Eleven editor panels, eleven one-line changes. Zero new infrastructure.
Net delta: minus five lines.
The valve had outlived the river it was built for and was now feeding eleven new ones.
The Hint, Not the Spec
But the AI still needed to understand what was in the form. The cathedral’s question was real, even if the cathedral’s answer was wrong.
The old answer: model the UI as markdown. One format for everything. The AI reads the spec that generates the form.
The new answer: enrich the existing UI with hints.
The form stays rich, bespoke HTML — templ components, hand-crafted per domain. The credential form and the query provider form share almost nothing, and they shouldn’t pretend to. But every RecordInput already has a name, a label, a value, a data-original. Add one more thing: a description.
"The unique identifier for this store. Used in job configs
and query provider references. Cannot be changed after creation."
For the human: an (i) icon next to the label. Hover to read. Unobtrusive.
For the AI: the same text, scraped from the DOM. Name, label, value, description. Structured context, pulled on demand.
The form doesn’t change shape. It just becomes legible to a second audience.
The Squirrel stared at this for a long time. “That’s not a window that opens both ways.”
“No,” the Lizard said. “It’s a window that was always open. We just hung a sign on it.”
The Squirrel’s True Role
But here is what the tidy narrative misses.
Without the cathedral, there is no hint.
The vision — the AI should see what the human sees, through the same format, without translation — was correct. It was always correct. December 16’s revelation was real. The spec that renders IS the spec that informs. Three readers, one input. That insight survived the cathedral’s sinking. It just changed shape.
The cathedral said: model the entire UI as markdown. The hint says: enrich the existing UI with descriptions. Same vision. Different scale. The hint is the cathedral compressed to napkin size — the same insight, stripped of the enthusiasm that made it a DSL.
The Squirrel does not over-engineer. The Squirrel scouts. The 827-line manifesto that shipped as six lines of HTMX was not wasted — it was reconnaissance. The markdown-as-UI dream that sank under its own weight was not a mistake — it was the first draft of a napkin that hadn’t been written yet.
Every Squirrel proposal contains a napkin. The Lizard’s job is not to reject the proposal. The Lizard’s job is to find the napkin inside it.
THE CATHEDRAL WAS NOT WRONG
THE CATHEDRAL WAS EARLYTHE NAPKIN INSIDE THE CATHEDRAL
IS THE ONE THAT SHIPS— The Lizard, being uncharacteristically generous
The Laws Agree
Here is the thing about Gall’s Law, YAGNI, and Zawinski’s Law. They are usually cited independently, as if they were separate principles from separate traditions observing separate phenomena. They are not. They are three descriptions of the same force, viewed from different angles.
Gall’s Law says: complex systems evolve from simple ones that work. The data-solid-* convention was a simple system that worked. It evolved — not by design, but by discovery — into the foundation for editor context, LLM awareness, and eventually dual-audience forms. The markdown-as-UI cathedral was a complex system designed from scratch. It never worked for forms, because it couldn’t. The working simple system hadn’t been built yet.
YAGNI says: don’t build what you don’t need. The for-loop that forwards all solid* attributes was YAGNI-compliant — it was less code than forwarding two specific ones. The markdown form DSL was a YAGNI violation — an abstraction built for forms that didn’t exist yet, to solve problems that hadn’t been encountered yet.
Zawinski’s Law says: every abstraction expands until it can describe everything. The markdown widget spec worked for widgets because widgets are simple — a type, a query, a title. But the moment it was asked to describe forms, it began expanding. Types. Validation. Conditionals. Dynamic lists. The DSL was expanding toward HTML, which is where it started.
Three laws. One verdict. The valve survives. The cathedral sinks.
The Tally
December solutions born: 2
Days apart: 6
Months until verdict: 3
The Campaign (March 19-20):
Commits: 19
Lines inserted: +6,633
Lines deleted: -13,022
Net: -6,389
Domains migrated: 11
Domains deleted: 1 (reports, 5,242 lines)
Features gained: content editors, LLM context, dual-audience forms
Features lost: 0
The codebase got smaller AND smarter
The Valve (December 15):
Lines of JavaScript: 6
Features it was built for: 2 (sidebar modes)
Features that survived: 0
Rivers it crosses today: 11
New infrastructure added (March): 0
The Cathedral (December 16):
Diagrams on the whiteboard: 4
Readers identified: 3
Ornate scrolls received: 1
Forms it could describe: easy ones
Forms it could not describe: the ones that matter
Laws consulted: 3
Laws that agreed: 3
Times in recorded history that
Gall, YAGNI, and Zawinski agreed: 1 (this one)
Form fields that serve two audiences: all of them (soon)
DSL required: 0
New abstraction layers: 0
Help text added to existing component: 1 string parameter
The valve was boring.
The cathedral was beautiful.
The valve had six lines.
The cathedral had scrolls.
The valve survived the death of its purpose
and found eleven new ones.
The cathedral sank under the weight
of the forms it could not describe.
The laws did not choose the valve.
The laws described why the valve
was always going to be chosen.
A for-loop. A data attribute. A header.
There is no step 3.
But there is a step 4,
which the Squirrel proposed
and the Lizard did not refuse:
hang a sign on the window.
An (i) icon. A description string.
The form field that was always open
to both audiences
without knowing it.
Not a window that opens both ways.
A window that was never closed.
The Lizard blinks.
The laws agree.
The valve holds.
Post-credits scene:
Late at night, the Squirrel is staring at the pipeline.
“DOM context to HTTP context to Go context to LLM context.”
“Four meanings of the word context,” the Squirrel says slowly. “Four completely different systems. And they just… connect.”
The Lizard does not look up.
“The data attribute is a key-value pair. The HTTP header is a key-value pair. Go’s [context.Context](/wiki/contextcontext) is a key-value bag. The LLM system prompt is key-value metadata.”
“Same shape,” the Squirrel whispers. “All the way down.”
“Rob Pike designed context.Context in 2014,” the Squirrel continues. “For cancellation and request-scoped values flowing through goroutines. He could not have known that in 2025, someone would use it to carry context to context — DOM context to LLM context, through Go context. Even the name was correct.”
The Lizard blinks.
“Or could he?”
The Squirrel pulls out the dossier. Pike’s Rules. The contested theory. The circumstantial evidence. The Plan 9 minimalism. The fifteen years without generics. The refusal to add features. The five rules on a napkin.
“Rule 5,” the Squirrel reads. “Data dominates. If you’ve chosen the right data structures, the algorithms will be self-evident.”
The Squirrel looks at context.Context. A key-value bag that flows through goroutines.
The Squirrel looks at data-solid-*. Key-value pairs that flow through the DOM.
The Squirrel looks at solidEditorType: skill. A key-value pair flowing from browser to LLM.
“The data structure is the same at every layer,” the Squirrel says. “Key-value. The algorithm — forward, collect, read — became self-evident the moment the data structure was chosen.”
The Lizard produces a very small scroll.
THE LIZARD DOES NOT CONFIRM
THE LIZARD DOES NOT DENY
THE LIZARD NOTES THAT
context.Context WAS DESIGNED IN 2014
data-solid-* WAS DESIGNED IN 2025
AND NEITHER KNEW ABOUT THE OTHER
COINCIDENCE
The Squirrel files the scroll in the dossier, next to all the other coincidences.
The dossier is getting thick.
See also:
The Two Solutions:
- The Poor Man’s Signals — December 15: the valve
- The Window That Opened Both Ways — December 16: the cathedral
- The Stones in the River — March 20: the valve crosses new rivers
The Laws:
- Gall’s Law — Complex systems evolve from simple ones that work
- YAGNI — Don’t build what you don’t need
- Zawinski’s Law — Every DSL expands until it can describe forms
The Resolution:
- S-520 — Dual-audience form fields: the hint, not the spec
- The Hundred Forms — Solid Edition — The form components the hints attach to
- riclib — The man who places stones
The Changelog:
fbea244feat: pull-based editor context via DOM data attributesa5296a0feat: wire agent to read editor context from solid headers