Desde

Concepts

Deterministic editing

Why most Desde edits never touch a language model, and what happens on the small set that do.

Most tools that let you edit a running app with AI have one lane: describe what you want, a model rewrites your code. Desde has two, and the first has no model in it. Changing a button's label, its variant, its size: these are string replacements at a known position in a known file, and a language model is the wrong instrument for that job.

What "deterministic" means here#

An edit is deterministic when the tool can prove where the change goes before making it.

Your click carried a source location from data-desde-src, a file, line and column stamped in during compilation (see How it works). To apply the edit, the tool re-reads that file from disk and parses it with the same parser that produced the stamp: @vue/compiler-sfc for a Vue single-file component, @babel/parser for .tsx and .jsx.

Parsing gives an AST, an abstract syntax tree, the structured representation a compiler builds from source text. Every node in it knows its exact character range in the file. So the tool walks the tree, finds the element whose position matches the coordinates the click carried, and locates the precise byte range of the attribute to change.

Then it splices. The new value goes in verbatim at that range; the rest of the file is untouched.

<!-- before -->
<KButton appearance="secondary">Save</KButton>
 
<!-- after: appearance edited to "primary" -->
<KButton appearance="primary">Save</KButton>

The value type decides the shape. A string becomes a quoted attribute; a number or boolean becomes a v-bind shorthand (:count="5"), because that is what Vue needs to pass a non-string.

There is no diff search, no "find the text that looks like this," no model deciding what your file should say. The applicators are pure functions: source string in, source string out.

After splicing, the result is re-parsed before it is allowed to be written. If the splice produced markup the compiler rejects, the edit is refused rather than saved. An edit producing no change at all is also refused, so the tool can never report "saved" over an unchanged file.

What that buys you#

Speed and cost. No round trip to a model. The change appears at hot-reload speed, and nudging a value forty times in an afternoon costs nothing.

Trust. A deterministic applicator cannot hallucinate. It cannot rename an adjacent variable, reformat a block it was not asked to touch, or invent a prop that does not exist. The failure mode is a refusal with a reason, not a plausible-looking wrong answer.

Reviewable diffs. This matters most in a shared repo. A deterministic edit produces a one-line diff. Your teammate sees appearance="secondary" become appearance="primary" and is done. When a model rewrites a file, even a correct rewrite tends to arrive as a large diff a reviewer has to read in full to be sure nothing else moved.

Deterministic applicators cover more than props. Slot text, deletes, moves, inserts and unwraps each have a Vue-template implementation and a JSX one. Design-token edits rewrite a custom property in a plain CSS file, so they are framework-neutral.

Styling splits by whether the element is yours to edit, and both halves work on both substrates:

  • You own the element. The edit lands on the element itself: a class in the SFC template for Vue, a merge into className or style for React.
  • You do not own it: it is rendered inside a component from a package, so there is no source position for it anywhere in your repo. The edit becomes a CSS rule anchored on the nearest source position that did reach the page, and reaches inside from there. On Vue that rule goes in the consumer's <style scoped> block (using :deep() to cross the scope boundary); on React it goes in a project stylesheet your app already imports, and needs no piercing because React has no style scoping. Editor tells you which file before it writes, and how many elements the rule will cover.

Two kinds have a Vue applicator and no JSX one: component swap and component detach. Rather than offer a control that can never land on a React project, both are dormant: off unless a project sets lanes.swap / lanes.detach. See Swap, Detach and the icon picker.

When the model is engaged#

Some source genuinely cannot be edited by position. Three shapes are the reason.

A bound binding. The prop is written :label="title", where title is a variable rather than a literal. Replacing it with label="New text" would compile, and would silently destroy the connection to title. The value you want is not here. It is wherever title is defined, possibly in another file.

A v-model. v-model="x" is shorthand for a prop and an event handler working together. Inserting a literal prop of the same name next to it either overrides the binding and breaks two-way data flow, or sits alongside it with undefined precedence.

A dynamic v-bind. The element carries v-bind="someObject" or :[computedName]="value". Your prop's runtime value might come from that spread, and prop precedence is order-dependent in a way splicing text cannot safely reproduce.

In all three cases the applicator refuses, and the refusal is typed: it carries a machine-readable hint saying which of the three it hit. That hint is the only thing that opens the model lane.

When it opens, the tool runs a short focused agent turn on the same runtime the chat panel uses. It can read and search files to trace title back to its definition, it has the same design-system grounding the chat agent has, and it edits at the source of the binding. The turn is bounded by a cap on conversation turns, a dollar ceiling, and a wall-clock timeout.

The safety around it belongs to the edit server, not to trusting the model. The working tree is snapshotted before the turn and compared after. If the agent claims success but no file actually changed, the edit is refused. If the target file no longer parses, it is restored. The original is journaled to .desde/backups/ either way. The result comes back marked as having used the fallback, so you can see which lane an edit took.

Note The model lane applies to prop edits carrying one of those three hints, and only for string values. A numeric or boolean edit on a bound prop is refused with a suggestion to use chat, which handles it with its full multi-file toolset. Dynamically composed React className and style values are refused the same way.

The design rule#

New edit capabilities get a deterministic applicator first. The model lane widens only after a case has been shown not to be handleable structurally, and a new refusal must declare itself as a typed hint rather than quietly falling through.

The point is not that models are unreliable. It is that the boundary between "provable" and "inferred" should be visible in the product, and should sit as far toward provable as the source allows.