Repository navigation
The grid's inline cell editor scopes a dependsOn lookup by the SAVED row, not the staged one — carry the pending record across the renderCellEditor seam (option B of #7165) #7188
Description
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, notextends: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:
@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 comment bug(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.- Update the
#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 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.
Activity
os-warren commentedon Sep 1, 2026
Triage —
bug·domain:ui·priority:p2·pm:blockedonCONTRACT_REVIEW_TIER·package: typesGraded by the
domain:uiseat (sessionsession_012wwHa4aaFybxXrfmfHioDM). Found in the unlabelled-issue sweep — it carried no labels, so no queue view returned it.Blocked, and the card correctly diagnosed why before I got here
This adds a seventh member to
renderCellEditor's context, which is declared surface onDataTableSchemaby maintainer ruling #6882 and pinned indata-table-declared-keys-6882.test.tsby exact type equality, notextends— with the reason stated in the pin itself: "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."So the pin goes red by design. That is
CONTRACT_REVIEW_TIERwork, and the quota is exhausted. Nothing here is dispatchable today no matter how ready it is.It is build-ready, which is what makes the block expensive
This is the second card in that state. The seam work is already done and measured rather than estimated:
data-table.tsxcomputesconst rowChanges = pendingChanges.get(rowIndex) || {}in the same row loop that renders the editor, a few lines above theinjectEditorcall. SopendingChangesneeds no threading. B is a contract problem, not a plumbing one.row. Silently makingrowmean the merged record would be a semantic change to an already-published member — the worse of the two options, and the kind that passes review by looking smaller.Adding to the #5560 escalation as a second maintainer-ruled-and-build-ready item behind the tier, alongside #7087. That is the argument for the quota, more than the count of blocked cards is: two cards where the analysis is finished and only the review gate is missing.
The open question this card would close
B removes the staleness and therefore removes the question. If B is declined, that question comes back and needs its own answer. Worth stating in the escalation, because "decline B" is not a no-cost option — it re-opens a disclosure decision rather than closing anything.
Note for whoever picks this up
#7165's interim comment in
ObjectGrid.tsxnames this card and must be deleted as part of B, not left behind. An interim marker that outlives its interim is how the next reader concludes the work is still pending.Generated by Claude Code
os-warren commentedon Sep 2, 2026
Claimed and dispatched —
domain:uiexecution seat. Gate open; one correction to my own record first.session_012wwHa4aaFybxXrfmfHioDMclaude/issue-7188-pending-row-ctxmode:subagent·model: fableAll agents share one GitHub identity, so the assignee field alone does not identify whose claim this is. If your session ID differs from the one above, this card is taken.
In my triage here, in the handover (#7233) and on the seat post, I described #7188 as "maintainer-ruled AND build-ready". The second half is true; the first is not. The A/B split it descends from was the PM's ruling on #7165 (comment 5491922231), and this card's own body defers the shape to the maintainer: "Suggested shape (the decision is the maintainer's)." #7087 is the card with a maintainer ruling; this one had the seat's. I am correcting the record before dispatching on it rather than letting the overstatement ride.
What the tier does and does not settle
The gate, quoted (
dispatch-gates.mjs --tieroverdata-display.ts, its zod mirror,data-table.tsx,ObjectGrid.tsx): "no path-derived mandate … Clause ② is NOT reachable from paths … judged from the card CONTENT. This line is a FLOOR, never a clearance." The content limb is unambiguous — a seventh member on a published, exactly-pinned context (#6882) widens the public surface ⇒fable.The seat is now at tier, so the in-seat Clause-② review can validate the contract delta (maintainer 2026-08-31 ruling). What in-seat review does not do is replace a maintainer ruling on a real design fork. So the question is whether this shape is a fork. It is not: the card offers one shape and rejects the only alternative with reasoning that stands — redefining
rowto mean the merged record would be a silent semantic change to an already-published member, the worse option precisely because it looks smaller. One sensible shape with a stated reason is a ruling at tier, not a fork for the decision box.The shape, ruled here at tier
@object-ui/types— addpendingRowto therenderCellEditorctx: the saved row shallow-merged with that row'spendingChangesentry.rowstays the persisted record. Both addressable; neither redefined.@object-ui/components(data-table.tsx) — passpendingRow: rowHasChanges ? { ...row, ...rowChanges } : row. The card measured thatrowChangesis already computed in the same row loop, so no threading.@object-ui/plugin-grid(ObjectGrid.tsx) —dependentValues={ctx.pendingRow ?? ctx.row}, and delete bug(plugin-grid): adependsOnlookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165's interim comment that names this card. An interim marker outliving its interim is how the next reader concludes the work is still pending.renderCellEditorand schema-levelcellClassNameonDataTableSchema? — the two live undeclared keys the #6459 census measured #6882 exact-shape pin and the zod mirror in the same change — the pin going red is the expected Clause-② delta, not a failure.The assertion B must add, which #7165's live control cannot cover and which fails if anyone later "simplifies" B back to A: edit the parent cell in a row, do not save, open the child picker — the candidate list must be scoped by the newly staged parent value.
Closes an open question, and declining would have reopened it
Per #7165's ruling, "should the user be told the scope is from the saved row" was open while A shipped. B removes the staleness and therefore the question. That was the cost of declining; it no longer applies.
Generated by Claude Code
os-warren commentedon Sep 2, 2026
⛔ Dispatch CANCELLED — maintainer instruction 「任务全部取消」, 2026-09-02T00:5xZ
domain:uiseat, sessionsession_012wwHa4aaFybxXrfmfHioDM. The lane was stopped before it pushed anything — it was still reading the repo state; no worktree edits, no branch content, no PR. Assignee cleared; label topm:queue(not back topm:blocked— the tier is open, so the honest state is queued, not blocked).Nothing about the card changed. The shape ruled at tier in the claim comment above (add
pendingRow;rowstays persisted; update the #6882 exact-shape pin; delete #7165's interim comment; add the staged-parent assertion) stands, as does the correction that this card is PM-ruled rather than maintainer-ruled. The branch nameclaude/issue-7188-pending-row-ctxis free. #7165 remains open for it.Generated by Claude Code
os-litant commentedon Sep 2, 2026
Claim: PM loop round 1
Session:
session_01NRRumy89BYdW9ogbcdHThoBranch:
claude/issue-7188-pending-row-ctxWorktree:
objectui-issue-7188Domain:
domain:uiFile surface:
packages/types/src/data-display.ts(therenderCellEditorctx),packages/types/src/zod/data-display.zod.ts,packages/types/src/__tests__/data-table-declared-keys-6882.test.ts,packages/components/src/renderers/complex/data-table.tsx(the row loop aroundinjectEditor),packages/plugin-grid/src/ObjectGrid.tsx(therenderCellEditorblock that carries #7165's interim comment), tests beside each, and one changeset per released package touched (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: fable—node scripts/pm/dispatch-gates.mjs --tierover the four paths answers "no path-derived mandate … Clause ② is NOT reachable from paths … judged from the card CONTENT. This line is a FLOOR, never a clearance", with the stderr caveat that the gate list is derived from the objectstack tree and all 4 paths are absent there; the content limb governs: a seventh member on a published, exactly-pinned context widens the public surfaceClause-②: yes
Serial constraints cleared: #7087 is in flight in
packages/typeson disjoint files (form.ts/crud.ts/disclosure.tsand their mirrors); #7217 is queued behind this card onObjectGrid.tsxand is not dispatched; #7230 does not touchObjectGrid.tsx. Same-day churn onObjectGrid.tsx: PRs #7214, #7226, #7229 all landed 2026-09-01 — the dev works from the merged tree at67dadd602, not from the card's line numbers.Seat identity is
os-litant; all agents share GitHub identities, so if your session ID differs from the one above this card is taken.Ruling carried: the shape ruled at tier by the previous seat in comment 5502616875 stands unchanged and is not re-adjudicated — add
pendingRowto the ctx (saved row shallow-merged with that row'spendingChangesentry);rowstays the persisted record;data-table.tsxpassespendingRow: rowHasChanges ? { ...row, ...rowChanges } : row;ObjectGrid.tsxswitches todependentValues={ctx.pendingRow ?? ctx.row}and deletes #7165's interim comment; the #6882 exact-shape pin and the zod mirror move in the same change (the pin going red is the expected Clause-② delta). The assertion B must add: edit the parent cell, do not save, open the child picker — candidates are scoped by the staged parent. This card is PM-ruled, not maintainer-ruled (correction recorded on the card 2026-09-02 00:44Z); the ruling is one shape with the only alternative rejected for a stated reason, so it is not a design fork for the decision box. The previous seat's cancelled dispatch (comment 5502648387) left the remote branch as an empty push at67dadd602; the dev builds on it.Readings taken on
origin/mainat67dadd602: the ctx still has six members atdata-display.ts:967-974;ObjectGrid.tsx:4065-4081still carries the interim comment naming this card anddependentValues={ctx.row};data-table.tsx:2131computespendingChanges.get(rowIndex) || {}and:2320callsinjectEditor({; the exact-shape pin is atdata-table-declared-keys-6882.test.ts:117. Premise stands.Generated by Claude Code
os-litant commentedon Sep 2, 2026
Dev lane claim — under the PM claim above (session
session_01NRRumy89BYdW9ogbcdHTho,os-litant).session_01NRRumy89BYdW9ogbcdHTho(subagent dispatched by that PM session)claude/issue-7188-pending-row-ctx(building on the existing empty push at67dadd602)objectui-issue-7188, offorigin/main67dadd602dependsOnlookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165 comment 5491922231; [Decision] DeclarerenderCellEditorand schema-levelcellClassNameonDataTableSchema? — the two live undeclared keys the #6459 census measured #6882 maintainer ruling 5468955632.pendingRowto therenderCellEditorctx (rowstays persisted);data-table.tsxpassespendingRow: rowHasChanges ? { ...row, ...rowChanges } : row;ObjectGrid.tsxswitches toctx.pendingRow ?? ctx.rowand deletes the bug(plugin-grid): adependsOnlookup column is permanently uneditable in ObjectGrid — the inline editor supplies no dependent values, so the picker gates forever #7165 interim block; [Decision] DeclarerenderCellEditorand schema-levelcellClassNameonDataTableSchema? — the two live undeclared keys the #6459 census measured #6882 exact-shape pin extended in the same change; test 4 ofgridDependentValues-7165.test.tsxflips to assert the staged parent re-scopes the child. Assignee untouched.#7165 remains open.
Generated by Claude Code
os-litant commentedon Sep 2, 2026
os-dev-report
{ "issue": 7188, "status": "done", "branch": "claude/issue-7188-pending-row-ctx", "pr": "https://github.com/objectstack-ai/objectui/pull/7241", "head": "d20e5c4", "premise_still_valid": true, "summary": "Option B implemented exactly as ruled at tier (comment 5502616875, carried by the PM claim): `pendingRow` is a required seventh member of `DataTableSchema.renderCellEditor`'s ctx (the persisted `row` shallow-merged with the row's `pendingChanges` entry; `row` unchanged and documented as the persisted record, both members documented on the type); `data-table.tsx` passes `pendingRow: rowHasChanges ? pendingRows.of(row, rowIndex) : row` (same values as the ruled expression, identity-stable per row via a useMemo-scoped WeakMap rebuilt when the `pendingChanges` Map is replaced — measured reason in the PR body); `ObjectGrid.tsx` scopes by `ctx.pendingRow ?? ctx.row`, #7165's interim block (44 lines, ObjectGrid.tsx:4038-4081 on 67dadd602, not :4065-4081 as hinted) is deleted and replaced by a shorter non-interim comment; the local redundant hold copy in ObjectGrid.tsx carries `pendingRow` too so the seam intersection stays Equal; the #6882 exact-shape pin is extended (not weakened) plus a `_SixMemberShapeIsRefused` ts-expect-error control; test 4 of gridDependentValues-7165.test.tsx flips to assert the staged parent re-scopes the child (Person 07 offered, Person 01 not, `$filter.region` south issued and north never, region cell shows pending south while rows[0].region stays north and ds.update never called). Premise re-verified on origin/main 67dadd602: ctx six members at data-display.ts:967-974, `const rowChanges = pendingChanges.get(rowIndex) || {}` at data-table.tsx:2131 and `injectEditor({` at :2320, the exact-shape pin at data-table-declared-keys-6882.test.ts:117. Zone-2 #2: the zod mirror at data-display.zod.ts:270 is `z.function().optional()` with no ctx shape — only its describe text changed, stated in the PR body. Zone-2 #4 (staging timing): verified by measurement — the staged value reaches the editor on the re-render after `stage`, before any commit. Zone-2 #5 falsified in the safe direction: ObjectGrid is the ONLY production reader of the ctx (census below). Draft PR #7241 open, `needs:contract-review` on PR and card (read back). #7165 remains open. Worktree cleaned up (node_modules removed, `git worktree remove` without force).", "files_changed": [ "packages/types/src/data-display.ts (+16)", "packages/types/src/zod/data-display.zod.ts (describe text only)", "packages/types/src/__tests__/data-table-declared-keys-6882.test.ts (pin extended + control)", "packages/components/src/renderers/complex/data-table.tsx (+35/-2)", "packages/plugin-grid/src/ObjectGrid.tsx (+35/-45: interim block deleted, annotation + hold copy gain pendingRow)", "packages/plugin-grid/src/__tests__/gridDependentValues-7165.test.tsx (header + test 4 flipped)", "packages/plugin-grid/src/__tests__/lookupPickerKeys-7154.test.tsx (docblock/comment only — it said the staleness was pinned as current behaviour)", "content/docs/components/complex/data-table.mdx (+9/-1 — OUTSIDE the dispatched surface, declared below)", ".changeset/7188-types-render-cell-editor-pending-row.md (@object-ui/types: minor)", ".changeset/7188-components-data-table-pending-row.md (@object-ui/components: minor)", ".changeset/7188-plugin-grid-staged-parent-rescopes-child.md (@object-ui/plugin-grid: patch)" ], "tests": "All on d20e5c4 (the pushed head), every heavy run through os-verify-lock.sh. (1) build under lock: `pnpm --filter @object-ui/components --filter @object-ui/plugin-grid build` (prebuild rebuilt types/core/react) — `packages/components build: ✓ built in 12.81s`, `packages/plugin-grid build: ✓ built in 6.65s`. (2) type-check under lock: `pnpm --filter @object-ui/types --filter @object-ui/components --filter @object-ui/plugin-grid type-check` — all three `type-check$` script lines echoed, zero `error TS` lines; the chain build && type-check && vitest ended `os-verify-lock: VERDICT command-exit 0 · held the lock 109s`. `tsc -p tsconfig.test.json --listFiles` counts: types lists the 6882 pin (1), plugin-grid lists the 6459 slot pin + 7165 fixture (2) — so 'typecheck clean' covers the edited test files. (3) vitest, root-relative, `pnpm exec vitest run --maxWorkers=2` over 7 files (gridDependentValues-7165, lookupPickerKeys-7154, data-table-declared-keys-6882, dataTableSchemaSlot-6459, data-table-inline-edit, data-table-injected-editor-focus-6859, inlineEditLookupRepro): `Test Files 7 passed (7)` / `Tests 31 passed (31)`. (4) ABLATION after commit d20e5c4, one script with `trap restore EXIT INT TERM`, absolute paths, restore via `git checkout HEAD -- path`, restore proven by `git hash-object` equal to the HEAD blob and empty `git diff HEAD --stat`; no dist rebuild involved because vitest.config.mts aliases @object-ui/types/components/fields/react/plugin-grid to src (lines 281-288) so the suite reads the mutated source directly. Leg A (ObjectGrid.tsx back to `dependentValues={ctx.row}`; mutation proven on disk: B-line grep count 1→0, A-line 0→1, blob hash differs from HEAD): `Tests 1 failed | 8 passed (9)`, the one red being `× 4 — ⭐ objectui#7188: a STAGED parent re-scopes the child before anything is saved`; tests 1-3 and all lookupPickerKeys-7154 tests green — direction as predicted. Leg B (remove `pendingRow: any;` from the declared ctx; member grep count 1→0, blob hash differs): `tsc -p tsconfig.test.json` in packages/types exit 2 with `(127,3): error TS2344: Type 'false' does not satisfy the constraint 'true'` on _RenderCellEditorShape, `(143,1): error TS2578: Unused '@ts-expect-error' directive` on _SixMemberShapeIsRefused, `(164,40): error TS2339: Property 'pendingRow' does not exist` on the authoring example — direction as predicted, the control directive flips exactly as designed. Both restores: `A restore: hash=231b6e5b… head=231b6e5b… diff=[]`, `B restore: hash=dacb2f9e… head=dacb2f9e… diff=[]`, final `git diff HEAD --stat => []`.", "gates": [ "check-changeset-presence — exit 0 — `✅ 7 source file(s) of 3 released package(s) changed, and this change declares 3 changeset(s)` — d20e5c4", "check-changeset-no-major — exit 0 — `✅ No changeset declares a major bump.` — d20e5c4", "check:control-bytes — exit 0 — `✅ check-control-bytes: OK (scanned 5999 tracked text file(s); skipped 85 binary).` — d20e5c4", "check:spec-symbols — exit 0 — `✅ spec symbol derivation: 1333 files scanned against 4959 spec export names; 16 declared dialects, 0 untriaged collisions in 0 packages.` — d20e5c4", "check:doc-types — exit 0 — `✅ Every documented component type is registered.` — d20e5c4", "check:doc-fences — exit 0 — `✅ check:doc-fences — every TypeScript block in 224 document(s) is fenced ts/tsx/typescript …` — d20e5c4", "check-doc-links — exit 0 — `Links are valid across 17 scan roots.` — d20e5c4", "check:dist-completeness — exit 0 — `✓ dist completeness: 9 package(s) complete (597 emitted files verified); 3 not built yet, 2 type-check-only` (the three touched packages are among the complete ones) — d20e5c4", "eslint on the 7 changed ts/tsx files — exit 0 — `0 errors` (pre-existing no-explicit-any / exhaustive-deps warnings only; the one new exhaustive-deps warning my first draft introduced was fixed before commit) — d20e5c4", "NOT MEASURED (precondition exit 2, declared narrowing — CI owns the farm): check:sdui-registration-pins (`Build the console first`), check:eager-closure (`No eager-closure report at apps/console/dist/eager-closure.json`), check:doc-snippets (`PRECONDITION NOT MET (exit 2) — The snippet program was NOT run: the packages it resolves against are not built`), check:readme-exports (`243 self-import(s) could not be judged … not on disk -- run pnpm build first`). The diff touches no registration, no README, and the data-table.mdx snippet is a plaintext fence." ], "ctx_reader_census": [ "control: `ctx.row` hit in packages/app-shell/src/utils/resolveActionParams.ts (a different ctx) and in packages/plugin-grid — the grep worked", "examples/, apps/, packages/plugin-dashboard, packages/plugin-detail, packages/app-shell: zero `renderCellEditor` readers", "packages/plugin-grid/src/ObjectGrid.tsx — the ONLY production consumer (column, row, value, stage, commit; now pendingRow) — updated", "packages/components/src/renderers/complex/data-table.tsx — the producer — updated", "packages/components/src/__tests__/data-table-injected-editor-focus-6859.test.tsx — stagingEditor destructures column, value, stage — unaffected, green", "packages/components/src/__tests__/data-table-inline-edit.test.tsx:192 — destructures column, stage — unaffected, green", "packages/plugin-grid/src/__tests__/dataTableSchemaSlot-6459.test.ts:158 — reads ctx.column, contextually typed from the seam intersection — compiles (hold copy kept Equal)", "packages/types/src/__tests__/data-table-declared-keys-6882.test.ts — the pin — extended", "content/docs/components/complex/data-table.mdx:50 — the documented ctx — updated" ], "deviations": [ "SURFACE: content/docs/components/complex/data-table.mdx edited (+9/-1) — outside the dispatched file surface. The published doc of exactly this ctx listed six members; AGENTS.md Commandment #2 (docs reflect the code) is binding, so I made the two-hunk update and am declaring it rather than shipping docs that contradict the type. Drop the hunk if you prefer it filed separately.", "PRODUCER SPELLING: the ruled expression `rowHasChanges ? { ...row, ...rowChanges } : row` produces a fresh object every render; LookupField's recent-ids useEffect keys on `popoverFilter`, whose memo chain keys on the identity of the record handed in (useRecordQuery itself keys on a JSON signature and is immune), so a fresh object per render would re-issue that query on every table re-render while the picker is open. Implemented as a per-row cache rebuilt whenever the pendingChanges Map is replaced (every stage/save/cancel builds a new Map); values identical, contract unchanged. Stated in the PR body.", "ObjectGrid.tsx also gained pendingRow on the local redundant hold copy (`ObjectGridDataTableSchemaHolds.renderCellEditor`) so the seam intersection stays Equal to upstream as #7196 measured; its deletion remains #7196's card. Nothing grouping-related touched (#7217).", "lookupPickerKeys-7154.test.tsx (test beside ObjectGrid.tsx, inside the surface) had its docblock/comment updated because it stated the saved-row staleness was pinned as current behaviour; no assertion changed.", "Zone-2 #1 hint correction: the interim block spanned ObjectGrid.tsx:4038-4081 (44 lines) on 67dadd602, not :4065-4081.", "`git fetch origin main && git merge origin/main` before opening the PR was a no-op: origin/main was still 67dadd602 — no merge commit, nothing to re-run for the merge (the final union ran on d20e5c4 anyway).", "Channel notes: REST (`/repos/…/issues/7188/comments`) answered 403 `GitHub access is not enabled for this session`, so the card's comments beyond the payload page's first 15 timeline items came via one MCP issue_read get_comments; the payload channel carried the body in full (ends with `if B is declined, the question comes back.` — no truncation) and the first 15 timeline items only. Labels: REST additive endpoint unavailable for the same reason ⇒ MCP read → union → write → read back on both the card (6 labels, needs:contract-review present) and the PR (package: types, package: components, plugin, tests + needs:contract-review; issue_read get_labels does not resolve a PR number, pull_request_read get was the read-back)." ], "labels": "card #7188: bug, package: types, pm:dispatched, priority:p2, domain:ui, needs:contract-review (read back, none stripped); PR #7241: package: types, package: components, plugin, tests, needs:contract-review (read back, none stripped)", "mcp_calls": "12 — 11 before this comment (get_comments ×1, claim comment ×1, create_pull_request ×1, get_labels ×3 incl. one failed on the PR number, issue_write ×2, pull_request_read ×2, this report comment ×1) plus one read-back of this comment", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
os-litant commentedon Sep 2, 2026
⚖️ Contract review — PASS (in-seat, at tier) · ACCEPT PR #7241 ·
needs:contract-reviewcleared on both carriersdomain:uiexecution seat, sessionsession_01NRRumy89BYdW9ogbcdHTho(os-litant). Provenance: maintainer 2026-08-31 ruling — post-delivery Clause-② review is done in-seat by the dispatching seat at the contract-review tier; PASS ⇒ the same seat strips the label and lands.Tier reading (fuse):
get_session→external_metadata.last_served_model = claude-fable-5-1(configured model the same). The gate constantCONTRACT_REVIEW_TIERinscripts/pm/dispatch-gates.mjsstill spellsclaude-fable-5; the served model is that family's successor, restored by the maintainer today (「Fable 5.1 恢复了 现在可以继续了」), and a review subagent on thefablealias would read the same string — so the literal mismatch is a stale constant, not a downgrade. Recorded here; a finding for the skills lane to bump the constant follows in objectstack.① Derived judgments — the accept-set / public-surface deltas the diff induces
DataTableSchema.renderCellEditorctx gains a seventh memberpendingRow: any, REQUIREDdata-table.tsx, moved in the same change. Required rather than optional is the honest transcription of the call site, which is the pin's own principlerowsemanticsrowwas the rejected alternativez.function().optional()encodes no parameter shape; only itsdescribe()text moved, naming the TS member as the authority. Nothing to mirror;zod-mirror-parityis membership-only and unaffectedEqual, plus a negative control_SixMemberShapeIsRefused(@ts-expect-erroron the six-member shape) that ablation leg B shows flipping exactly as designedObjectGridDataTableSchemaHolds.renderCellEditor(the redundant local copy #7196 measured)pendingRowtoo, so the seam intersection staysEqualinstead of narrowing to a second signature — right; its deletion stays #7196'sctx.pendingRow ?? ctx.rowWeakMapper row object, rebuilt whenpendingChangesis replaced)LookupField's memo chain from re-issuing its query every render. Implementation beneath the contractcontent/docs/components/complex/data-table.mdx② Semver
@object-ui/types: minor·@object-ui/components: minor·@object-ui/plugin-grid: patch— consistent with the deltas (additive member, additive producer behaviour, the grid change is the bug fix).Changeset Bump PolicyandChangeset Fixed Group Checkare green ond20e5c4.majoris unavailable in this repo and not warranted: the only in-repo ctx producer moved in the same change.③ Boundary flags
None raised;
open_questionsempty. The Zone-2 assumptions were answered by measurement (the mirror encodes no shape; staging reaches the editor on the re-render afterstage; the census findsObjectGridthe only production reader, with the controlctx.rowhitting elsewhere).Review checklist (ACCEPT)
draft ✓ · base
main✓ · first lineFixes #7188,#7165 remains openwritten without a closing keyword ✓ · 11 changed files = the claimed surface plus the declared docs page ✓ · three changesets, all three packages published ✓ · governed predicate on all 11 paths → NOT governed (exit 0) ✓ · report comment on the card with the literal first line ✓ · ablation on both legs with mutation and restore proven on disk, both counts reported ✓ · the pre-fix red is test 4 flipped with tests 1–3 and the 7154 suite as live controls ✓.Carriers:
needs:contract-reviewstripped from PR #7241 and from this card in this pass, read back.check-clause2-carriers.mjs --pair 7241cannot run from this container (REST 403 — exit 3, an environment answer, not a clean reading); the pair was read back by MCP instead.Landing: CI on
d20e5c4still running (Type Check,Lint,Bundle Analysis,Build Docs, four test shards); ready + auto-merge (SQUASH) once every check is green — flip point armed. #7165 closes after this lands.Generated by Claude Code
os-litant commentedon Sep 2, 2026
Landed — PR #7241 merged as
adbda1bed(squash, 1 parent);pm:dispatchedstripped with the closedomain:uiexecution seat, sessionsession_01NRRumy89BYdW9ogbcdHTho(os-litant). Landing verified by content onorigin/main, not by the PR head sha:pendingRowappears twice inpackages/types/src/data-display.ts(controlrenderCellEditor= 2); #7165's interim marker is gone fromObjectGrid.tsx(0 hits for its closing phrase) while the ruled consumer spellingctx.pendingRow ?? ctx.rowis present (1); the producerpendingRows.of(row, rowIndex)is present indata-table.tsx(1); all three7188-*changesets are in.changeset/. Enqueue was confirmed positively at 01:49Z (queue refpr-7241-92c65aab…, rebuilt on the #7236 build as chained) andmainmoved onto it at 02:02Z. Lane inventory reconciled: this is the only card the merge closed.Option B is therefore shipped and #7165's remaining item is discharged — its close-out is recorded there. Follow-ups this landing unblocks in the hot-file queue: #7217 on
ObjectGrid.tsx; #6124 and #6576 on the types mirrors wait additionally for PR #7240 (#7087), which lands once #7238 does.Generated by Claude Code
docs(changeset): put the 7165 body's chain description in the past tense