Skip to content

Commit 272bd65

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-29520-sql-driver-codemod-pilot
# Conflicts: # packages/drivers/driver-sql/src/sql-driver.ts
2 parents 5e2cef7 + 2ea6042 commit 272bd65

161 files changed

Lines changed: 11495 additions & 7456 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.

‎.changeset/29366-field-patches-key-grammar.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ Skills-docs: updated here — `content/docs/data-modeling/object-extensions.mdx`
1919
- **Before.** `fieldPatches: { Industry: { label: 'Sector' } }` parsed in `defineStack`, was stored by the save door and booted: the registry fold named the patch once in the log and applied nothing, and `os validate` refused it only when the target object was in the same stack.
2020
- **Now.** One `invalid_key` issue at `fieldPatches.Industry`, naming the grammar and an example. The key schema is the one `ObjectSchema.fields` reads, hoisted to one constant, so the two cannot drift.
2121
- **Unchanged.** A snake_case key parses as before, and a `__c` key is still refused by the `__c` rule with its own prescription.
22-
- **Who sees it.** No extension in this repository's examples, skills, docs or templates writes such a key. A stored `object_extension` row that carries one no longer parses, so the sync names the row (`INVALID_METADATA`) and registers none of it, its `fields` included; its next save is refused until the key is renamed. `os migrate meta --stored` does not list such a row beforehand: the stored pass has no stack collection for an `object_extension` row and hands it back untouched, so check each stored extension's `fieldPatches` keys against the grammar before upgrading.
22+
- **Who sees it.** No extension in this repository's examples, skills, docs or templates writes such a key. A stored `object_extension` row that carries one no longer parses, so the sync names the row (`INVALID_METADATA`) and registers none of it, its `fields` included; its next save is refused until the key is renamed. `os migrate meta --stored` lists such a row beforehand, one TODO for each refused key at `fieldPatches.KEY`, and leaves it as stored for its owner to rename.
2323

2424
**FROM → TO.** `fieldPatches: { Industry: { label: 'Sector' } }` → `fieldPatches: { industry: { label: 'Sector' } }`: key each patch by the name of the field the extended object declares (the key of its `fields` entry), never by its label. The upgrade guide entry is `object-extension-field-patch-key-grammar-refused`.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
---
4+
5+
fix(metadata-protocol): the layered read of a form view naming a field its object lacks now carries the form-field-unknown warning
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
`GET /api/v1/meta/view/NAME?layers=true` judged its `_diagnostics` with the schema alone, so a published form view whose section names a missing field (or an undeclared field group) read `{ valid: true }` there, while `GET /api/v1/meta/view/NAME` of the same view carried the lint's warning. Studio's item editor reads the layered answer when no draft is pending, so the author of a published form view saw no warning.
11+
12+
The layered read now runs the same form-layout pass on its `effective` body that the single-item read runs, against the object the registry serves for each name the view spells. The warnings land at the same paths, and `valid` stays the schema's answer. A form naming only real fields still reads `{ valid: true }`.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/lint': minor
4+
'@objectstack/platform-objects': patch
5+
---
6+
7+
feat(spec): a flow start input can declare that it is the id of a record of a named object (`objectName`)
8+
9+
Clause-②: yes (widening)
10+
Metadata-protocol: added
11+
12+
`FlowVariableSchema` declares one optional key, `objectName`: on an input (`isInput: true`), it says the input's value is the id of one record of the named object, e.g. `{ name: 'accountId', type: 'text', isInput: true, objectName: 'crm_account' }`. The key is **planned**: no door reads it yet, and `os validate` warns on every flow that writes it (`liveness-planned-property`). The contract it states, for the start doors to enforce: every door that starts the flow by name (the trigger route, an action of type flow over REST and MCP `run_action`, a declared endpoint of type flow) loads a declared input from that object in the caller's own scope before the run exists; an id the caller cannot read there is refused `404 RECORD_NOT_FOUND`, and a value that is not an id is refused `400 VALIDATION_FAILED` naming the input. An input without the declaration keeps the visible-read rule: it is judged as a record only where the flow's own definition reads it as a record id.
13+
14+
- **Spec.** `objectName` is an object name (snake_case) and is refused on a variable that is not an input, since no caller's value reaches one. `type` stays the data type: the variable holds the id. A `reference` or `object` key on a flow variable is still refused, and the refusal now names `objectName`. The flow form's variables repeater gains the column, labelled in the metadata-form bundles (en, zh-CN, ja-JP, es-ES).
15+
- **Lint.** `flow-input-unknown-object`: a declared object the stack does not define is a warning (it may come from another installed package), and an error at the runtime publish gate when the write's package closure does not reach it, the verdict `flow-trigger-unknown-object` gives a trigger object. A `sys_` name is not judged.
16+
17+
This is the contract half: the start doors read the declaration in a later change, and until then no door loads a declared input, so a start that hands the input an id the caller cannot read still runs. Nothing to do for existing flows.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@object-ui/fields': patch
3+
'@objectstack/spec': patch
4+
'@objectstack/platform-objects': patch
5+
---
6+
7+
feat(fields, spec): a `referenceNameVisible` lookup or user field shows the name of a record the reader may not open as plain text, and the field form offers the key
8+
9+
Clause-②: no
10+
Metadata-protocol: none
11+
12+
The console half of `FieldSchema.referenceNameVisible`, after its server half began serving the name:
13+
14+
- **Lookup and user cells, and the record page.** When `$expand` answers a reference as `{ id, name, readRefused: true }` (the reader may read the record carrying the field, but not the record it points at), the lookup cell and the user cell draw `name` as plain text: no link to the record, no hover card, and no read of that record or of its object. A `multiple: true` field draws one text chip per such element, and its other elements keep their faces. The floor name `Record #` with the id is drawn exactly as served. The record page's field grid draws these values through the same cells.
15+
- **Unchanged.** A field without the key, and a target the reader can read, keep today's faces: a readable record is a link, and a bare id the reader's own read answers "not found" for keeps the restricted-record placeholder.
16+
- **Field form.** The `field` metadata form gains a `referenceNameVisible` switch ("Show Name When Unreadable"), shown on `lookup` and `user` fields only, the types the schema accepts the key on. Its label and help text are translated for `zh-CN`, `ja-JP` and `es-ES`.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec): declare the nine remaining served `features` keys on the `GET /api/v1/auth/config` response
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: added
9+
10+
`AuthFeaturesConfigSchema` gains nine optional keys that the server already serves in `features` and the console already reads. The contract did not declare them, so a client that parsed the response through `GetAuthConfigResponseSchema` lost all nine, because a plain object schema strips keys it does not know.
11+
12+
- **Flags (booleans):** `multiOrgEnabled` (an organization wall is enforced; default-on, so absent reads as on), `degradedTenancy` (isolation was asked for but is not active), `oidcProvider` (the embedded OIDC authorization server is wired; default-on), `deviceAuthorization` (the device-code CLI login is wired), `admin` (the admin plugin is wired, forced on by SCIM).
13+
- **Values:** `tenancyPosture` (`single` | `group` | `isolated`, the same vocabulary as `TenancyPostureSchema`), `audiencePosture` (`invite_only` | `email_domain` | `open`, the same vocabulary as `AUDIENCE_POSTURES`), `termsUrl` and `privacyUrl` (the legal links under the login card; absent when the operator suppressed them).
14+
- None carries a default: a key the server does not send stays absent.
15+
- A new test holds the schema's declared keys equal to `PUBLIC_AUTH_FEATURES` plus `PUBLIC_AUTH_CONFIG_NON_FLAG_KEYS` (the retired `passkeys` / `magicLink` tombstones excepted), so the next served and classified key cannot be left undeclared.
16+
- The development-only top-level `devSeedAdmin` hint stays undeclared on purpose: it carries the seeded admin's password and no production deployment serves it.
17+
18+
What to do: nothing. Every payload accepted before is accepted unchanged. A client that typed the response from `@objectstack/spec` (for example through `@objectstack/client`'s re-exported `AuthFeaturesConfig`) now sees the nine keys as optional.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@object-ui/data-objectstack': patch
3+
---
4+
5+
perf(data-objectstack)!: an object route reads that object's saved views (`GET /meta/view?object=`), not every view of every object (#29550)
6+
7+
Clause-②: no (narrowing) — `listViews` and `listViewOverrides` no longer list a stored `view` row that carries no `viewKind` or no top-level `object` (a legacy bare spec, a `{ list }` wrapper, a row bound only through `data.object`). The draft-preview read applies the same rule. Nothing is widened.
8+
Metadata-protocol: none
9+
10+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed, renamed or re-shaped, and no export, signature or answer shape changes. The adapter's two view readers now apply the rule the server's one-object view read (ADR-0017 section 3.5) and the view write door (#7741) already apply: a row is bound to an object by `object` plus `viewKind`. There is no key for a migration to rewrite; a row stored before the write door refused unbound rows is re-bound by saving the view again. -->
11+
12+
**BREAKING** read narrowing in `@object-ui/data-objectstack` (`ObjectStackAdapter.listViews`, `ObjectStackAdapter.listViewOverrides`), which feed the Console's saved-view tabs and per-view settings.
13+
14+
- **Before.** On every object route, both readers downloaded the whole published view list (`GET /api/v1/meta/view`, every view of every object; 7 KB on app-crm) and kept one object's rows in the browser, matching a row on `data.object` or `object`, with or without `viewKind`.
15+
- **Now.** While nothing on the page has loaded the whole list, they read `GET /api/v1/meta/view?object=OBJECT`, the read ADR-0017 declares for the view switcher, once for both readers. Once something has loaded the whole list, or while its read is on the wire (Studio, the MetadataProvider, a packaged app's object scope), they answer from it with no request of their own. Both paths keep the same rows in the same order: `viewKind` set, `object` naming the object, sorted by `order` and then `name`. The draft-preview read (`listViews` with `previewDrafts`) keeps its own `?preview=draft` request and narrows its rows by the same rule.
16+
- **Order.** Saved-view tabs follow the contract's order (`order`, then `name`). Before, they followed the order of the server's whole list, so tabs with no `order` could appear in a different order.
17+
- **Who sees it.** A deployment holding a `view` row stored before the write door refused rows without `object` and `viewKind` (#7741). That row was a saved-view tab and a per-view setting before, and is neither now. The server's one-object read never served it. Views the Console saves today carry both fields, and none of the rows on a fresh app-crm or showcase deployment were affected.
18+
19+
**The remedy.** Open the view in the Console and save it again: the save writes `object` and `viewKind`, and the view is listed again.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): the stored pass reads a stored `object_extension` row as an `objectExtensions` entry, so conversions replay over it and `os migrate meta --stored` lists a `fieldPatches` key no field name can be
6+
7+
Clause-②: no (no accept set moves; the stored pass and the `--stored` listing widen)
8+
9+
Metadata-protocol: none
10+
11+
`applyConversionsToStoredItem` wraps a stored row in the stack collection its type maps to, and returned a row untouched when its type mapped to none. `object_extension` (ADR-0146 D10) mapped to none, so no conversion that walks `objectExtensions` ever ran over a stored extension row at a rehydration seam, and no refused-shape detector could list one: `os migrate meta --stored` read every stored extension as already on protocol.
12+
13+
- **Now.** A stored `object_extension` row is read as one `objectExtensions` entry, beside `translation` in the stored-only map; the manifest-collection maps still carry no `objectExtensions`, because a stack's extensions merge into their targets rather than registering as named items. A stored extension with a `time` default written with a `Z` suffix, for example, is now served without it, with the conversion's notice, as an object row already was.
14+
- **Listed.** `os migrate meta --stored` lists each `fieldPatches` key the extension's own parse refuses as no field name (`Industry`, `Annual Revenue`, `annual-revenue`): one TODO at `fieldPatches.KEY` under the row, naming the extended object, leaving the row as stored. Rename each listed key to the field it meant before upgrading. The upgrade guide entry `object-extension-field-patch-key-grammar-refused` says so instead of naming a manual check.
15+
- **Unchanged.** A canonical extension row passes through by reference, every other row type converts exactly as before, and no schema accepts or refuses anything it did not.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
feat(spec): `ConnectorConnectionState` declares `redirectUri`, the deployment's derived OAuth redirect URI
6+
7+
Clause-②: yes (widening)
8+
Metadata-protocol: none
9+
10+
An administrator connecting an `authorization_code` connector instance with an installer-supplied client (`clientSource: 'installer'`) must register this deployment's redirect URI with the vendor before there is a client to connect with. The server derives that URI, but no answer carried it. `ConnectorConnectionState` (`@objectstack/spec/contracts`) now declares one optional key for it:
11+
12+
- **`redirectUri?: string`** is the absolute URL of `GET /api/v1/automation/connectors/oauth/callback`, derived by the server and fixed per deployment: the automation plugin's `connectorOAuthRedirectUri` option, else that path under `OS_AUTH_URL`, else under `OS_BASE_URL`. It is never taken from a request, and no connection request takes it.
13+
- **Present** on the connection read (`getConnectorConnection`) whenever the server can derive it, whatever the connection's `status`. **Absent** when the deployment has no base to derive it from (a connect is then refused `503`), and never guessed from the request's origin. Only an `authorization_code` instance has a connection, so no other grant carries it.
14+
- **Declared ahead of the service.** This release adds the contract only: until the automation service sets the key, no answer carries it. Nothing an implementer or caller writes today stops compiling.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/metadata-protocol': patch
3+
---
4+
5+
fix(metadata-protocol): a `.custom` pair's published override of a dashboard, report or email template no longer sits in the registry under the base's envelope
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
A published override of a base's dashboard, report or email template, saved in the base's `<base>.custom` pair, was registered in the registry's bare slot wearing the BASE's artifact envelope (`_packageId` = the base, `_provenance: 'package'`). The base's per-package layer, which the base's export reads, then listed the override beside the base's own item. Views already stayed out of that slot. The override is now kept out of it for these three types as well, and is still served from its stored row. The by-name read, the effective list and the layered read serve exactly what they served before. The base's per-package layer lists only the base's own item, and the pair's layer lists only the override. A registry read that names the base's item directly answers the base's own item. A row of a package that ships its own artifact of the name keeps its entry as before. Nothing to migrate: a restart re-hydrates the registry under the new rule.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
'@objectstack/plugin-sharing': patch
3+
'@objectstack/plugin-approvals': patch
4+
---
5+
6+
fix(plugins)!: business-unit recipients and approvers reach every member past 10,000 rows, a failed business-unit member read is refused loudly instead of answered with nobody, and a failed `resolveAs` expansion says so
7+
8+
Clause-②: no (narrowing)
9+
Metadata-protocol: none
10+
11+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed, renamed or re-shaped and no metadata changes: the business-unit expansions and the primary-business-unit backfill read lists they already read, to completion, and refuse a read that already failed. No author has anything to rewrite. -->
12+
13+
**BREAKING** only where a `sys_business_unit_member` read fails: the business-unit expansions used to answer nobody (an empty list) then, and now refuse.
14+
15+
This extends the family the team, position and role fixes closed to the last membership object, `sys_business_unit_member`. Its reads still read once with a 10,000-row bound and turned any read error into nobody.
16+
17+
- **Every member, nobody else.** In plugin-sharing, `BusinessUnitGraphService.expandUnitMembers` (every `business_unit` sharing recipient) and `BusinessUnitGraphService.expandUsers` (every `unit_and_subordinates` recipient) now read in pages of 10,000 ordered by id until the members are exhausted. In plugin-approvals, the `department` / `business_unit` / `bu` approver and an `expression` approver with `resolveAs: 'department'` do the same. Past 10,000 members, the members past the 10,000th row used to get no grant and no approval slot. They now do.
18+
- **The primary-business-unit backfill projects every primary membership.** The boot backfill of `sys_user.primary_business_unit_id` used to scan the `is_primary` rows once at the same bound, so users past the 10,000th row were never projected and nothing said so. It now pages to completion. A scan that fails, or a page the driver returned without honouring the id cursor, writes nothing and logs why (a failed scan logged before too, now with the consequence).
19+
- **Smaller units are unchanged.** A unit or subtree with fewer than 10,000 members issues one read, as before, and answers the same set.
20+
- **Loud, never short or empty.** A member read that fails, or a page the driver returned without honouring the id cursor, now throws.
21+
- Sharing, whole-rule passes: the expansion rejects, so that rule's reconcile or revoke pass fails, its callers log their existing per-rule warning, and the grants it already holds stay. Before, a failed read answered `[]`, so the retired-recipient revoke a business-unit graph write triggers took the rule's grants away from every member.
22+
- Approvals: the approver slot falls back to its literal (`department:ID`) and logs `could not be expanded — a directory or read-filter lookup failed` with its `error`. Before, a failed read logged "expanded to nobody", indistinguishable from an unstaffed unit.
23+
- Ownership queues: a `business_unit` or `unit_and_subordinates` queue specification whose member read fails still contributes nobody, the posture the queue declares for a failed member-table read, and now logs it once.
24+
- **A failed `resolveAs` expansion says so.** An `expression` approver whose `resolveAs` value could not be expanded (a `department`, `position` or `team` read failed) kept its `KIND:VALUE` literal slot with no log line. It now logs the same `could not be expanded` warning the static approver types log, with the `error` and the `expression` source.
25+
- **A missing object is still no members.** A stack that never registers, or never creates, `sys_business_unit_member` keeps answering no members through it, and the backfill has nothing to project. That is judged by `isObjectAbsentError(error, 'sys_business_unit_member')`; any other error stays loud.
26+
27+
**The remedy.** None is needed. A refusal means the membership table could not be read: fix that read, and the next expansion succeeds. A failed read is never cached.

0 commit comments

Comments
 (0)