Skip to content

Commit 3d4e3b5

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/project-thread-eqmkj9
2 parents 9ca57be + dbca3ef commit 3d4e3b5

39 files changed

Lines changed: 2129 additions & 1784 deletions
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+
refactor(metadata-protocol): the metadata list reads, the audit read and the UI-view read move out of `protocol.ts` through the protocol host
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
No behaviour change. The bodies of `getMetaItems`, `getMetaItemsForExecution`, `getMetaItemsPerPackage`, `readFlattenedMetaItems`, `auditMetaItem` and `getUiView` now live in `protocol-meta-list-reads.ts` and `protocol-meta-audit-ui-view.ts`, reached through the per-instance host (`protocol-host.ts`). Each method keeps its docblock and signature on `ObjectStackProtocolImplementation`, and each moved body differs from the method body only by `this.` reading `host.`. The published `index.d.ts` (ESM and CJS) is byte-identical to the one `main` builds before this change. Nothing to migrate.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
'@object-ui/plugin-designer': patch
3+
---
4+
5+
refactor(plugin-designer)!: retire `useConfirmDialog` and `VersionHistory` from the entry, and delete the designers' dead collaboration code
6+
7+
Clause-②: no (narrowing)
8+
Metadata-protocol: none
9+
10+
<!-- adr-0087: not-required (runtime-export-retired @object-ui/plugin-designer#useConfirmDialog, @object-ui/plugin-designer#VersionHistory) the retired operands are a React hook and a React component in a published ui package's entry that nothing in the workspace or in hotcrm imported; no packages/spec schema, registered node type, ADR-0087 conversion surface or stored shape names them, so there is nothing for `objectstack migrate meta` to rewrite -->
11+
12+
**BREAKING** for a host that imports `useConfirmDialog` or `VersionHistory` from `@object-ui/plugin-designer`. Neither had an importer anywhere in the workspace or in hotcrm, so the Console and every `@object-ui/*` package are unaffected.
13+
14+
- The `VersionHistory` timeline module is deleted: nothing in the package rendered it. The `designer.versionHistory` translation key stays in the locale packs for now, with no reader.
15+
- `useConfirmDialog` stays inside the package, because the designers use it for their delete prompts. Only the entry stops publishing it.
16+
- The collaboration code that became unreachable once the previous release stopped exporting `CollaborationProvider` is deleted. That covers the provider module, the always-null `useCollaboration()` branches in the page, data model, process and report designers, and their "N users" / "Connected" toolbar chips. Nothing outside the package could mount the provider, so no designer behaves differently.
17+
18+
What leaves, and what to write instead (neither has a replacement export):
19+
20+
- FROM `useConfirmDialog` TO no replacement: render your own confirm prompt with `ConfirmDialog` (still exported) and local `useState`.
21+
- FROM `VersionHistory` TO no replacement: render the version list in your own component. The designers never showed one.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/core': patch
3+
---
4+
5+
fix(core): the fellow-org peer list keeps every member the first membership read returned, even when the completion read sees fewer
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
In an organization of 1,000 members or more, the resolver reads the membership twice: a first read bounded at 1,000 rows, then a completion read in id order. The completion's rows used to replace the first read's, so a completion that saw fewer members than the first read (an older snapshot on another connection) left `org_user_ids` shorter than the first read alone would have, and those members vanished from ordinary members' `sys_user` reads with no signal. The two reads are now unioned. Both are filtered to the active organization, so the union cannot add anyone outside it. No read is added or changed.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): `email.defaultTemplateContext` now names `baseUrl` beside `appName` as a key the runtime resolves itself
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The `.describe()` and TSDoc on `EmailServiceConfigSchema.defaultTemplateContext` said every key is passed through as written except `appName`. Since the email plugin started resolving the deployment address for templates, `baseUrl` is resolved too, so the text now names both keys and gives `baseUrl` its chain: `OS_AUTH_URL` (legacy `BETTER_AUTH_URL`), then `OS_BASE_URL`, then the value written in `defaultTemplateContext.baseUrl`, then the local `http://localhost` default on the serve port, with any trailing slash trimmed. A send's own data still wins over both keys.
11+
12+
Text only: the key stays a free-form record, and nothing an author can write changes.

‎content/docs/references/system/email-config.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ const result = EmailAddressConfigSchema.parse(data);
9898
| **queueDelivery** | `boolean` | optional | Deliver through the durable sys_job_queue instead of inline (or OS_EMAIL_QUEUE_ENABLED env). Default false. Reuses `retries` as the queue attempt budget; requires a queue service and sys_email persistence, else the boot fails |
9999
| **options** | `Record<string, any>` | optional | Provider-specific extras. smtp: host (required) / port / secure / user / password, mirroring OS_EMAIL_SMTP_HOST / _PORT / _SECURE / _USER / _PASSWORD. postmark: messageStream |
100100
| **appName** | `string` | optional | Product name templates interpolate as the appName variable — OS_APP_NAME env wins, then this, then defaultTemplateContext.appName, then the top-level config appName, then "ObjectStack". Also seeds the placeholder no-reply sender when no defaultFrom is configured |
101-
| **defaultTemplateContext** | `Record<string, any>` | optional | Free-form render context merged into every sendTemplate() call, under the per-call data. Passed through unchanged except appName, which is resolved by its own chain — OS_APP_NAME and the appName key both override the value written here |
101+
| **defaultTemplateContext** | `Record<string, any>` | optional | Free-form render context merged into every sendTemplate() call, under the per-call data. Passed through unchanged except appName and baseUrl, each resolved by its own chain — OS_APP_NAME and the appName key both override appName written here; baseUrl takes OS_AUTH_URL, then OS_BASE_URL, then the value written here, then the local http://localhost default, with no trailing slash |
102102

103103
### Nested Shape: `EmailServiceConfig.defaultFrom`
104104

@@ -122,7 +122,7 @@ const result = EmailAddressConfigSchema.parse(data);
122122
| **persist** | `boolean` | optional | Persist to sys_email (default true) |
123123
| **queueDelivery** | `boolean` | optional | Deliver through the durable sys_job_queue instead of inline (or OS_EMAIL_QUEUE_ENABLED env). Default false. Reuses `retries` as the queue attempt budget; requires a queue service and sys_email persistence, else the boot fails |
124124
| **appName** | `string` | optional | Product name templates interpolate as the appName variable — OS_APP_NAME env wins, then this, then defaultTemplateContext.appName, then the top-level config appName, then "ObjectStack". Also seeds the placeholder no-reply sender when no defaultFrom is configured |
125-
| **defaultTemplateContext** | `Record<string, any>` | optional | Free-form render context merged into every sendTemplate() call, under the per-call data. Passed through unchanged except appName, which is resolved by its own chain — OS_APP_NAME and the appName key both override the value written here |
125+
| **defaultTemplateContext** | `Record<string, any>` | optional | Free-form render context merged into every sendTemplate() call, under the per-call data. Passed through unchanged except appName and baseUrl, each resolved by its own chain — OS_APP_NAME and the appName key both override appName written here; baseUrl takes OS_AUTH_URL, then OS_BASE_URL, then the value written here, then the local http://localhost default, with no trailing slash |
126126
| **options** | `{ host?: string; port?: integer; secure?: boolean; user?: string; … }` | optional | Provider-specific extras, non-secret members only. smtp: host / port / secure / user, mirroring OS_EMAIL_SMTP_HOST / _PORT / _SECURE / _USER (the password is OS_EMAIL_SMTP_PASSWORD only). postmark: messageStream |
127127

128128
### Nested Shape: `StackEmailConfig.defaultFrom`

‎docs/qa/platform-checklist/FOLLOW-UPS.md‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,3 +874,68 @@ and `readonlyHint` (`ui/packages/i18n/src/namespaces/studio/en.ts`, rendered by
874874
`BuilderLanding.environmentOverlayCopy-12171.test.ts` pins that phrase. The door is now
875875
Customize, which writes into the base's `.custom` pair. Reach: source-read only. The finding
876876
is in the slice's report for the seat to card.
877+
878+
## 15. Scoped run 2026-11-02 — the live journey and the first run of the ADR-0146 items (#28579 slice 3)
879+
880+
**Not a sweep.** This slice adds one live spec, `ui/e2e/live/studio-customize-journey.spec.ts`,
881+
and makes one checklist-test run over §14's seven new items on a fresh showcase built from
882+
`main` `128681f4`. The run record is the slice's `qa-run` issue. The spec walks a view's
883+
Customize, Restore package original, Add field `region__c` and the uninstall refusal on the
884+
read-only showcase. It sits in the required `Live E2E UI` job's allowlist. Three items name it
885+
in `automated.ref`: the display item's view variant, the `__c` item and the ensure door's 422.
886+
The flow override, the counts and marks, the redirects and the ceremony have no live spec, so
887+
§14a still holds for them. `studio-authoring.access-permission-set-lifecycle` gains an
888+
`automated` block that is scoped to acceptance[5].
889+
890+
### 15a. Assertion defects — fixed in this diff, each a revision with its history row
891+
892+
- `studio-authoring.package-customization-counts-and-marks` acceptance[2] said a writable
893+
package is never asked. The All metadata area does ask every package except a pair, and its
894+
own pin asserts that read. Only the landing asks for read-only bases alone.
895+
- `platform-core.setup-metadata-redirects-to-studio` had three gaps:
896+
- It did not count the console's app-segment rewrite (`/apps/setup` →
897+
`/apps/com.objectstack.setup`). With the canonical segment typed, a metadata URL reaches
898+
Studio in one hop.
899+
- A package leg's object item opens its Data pillar (ADR-0146 D1), and the item did not
900+
allow for that.
901+
- Its datasource clause named the jsdom stubs' test ids instead of the real pages.
902+
- `studio-authoring.package-extension-field-c` steps[6] named an object without the package's
903+
namespace prefix. The server answers that with 422 `NAMESPACE_PREFIX` before the `__c` judge
904+
runs.
905+
906+
### 15b. Product findings — reported, not filed here
907+
908+
Each one is in the slice's report for the seat to card. The reach shown is the one measured on
909+
the run's boot.
910+
911+
1. **The base's export carries a dashboard or report override.** After a published override of
912+
`showcase_ops_dashboard` (or `showcase_hours_by_status`) in `com.example.showcase.custom`,
913+
`GET /api/v1/packages/com.example.showcase/export` holds the item with the edited label and
914+
the `overrides` key, instead of the base unchanged. A view override does not leak this way.
915+
This contradicts ADR-0146 D8's export rule. D8 also says the export of the app's surface
916+
serves the override, and the ADR has to say which export each rule means. It fails
917+
`studio-authoring.package-customize-restore-display` acceptance[4]. It was reproduced through
918+
Studio and over the API.
919+
2. **A published base-field patch is not served on the object.** A label in the extension's
920+
`fieldPatches` shows only on `/meta/object/showcase_task/layers`. The read naming no package,
921+
the read naming the base, and the list all serve the shipped label. This contradicts D10 and
922+
`registry-extension-field-patches.test.ts`, which pins every read serving the patch. Reach:
923+
any `fieldPatches` label on `showcase_task`. It is an off-clause probe of the `__c` item.
924+
3. **Studio's cross-package list of a type cannot be opened.** Typed directly, even with
925+
`?package=all`, `/studio/~all/metadata/object` and `/studio/~all/metadata/flow` are rewritten
926+
to `?package=com.example.showcase` and then to that package's leg. The cause is the `?package=`
927+
repair in `ResourceListPage.tsx`, which runs whenever the area names no package. It fails the
928+
redirects item's acceptance[0] (for `/apps/setup/metadata/object`) and acceptance[3] (the
929+
package-less Packaged automation bookmark).
930+
4. **Studio does not know a system-scope package is read-only.** `parsePackages` drops system
931+
and cloud packages, so `com.objectstack.plugin-auth` opens with no read-only chip and no
932+
Customize. An edit to `system_overview` is refused with 403 `NOT_OVERRIDABLE` in an alert. Over
933+
the API, that dashboard is customizable in its pair: ensure answers 201 with namespace
934+
`sys_custom` and `^3`, the declared PUT and the publish land, and the reads serve the override.
935+
5. **The uninstall refusal's remedy describes the closed model.** The 422
936+
`WRITABLE_PACKAGE_REQUIRED` text (`CUSTOMISE_BESIDE_REMEDY` in
937+
`packages/runtime/src/domains/packages.ts`) still says to save an item under its own name.
938+
Since D11 that package-less save is refused with 403. Reach: `DELETE
939+
/api/v1/packages/com.example.showcase`.
940+
6. **The pair's extension has no name in the list.** The All metadata `object_extension` list
941+
draws the pair's extension row with "—" in the name column. This is a display issue only.

