Skip to content

Commit 3330725

Browse files
Merge branch 'main' into claude/issue-15204-s5-objectui-pin
2 parents ecd4ed7 + b7cd1af commit 3330725

181 files changed

Lines changed: 7842 additions & 2224 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
"@objectstack/runtime": minor
3+
"@objectstack/core": minor
4+
"@objectstack/platform-objects": minor
5+
"@objectstack/service-automation": minor
6+
---
7+
8+
The security catalog's activation door switches a position or permission set the environment authored, and a ledger write it refuses answers with the status its code means
9+
10+
Clause-②: yes (widening)
11+
12+
- **The door accepts environment-authored items** (`@objectstack/runtime`). `POST /api/v1/security/_activation/:type/:name` switched a position or permission set off and on only when a package shipped it. FROM: an item saved through the metadata door (`PUT /api/v1/meta/position/:name`, `PUT /api/v1/meta/permission/:name`) was refused `503` with `{ code: 'VALIDATION_FAILED', message: 'Package is required' }`, and nothing was written. TO: it is switched like a packaged one, `200`, and the resolver honours the row: a switched-off position stops granting its permission sets, and a switched-off set stops granting by every path. Who may call the door is unchanged (`manage_metadata`; the platform operator under a `group` or `isolated` posture), and so are its other refusals (an undeclared name `404`, a type with no switch or an audience anchor `400`, the last administrator `403`).
13+
- **A refused ledger write keeps its code and its status together** (`@objectstack/runtime`, ADR-0112). FROM: the door kept a thrown `code` but answered `503` whenever the error declared no status, so a record validation refusal was served as `503 VALIDATION_FAILED`. TO: the door answers through the dispatcher's one thrown-error rule: a record validation refusal is `400 VALIDATION_FAILED`, a hook's refusal keeps its own pair (the last-admin guard's `403 PERMISSION_DENIED`), and an error that declares no status is `503 SERVICE_UNAVAILABLE`.
14+
- **The ledger row of an item no package ships carries no package** (`@objectstack/platform-objects`, `@objectstack/core`). `sys_metadata_activation.package_id` is no longer required. It is empty exactly when no package ships the item. It is not part of the row identity (`metadata_type`, `name`), and no reader decides anything from it. The column was already nullable on disk (ADR-0113: `required` is a write-time check), so there is nothing to migrate.
15+
- **One spelling for "no package"** (`@objectstack/core`). `MetadataActivationRow.packageId` is optional. FROM: `packageId: string`, with `''` the only way to say "no package". TO: omit `packageId` for an item no package ships, and the store writes no value. `ObjectStoreMetadataActivationStore.setActive` refuses `packageId: ''` with a `TypeError` before it reads or writes anything, and `list()` returns `packageId` only when the row names a package. If your code passes `''`, omit the key instead.
16+
- **The flow row follows the shared row** (`@objectstack/service-automation`). `FlowActivationRow.packageId` is optional to match. Every flow row still names its package, because the flow toggle refuses a flow no package ships before it writes.
17+
- **The action activation door's answer for an action no package ships changes; the action is still refused.** `POST /api/v1/actions/_activation/:object/:action` still passes `''` as the package of such an action. The write is still refused and still writes nothing, but the store's `TypeError` refuses it now, with the store's sentence. FROM: `503 VALIDATION_FAILED`. TO: `503 SERVICE_UNAVAILABLE`. An environment-authored action could not be switched on `main` before this change and cannot after it.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/plugin-security': minor
4+
'@objectstack/rest': minor
5+
---
6+
7+
feat(plugin-security,rest,spec)!: the permission-set "Discard Overlay" action, its REST route and its contract member are retired; no door creates the overlay it discarded, and a database holding one is refused at cold boot (ADR-0131 cutover, ADR-0048)
8+
9+
Clause-②: no (narrowing)
10+
11+
<!-- adr-0087: registered security-permission-set-overlay-discard-retired -->
12+
13+
**BREAKING** — a narrowing of the public surface: one HTTP route, one Setup action and one optional contract member are removed. Shipped as `minor` under the launch-window convention for breaking changes.
14+
15+
The action deleted the environment-wide `sys_metadata` row that shadowed a package-declared permission set, then re-projected the set's `sys_permission_set` row. Neither half has anything left to do:
16+
17+
- No door creates such an overlay. The metadata door refuses a save over a packaged permission set on every topology, whatever `OS_METADATA_WRITABLE` says. The data door refuses an edit of one (clone it instead). The data API cannot write `sys_metadata`. A package that declares a name the environment holds is refused at install.
18+
- An overlay stored before those refusals makes the cold boot refuse the deployment with `422 NAMESPACE_CONFLICT`, so a server that could answer the route never holds one.
19+
- The authorization resolver reads the security catalog, not `sys_permission_set` rows, so the re-projected row granted nothing.
20+
21+
### What was removed
22+
23+
| removed | what to use instead |
24+
| --- | --- |
25+
| `POST /api/v1/security/permission-sets/:id/discard-overlay` | `os migrate security-catalog-overlays` with the flags and environment the deployment boots with lists every overlay row with no server running; `--apply` deletes them (a history tombstone per row). |
26+
| the **Discard Overlay** action on `sys_permission_set` in Setup | the same offline step. To keep a customization of a packaged set, clone it under a new name (the **Clone** action) and assign the clone. |
27+
| `ISecurityService.discardPermissionSetOverlay` and `PermissionSetOverlayDiscardResult` (`@objectstack/spec/contracts`) | nothing: delete the call and the import. `tsc` names each one. |
28+
| `discardPermissionSetOverlay`, `PermissionSetNotFoundError`, `PermissionSetOverlayStateError` and the `PermissionSetOverlayDiscardDeps` / `PermissionSetOverlayDiscardResult` types (`@objectstack/plugin-security`) | nothing: delete the import. |
29+
30+
**The one-line fix:** stop calling the route or the member, and clear any overlay offline with `os migrate security-catalog-overlays --apply` before the first boot on this release.
31+
32+
`ERROR_CODE_LEDGER['@objectstack/plugin-security']` no longer lists `INVALID_STATE` or `NOT_FOUND`: the removed action was their only producer in this package. Both codes stay registered under `@objectstack/rest`, and the union of codes is unchanged.
33+
34+
The permission-set drift diagnostic no longer names the action in its `overlay_shadow` detail, and the boot reading of shadowed packaged sets names the offline step instead.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/verify': patch
3+
---
4+
5+
fix(verify): the RLS probe persona is provisioned through the registry — `provisionRlsProbePersona` no longer mints a `sys_permission_set` row
6+
7+
Clause-②: no
8+
9+
`provisionRlsProbePersona` used to find-or-create a `sys_permission_set` row named `verify_rls_probe` and grant it. Under ADR-0131 D3/D4 a permission set's one home is the registry: the authorization resolver reads the grant's `permission_set` name and takes the definition from the security catalog, never from a row. So a row this function minted was a copy of a definition nothing read.
10+
11+
It now:
12+
13+
- asks the security catalog (`securityCatalogReaderOf`, the read the resolver itself uses) whether `verify_rls_probe` is registered. `rlsProbeSecurity(config)` registers it at boot;
14+
- reads the id of the row the boot already seeded for that registered set, because `sys_user_permission_set.permission_set_id` is still a required column;
15+
- writes the one grant, carrying both columns.
16+
17+
It throws, writing nothing, when the catalog does not serve the probe set or the boot seeded no row for it. `objectstack verify --rls` reports either as a degraded probe persona and fails the run, as it already does for any provisioning failure. Fix for a caller that hits it: boot the stack with `rlsProbeSecurity(config)` as the `security` option of `bootStack`.

‎.changeset/22161-lint-slice-5-one-line.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ Clause-②: no
1414

1515
A verdict no longer repeats what the finding's `where` and `path` already name: the validation rule and its object, the JSON Pointer of a `format`, the measure or dimension name, the source of an uncompilable `regex` (the `fix:` line still quotes it). A refusal the verdict quotes is quoted to its verdict. `TimeRelativeTriggerSchema`'s refusal of a descriptor is quoted for ONE issue (an unrecognized key first) and the rest are counted, `(and N more)`; ajv's metaschema refusal is quoted to its first violation, with the rest counted; a `@objectstack/spec` unknown-key refusal is quoted to the key and the schema's rename (`Did you mean …?`), without the wrong-layer prescription and the history sentence that follow it; and when no form of an `<ObjectChart aggregate>` `groupBy` union matches, the verdict keeps the forms whose complaint is about the value's content. A measure or dimension over a column its join chain reaches names a `joined object` (it read "(reached through this dataset's join chain)"). The `fix` (the CLI's `fix:` line, the runtime issue's `hint`), every rule id, severity and `path`, and what each rule accepts or refuses are unchanged. A tool that matched the old message text should match on `rule` and `path` instead.
1616
- **`os explain <rule-id>` takes these 15 ids**, for example `os explain flow-trigger-unroutable`. It prints the reasoning the verdicts no longer carry: the engine's trigger routing chain, the record-trigger token grammar and the time-relative routing predicate, what nothing reports at run time, and where the publish gate refuses the flow; the inbound api secret (ADR-0041) and why the engine refuses to register a flow without one; the write path's fail-open skip of a rule whose `regex` or `schema` does not compile, the runtime's ajv environment, and what ajv does with an unregistered `format` name; each aggregate's accepted field types (the `AGGREGATE_FIELD_TYPE_COMPATIBILITY` rows, written out), what a JSON-stored column is, and why analytics refuses to group by one; how a forwarded `<ObjectChart drillDown>` or `aggregate` refusal is quoted and which keys the schema answers with a prescription instead of a rename (`schedule` and `runAs` on a time-relative descriptor, `drilldown`, `mode`, `report`, `view` and `sort` on a drill block, five aggregate and two `groupBy` keys); what an unprovisioned anchor is; why a `record:*` block renders empty on a react page; and why an unparseable react source is a warning. Paragraphs shared across ids are one text, printed under every id they explain. The `rule:` line under each of these findings now ends with `` — `os explain <rule-id>` for … ``. The no-argument listing and its `--json` `rules` array list the 15 ids, and so does the unknown-id error's `Rules with an explanation:` line. `RULE_EXPLANATIONS` in `@objectstack/lint` gains the 15 entries.
17-
- **Where the new text prints.** On the CLI, all 15 ids: `os validate`, `os build` (and `os compile`, which `os dev` runs on every compile), `os lint`, `os verify`, and the scaffold check `os init` and `os generate` run print the new `message` on the text face, and `os validate --json` and `os build --json` carry it in their `errors` and author-time `issues`. At the runtime publish gate (Studio, REST `/meta`, MCP): a `flow` write carries the five flow ids (all errors, refusing an active flow's publish), a `dataset` write the two dataset ids, and an `object` write the three validation-rule ids — the 422 issue's `message` and the refusal log line under `OS_ALLOW_UNLINTED_METADATA_WRITES` change. Each issue's `hint` is unchanged.
17+
- **Where the new text prints.** On the CLI, all 15 ids: `os validate`, `os build` (and `os compile`, which `os dev` runs on every compile), `os lint`, `os verify`, and the scaffold check `os init` runs print the new `message` on the text face, and `os validate --json` and `os build --json` carry it in their `errors` and author-time `issues`. At the runtime publish gate (Studio, REST `/meta`, MCP): a `flow` write carries the five flow ids (all errors, refusing an active flow's publish), a `dataset` write the two dataset ids, and an `object` write the three validation-rule ids — the 422 issue's `message` and the refusal log line under `OS_ALLOW_UNLINTED_METADATA_WRITES` change. Each issue's `hint` is unchanged.
1818
- **Never at the runtime gate:** the five `react-*` ids. Their rule runs at that door only for a flow write, whose snapshot carries no pages, so they speak only on the CLI doors above.

‎.changeset/22161-lint-slice-6-one-line.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ Clause-②: no
1616

1717
A verdict no longer repeats what the finding's `where` already names: the view that wires an action (`action-dispatch-contract-mismatch`). The unprovisioned-anchor ids print the same one-clause cause the other converted anchor rules print (`'owner_id' is an injected column with no storage on external object 'x'`), and the two virtual-entry ids (`sort-field-unsortable`, `searchable-field-unsearchable`) state the storage fact in one shared wording. `searchable-field-unsearchable` quotes at most three names of an object's declared set, then `(and N more)`. A retired component type (`user:profile`, `element:filter`, `element:form`, `ai:chat_window`) is quoted to the head of its prescription in `RETIRED_PAGE_COMPONENT_TYPES` (`` `element:filter` was removed in @objectstack/spec 17 (ADR-0049) ``), and the verdict says the parse refuses the node by name; the whole prescription is the parse door's refusal of the same name, which `os validate` and `os build` print. `filter-preset-comparand` opens with the first sentence of the refusal the schema door shares (`"last_30_days" is a dashboard date-range PRESET name, not a filter value`), then names the operator and the preset's `{date-macro}` window. The `fix` (the CLI's `fix:` line, the runtime issue's `hint`), every rule id, severity and `path`, and what each rule accepts or refuses are unchanged. A tool that matched the old message text should match on `rule` and `path` instead.
1818
- **`os explain <rule-id>` takes these 12 ids**, for example `os explain sort-field-unknown`. It prints the reasoning the verdicts no longer carry: why an unknown sort field breaks a view's first fetch and every load after it, and which list-view surfaces the sort and search rules walk and skip; what a `formula` field's lack of storage does to an ORDER BY and to a search; what an unprovisioned anchor is and what sorting or searching one measured; how a stale `searchableFields` entry narrows a search or falls through to the auto-default set, and how a list view's narrowing reaches the runtime as the `$searchFields` override; how a metadata-form predicate path is resolved against the edited schema, why a dead predicate fails open, and why the right side of `==` / `!=` is a literal; how the two bulk wirings call an action and why nothing refuses a mismatch at run time; which component types the vocabulary closes and where a retired type's prescription lives; and where a date-range preset name is understood and what each layer does with a bare one. Paragraphs shared across ids are one text, printed under every id they explain. The `rule:` line under each of these findings now ends with `` — `os explain <rule-id>` for … ``. The no-argument listing and its `--json` `rules` array list the 12 ids, and so does the unknown-id error's `Rules with an explanation:` line. `RULE_EXPLANATIONS` in `@objectstack/lint` gains the 12 entries.
19-
- **Where the new text prints.** On the CLI, all 12 ids: `os validate`, `os build` (and `os compile`, which `os dev` runs on every compile), `os lint`, `os verify`, and the scaffold check `os init` and `os generate` run print the new `message` on the text face, and `os validate --json` and `os build --json` carry it in their `errors` and author-time `issues`. At the runtime publish gate (Studio, REST `/meta`, MCP), the 422 issue's `message` and the refusal log line under `OS_ALLOW_UNLINTED_METADATA_WRITES` change for: the three `searchable-field-*` ids on a `view`, `object` or `flow` write; the three `sort-field-*` ids on a `view` or `flow` write; the three `predicate-*` ids on a `view` write; and `filter-preset-comparand` on a `dashboard`, `view`, `object`, `page`, `flow` or `report` write. Each issue's `hint` is unchanged.
19+
- **Where the new text prints.** On the CLI, all 12 ids: `os validate`, `os build` (and `os compile`, which `os dev` runs on every compile), `os lint`, `os verify`, and the scaffold check `os init` runs print the new `message` on the text face, and `os validate --json` and `os build --json` carry it in their `errors` and author-time `issues`. At the runtime publish gate (Studio, REST `/meta`, MCP), the 422 issue's `message` and the refusal log line under `OS_ALLOW_UNLINTED_METADATA_WRITES` change for: the three `searchable-field-*` ids on a `view`, `object` or `flow` write; the three `sort-field-*` ids on a `view` or `flow` write; the three `predicate-*` ids on a `view` write; and `filter-preset-comparand` on a `dashboard`, `view`, `object`, `page`, `flow` or `report` write. Each issue's `hint` is unchanged.
2020
- **Never at the runtime gate:** `component-type-unknown`, which runs on the CLI doors only, and `action-dispatch-contract-mismatch`, whose rule runs at that door only for a `flow` write, whose snapshot carries no actions to judge, so both speak only on the CLI doors above.

0 commit comments

Comments
 (0)