Skip to content

Fix dynamic playground form state reconciliation#3972

Merged
rossirpaulo merged 2 commits into
canaryfrom
paulo/fix-dynamic-form-state
Jul 10, 2026
Merged

Fix dynamic playground form state reconciliation#3972
rossirpaulo merged 2 commits into
canaryfrom
paulo/fix-dynamic-form-state

Conversation

@rossirpaulo

@rossirpaulo rossirpaulo commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • reconcile dynamic form values against the current function and type schema
  • preserve compatible user edits while adding new required fields or resetting incompatible values
  • serialize the same defaults that typed controls display, including untouched nested booleans
  • remount schema-backed widgets when the schema changes and reconcile again immediately before form-mode runs
  • keep raw mode unchanged and continue omitting defaulted function parameters so runtime defaults still apply

Root cause

Form seeding and marker normalization were guarded only by the selected function name. When a same-name function changed shape during hot reload, those guards did not run again and widget-local state could survive against the old schema.

Separately, typed controls could display HTML fallback defaults for properties that were not yet present in serialized form state. Running before the hydration effect completed therefore submitted an incomplete object even though the form visibly showed a value.

Validation

  • pnpm --filter @b/pkg-playground test — 108 tests passed
  • pnpm --filter app-vscode-webview test:unit:run — 15 tests passed
  • pnpm --filter @b/pkg-playground typecheck
  • pnpm --filter app-vscode-webview typecheck
  • pnpm --filter app-vscode-webview build
  • live playground verification for a populated Person gaining a required field
  • live playground verification for an untouched nested boolean submitting false

Summary by CodeRabbit

  • Bug Fixes
    • Improved function argument forms when schemas change during hot reload, preserving compatible values and adding new required fields.
    • Corrected serialization of nested classes and default boolean values.
    • Ensured incompatible or incomplete arguments are reset to valid schema defaults.
    • Improved handling of enums, nested classes, aliases, unions, and recursive required fields.
  • Tests
    • Added coverage for schema migration, default serialization, typed values, and repeated reconciliation behavior.

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beps Ready Ready Preview, Comment Jul 10, 2026 8:21pm
promptfiddle Ready Ready Preview, Comment Jul 10, 2026 8:21pm
promptfiddle2 Ready Ready Preview, Comment Jul 10, 2026 8:21pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Argument handling now reconciles form values against active schemas, including defaults, typed markers, recursive classes, and schema changes. ExecutionPanel uses reconciled values for rendering and execution, while tests cover serialization, migration, idempotence, and lifecycle behavior.

Changes

Argument reconciliation flow

Layer / File(s) Summary
Reconciliation model and recursive defaults
typescript2/pkg-playground/src/args-form-model.ts
Replaces normalizeArgs with reconcileArgs, adds schema-aware defaulting, typed marker normalization, incompatible-value replacement, alias handling, and bounded recursive class expansion.
Form and execution integration
typescript2/pkg-playground/src/ExecutionPanel.tsx
Scopes reconciliation by project, function, and schema; renders reconciled form values; validates and encodes reconciled run arguments; and stores the exact JSON used for execution.
Reconciliation and lifecycle coverage
typescript2/pkg-playground/src/args-form-model.test.ts, typescript2/app-vscode-webview/src/ExecutionPanel.strict-mode.test.tsx
Tests enum and class reconciliation, recursive defaults, schema migration, idempotence, cached-value preservation, and nested boolean serialization.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ArgsForm
  participant ExecutionPanel
  participant reconcileArgs
  participant FunctionEncoder
  ArgsForm->>ExecutionPanel: submit form arguments
  ExecutionPanel->>reconcileArgs: reconcile against active schema
  reconcileArgs-->>ExecutionPanel: return reconciled arguments
  ExecutionPanel->>FunctionEncoder: encode arguments for execution
  FunctionEncoder-->>ExecutionPanel: return execution bytes
Loading

Possibly related PRs

  • BoundaryML/baml#3935: Adds the schema and typed marker protocol consumed by the reconciliation and form execution logic.

Poem

A bunny found a schema bright,
And filled each field just right.
Old text stayed snug, new defaults grew,
Markers wore their proper hue.
The form hopped on, the run took flight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: reconciling dynamic playground form state with schema changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch paulo/fix-dynamic-form-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

⏭️ Performance benchmarks were skipped

Perf benchmarks (CodSpeed) are opt-in on pull requests — they no longer run on every push. They always run automatically after merge to canary/main.

To run them on this PR, do any of the following, then push a commit (or re-run CI):

  • Add RUN_CODSPEED=1 to the PR description, or
  • Include run-perf or /perf in the PR title or any commit message.

