Found while implementing #5727 (guarding readPrefill's seeding with isRuntimeDefault). Not fixed in that PR — that card's fence was the seeding half, and this is the submit half: a different call site, a different predicate, and its own decision about what "empty" means.
Measured on origin/main1939c9610.
Mechanism
After #5727 a create form on the console's two form routes correctly opens a runtime-default control EMPTY and leaves the key out of values. But handleSubmit submits values wholesale:
constresult=mode==='public'
? awaitsubmitPublic(identifier,values)
: awaitsubmitInternal(loaded.object,values,editingId);
A rendered control that the user focuses and then clears writes back through onChange={(e) => onChange(e.target.value)} (FieldInput, the text/email/url/date/time/textarea arms), so values gains the key with ''. ObjectQL.applyFieldDefaults resolves a declared default only for a field that arrives absent or null — a blank string is neither, so submitting one stores '' and silently defeats the declaration. That is the same suppression #5727 closed, reached by the other door.
The sibling chain already has the answer and names it as the other half of the same fix: @object-ui/plugin-form's omitServerResolvedDefaults (packages/plugin-form/src/schemaDefaults.ts) drops exactly the keys whose declared default isRuntimeDefault and whose submitted value isMissingForRequired. Its docblock states the pairing outright — "excusing a server-owned field from required is only half an answer if the form then submits the key anyway". apps/console/src/components/FormPage.tsx calls no equivalent: omitServerResolvedDefaults returns zero hits in that file.
Why this is not a duplicate
Honestly bounded
Not browser-verified. Static evidence: the handleSubmit call site, the onChange write, the absent omitServerResolvedDefaults call, and the sibling module's own statement that the two halves belong together. Reachability requires the user to touch and clear the control, which narrows it relative to #5727 (that one fired on every open). Note also that the console builds its payload from values rather than from an object schema keyed by field name, so whether the sibling's signature (data, objectSchema) fits this call site as-is, or the fence wants the RenderableField rows it already holds, is a call for triage rather than for this note.
Related: #5727, #4047, #4068, #4069, #4085.
Generated by Claude Code
Found while implementing #5727 (guarding
readPrefill's seeding withisRuntimeDefault). Not fixed in that PR — that card's fence was the seeding half, and this is the submit half: a different call site, a different predicate, and its own decision about what "empty" means.Measured on
origin/main1939c9610.Mechanism
After #5727 a create form on the console's two form routes correctly opens a runtime-default control EMPTY and leaves the key out of
values. ButhandleSubmitsubmitsvalueswholesale:A rendered control that the user focuses and then clears writes back through
onChange={(e) => onChange(e.target.value)}(FieldInput, the text/email/url/date/time/textarea arms), sovaluesgains the key with''.ObjectQL.applyFieldDefaultsresolves a declared default only for a field that arrives absent or null — a blank string is neither, so submitting one stores''and silently defeats the declaration. That is the same suppression #5727 closed, reached by the other door.The sibling chain already has the answer and names it as the other half of the same fix:
@object-ui/plugin-form'somitServerResolvedDefaults(packages/plugin-form/src/schemaDefaults.ts) drops exactly the keys whose declared defaultisRuntimeDefaultand whose submitted valueisMissingForRequired. Its docblock states the pairing outright — "excusing a server-owned field fromrequiredis only half an answer if the form then submits the key anyway".apps/console/src/components/FormPage.tsxcalls no equivalent:omitServerResolvedDefaultsreturns zero hits in that file.Why this is not a duplicate
defaultValueliterally into the control (readPrefill), suppressing the server-side resolution the declaration asks for #5727 fixed SEEDING on this renderer (the key never entersvaluesfrom the declaration). This is the submit filter, which is what handles a key the USER put there.defaultValueis a runtime token (NOW()/current_user) cannot be submitted from a create form #4069 /requiredWhen+ a runtimedefaultValuestill deadlocks a create form (the conditional half of #4069) #4085 carried therequiredhalf onto this renderer viaresolveFieldRuleState; neither touches the payload.defaultValueliterally into the control (readPrefill), suppressing the server-side resolution the declaration asks for #5727 in shape — the second form renderer missing what the first one carries — different call site.Honestly bounded
Not browser-verified. Static evidence: the
handleSubmitcall site, theonChangewrite, the absentomitServerResolvedDefaultscall, and the sibling module's own statement that the two halves belong together. Reachability requires the user to touch and clear the control, which narrows it relative to #5727 (that one fired on every open). Note also that the console builds its payload fromvaluesrather than from an object schema keyed by field name, so whether the sibling's signature (data,objectSchema) fits this call site as-is, or the fence wants theRenderableFieldrows it already holds, is a call for triage rather than for this note.Related: #5727, #4047, #4068, #4069, #4085.
Generated by Claude Code