feat(spec): declare requiredPermissions on record:details / highlights / related_list with one true describe shared with record:quick_actions - #19913
Conversation
…s / related_list with one true describe shared with record:quick_actions The three record blocks refused requiredPermissions by name while objectui's renderers read it as an ADR-0066 capability set, fail-closed, at the pinned console. Declare it on all three with record:quick_actions' shape, and give all four ONE describe (seat ruling A): capabilities not object actions, all required, a notice in place of the content, presentation only, and fail-open when the client cannot resolve capabilities. The quick_actions describe was false on object scoping, so its published text changes too. Rewrites the texts that said the key was deliberately not declared, flips the absence pin to accept pins (instruments A/B, lit controls aria/fields), and corrects the pending pair changeset that the declaration makes false. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…e for the record-block requiredPermissions declaration gen:schema adds the three ui/Record*Props:requiredPermissions rows; gen:docs adds the three table rows and carries the new shared describe onto record:quick_actions. check:generated: all 15 artifacts up to date. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 20 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9a9bfaab55707c5231ec037cd38c1d7c0307ca40 && git checkout 9a9bfaab55707c5231ec037cd38c1d7c0307ca40
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5 b19ac16c77e812659a5a98d5740b82876b265ebf && git checkout -B drift-repro 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5 && git merge --no-ff b19ac16c77e812659a5a98d5740b82876b265ebf
node scripts/docs-audit/affected-docs.mjs --json 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5
|
|
Contract reviewServed-tier: Reviewed and posted 2026-09-23T18:24Z by the at-tier review subagent the ① Derived judgmentsShared describe (
Pins: head run 25/25 green. My ablation (three declarations replaced, anchor hits 3, decl lines 4→1, then Census (head Generated files: Shipping sentences: describe — true (above). New changeset ② Semver levelBoth changesets ③ Boundary flags
Blocking: (1) Implemented-by: VERDICT: FAIL |
…he gate hides the block At the pinned console the capability gate renders an insufficient-permissions notice in place of the block's content; it does not hide the block. Deletions only: the pending pair changeset ends at "not a member of this pair", the constant's docblock drops "hides a block and", the family header drops "and fail-closed", the new changeset drops the "hides the bar" clause, and the shared describe ends at "it fails open." (an empty requiredPermissions list sets no gate, so "a resolved empty set gates" misread). Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…ord-block requiredPermissions describe gen:docs: the four requiredPermissions rows lose "; a resolved empty set gates like any other" and nothing else. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
…round 2 Deletions only, per the seat's ruling on the two held questions: the RecordDetailsProps family header drops "hides the whole block and" (it now reads "presentation only: it authorises nothing."), and the new changeset drops "; a resolved empty set gates" so it ends at "(fails open)." like the describe. Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1 Co-authored-by: Claude <noreply@anthropic.com>
门禁评论
|
Contract reviewServed-tier: Reviewed and posted 2026-09-23T19:56Z by the at-tier review subagent the ① Derived judgmentsShared describe (
The five properties ruling
Pins: head 25/25. Instrument A with each block's base document: all three accept Census (head, git objects, Generated files: Every sentence that ships: describe — true (above). New changeset ② Semver levelBoth changesets ③ Boundary flags
Blocking: none. Implemented-by: VERDICT: PASS |
Maintainer confirmation — the DELIBERATE CORRECTION of
|
…cord-block-required-permissions
The os-regen driver kept the branch's side of ui.json in the merge of origin/main (6696056); main's thirteen DocNavItem keys were the dropped side. gen:schema on the merged tree restores them next to this branch's three record-block requiredPermissions keys, and nothing else. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
…s at objectui f8a9d0fb0596 origin/main moved `.objectui-sha` from 62597c588072 to f8a9d0fb0596, and the shared docblock of the block-level requiredPermissions gate asserts the pin it was read at. Every one of its 21 anchors was re-read at the new pin: the cited range holds the same text it held at 62597c588072 in all 21. record-details.tsx moved +1 (an import line) and record-related-list.tsx moved +34 (an import block and the props-type docblock), so nine anchors take new numbers: record-details 186/223/234/234-242 -> 187/224/235/235-243 and record-related-list 184/202/229/242/242-250 -> 218/236/263/276/276-284. record-highlights.tsx, record-quick-actions.tsx, MePermissionsProvider.tsx, PermissionProvider.tsx and usePermissions.ts are byte-identical across the hop, so their twelve anchors keep their numbers. Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: 101/101 Isolated at-tier reviewer subagent, run by the Reviewed 2026-09-25T03:55Z by the contract-tier review subagent the ① Derived judgments(a) The merge is faithful.
(b) The re-measure holds at
(c) Changesets, test, mdx unchanged. Blob ids at (d) No stale pin left in the PR's own diff; the gate is green on the head. (e) The fix-round section of the PR body, sentence by sentence. True and measured: the dequeue cause (#20036 = ② Semver levelUnchanged by the round and still correct: both changesets ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #18159
Clause-②: yes
Rewritten short by the
domain:spec#5seat (2026-09-23T19:59Z). The dev reports are on #18159 (5798750532,5800008407,5800978880,5801656924); the earlier long body is in the edit history.requiredPermissionsis now declared onrecord:details,record:highlightsandrecord:related_list, with the shaperecord:quick_actionsalready has (z.array(z.string()).optional()). Ruling: batch #197 item 2, letter A (5749268463), after objectui#10058 became installable at.objectui-sha62597c588072.One describe, four blocks (seat ruling
5798783314)The ruling asks for the same describe as
record:quick_actions. That published describe said "…every named permission on this object", which is false for the renderer at the pin: the check ishasCapabilities, and the renderer's own comment says the capability "is not object-scoped". So all four blocks now share ONE describe, from one constant (RECORD_BLOCK_REQUIRED_PERMISSIONS_DESCRIPTION). The publishedrecord:quick_actionsdescribe changes; its shape does not. The describe states:The renderer lines behind each clause are in the dev report
5800008407.Other changes
RecordDetailsPropsfamily header, the related-list and highlights pointers, and the test header..shapeenumeration per block, withariaandfieldsas lit controls, plus a pin that the four JSON Schemas of the key are identical. Removing the three declarations turns 11 of 25 pins red.authorable-surface/ui.jsonandcomponent.mdxare regenerated.Check Changesetis red on purposeThe pending note
.changeset/18159-record-block-field-security-pair.md(from PR #19185) said the third key "is deliberately NOT declared". This PR makes that false, so the PR corrects it. That is a DELIBERATE CORRECTION undercheck-empty-changeset.mjs: the check stays red, and the correction awaits the maintainer's written confirmation on this PR. The line to confirm is quoted verbatim in5801679846.Check Changesetis not a required context.Fix round 2026-09-25:
origin/mainmerged, objectui pin re-measuredFix round for
domain:spec#4(sessionsession_019c3Hi6ZMU1p6m6aA6Bz45d), new headb19ac16c77. The merge queue dequeued the PR.Type Check · source gatesfailed atcheck:objectui-pin-citations: #20036 (0bf85eaae6) moved.objectui-shafrom62597c588072tof8a9d0fb0596, and the sharedrequiredPermissionsdocblock incomponent.zod.tsstill asserted the old pin. This round adds three commits and changes nothing else:62decd77ddmergesorigin/mainat66960564d9withscripts/pm/os-regen-merge.sh(merge commit; no rebase or force-push). It had no text conflict.a3075b6aceregeneratespackages/spec/authorable-surface/ui.jsonon the merged tree. In that file the merge driver kept this branch's side and dropped main's 13DocNavItemkeys. The regenerated file has main's keys plus this PR's threerequiredPermissionskeys, and nothing else.b19ac16c77re-measures the docblock at the new pin. It updates the sha and the anchors together (10 lines replaced, 0 added).What was measured
objectui at
f8a9d0fb0596f4521076628e2bbfe27e6ce67d52was read from a scratch fetch of both pins. The seven cited files match the codeload tarball of the same commit byte for byte. All 21 anchors in the docblock were re-read. At the new pin, each cited range holds the same text it held at62597c588072. No read point changed meaning or disappeared.record-details.tsxshifted +1 (one new import line).record-related-list.tsxshifted +34 (an import block and the props-type docblock). Nine anchors get new numbers.record-highlights.tsx,record-quick-actions.tsx,MePermissionsProvider.tsx,PermissionProvider.tsxandusePermissions.tsdid not change between the two pins, so their twelve anchors keep their numbers.f8a9d0fb0596record-details.tsx:234→:235if (required.length > 0 && !perms.hasCapabilities(required)) {record-details.tsx:223→:224* capability is not object-scoped, and the old && objectName conjunct wasrecord-details.tsx:234-242→:235-243if (required.length > 0 && !perms.hasCapabilities(required)) {record-details.tsx:186→:187if (!ctx) {record-related-list.tsx:242→:276if (required.length > 0 && !perms.hasCapabilities(required)) {record-related-list.tsx:229→:263* capability is not object-scoped. This site never carried therecord-related-list.tsx:242-250→:276-284if (required.length > 0 && !perms.hasCapabilities(required)) {record-related-list.tsx:184→:218if (!objectName) {record-related-list.tsx:202→:236if (perms.isLoaded && !perms.can(objectName, 'read')) {record-highlights.tsx:93(unchanged)const highlightsAllowed = required.length === 0 || perms.hasCapabilities(required);record-highlights.tsx:77(unchanged)* capability is not object-scoped, and the old && objectName conjunct wasrecord-highlights.tsx:151-164(unchanged)if (!highlightsAllowed) {record-highlights.tsx:146-149(unchanged)useRegisterHighlightFields(record-quick-actions.tsx:263(unchanged)if (required.length > 0 && !perms.hasCapabilities(required)) {record-quick-actions.tsx:252(unchanged)* capability is not object-scoped, and the old && objectName guard was arecord-quick-actions.tsx:263-271(unchanged)if (required.length > 0 && !perms.hasCapabilities(required)) {MePermissionsProvider.tsx:416, and the later:416(unchanged)return required.every((p) => held.has(p));MePermissionsProvider.tsx:414(unchanged)if (!Array.isArray(perms)) return true;PermissionProvider.tsx:77(unchanged)const ALL_CAPABILITIES: PermissionContextValue['hasCapabilities'] = () => true;usePermissions.ts:45(unchanged)hasCapabilities: () => true,(The inner backticks around
&& objectNamein the three docblock lines are left out of the table.)One observation from the re-read, which changes no anchor. The props-type docblock that
record-related-list.tsxgained (:89-92) says no block the contract maps onto this tag declaresrequiredPermissions, and says not to reopen the type to admit it. This PR makes that sentence stale. That is the objectui half ruled A on objectstack-ai/objectui#10281, and it moves to thedomain:uiseat. The renderer's read of the key (:240-242, through a cast) and its gate (:276) are unchanged.Gates, on head
b19ac16c77check:objectui-pin-citationsexits 0 on--self-test, on the ordinary run and on--verify-anchors, with objectui at the new pin supplied throughOBJECTUI_ROOT. The ordinary run prints: "48 asserting objectui pin citation(s) match .objectui-sha (f8a9d0fb0), 40 historical citation(s) recorded and not checked, across 1555 spec source(s). 7 anchor content assertion(s) verified against objectui at f8a9d0fb0". CI has no objectui checkout, so it verifies the sha label only.pnpm --filter @objectstack/spec build, thencheck:generated: all 15 generated artifacts are up to date.typecheckexits 0. The full spec suite: 535 files, 15727 passed, 2 todo.node scripts/pm/dispatch-gates.mjs --commandsderived 107 commands for this change set. All 107 ran, and--ranreconciles 107 of 107 with recorded exit codes and 0 NOT MEASURED. Seven of them first exited 3 (no build to read). They were re-run green after building the lint closure, the client-react closure and then every package. 106 exit 0. The one exit 1 ischeck-empty-changeset.mjs. That is the deliberate correction described above, which the maintainer confirmed at headaeb6a57456(5823706830).git diff --exit-code aeb6a57456 b19ac16c77 -- .changeset/18159-record-block-field-security-pair.md .changeset/18159-record-block-required-permissions.mdexits 0. The blobs ared502a9a04c6aand80546d851168on both heads. The test file andcomponent.mdxare byte-identical to that head as well.🤖 Generated with Claude Code
https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1