What was measured (on dc7a48d02, branch claude/issue-7190-inline-field-input-dependson)
Two widgets in @object-ui/fields resolve the record a cascade gates on through the same three-step chain:
dependentValues ?? ctx.formValues ?? ctx.data ?? {}
packages/fields/src/widgets/LookupField.tsx:352 — dependent lookups (dependsOn).packages/fields/src/widgets/useCascadingOptions.ts:38 — the option cascades behind SelectField, RadioField, CheckboxesField and MultiSelectField.
⭐ ctx cannot ever carry either of the last two.
SchemaRendererContextType (packages/react/src/context/SchemaRendererContext.tsx) declares exactly four members: dataSource, debug, debugFlags, apiFetch. There is no data and no formValues, and SchemaRendererProvider accepts no such prop.
The repository contains exactly one non-test provider of that context — the definition itself, at line 46 of that same file, whose value is { dataSource, debug, debugFlags, apiFetch }.
Census command, and its control, because a "one result" reading is worthless without one:
grep -rn "SchemaRendererContext.Provider" packages/ examples/ | grep -v "__tests__\|\.test\." # 2 lines, both the definition's open+close tag
grep -rn "SchemaRendererContext.Provider" packages/ examples/ | wc -l # 96 — the instrument is live
⇒ In production, the tail of that chain is unconditionally the empty object. Every surface that does not pass an explicit dependentValues prop gets {} — which for a dependsOn lookup means a permanently gated picker, and for a cascading option list means an unscoped one.
Why this is worth a card rather than a comment: three places describe the tail as live
The dead tail is not merely unused, it is documented as a working channel, which is what makes it mislead readers into treating a host as the supplier:
LookupField.tsx — its own docblock calls ctx.data the "record scope" channel.packages/fields/src/widgets/toHostProps.ts:68 — "the explicit prop wins, then ctx.formValues, then …".packages/app-shell/src/views/ActionParamDialog.tsx:330 — the strongest one: "the dialog passed nothing, so useCascadingOptions fell through to SchemaRendererContext's formValues / data — the OUTER page's record". There is no outer page's record in that context; the fall-through reached {}.
This is the class objectui#7167 corrected for the grid's relational copy set: comments asserting a route that the code does not have.
Measured reach
Both known instances of "a dependsOn lookup gates forever" are instances of this root, not of two independent host bugs:
- objectui#7165 — the grid's inline lookup column.
renderCellEditor's context is { column, row, value, stage, commit, cancel }; nothing forwards row, so the chain falls to the empty tail. - objectui#7190 — the detail page. Measured in the real app-shell record page on this branch: gated, disabled, "Select region first", with a live control lookup enabled in the same render, and both of
InlineFieldInput's call sites gating.
The fork — this is a design decision, not a mechanical repair
Three readings, and they lead to different architectures. ⛔ Not chosen here.
- A — keep prop drilling. Every record-scoped host passes
dependentValues at each call site (the grid's editor seam, the detail body, the highlights strip, and any future one). Cheapest per site; the defect class stays re-openable by the next host that forgets, exactly as it has twice. - B — make the context channel real. Add
data to SchemaRendererContextType and have record-scoped hosts provide it, so the documented fallback becomes true and every surface is served at once. Addresses the class rather than the instances; costs a public contract change on @object-ui/react, and it must be settled whether "record scope" means the saved record or the in-flight staged one (objectui#7188 is the same question for the grid). - C — retire the dead tail under ADR-0049 enforce-or-remove, making
dependentValues the only channel, and correct the three comments. Honest and smallest; it also removes the option of ever addressing this centrally, so it should only be chosen if B is being rejected on purpose.
A and C are not mutually exclusive; B excludes C.
Not filed as a duplicate
Searched before filing. The two dependent-lookup cards (objectui#7165, objectui#7190) are about their hosts; objectui#7188 is the grid's saved-versus-staged seam. None of them is about the context contract itself, and none proposes B or C.
What was measured (on
dc7a48d02, branchclaude/issue-7190-inline-field-input-dependson)Two widgets in
@object-ui/fieldsresolve the record a cascade gates on through the same three-step chain:packages/fields/src/widgets/LookupField.tsx:352— dependent lookups (dependsOn).packages/fields/src/widgets/useCascadingOptions.ts:38— the option cascades behindSelectField,RadioField,CheckboxesFieldandMultiSelectField.⭐
ctxcannot ever carry either of the last two.SchemaRendererContextType(packages/react/src/context/SchemaRendererContext.tsx) declares exactly four members:dataSource,debug,debugFlags,apiFetch. There is nodataand noformValues, andSchemaRendererProvideraccepts no such prop.The repository contains exactly one non-test provider of that context — the definition itself, at line 46 of that same file, whose value is
{ dataSource, debug, debugFlags, apiFetch }.Census command, and its control, because a "one result" reading is worthless without one:
⇒ In production, the tail of that chain is unconditionally the empty object. Every surface that does not pass an explicit
dependentValuesprop gets{}— which for adependsOnlookup means a permanently gated picker, and for a cascading option list means an unscoped one.Why this is worth a card rather than a comment: three places describe the tail as live
The dead tail is not merely unused, it is documented as a working channel, which is what makes it mislead readers into treating a host as the supplier:
LookupField.tsx— its own docblock callsctx.datathe "record scope" channel.packages/fields/src/widgets/toHostProps.ts:68— "the explicit prop wins, thenctx.formValues, then …".packages/app-shell/src/views/ActionParamDialog.tsx:330— the strongest one: "the dialog passed nothing, souseCascadingOptionsfell through toSchemaRendererContext'sformValues/data— the OUTER page's record". There is no outer page's record in that context; the fall-through reached{}.This is the class objectui#7167 corrected for the grid's relational copy set: comments asserting a route that the code does not have.
Measured reach
Both known instances of "a
dependsOnlookup gates forever" are instances of this root, not of two independent host bugs:renderCellEditor's context is{ column, row, value, stage, commit, cancel }; nothing forwardsrow, so the chain falls to the empty tail.InlineFieldInput's call sites gating.The fork — this is a design decision, not a mechanical repair
Three readings, and they lead to different architectures. ⛔ Not chosen here.
dependentValuesat each call site (the grid's editor seam, the detail body, the highlights strip, and any future one). Cheapest per site; the defect class stays re-openable by the next host that forgets, exactly as it has twice.datatoSchemaRendererContextTypeand have record-scoped hosts provide it, so the documented fallback becomes true and every surface is served at once. Addresses the class rather than the instances; costs a public contract change on@object-ui/react, and it must be settled whether "record scope" means the saved record or the in-flight staged one (objectui#7188 is the same question for the grid).dependentValuesthe only channel, and correct the three comments. Honest and smallest; it also removes the option of ever addressing this centrally, so it should only be chosen if B is being rejected on purpose.A and C are not mutually exclusive; B excludes C.
Not filed as a duplicate
Searched before filing. The two dependent-lookup cards (objectui#7165, objectui#7190) are about their hosts; objectui#7188 is the grid's saved-versus-staged seam. None of them is about the context contract itself, and none proposes B or C.