Why
Since #2700 / ADR-0059, ActionParamDialog routes every param through the shared form field-widget renderer (paramToField() → getLazyFieldWidget). That was the whole win — but it also means each param now emits its widget's own value shape, and those shapes differ:
#2700's acceptance list explicitly called for value-shape parity coverage ("confirm each round-trips to what the action runner/endpoint expects; add coverage"), but no test net pins it today. Without one, a future widget swap can silently change a param's emitted shape and break an endpoint's input contract with no failing test.
Scope
Explicitly out of scope (owned elsewhere)
Acceptance
Refs
Why
Since #2700 / ADR-0059,
ActionParamDialogroutes every param through the shared form field-widget renderer (paramToField()→getLazyFieldWidget). That was the whole win — but it also means each param now emits its widget's own value shape, and those shapes differ:date/datetime/time→ ISO-ish stringsnumber→number(not string)boolean→booleanselect→string;select+multiple→string[](new in feat(fields): select+multiple → multi-value chip picker; restore fields/core lint gates #2709)lookup/user→ idstringorstring[]file/image→ an upload descriptor object{name, url, size, mime_type, …}(or an array) — not a fileId string#2700's acceptance list explicitly called for value-shape parity coverage ("confirm each round-trips to what the action runner/endpoint expects; add coverage"), but no test net pins it today. Without one, a future widget swap can silently change a param's emitted shape and break an endpoint's input contract with no failing test.
Scope
paramToFielddrift test's style) asserting, for eachFORM_FIELD_TYPEStype usable as a param, the shape the dialog emits on confirm (primitive vs. object vs. array;multiple→ array).number→number,boolean→boolean,date→string,select→string,select+multiple→string[],lookup→id(s).type:'X'param will POST.Explicitly out of scope (owned elsewhere)
file/image→ fileId value-shape gap and its serialization at the param boundary is owned by [P2] approvals: declare attachments on approve/reject and retire the inbox's hardcoded composer #2698 (Phase 1) — reference it, don't duplicate. This issue covers the general net for the non-upload types and the overall contract table; it should adopt whateverfileserialization [P2] approvals: declare attachments on approve/reject and retire the inbox's hardcoded composer #2698 lands.Acceptance
Refs
autonumber(ADR-0059 follow-ups) #2707 (upload guard) · feat(fields): select+multiple → multi-value chip picker; restore fields/core lint gates #2709 (select+multiple→string[]) · [P2] approvals: declare attachments on approve/reject and retire the inbox's hardcoded composer #2698 (owns thefilefileId serialization — Phase 1)