Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-form): consult a declared submitHandler before SimpleObjectForm's inline carve-out - #6406
Merged
os-support-ai merged 1 commit intoAug 25, 2026
Conversation
…tForm's inline carve-out SimpleObjectForm.handleSubmit opened with the inline-fields carve-out, ahead of the persistence chain, so a declared submitHandler was never reached: a host that owns the write got a success signal for a write it never performed (measured onSuccess 1 / submitHandler 0). Gate the carve-out on the absence of a declared seam and move the no-submit-target refusal into the persistence chain after it, matching the five variant renderers and reusing the shared refusal from submitTarget.ts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011SfZeFWrhGLHmfq61xbz4q
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-support-ai
marked this pull request as ready for review
August 25, 2026 20:49
Uh oh!
There was an error while loading. Please reload this page.
os-support-ai
deleted the
claude/issue-6388-simpleform-submithandler-order
branch
August 25, 2026 21:01
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#6388
ObjectFormSchema.submitHandleris documented as handing the collected values to the host instead of callingdataSource.create/dataSource.update— a seam that by construction needs no adapter of its own.SimpleObjectForm.handleSubmitnevertheless opened with the inline-fields carve-out, checked before the persistence chain, so a declaredsubmitHandlerwas never reached: a host that had said it owns the write got a success signal for a write it was never asked to perform.Re-derived on the merged tree (
faa863dce), drivingformType: 'simple'throughObjectFormwithcustomFields, asubmitHandlerand nodataSource:onSuccesssubmitHandlersimple+customFields+submitHandler, nodataSourceThe change
packages/plugin-form/src/ObjectForm.tsx, two edits, in the shape PR #6386 landed for the five variant renderers:!dataSource && !schema.submitHandler && hasInlineFields;else if (!dataSource)branch after thesubmitHandlerbranch. It is nownoSubmitTargetError()fromsubmitTarget.ts— the family's shared refusal, whose message is the string this file already threw, verbatim — instead of a private literal. Expressing it there is also what lets TypeScript narrowdataSourcefor the create/edit routes with no assertion, and what routes the refusal through the container'scatch, soschema.onErrornow learns why.hasInlineFieldsvshasInlineFieldSource— measured, and deliberately not swappedThey are not the same predicate, and the difference decides
simple's behaviour:hasInlineFieldsisschema.customFields && schema.customFields.length > 0;hasInlineFieldSource's first limb isArray.isArray(customFields) && customFields.length > 0. Same shape, modulo a malformed non-arraycustomFieldscarrying alength.hasInlineFieldSourcealso accepts sections whose every field is an inline runtimeFormField— that is how the sectioned variants declare an inline field source, becauseTabbedFormSchema/SplitFormSchema/WizardFormSchemahave nocustomFieldsof their own.SimpleObjectFormdoes not read section fields as a field source. In its sections path,section.fieldsbuilds a name-keyed map and then filterssourceFields— resolved fromcustomFieldsor from the object schema — by those names, merging onlyvisibleOn/span/colSpanoverrides onto what it found. So with all-inline sections, nocustomFieldsand no adapter, the form resolves zero fields.Adopting the shared predicate for
simplewhile "aligning" it would therefore have widened the carve-out into a success signal for a form that collected nothing — #6300's own defect class in a narrower dress.simplekeeps its own predicate; only the refusal is shared. The narrowing is not left to prose: block 3'ssimpleBOUNDARY case pins that such a form refuses.Ghost-assertion guard
The fix was committed first; then only
ObjectForm.tsxwas reverted tofaa863dce(git checkout faa863dce -- packages/plugin-form/src/ObjectForm.tsx) with the new test file left in place. The mutation was confirmed on disk by blob hash —b109875548f2…became379c0a535c5b…, equal to the base blob — and restored withgit checkout HEAD -- ...from atrap, verified by an emptygit diff HEADand a hash back atb109875548f2….faa863dceimplementationsimple: inline fields + a declared seam — the seam is what runsexpected "vi.fn()" to be called 1 times, but got 0 times(submitHandler)simple: the seam wins over a PRESENT dataSource too (#6176)!dataSourcecarve-out, and this case is what says sosimple: CONTROL — no seam declared, the carve-out still firessimple: BOUNDARY — sections of inline fields are NOT its field sourceexpected "vi.fn()" to be called 1 times, but got 0 times(onError)Whole-file readings: against the base implementation
Test Files 1 failed (1)/Tests 2 failed | 33 passed (35); after the fixTest Files 1 passed (1)/Tests 35 passed (35).Degenerate control
The control fixture is
simple+customFields+onSuccess, nosubmitHandlerand nodataSource— block 3'sformType 'simple': CONTROL — no seam declared, the carve-out still fires. It asserts that the carve-out still runs and thatonSuccessreceives the raw collected values (not an adapter result), i.e.simple's legitimate inline collector is untouched. It passes on the base tree and after the fix, deliberately: it is what makes the block 1 pin attributable to the declared seam rather than to the carve-out having been narrowed or removed.No assertion in
submitTargetRefusal.test.tsxwas weakened or deleted — the diff on that file is 142 insertions, 0 deletions.Checks
All at
3b4e72eec; heavy runs serialized through the container's shared verify lock.pnpm exec vitest run packages/plugin-form/(repo root, canonical invocation) —Test Files 70 passed (70),Tests 720 passed (720),VITEST_EXIT=0pnpm --filter @object-ui/plugin-form run type-check=tsc --noEmit && tsc -p tsconfig.test.json(the second one is what covers the new test file) —TYPECHECK_EXIT=0, after building the dependency closurepnpm --filter '@object-ui/plugin-form^...' build(12 projects, Done)pnpm --filter @object-ui/plugin-form run lint—711 problems (0 errors, 711 warnings),LINT_EXIT=0; the warnings are pre-existingno-explicit-anyacross the packagenode scripts/check-changeset-presence.mjs—2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)node scripts/check-changeset-no-major.mjs—No changeset declares a major bumpnode scripts/check-control-bytes.mjs—OK (scanned 5285 tracked text file(s))Boundaries
The five variant renderers are untouched (correct as of PR #6386),
packages/specis untouched, and no accept set was widened — this restores a documented seam rather than admitting anything new. Changeset:patchon@object-ui/plugin-form.Draft on purpose: the PM lands it.
Generated by Claude Code
Generated by Claude Code