Repository navigation
fix(plugin-detail): the record page's inline edit reads the stored row's field locks - #27226
Merged
Merged
Conversation
…w's field locks record:details and record:highlights wrap their bodies in the record field projection scope, so a when-conditioned field lock disables a field only on the rows the server's write gate refuses. Claude-Session: https://claude.ai/code/session_01JQwxbZ6ALxKhRMeuJrvUeU Co-authored-by: Claude <noreply@anthropic.com>
…ord field projection Claude-Session: https://claude.ai/code/session_01JQwxbZ6ALxKhRMeuJrvUeU Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
席位核对ACCEPT · seat 席位意见:记录详情页的行内编辑原先读
|
This was referenced Oct 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Warren · project thread
Fixes #27146
维护者速读
record:details正文与record:highlights高亮条)改问服务器对这条记录的字段投影,不再只读不含行的/auth/me/permissions。when的字段锁(如审批后锁定摘要)在草稿行上也被禁用,而服务器其实允许写;编辑对话框已在 fix(ui): the record form's edit dialog reads the record's field projection #27137 修好,详情页是剩下的一处。getRecordFieldAccess时才多发一次请求;两个区块同页时各问一次。回滚即还原三个 plugin-detail 文件。ui/packages/plugin-detail/src/recordFieldAccess.tsx、renderers/record-details.tsx、renderers/record-highlights.tsx。pnpm --dir ui exec vitest run packages/plugin-detail/src/renderers/__tests__/record-blocks.recordFieldAccess-27146.test.tsx,预期 10 个测试全绿。Clause-②: no
Metadata-protocol: none
What changes
ui/packages/plugin-detail/src/recordFieldAccess.tsx(RecordFieldAccessForRecord, internal, not exported): mountsRecordFieldAccessScope(@object-ui/permissions) for the block's stored record when the page's inline-edit session may edit (canEdit), the block offers in-place edit, the record has an id and the data source carriesgetRecordFieldAccess. Below it, the unchangedresolveFieldAffordance('recordEdit', …)calls inDetailSectionandHeaderHighlightread the server's projection for that row (ruling 6457467702, letter B: the server's answer, no clientwhenevaluator). It asks again on a write the data source reports for the record and on the data-invalidation bus for it, the triggers the form'sRecordFieldAccessForFormholds.record:detailswraps itsDetailViewin it (gated on its owninlineEditverdict);record:highlightswraps itsHeaderHighlight.content/docs/permissions/field-level-security.mdx: the record-scoped projection paragraph now names the record page's in-place edit as a reader, and states the card's declared limitation — a list view's inline edit locks a whole column and keeps the row-blind answer.@object-ui/plugin-detailpatch.Why the mount sits on the two blocks, not on the page host
Measured on
origin/mainb322b5da:record:detailsandrecord:highlightsare sibling blocks of the page schema; their nearest common ancestors are the page host's providers inapp-shellRecordDetailViewand the generic page renderer, so noplugin-detailpoint covers both. The dispatch's fallback was one wrapper inRecordDetailView, but a scope there would also cover the page's action dialogs and related lists:RecordFieldAccessScopededupes only a nested scope for the SAME record, so a form editing ANOTHER row of the same object (a self-referencing child, say) would read this row's answer until its own arrived, or for good if its ask failed. Mounting on the two bodies keeps the answer on the surfaces that edit this row. The cost: a page drawing both blocks asks twice per load and per save.Why a copy of the trigger lines rather than a shared export
RecordFieldAccessForFormlives in@object-ui/plugin-formand is not exported;plugin-detaildoes not depend onplugin-form, and@object-ui/permissionsdoes not depend on@object-ui/react(the bus hook). Sharing it would mean a new published export (Clause-② yes) or a new dependency edge, so the ~20 trigger lines are repeated with a note naming the twin.Tests
ui/packages/plugin-detail/src/renderers/__tests__/record-blocks.recordFieldAccess-27146.test.tsx, for each ofrecord:detailsandrecord:highlightsunder the record page's providers (MePermissionsProviderserving the conditioned entry as its lock, record context, inline-edit session):summaryedits in place,ref_no(locked on every row) stays a read display;summarystays locked;summarylocks;getRecordFieldAccesskeeps the row-blind lock;Ablation (one-shot, not kept):
scripts/ablation-replace.mjsturned the wrapper'sif (!asks)into an always-true condition (the scope never mounts; anchor 1 → 0, blob2cbc16ec79d0→c38590de709a): 6 of 10 red (the draft, approved and re-ask rows for both blocks), the 4 controls green; restored blob == HEAD,git diff HEADempty.Acceptance notes
RecordDetailPanel(kanban / calendar / gantt record drawers) draws the sameDetailSectionand its in-place edit still reads the row-blind answer. Not changed here: its saves run through the host'sonFieldSave, so the re-ask trigger needs its own reading. Noted for the seat.HeaderHighlightinsideDetailView(an authoredhighlightFieldsheader) is covered on the record page through therecord:detailsmount.Verification
Local runs
pnpm --dir ui exec vitest run packages/plugin-detail/+pnpm --filter @object-ui/plugin-detail type-checkon7d0d12c8: Test Files 322 passed, 1 skipped; Tests 2951 passed, 18 skipped; type-check exit 0 (lock VERDICT command-exit 0).origin/main(feat(app-shell,plugin-detail,components,i18n): the record page's timeline and related columns are each reader's per-object switches, toggled from the crumb row #27120 landed inplugin-detail,components,i18n): rebuilt@object-ui/i18nand@object-ui/components, re-ranpackages/plugin-detail/src/renderers/plusDetailSection.fieldWriteGrant-12103: 108 files, 1090 tests passed.dispatch-gates --commands: 68 derived families, each run on9baa1e1aand recorded exit 0 (check:skill-examplesfirst exited 3, PREREQUISITE NOT MET, until@objectstack/client-reactwas built, then 0);--ranreconciliation: 68 run, 0 NOT-MEASURED, 0 UNRUN.check-sdui-manifestexit 0 among them.Human Minimum
packages/specholds no runtime logic.pnpm lintpasses (CI).main, one pull request.packages/specuntouched.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code