Repository navigation
fix(plugin-form): a detail that loaded but could not DERIVE gets a config hint naming relationshipField - #6407
Merged
os-support-ai merged 1 commit intoAug 25, 2026
Conversation
…nfig hint The third and last arm of MasterDetailForm's detail resolver to sit on a permanent "Loading columns…". deriveDetail throws when no lookup/master_detail field on the child object references the parent; the catch returned the entry unresolved, so it fell through to the loading branch — and the derive is not retried, so those columns could never arrive. The entry now carries status: 'underivable', and the renderer gives it a branch of its own: the config-hint shape (objectui#5940 / objectui#6360) naming both ends of the relationship it could not find and `relationshipField` as the key to set. Deliberately NOT objectui#6372's refusal placeholder, which states the schema could not be LOADED — false for a schema that loaded fine. The thrown error is still logged with its stack. Per triage's ruling on objectui#6394 (option 2), which also authorized rewriting the one objectui#6360 assertion whose premise was measured inaccurate: it was titled as a pending fetch while its fixture reached the failed derive. 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:52
os-support-ai
deleted the
claude/issue-6394-master-detail-derive-config-hint
branch
August 25, 2026 21:05
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 #6394
Per triage's ruling on that card: option 2 — a failed derive gets the config-hint placeholder naming
relationshipFieldas the key to set, the objectui#5940 / objectui#6360 shape.What changed
MasterDetailForm's detail resolver had a third arm still sitting on a permanentLoading columns…: the schema loaded fine andderiveDetailthen threw, because no lookup/master_detailfield on the child object references the parent. Thecatchreturned the entry unresolved (return entry;), so it fell through to!d.columns?.length ? <p>Loading columns…</p>— and the derive is not retried, so those columns could never arrive.The entry now carries
status: 'underivable'and gets a render branch of its own:⛔ Deliberately not objectui#6372's refusal placeholder, which states the schema could not be loaded — triage's words, "dishonest copy for a schema that loaded fine." The thrown error is still logged with its stack (objectui#6372); the placeholder shows the author the key rather than the raw message.
MasterDetailLinesgains one prop,parentObjectName, so the hint can name both ends of the relationship it could not find. Everything else in the file is untouched.Three arms, three distinct outcomes
childObject[{ title: 'Unconfigured' }]md-detail-no-child-objectgetObjectSchemarejectsmd-detail-schema-unavailablemd-detail-no-relationship-field(new)Loading columns…(true here)All five are asserted, so the new behaviour is attributable to the failed derive rather than to "this renderer shows a hint for every unresolved detail".
⭐ Pinned-test rewrite 1 of 2 — the one triage authorized
The ruling clause that moves it, quoted verbatim from the triage comment:
OLD, verbatim (
packages/plugin-form/src/MasterDetailForm.detailChildObjectDecline.test.tsx):NEW, verbatim:
The original direction it existed to pin — the
!childObjecthint must not appear for a detail that named its child object — is carried over unchanged and unweakened. What moved is only the outcome the fixture actually reaches. No other assertion in that file was touched; the file's describe-block prose that described this test as "holding the loading branch alive" was corrected in place, because it stated the same measured-false premise.⭐ Pinned-test rewrite 2 of 2 — forced by the same ruling, disclosed under the same rule
Not named in the dispatch order, and disclosed here because the standing rule is about pinned tests, not about which file they live in. objectui#6372's own control in
MasterDetailForm.detailSchemaFetchFailure.test.tsx— "DEGENERATE CONTROL: a fetch that SUCCEEDS but fails to DERIVE is not called a load failure" — also waited onLoading columns…for this arm. That wait is unfixably tied to the behaviour triage has just ruled out; the control's subject is unchanged (this is not a load failure) and is still asserted.OLD, verbatim:
NEW, verbatim:
The assertion is strengthened, not weakened: it went from "some spinner text is on screen" to "this exact placeholder is on screen, and the load-failure one is not". Its comment block was updated to say which card moved it. Everything else in that file — including objectui#6372's refusal assertions, its log assertion, and the never-settling-promise control — is untouched.
Ghost-assertion guard
Method: the fix was committed first, then only
packages/plugin-form/src/MasterDetailForm.tsxwas reverted to the pinned base commitfaa863dce(the merge of PR #6393), leaving the new tests in place. The mutation was confirmed on disk by blob identity, not by an exit code:Restore was proved the same way (
git checkout HEAD -- <file>, never a baregit checkout --):git diff HEADempty,disk blob a9c54d1a… == HEAD blob a9c54d1a…. The runner carried atrap … EXIT INT TERMrestore.Against unmodified base source —
Test Files 3 failed (3)/Tests 4 failed | 12 passed (16),os-verify-lock: VERDICT command-exit 1:faa863dcerelationshipField…Unable to find an element by: [data-testid="md-detail-no-relationship-field"]expected null not to be null(anchored on the new placeholder, so it cannot read green without it)Unable to find an element by: [data-testid="md-detail-no-relationship-field"]expected null not to be null⭐ The 12 that pass on both are the controls, and passing on both is what makes them controls: they pin behaviour this PR does not change. Every assertion that states the new behaviour fails on base source.
On this branch —
Test Files 3 passed (3)/Tests 16 passed (16),os-verify-lock: VERDICT command-exit 0.Verification (all at final commit
028664357, clean tree)pnpm exec vitest run packages/plugin-form/Test Files 71 passed (71)/Tests 722 passed (722)pnpm --filter @object-ui/plugin-form type-check(tsc --noEmit && tsc -p tsconfig.test.json)pnpm --filter @object-ui/plugin-form lint(eslint ., 101 files)✖ 713 problems (0 errors, 713 warnings), exit 0 — warnings are the package's pre-existingno-explicit-anystyle, 0 errorspnpm check:control-bytes✅ OK (scanned 5286 tracked text file(s))pnpm check:shell-escape-residue✅ OKnode scripts/check-changeset-no-major.mjs✅ No changeset declares amajorbump.Dependency closure was built first (
pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-form^...' build) — without ittype-checkreportsCannot find module '@object-ui/*'for missingdist/*.d.ts, which reads exactly like a broken import.Declared narrowing: the repo-wide
pnpm lint(turbo → each package's owneslint .) was not run locally; CI owns the farm. The narrowing is measurable, not assumed: (1) the linted population came from eslint's own resolution, not a guess —eslint --format json packages/plugin-form/reports 101 files, i.e. the whole affected package, a superset of the 4 files this PR touches; (2)eslint.config.jsconfigures no type-aware linting (noparserOptions.project/projectService), so this diff cannot move the verdict on any file it did not touch; (3) the diff changes no exported API, so no other package'seslint .sees different input. Repo-widepnpm lintandpnpm checkrun in CI regardless.Not touched
LineItemsPanel.tsx,deriveMasterDetail.ts,packages/spec,content/docs/releases/. objectui#6372's and objectui#6360's landed arms are untouched beyond the two disclosed assertions. Changeset:patch,@object-ui/plugin-form.⛔ Draft on purpose — the PM lands it.
Generated by Claude Code