@rossirpaulo
rossirpaulo marked this pull request as ready for review July 10, 2026 19:52
@rossirpaulo
rossirpaulo added this pull request to the merge queue Jul 10, 2026
@rossirpaulo
rossirpaulo removed this pull request from the merge queue due to a manual request Jul 10, 2026
@rossirpaulo
rossirpaulo added this pull request to the merge queue Jul 10, 2026
@rossirpaulo
rossirpaulo removed this pull request from the merge queue due to a manual request Jul 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
typescript2/pkg-playground/src/args-form-model.ts (1)

145-151: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inconsistent parameter ordering between defaultValue and reconcileValue.

defaultValue(…, classPath, aliasPath) orders the two path sets (classPath, aliasPath), while reconcileValue(…, aliasPath, classPath) (Lines 403-411) uses the opposite (aliasPath, classPath). Every current call site threads them correctly, but two sibling recursive helpers taking the same two Set<string> args in opposite positions is easy to silently transpose in a future edit (both are Set<string>, so TypeScript won't catch a swap). Consider aligning the order.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@typescript2/pkg-playground/src/args-form-model.ts` around lines 145 - 151,
Align the parameter ordering of defaultValue and reconcileValue so both accept
classPath before aliasPath; update their recursive implementations and every
call site accordingly, preserving the correct set passed to each parameter.
typescript2/pkg-playground/src/ExecutionPanel.tsx (2)

2012-2015: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize argsSchemaKey to avoid recomputing JSON.stringify on every render.

argsSchemaKey is recomputed on every render of ExecutionPanel (it's a plain const, not useMemo), even for renders triggered by unrelated state (cursorOffset, highlightedNodeId, workflowCacheVersion, etc.). It stringifies paramSchemas and the entire projectTypes map every time, which is wasted work whenever those references haven't actually changed.

♻️ Proposed fix
-  const argsSchemaKey = JSON.stringify([
-    paramSchemas ?? null,
-    projectTypes ?? null,
-  ]);
+  const argsSchemaKey = useMemo(
+    () => JSON.stringify([paramSchemas ?? null, projectTypes ?? null]),
+    [paramSchemas, projectTypes],
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@typescript2/pkg-playground/src/ExecutionPanel.tsx` around lines 2012 - 2015,
Memoize the argsSchemaKey computation in ExecutionPanel using useMemo, with
paramSchemas and projectTypes as dependencies, so JSON.stringify only runs when
either input reference changes.

2030-2135: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Unguarded reconcileArgs calls could crash rendering/effects if it ever throws.

reconcileArgs is invoked directly with no try/catch in three places: the reconciledFormArgs memo (Lines 2030-2033), the schema-scoped reconcile effect (Line 2067), and onRunFunction (Line 2105). A throw from any of these (e.g. an unexpected/malformed live-edited schema) would crash the render (memo) or surface as an unhandled effect error, with no fallback to the raw-JSON view. Given schemas here come from a live-editing playground (higher chance of transiently malformed/edge-case shapes), some defensive handling — e.g. falling back to the unreconciled value and surfacing a notice — would improve resilience.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@typescript2/pkg-playground/src/ExecutionPanel.tsx` around lines 2030 - 2135,
Guard every reconcileArgs call in reconciledFormArgs, the schema-scoped
reconciliation useEffect, and onRunFunction with try/catch. Preserve the
original parsed/raw arguments when reconciliation fails, and surface a clear
validation or form notice instead of allowing render or effect errors to escape;
ensure the run path still submits the unreconciled arguments only when
reconciliation fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@typescript2/pkg-playground/src/args-form-model.ts`:
- Around line 145-151: Align the parameter ordering of defaultValue and
reconcileValue so both accept classPath before aliasPath; update their recursive
implementations and every call site accordingly, preserving the correct set
passed to each parameter.

In `@typescript2/pkg-playground/src/ExecutionPanel.tsx`:
- Around line 2012-2015: Memoize the argsSchemaKey computation in ExecutionPanel
using useMemo, with paramSchemas and projectTypes as dependencies, so
JSON.stringify only runs when either input reference changes.
- Around line 2030-2135: Guard every reconcileArgs call in reconciledFormArgs,
the schema-scoped reconciliation useEffect, and onRunFunction with try/catch.
Preserve the original parsed/raw arguments when reconciliation fails, and
surface a clear validation or form notice instead of allowing render or effect
errors to escape; ensure the run path still submits the unreconciled arguments
only when reconciliation fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: aa16d0f3-d0a7-4d24-9dd5-5632da6a17f3

📥 Commits

Reviewing files that changed from the base of the PR and between 3eaee78 and 756d703.

📒 Files selected for processing (4)
  • typescript2/app-vscode-webview/src/ExecutionPanel.strict-mode.test.tsx
  • typescript2/pkg-playground/src/ExecutionPanel.tsx
  • typescript2/pkg-playground/src/args-form-model.test.ts
  • typescript2/pkg-playground/src/args-form-model.ts

Merged via the queue into canary with commit 74f7231 Jul 10, 2026
34 of 35 checks passed
@rossirpaulo
rossirpaulo deleted the paulo/fix-dynamic-form-state branch July 10, 2026 20:12
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