Skip to content

Commit 9be4657

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-gru6pg
2 parents ac294b0 + 8dfd126 commit 9be4657

37 files changed

Lines changed: 637 additions & 173 deletions
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/plugin-sharing': patch
3+
'@objectstack/plugin-security': patch
4+
'@objectstack/cli': patch
5+
'@objectstack/spec': patch
6+
---
7+
8+
fix(plugin-sharing): the declared-rule organization walk ends on the rows read, and a row with no readable id answers unknown; texts that still described the retired NULL-organization reading are corrected
9+
10+
Clause-②: no
11+
Metadata-protocol: none
12+
13+
- **`@objectstack/plugin-sharing`.** The boot's organization walk for declared sharing-rule scopes (`resolveDeclaredRuleScopes`) judged "short page, stop" on the ids it kept, so a full page of `sys_organization` holding one row with no readable `id` ended the walk early and served a partial list, and the boot sweep then revoked the declared-rule grants of every organization past it. The walk now ends on the number of rows READ, and such a row makes the answer unknown: declared rules are served in no organization until the next boot and no declared rule's grant is revoked, with a warning that names the cause. Under the shipped schema every organization row has a text id, so no install is expected to see the new warning.
14+
- **`@objectstack/plugin-security`.** The Organization help on `sys_user_position` and `sys_user_permission_set` no longer says an empty value means "global" or "applies in every org context". A grant or assignment applies only in its own organization, and one with no organization applies nowhere. The four locale bundles carry the new text.
15+
- **`@objectstack/cli`.** `os migrate organization-ownership`'s warning about rows whose owner cannot be derived reads "…while one stays NULL, except on a table whose column is not the tenancy anchor." where it read "(but on a table …)".
16+
- **`@objectstack/spec`.** Wording only, no schema moves: the upgrade guide's `driver-read-scope-follows-tenant-wall` entry names update and delete "by id or by predicate"; the `sharing-rule-declared-mirror-retired` entry no longer paraphrases ADR-0131 D13 as asking an entry of every retirement; `IEmailService`'s `organizationId` TSDoc no longer says an absent value leaves the row unstamped (the engine stamps the derivable organization or refuses the insert); a TSDoc citation in `schedule-organization.zod.ts` names the run store instead of a deleted file.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/core': patch
3+
'@objectstack/plugin-auth': patch
4+
'@objectstack/plugin-security': patch
5+
'@objectstack/cli': patch
6+
---
7+
8+
fix(core,plugin-auth)!: the last-admin guard judges a `sys_organization` insert that moves the Default Organization, and one definition of the Default Organization is exported from core
9+
10+
Clause-②: yes (narrowing)
11+
Metadata-protocol: none
12+
13+
<!-- adr-0087: not-required (no-migration-prescription) No key of any metadata schema is removed, renamed or re-shaped, so there is nothing for `objectstack migrate meta` to rewrite and no tombstone. The narrowing is one more organization write the last-administrator guard refuses at runtime. -->
14+
15+
**BREAKING** for a `single`-posture deployment whose only organization is not slugged `default` and whose last administrator holds nothing but the Default Organization's `admin_full_access` grant.
16+
17+
- **`registerLastAdminGuard` (`@objectstack/plugin-auth`) now judges a `sys_organization` insert.** Under `single` the Default Organization is the one slugged `default`, else the only one, and its `admin_full_access` grant is what makes the platform administrator one (ADR-0131 D5). Adding an organization can move it with no grant touched: a second organization beside a sole one with another slug leaves none, and a new one slugged `default` takes its place.
18+
- FROM: the insert was admitted. The administrator lost platform standing in silence, and the guard then admitted a ban or delete of that same administrator, because it no longer counted anyone.
19+
- TO: the insert is refused (`PERMISSION_DENIED`, 403, on `sys_organization`) when it would leave nobody who can sign in and administer. An organization owner or admin, an `OS_PLATFORM_OWNER_EMAIL` administrator, or a Default Organization slugged `default` (which a new organization cannot move) admits it, as on the update and delete halves. Under a wall no grant anchors, so the insert is admitted and costs no reads.
20+
- **One definition of the Default Organization (`@objectstack/core`).** New exports: `findDefaultOrganization(read)` (the rule, over a read the caller supplies), `DEFAULT_ORGANIZATION_SLUG`, and `readDefaultOrganizationId(ql)` (the resolver's own read, which fails loud on an unreachable store). plugin-auth's boot invariant, default-organization bootstrap, tenancy service and last-admin guard, plugin-security's `resolveDefaultOrganizationId`, and the ownership ceremony's plan now name the organization through it. Each keeps its own read policy: whether a failed read throws or reads as none, the guard's simulation of a pending write, and the ceremony's raw SQL. Two edge answers align with the resolver's. A `default`-slugged row with a blank id now falls through to the only-organization reading in the ceremony's plan. A numeric id is now read as its string in plugin-security's reader.
21+
22+
**What an operator does.** Before you create a second organization on a `single` deployment whose only organization has another slug, either give that organization the slug `default` (while it is the only one, that changes nothing else), or make sure another account can administer: an organization owner or admin, or an address in `OS_PLATFORM_OWNER_EMAIL`.

‎.changeset/console-0d12e95361b2.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
"@objectstack/console": patch
3+
---
4+
5+
Console (objectui) refreshed to `0d12e95361b2`. Frontend changes in this range:
6+
7+
Derived from the changesets objectui declared over the range — 2 releasing of 3 changesets added across 5 non-merge commits; omitted: 1 release-nothing changeset, 2 commits carrying no changeset (they ship no package code).
8+
9+
- **minor** — An approver who is not an administrator sees the whole approval request page again: its fields, its decision panel with the decisions they may take, and its Timeline (objectui#133… (objectui `0d12e9536`)
10+
- **patch** — Setup → Datasources offers "Sync objects" only on a datasource that can be synced (objectui#13318). (objectui `b8fa2237d`)
11+
12+
**In this console build, declared nowhere** — objectui merged 2 commits in this range with no `.changeset/*.md`. The code is inside the pin above and ships here, but nothing upstream declared them, so they appear in no objectui CHANGELOG and in no entry above. Listed by subject rather than counted, because a count cannot tell a dependency bump from a form-behaviour change (objectstack#6174); the upstream gate that would prevent this is objectui#3387.
13+
14+
- _(no changeset)_ fix(pin): the missing-entry remedy builds with declarations whatever OS_SKIP_DTS is exported (#13325) (objectui `74ebad095`)
15+
- _(no changeset)_ fix(hooks): port subst_end() into guard-main-checkout-bash (objectstack 7e40e8a03 and 338297e46) (#13320) (objectui `1aa88519a`)
16+
17+
Metadata-protocol: none (the console refresh changes no key, value or type a metadata author writes)
18+
19+
objectui range: `cd14d8eeffe1...0d12e95361b2`

‎.objectui-sha‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
cd14d8eeffe11ae03b6acf82e663c1d293dfdf5f
1+
0d12e95361b20b1eb9012f97d2d36e5fa7f14374

‎content/docs/permissions/tenant-audit-census.mdx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ now costs nothing, and the population is **re-derivable** rather than quoted.
4646
`auditMissingTenant(object, op, options)` is gated in this order:
4747

4848
1. `OS_TENANT_AUDIT=0` — off entirely;
49-
2. `options.bypassTenantAudit` — the engine sets this for **every**
50-
`ExecutionContext.isSystem` write (ObjectQL's `buildDriverOptions`);
49+
2. `options.bypassTenantAudit` — on an `ExecutionContext.isSystem` write the
50+
engine sets it only where the object's schema puts the write out of scope:
51+
an object with no tenant field, or a federated one (ObjectQL's
52+
`buildDriverOptions`). A caller may still pass it explicitly;
5153
3. a present `options.tenantId` — the engine fills it from `execCtx.tenantId`;
5254
4. the deployment posture — the warning is only meaningful on a walled install;
5355
5. the object having a tenant field.

‎content/docs/references/automation/schedule-organization.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ multi-plant MES example is the ADR's own). It does not.
7676
`sys_automation_run` has ALREADY been ruled to use: `ObjectStoreSuspendedRunStore`
7777
resolves a run's organization as `organizationOf(<subject record>) ?? ctx.tenantId`
7878
— subject first, the acting context as the fallback and *never* the primary
79-
(`objectql/src/tenancy/platform-object-tenancy.ts`, the `sys_automation_run`
80-
evidence line). Before this card the two halves disagreed under `group`: the
79+
(`service-automation/src/suspended-run-store.ts`, where the store stamps a
80+
run's row). Before this card the two halves disagreed under `group`: the
8181
history row was stamped from the record while the inbox and delivery rows
8282
followed an acting context that could not exist there, so they were refused.
8383
Filling the acting context from the record makes one run carry ONE

‎packages/cli/src/commands/migrate/organization-ownership.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ export default class OrganizationOwnershipCeremony extends Command {
632632
printWarning(
633633
`${summary.unattributableRows} row(s) whose owner cannot be derived are listed by id, with why, in the plan ` +
634634
'file. The ceremony neither guesses their owner nor deletes them, and --apply does not certify the database ' +
635-
'while one stays NULL (but on a table whose column is not the tenancy anchor): assign each its ' +
635+
'while one stays NULL, except on a table whose column is not the tenancy anchor. Assign each its ' +
636636
'organization, or delete it by hand, before or after --apply, and re-run it.',
637637
);
638638
}
@@ -1121,7 +1121,7 @@ export default class OrganizationOwnershipCeremony extends Command {
11211121
}
11221122
console.log('');
11231123
// Unattributable rows hold certification (ruling C on ADR-0131 D10 fate
1124-
// 4) but on a table whose column is not the tenancy anchor; `advisory`
1124+
// 4), except on a table whose column is not the tenancy anchor; `advisory`
11251125
// counts those and the advisory departures, each said in its own terms.
11261126
const unattributableHeld = report.tables.reduce((n, t) => n + (t.held?.rows ?? 0), 0);
11271127
const unattributableReported = report.tables.reduce((n, t) => n + t.unattributable.length, 0) - unattributableHeld;

0 commit comments

Comments
 (0)