Let a model write the program - #10
Merged
Merged
Conversation
The last piece of the DSL work: describe a pattern, and a model writes a program the app then validates and renders exactly as if it had been typed by hand. The program lands in the panel's editor rather than on the canvas, so it can be read and corrected before anything is rendered. Structured outputs do the work. output_config.format constrains the reply to a JSON schema, so there is no prose to parse out of and a bad program is a validation error rather than a parse failure. The schema and the system prompt are both generated from DSL_SHAPES and DSL_RANGES, so neither can drift from the renderer: a primitive added to the DSL appears in both automatically. What the schema cannot express — numeric bounds, lengths, recursion — still goes through dslValidateShape's clamping, which is where it belonged. Raw fetch rather than the Anthropic SDK: the renderer is one HTML file with no bundler and no node_modules, which is the documented raw-HTTP case. The request uses claude-opus-5, where thinking is on by default and max_tokens caps thinking plus output together, so it is sized for both; effort is low, because writing a small program is not work that deeper reasoning improves. Sampling parameters and budget_tokens are rejected by that model and are absent. The key lives in localStorage, is sent only to Anthropic, and never appears in a request body or in the packaged app — both checked. With no key the control is absent rather than broken, and aiWrite() refuses before reaching the network. electron/main.js widens connect-src to api.anthropic.com and nothing else. Not https:, which would let a compromised renderer phone anywhere; verify-electron.py now fails on that, and I checked it fails on https: rather than trusting it. Eighteen behaviour checks cover the request shape and every failure mode — refusal, HTTP error, non-JSON reply, empty reply, unknown shape, empty description, absent key. The model is stubbed; no test needs a key or a network. Two harness bugs surfaced doing this. grabFunction() sliced from `function` and dropped a preceding `async`, so the extracted copy threw "await is only valid in async functions" — an error about the test harness wearing the costume of an app bug; both copies are fixed. And verify-stamps.py scanned the whole script for three-string arrays, so the schema's ['circle','square','diamond'] was read as a stamp and failed to decode as pattern data; it now reads the STAMPS table only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UCrKKTD2mwHx8Fm1EnJiQ3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 6 of the v0.2.0 plan — generation, and the last phase.
Describe a pattern; a model writes a program the app then validates and renders exactly as if it had been typed by hand. The program lands in the panel's editor rather than on the canvas, so it can be read and corrected before anything renders.
Structured outputs do the work
output_config.formatconstrains the reply to a JSON schema — no prose to parse out of, and a bad program is a validation error rather than a parse failure.The schema and the system prompt are generated from
DSL_SHAPESandDSL_RANGES, so neither can drift from the renderer: a primitive added to the DSL appears in both automatically. What a schema cannot express — numeric bounds, lengths, recursion — still goes throughdslValidateShape's clamping, which is where it belonged.Request shape
Raw
fetchrather than the Anthropic SDK: the renderer is one HTML file with no bundler and nonode_modules, which is the documented raw-HTTP case. Usingclaude-opus-5, where thinking is on by default andmax_tokenscaps thinking plus output together — so it is sized for both. Effort islow: writing a small program is not work that deeper reasoning improves. Sampling parameters andbudget_tokensare rejected by that model and are absent. All of this is pinned by tests, because a wrong model or a rejected parameter is a 400 nobody would guess from the UI.The security surface, narrowed
localStorage, is sent only to Anthropic, and never appears in a request body or in the packaged app — both checked.aiWrite()refuses before reaching the network.connect-srcwidens toapi.anthropic.comand nothing else — nothttps:, which would let a compromised renderer phone anywhere.verify-electron.pynow fails on that, and I confirmed it fails onhttps:rather than trusting it.Tests
18 behaviour checks covering the request shape and every failure mode — refusal, HTTP error, non-JSON reply, empty reply, unknown shape, empty description, absent key. The model is stubbed; no test needs a key or a network.
Two harness bugs this surfaced
grabFunction()sliced fromfunctionand dropped a precedingasync, so the extracted copy threw "await is only valid in async functions" — an error about the test harness wearing the costume of an app bug. Both copies fixed.verify-stamps.pyscanned the whole script for three-string arrays, so the schema's['circle','square','diamond']was read as a stamp and failed to decode as pattern data. It now reads theSTAMPStable only.🤖 Generated with Claude Code
https://claude.ai/code/session_01UCrKKTD2mwHx8Fm1EnJiQ3