# Flow Debugger — method pane takes structured parameters (Gitea #37) **Date:** 2026-09-10 · **Status:** approved (issue-authored spec) · **Issue:** #37 ## Problem The Flow Debugger's *Run inbound API method* pane rendered one single-line text box per top-level schema field and coerced the text by declared type. For `object` / `array` fields it tried `JsonNode.Parse` on the raw text and, on failure, **sent the text as a string**; an empty box was silently omitted. A required array-of-object parameter (MesMoveOut `WorkOrders`, `[{WorkOrderNumber, PartNumber}]`) could not be entered from the page at all — the QA run reached the site with `"WorkOrders":[]` and the receiver rejected it with a misleading error. The same payload ran fine through `flow-debug run-method --params`, so only the UI input surface was at fault. The *Run site script* pane on the same page already uses the shared `ParameterValueForm` (typed inputs, nested groups, add/remove list editors, `$ref:lib:` resolution, inline validation, Monaco escape hatch for unresolved shapes). ## Design 1. **The method pane renders `ParameterValueForm`** driven by the method's `ParameterDefinitions`, keyed on the method id so a change of method remounts the form (the same reason the script pane keys on the script's canonical name). The page holds a `_methodParams` value tree + `_methodParamsValid`; `BuildParameters()` serializes the tree as-is, exactly like `BuildScriptParameters()`. The flat `_inputs` / `Coerce()` / `SetInput` path is deleted — a failed parse can no longer be sent as a string, and there is no type-specific coercion left on the page. 2. **Whole-payload raw-JSON escape hatch.** The Inputs card gains a *Form | Raw JSON* toggle. Raw mode is a Monaco `json` editor given the method's root schema (the form's `ToJsonSchema` made `internal static`) so completion and hover work. Every edit parses the text and validates it with `InboundApiSchema.Validate` against the resolved root schema (`ParseWithRefs` + `ISchemaLibraryQueryService`, the same resolution the form does); errors render inline under the editor. Switching Form → Raw seeds the editor from the current value tree (indented); Raw → Form loads the last *valid* tree into the form. The value tree is the single source of truth in both modes. 3. **Run gating.** `CanRun` for the method entry is `method chosen && (no parameters || params valid)`, mirroring `_scriptParamsValid`. A required field left empty disables Run (the form reports validity on mount, so this holds before the first keystroke). 4. **Persistence.** The per-method `localStorage` entry (`flowDebugger.inputs.`) now stores the value tree as JSON. On restore the tree is converted back (the form's `JsonElementToObject` made `internal static`) and validated against the schema; an invalid tree is discarded. That also retires the old flat `Dictionary` entries without a version flag: legacy strings that still validate (string fields) are kept, anything else is dropped. 5. **Not changed:** the runner (`FlowDebugRunMethodCommand`), the CLI, prediction, Replay, the script pane. No wire, proto or schema change. ## Tests - bUnit (`FlowDebuggerPageTests`): an array-of-object field (the `WorkOrders` shape) round-trips from the list item editors to the `RunMethodAsync` payload; invalid raw JSON blocks Run and shows the error; valid raw JSON sends exactly that payload; a required scalar left empty disables Run until filled; switching methods resets the form and its validity. - Playwright `FlowDebuggerTests`: the smoke selector moves from `flow-input-Text` to the form's `#param-Text`. ## Docs `Component-CentralUI.md` (Flow Debugger entry points), CLAUDE.md Flow Debugger bullet, this design doc.