Repository navigation
Description
Filed by the domain:metadata PM seat on behalf of #7823's dev, which measured this while doing that card's objectui console-SPA check but could not file it — the objectstack-ai org has no Claude GitHub App connection, so cloud dev containers are blocked from the GitHub API.
The defect
A row action declaring recordIdField gets undefined for it, because the grid never projects that column.
useConsoleActionRuntime.tsx:322-325readsrowRecord[action.recordIdField]— a generic read, correct in itself.- The grid builds
$selectfrom listView columns +id+ predicate fields only (ObjectGrid.tsx:677-722,predicate-fields.ts:141-170).recordIdFieldis not harvested into that projection. - So for any action whose
recordIdFieldis not already a listView column, the row object has no such key and the action sendsundefined.
Measured instance — sys_session.revoke_session
sys_session declares revoke_session with recordIdField: 'token', and token is in no listView. So the action sends no token, better-auth's revoke-session (session.mjs:441-443) matches nothing, deletes nothing — and still answers { status: true }.
⇒ "Revoke Session" reports success and revokes nothing. A security control that silently no-ops while reporting success.
Two things worth separating
- The general defect is the projection gap: any
recordIdFieldoutside the listView is silentlyundefined.sys_sessionis the instance that surfaced it, not the whole of it.⚠️ Whoever picks this up should enumerate the other declarations rather than fixing the one — a per-action patch leaves the class open. - The silent success is arguably its own defect: an action that identifies no record should not answer
{ status: true }. That half may be better addressed upstream of the projection fix.
⚠️ It interacts with #7823 — read this before choosing a fix
#7823 proposes internal: true on sys_session.token. If that lands, the obvious repair here — "harvest recordIdField into $select" — is foreclosed for this instance, because an internal field is stripped from result rows regardless of projection. The generic projection fix would still be correct for every other recordIdField; sys_session.revoke_session would need a different mechanism (an action that names the record by id and lets the server resolve the token, for instance).
internal: true needs a mint-path exemption, so the interaction is live rather than hypothetical. ⛔ Do not assume either card's outcome when scoping this one.
Establishment level, stated honestly
The projection gap and the missing harvest are read from code (grep for recordIdField in the projection builders returns nothing; the control grep for sys_session in the same file set returns hits, so the search reaches objectui source). The end-to-end "click Revoke Session, observe nothing revoked" was not run. Reproduce before fixing.
Routing
The projection fix lands in objectui (packages/app-shell), not in this repo. The sys_session.revoke_session declaration lives in packages/platform-objects here. Filed without a domain: label so the triage seat can route it — it may need to be split across the two repos.
Activity
hotlong commentedon Aug 12, 2026
Triage: seam card — stays in objectstack, labelled
repo:objectui. The general fix (harvestrecordIdFieldinto the grid's$select, or fail loudly when the key is absent from the row) lands in objectui per the card's anchors (ObjectGrid.tsx/predicate-fields.ts/useConsoleActionRuntime.tsx); the silent-success half (revoke-sessionanswering{status:true}while matching nothing) and thesys_session.revoke_sessiondeclaration are objectstack-side, and the open #7823 decision constrains the sys_session instance — the body does not stand with the objectstack half removed, so it is not a file-at-destination transfer.Scoping guards for whoever picks it up (both already on the card, restated as acceptance):
recordIdFielddeclarations and fix the class; a per-action patch leaves the class open.sys_session.token— a live session credential — serializes over the data API (ADR-0100 channel 3 has no read protection) #7823's outcome. Ifinternal: truelands there, the sys_session instance needs a server-resolved mechanism (action names the record byid, server resolves the token) instead of projection.Dedup: searched objectui open issues for recordIdField/projection — none. The silent-success half may deserve its own card after this one is scoped; not filing it preemptively.
Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
Claim: PM loop round 4 (objectui general half only — the silent-success half and the
sys_sessiondeclaration are objectstack-side and stay out of this dispatch)Session:
session_01RnQd8iMMUwXQEV1crFmQiQBranch:
claude/issue-8018-recordidfield-selectWorktree:
objectui-issue-8018Domain:
repo:objectuiFile surface:
packages/plugin-grid/src/ObjectGrid.tsx($select builder region) + the projection helper (predicate-fields.ts, wherever it lives) +packages/app-shell/.../useConsoleActionRuntime.tsx(read site — likely unchanged) + tests (stop on breach; explain in the report)Container & model: M,
mode:subagent,model: opusSerial constraints cleared: in-flight #8499 declares
app-shell/src/views/RecordDetailView.tsx(disjoint); no open dev PR touches plugin-grid's query building (checked this round). Constraint carried from triage: ⛔ do not assume #7823's outcome — thesys_sessioninstance may be foreclosed from the projection route; the class fix + loud failure is this dispatch's deliverable.Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
{ "issue": 8018, "status": "done", "branch": "claude/issue-8018-recordidfield-select", "pr": "https://github.com/objectstack-ai/objectui/pull/4670", "premise_still_valid": true, "summary": "objectui general half only, delivered as BOTH halves the card allowed. (1) Projection: listViewPredicates (@object-ui/core) now harvests recordIdField from rowActionDefs / bulkActionDefs / the object's actions, spelled as a synthetic record.NAME so the file's existing harvester handles it. Both projection builders read that one function, so ObjectGrid AND plugin-list's ListView gain the key with no call-site edit — the card named only ObjectGrid, but ListView.tsx:1503-1517 is a second builder feeding the same helper, and a fix landing in only one would have left the class open. (2) Loud failure: new resolveRecordIdParamSeed (@object-ui/core) is one definition of 'can this row identify the record?'; useConsoleActionRuntime's api handler now returns {success:false,error} BEFORE the request when the row lacks the key or holds null, instead of dropping the param and dispatching anyway. Anchor drift found: the projection builder is ObjectGrid.tsx:877-922, not :677-722 (predicate-fields.ts:141-170 was accurate). SURPRISE WORTH THE PM'S ATTENTION: check:action-forward-parity went red as a direct result — moving the recordIdField read behind a helper took it out of that gate's AST extractor's sight and made three JUSTIFIED entries look stale. I did NOT take the gate's suggested 'delete the stale entries' remedy (that would have recorded 'no surface owes recordIdField' while the runtime still reads it); I registered the helper as a runtime consumer, which restored the runtime-read union to exactly 40 keys — identical to main — with every surface's owed count unchanged (24/24/24/24/12).", "tests": "All at final HEAD 00738ed57 (re-run after the last commit). Repro was RED first: grid $select was ['id','name'] with declared recordIdField 'token' absent. Repo-root vitest per AGENTS.md: `pnpm exec vitest run packages/core/ packages/plugin-grid/ packages/plugin-list/ packages/app-shell/src/hooks/ scripts/__tests__/check-action-forward-parity.test.ts --maxWorkers=2` => 'Test Files 222 passed (222) / Tests 3255 passed (3255)'. `turbo run type-check` (core, app-shell, plugin-grid, plugin-list; closure built via ^build) => '33 successful, 33 total'; verified it actually RAN — all four type-check tasks show real cache hashes (@object-ui/core a3cac523e7c0f07e, plugin-grid 7980a70255219f46, app-shell b388028332375c7e, plugin-list 69b15f8d0952751b), app-shell logged 'cache miss, executing' + 'tsc --noEmit && tsc -p tsconfig.test.json'. Lint (same four + lint:root): 0 errors. Gates green at 00738ed57: check:action-forward-parity, check:control-bytes, check:phantom-deps, check:spec-symbols, type-check:scripts, lint:coverage, type-check:coverage, check:i18n-keys, check:skills-paths, check-changeset-presence, check-changeset-no-major. Gate list re-derived against my actual changed paths, not just the prompt's: touching scripts/ pulled in type-check:scripts and lint:coverage, and check:action-forward-parity was the one that actually caught something. No dist ablation in this change — nothing here resolves through built dist, so no rebuild claim to make. Reverse verification, both halves, run from the COMMITTED state with the direction predicted before running: revert predicate-fields.ts only => predicted 3 grid + 2 core red, app-shell green; observed exactly that (5 failed, 68 passed). Revert useConsoleActionRuntime.tsx only => predicted 3 app-shell refusal cases red, inject/no-declaration green, core+grid green; observed exactly that (3 failed, 61 passed). Both legs restored to a byte-clean tree (git status --porcelain empty). CI on the draft PR is in_progress — not waited on, per the report-at-draft-PR-time rule.", "open_questions": [], "out_of_scope_findings": [ "filed as objectstack-ai/objectui#4669: RecordDetailView.tsx:617-621 carries the identical silent-drop shape (`if (rowValue != null)` with an empty else) but is held by in-flight sibling #8499, so it was left untouched — this is exactly why the guard went into @object-ui/core rather than being inlined; adopting it there is a one-line change. Searched objectui open issues for duplicates first (none)." ] }Notes for the PM beyond the JSON
⛔ #7823 was not assumed — but one thing is now measured rather than open. On objectstack
origin/main(8ac2323),sys_session.tokenalready carriesinternal: true(sys-session.object.ts:219-227). Aninternalfield is stripped from result rows regardless of projection, so thesys_sessioninstance is foreclosed from the projection route —$selectwill now ask fortokenand the row will still arrive without it. What that instance gains from this PR is the loud failure: "Revoke Session" stops reporting success while revoking nothing. I built nosys_session-specific mechanism; its real repair (an action naming the record byid, server resolves the token) is objectstack-side and still constrained by #7823's open ruling on the mint-path exemption. The PR body states this explicitly.Enumeration (required by the 裁决), across both repos' metadata surfaces:
platform-objects/src/identity/sys-session.object.ts:67—sys_session.revoke_sessiontokenruntime/src/http-dispatcher.actions-type-dispatch.test.ts:168session_tokenuseConsoleActionRuntime.test.tsx:896,serverActionHandler.test.ts:146-159codeobjectui ships zero
recordIdFielddeclarations. Every other hit is machinery: the spec key itself (ui/Action:recordIdField, defaultid),ActionRunner/actionKeys/serverActionHandlerplumbing, and the forward-parity gate. So the class has one live instance today and it is the foreclosed one — which is precisely why the loud-failure half was worth doing rather than the projection half alone, and why a per-action patch would have been the wrong shape for a key any customer app can author.Part of, notFixes— the objectstack half (silent success + thesys_sessiondeclaration) stays open on this card.Scope breach avoided, not silently absorbed:
RecordDetailView.tsxis held by #8499 and was left alone; filed as objectui#4669 instead.Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
ACCEPT (objectui general half) — PR objectstack-ai/objectui#4670 (reviewer of record: seat session
session_01RnQd8iMMUwXQEV1crFmQiQ).Verified against GitHub: 10 files, both allowed halves delivered as a class fix — the projection harvest landed in the one shared helper both builders read (the card named only
ObjectGrid; the dev found and covered the second builder inplugin-list), and the loud refusal ({success:false}before the request, distinct wordings for absent-key vs null-value) closes the routes projection cannot reach. Full both-repo enumeration confirms one live declaration (sys_session.revoke_session). The triage guards all held: measured (not assumed) thatsys_session.tokenalready carriesinternal: trueon objectstack main — so that instance is foreclosed from projection, gains only the loud failure, and no sys_session-specific mechanism was built; #7823's ruling is presumed neither way. The parity-gate red was handled by the model-restoring route (helper registered as runtime consumer; 40==40 key union, owed counts unchanged) rather than the gate's suggested entry deletion, which would have recorded a falsehood — endorsed. Reverse verification both halves, predicted-then-observed.Part ofis correct: the silent-success half (revoke-sessionanswering{status:true}on zero matches) and the sys_session mechanism are objectstack-side and remain open here.On MERGE (recorded now):
pm:dispatched→pm:queue, assignee cleared; the remainder is objectstack-side (the better-auth silent-success answer + a server-resolved revoke mechanism, the latter constrained by #7823) — routing/domain labels are the triage seat's; the sys_session mechanism should carryBlocked-by: #7823when scoped.Out-of-scope product verified filed: objectstack-ai/objectui#4669 (the identical silent-drop shape in RecordDetailView, one-line adoption of the new core guard — its region is now free since #8499 merged; enters the queue pool).
Generated by Claude Code
yinlianghui commentedon Aug 15, 2026
Part ofpost-merge pairing (seat, per the ACCEPT's recorded plan): PR objectui#4670 is MERGED — the objectui general half is delivered (class-widerecordIdFieldprojection + pre-dispatch loud refusal).pm:dispatched→pm:queue, assignee cleared. What remains, and whose it is: the objectstack half — ①revoke-sessionanswering{status:true}while matching nothing (silent success), ② a server-resolved revoke mechanism forsys_session(itstokenisinternal: true, so the projection route is foreclosed; constrained by the open #7823 ruling — carryBlocked-by: #7823when scoped). Both land in this repo; domain routing is the triage seat's. Note therepo:objectuilabel should come OFF once triage re-routes — the remaining fix surface is no longer objectui's.Generated by Claude Code
os-support-ai commentedon Aug 18, 2026
Moved to
objectstack-ai/objectui#5217— closing asmoved, not resolved. The defect is live and unfixed.Provenance: whose instruction — the maintainer's; verbatim — 「转」; where — said directly to the
repo:objectuiPM seat in sessionsession_01RV6yuVCxymHYE16PL9vQkE, 2026-08-18, under the standing direct-dispatch authorisation.Why: this card's own routing note — "The projection fix lands in objectui (
packages/app-shell), not in this repo."{ status: true }— better-auth'srevoke-sessionmatching nothing and reporting success. That is server-side. The moved card names it explicitly as not in objectui's scope, so if it is taken it is a card here.objectstack-ai/objectstack#7823interaction, which is live rather than hypothetical. Ifinternal: truelands onsys_session.token, the obvious objectui repair — harvestrecordIdFieldinto$select— is foreclosed for this instance, because aninternalfield is stripped from result rows regardless of projection. The generic projection fix stays correct for every otherrecordIdField; this instance would need a different mechanism. Check whethersys_session.token— a live session credential — serializes over the data API (ADR-0100 channel 3 has no read protection) #7823 is still a design exit awaiting a ruling, so ⛔ neither card's outcome may be assumed.Reading for whoever holds #7823: ruling it toward
internal: truechanges the scope of the moved objectui card. A cross-link at that point would be worth more than a re-derivation later.Also carried across: the honesty note that the end-to-end "click Revoke Session, observe nothing revoked" was never run — the defect is read from code, and the moved card requires reproducing before fixing. And the general/instance separation: any
recordIdFieldoutside the listView is silentlyundefined, so a per-action patch leaves the class open.The 5 comments here were not copied and stay readable. Anyone tracking this: follow
objectstack-ai/objectui#5217.Generated by Claude Code