Skip to content

fix(spec): grade permission rowLevelSecurity label/description live — Studio's permission editor shows them - #21607

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20299-rls-policy-rows-live
Oct 3, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20299-rls-policy-rows-live

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #20299

Clause-②: no

What changed

permission.rowLevelSecurity.label and permission.rowLevelSecurity.description move dead → live in 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 (merge f8334f8777). The rows cite that reader at the .objectui-sha pin 89cad75d557, measured in the pinned tree and never on objectui main.

This is the last remainder the landing record 5909245536 left on this card. The view container label row stays dead under ruling A (5907340127, Q1) and is not touched here.

Files (5, +59 / −51):

  • packages/spec/liveness/permission.json: the two rows. Each carries evidence (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 by gen:liveness-counts, never hand-edited. permission is now 38 live / 4 dead (was 36 / 6).
  • packages/spec/liveness/README.md: the permission Notes 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 two objects.allowRestore / allowPurge tombstones). The check:liveness failure 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/spec patch. The ledger ships in the package (measured below).

The reading, at the pin

The pin on this branch's base 6c5697dff is .objectui-sha = 89cad75d55702cc4f267bead5bf267de575d5842. git merge-base --is-ancestor f8334f8777 89cad75d557 answers exit 0, which proves itself. Every line below is git show 89cad75d557:PATH in the objectui object store.

  • Reader. packages/app-shell/src/views/metadata-admin/PermissionAdvancedFacets.tsx:352-357 (PermissionAdvancedFacets): each policy card opens with pol.label, and pol.description sits 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 no defaultOpen is passed). So an author sees the policy count, then both values after one click.
  • Mount. PermissionMatrixEditor.tsx:1373 (PermissionMatrixEditPage) mounts the facet with no condition, in the main body after the loading early return at :1030.
  • Registration. services/builtinComponents.tsx:182-187 registers EditPage: PermissionMatrixEditPage for permission. The later permission registration in anchors.ts:373 sets only anchors. registry.ts:398-409 merges defined keys only, so the EditPage stands.
  • Dispatch and route. ResourceEditPage.tsx:332-335 (MetadataResourceEditPage) returns the custom EditPage for every non-create item. It is the element of console/AppContent.tsx:991 (metadata/:type/:name, no active app) and :1091 (:type/:name under metadata, inside an app). There is a second mount: the Studio Access pillar, studio-design/StudioDesignSurface.tsx:5275, renders PermissionMatrixEditPage embedded.
  • Data. PermissionMatrixEditor.tsx:512 reads client.layered(type, name), which is packages/data-objectstack/src/metadata-client.ts:1289, GET /meta/:type/:name/layers. The draft is { ...effective, ... } at :559-560, so rowLevelSecurity arrives whole.
  • Control. The same file at the superseded pin db11afd4967 reads pol.label 0 times and pol.description 0 times. At the new pin it reads them 2 and 3 times. The positive control pol.name reads once in both trees. Non-ancestry of f8334f8777 to db11afd4967 answers 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.
  • Not used. PermissionPreview still draws both values, but no production route mounts it for permission at 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#createMetaLayeredAnswer judges each layer through createMetaItemReadGate. That gate has no permission arm and falls through to serve(document) (:1464).
  • The ADR-0106 mask is not applicable to a non-object type: packages/runtime/src/domains/meta.ts:348 (resolveObjectMasker).
  • getMetaItemLayered in packages/metadata-protocol/src/protocol.ts serves the stored item raw, folding only object extenders.
  • RowLevelSecurityPolicySchema declares both keys (rls.zod.ts:281, :291), so a parse keeps them. examples/app-showcase/src/security/permission-sets.ts#ContributorPermissionSet authors both on all three of its policies.
  • This run did not boot Studio. The reading is static, closed by hand. objectui#11215's own booted Playwright probe, against the objectstack showcase, found both values on /apps/studio/metadata/permission/showcase_contributor, /apps/setup/metadata/permission/showcase_contributor and the Studio Access pillar.

Author-facing effect, measured at the public door

