Skip to content

Intent assistant: patch-shaped proposals - stop re-emitting the COMPLETE app.intent on every turn #6958

Description

@delchev

Problem

The intent assistant's tool contract demands the whole document every time: the propose_intent tool's one content argument is "The COMPLETE updated app.intent YAML document", and the repair prompt re-demands "the corrected COMPLETE YAML". There is no patch shape.

Cost therefore scales with total model size, not the size of the change: a one-field edit to a 400-line application re-emits all 400 lines, every repair round re-emits them again, and the output ceiling (DIRIGIBLE_INTENT_AI_MAX_TOKENS) plus the token bill tighten exactly as the application grows — the opposite of what an editing assistant needs. An agent editing files replaces the lines that changed; the assistant's equivalent is retyping the file.

The prior decision this revisits — the PR must answer it

The whole-document contract was deliberate: "full proposed YAML + we diff it" was chosen over LLM-authored unified diffs (fragile to apply — offsets drift, context lines mismatch) and structured edit-ops (lose comments and formatting). The design notes also record the original intent was the opposite ("edit shape, not file shape … Claude's job is proposing a patch"), so this is a revisit of a reversal, and a patch-shaped contract only lands if it beats both objections:

  • Apply fragility → not unified diffs. Anchored structured edits applied server-side against the current buffer content (e.g. replace/insert/delete keyed to a YAML path or an exact-match text anchor, refused loudly on a non-unique or missing anchor), with the full-document proposal kept as the fallback the model may still choose for a large rewrite.
  • Comment/format loss → apply as text-range splices on the original document (locate the anchored node's byte range, splice the replacement), never a parse-serialize round-trip; everything outside the spliced ranges is byte-identical by construction, which also preserves the diff-stability contract the DSL's design depends on.
  • Validation unchanged → the server materializes the patched full document, runs the same validation pipeline (parse today, dry-run generation once Intent assistant validates proposals only with the parser - extend the repair loop through a dry-run generation #6956 lands), and hands the editor the same complete-YAML proposal it renders as a Monaco diff today — so the editor/Builder accept path can stay almost untouched; what changes is what travels over the upstream wire.

Scope

Its own PR, and a larger one — it touches the shared ai/ layer's tool contract and repair prompt, the apply path, and both surfaces (Intent Editor pane + Builder shell). Sequenced after #6955 (streaming/thinking/budget) and #6956 (dry-run validation): both reduce the pressure this removes, and their tests are the safety net for changing the contract.

Acceptance

  • An edit's upstream output cost is proportional to the change, not the document (assert on a scripted upstream: a one-entity edit to a large fixture proposes a patch, not the file).
  • A patched document is byte-identical outside the edited ranges — comments, key order, formatting preserved.
  • A non-applicable patch (stale anchor, non-unique match) is refused into the repair loop, never half-applied.
  • The full-document proposal path still works (fallback), so a stubborn model or a rewrite-sized change degrades to today's behaviour, not to failure.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions