bug(plugin-grid): a dependsOn lookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165

Description

@os-warren

Provenance: measured while implementing objectui#7154, which asked whether copying dependsOn onto the grid's relational meta could expose or worsen objectui#2215. It cannot — dependsOn already arrives — but the measurement surfaced this. Filed unassigned.

What is measured

Rendered on 51449a043 (origin/main), an ObjectGrid with editable: true over an object schema carrying:

region: { type: 'text' }
regional_owner: { type: 'lookup', reference: 'os_7154_person', dependsOn: ['region'] }

and a row whose region is 'north'. Single-click the regional_owner cell and the inline editor renders:

  • trigger data-testid = lookup-trigger-gated, disabled = true, text "Select region first"
  • the browse-all button next to it disabled too

The control column in the same render (same reference, same records, no dependsOn) gives lookup-trigger-owner, enabled. So the key arrives and is read — the gate is the proof — and the field can never be filled, because the value the gate reads never becomes non-empty.

Pinned as current behaviour in packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (the dependsOn case), added by objectui#7154's PR.

Root cause — the half of PR objectui#2216 the grid never got

objectui#2215 ("Cascading lookup (dependsOn) broken in forms; table picker bypasses the dependent filter") was closed COMPLETED by PR objectui#2216, which fixed two halves:

  1. the FORM renderer injects its live watched record as the dependentValues prop, and
  2. every picker surface takes the dependsOn chain as a hard baseFilter, so no picker bypasses the cascade.

Half 2 is host-independent and is live on the grid path. Half 1 is per-host, and the grid was never given it. LookupField resolves dependentValues ?? ctx.formValues ?? ctx.data ?? {} (packages/fields/src/widgets/LookupField.tsx, the resolvedDependentValues memo):

  • dependentValues — the grid passes none. renderCellEditor in ObjectGrid.tsx renders FieldEditWidget with field / value / onChange only.
  • ctx.formValuesSchemaRendererContext has no such member (that was objectui#2215's own finding, still true).
  • ctx.data — the grid does not set it for a row.

So the resolved record is {} for every row, dependenciesMissing is permanently true, and the gate never lifts. Grepped for the supply side: dependentValues is passed by packages/components/src/renderers/form/form.tsx, packages/app-shell/src/views/ActionParamDialog.tsx and packages/plugin-grid/src/components/BulkActionDialog.tsx — the grid's own inline editor is not among them.

Why this was not fixed in objectui#7154

It is a behaviour change outside that card (which is about the relational copy set), and its shape is a real decision rather than a mechanical fill-in. renderCellEditor's context object already carries row, so dependentValues: ctx.row is one line — but row is the SAVED record. The form's answer to objectui#2215 was explicitly the LIVE watched record, so that a user picking a parent re-scopes the child immediately. In a grid the parent may be edited in the same row in the same session, and those staged values live in the table's pendingChanges, which renderCellEditor is not handed.

Two readings, and they are not the same product:

  • A. Feed the saved row (ctx.row). One line, unblocks the field, scopes the query by what is persisted. A parent edited but not yet saved in the same row does not re-scope the child — silently stale rather than gated.
  • B. Feed the row merged with its staged edits. Matches the form's semantics, and needs the DataTableSchema editor context to carry the pending record — a contract change on the seam (renderCellEditor's context is { column, row, value, stage, commit, cancel }).

Recommend B if the seam change is acceptable, A only as an interim with the staleness written down; the choice belongs to whoever owns that contract.

What is NOT measured

  • No user report is attached; this is a rendering measurement in the test environment, not a browser dogfood run.
  • Whether any shipped app declares dependsOn on a field that also appears in an editable grid. The defect is reachable by construction from the spec (FieldSchema.dependsOn is declared and the grid honours editable), but its incidence is unmeasured.
  • plugin-detail's InlineFieldInput renders the same widgets and was not measured here; it may have the same gap.

Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      bug(plugin-grid): a dependsOn lookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165

      Description

      @os-warren

      Provenance: measured while implementing objectui#7154, which asked whether copying dependsOn onto the grid's relational meta could expose or worsen objectui#2215. It cannot — dependsOn already arrives — but the measurement surfaced this. Filed unassigned.

      What is measured

      Rendered on 51449a043 (origin/main), an ObjectGrid with editable: true over an object schema carrying:

      region: { type: 'text' }
      regional_owner: { type: 'lookup', reference: 'os_7154_person', dependsOn: ['region'] }
      

      and a row whose region is 'north'. Single-click the regional_owner cell and the inline editor renders:

      • trigger data-testid = lookup-trigger-gated, disabled = true, text "Select region first"
      • the browse-all button next to it disabled too

      The control column in the same render (same reference, same records, no dependsOn) gives lookup-trigger-owner, enabled. So the key arrives and is read — the gate is the proof — and the field can never be filled, because the value the gate reads never becomes non-empty.

      Pinned as current behaviour in packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (the dependsOn case), added by objectui#7154's PR.

      Root cause — the half of PR objectui#2216 the grid never got

      objectui#2215 ("Cascading lookup (dependsOn) broken in forms; table picker bypasses the dependent filter") was closed COMPLETED by PR objectui#2216, which fixed two halves:

      1. the FORM renderer injects its live watched record as the dependentValues prop, and
      2. every picker surface takes the dependsOn chain as a hard baseFilter, so no picker bypasses the cascade.

      Half 2 is host-independent and is live on the grid path. Half 1 is per-host, and the grid was never given it. LookupField resolves dependentValues ?? ctx.formValues ?? ctx.data ?? {} (packages/fields/src/widgets/LookupField.tsx, the resolvedDependentValues memo):

      • dependentValues — the grid passes none. renderCellEditor in ObjectGrid.tsx renders FieldEditWidget with field / value / onChange only.
      • ctx.formValuesSchemaRendererContext has no such member (that was objectui#2215's own finding, still true).
      • ctx.data — the grid does not set it for a row.

      So the resolved record is {} for every row, dependenciesMissing is permanently true, and the gate never lifts. Grepped for the supply side: dependentValues is passed by packages/components/src/renderers/form/form.tsx, packages/app-shell/src/views/ActionParamDialog.tsx and packages/plugin-grid/src/components/BulkActionDialog.tsx — the grid's own inline editor is not among them.

      Why this was not fixed in objectui#7154

      It is a behaviour change outside that card (which is about the relational copy set), and its shape is a real decision rather than a mechanical fill-in. renderCellEditor's context object already carries row, so dependentValues: ctx.row is one line — but row is the SAVED record. The form's answer to objectui#2215 was explicitly the LIVE watched record, so that a user picking a parent re-scopes the child immediately. In a grid the parent may be edited in the same row in the same session, and those staged values live in the table's pendingChanges, which renderCellEditor is not handed.

      Two readings, and they are not the same product:

      • A. Feed the saved row (ctx.row). One line, unblocks the field, scopes the query by what is persisted. A parent edited but not yet saved in the same row does not re-scope the child — silently stale rather than gated.
      • B. Feed the row merged with its staged edits. Matches the form's semantics, and needs the DataTableSchema editor context to carry the pending record — a contract change on the seam (renderCellEditor's context is { column, row, value, stage, commit, cancel }).

      Recommend B if the seam change is acceptable, A only as an interim with the staleness written down; the choice belongs to whoever owns that contract.

      What is NOT measured

      • No user report is attached; this is a rendering measurement in the test environment, not a browser dogfood run.
      • Whether any shipped app declares dependsOn on a field that also appears in an editable grid. The defect is reachable by construction from the spec (FieldSchema.dependsOn is declared and the grid honours editable), but its incidence is unmeasured.
      • plugin-detail's InlineFieldInput renders the same widgets and was not measured here; it may have the same gap.

      Generated by Claude Code

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          bug(plugin-grid): a dependsOn lookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165

          Description

          @os-warren

          Provenance: measured while implementing objectui#7154, which asked whether copying dependsOn onto the grid's relational meta could expose or worsen objectui#2215. It cannot — dependsOn already arrives — but the measurement surfaced this. Filed unassigned.

          What is measured

          Rendered on 51449a043 (origin/main), an ObjectGrid with editable: true over an object schema carrying:

          region: { type: 'text' }
          regional_owner: { type: 'lookup', reference: 'os_7154_person', dependsOn: ['region'] }
          

          and a row whose region is 'north'. Single-click the regional_owner cell and the inline editor renders:

          • trigger data-testid = lookup-trigger-gated, disabled = true, text "Select region first"
          • the browse-all button next to it disabled too

          The control column in the same render (same reference, same records, no dependsOn) gives lookup-trigger-owner, enabled. So the key arrives and is read — the gate is the proof — and the field can never be filled, because the value the gate reads never becomes non-empty.

          Pinned as current behaviour in packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (the dependsOn case), added by objectui#7154's PR.

          Root cause — the half of PR objectui#2216 the grid never got

          objectui#2215 ("Cascading lookup (dependsOn) broken in forms; table picker bypasses the dependent filter") was closed COMPLETED by PR objectui#2216, which fixed two halves:

          1. the FORM renderer injects its live watched record as the dependentValues prop, and
          2. every picker surface takes the dependsOn chain as a hard baseFilter, so no picker bypasses the cascade.

          Half 2 is host-independent and is live on the grid path. Half 1 is per-host, and the grid was never given it. LookupField resolves dependentValues ?? ctx.formValues ?? ctx.data ?? {} (packages/fields/src/widgets/LookupField.tsx, the resolvedDependentValues memo):

          • dependentValues — the grid passes none. renderCellEditor in ObjectGrid.tsx renders FieldEditWidget with field / value / onChange only.
          • ctx.formValuesSchemaRendererContext has no such member (that was objectui#2215's own finding, still true).
          • ctx.data — the grid does not set it for a row.

          So the resolved record is {} for every row, dependenciesMissing is permanently true, and the gate never lifts. Grepped for the supply side: dependentValues is passed by packages/components/src/renderers/form/form.tsx, packages/app-shell/src/views/ActionParamDialog.tsx and packages/plugin-grid/src/components/BulkActionDialog.tsx — the grid's own inline editor is not among them.

          Why this was not fixed in objectui#7154

          It is a behaviour change outside that card (which is about the relational copy set), and its shape is a real decision rather than a mechanical fill-in. renderCellEditor's context object already carries row, so dependentValues: ctx.row is one line — but row is the SAVED record. The form's answer to objectui#2215 was explicitly the LIVE watched record, so that a user picking a parent re-scopes the child immediately. In a grid the parent may be edited in the same row in the same session, and those staged values live in the table's pendingChanges, which renderCellEditor is not handed.

          Two readings, and they are not the same product:

          • A. Feed the saved row (ctx.row). One line, unblocks the field, scopes the query by what is persisted. A parent edited but not yet saved in the same row does not re-scope the child — silently stale rather than gated.
          • B. Feed the row merged with its staged edits. Matches the form's semantics, and needs the DataTableSchema editor context to carry the pending record — a contract change on the seam (renderCellEditor's context is { column, row, value, stage, commit, cancel }).

          Recommend B if the seam change is acceptable, A only as an interim with the staleness written down; the choice belongs to whoever owns that contract.

          What is NOT measured

          • No user report is attached; this is a rendering measurement in the test environment, not a browser dogfood run.
          • Whether any shipped app declares dependsOn on a field that also appears in an editable grid. The defect is reachable by construction from the spec (FieldSchema.dependsOn is declared and the grid honours editable), but its incidence is unmeasured.
          • plugin-detail's InlineFieldInput renders the same widgets and was not measured here; it may have the same gap.

          Generated by Claude Code

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              bug(plugin-grid): a dependsOn lookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165

              Description

              @os-warren

              Provenance: measured while implementing objectui#7154, which asked whether copying dependsOn onto the grid's relational meta could expose or worsen objectui#2215. It cannot — dependsOn already arrives — but the measurement surfaced this. Filed unassigned.

              What is measured

              Rendered on 51449a043 (origin/main), an ObjectGrid with editable: true over an object schema carrying:

              region: { type: 'text' }
              regional_owner: { type: 'lookup', reference: 'os_7154_person', dependsOn: ['region'] }
              

              and a row whose region is 'north'. Single-click the regional_owner cell and the inline editor renders:

              • trigger data-testid = lookup-trigger-gated, disabled = true, text "Select region first"
              • the browse-all button next to it disabled too

              The control column in the same render (same reference, same records, no dependsOn) gives lookup-trigger-owner, enabled. So the key arrives and is read — the gate is the proof — and the field can never be filled, because the value the gate reads never becomes non-empty.

              Pinned as current behaviour in packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (the dependsOn case), added by objectui#7154's PR.

              Root cause — the half of PR objectui#2216 the grid never got

              objectui#2215 ("Cascading lookup (dependsOn) broken in forms; table picker bypasses the dependent filter") was closed COMPLETED by PR objectui#2216, which fixed two halves:

              1. the FORM renderer injects its live watched record as the dependentValues prop, and
              2. every picker surface takes the dependsOn chain as a hard baseFilter, so no picker bypasses the cascade.

              Half 2 is host-independent and is live on the grid path. Half 1 is per-host, and the grid was never given it. LookupField resolves dependentValues ?? ctx.formValues ?? ctx.data ?? {} (packages/fields/src/widgets/LookupField.tsx, the resolvedDependentValues memo):

              • dependentValues — the grid passes none. renderCellEditor in ObjectGrid.tsx renders FieldEditWidget with field / value / onChange only.
              • ctx.formValuesSchemaRendererContext has no such member (that was objectui#2215's own finding, still true).
              • ctx.data — the grid does not set it for a row.

              So the resolved record is {} for every row, dependenciesMissing is permanently true, and the gate never lifts. Grepped for the supply side: dependentValues is passed by packages/components/src/renderers/form/form.tsx, packages/app-shell/src/views/ActionParamDialog.tsx and packages/plugin-grid/src/components/BulkActionDialog.tsx — the grid's own inline editor is not among them.

              Why this was not fixed in objectui#7154

              It is a behaviour change outside that card (which is about the relational copy set), and its shape is a real decision rather than a mechanical fill-in. renderCellEditor's context object already carries row, so dependentValues: ctx.row is one line — but row is the SAVED record. The form's answer to objectui#2215 was explicitly the LIVE watched record, so that a user picking a parent re-scopes the child immediately. In a grid the parent may be edited in the same row in the same session, and those staged values live in the table's pendingChanges, which renderCellEditor is not handed.

              Two readings, and they are not the same product:

              • A. Feed the saved row (ctx.row). One line, unblocks the field, scopes the query by what is persisted. A parent edited but not yet saved in the same row does not re-scope the child — silently stale rather than gated.
              • B. Feed the row merged with its staged edits. Matches the form's semantics, and needs the DataTableSchema editor context to carry the pending record — a contract change on the seam (renderCellEditor's context is { column, row, value, stage, commit, cancel }).

              Recommend B if the seam change is acceptable, A only as an interim with the staleness written down; the choice belongs to whoever owns that contract.

              What is NOT measured

              • No user report is attached; this is a rendering measurement in the test environment, not a browser dogfood run.
              • Whether any shipped app declares dependsOn on a field that also appears in an editable grid. The defect is reachable by construction from the spec (FieldSchema.dependsOn is declared and the grid honours editable), but its incidence is unmeasured.
              • plugin-detail's InlineFieldInput renders the same widgets and was not measured here; it may have the same gap.

              Generated by Claude Code

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  bug(plugin-grid): a dependsOn lookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165

                  Description

                  @os-warren

                  Provenance: measured while implementing objectui#7154, which asked whether copying dependsOn onto the grid's relational meta could expose or worsen objectui#2215. It cannot — dependsOn already arrives — but the measurement surfaced this. Filed unassigned.

                  What is measured

                  Rendered on 51449a043 (origin/main), an ObjectGrid with editable: true over an object schema carrying:

                  region: { type: 'text' }
                  regional_owner: { type: 'lookup', reference: 'os_7154_person', dependsOn: ['region'] }
                  

                  and a row whose region is 'north'. Single-click the regional_owner cell and the inline editor renders:

                  • trigger data-testid = lookup-trigger-gated, disabled = true, text "Select region first"
                  • the browse-all button next to it disabled too

                  The control column in the same render (same reference, same records, no dependsOn) gives lookup-trigger-owner, enabled. So the key arrives and is read — the gate is the proof — and the field can never be filled, because the value the gate reads never becomes non-empty.

                  Pinned as current behaviour in packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (the dependsOn case), added by objectui#7154's PR.

                  Root cause — the half of PR objectui#2216 the grid never got

                  objectui#2215 ("Cascading lookup (dependsOn) broken in forms; table picker bypasses the dependent filter") was closed COMPLETED by PR objectui#2216, which fixed two halves:

                  1. the FORM renderer injects its live watched record as the dependentValues prop, and
                  2. every picker surface takes the dependsOn chain as a hard baseFilter, so no picker bypasses the cascade.

                  Half 2 is host-independent and is live on the grid path. Half 1 is per-host, and the grid was never given it. LookupField resolves dependentValues ?? ctx.formValues ?? ctx.data ?? {} (packages/fields/src/widgets/LookupField.tsx, the resolvedDependentValues memo):

                  • dependentValues — the grid passes none. renderCellEditor in ObjectGrid.tsx renders FieldEditWidget with field / value / onChange only.
                  • ctx.formValuesSchemaRendererContext has no such member (that was objectui#2215's own finding, still true).
                  • ctx.data — the grid does not set it for a row.

                  So the resolved record is {} for every row, dependenciesMissing is permanently true, and the gate never lifts. Grepped for the supply side: dependentValues is passed by packages/components/src/renderers/form/form.tsx, packages/app-shell/src/views/ActionParamDialog.tsx and packages/plugin-grid/src/components/BulkActionDialog.tsx — the grid's own inline editor is not among them.

                  Why this was not fixed in objectui#7154

                  It is a behaviour change outside that card (which is about the relational copy set), and its shape is a real decision rather than a mechanical fill-in. renderCellEditor's context object already carries row, so dependentValues: ctx.row is one line — but row is the SAVED record. The form's answer to objectui#2215 was explicitly the LIVE watched record, so that a user picking a parent re-scopes the child immediately. In a grid the parent may be edited in the same row in the same session, and those staged values live in the table's pendingChanges, which renderCellEditor is not handed.

                  Two readings, and they are not the same product:

                  • A. Feed the saved row (ctx.row). One line, unblocks the field, scopes the query by what is persisted. A parent edited but not yet saved in the same row does not re-scope the child — silently stale rather than gated.
                  • B. Feed the row merged with its staged edits. Matches the form's semantics, and needs the DataTableSchema editor context to carry the pending record — a contract change on the seam (renderCellEditor's context is { column, row, value, stage, commit, cancel }).

                  Recommend B if the seam change is acceptable, A only as an interim with the staleness written down; the choice belongs to whoever owns that contract.

                  What is NOT measured

                  • No user report is attached; this is a rendering measurement in the test environment, not a browser dogfood run.
                  • Whether any shipped app declares dependsOn on a field that also appears in an editable grid. The defect is reachable by construction from the spec (FieldSchema.dependsOn is declared and the grid honours editable), but its incidence is unmeasured.
                  • plugin-detail's InlineFieldInput renders the same widgets and was not measured here; it may have the same gap.

                  Generated by Claude Code

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

                    Type

                    No type

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      bug(plugin-grid): a dependsOn lookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165

                      Description

                      @os-warren

                      Provenance: measured while implementing objectui#7154, which asked whether copying dependsOn onto the grid's relational meta could expose or worsen objectui#2215. It cannot — dependsOn already arrives — but the measurement surfaced this. Filed unassigned.

                      What is measured

                      Rendered on 51449a043 (origin/main), an ObjectGrid with editable: true over an object schema carrying:

                      region: { type: 'text' }
                      regional_owner: { type: 'lookup', reference: 'os_7154_person', dependsOn: ['region'] }
                      

                      and a row whose region is 'north'. Single-click the regional_owner cell and the inline editor renders:

                      • trigger data-testid = lookup-trigger-gated, disabled = true, text "Select region first"
                      • the browse-all button next to it disabled too

                      The control column in the same render (same reference, same records, no dependsOn) gives lookup-trigger-owner, enabled. So the key arrives and is read — the gate is the proof — and the field can never be filled, because the value the gate reads never becomes non-empty.

                      Pinned as current behaviour in packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (the dependsOn case), added by objectui#7154's PR.

                      Root cause — the half of PR objectui#2216 the grid never got

                      objectui#2215 ("Cascading lookup (dependsOn) broken in forms; table picker bypasses the dependent filter") was closed COMPLETED by PR objectui#2216, which fixed two halves:

                      1. the FORM renderer injects its live watched record as the dependentValues prop, and
                      2. every picker surface takes the dependsOn chain as a hard baseFilter, so no picker bypasses the cascade.

                      Half 2 is host-independent and is live on the grid path. Half 1 is per-host, and the grid was never given it. LookupField resolves dependentValues ?? ctx.formValues ?? ctx.data ?? {} (packages/fields/src/widgets/LookupField.tsx, the resolvedDependentValues memo):

                      • dependentValues — the grid passes none. renderCellEditor in ObjectGrid.tsx renders FieldEditWidget with field / value / onChange only.
                      • ctx.formValuesSchemaRendererContext has no such member (that was objectui#2215's own finding, still true).
                      • ctx.data — the grid does not set it for a row.

                      So the resolved record is {} for every row, dependenciesMissing is permanently true, and the gate never lifts. Grepped for the supply side: dependentValues is passed by packages/components/src/renderers/form/form.tsx, packages/app-shell/src/views/ActionParamDialog.tsx and packages/plugin-grid/src/components/BulkActionDialog.tsx — the grid's own inline editor is not among them.

                      Why this was not fixed in objectui#7154

                      It is a behaviour change outside that card (which is about the relational copy set), and its shape is a real decision rather than a mechanical fill-in. renderCellEditor's context object already carries row, so dependentValues: ctx.row is one line — but row is the SAVED record. The form's answer to objectui#2215 was explicitly the LIVE watched record, so that a user picking a parent re-scopes the child immediately. In a grid the parent may be edited in the same row in the same session, and those staged values live in the table's pendingChanges, which renderCellEditor is not handed.

                      Two readings, and they are not the same product:

                      • A. Feed the saved row (ctx.row). One line, unblocks the field, scopes the query by what is persisted. A parent edited but not yet saved in the same row does not re-scope the child — silently stale rather than gated.
                      • B. Feed the row merged with its staged edits. Matches the form's semantics, and needs the DataTableSchema editor context to carry the pending record — a contract change on the seam (renderCellEditor's context is { column, row, value, stage, commit, cancel }).

                      Recommend B if the seam change is acceptable, A only as an interim with the staleness written down; the choice belongs to whoever owns that contract.

                      What is NOT measured

                      • No user report is attached; this is a rendering measurement in the test environment, not a browser dogfood run.
                      • Whether any shipped app declares dependsOn on a field that also appears in an editable grid. The defect is reachable by construction from the spec (FieldSchema.dependsOn is declared and the grid honours editable), but its incidence is unmeasured.
                      • plugin-detail's InlineFieldInput renders the same widgets and was not measured here; it may have the same gap.

                      Generated by Claude Code

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

                        Type

                        No type

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          bug(plugin-grid): a dependsOn lookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165

                          Description

                          @os-warren

                          Provenance: measured while implementing objectui#7154, which asked whether copying dependsOn onto the grid's relational meta could expose or worsen objectui#2215. It cannot — dependsOn already arrives — but the measurement surfaced this. Filed unassigned.

                          What is measured

                          Rendered on 51449a043 (origin/main), an ObjectGrid with editable: true over an object schema carrying:

                          region: { type: 'text' }
                          regional_owner: { type: 'lookup', reference: 'os_7154_person', dependsOn: ['region'] }
                          

                          and a row whose region is 'north'. Single-click the regional_owner cell and the inline editor renders:

                          • trigger data-testid = lookup-trigger-gated, disabled = true, text "Select region first"
                          • the browse-all button next to it disabled too

                          The control column in the same render (same reference, same records, no dependsOn) gives lookup-trigger-owner, enabled. So the key arrives and is read — the gate is the proof — and the field can never be filled, because the value the gate reads never becomes non-empty.

                          Pinned as current behaviour in packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (the dependsOn case), added by objectui#7154's PR.

                          Root cause — the half of PR objectui#2216 the grid never got

                          objectui#2215 ("Cascading lookup (dependsOn) broken in forms; table picker bypasses the dependent filter") was closed COMPLETED by PR objectui#2216, which fixed two halves:

                          1. the FORM renderer injects its live watched record as the dependentValues prop, and
                          2. every picker surface takes the dependsOn chain as a hard baseFilter, so no picker bypasses the cascade.

                          Half 2 is host-independent and is live on the grid path. Half 1 is per-host, and the grid was never given it. LookupField resolves dependentValues ?? ctx.formValues ?? ctx.data ?? {} (packages/fields/src/widgets/LookupField.tsx, the resolvedDependentValues memo):

                          • dependentValues — the grid passes none. renderCellEditor in ObjectGrid.tsx renders FieldEditWidget with field / value / onChange only.
                          • ctx.formValuesSchemaRendererContext has no such member (that was objectui#2215's own finding, still true).
                          • ctx.data — the grid does not set it for a row.

                          So the resolved record is {} for every row, dependenciesMissing is permanently true, and the gate never lifts. Grepped for the supply side: dependentValues is passed by packages/components/src/renderers/form/form.tsx, packages/app-shell/src/views/ActionParamDialog.tsx and packages/plugin-grid/src/components/BulkActionDialog.tsx — the grid's own inline editor is not among them.

                          Why this was not fixed in objectui#7154

                          It is a behaviour change outside that card (which is about the relational copy set), and its shape is a real decision rather than a mechanical fill-in. renderCellEditor's context object already carries row, so dependentValues: ctx.row is one line — but row is the SAVED record. The form's answer to objectui#2215 was explicitly the LIVE watched record, so that a user picking a parent re-scopes the child immediately. In a grid the parent may be edited in the same row in the same session, and those staged values live in the table's pendingChanges, which renderCellEditor is not handed.

                          Two readings, and they are not the same product:

                          • A. Feed the saved row (ctx.row). One line, unblocks the field, scopes the query by what is persisted. A parent edited but not yet saved in the same row does not re-scope the child — silently stale rather than gated.
                          • B. Feed the row merged with its staged edits. Matches the form's semantics, and needs the DataTableSchema editor context to carry the pending record — a contract change on the seam (renderCellEditor's context is { column, row, value, stage, commit, cancel }).

                          Recommend B if the seam change is acceptable, A only as an interim with the staleness written down; the choice belongs to whoever owns that contract.

                          What is NOT measured

                          • No user report is attached; this is a rendering measurement in the test environment, not a browser dogfood run.
                          • Whether any shipped app declares dependsOn on a field that also appears in an editable grid. The defect is reachable by construction from the spec (FieldSchema.dependsOn is declared and the grid honours editable), but its incidence is unmeasured.
                          • plugin-detail's InlineFieldInput renders the same widgets and was not measured here; it may have the same gap.

                          Generated by Claude Code

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

                            Type

                            No type

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              bug(plugin-grid): a dependsOn lookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165

                              Description

                              @os-warren

                              Provenance: measured while implementing objectui#7154, which asked whether copying dependsOn onto the grid's relational meta could expose or worsen objectui#2215. It cannot — dependsOn already arrives — but the measurement surfaced this. Filed unassigned.

                              What is measured

                              Rendered on 51449a043 (origin/main), an ObjectGrid with editable: true over an object schema carrying:

                              region: { type: 'text' }
                              regional_owner: { type: 'lookup', reference: 'os_7154_person', dependsOn: ['region'] }
                              

                              and a row whose region is 'north'. Single-click the regional_owner cell and the inline editor renders:

                              • trigger data-testid = lookup-trigger-gated, disabled = true, text "Select region first"
                              • the browse-all button next to it disabled too

                              The control column in the same render (same reference, same records, no dependsOn) gives lookup-trigger-owner, enabled. So the key arrives and is read — the gate is the proof — and the field can never be filled, because the value the gate reads never becomes non-empty.

                              Pinned as current behaviour in packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (the dependsOn case), added by objectui#7154's PR.

                              Root cause — the half of PR objectui#2216 the grid never got

                              objectui#2215 ("Cascading lookup (dependsOn) broken in forms; table picker bypasses the dependent filter") was closed COMPLETED by PR objectui#2216, which fixed two halves:

                              1. the FORM renderer injects its live watched record as the dependentValues prop, and
                              2. every picker surface takes the dependsOn chain as a hard baseFilter, so no picker bypasses the cascade.

                              Half 2 is host-independent and is live on the grid path. Half 1 is per-host, and the grid was never given it. LookupField resolves dependentValues ?? ctx.formValues ?? ctx.data ?? {} (packages/fields/src/widgets/LookupField.tsx, the resolvedDependentValues memo):

                              • dependentValues — the grid passes none. renderCellEditor in ObjectGrid.tsx renders FieldEditWidget with field / value / onChange only.
                              • ctx.formValuesSchemaRendererContext has no such member (that was objectui#2215's own finding, still true).
                              • ctx.data — the grid does not set it for a row.

                              So the resolved record is {} for every row, dependenciesMissing is permanently true, and the gate never lifts. Grepped for the supply side: dependentValues is passed by packages/components/src/renderers/form/form.tsx, packages/app-shell/src/views/ActionParamDialog.tsx and packages/plugin-grid/src/components/BulkActionDialog.tsx — the grid's own inline editor is not among them.

                              Why this was not fixed in objectui#7154

                              It is a behaviour change outside that card (which is about the relational copy set), and its shape is a real decision rather than a mechanical fill-in. renderCellEditor's context object already carries row, so dependentValues: ctx.row is one line — but row is the SAVED record. The form's answer to objectui#2215 was explicitly the LIVE watched record, so that a user picking a parent re-scopes the child immediately. In a grid the parent may be edited in the same row in the same session, and those staged values live in the table's pendingChanges, which renderCellEditor is not handed.

                              Two readings, and they are not the same product:

                              • A. Feed the saved row (ctx.row). One line, unblocks the field, scopes the query by what is persisted. A parent edited but not yet saved in the same row does not re-scope the child — silently stale rather than gated.
                              • B. Feed the row merged with its staged edits. Matches the form's semantics, and needs the DataTableSchema editor context to carry the pending record — a contract change on the seam (renderCellEditor's context is { column, row, value, stage, commit, cancel }).

                              Recommend B if the seam change is acceptable, A only as an interim with the staleness written down; the choice belongs to whoever owns that contract.

                              What is NOT measured

                              • No user report is attached; this is a rendering measurement in the test environment, not a browser dogfood run.
                              • Whether any shipped app declares dependsOn on a field that also appears in an editable grid. The defect is reachable by construction from the spec (FieldSchema.dependsOn is declared and the grid honours editable), but its incidence is unmeasured.
                              • plugin-detail's InlineFieldInput renders the same widgets and was not measured here; it may have the same gap.

                              Generated by Claude Code

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat

                                Type

                                No type

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions