Skip to content

feat(sdk): variable declaration edit ops (declare/update/remove)#2047

Open
jrusso1020 wants to merge 1 commit into
07-07-feat_sdk_variable_declaration_read_apis_browser-safe_variables_entryfrom
07-07-feat_sdk_variable_declaration_edit_ops_declare_update_remove_
Open

feat(sdk): variable declaration edit ops (declare/update/remove)#2047
jrusso1020 wants to merge 1 commit into
07-07-feat_sdk_variable_declaration_read_apis_browser-safe_variables_entryfrom
07-07-feat_sdk_variable_declaration_edit_ops_declare_update_remove_

Conversation

@jrusso1020

@jrusso1020 jrusso1020 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

What

Second PR of the template-variables stack: the SDK write surface for the variable schema.

  • New EditOps + typed Composition wrappers: declareVariable(declaration), updateVariableDeclaration(id, declaration) (wholesale replace, id immutable — rename = remove + declare), removeVariableDeclaration(id) (last removal drops the whole attribute)
  • Patch grammar: /variableDeclarations/{id} ↔ override key varDecl.{id} — distinct from /variables/{id}, which stays the default-value path; apply-patches replays declaration patches for T3 host undo
  • can() validation: E_DUPLICATE_VARIABLE, E_VARIABLE_NOT_FOUND, E_INVALID_ARGS, and E_FRAGMENT_COMPOSITION
  • parsers: export isCompositionVariable so writers validate with the exact predicate readers filter by
  • All variable ops now maintain the --{id} CSS compat custom prop consistently (declare included), and updateVariableDeclaration emits a paired /variables/{id} patch when the default changes so the T3 override-set stays coherent regardless of replay order (review fixes absorbed into this PR)

Why

setVariableValue could edit an existing declaration's default but nothing could create, reshape, or delete declarations — the missing half of the schema-authoring surface Studio's Variables panel (and any embedder) needs. Everything flows through the established dispatch → patch → override-set pipeline so history, undo, and embedded hosts work for free.

How

Notable contract decision: fragment compositions refuse declaration ops (E_FRAGMENT_COMPOSITION). A fragment source has no <html> of its own — a declaration written to the synthetic wrapper is silently dropped by serialize(), which a test proved before the guard existed. Refusing loudly beats losing data quietly.

The patch replay path (apply-patches.ts) is deliberately not strict: inverse patches capture raw entries verbatim (including loose hand-authored declarations the strict parser drops), so undo must restore them verbatim — gating replay on isCompositionVariable made undo of a remove/update on a loose entry silently no-op (caught in review, fixed here).

Test plan

  • Unit tests: session.variabledecls.test.ts — dispatch semantics, can() error paths, CSS compat sync, patch grammar round-trips, undo/redo (including CSS prop restoration), fragment refusal
  • Full SDK suite green (435)
  • Manual: declare/edit/remove driven through the Studio panel against a live server; disk state and undo verified after each operation (see fix(studio): code-review and live-test fixes for the variables stack #2052)

🤖 Generated with Claude Code

Second PR of the template-variables Studio stack — the SDK write surface.
The SDK could edit an existing declaration's default (setVariableValue) but
had no way to create, reshape, or delete declarations. These ops complete
the schema-authoring surface so Studio and embedders build on one contract.

- new EditOps + typed Composition wrappers:
  - declareVariable(declaration) — creates data-composition-variables when
    absent; refuses duplicates and invalid shapes
  - updateVariableDeclaration(id, declaration) — wholesale replace; id is
    immutable (rename = remove + declare); keeps the --{id} CSS compat
    custom prop in sync when the default changes, mirroring setVariableValue
  - removeVariableDeclaration(id) — drops the attribute with the last entry
    and clears the CSS compat prop
- patch grammar: /variableDeclarations/{id} ↔ override key varDecl.{id}
  (distinct from /variables/{id}, which stays the default *value* path);
  apply-patches replays declaration patches for T3 host undo
- can() validation: E_DUPLICATE_VARIABLE, E_VARIABLE_NOT_FOUND,
  E_INVALID_ARGS, and E_FRAGMENT_COMPOSITION — fragment sources have no
  <html> to carry the schema, so declaring would silently vanish on
  serialize; the ops refuse instead
- parsers: export isCompositionVariable (the parse filter predicate) so
  writers validate with the exact same rules readers filter by

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jrusso1020 jrusso1020 force-pushed the 07-07-feat_sdk_variable_declaration_edit_ops_declare_update_remove_ branch from d3ab9db to ed9f222 Compare July 8, 2026 08:00
@jrusso1020 jrusso1020 force-pushed the 07-07-feat_sdk_variable_declaration_read_apis_browser-safe_variables_entry branch from df8c269 to 9e24243 Compare July 8, 2026 08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant