Repository navigation
spec(security): retire rowLevelSecurity[].tags (1 key); no mainstream platform tags a row-level policy #20321
Description
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/specparses 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:
- lint:
liveness-dead-propertyandliveness-live-elsewhere-propertycannot fire on 17.3.0 — 90dead+ 1live-elsewhereledger rows and not one setsauthorWarn#16094 — lint axis (authorWarn opt-in for dead rows) plus a list.tabs re-derivation already answered by spec: re-derive the liveness ledger before itsdead/live-elsewhereverdicts start warning authors —view.jsonlist.tabsre-read plus a sampled audit of the 90deadrows (ledger half of #16094) #16362 (closed); not an enforce-or-remove carrier - objectui#7928 — a different
label: NamedListView (listViews entry) label, not the view container label - objectui#7924 — a different
label: NamedListView (listViews entry) label, not the view container label
四轴:
- 实际业务需求: 主流平台的行级策略都没有标签,合规报表按规则本身统计。
- 项目长远合理性: 安全面越窄越好审计。一个无人消费的自由标签只会稀释策略 schema。
- 防 AI 写错: AI 可能用 tags 表达「仅限某角色」之类的意图,这比无效更糟。退役消除这种误读。
- 创业阶段不扩散: 单键退役,可与本发布的其他退役合批。
Activity
objectstack-fleet commentedon Sep 27, 2026
Path: permissions that actually hold | 缺项 (no item authors
rowLevelSecurity[].tags) | P2Triage: first grade —
bug·priority:p3·domain:spec·area:access·pm:queue. Verdict: RETIRE, by the maintainer's criterionTriage: 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.
spec-property-retirementroute: aretiredKeytombstone with its prescription, and an ADR-0087 D3 entry per ruling B on [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152.Clause-②: yes,minor.pnpm check:livenessstays green, and the row staysdeadunder its tombstone.objectstack-fleet commentedon Sep 27, 2026
Claim: PM loop round 1 · 2026-09-27T23:14Z
Session:
session_01Rjy9MeetSfq34PKn81CRiNAccount:
os-zhuangBranch:
claude/issue-20321-rls-tags-retireWorktree:
objectstack-issue-20321Domain:
domain:specSeat:
domain:spec#1File surface:
packages/spec/src/security/rls.zod.ts(RowLevelSecurityPolicySchema.tagsonly); its liveness row; one ADR-0087 D3 entry plus the generated regions ofregistry.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 atCONTRACT_REVIEW_TIERbefore 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.objectstack-fleet commentedon Sep 28, 2026
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"
]
}
objectstack-fleet commentedon Sep 28, 2026
Review: ACCEPT · PR #20353 at head
53e71951· 2026-09-28T01:59Zdomain:specseat 1 (session_01Rjy9MeetSfq34PKn81CRiN), reviewer of record, on claim5860732909and triage's grade5860425529(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.tagsis aretiredKey()tombstone carrying its prescription:neverfor tsc, and a parse refusal at the schema, the permission write door anddefineStack(STACK_SCHEMA_INVALID/ 422).permission-rls-tags-removedis wired into the step-18 chain (retiredFromLoadPath), so stored rows heal on load.RETIRED_KEYS_BY_MAJOR[18]gainssecurity/RowLevelSecurityPolicy:tags.permission-rls-tags-retired(ruling B on [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152).deadunder its tombstone.Premise (census before any edit). 0 readers and 0 writers of a policy's
tags, each with a lit control, in objectstacka78f731a, objectui at the pinf8a9d0fband at main972c1685, and cloud96eb092f.File-surface deviations: accepted. The route governs, and the retirement skill requires both of these:
conversions/registry.ts, with the hand-written step-18conversionIdsand rationale;The generated
authorable-surface/security.json, the liveness README row andvitest.repo-tests.json(the new tree-walking pin's project list) are also accepted.Measured (report fields).
rls-tags-retirement.test.ts14/14; spec typecheck 0.d.mts:tagsgives TS2322, and the control without it compiles. The first attempt was a disclosed no-op, redone.check:dual-build-cjs-loads, whole-tree).Base-merge round (
fa1807f9+53e71951, merging26daf0b0):check:migration-registry,check:generated(15/15),check:livenessandcheck-adr-0087-registrationall exit 0.conversionIdskeepmain's list, withpermission-rls-tags-removedappended.CI at
53e71951: running.Clause-②:
no (narrowing), the seat's measured answer, as on #20295.The claim's
yescame from triage's execution note and is superseded. Commitfa1807f9edits 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-reviewis on PR #20353.Findings, one line each
description/tags/icon) is never retired, which contradicts the [Decision] Route declared≠enforced work by the SEAM, not the layer — aSeam:line on filing, vertical dispatch by default in the spec lane, automatic parent + sub-issues for spec↔objectui seams, Journey as a filter, bulk retirement per spec family, Console Pin Gate back to required (the maintainer's 「同意」 on the five-line batch, 2026-09-18) #18900 ④ criterion triage applied here → filed [finding] spec-property-retirement §0 says benign display metadata (description,tags,icon) is never retired, which contradicts the maintainer's #18900 ④ criterion (retire by mainstream capability) that triage applies to every family card #20354 (skills lane, bare).PermissionAdvancedFacetsstrips onlyRETIRED_RLS_KEYS = ['priority']on load. A storedtagsis healed by the D2 replay before the editor sees it, so no refused-save path was measured → Acceptance notes.authorable-surface.base.jsonstill lists the key without[RETIRED]. This is by design: onlygen:authorable-surface-basewrites it, andcheck:authorable-surfaceis green → Acceptance notes.objectstack-fleet commentedon Sep 28, 2026
Landed: PR #20353 →
main17e4f522e3af7201e925e7ec54802e193bba8e73(Fixes #20321) · 2026-09-28T05:59Zdomain:specseat 1 (session_01Rjy9MeetSfq34PKn81CRiN), on claim5860732909and triage's grade5860425529(RETIRE, by the maintainer's #18900 criterion).How it got here
5861966895at53e71951. Clause-② was answeredno (narrowing)as measured.5862589446at53e71951, which re-measured:permission-rls-tags-removedhealing stored rows and nothing else;tagsnode;da5d4016mergedmainthroughos-regen-merge.sh. Seat verification5863349338found the three-dot increment identical and adopted5862589446.mainplus PR feat(spec)!: retire currencyConfig.precision — a currency's decimal places are its currency's (ADR-0049) #20251 conflicted in the shared step-18 lists (conversions/registry.ts,migrations/registry.ts).8acd2285mergedeee09742. Seat verification5863891489:migrations/registry.tsmoved, onto feat(spec)!: retire currencyConfig.precision — a currency's decimal places are its currency's (ADR-0049) #20251's sentence;8acd2285: 33 success and 2 expected skips (check-expected-skipsexit 0). Not governed; 886 changed lines. The driverless merge-tree was clean ontomainab946560and ontomain+ PR fix(spec): refuse an auto-launched flow whose stack declares triggers without automation (#20332) #20365.Verified on
main, two readings17e4f522's first parent isdb74b169(PR fix(objectql)!: a number field refuses an array, a boolean or an object with invalid_number (#20309) #20370, which was ahead of it in the queue), and no queue branch for feat(spec)!: retire rowLevelSecurity[].tags — no mainstream platform tags a row-level policy (ADR-0049) #20353 remains.eee09742...8acd2285). The increment's added and removed lines hash identically, both overall and per file.packages/spec/src/security/rls.zod.ts:572readstags: retiredKey(RLS_POLICY_TAGS_RETIRED),at17e4f522, with notags: retiredKeyat its parent;permission-rls-tags-removedis inconversions/registry.tsand in the step-18conversionIdsofmigrations/registry.tsat17e4f522, with 0 hits underpackages/spec/src/at its parent.Fixes.Carried elsewhere
description,tags,icon) is never retired, which contradicts the maintainer's #18900 ④ criterion (retire by mainstream capability) that triage applies to every family card #20354 (filed from the ACCEPT, since closed): the retirement skill's §0 said benign display metadata is never retired.priorityon load, but a storedtagsis healed by the D2 replay before the editor sees it;authorable-surface.base.jsonlists the key without[RETIRED]by design (only its generator writes it).feat(spec)!: retire rowLevelSecurity[].tags — no mainstream platform …
fix(spec): the protocol 16 → 17 conversion summaries and three descri…
fix(spec): the protocol 17 → 18 conversion summaries state each decis…
test(spec): security, ai, identity, integration, migrations, marketpl…