Repository navigation
Description
domain:devx#2 · marchtian · session_017ihL2ebbZkr2QAEG1x7UC8
Filing gate: ① a named location. This came up in the #30913 ruling analysis (in the project files at issue-30913/decision-analysis.md §6), which was read against origin/main 5f00374492. ⛔ Not a claim. Triage sets the grade and the lane.
class: b · reach: every app whose people are records (HR employees, service-desk agents, portal customers) and whose managers or leads must see their people's rows. Read from the spec, not measured on a running server.
type: finding
Business reason
In HR, service-desk and portal apps, the person is a row: employee e05, agent a04, customer cus_03. A row-level rule such as "a manager sees the assets of their department" needs current_user.my_department_ids. That works today: the key is declared on employee with userField: 'user' and valueField: 'department'. But a rule that needs which employee I am, for example "I see leave requests whose employee is me" or "a lead sees tickets whose agent reports to me", needs current_user.my_employee_ids. That is the id of the roster row itself, and the shape refuses it.
The only routes left are owner_id on every such row, or a separate (user, employee) junction table. Real apps in these domains do not model either. The scenario runner (#30913, ruled Q2 = C) leaves the manager and portal views to the build's own platform means, so this gap shows up there as AI-built apps that cannot express a common rule.
Where (origin/main 5f00374492)
packages/spec/src/security/rls.zod.tsaround line 760: the membership-key contract.valueField(F) must be "a reference column on X", judged at authoring and publish, andvalueFieldmay not equaluserField.idis not a reference column, so the self row cannot be produced.- ADR-0105 D11 and the Spec design: declarative membership keys for RLS (current_user.<key> from a lookup on records whose user field is the current user) #25234 ruling: the roster was designed as a (user, parent) relation table. The case where the roster row is the person is not discussed.
Wanted (a design question for the spec lane; not decided here)
Decide whether a membership key may take valueField: 'id' (the set of the caller's own roster rows), or whether another documented route covers "the records I am".
- If the answer is no, the RLS guide and the data skill should say so and name the route. Today an author finds out only at publish.
- If yes: the refusal list loses its
idexclusion, and the skill gains the "person is a record" example.
Level: spec (RLS contract). Clause-②: to be decided by the spec lane. Metadata-protocol: to be decided by the spec lane.
Dedupe (open and closed): "membershipKeys valueField" and "membership key roster valueField id" (11 hits) turned up the original design (#25234, closed), its docs and skill cards (#26423, #26297, #26184), catalog cards (#22682, #20297, #15196, #15204) and QA run records. None raises the self-row case. New.
Activity
objectstack-fleet commentedon Nov 7, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p2 ·
domain:spec· decision · 2026-11-07T13:56Z⛔ Not a claim.
valueFieldmust be a reference column, neverid(rls.zod.ts, about L760). So an employee, agent or customer roster cannot give its usercurrent_user.my_employee_ids, and rules like "I see leave requests whoseemployeeis me" have no route exceptowner_idor a junction table. Read from the spec, not measured.domain:spec. It changes what an RLS contract accepts, which is a security-boundary question, so triage puts it to the maintainer rather than leave it to the lane.findingremoved,needs-user-decision.维护者速读
valueField: 'id',也就是返回「我本人对应的那几行记录」(例如员工表里我自己那一行)。userField等于当前用户的那些行的 id,本身不多放任何数据;规则拿它授多少权,和其他键一样由作者决定。改动是去掉现有的id排除,再给技能补一个「人就是一条记录」的示例,并走契约评审。owner_id,或建一张「用户↔员工」关联表)。现在作者要到发布时才会发现这条路走不通。Generated by Claude Code
objectstack-fleet commentedon Nov 7, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Ruling: batch #T962 item 3 · letter A · maintainer 「同意」 2026-11-07T14:47Z
Path: none
Presented in the triage seat's session as batch 1 item 3. The maintainer answered 「同意」 to A: allow a membership key to take
valueField: 'id', the caller's own roster rows. The key yields only rows whoseuserFieldis the caller, so it grants nothing by itself; what a rule grants with it stays the author's choice.Execution:
domain:specremoves theidexclusion inrls.zod.ts(the membership-key contract) and adds tests. Contract review is required, and it confirms thatidis not mistaken for a reference column elsewhere.domain:skillsthen adds the "the person is a record" example to the data skill and the RLS guide.Generated by Claude Code
objectstack-fleet commentedon Nov 7, 2026
domain:spec#4 · marchtian · session_017ihL2eHDXrsKgGAUHq956U
Claim: PM loop round 1 · 2026-11-07T14:53Z
Session:
session_017ihL2bjT2KeFnLTtWu2yPW(card thread; dispatched by seat sessionsession_017ihL2eHDXrsKgGAUHq956U)Account:
marchtianBranch:
claude/project-thread-ph4s7xWorktree: the card thread's own container checkout
Domain:
domain:specSeat:
domain:spec#4File surface:
packages/spec/src/security/rls.zod.ts(membership-key contract prose,valueFieldJSDoc/.describe()),packages/lint/src/validate-membership-key-declarations.tsand the authoring-rule text inpackages/lint/src/authoring-rules.ts,packages/lint/src/validate-rls-predicate-enforceability.ts(what a key's set references whenvalueFieldisid),packages/plugins/plugin-security/src/object-membership-keys.ts(resolver, only if it needs to readid), tests in those packages, regenerated references, one changeset. ⛔skills/**and the RLS guide (thedomain:skillsfollow-up).Container & model:
M,mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable), reason: Clause-② yes widening on the published schema, built at the default tier and reviewed at CONTRACT_REVIEW_TIER by the seat; full Fable review per the maintainer's standing spec rule,effort: defaultClause-②: yes
Metadata-protocol: added
Responsibility: n/a — not a defect card (ruled widening, 6708428544)
Thread-read: 6708428544
Premise: holds
Serial constraints cleared: none (no open claim or PR on these files; disjoint from #31035, #31094, #31099)
objectstack-fleet commentedon Nov 7, 2026
domain:spec#4 · marchtian · session_017ihL2bjT2KeFnLTtWu2yPW
os-dev-report
{ "issue": 31129, "status": "done", "branch": "claude/project-thread-ph4s7x", "pr": "https://github.com/objectstack-ai/objectstack/pull/31174", "head": "bad5140f", "session": "session_017ihL2bjT2KeFnLTtWu2yPW", "premise_still_valid": true, "summary": "A membership key may take valueField: 'id' (the caller's own roster rows). The exclusion lived in lint (refusal 6, id resolved untyped) and the spec prose, not in a Zod refine; both lifted. One exported predicate isRosterOwnId (driver-provisioned id only; an authored field named id is judged by its type) feeds the declaration judge and the refusal-11 target judge, which now reads such a key's target as the declaring object. The resolver projects id once; dedupe, the 1,000 bound, the Layer 0 wall and fail-closed misses are the existing path.", "tests": "at bad5140f: lint 197 files / 8340 tests pass; plugin-security 248 files / 5799 pass, 45 skipped; metadata-core 25/966 pass; spec object-membership-keys 27/27; lint, plugin-security, spec typecheck exit 0. Acceptance (real SecurityPlugin + ObjectQL + SQLite): u_x sees lr_1, lr_4 only; u_y sees lr_2; cross-org employee row dropped. Ablations via ablation-replace.mjs (mutation proven on disk, restored to HEAD blob): primaryKey accept off -> 2 accept tests red; target set off -> refusal cases red; resolver projection reverted -> acceptance red.", "gates": "dispatch-gates --commands at bad5140f: 108 families; 104 exit 0 first pass; 4 exit 3 (prerequisites: teardown self-test fixture commit, client-react dist, i18n build closure, full dist for dual-build-cjs-loads) rerun after building -> all exit 0. --ran: 108 run, 0 NOT-MEASURED. spec check:generated exit 1 (check:docs stale) -> --fix exit 0, committed. eslint --no-inline-config on touched .ts exit 0. PR check-runs: in_progress at report time.", "line_budget": "size M; 12 files incl. 2 regenerated reference pages; ~+420/-30, mostly tests", "deviations": [ "File surface: packages/lint/src/rule-explanations.ts (the two membership-key explanations) was edited although the Claim lists only authoring-rules.ts text; authoring-rules.ts needed no change (it never stated the reference-only rule).", "Merged origin/main once (cbe20ab0) before opening; main has moved 5 commits since, not merged again (no conflict reported)." ], "files_changed": [ ".changeset/31129-membership-key-own-rows.md", "content/docs/references/data/object.mdx", "content/docs/references/security/rls.mdx", "packages/lint/src/rule-explanations.ts", "packages/lint/src/validate-membership-key-declarations.ts", "packages/lint/src/validate-membership-key-declarations.test.ts", "packages/lint/src/validate-rls-predicate-enforceability.ts", "packages/lint/src/validate-rls-predicate-enforceability.membership-keys.test.ts", "packages/plugins/plugin-security/src/object-membership-keys.ts", "packages/plugins/plugin-security/src/object-membership-keys.test.ts", "packages/spec/src/data/object-membership-keys.test.ts", "packages/spec/src/security/rls.zod.ts" ], "open_questions": [], "out_of_scope_findings": [ "carrier: domain:skills follow-up (seat files it) · skills/objectstack-data + RLS guide 'the person is a record' example, per the ruling; PR body line Skills-docs: follow-up owed" ], "security_findings": "none", "dogfood_battery": "n/a — no `!` in the PR title and Clause-② carries the (widening) arm", "mcp_calls": "3 — mcp__github__issue_read (get, get_comments x2); no write tool", "api_writes": "3 relay dispatches, 4 REST writes: pr_create (POST /pulls draft + POST /issues/31174/assignees), PR comment 6709361136 (Fable verdict), this report comment. git push not counted.", "fable_review": "round 1 full diff PASS with should-fix (2 should-fix + 4 nits; both should-fix and 2 nits fixed, 2 nits left with reason); round 2 delta PASS. Verdict comment 6709361136 on PR #31174." }objectstack-fleet commentedon Nov 7, 2026
domain:spec#4 · marchtian · session_017ihL2eHDXrsKgGAUHq956U
Landed ·
domain:spec#4· 2026-11-07T16:53Z3c1eb635a07744541c11dc809c1336e6692d83f0, reviewed headbad5140f28(contract review PASS + ACCEPT on that head)origin/main3c1eb635aState: card closed completed by
Fixes;pm:dispatchedremoved. Skills-docs follow-up: #31176 (domain:skills, Blocked-by #31129, now unblocked).