fix(spec): grade permission rowLevelSecurity label/description live — Studio's permission editor shows them - #21607
Merged
objectstack-fleet[bot] merged 4 commits intoOct 3, 2026
Conversation
…the objectui pin The permission editor's Row-Level Security section draws each policy's label and description (PermissionAdvancedFacets, objectui PR #11215), read at the .objectui-sha pin 89cad75d557. Both rows go dead -> live, citing the reader and the producer chain. Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
…/ 4 dead) gen:liveness-counts output for the two rowLevelSecurity rows that moved dead -> live. No shard hand-edited. Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
…iner's name/label permission.rowLevelSecurity.label / .description went live, so the two pins that used them as the authorable dead sample would fail. Their own comment prescribes the move: another dead row of a type the walk visits, authored by a fixture the shipped schema parses. The view container's own name and label are that row pair. Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
…escription re-grade The ledger ships in @objectstack/spec (files[] includes liveness), so the re-grade is a patch. The README's permission Notes cell enumerated these two keys in the dead set; it now records the re-grade and the dead set that remains. Claude-Session: https://claude.ai/code/session_01T9u38rswFp5Rw8DswRUReJ Co-authored-by: Claude <noreply@anthropic.com>
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
objectstack-fleet
Bot
deleted the
claude/issue-20299-rls-policy-rows-live
branch
October 3, 2026 17:23
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.
Fixes #20299
Clause-②: no
What changed
permission.rowLevelSecurity.labelandpermission.rowLevelSecurity.descriptionmovedead→livein the liveness ledger. Studio's permission editor now draws both on every row-level security policy card. That came from objectui#11199, landed as PR objectui#11215 (mergef8334f8777). The rows cite that reader at the.objectui-shapin89cad75d557, measured in the pinned tree and never on objectuimain.This is the last remainder the landing record
5909245536left on this card. Theviewcontainerlabelrow staysdeadunder ruling A (5907340127, Q1) and is not touched here.Files (5, +59 / −51):
packages/spec/liveness/permission.json: the two rows. Each carriesevidence(the reader),producer(registration → route → data read → framework answer → an authored producer) and a note that keeps the superseded verdict as history.packages/spec/liveness/state-counts/permission.md: regenerated bygen:liveness-counts, never hand-edited.permissionis now 38 live / 4 dead (was 36 / 6).packages/spec/liveness/README.md: thepermissionNotes cell listed these two keys in the dead set. One dated clause records the re-grade and the dead set that remains (priority,tags, and the twoobjects.allowRestore/allowPurgetombstones). Thecheck:livenessfailure text names this cell as owed when a count moves.packages/lint/src/lint-liveness-properties.test.ts: two pins re-subjected; see below..changeset/20299-rls-policy-rows-live.md:@objectstack/specpatch. The ledger ships in the package (measured below).The reading, at the pin
The pin on this branch's base
6c5697dffis.objectui-sha=89cad75d55702cc4f267bead5bf267de575d5842.git merge-base --is-ancestor f8334f8777 89cad75d557answers exit 0, which proves itself. Every line below isgit show 89cad75d557:PATHin the objectui object store.packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx:352-357(PermissionAdvancedFacets): each policy card opens withpol.label, andpol.descriptionsits beneath it, both verbatim. A policy whose values are absent or empty draws nothing. The section is a collapsible that opens closed (FacetSection,useState(!!defaultOpen), and nodefaultOpenis passed). So an author sees the policy count, then both values after one click.PermissionMatrixEditor.tsx:1373(PermissionMatrixEditPage) mounts the facet with no condition, in the main body after theloadingearly return at:1030.services/builtinComponents.tsx:182-187registersEditPage: PermissionMatrixEditPageforpermission. The laterpermissionregistration inanchors.ts:373sets onlyanchors.registry.ts:398-409merges defined keys only, so the EditPage stands.ResourceEditPage.tsx:332-335(MetadataResourceEditPage) returns the custom EditPage for every non-create item. It is the element ofconsole/AppContent.tsx:991(metadata/:type/:name, no active app) and:1091(:type/:nameundermetadata, inside an app). There is a second mount: the Studio Access pillar,studio-design/StudioDesignSurface.tsx:5275, rendersPermissionMatrixEditPageembedded.PermissionMatrixEditor.tsx:512readsclient.layered(type, name), which ispackages/data-objectstack/src/metadata-client.ts:1289,GET /meta/:type/:name/layers. The draft is{ ...effective, ... }at:559-560, sorowLevelSecurityarrives whole.db11afd4967readspol.label0 times andpol.description0 times. At the new pin it reads them 2 and 3 times. The positive controlpol.namereads once in both trees. Non-ancestry off8334f8777todb11afd4967answers exit 1 on this shallow store. Its control leg,5b2ea17570, which is older than the fix and known to be in that history, answers exit 0. The fix is also dated after the old pin.PermissionPreviewstill draws both values, but no production route mounts it forpermissionat this pin. That is the gap the superseded note recorded. It is not cited.Producer, framework side (this branch's HEAD)
packages/rest/src/meta-item-read-gate.ts#createMetaLayeredAnswerjudges each layer throughcreateMetaItemReadGate. That gate has nopermissionarm and falls through toserve(document)(:1464).packages/runtime/src/domains/meta.ts:348(resolveObjectMasker).getMetaItemLayeredinpackages/metadata-protocol/src/protocol.tsserves the stored item raw, folding only object extenders.RowLevelSecurityPolicySchemadeclares both keys (rls.zod.ts:281,:291), so a parse keeps them.examples/app-showcase/src/security/permission-sets.ts#ContributorPermissionSetauthors both on all three of its policies./apps/studio/metadata/permission/showcase_contributor,/apps/setup/metadata/permission/showcase_contributorand the Studio Access pillar.Author-facing effect, measured at the public door
Since the dead-verdict ruling, a
deadrow drawsliveness-dead-propertyby itself, so this flip silences two warnings.pnpm --filter @objectstack/example-showcase validate(os validate) at018e3971dbprints noliveness: deadline. With the RLSlabelrow forced back todeadon disk (throughscripts/ablation-replace.mjs, restored, blob2906221a2b91equal to HEAD), the same command prints⚠ permission 'showcase_contributor': sets rowLevelSecurity.label but this permission property has no runtime effect (liveness: dead).Theplannedwarning onexternalSharingModelappears in both runs, which shows the liveness lint ran each time. A warning is not a refusal, so the accept set is unchanged.Lint pins re-subjected (a file-surface extension, declared)
Two pins in
lint-liveness-properties.test.tsused these rows as their authorabledeadsample. One is "the dead and live-elsewhere verdicts warn on their own"; the other is "the authored dead … keys show the dead default hint, not the ledger note". Their own comment prescribes the move: "If either row changes verdict, re-subject this pin to anotherdeadrow of a type the walk visits".e4281055:Tests 2 failed | 93 passed (95), withexpected undefined to be 'liveness-dead-property'andlabel: expected undefined to be defined.deadrows left in walked types are theviewcontainer's ownnameandlabel. Every otherdeadrow in those types is aretiredKeytombstone. Both parse on a container through the shippedViewSchema, neither has anauthorHint, and thenamenote carries a tracker id, which is the leak the hint pin guards.livekeys beside them (object,list.type,list.data,list.columns) stay silent. The unusedPermissionSetSchemaimport is gone.4d54abd0:Tests 95 passed (95).viewcontainerlabelrow tolivethroughscripts/ablation-replace.mjsin WRAP mode. The anchor went 1 → 0 and the blob02fa2030→3d93a0b8. Result:Tests 2 failed | 93 passed (95), exactly the two re-subjected pins, both onlabel. The tool restored the file: blob equal to HEAD02fa2030andgit diff HEADempty. Predicted direction: red. Observed: red.Tests and gates, at
018e3971dbpnpm --filter @objectstack/spec check:liveness: exit 0.symbol anchors: 875 pointer(s) written path#symbol, 875 naming a symbol the cited file contains, andstate-counts/ is current.@objectstack/lintwhole package (vitest run --maxWorkers=2):Test Files 119 passed (119),Tests 5620 passed (5620).typecheckexit 0.tsc -p tsconfig.test.json --listFilesOnlynames the edited test file once, and it is not intest-typecheck-debt.json.@objectstack/spec,localproject,scripts/liveness/:10 passed,269 passed.repoproject ledger readers (evidence,proof-registry,count-shards-merge):3 passed,92 passed. This is a declared narrowing to the files that read the ledgers; CI runs both projects whole.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 71 commands. All 71 ran, with exit codes captured before any pipe. 67 exited 0 on the first pass. Four exited 3 (PREREQUISITE NOT MET):check-plugin-teardown-shape --self-test(pinned commit absent from the shallow store),check:docs-transcript-drift(needs@objectstack/lintdist),check:dual-build-cjs-loadsandcheck:lean-entry-closure(need built entry points). Each prerequisite was met (the one commit fetched; lint built; a full turbo build, 72 tasks with 71 cache hits) and each re-ran to exit 0.--ranover the final record:71 derived, 71 run, 0 NOT-MEASURED, 0 UNRUN.--no-inline-config --format json: 1 file, 0 errors, 0 warnings.--print-configresolves rules for it, so it is in the linted population. This repo's config never enables type-aware linting (noparserOptions.projectorprojectService), so the diff cannot move any untouched file's verdict. The repo-widepnpm lintis CI's.npm pack --dry-run --ignore-scripts --jsoninpackages/specafter the build listsliveness/permission.json,liveness/state-counts/permission.mdandliveness/README.md. Positive control:dist/index.jsis listed. Negative control:scripts/liveness/check-liveness.mtsis absent. So this is apatchchangeset, andskip-changesetdoes not apply.check:nul-bytesexit 0, and a control-byte scan of the five edited files finds 0 hits.Acceptance notes
viewcontainerlabelstaysdead(ruling A). The landing record asked triage to route its enforce-or-remove question. Closing this card does not carry that routing, so it needs its own carrier if triage has not filed one.packages/spec/liveness/validation.json: the notes oflabel,descriptionandtagseach end with a dated 2026-09-07 sentence: "PermissionPreview only COUNTS its rowLevelSecurity array … which is why permission.rowLevelSecurity.label / .description / .tags stay dead on this same instrument." That was true of the instrument when written, so it is left as history and is outside this card's surface.PermissionPreviewis still registered forpermissionand mounted by no production route at the pin. objectui#11215's own notes record this; it is not a defect here.Generated by Claude Code