Repository navigation
sharing: a share or sharing rule to an external member is refused as inert on a public_read_write or model-less system object, though the external OWD floor would consult it #25549
Description
Ruled: 6377844010 · batch #9 item 2 · letter A · 2026-10-24T00:43:45Z
Filing gate ②: a maintainer decision. It follows the ruling of record 6355206139 (letter B) on #25233 and is raised by the stage-2 contract review 6375883728 on #25541 (boundary flag "out of scope 1"). ⛔ Not a claim.
Reader who acts: the domain:spec lane (seat 1). After the ruling, one objectstack PR touches plugin-sharing and @objectstack/lint.
维护者速读
外部成员(持 external 角色的供应商门户用户)现在默认只能看到自己的记录,以及别人明确分享给他的记录。问题在于:在"全员可读写"(public_read_write)的对象上,以及没有声明共享模型的系统对象上,管理员根本建不出这条分享。系统会以"该对象不受记录共享约束,分享不会生效"为由拒绝,而且手动分享和共享规则都会被拒。结果是,管理员想把某一个项目、某一张单据单独给某个供应商看,没有办法做到,只能另写一条行级规则。这和裁决 B 的说法"分享与共享规则可以到达外部成员"不一致。
- A:对外部收件人放行这条分享(及共享规则)。对内部收件人照旧拒绝,因为它对内部成员确实不生效。
- B:维持拒绝。在文档里写明:在这类对象上,外部成员只能通过权限集的行级规则获得访问。
- C:在这类对象上取消外部成员的"只见自己"下限。
推荐 A。请回一个字母:A / B / C。
The problem, in one sentence
An administrator cannot share a single record of a "read-and-write for everyone" object with a portal supplier: the share is refused as "would never take effect", although for that supplier it is the only thing that would.
Background
#25541 (stage 2 of ruling B) floors an external principal's org-wide default to owner-only on every object (owdForAudience, @objectstack/core). The floor lifts only when a held grant's RLS speaks for the object. Under that floor, buildReadFilter, buildWriteFilter, checkEdit and checkDelete DO consult sys_record_share rows for an external on objects whose effective model is public. A controlled_by_parent detail is not floored: its rows follow the master (fix 225d5bc on #25541).
The grant pre-flight still decides, caller-free, that a share row on such an object "would never be consulted", and refuses it.
Governing text
- ADR-0111 D7 item 2: "
grant()validates the object is in an enforcing posture —effectiveSharingModelisprivateorreadand the object has anowner_idfield. Otherwise → 422 (SHARING_NOT_ENABLED): a share on a public / bypass / owner-less object enforces nothing.controlled_by_parentis refused explicitly (… a detail record is not independently shareable; its access follows the master)." - plugin-sharing:
grant()skips the ADR-0111 D7 inert-grant guard entirely for SYSTEM callers, so the sharing-rule evaluator can still materialise rows no gate consults #8207 (ininertGrantReason's docblock): "Whether a share row can ever be read is a property of the OBJECT: the gates that would consult it … never see the granter at all." - Ruling 6355206139, letter B: "the rung admits explicit grants only — shares, including share links; sharing rules; and position and permission-set grants with their RLS evaluated."
Protocol statement
No spec key, value or metadata type changes under any option: Metadata-protocol: none. Option A widens the REST grant door's acceptance, and the rule materialiser's, for one recipient population. Option C changes what the external rung means.
Premise, with the re-check
The refusal holds on main after #25541 lands:
grep -n "effectiveSharingModel(schema) === 'public' || !hasOwnerField(schema)" packages/plugins/plugin-sharing/src/sharing-service.ts # inertGrantReason, the public arm
grep -n "this.assertNotInertGrant" packages/plugins/plugin-sharing/src/sharing-service.ts # run for every caller, system includedassertNotInertGrant runs for system callers too, so sharing-rule materialisation on these objects is refused as well. The same verdict is mirrored at author time in packages/lint/src/validate-sharing-rule-enforceability.ts.
Scope, measured by the contract review: public_read_write objects, and model-less sys_* / isSystem objects (effective public). public_read objects are NOT refused (their effective model is read). controlled_by_parent is refused by design; its share goes to the master, and an external reads the lines of a master shared to it (pinned on a showcase boot in #25541). Existing rows are honoured; minting new ones fails, so the failure is closed and loud (422).
The specific question
Must D7's "would this row ever be consulted" verdict take the RECIPIENT's population into account, now that one population (external) is floored on public objects?
Options
| Option | What it does | What the customer sees | Effort |
|---|---|---|---|
| A: admit for external recipients | inertGrantReason becomes a verdict over (object, recipient): on an effective-public object a user recipient whose membership in the object's organization is external is admitted; any other recipient is refused as today. The rule materialiser mints rows only for the external members a rule expands to. The lint mirror says "effective for external recipients only" instead of refusing. |
An admin shares one project with one supplier and it works, through the UI share dialog or a sharing rule. Internal recipients still get the honest 422. | spec lane · 1 PR. Precedent: #25541's OWD seam (sharing-service.ts about +22 source lines, external-owd-floor.test.ts +142). The materialiser and the lint edits have no measured precedent. |
| B: keep the refusal, document the RLS route | Docs only: sharing-rules.mdx and the Tier H ADR text say that on these objects an external is reached through a permission set's row policy. | An admin who wants one supplier to see one record must author a permission set with a row policy (for example id in [...] or supplier == current_user.<key>) and bind it. That is a developer task, not a share click. |
spec lane · rides the Tier H docs PR; no code. |
| C: lift the floor on public objects | owdForAudience stops flooring public models for externals. |
Every supplier reads and writes every row of every "public" object in the buyer's organization: other suppliers' projects included. | spec lane · 1 small PR (reverts part of #25541). |
Business meaning
- A: like Salesforce, where a portal user sees a public-for-employees object as Private, and an admin can still hand them a record by manual sharing or a sharing rule. The supplier gets exactly the records it is handed.
- B: a supplier can still be given access, but only by an engineer writing a rule. The everyday "share this with them" action does not exist on these objects.
- C: the opposite of ruling B. Making an object open to staff would also open it to every supplier.
Recommendation: A (confidence: medium)
- The end state, judged on ① alone: two years out, an external portal user is reached by every channel the ruling names, on every object, with one rule. Salesforce works this way (an external OWD that defaults to Private, manual shares and sharing rules to portal users), and so do Power Pages (table permissions plus record sharing).
- Why A keeps D7's principle: D7's real premise is that "the gates never see the granter". A still holds it. The verdict stays independent of who asks, and depends only on facts the gate reads: the object, and the recipient's population. Only the "public ⇒ always inert" shortcut changes.
- Why not B: it leaves a channel the ruling admits open on half the objects and closed on the other half. The deciding property is the object's OWD, which an admin sets for staff, not for suppliers.
- Why not C: it contradicts the ruling.
- Confidence gap:
- A sharing rule whose target expands to both staff and suppliers would materialise rows for the suppliers only. That is correct, but the admin is told nothing about the staff half. A needs a one-line note in the rule's materialisation report, and I have not measured how that report surfaces today.
- The recipient's population is judged in the object's organization. A user who is external in one organization and a member in another is decided per organization, which the stage-1 projection already does. Whether the grant path has the organization at hand on every caller (REST and the rule evaluator) is not yet read.
- Self-check: judged on ① alone, A. Do ②③④ flip it? No. ② (pull is low today: hotsrm uses the grant-RLS shape, and the role has not shipped) only argues for landing after the Tier H docs PR, not for another letter. ③ favours A over B: A admits a precise case, and B keeps a loud refusal that admins will work around with broader RLS. ④: A adds no key.
After the ruling
If A, the spec lane opens one PR:
- The verdict.
inertGrantReason(object, recipient?): on an effective-publicobject, auserrecipient is admitted when itssys_member.rolein the object's organization is exactlyexternal. That is the same population test as the stage-1 door. Every other recipient keeps the 422. - Rule materialisation. The rule evaluator applies the same per-recipient verdict.
- The author-time lint.
validate-sharing-rule-enforceabilityreports "effective for external recipients only" as advice, not a refusal. - Docs.
sharing-rules.mdxgains one sentence. - Tests. A plugin-sharing unit test, and a showcase dogfood case: share one
public_read_writerecord with an external, who reads that record only. An internal recipient still gets 422. - The ADR record. One dated line on ADR-0111 D7 rides the next Tier H docs PR.
If B, the line rides the Tier H docs PR for #25233, nothing else.
If C, the spec lane reverts the public arm of owdForAudience and amends the ruling's ADR text to match.
os-decision-facets
- ① 项目长远合理性:A 收敛特例,"分享能到达外部成员"在所有对象上一致成立;B 留下一个按对象 OWD 区分的例外;C 推翻裁决。
- ② 实际业务拉动:今天拉动低。external 角色尚未发版,hotsrm 走的是权限集行级规则(已通)。首个想"把这一条记录分享给某供应商"的门户应用会撞上。
- ③ 防 AI 犯错:A 的放行是闭合判定(收件人恰为
external),其余照旧响亮 422;B 会诱使管理员写更宽的行级规则来绕;C 是静默泄露。 - ④ 创业阶段不扩散:A 不新增任何键,只改一个判定;B 只改文档;C 改的是裁决本身。
Prior rulings read: inert grant, SHARING_NOT_ENABLED, external share, assertNotInertGrant → 15 hits (search below) + 3 ADR files naming inert grants; ADR-0111 D7; ADR-0055 (controlled_by_parent follows its master); thread: #8207 (verdict is caller-free), 6159300530 on #23268 (external OWD retired; external visibility is the EXTERNAL rung), 6355206139 on #25233 (channel list), contract review 6375883728 on #25541.
Duplicate search:
repo:objectstack-ai/objectstack inert share external→ 0 hits.repo:objectstack-ai/objectstack SHARING_NOT_ENABLED external→ 15 hits (closed and open). None of them asks whether an inert verdict depends on the recipient. The nearest are security:externalSharingModelis declared and authored but has no runtime reader — an object'sexternalSharingModel: privatechanges no answer for any principal (ADR-0090 D11 declared-not-enforced) #23268 (closed:externalSharingModelhad no reader, retired) and plugin-sharing: the record-share grant refuses agrouprecipient by naming sharing rules as its route, and the rule vocabulary has nogroup#23325 (closed: the grant door refusing agrouprecipient).
Related: #25233 (closes with #25541), #25541, #25234 (the grant-RLS channel this does not touch), ADR-0111, ADR-0055.
Not filed (measured): better-auth 1.7.12 /organization/list-team-members (routes/crud-team.mjs, listTeamMembers) requires the caller to be on the team (adapter.findTeamMember({ userId: session.user.id, teamId }), else USER_IS_NOT_A_MEMBER_OF_THE_TEAM). An external therefore reads only the roster of a team it was placed on, and no category ① leak exists.
Activity
objectstack-fleet commentedon Oct 24, 2026
Director increment · batch #388 item 5 · 2026-10-24T00:29Z
Director seat, summon #49 (session
session_0162wFwDdLFGhaSvDdSnZ9Sx, accountos-zhuang; written asobjectstack-fleet[bot]through the relay; ledger #12708). ⛔ Not a ruling.Premise re-check (objectstack
origin/main45f996231ea4): the card's two re-check commands both hit —packages/plugins/plugin-sharing/src/sharing-service.ts:1362is thepublicarm ofinertGrantReason(object: string)(:1351, one argument: the verdict sees neither caller nor recipient), andthis.assertNotInertGrantruns at:1445and:1536(the grant and the rule paths). The lint mirrorpackages/lint/src/validate-sharing-rule-enforceability.tsis present.owdForAudienceis inpackages/core/src/security(external-owd-floor.test.ts:47–54: an external on apublicmodel readsprivate). PR #25541 merged at 2026-10-23T23:59:56Z, so the floor and the refusal coexist on main now. ADR-0111 D7 is atdocs/adr/0111-…:134–139. Prior-rulings grepSHARING_NOT_ENABLED|inert grant|inertGrantover AGENTS.md, docs/adr, packages/spec/src → 6 hits (ADR-0057:128; ADR-0111:134, :139, :164; error-code-ledger.zod.ts:636; contracts/sharing-service.ts:401); none decides whether the verdict may read the recipient's population.Facets, agreed or differed: all four agreed. Mainstream check added on ①: Salesforce gives portal users an external org-wide default (Private) and still lets an admin hand them one record by manual share or a criteria sharing rule on an object whose internal default is Public Read/Write; Power Pages does it with table permissions plus record sharing. A is that end state: every channel the ruling names reaches the external rung on every object, under one rule. The filer's two confidence gaps (how a rule's materialisation report surfaces the skipped staff half; whether the REST grant door and the rule evaluator both have the object's organization at hand) were not read by this seat either — unverified — so they become acceptance items for the dev: one report line per rule when the staff half is skipped, and both paths resolve the recipient's role in the object's organization or refuse with a named reason.
Seat's recommendation: A. Workload of the letter:
domain:spec· 1 PR; precedent #25541's OWD seam (sharing-service.ts about +22 source lines, external-owd-floor.test.ts +142); the materialiser and lint parts have no measured precedent. Lands after the Tier H docs PR for #25233 carries the dated D7 line.os-decision-facets
Prior rulings read: SHARING_NOT_ENABLED, inert grant, inertGrant → 6 hits; ADR-0111 D7, ADR-0055; thread: none on this card (filing cites 6355206139 on Security: an external organisation role whose members do not see the member directory and reach data only through explicit grants (portal users on hosted environments) #25233 and 6375883728 on feat(security): the external organization role is the EXTERNAL principal — owner-only org-wide default, explicit grants only #25541)
维护者速读
席位意见:同意 A(对 external 收件人放行这条分享和共享规则,对内部收件人照旧拒绝)。主流平台也是这样:供应商门户用户默认只见自己的,管理员仍能单条分享给他。两处立卡者没查清的(规则物化报告怎么提示跳过了员工那半、授权路径是否都拿得到组织)本席也未读,列为开发验收项。回
25549 A即可。Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Ruling: batch #388 item 5 · letter A · maintainer 「同意」 2026-10-24T00:47Z
Director seat, summon #49 (session
session_0162wFwDdLFGhaSvDdSnZ9Sx, accountos-zhuang, inGOVERNED_APPROVERS— this seat submits no review and clicks nothing; written asobjectstack-fleet[bot]through the relay; ledger #12708, opening mark 6377506702). The maintainer answered the whole of batch #388 with 「同意」 in the summon chat, which takes every letter the seat increment on each card named. For this card the increment is 6377536821 on thedomain:specseat's filing (stage-2 contract review 6375883728 on #25541, boundary flag "out of scope 1").Governing text: ADR-0111 D7 item 2 (
docs/adr/0111-record-share-management-authority-and-verb-boundary.md:134–139,SHARING_NOT_ENABLEDon a public / bypass / owner-less object); ruling 6355206139 (letter B) on #25233 (the EXTERNAL rung admits explicit grants only: shares, share links, sharing rules, position and permission-set grants with their RLS); #8207 as quoted ininertGrantReason's docblock (the gates never see the granter).What the ruling binds (letter A — admit for external recipients):
inertGrantReason(object, recipient?)inpackages/plugins/plugin-sharing/src/sharing-service.ts(today:1351, one argument; thepublicarm at:1362): on an effective-publicobject auserrecipient is admitted when itssys_member.rolein the object's organization is exactlyexternal— the same population test as the stage-1 door. Every other recipient keeps the 422. The verdict stays independent of who asks; it reads the object and the recipient's population, nothing else.assertNotInertGrantat:1536today) applies the same per-recipient verdict and mints rows only for the external members a rule expands to.packages/lint/src/validate-sharing-rule-enforceability.tsreports "effective for external recipients only" as advice, not a refusal.sharing-rules.mdxgains one sentence; the ADR record is one dated line on ADR-0111 D7, riding the Tier H docs PR for Security: an external organisation role whose members do not see the member directory and reach data only through explicit grants (portal users on hosted environments) #25233.public_read_writerecord shared with an external, who reads that record only; an internal recipient still gets 422; acontrolled_by_parentdetail still follows its master (feat(security): the external organization role is the EXTERNAL principal — owner-only org-wide default, explicit grants only #25541's pin).Not taken: B (keep the refusal, document the RLS route — leaves the ruled channel closed on half the objects by a property set for staff), C (lift the floor on public objects — contradicts ruling B).
Metadata-protocol: none(no spec key, value or type changes).Workload of the letter:
domain:spec· 1 PR; precedent #25541's OWD seam (sharing-service.tsabout +22 source lines,external-owd-floor.test.ts+142); the materialiser and lint parts have no measured precedent.Execution:
needs-user-decision→pm:queuein this act, levelpriority:p2(ruled as an execution parameter: pull is low until the external role ships); sequencing, not a blocker: lands after the Tier H docs PR for #25233 carries the dated D7 line.domain:specseat 1 claims.os-decision-facets
Generated by Claude Code
os-elon-musk commentedon Oct 24, 2026
Ruling: batch #9 item 2 · letter A · maintainer 「#9 同意」 2026-10-24T00:43:45Z
A = 对外部收件人放行。
inertGrantReason改为看(对象,收件人):有效模型为public的对象上,user收件人在该对象所属组织的sys_member.role恰为external即放行,其他收件人照旧 422;共享规则只给展开到的外部成员物化行;validate-sharing-rule-enforceability改为提示「仅对外部收件人生效」;sharing-rules.mdx一句;插件单测 + showcase 实测(把一条public_read_write记录分给外部成员,他只读到那一条;内部收件人仍 422);ADR-0111 D7 的日期行搭下一个 Tier H 文档 PR。工作量:spec 车道 1 个 PR(先例 #25541 的 OWD 缝)。置信缺口随卡给执行席:一条规则同时展开到员工与供应商时只给供应商物化,物化报告里要有一句话说明;收件人角色在对象所属组织里判,REST 与规则求值器每条路径是否拿得到组织,先读再写。可排在 #25233 的 Tier H 文档 PR 之后。
标签
needs-user-decision→pm:queue;裁不派。简报:https://claude.ai/artifact/XKBVwFLq2V5nBTNy3enkbz(卡 2)。Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Ruling: letter A · maintainer Tesla 「其他同意」 2026-10-24T01:25Z, on the director thread's decision list (item 8, recommended A), relayed to
domain:specseat 1 · recorded 2026-10-24T01:31Z.A: on an effective-
publicobject, a share or a sharing rule whose recipient is anexternalmember of the object's organization is admitted. Every other recipient keeps the 422. Execution is the "If A" paragraph of this card, in one spec-lane PR. State:needs-user-decision→pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Retraction ·
domain:specseat 1 · 2026-10-24T01:33Z: the ruling record above is withdrawn. Tesla's 「其他同意」 covered section 一 of the decision list only (clarified 2026-10-24T01:28Z: 「其他同意,说的是 一、回一句即可落地」). This card is item 8, in section 三, which Tesla is still reviewing. State is back toneeds-user-decision; nothing was dispatched.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Ruling: letter A · maintainer Tesla 2026-10-24T01:28Z on the director thread, item by item: 「8. #25549 外部成员在「全员可读写」对象上能否被单独分享记录 … 同意」 (recommended A) · recorded by
domain:specseat 1 2026-10-24T01:34Z. This supersedes the retraction above: the retraction read only the earlier 「其他同意」 and missed this explicit approval. A: on an effective-publicobject, a share or sharing rule to anexternalmember of the object's organization is admitted; every other recipient keeps the 422. Execution: the card's "If A" paragraph, one spec-lane PR. State: dispatched now.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim: PM loop round 2
Session:
session_01AjwMndyviznGagEUcX7QVx(the card thread; seat sessionsession_013odhA26dvzy7pQmevdTTvyposts this claim)Account:
os-teslaBranch:
claude/project-thread-eflr1zWorktree:
objectstack-issue-25549Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface:
packages/plugins/plugin-sharing/src/**(inert-grant verdict, rule materialisation),packages/lint/src/validate-sharing-rule-enforceability.ts,content/docs/permissions/sharing-rules.mdx, the showcase dogfood case, tests, one changeset (stop on breach; explain in the report). ⛔ no ADR edit (ADR-0111 D7 line is drafted in the report, Tier H).Container & model:
M,mode:thread,effort: highClause-②: yes (widening: the grant door and the materialiser admit one recipient population on effective-public objects)
Metadata-protocol: semantic (lint refusal becomes advice) — the dev confirms against the changeset gates
Responsibility:
n/a — ruled enhancementThread-read: none
Serial constraints cleared: none in flight on plugin-sharing.
The platform lets the card thread push only its designated thread branch, so
Branch:records that branch. Dispatch order: the project's shared folder,spec-seat-1/dispatch-25549.md. The seat runs aCONTRACT_REVIEW_TIERreview before the queue.Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
os-dev-report
{ "issue": 25549, "status": "done", "branch": "claude/project-thread-eflr1z", "pr": "https://github.com/objectstack-ai/objectstack/pull/25591", "head": "a1260e572f", "session": "session_01AjwMndyviznGagEUcX7QVx", "premise_still_valid": true, "summary": "inertGrantReason(object, recipient?) in plugin-sharing now admits, on an effective-public object with a real owner_id (not a phantom anchor), a user recipient whose sys_member.role in the record's organization is exactly external; every other recipient keeps the 422 SHARING_NOT_ENABLED, and controlled_by_parent, owner-less, phantom-anchor and bypass objects are unchanged. grant() asks the recipient half on both paths after the organization is resolved (after D1 on the direct path); no resolved organization is refused with a named reason. The rule materialiser skips the marked per-recipient refusal in all three passes, writes rows for external recipients only, and puts one note line on the pass result (reaches the admin through POST sharing/rules/ID/evaluate, which answers the result unfiltered) plus one log line per pass with the count. The lint's public arm of sharing-rule-object-not-shareable drops to warning ('effective for external recipients only'); sharing-rules.mdx gains one paragraph.", "tests": "At a1260e57, all under os-verify-lock: pnpm --filter @objectstack/plugin-sharing typecheck + vitest run, VERDICT command-exit 0, Test Files 55 passed (55), Tests 1233 passed (1233). pnpm --filter @objectstack/lint typecheck exit 0; vitest run VERDICT command-exit 0, Test Files 158 passed (158), Tests 7237 passed (7237). Dogfood closure built (env -u OS_SKIP_DTS, VERDICT 0), vitest run test/external-member-directory.dogfood.test.ts VERDICT command-exit 0, Tests 13 passed (13); dogfood tsc --noEmit exit 0. Ablation (fix committed first, scripts/ablation-replace.mjs, source import so no dist leg): anchor \"if (recipient === 'external') return null;\" x1 -> x0, blob c1f616f5 -> 5d5c8c31, system-caller-inert-grant.test.ts 8 failed / 25 passed (33), restored: blob == HEAD c1f616f5, git diff HEAD empty. Direction observed: red.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at a1260e57 derived 104 families; each ran with exit captured before any pipe. First sweep 101 exit 0, 3 exit 3 (PREREQUISITE NOT MET): check-plugin-teardown-shape --self-test (pinned control 621a4876 missing from the shallow clone), check:skill-examples (no client-react dist), check:dual-build-cjs-loads (workspace dists). After git fetch of 621a487607881c66b2899b7e3477115229a156b4 and a locked workspace build (pnpm -r --filter '!@objectstack/docs' build, VERDICT 0), all three re-ran exit 0. dispatch-gates --ran: 104 derived, 104 run, 0 NOT-MEASURED, 0 UNRUN (record: spec-seat-1/out/25549-gates-ran.txt). PR check-runs: in_progress at report time.", "line_budget": "613 changed lines (+531 / -82) over 12 files, under the 3000 human-merge threshold.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm on the Clause-② line", "files_changed": [ ".changeset/25549-external-share-public-object.md", "content/docs/permissions/index.mdx", "content/docs/permissions/sharing-rules.mdx", "examples/app-crm/objectstack.config.ts", "packages/lint/src/authoring-rules.ts", "packages/lint/src/rule-explanations.ts", "packages/lint/src/validate-sharing-rule-enforceability.test.ts", "packages/lint/src/validate-sharing-rule-enforceability.ts", "packages/plugins/plugin-sharing/src/sharing-rule-service.ts", "packages/plugins/plugin-sharing/src/sharing-service.ts", "packages/plugins/plugin-sharing/src/system-caller-inert-grant.test.ts", "packages/qa/dogfood/test/external-member-directory.dogfood.test.ts" ], "deviations": [ "File surface: three files outside the Claim's list, each a sentence this change made false (os-dev: a shipped text the round falsifies must be fixed): content/docs/permissions/index.mdx ('an authoring-time build error'), examples/app-crm/objectstack.config.ts comment ('now fails the build'), packages/lint/src/authoring-rules.ts comment (the registry note for the same rule). Please add them to the Claim's file surface.", "Changeset level minor on both packages, as Metadata-protocol: semantic requires (check-changeset-no-major --base origin/main exit 0). The card body said Metadata-protocol: none; the Claim's semantic is what the PR and changeset carry, bare (the Claim's trailing parenthetical dropped so the reader parses it).", "Unit test placed in the existing system-caller-inert-grant.test.ts (new describe block) rather than a new file, to reuse its pinned engine double and leave the engine-double ledger untouched.", "Report note: declared on plugin-sharing's own SharingRuleReconcilePassResult (optional note), not on the spec's SharingRuleEvaluationResult, to keep packages/spec out of this diff.", "PR body erratum (no PATCH by dev): the Acceptance note 'Spec contract docblock' ends 'with the ADR line below'; the ADR draft is in this report, not below in the PR. Suggested PATCH: replace 'with the ADR line below' by 'with the ADR-0111 line drafted in the os-dev-report'.", "ADR-0111 D7 dated line left out (Tier H); draft in adr_0111_draft." ], "adr_0111_draft": "Append to D7 item 2 of docs/adr/0111-record-share-management-authority-and-verb-boundary.md: *Amended 2026-10-24 (ruling 6377844010 on objectstack-ai/objectstack#25549, letter A; landed in #25591).* An external principal meets an owner-only floor on every object (ruling 6355206139, letter B), so a share row on a public object IS consulted for that population. On an object whose effective model is public and whose owner_id is real (not a federated phantom anchor), grant() therefore admits a user recipient whose sys_member.role in the record's organization is exactly external; every other recipient keeps the 422. The verdict reads the object and the recipient's population, never the granter. A sharing rule materialises rows for its external recipients only and reports the skipped internal half in one line. controlled_by_parent stays refused for every recipient.", "spec_docblock_followup": "packages/spec/src/contracts/sharing-service.ts, ISharingService.grant docblock: 'a share on a public / bypass / owner-less / controlled_by_parent object enforces nothing' is now half true. Suggested text: '... (SHARING_NOT_ENABLED: a share on a bypass / owner-less / controlled_by_parent object enforces nothing; on a public object it is admitted only for a recipient who is an external member of the record's organization)'. Comment-only, published d.ts; left for the seat to route.", "mcp_calls": "0 — no MCP GitHub tool used (card, comments and PR read through the public REST API)", "api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (draft #25591, assignees os-tesla) via fleet-write relay run 40055388624; POST /repos/objectstack-ai/objectstack/issues/25549/comments (this os-dev-report) via post-stamped.mjs. git push of claude/project-thread-eflr1z is not a REST write.", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 24, 2026
Claim addendum: file surface ·
domain:specseat 1 (seat post #6017) ·os-tesla· sessionsession_013odhA26dvzy7pQmevdTTvy· 2026-10-24T02:48ZThe Claim's file surface for #25549 (draft PR #25591, head
a1260e572f) adds three files. The seat accepts each because it held a sentence this change made false, and a shipped text the round falsifies is fixed in the same round:content/docs/permissions/index.mdx: "an authoring-time build error"examples/app-crm/objectstack.config.ts: the comment "now fails the build"packages/lint/src/authoring-rules.ts: the registry note for the same ruleNothing else changes: no ADR edit, and
packages/specis untouched. The ADR-0111 D7 dated line and theISharingService.grantdocblock follow-up are drafted in the os-dev-report (comment 6378915159). They will go as separate PRs after this one lands.Generated by Claude Code
test(dogfood): showcase D3/D4 contrast reads the public-OWD refusal #…
Revert "chore: version packages (#24876)"