Skip to content

Commit 5ec0951

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-18069-user-authored-seat-content-row
2 parents 829af25 + e248c4d commit 5ec0951

16 files changed

Lines changed: 1287 additions & 86 deletions
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
"@objectstack/cli": patch
3+
---
4+
5+
`os generate migration` gives the file family — `file` / `image` / `avatar` / `video` / `audio` — the **same column width in both formats**. The typescript format emitted a bare `table.string(name)`, knex's `varchar(255)`, while `--format sql` emitted `VARCHAR(2048)` for the same field, so one command answered one field with two widths depending on the flag (#17883).
6+
7+
2048 is not a new number: ADR-0104 ruled the generator's `VARCHAR(2048)` the end-state for this family, `driver-sql` moved to it (`MEDIA_ID_VARCHAR_CHARS`, #15989), and `os migrate files-to-references --apply` retypes the column to `varchar(2048)`. The typescript format was the one producer left at 255 — so a deployment scaffolded from it declared a width the migration it will later run retypes away from.
8+
9+
```diff
10+
- table.string('cover_image').nullable();
11+
+ table.string('cover_image', 2048).nullable();
12+
```
13+
14+
- **No regeneration is required of anyone.** `syncSchema` / `initObjects` are additive and never alter an existing column's type, and a `sys_file` id is far shorter than 255, so nothing stored today is at risk either way. What moves is the **declared** width of tables generated from now on.
15+
- **The width is now read from the sql format's own entry** instead of being retyped beside it, so the two formats cannot drift apart again; `generate-file-reference-width.pin.test.ts` measures both against `driver-sql`'s constant, which is what stops the two halves from "meeting in the middle" at some third value.
16+
-**Nothing outside the family moved.** The `text` family, the reference types the file family used to share an arm with (`lookup` / `master_detail` / `user` / `tree`), `autonumber`, and every `--format sql` answer are byte-identical.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@objectstack/plugin-security": minor
3+
"@objectstack/spec": patch
4+
---
5+
6+
A package whose `manifest.permissions` carries the ADR-0025 capability grant is now NAMED when the audience-binding reconciler skips it, instead of vanishing; and both halves of the `permissions` key now point at each other in the spec (#18031).
7+
8+
`permissions` has two incompatible readings and the package registry stores both in the same slot. At the AUTHORING stage `ManifestSchema.permissions` is the capability grant a plugin requests — the legacy flat `string[]`, or the structured `{ services, hooks, network, fs }` block (ADR-0025 §3.2). At the ASSEMBLED stage the collection wins and the same key is the ADR-0090 `PermissionSet[]` collection (`AssembledPackageBodySchema`, ADR-0130 D4). `SchemaRegistry.installPackage` records whichever stage its caller handed it.
9+
10+
- **`collectDeclaredSuggestions` reports the reading it cannot use.** It wants the assembled one. Handed the authoring one it returned an empty list and logged nothing: the structured arm is an object, so `Array.isArray(manifest.permissions)` was false and the value never entered the loop; every member of the legacy arm is a bare string, so `consider`'s `typeof ps !== 'object'` line dropped all of them. A package declaring the other reading produced no `sys_audience_binding_suggestion` row, no prompt and no log. It now warns once per engine per package and arm, naming which arm it found, what is lost if the author meant permission sets (no admin is ever prompted to bind the set, and the deployment goes on looking healthy), and where the sets belong — the package's own `defineStack({ permissions: [ … ] })`, which is what the assembled body carries.
11+
- **`warn`, not `error`, and deliberately.** Nothing here claims to have persisted anything, so this is a functional degradation — a prompt that is not offered. Same reasoning, one step weaker, as the write-refusal report beside it, and the same sink (`SuggestionDeps['logger']`, which declares no `error`).
12+
- **Reported once per engine per package+arm.** The pass runs at boot, after every package-door `permission` publish and on every list call, while a manifest's shape is fixed for as long as that package is installed; an undeduplicated line would repeat on every console page load and be skimmed past.
13+
- **The spec half is declaration text only — no key, export, arm or accept-set moved.** `ManifestSchema.permissions` now says it describes the AUTHORING stage and names the assembled-stage counterpart; the stack collection `permissions` names the manifest-stage grant; and `InstalledPackageSchema.manifest` says it is the authoring STAGE rather than "whatever was stored", pointing at `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema` for the stage a `defineStack()` host installs.
14+
-**The union at the key was NOT widened, and must not be.** Widening a manifest key into a union of both stages is road C of #14242, rejected by name by the maintainer on 2026-09-02 in favour of road B — declare the assembled stage rather than widen the authoring one — because a union at the key makes neither stage checkable (Prime Directive #12). That ruling is why the fix here is a report and a cross-reference rather than a schema change.

content/docs/references/api/package-api.mdx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Installed package row whose manifest is the assembled package body
7272
| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project |
7373
| **name** | `string` || Human-readable package name |
7474
| **description** | `string` | optional | Package description |
75-
| **permissions** | `{ name: string; label?: string; description?: string; packageId?: string; … }[]` | optional | Permission Sets |
75+
| **permissions** | `{ name: string; label?: string; description?: string; packageId?: string; … }[]` | optional | Permission Sets — the ADR-0090 collection half of `permissions`; at the manifest/authoring stage the same key is the ADR-0025 capability grant instead (`ManifestSchema.permissions`) |
7676
| **objects** | `{ name: string; label?: string; pluralLabel?: string; description?: string; … }[]` | optional | Business Objects definition (owned by this package) |
7777
| **datasources** | `{ name: string; label?: string; driver: string; config: Record<string, any>; … }[]` | optional | External Data Connections |
7878
| **dependencies** | `Record<string, string>` | optional | Package dependencies |
@@ -177,7 +177,7 @@ Installed package with runtime lifecycle state
177177

178178
| Property | Type | Required | Description |
179179
| :--- | :--- | :--- | :--- |
180-
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Full package manifest |
180+
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Package manifest at the AUTHORING stage; a row installed by a `defineStack()` host carries the assembled body instead — see `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema` |
181181
| **status** | `Enum<'installed' \| 'disabled' \| 'installing' \| 'upgrading' \| 'uninstalling' \| 'error'>` | optional (default: `"installed"`) | Package state: installed, disabled, installing, upgrading, uninstalling, or error |
182182
| **enabled** | `boolean` | optional (default: `true`) | Whether the package is currently enabled |
183183
| **installedAt** | `string` | optional | Installation timestamp |
@@ -228,7 +228,7 @@ Installed package with runtime lifecycle state
228228

229229
| Property | Type | Required | Description |
230230
| :--- | :--- | :--- | :--- |
231-
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Full package manifest |
231+
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Package manifest at the AUTHORING stage; a row installed by a `defineStack()` host carries the assembled body instead — see `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema` |
232232
| **status** | `Enum<'installed' \| 'disabled' \| 'installing' \| 'upgrading' \| 'uninstalling' \| 'error'>` | optional (default: `"installed"`) | Package state: installed, disabled, installing, upgrading, uninstalling, or error |
233233
| **enabled** | `boolean` | optional (default: `true`) | Whether the package is currently enabled |
234234
| **installedAt** | `string` | optional | Installation timestamp |
@@ -253,7 +253,7 @@ Installed package with runtime lifecycle state
253253
| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project |
254254
| **name** | `string` || Human-readable package name |
255255
| **description** | `string` | optional | Package description |
256-
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions: legacy string[] or structured plugin block (ADR-0025 §3.2) |
256+
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions at the AUTHORING stage: legacy string[] or structured plugin block (ADR-0025 §3.2) — at the assembled stage the same key is the ADR-0090 `PermissionSet[]` collection instead (`AssembledPackageBodySchema`) |
257257
| **objects** | `string[]` | optional | Glob patterns for ObjectQL schemas files |
258258
| **datasources** | `string[]` | optional | Glob patterns for Datasource definitions |
259259
| **dependencies** | `Record<string, string>` | optional | Package dependencies |
@@ -316,7 +316,7 @@ Installed package row whose manifest is the assembled package body
316316
| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project |
317317
| **name** | `string` || Human-readable package name |
318318
| **description** | `string` | optional | Package description |
319-
| **permissions** | `{ name: string; label?: string; description?: string; packageId?: string; … }[]` | optional | Permission Sets |
319+
| **permissions** | `{ name: string; label?: string; description?: string; packageId?: string; … }[]` | optional | Permission Sets — the ADR-0090 collection half of `permissions`; at the manifest/authoring stage the same key is the ADR-0025 capability grant instead (`ManifestSchema.permissions`) |
320320
| **objects** | `{ name: string; label?: string; pluralLabel?: string; description?: string; … }[]` | optional | Business Objects definition (owned by this package) |
321321
| **datasources** | `{ name: string; label?: string; driver: string; config: Record<string, any>; … }[]` | optional | External Data Connections |
322322
| **dependencies** | `Record<string, string>` | optional | Package dependencies |
@@ -481,7 +481,7 @@ Install package request
481481
| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project |
482482
| **name** | `string` || Human-readable package name |
483483
| **description** | `string` | optional | Package description |
484-
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions: legacy string[] or structured plugin block (ADR-0025 §3.2) |
484+
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions at the AUTHORING stage: legacy string[] or structured plugin block (ADR-0025 §3.2) — at the assembled stage the same key is the ADR-0090 `PermissionSet[]` collection instead (`AssembledPackageBodySchema`) |
485485
| **objects** | `string[]` | optional | Glob patterns for ObjectQL schemas files |
486486
| **datasources** | `string[]` | optional | Glob patterns for Datasource definitions |
487487
| **dependencies** | `Record<string, string>` | optional | Package dependencies |
@@ -605,7 +605,7 @@ Upgrade package request
605605
| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project |
606606
| **name** | `string` || Human-readable package name |
607607
| **description** | `string` | optional | Package description |
608-
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions: legacy string[] or structured plugin block (ADR-0025 §3.2) |
608+
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions at the AUTHORING stage: legacy string[] or structured plugin block (ADR-0025 §3.2) — at the assembled stage the same key is the ADR-0090 `PermissionSet[]` collection instead (`AssembledPackageBodySchema`) |
609609
| **objects** | `string[]` | optional | Glob patterns for ObjectQL schemas files |
610610
| **datasources** | `string[]` | optional | Glob patterns for Datasource definitions |
611611
| **dependencies** | `Record<string, string>` | optional | Package dependencies |
@@ -691,7 +691,7 @@ Resolve dependencies request
691691
| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project |
692692
| **name** | `string` || Human-readable package name |
693693
| **description** | `string` | optional | Package description |
694-
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions: legacy string[] or structured plugin block (ADR-0025 §3.2) |
694+
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions at the AUTHORING stage: legacy string[] or structured plugin block (ADR-0025 §3.2) — at the assembled stage the same key is the ADR-0090 `PermissionSet[]` collection instead (`AssembledPackageBodySchema`) |
695695
| **objects** | `string[]` | optional | Glob patterns for ObjectQL schemas files |
696696
| **datasources** | `string[]` | optional | Glob patterns for Datasource definitions |
697697
| **dependencies** | `Record<string, string>` | optional | Package dependencies |

content/docs/references/api/protocol.mdx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ Disable package response
782782

783783
| Property | Type | Required | Description |
784784
| :--- | :--- | :--- | :--- |
785-
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Full package manifest |
785+
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Package manifest at the AUTHORING stage; a row installed by a `defineStack()` host carries the assembled body instead — see `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema` |
786786
| **status** | `Enum<'installed' \| 'disabled' \| 'installing' \| 'upgrading' \| 'uninstalling' \| 'error'>` | optional (default: `"installed"`) | Package state: installed, disabled, installing, upgrading, uninstalling, or error |
787787
| **enabled** | `boolean` | optional (default: `true`) | Whether the package is currently enabled |
788788
| **installedAt** | `string` | optional | Installation timestamp |
@@ -826,7 +826,7 @@ Enable package response
826826

827827
| Property | Type | Required | Description |
828828
| :--- | :--- | :--- | :--- |
829-
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Full package manifest |
829+
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Package manifest at the AUTHORING stage; a row installed by a `defineStack()` host carries the assembled body instead — see `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema` |
830830
| **status** | `Enum<'installed' \| 'disabled' \| 'installing' \| 'upgrading' \| 'uninstalling' \| 'error'>` | optional (default: `"installed"`) | Package state: installed, disabled, installing, upgrading, uninstalling, or error |
831831
| **enabled** | `boolean` | optional (default: `true`) | Whether the package is currently enabled |
832832
| **installedAt** | `string` | optional | Installation timestamp |
@@ -1494,7 +1494,7 @@ Get package response
14941494

14951495
| Property | Type | Required | Description |
14961496
| :--- | :--- | :--- | :--- |
1497-
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Full package manifest |
1497+
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Package manifest at the AUTHORING stage; a row installed by a `defineStack()` host carries the assembled body instead — see `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema` |
14981498
| **status** | `Enum<'installed' \| 'disabled' \| 'installing' \| 'upgrading' \| 'uninstalling' \| 'error'>` | optional (default: `"installed"`) | Package state: installed, disabled, installing, upgrading, uninstalling, or error |
14991499
| **enabled** | `boolean` | optional (default: `true`) | Whether the package is currently enabled |
15001500
| **installedAt** | `string` | optional | Installation timestamp |
@@ -1891,7 +1891,7 @@ Install package request
18911891
| **scope** | `Enum<'cloud' \| 'system' \| 'project'>` | optional (default: `"project"`) | Deployment scope: cloud \| system \| project |
18921892
| **name** | `string` || Human-readable package name |
18931893
| **description** | `string` | optional | Package description |
1894-
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions: legacy string[] or structured plugin block (ADR-0025 §3.2) |
1894+
| **permissions** | `string[] \| { services?: string[]; hooks?: string[]; network?: string[]; fs?: string[] }` | optional | Required permissions at the AUTHORING stage: legacy string[] or structured plugin block (ADR-0025 §3.2) — at the assembled stage the same key is the ADR-0090 `PermissionSet[]` collection instead (`AssembledPackageBodySchema`) |
18951895
| **objects** | `string[]` | optional | Glob patterns for ObjectQL schemas files |
18961896
| **datasources** | `string[]` | optional | Glob patterns for Datasource definitions |
18971897
| **dependencies** | `Record<string, string>` | optional | Package dependencies |
@@ -1928,7 +1928,7 @@ Install package response
19281928

19291929
| Property | Type | Required | Description |
19301930
| :--- | :--- | :--- | :--- |
1931-
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Full package manifest |
1931+
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Package manifest at the AUTHORING stage; a row installed by a `defineStack()` host carries the assembled body instead — see `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema` |
19321932
| **status** | `Enum<'installed' \| 'disabled' \| 'installing' \| 'upgrading' \| 'uninstalling' \| 'error'>` | optional (default: `"installed"`) | Package state: installed, disabled, installing, upgrading, uninstalling, or error |
19331933
| **enabled** | `boolean` | optional (default: `true`) | Whether the package is currently enabled |
19341934
| **installedAt** | `string` | optional | Installation timestamp |
@@ -2130,7 +2130,7 @@ Installed package with runtime lifecycle state
21302130

21312131
| Property | Type | Required | Description |
21322132
| :--- | :--- | :--- | :--- |
2133-
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Full package manifest |
2133+
| **manifest** | `{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }` || Package manifest at the AUTHORING stage; a row installed by a `defineStack()` host carries the assembled body instead — see `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema` |
21342134
| **status** | `Enum<'installed' \| 'disabled' \| 'installing' \| 'upgrading' \| 'uninstalling' \| 'error'>` | optional (default: `"installed"`) | Package state: installed, disabled, installing, upgrading, uninstalling, or error |
21352135
| **enabled** | `boolean` | optional (default: `true`) | Whether the package is currently enabled |
21362136
| **installedAt** | `string` | optional | Installation timestamp |

0 commit comments

Comments
 (0)