‎docs/qa/platform-checklist/areas/automation.json‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2858,7 +2858,7 @@
28582858
"title": "Customize a read-only base's flow in Studio: the pair's item under the base's name, declaring the override, is armed in place of the base in one reload, keeps the base's ledger switch, lets a waiting run finish on the base, and Restore re-arms the base — New similar stays a separate copy under a new name",
28592859
"since": "v18",
28602860
"status": "active",
2861-
"revision": 1,
2861+
"revision": 2,
28622862
"priority": "P1",
28632863
"surface": "mixed",
28642864
"personas": [
@@ -2939,7 +2939,7 @@
29392939
],
29402940
"automated": {
29412941
"kind": "unit",
2942-
"ref": "ui/packages/app-shell/src/views/studio-design/AutomationsPillar.customizeFlow-28570.test.tsx + AutomationsPillar.newSimilar-28567.test.tsx (jsdom: acceptance[0], [4] and [5]'s console calls against a mocked server) + packages/services/service-automation/src/flow-sync-one-precedence.test.ts (engine layer: the override is armed by every step and its removal re-arms the base; the ledger is the base's; 'with N-12: a run suspended on the base finishes on the base' — corroborates acceptance[1]–[4], drives no HTTP door). the browser journey has no live spec yet: the e2e is owed by #28579 slice 3 (the card's Wanted 3), so until it lands every browser and api clause is the run's live leg"
2942+
"ref": "ui/packages/app-shell/src/views/studio-design/AutomationsPillar.customizeFlow-28570.test.tsx + AutomationsPillar.newSimilar-28567.test.tsx (jsdom: acceptance[0], [4] and [5]'s console calls against a mocked server) + packages/services/service-automation/src/flow-sync-one-precedence.test.ts (engine layer: the override is armed by every step and its removal re-arms the base; the ledger is the base's; 'with N-12: a run suspended on the base finishes on the base' — corroborates acceptance[1]–[4], drives no HTTP door). the live spec of #28579 slice 3 (ui/e2e/live/studio-customize-journey.spec.ts) customizes a view and an object, not a flow, so every browser and api clause is the run's live leg"
29432943
},
29442944
"source": [
29452945
"packages/services/service-automation/src/flow-precedence.ts#resolveFlowPrecedence (the declared .custom rank, #28412)",
@@ -2956,6 +2956,12 @@
29562956
"date": "2026-11-02",
29572957
"change": "new item (ADR-0146 N-11 #28412 / PRs #28693 and #28770, N-18 #28570 / PR #28824, N-22 #28567 / PR #28668): a flow customized in Studio is the pair's item under the base's name declaring the override, armed in place of the base, keeping the base's ledger switch, with a waiting run finishing on the base and Restore re-arming it; New similar is the separate copy under a new name. Specimen showcase_budget_approval. Read from source at base eb123b7ccc",
29582958
"ref": "#28579"
2959+
},
2960+
{
2961+
"revision": 2,
2962+
"date": "2026-11-02",
2963+
"change": "automated no longer says the e2e is owed by #28579 slice 3: the slice 3 live spec (ui/e2e/live/studio-customize-journey.spec.ts) does not drive a flow, so the live leg stays the run's. No clause, step or fixture changed",
2964+
"ref": "#28579"
29592965
}
29602966
]
29612967
}

0 commit comments

Comments
 (0)