You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 3aae216
Browse filesBrowse the repository at this point in the historyBrowse files
docs(sharing): record the external-recipient arm on ADR-0111 D7 and the grant contract
ADR-0111 D7 item 2 gains the dated amendment for the external-recipient
arm. ISharingService.grant's docblock stops saying a share on a public
object enforces nothing. The rule pass note also covers a record whose
organization could not be resolved, and a test comment no longer names
a file that does not exist.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AjwMndyviznGagEUcX7QVx
docs: the sharing-rule pass note and the `ISharingService.grant` contract say what a share on a public object does
7
+
8
+
Clause-②: no
9
+
Metadata-protocol: none
10
+
11
+
A follow-up to the external-share change. The one-line `note` a sharing-rule pass carries when it skips recipients on a public object now also covers a record whose organization could not be resolved, which admits no recipient. The `ISharingService.grant` docblock no longer says a share on a public object enforces nothing: it is admitted for an `external` member of the record's organization.
Copy file name to clipboardExpand all lines: docs/adr/0111-record-share-management-authority-and-verb-boundary.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -137,6 +137,8 @@ Three write-time refusals so a persisted share always means something:
137
137
138
138
1.**`recipientType` is narrowed to `user`** at the grant boundary; anything else → **400**. The `group` / `position` / `unit_and_subordinates` / `guest` values are not enforced by any gate today — persisting them is the ADR-0078 inert-metadata trap. The vocabulary is consolidated to one list in the contract; non-`user` principals become a documented *future direction* (delivered via rule materialisation, which already expands `team` / `business_unit` into per-user rows), not an accepted-but-ignored input on the manual surface.
139
139
2.**`grant()` validates the object is in an enforcing posture** — `effectiveSharingModel` is `private` or `read`**and** the object has an `owner_id` field. Otherwise → **422** (`SHARING_NOT_ENABLED`): a share on a public / bypass / owner-less object enforces nothing. `controlled_by_parent` is refused explicitly (Salesforce parity — a detail record is not independently shareable; its access follows the master).
140
+
141
+
*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 half in one line. `controlled_by_parent` stays refused for every recipient.
140
142
3.**Manual and rule shares coexist by `source`.** The upsert key becomes `(object, record, recipient, source)` so a manual grant no longer clobbers a `source: 'rule'` row and vice versa. When multiple rows grant the same recipient, the widest `access_level` wins (Salesforce's RowCause model — grants are additive, never subtractive).
141
143
142
144
### D8 — Share-link re-share semantics are made explicit
0 commit comments