Skip to content

Commit ab5209b

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-23226-guest-owner-stamp
2 parents 3e44b3d + 6d96721 commit ab5209b

17 files changed

Lines changed: 1971 additions & 429 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
'@objectstack/objectql': minor
3+
'@objectstack/service-messaging': minor
4+
'@objectstack/service-automation': minor
5+
'@objectstack/plugin-approvals': minor
6+
---
7+
8+
fix(objectql,service-messaging,service-automation,plugin-approvals)!: a notification carries the organization of what it is about, and one no organization owns is refused instead of stored with `organization_id` NULL
9+
10+
Clause-②: no (narrowing) — notification rows gain the organization they should already carry; what narrows is an emit that names no organization on an install where none is derivable, which the engine now refuses where it used to store the event row with `organization_id` NULL.
11+
12+
<!-- adr-0087: not-required (no-migration-prescription) A runtime narrowing of which notification writes the engine accepts, not a metadata change. No spec key, authorable spelling, export, option, config field or stored shape is added, removed, renamed or re-shaped, and no stored row is rewritten: rows already stored with organization_id NULL keep it until the v18 ownership ceremony attributes them, which already lists sys_notification with the attribution fate. The only type change is an additive optional `organizationId` on the input of `ApprovalMessagingSurface.emit`, the approvals service's messaging surface: the interface is not listed in the package index, but it is published surface, reachable through the exported `ApprovalServiceOptions.messaging` and the public `ApprovalService.attachMessaging`, so it is an additive widening that no existing implementation has to change for. The other categories are closed on facts: the four packages publish (not unpublished); no ADR-0087 id covers notification ownership and this diff adds none (not registered / already-registered); nothing is removed or narrowed on an interface or type (not runtime-interface-only / type-surface-only); and no runtime export is retired (not runtime-export-retired). -->
13+
14+
**BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes.
15+
16+
Every notification emitted from a system-context run — a scheduled flow, a record change written by a system context, an approval node — was stored in `sys_notification` with `organization_id` NULL, so reports and cleanup that filter by organization did not see it. Each emit also logged a `[notify] no organization in scope` warning. On an install holding several organizations, the deliveries of the same emit were refused while the flow step reported success, so nothing reached the inbox.
17+
18+
**Who owns a notification now:**
19+
20+
- **The run's organization.** A run that carries one emits exactly as before.
21+
- **Else the record it is about.** A `notify` node in a run with no organization reads the organization of the record the notification is about. That is the declared click-through record (`sourceObject` + `sourceId`), else the run's trigger record. It is read from the stored row by id, never from the record snapshot the run carries.
22+
- **Approvals.** Every approval notification carries its request's organization: `approval.requested`, reminders, comments, send-backs, reassignments, escalations and out-of-office notices.
23+
- **Else the owner the engine derives for a system write** (ADR-0131 D9). `sys_notification` is now in the platform-object tenancy ledger, so an emit that names no organization gets the install's sole organization under the `single` posture.
24+
- **The rows below follow the event.** `emit()` stamps the stored event row's organization on its delivery, inbox row and receipt, on both the inline and the outbox path.
25+
26+
**Refused now:** where no organization is derivable (a walled posture, or a `single` install holding more than one organization), an emit that names none is refused with `ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED`. Nothing is written for it. A `notify` step that hits the refusal fails with `notify failed: ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED: …` instead of succeeding. `MessagingService.emit()` rejects with the engine's error for any other producer.
27+
28+
**What to do when it refuses:** give the notification an owner. Name the record it is about on the `notify` node (`sourceObject` + `sourceId`), declare the scheduled flow's acting organization (`organization` on its start node), or pass `organizationId` to `emit()` from your own code.
29+
30+
The `[notify] no organization in scope` warning is gone, because the NULL row it predicted is no longer written. Rows already stored with `organization_id` NULL are not rewritten here. The v18 organization-ownership ceremony attributes them from their source record.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@objectstack/plugin-security': patch
3+
---
4+
5+
fix(plugin-security): an environment kernel's security catalog serves metadata-door permission sets and positions again
6+
7+
Clause-②: no
8+
9+
On an environment kernel (the per-project kernel a hosted tenant runs), a permission set or a position saved, published or deleted through the metadata door is in the security catalog again:
10+
11+
- It resolves from the kernel's metadata service, under the package its stored row is bound to.
12+
- A user granted it is authorized by it.
13+
- An `isDefault: true` set in a runtime package records its pending `everyone` suggestion again (ADR-0090 D5).
14+
15+
After a restart, the stored overlays are served again too.
16+
17+
This regressed in C3 stage 8 (`feat(plugin-security)!: retire the four security-catalog row objects`). That change unwired the permission-set projector. On an environment kernel, the projector's in-memory echo was the only thing that put a stored set into the metadata service, because the metadata door hydrates no project row into the engine registry. Since stage 8, such a set resolved to nothing and granted nothing (fail-closed), and no suggestion was recorded.
18+
19+
Positions never had an echo. On an environment kernel, a metadata-door position was unresolved before stage 8 as well, and it now resolves.
20+
21+
`SecurityPlugin` now registers its own mutation projector for `permission` and `position` on the protocol, plus one pass over the stored environment-wide overlays at `kernel:ready`. Both only register the overlay body in the metadata service. They write no row, and they change nothing a host kernel resolves, since a host kernel reads these from the engine registry first.

0 commit comments

Comments
 (0)