Repository navigation
feat(plugin-form): derive the per-row form's fields and its offer through the spec rule deriveInlineRowFormFields / isInlineRowFormOffered (objectui#11428) - #11756
Merged
objectstack-fleet[bot] merged 3 commits intoOct 7, 2026
Conversation
…ectui#11428) The published entry is about to import `deriveInlineRowFormFields` and `isInlineRowFormOffered` from `@objectstack/spec/data`, which the spec first exports in 17.7.0. The old floor admitted 17.6.x, which carries neither name. The resolved version stays 17.7.0; the lockfile moves only this importer's specifier line. Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju Co-authored-by: Claude <noreply@anthropic.com>
…ough the spec rule `deriveInlineRowFormFields` / `isInlineRowFormOffered` (objectui#11428) `deriveFormFields` now returns `@objectstack/spec/data`'s `deriveInlineRowFormFields` answer, and `MasterDetailForm` offers a row the expand form when `isInlineRowFormOffered` says so, asked with the collection's resolved `inlineMode`, `formFields` and `columns`. The local `SYSTEM_FIELDS` and `NON_INPUT_TYPES` sets, which only the row form read, are removed; `SORT_FIELD_NAMES` stays for the sort-field pick. Pins: a 15-entry corpus holds `deriveFormFields` equal to the spec's answer and to the replaced filter's answers (checked against that filter before the swap); a file-scoped spy pins that both sites read the spec, with stubbed answers the local rules could never give. Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju Co-authored-by: Claude <noreply@anthropic.com>
The captured grid props, the child object fixtures, the data source and the block schema are typed against `GridProps`, `DataSource` and `MasterDetailFormSchema` instead of `any`. No assertion changes. Claude-Session: https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju Co-authored-by: Claude <noreply@anthropic.com>
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
|
objectstack-fleet
Bot
deleted the
claude/issue-11428-inline-row-form-spec-derivation
branch
October 7, 2026 03:34
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 free
to 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 #11428
Clause-②: no
What this PR is
The per-row expand form of an inline master-detail collection now takes both of its rules from
@objectstack/spec/data, as the collection's grid columns already do (objectui#11345):deriveFormFieldsreturnsderiveInlineRowFormFields(childSchema, { relationshipField, exclude }). objectui's own per-field rendering over the returned names is unchanged: the row form still renders each name throughObjectForm.MasterDetailFormreplaces its hand-written offer expression withisInlineRowFormOffered({ inlineMode, formFields, columns }), asked with the collection's resolved values, authored or derived.Deleted from
packages/plugin-form/src/deriveMasterDetail.ts:SYSTEM_FIELDSandNON_INPUT_TYPES, which onlyderiveFormFieldsread.SORT_FIELD_NAMESstays, because the sort-field pick inderiveDetailstill reads it.The direction is triage's first grade (comment 5944853069), unlocked at 17.7.0 (comment 6029278659). The default record form in
ObjectForm.tsxis not touched; it waits on objectstack-ai/objectstack#21281.The fence
deriveFormFieldskeeps its signature. The builtdist/deriveMasterDetail.d.tsbefore (BASEc0862c1) and after differ only in that function's doc comment, anddist/MasterDetailForm.d.tsis byte-identical. No export, prop, type member or i18n key is added to any@object-ui/*package, which is whyClause-②: nostands.Landing site, and one addition to the claimed file surface
The landing sites are the claimed ones:
deriveMasterDetail.ts,MasterDetailForm.tsx, two tests beside them, and.changeset/11428-inline-row-form-spec-rule.md.Outside the claim's declared surface, as the unlock comment directs ("any other importer raises its own floor, and the PR says so"):
packages/plugin-form/package.json(@objectstack/specgoes from^17.6.0to^17.7.0) and that importer's one specifier line inpnpm-lock.yaml. The resolved version stays 17.7.0. They are commita62959a, kept separate so it can be judged on its own. The reason: the publisheddist/index.jsnow importsderiveInlineRowFormFieldsandisInlineRowFormOfferedfrom@objectstack/spec/data, and 17.6.x exports neither.Evidence (head
328a381)The installed exports.
@objectstack/spec17.7.0dist/data/index.d.mtsdeclares both, besidederiveInlineGridColumns:deriveInlineRowFormFields(def: unknown, opts?: { relationshipField?: string; exclude?: readonly string[] }): string[]isInlineRowFormOffered(opts: { inlineMode?: 'grid' | 'form'; formFields?: readonly unknown[]; columns?: readonly unknown[] }): booleanThe pins.
deriveMasterDetail.inlineRowFormFields-11428.test.ts, the twin of the objectui#11345 corpus: one 15-entry corpus, two answers per entry. The names equal (toStrictEqual) the spec'sderiveInlineRowFormFieldsanswer, and they equal the arrays the replaced local filter returned. The second leg was run against the replaced filter, before the swap:Tests 30 passed (30).inlineRowForm.specSource-11428.test.tsx: both exports behind pass-through spies (a file-scopedvi.mockthat spreads the real module, with a factory-ran probe). The pass-through rows are the existing behaviour: the three authored offer arms ofmasterDetailDetailsMembers-8071row 3, plus a fully derived collection. The stubbed rows hand back answers the local rules could never give: a stubbed field list comes back fromderiveFormFieldsunchanged and drivesderiveDetail'sformFieldsand its smart default; an inverted offer answer inverts every collection's offer, while list mode and its Add stay. Run against the BASE implementation, before the swap:Tests 4 failed | 1 passed (5), the one green being the mock-installed check.deriveFormFields (per-row expand form)inderiveMasterDetail.test.ts, andmasterDetailDetailsMembers-8071row 3.Ablations on committed code (
328a381). Each ran through objectstack'sscripts/ablation-replace.mjs. In each, the anchor went from 1 hit to 0, the blob changed, and the restore was proven (blob equals HEAD,git diff HEADempty).masterDetailDetailsMembers-8071green. ObservedTests 2 failed | 12 passed (14). Row 3 of 8071 stays green under this ablation, which is why an equality pin cannot stand in for the spy.deriveFormFieldsreverted to the replaced local filter. Predicted 2 red (the two derive spy pins) with the corpus green. ObservedTests 2 failed | 33 passed (35).One-off differential. Not committed; a historical reading at
7ae2f6b, whose source is the head's. It compares BASEc0862c1with this branch.deriveFormFields, 100,000 random cases: system, sort-position and prototype-named keys; random types and flags, truthy and falsy; falsy and primitive definitions; array, null and non-object field maps; randomrelationshipFieldandexclude. Compared by deep equality plus theutil.inspectrendering. 0 mismatches. Control leg (the BASE filter plus areadonlydrop): 14,118 mismatches, so the instrument can fire.inlineModegrid, form, absent or junk;formFieldsandcolumnsabsent, null or of random length. 0 mismatches. Control leg (greater-or-equal in place of strictly greater): 62,517 mismatches.Floor. objectui's
scripts/check-spec-range-floors.mjsverdict through its ownanalyze(), scoped to@object-ui/plugin-formover its built dist:^17.7.0: 0 findings;^17.6.0(in memory):floor-too-lowon exactlyderiveInlineRowFormFieldsandisInlineRowFormOffered.328a381)pnpm exec vitest run --maxWorkers=2 packages/plugin-form/Test Files 167 passed (167),Tests 1970 passed / 1 skipped (1971)pnpm --filter @object-ui/plugin-form type-check, afterturbo run build --filter=@object-ui/plugin-form. Bothtsclegs ran, and--listFilesshows the test program reads both new filespnpm --filter @object-ui/plugin-form lintcheck:phantom-deps,check:unused-deps,check:esm-specifiers,check:self-import,check:spec-symbols,check:unreferenced-sources,check:test-path-roots,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:lockfile-integrity,check:lockfile-dedupe,check:installed-pin-claims,check:changeset-claims,check:pending-changeset-literals,check:new-line-citations,check:control-bytes,check:element-data-source-declaration,check:handler-key-reads,check:metadata-write-doors,check:published-tsconfig-excludescripts/check-changeset-presence.mjs,scripts/check-changeset-no-major.mjs,scripts/check-changeset-fixed.mjs,scripts/check-lint-coverage.mjs,scripts/check-type-check-coverage.mjsscripts/check-governed-queue-guard.mjs --testover the seven changed pathsNOT MEASURED locally. These need every published package built, and CI or the release path runs them:
check:spec-floors(the scoped verdict above stands in);check:readme-exports(this diff touches no README and no export);check:dist-completeness, the load leg ofcheck:node-esm-load, andcheck:published-dist;pnpm lintandpnpm test.Acceptance notes
INLINE_GRID_SORT_FIELDS(through both derive exports) and objectui'sSORT_FIELD_NAMES, whichderiveDetailreads to pick the line's sort field.@objectstack/spec/datadoes not export the set. An observation only; the sort-field pick is not this card's rule. Carrier: none.Changeset
.changeset/11428-inline-row-form-spec-rule.mddeclares@object-ui/plugin-formpatch, the level measured above (the output is unchanged), and carries the floor sentence.Session:
https://claude.ai/code/session_01FngvPpdrnhHMdHHq6vwwju(os-dev, dispatched by thedomain:uiseat 2).Generated by Claude Code