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 00d5635
Browse filesBrowse the repository at this point in the historyBrowse files
docs: the external org-wide-default floor's docblock no longer lists `controlled_by_parent` among the models it closes
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
`owdForAudience`'s module docblock said an external is floored on a `controlled_by_parent` detail. It is not: plugin-sharing exempts the detail, whose rows follow its master, and the master meets the floor (#25541). Comment only; no behaviour changes.
docs(skills): the objectstack-data skill's `current_user` placeholder table names a declared membership key
6
+
7
+
Clause-②: no
8
+
9
+
The security rules' table of the `current_user.*` placeholders the RLS compiler resolves listed only the kernel's keys. It now has a row for `current_user.KEY`, the set an object's `membershipKeys.KEY` declares (for `IN`), so an author reading the catalog alone learns that a declared key resolves.
**The v18 line has exited prerelease mode.** While Changesets was in pre mode with the tag `next`, every release cut from `main` was the next `18.0.0-next.N` on npm's `next` tag, and `scripts/check-changeset-no-major.mjs` stood aside in pre mode, so a pull request could declare a major bump. Changesets has exited pre mode, so the line's GA version is `18.0.0`, which publishes to npm's `latest` tag, and the guard again fails a pull request that declares a `major` bump. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line).
21
21
22
-
**After 18.0.0, a breaking change ships in a MINOR or a PATCH release.** The maintainer's 2026-10-21 ruling ([#25105](https://github.com/objectstack-ai/objectstack/issues/25105)) keeps the no-major guard and grades each change by the metadata protocol: a key, value or metadata type added, narrowed or given a new meaning takes MINOR, and every other change takes PATCH, breaking or not. A non-protocol breaking change (a REST response, a boot refusal, a CLI exit status, a removed runtime export) keeps its `**BREAKING**` banner and ships as a PATCH. 18.1.0, the first release under the rule, is a MINOR that carries 87 changesets declared breaking.
22
+
**After 18.0.0, a breaking change ships in a MINOR or a PATCH release.** The maintainer's 2026-10-21 ruling ([#25105](https://github.com/objectstack-ai/objectstack/issues/25105)) keeps the no-major guard and grades each change by the metadata protocol: a key, value or metadata type added, narrowed or given a new meaning takes MINOR, and every other change takes PATCH, breaking or not. A non-protocol breaking change (a REST response, a boot refusal, a CLI exit status, a removed runtime export) keeps its `**BREAKING**` banner and ships as a PATCH. 18.1.0, the first release under the rule, is a MINOR that carries 90 changesets declared breaking.
23
23
</Callout>
24
24
25
25
ObjectStack follows [Semantic Versioning 2.0.0](https://semver.org/) (`MAJOR.MINOR.PATCH`). The table below describes what each component means; which version a breaking change ships in depends on the line, as the callout above says.
Copy file name to clipboardExpand all lines: docs/adr/0073-automation-execution-identity.md
+15-1Lines changed: 15 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,20 @@
11
11
12
12
---
13
13
14
+
## Amendment: a `runAs:'system'` run writes no identity assignment (2026-10-24, #25251)
15
+
16
+
M2's last bullet below says "`runAs:'system'` stays god-mode throughout". That no longer holds for one family of writes. Ruling 6354966423 (letter B) on #25251 carved identity assignment out of a flow's system elevation, the same way the 2026-10-03 ruling carved the stored-metadata tables out of app-authored work: a flow is app-authored automation, and `runAs` declares its authorization posture, not an identity (D2).
17
+
18
+
**What is refused.** An insert, update, delete, transfer, restore or purge of a row of `sys_user_position`, `sys_user_permission_set`, `sys_invitation` or `sys_member`, from a context that carries `isSystem`**and** a `flowRunId`. That is the context a `runAs:'system'` run's data nodes write under (`resolveRunDataContext`). The security middleware refuses it ahead of its `isSystem` short-circuit with `PERMISSION_DENIED` (403), and nothing is written. The refusal holds at any depth of the flow's own write: a cascade leg or a hook's write under the same context is refused the same way. Landed in #25483 (plugin-security, the run-time arm) and #25564 (the `FlowSchema.runAs` description and the ADR-0087 D3 entry `flow-run-as-system-identity-assignment-write-refused`).
19
+
20
+
**What is unchanged.** A system run's reads of the four objects, and its writes to every other object. A `runAs:'user'` run, which carries a `flowRunId` without `isSystem` and reaches the ADR-0090 D12 delegated-administration gate as the user it acts for. Every platform writer whose system context carries no flow run: the seeders, the publish materializer, better-auth and its reconciliation, invitation placement, and the organization-administrator grant.
21
+
22
+
**The remedy the refusal names.** For position holdings and permission-set grants, `runAs:'user'`: the D12 gate then judges the acting user and records that user as `granted_by`. For invitations and memberships, the organization endpoints (`invite-member`, `cancel-invitation`, `update-member-role`, `remove-member`), which apply the invitation's placement and role-cap checks.
23
+
24
+
So `system` stays full elevation everywhere else, and the M2 bullet is read with this one exception.
25
+
26
+
---
27
+
14
28
## Amendment: user-less data ops are now REFUSED (2026-07-28, #3760)
15
29
16
30
Two load-bearing claims below turned out to be wrong. Both were about *severity*, not about the model — D1–D5 stand unchanged, and D5 shipped in full.
@@ -21,7 +35,7 @@ Two load-bearing claims below turned out to be wrong. Both were about *severity*
21
35
22
36
**Consequently `runAs:'user'` + no trigger user now REFUSES the data operation** (`UnscopedRunDataAccessError`, thrown from `resolveRunDataContext` — the single place every data node resolves its context). This implements D5's ruling ("user-less `runAs:'user'` is a configuration error") at the only layer that can see the record-change case, since whether a triggering write carries a user is not knowable at authoring time.
23
37
24
-
Note what this deliberately is **not**: it does not re-badge these runs as `isSystem`. The security middleware's `isSystem` short-circuit precedes its package-managed-row / system-row / audience-anchor / delegated-admin gates, all of which a principal-less context still has to clear — so "unscoped" was never equivalent to "system", and elevating would have *widened* these runs (e.g. letting them write `sys_user_position`) rather than preserving the status quo.
38
+
Note what this deliberately is **not**: it does not re-badge these runs as `isSystem`. The security middleware's `isSystem` short-circuit precedes its package-managed-row / system-row / audience-anchor / delegated-admin gates, all of which a principal-less context still has to clear — so "unscoped" was never equivalent to "system", and elevating would have *widened* these runs (e.g. letting them write `sys_user_position`) rather than preserving the status quo — a write that, since the 2026-10-24 Amendment above, a `runAs:'system'` run is itself refused.
25
39
26
40
This is an **interim** posture, not a replacement for D2. Refusal is strictly safer than today and forward-compatible: when the `automation` principal lands, the refusal point becomes the place that resolves it, and these runs go from *refused* to *RLS-enforced* with no third state. Ordering is unchanged — D2 still waits for a real consumer.
0 commit comments