Filed unassigned by the #7165 execution lane, as ruled in #7165 comment 5491922231. #7165 shipped option A as an explicitly-labelled interim; this card is option B, the finished shape. #7165 stays open for it.
What A left undone
#7165's PR passes dependentValues={ctx.row} in ObjectGrid's renderCellEditor, which unblocks a dependsOn lookup column that was previously permanently uneditable. ctx.row is the saved record.
⇒ Edit the parent field in a row and, without saving, open the dependent child picker: the child is still scoped by the parent's persisted value. If the parent was previously empty, the child is still gated; if the parent was changed, the child lists candidates for the old parent.
That is strictly better than "can never be filled" and strictly not finished. PR #2216 answered the same question for the FORM renderer with the live watched record, so picking a parent re-scopes the child immediately. The grid should match.
The seam change — measured, not estimated
The data B needs is already in scope at the call site. packages/components/src/renderers/complex/data-table.tsx computes, in the row loop that renders the editor:
const rowChanges = pendingChanges.get(rowIndex) || {};
and a few lines later calls the injected editor with the saved row only:
const node = injectEditor({ column: col, row, value: editValue, stage: stageEdit, commit: …, cancel: cancelEdit });
So B is not a plumbing problem — pendingChanges does not need to be threaded anywhere. It is a contract problem: the context object has no member to carry the staged record, and that context's shape is published surface.
Why this is a contract-review-tier change (measured for #7165's "measurement 1")
renderCellEditor's context is declared on DataTableSchema in @object-ui/types — declared deliberately, by maintainer ruling 2026-08-30 (#6882), replacing a (schema as any) cast:
packages/types/src/data-display.ts — renderCellEditor?: (ctx: { column; row; value; stage; commit; cancel }) => React.ReactNode
packages/types/src/zod/data-display.zod.ts — the Zod mirror
packages/types/src/__tests__/data-table-declared-keys-6882.test.ts — pins the ctx shape by exact type equality, not extends:
type _RenderCellEditorShape = Expect<Equal<
Declared<DataTableSchema>['renderCellEditor'],
((ctx: CellEditorContext) => React.ReactNode) | undefined>>;
with the stated reason: "Declaring the key with any other shape is a different (and false) statement about the renderer, so the shape is pinned, not just the membership."
Adding a seventh member turns that pin red and edits a maintainer-ruled declaration. packages/plugin-grid/src/ObjectGrid.tsx states the same fence in its own words: "⛔ Do not 'fix' either hold by declaring the key on DataTableSchema as a rider — that package is published surface with its own review floor."
Suggested shape (the decision is the maintainer's)
Add one declared member rather than changing what row means — silently redefining row to be the merged record is a semantic change to a published member and would be the worse of the two:
The assertion B must add
#7165's PR ships a rendering test with a live control (a dependsOn column gates/ungates against a no-dependsOn column in the same render). B must add the case that control cannot cover, and it is the assertion that fails if someone later "simplifies" B back to A:
edit the parent cell in a row, do not save, then open the child picker — the candidate list must be scoped by the newly staged parent value.
Also live, per the PM's fence
⚠️ From the same ruling: while A ships, "should the user be told the scope is from the saved row" is an open question, not a settled one — the interim staleness is the same silent-wrongness class as the three refusals this seat landed against in #7146 / #7147 / #7148. B removes the staleness and therefore removes the question; if B is declined, the question comes back.
Filed unassigned by the #7165 execution lane, as ruled in #7165 comment 5491922231. #7165 shipped option A as an explicitly-labelled interim; this card is option B, the finished shape. #7165 stays open for it.
What A left undone
#7165's PR passes
dependentValues={ctx.row}inObjectGrid'srenderCellEditor, which unblocks adependsOnlookup column that was previously permanently uneditable.ctx.rowis the saved record.⇒ Edit the parent field in a row and, without saving, open the dependent child picker: the child is still scoped by the parent's persisted value. If the parent was previously empty, the child is still gated; if the parent was changed, the child lists candidates for the old parent.
That is strictly better than "can never be filled" and strictly not finished. PR #2216 answered the same question for the FORM renderer with the live watched record, so picking a parent re-scopes the child immediately. The grid should match.
The seam change — measured, not estimated
The data B needs is already in scope at the call site.
packages/components/src/renderers/complex/data-table.tsxcomputes, in the row loop that renders the editor:and a few lines later calls the injected editor with the saved row only:
So B is not a plumbing problem —
pendingChangesdoes not need to be threaded anywhere. It is a contract problem: the context object has no member to carry the staged record, and that context's shape is published surface.Why this is a contract-review-tier change (measured for #7165's "measurement 1")
renderCellEditor's context is declared onDataTableSchemain@object-ui/types— declared deliberately, by maintainer ruling 2026-08-30 (#6882), replacing a(schema as any)cast:packages/types/src/data-display.ts—renderCellEditor?: (ctx: { column; row; value; stage; commit; cancel }) => React.ReactNodepackages/types/src/zod/data-display.zod.ts— the Zod mirrorpackages/types/src/__tests__/data-table-declared-keys-6882.test.ts— pins the ctx shape by exact type equality, notextends:with the stated reason: "Declaring the key with any other shape is a different (and false) statement about the renderer, so the shape is pinned, not just the membership."
Adding a seventh member turns that pin red and edits a maintainer-ruled declaration.
packages/plugin-grid/src/ObjectGrid.tsxstates the same fence in its own words: "⛔ Do not 'fix' either hold by declaring the key onDataTableSchemaas a rider — that package is published surface with its own review floor."Suggested shape (the decision is the maintainer's)
Add one declared member rather than changing what
rowmeans — silently redefiningrowto be the merged record is a semantic change to a published member and would be the worse of the two:@object-ui/types— addpendingRow: any(orstagedRow) to the ctx: the saved row shallow-merged with that row'spendingChangesentry. Document thatrowstays the persisted record, so both are addressable.@object-ui/components(data-table.tsx) — passpendingRow: rowHasChanges ? { ...row, ...rowChanges } : row.@object-ui/plugin-grid(ObjectGrid.tsx) — switchdependentValues={ctx.row}toctx.pendingRow ?? ctx.row, and delete the interim commentbug(plugin-grid): adependsOnlookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165's PR left there naming this card.#6882exact-shape pin and the Zod mirror in the same change.The assertion B must add
#7165's PR ships a rendering test with a live control (a
dependsOncolumn gates/ungates against a no-dependsOncolumn in the same render). B must add the case that control cannot cover, and it is the assertion that fails if someone later "simplifies" B back to A:Also live, per the PM's fence