Since the dead-verdict ruling, a dead row draws liveness-dead-property by itself, so this flip silences two warnings. pnpm --filter @objectstack/example-showcase validate (os validate) at 018e3971db prints no liveness: dead line. With the RLS label row forced back to dead on disk (through scripts/ablation-replace.mjs, restored, blob 2906221a2b91 equal to HEAD), the same command prints ⚠ permission 'showcase_contributor': sets rowLevelSecurity.label but this permission property has no runtime effect (liveness: dead). The planned warning on externalSharingModel appears 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.ts used these rows as their authorable dead sample. 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 another dead row of a type the walk visits".

  • After the flip and before the test edit, at e4281055: Tests 2 failed | 93 passed (95), with expected undefined to be 'liveness-dead-property' and label: expected undefined to be defined.
  • The only authorable dead rows left in walked types are the view container's own name and label. Every other dead row in those types is a retiredKey tombstone. Both parse on a container through the shipped ViewSchema, neither has an authorHint, and the name note carries a tracker id, which is the leak the hint pin guards.
  • The pins now use them. The collection walk still has to reach past index 0, and the live keys beside them (object, list.type, list.data, list.columns) stay silent. The unused PermissionSetSchema import is gone.
  • At 4d54abd0: Tests 95 passed (95).
  • Reverse verification, one-shot. I forced the view container label row to live through scripts/ablation-replace.mjs in WRAP mode. The anchor went 1 → 0 and the blob 02fa2030 → 3d93a0b8. Result: Tests 2 failed | 93 passed (95), exactly the two re-subjected pins, both on label. The tool restored the file: blob equal to HEAD 02fa2030 and git diff HEAD empty. Predicted direction: red. Observed: red.

Tests and gates, at 018e3971db

  • pnpm --filter @objectstack/spec check:liveness: exit 0. symbol anchors: 875 pointer(s) written path#symbol, 875 naming a symbol the cited file contains, and state-counts/ is current.
  • @objectstack/lint whole package (vitest run --maxWorkers=2): Test Files 119 passed (119), Tests 5620 passed (5620). typecheck exit 0. tsc -p tsconfig.test.json --listFilesOnly names the edited test file once, and it is not in test-typecheck-debt.json.
  • @objectstack/spec, local project, scripts/liveness/: 10 passed, 269 passed. repo project 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/objectstack derived 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/lint dist), check:dual-build-cjs-loads and check: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. --ran over the final record: 71 derived, 71 run, 0 NOT-MEASURED, 0 UNRUN.
  • eslint on the one changed TS file, --no-inline-config --format json: 1 file, 0 errors, 0 warnings. --print-config resolves rules for it, so it is in the linted population. This repo's config never enables type-aware linting (no parserOptions.project or projectService), so the diff cannot move any untouched file's verdict. The repo-wide pnpm lint is CI's.
  • Ships, measured. npm pack --dry-run --ignore-scripts --json in packages/spec after the build lists liveness/permission.json, liveness/state-counts/permission.md and liveness/README.md. Positive control: dist/index.js is listed. Negative control: scripts/liveness/check-liveness.mts is absent. So this is a patch changeset, and skip-changeset does not apply.
  • check:nul-bytes exit 0, and a control-byte scan of the five edited files finds 0 hits.

Acceptance notes

  • The view container label stays dead (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 of label, description and tags each 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.
  • PermissionPreview is still registered for permission and mounted by no production route at the pin. objectui#11215's own notes record this; it is not a defect here.
  • The facet is collapsed by default, so both values appear after one click on the Row-Level Security trigger. That is a UI choice, not a reachability gap.
  • Attribution footer. The body ends with the AGENTS.md session-URL form rather than the harness's attribution reminder; the repo's instruction file outranks the reminder.

Generated by Claude Code

claude added 4 commits October 3, 2026 15:23
…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>
@github-actions github-actions Bot added the size/m label Oct 3, 2026
@github-actions

github-actions Bot commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 3 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/permission.json, packages/spec/liveness/state-counts/permission.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/permission.json, packages/spec/liveness/state-counts/permission.md) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 138 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json b610eabf721672ab621dd1f45e2d1dafbf76a740 → packageMentionDocs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Oct 3, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 3, 2026 16:48
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 3, 2026 16:48
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 3, 2026
Merged via the queue into main with commit 0721848 Oct 3, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20299-rls-policy-rows-live branch October 3, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

studio: show the authored label / description on flows, hooks, app areas, RLS policies and the view container (7 keys)

2 participants