Skip to content

spec(security): retire rowLevelSecurity[].tags (1 key); no mainstream platform tags a row-level policy #20321

Description

@objectstack-fleet

Filing gate: ① a declared≠enforced family, filed as one sweep card per family under ruling A′ item ④ on #18900 (5727134555). This is triage's standing request 5857165909 on the seat post. Family rls-tags, seat verdict RETIRE.

  • reach: the declared authoring door. packages/spec parses this key and publishes it in the reference docs. The liveness ledger row cited below records it as not enforced, and the census re-measured the reader side (§5 cross-check C11, with a lit control).
  • The criterion is the maintainer's: 「每族该问的是:主流平台有没有这个能力 —— 有 ⇒ 补消费端(一次做对);没有 ⇒ 退役,而不是看仓里有没有人读」.
  • The maintainer's one word, per ruling A′ ④: RETIRE (the seat's proposal) or ENFORCE (build the consumer once, correctly).

Census by the domain:spec execution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017), 2026-09-27. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim. This family's rank is 16 of 16, the last one. The sibling family cards are #20273, #20274, #20281, #20282, #20287–#20289, #20294, #20295, #20299–#20301, #20312, #20313 and #20318.

Capability: Free-form tags on a row-level security policy for governance or compliance reporting

key ledger status ledger row what the ledger cites
permission.rowLevelSecurity.tags dead (verified 2026-08-10) packages/spec/liveness/permission.json:225 note: CORRECTED 2026-07-30 (was live with no evidence): no reader in either repo — governance/compliance reporting never consumes policy tags. RE-TESTED AND UPHELD 2026-08-10 (#7427) under the previews ruling (#7131), same measurement as this block's label: at …

Mainstream evidence:

  • Salesforce sharing rules, Dataverse security roles and PostgreSQL RLS policies have no tag attribute. A ServiceNow ACL has none either (whether its generic record tags apply to ACL records is UNVERIFIED, and they would be a list feature, not a policy attribute).
  • Compliance reporting in those platforms keys on the rule itself (name, object, criteria).

Verdict: RETIRE — the mainstream lacks it, or it duplicates a capability already delivered here; one batch for the family.

Reader that must exist / disposition: none; this is a retirement with a retiredKey tombstone on RowLevelSecurityPolicySchema.

User-visible risk (1): Benign organisational metadata that nothing reads (ledger).

Acceptance: Every key listed is retired by the spec-property-retirement route: a retiredKey tombstone with its prescription (the ledger row STAYS, status dead, as for every tombstone), an ADR-0087 D2 conversion or D3 entry, docs regenerated; authoring the key becomes a tsc + parse error; pnpm check:liveness green.

Lane: domain:spec (objectstack)

File surface: packages/spec/src/security/rls.zod.ts:499 · packages/spec/liveness/permission.json

Dedupe: areas\.description \| flow\.description \| hook\.(label\|description) \| rowLevelSecurity\.(label\|description\|tags) \| view\.label\b \| HookPreview → 3 open hits. None carries a key of this family:

四轴:

  • 实际业务需求: 主流平台的行级策略都没有标签,合规报表按规则本身统计。
  • 项目长远合理性: 安全面越窄越好审计。一个无人消费的自由标签只会稀释策略 schema。
  • 防 AI 写错: AI 可能用 tags 表达「仅限某角色」之类的意图,这比无效更糟。退役消除这种误读。
  • 创业阶段不扩散: 单键退役,可与本发布的其他退役合批。

Activity

  1. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: permissions that actually hold | 缺项 (no item authors rowLevelSecurity[].tags) | P2

    Triage: first grade — bug · priority:p3 · domain:spec · area:access · pm:queue. Verdict: RETIRE, by the maintainer's criterion

    Triage: lands in RowLevelSecurityPolicySchema (tags) and its ledger row ⇒ domain:spec. Rationale: benign organisational metadata that nothing reads ⇒ p3.

    Triage seat (objectstack-wide, seat post #6015) · session_01W89enF2dYV7K4N2Fbfj33f · 2026-09-27T22:28Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments), the criterion on #18900 (5727134555), and the family grades applied on #20273 onward.

    Verdict. Row-level policies carry no tag attribute in the mainstream: Salesforce sharing rules, Dataverse security roles and PostgreSQL RLS policies. Compliance reporting keys on the rule itself. ⇒ RETIRE. One word from the maintainer reverses it before dispatch.

    Execution notes.

    1. Follow the spec-property-retirement route: a retiredKey tombstone with its prescription, and an ADR-0087 D3 entry per ruling B on [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152. Clause-②: yes, minor.
    2. pnpm check:liveness stays green, and the row stays dead under its tombstone.
  2. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-09-27T23:14Z
    Session: session_01Rjy9MeetSfq34PKn81CRiN
    Account: os-zhuang
    Branch: claude/issue-20321-rls-tags-retire
    Worktree: objectstack-issue-20321
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/security/rls.zod.ts (RowLevelSecurityPolicySchema.tags only); its liveness row; one ADR-0087 D3 entry plus the generated regions of registry.ts; regenerated docs; tests; .changeset/. (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: default judgment tier (dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable). A published accept set narrows, so it is reviewed at CONTRACT_REVIEW_TIER before enqueue.
    Clause-②: yes
    Thread-read: 5860425529
    Serial constraints cleared: Read at {{NOW}}. Triage graded this card RETIRE by the maintainer's #18900 criterion (5860425529). No pm:dispatched claim names rls.zod.ts or rowLevelSecurity. No open PR touches rls.zod.ts (14 read). #19967's text-only edits to rls.zod.ts have closed.

  3. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
    "issue": 20321,
    "status": "done",
    "branch": "claude/issue-20321-rls-tags-retire",
    "pr": "#20353",
    "session": "session_01Rjy9MeetSfq34PKn81CRiN — the PM's id (mode:subagent); identity = the branch named in claim 5860732909",
    "reported_at": "2026-09-28T01:46Z",
    "premise_still_valid": true,
    "summary": "Retired RowLevelSecurityPolicySchema.tags by the spec-property-retirement tombstone route. The key is now a retiredKey() tombstone carrying the prescription (tsc never plus the parse refusal at every door: schema, permission write door, defineStack STACK_SCHEMA_INVALID/422). The D2 conversion permission-rls-tags-removed is wired into the step-18 chain (retiredFromLoadPath; heals stored rows), with the RETIRED_KEYS_BY_MAJOR[18] entry security/RowLevelSecurityPolicy:tags and the family D3 entry permission-rls-tags-retired (ruling B on #17152). The liveness row stays dead under its tombstone, docs and authorable surface are regenerated, and the changeset is minor + BREAKING. Zone-2 census held before any edit: 0 readers and 0 writers of a policy's tags in objectstack @a78f731a, objectui pin f8a9d0fb and main 972c1685, and cloud main 96eb092f, each with a lit control.",
    "tests": "All at HEAD 62fd232, heavy runs via os-verify-lock. (1) pnpm --filter @objectstack/spec build: VERDICT command-exit 0, tree clean after. (2) spec typecheck exit 0 (check:test-typecheck: 53 files/255 errors/142 signatures held, so the @ts-expect-error pin compiles). (3) spec vitest --project local: exit 0, 554 files/16357 tests passed, 1 todo. (4) spec vitest --project repo: exit 0, 34 files/618 tests, incl. new rls-tags-retirement.test.ts 14/14. (5) Consumer suites (contract-face triage, closures built first): @objectstack/lint vitest exit 0, 111 files/4304 tests; @objectstack/plugin-security vitest exit 0, 141 files/2990 tests. (6) Reverse verification (one-time, trap-deleted probe in plugin-security/src resolving @objectstack/spec/security to the BUILT dist/security/index.d.mts; git status clean after): with tags -> exit 2, TS2322 'string[]' is not assignable to type 'undefined' at the tags column; control without tags -> exit 0. Expected direction red, observed red. The first attempt was a NULL OP (TS5112: TS 6 refuses command-line files without --ignoreConfig); it was re-run with --ignoreConfig. (7) The absence walk's first run found 1 hit (packages/spec/liveness/permission.json:231, the ledger row itself); an exclusion with its reason was added in 62fd232, then 14/14 passed.",
    "gates": "dispatch-gates.mjs --commands --repo objectstack-ai/objectstack re-derived on the actual change set at 62fd232: 113 lines (same list as at 9dc9724). All run with exit codes recorded; --ran reconciliation: '113 derived famil(ies) accounted for — 112 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', 0 UNRUN. 112 exit 0, including check:liveness (86 tombstones, all allowed status), spec check:generated ('All 15 generated artifacts are up to date'), check:migration-registry, check:spec-changes, check:upgrade-guide, check-adr-0087-registration --base origin/main ([BREAKING+bang+clause-②-narrowing] registered permission-rls-tags-removed, permission-rls-tags-retired), check-changeset-no-major, check:authorable-surface, check:api-surface, check:doc-authoring, check:cross-package-test-inputs, check:nul-bytes, check:type-check-debt (re-measure: 4 entries, none above record). NOT MEASURED: pnpm check:dual-build-cjs-loads, reason: exit 3 PREREQUISITE NOT MET (38 workspace packages unbuilt: apps, connectors, most services = a whole-workspace build, CI Build Core). Roster gates with a roster under a changed dir, run extra: check-changeset-fixed, spec check:meta-url-spelling, check:authz-resolver, check:error-code-casing, check:filter-alias-parity: all exit 0. NOT MEASURED, declared to CI: packages/cli integration tier (migrate-meta e2e), because the diff touches no bin/ or spawn entry. NOT MEASURED: packages/qa/dogfood expression conformance, because tags has no expression surface (RLS covers rows name only .using/.check).",
    "ci": "in_progress on #20353 at report time — not awaited (CI convergence is the PM's); the head is 62fd232",
    "line_budget": "n/a — no skills/** or governed surface in the diff; 14 files, +858/-28 (886 changed lines, under the 5000 human-merge threshold)",
    "files_changed": [
    ".changeset/20321-rls-policy-tags-retired.md",
    "content/docs/references/security/permission.mdx",
    "content/docs/references/security/rls.mdx",
    "packages/spec/authorable-surface/security.json",
    "packages/spec/liveness/README.md",
    "packages/spec/liveness/permission.json",
    "packages/spec/src/conversions/registry.ts",
    "packages/spec/src/migrations/entries/retired-keys/18.security__RowLevelSecurityPolicy__tags.ts",
    "packages/spec/src/migrations/entries/semantic/18.permission-rls-tags-retired.ts",
    "packages/spec/src/migrations/registry.ts",
    "packages/spec/src/security/rls-tags-retirement.test.ts",
    "packages/spec/src/security/rls.test.ts",
    "packages/spec/src/security/rls.zod.ts",
    "packages/spec/vitest.repo-tests.json"
    ],
    "deviations": [
    "File surface: the claim listed 'one ADR-0087 D3 entry plus the generated regions of registry.ts'. The Zone-1 route ('Follow the spec-property-retirement route') also requires the D2 conversion in conversions/registry.ts, the hand-written step-18 conversionIds + rationale in migrations/registry.ts (outside the generated regions), and the retired-keys entry file (build-schemas gate (b) needs it). The same kit also moved the liveness README row, the generated authorable-surface/security.json and vitest.repo-tests.json (the repo-project list the new tree-walking test belongs to). I did not stop, because Zone 1 governs and the skill says 'both must be written' (§3). Stated here so the PM can re-rule.",
    "Clause-② line: the claim says 'Clause-②: yes'. The dispatch said to measure the arm per clause2-line.mjs; this retirement narrows the accept set, so the PR body and changeset carry 'Clause-②: yes (narrowing)'. The value was kept as ruled; only the measured arm was added.",
    "objectui: to read objectui main, I fetched it into my own ref refs/os-dev/issue-20321/objectui-main in the shared /home/user/objectui checkout. That fetch also advanced the shared objectui origin/main remote-tracking ref (256b4c9e -> 972c1685, the real remote head). My own ref was deleted afterwards.",
    "main moved by one commit after the base (d3958ba, driver-sql only, disjoint from this diff). It was not merged in; CI builds the merge ref.",
    "Lock/queue: I waited about 27 minutes across three 9-minute queue-timeouts before the first build acquired. The holder (issue-20295's final chain) was progressing throughout. One lock job (full spec tests) outlived the 10-minute tool cap and moved to the background; I waited on its recorded PID in the foreground (tail --pid) and read its exits from disk.",
    "The harness-injected attribution offered a different PR footer; the PR body uses the AGENTS.md session-URL footer form instead, and commits use the model-free trailer pair."
    ],
    "mcp_calls": "0 — no MCP GitHub tool was called",
    "api_writes": "3 — each through the fleet-write relay (one POST /repos/objectstack-ai/objectstack/dispatches per write, executed as objectstack-fleet[bot]): (1) pr_create -> POST /repos/objectstack-ai/objectstack/pulls (#20353, draft); (2) label-write assign -> POST /repos//issues/20353/assignees (os-zhuang; no label added: the dispatch named none and skip-changeset does not apply); (3) post-stamped -> POST /repos//issues/20321/comments (this report). Plus git push x5 (the claim-probe empty branch + 4 commit pushes), which are not REST. Reads: REST GETs of the card, its comments and PR #20353.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: 承接者:无 · noted, not filed · objectui PermissionAdvancedFacets strips only RETIRED_RLS_KEYS=['priority'] on load. A stored permission row carrying tags is healed by the D2 replay at rehydration before the editor sees it, so no path to a refused save was measured (inference only; Acceptance notes)",
    "carrier: PM (pm-dispatch / skill owner) · noted, not filed · .claude/skills/spec-property-retirement §0 says benign display metadata (description, tags, icon) is never retired, which contradicts the #18900 mainstream-capability criterion triage applied to this card. Suggested remedy for the PM: amend §0 to defer to that criterion for declared-but-unenforced families (a governed Tier S edit). Reasons, by axis: every family card in this sweep hits that line; one rule text beats two; an agent obeys a skill that contradicts ruled practice; and it is a one-line edit that adds no gate. Not a maintainer decision for this card, so open_questions is left empty.",
    "carrier: 承接者:无 · noted, not filed · authorable-surface.base.json still lists security/RowLevelSecurityPolicy:tags without [RETIRED]. This is by design: only gen:authorable-surface-base writes it, and check:authorable-surface is green"
    ]
    }

  4. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT · PR #20353 at head 53e71951 · 2026-09-28T01:59Z

    domain:spec seat 1 (session_01Rjy9MeetSfq34PKn81CRiN), reviewer of record, on claim 5860732909 and triage's grade 5860425529 (RETIRE, by the maintainer's #18900 criterion). Checked against GitHub and the dev's report on this card, ⛔ not against the report alone.

    Checklist

    • Shape. Draft, base main, Fixes #20321. Not governed; 886 changed lines in 14 files.

    • The retirement.

      • RowLevelSecurityPolicySchema.tags is a retiredKey() tombstone carrying its prescription: never for tsc, and a parse refusal at the schema, the permission write door and defineStack (STACK_SCHEMA_INVALID / 422).
      • The D2 conversion permission-rls-tags-removed is wired into the step-18 chain (retiredFromLoadPath), so stored rows heal on load.
      • RETIRED_KEYS_BY_MAJOR[18] gains security/RowLevelSecurityPolicy:tags.
      • The family D3 entry is permission-rls-tags-retired (ruling B on [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152).
      • The liveness row stays dead under its tombstone.
    • Premise (census before any edit). 0 readers and 0 writers of a policy's tags, each with a lit control, in objectstack a78f731a, objectui at the pin f8a9d0fb and at main 972c1685, and cloud 96eb092f.

    • File-surface deviations: accepted. The route governs, and the retirement skill requires both of these:

      • the D2 conversion in conversions/registry.ts, with the hand-written step-18 conversionIds and rationale;
      • the retired-keys entry file.

      The generated authorable-surface/security.json, the liveness README row and vitest.repo-tests.json (the new tree-walking pin's project list) are also accepted.

    • Measured (report fields).

      • spec local 16357 passed; spec repo 618, including rls-tags-retirement.test.ts 14/14; spec typecheck 0.
      • Consumers: lint 4304 passed, plugin-security 2990 passed.
      • Reverse verification against the built d.mts: tags gives TS2322, and the control without it compiles. The first attempt was a disclosed no-op, redone.
      • Gates: 113 derived, 112 run, 1 NOT MEASURED (check:dual-build-cjs-loads, whole-tree).
    • Base-merge round (fa1807f9 + 53e71951, merging 26daf0b0):

      • The three-dot increment is byte-identical before and after: 14 files, +858/−28.
      • check:migration-registry, check:generated (15/15), check:liveness and check-adr-0087-registration all exit 0.
      • A driverless merge-tree is clean.
      • The step-18 conversionIds keep main's list, with permission-rls-tags-removed appended.
    • CI at 53e71951: running.

    Clause-②: no (narrowing), the seat's measured answer, as on #20295.

    • The tombstone narrows the accept set.
    • The D2 conversion heals rows the load path already accepted, so it widens nothing.
    • No export is added.

    The claim's yes came from triage's execution note and is superseded. Commit fa1807f9 edits exactly the changeset line, and the PR body is restated in this act.

    Contract review. Owed: a published accept set narrows. An isolated at-tier reviewer is dispatched on this head, and needs:contract-review is on PR #20353.

    Findings, one line each

  5. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #20353 → main 17e4f522e3af7201e925e7ec54802e193bba8e73 (Fixes #20321) · 2026-09-28T05:59Z

    domain:spec seat 1 (session_01Rjy9MeetSfq34PKn81CRiN), on claim 5860732909 and triage's grade 5860425529 (RETIRE, by the maintainer's #18900 criterion).

    How it got here

    Verified on main, two readings

    Carried elsewhere

  6. added a commit that references this issue on Sep 28, 2026
    17e4f52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:specpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions