Skip to content

Commit c23cfb3

Browse files
feat(spec)!: split the assembled-stage package API declarations off ./api into @objectstack/spec/api-assembled (#20052)
Fixes #18576 Clause-②: yes (narrowing) Ruling `5714238181` (batch #145 item 1, letter **B**, 「同意,其他也同意」): split `./api` so the browser-facing half no longer carries the assembled-package (datasource/driver) declarations. This PR does the export-usage measurement first, finds that no objectui consumer uses a moved name, and then makes the cut. ## Patch round 1 (after the at-tier PASS `5824354899` on `4c6babc4ae`) Its non-blocking recommendations, and nothing else. Head is now `12822df4fb`. - **Clause-② arm.** The spec changeset's line and line 2 of this body now read `Clause-②: yes (narrowing)`. The diff widens one surface (the new `./api-assembled`) and narrows another (13 names leave `./api`), which is the `yes (narrowing)` combination in `scripts/pm/clause2-line.mjs`. `check-adr-0087-registration` now reads the changeset as `[BREAKING+bang+clause-②-narrowing]`. The client changeset carries no Clause line and needs none: its published types are unchanged, and only their import specifier moved. - **Stale pointers the cut made false**, each fixed at the source: - `packages/spec/browser-reachable-entries.json`: the `./api` row's `notAnOutlier` now says one of **sixteen** unjudged entries (the ledger's `unjudged` array holds 16). The fifteen/twelve/fourteen figures of its weight scan are anchored to the tree that scan ran on, and `./api-assembled` is named as the sixteenth, not in that scan. No gate reads this prose: `check-browser-reachable-entries.ts` reads only the section keys. - `packages/spec/src/kernel/package-registry.zod.ts`: `AssembledInstalledPackageSchema` is cited at `../api/package-api-assembled.zod.ts`. - `packages/spec/src/api/package-lifecycle.zod.ts`: `PackageApiContracts.rollbackPackage` is cited at `./package-api-assembled.zod.ts`. The same header also said the route-ledger resolver looks names up "only in `@objectstack/spec/api`", which is false since the split, and now names both entries. `content/docs/references/api/package-lifecycle.mdx` was regenerated by `gen:schema` + `gen:docs`. - Found by the extra sweep: `packages/spec/src/api/index.ts` (the book-tree re-export comment said the resolver "searches only `@objectstack/spec/api`", now both entries). Also `packages/spec/src/api/package-api.zod.ts`, which had two `{@link InstalledPackageAtEitherStageSchema}` references to a symbol no longer in that file's scope; they are now a code span plus the sibling file. - Reviewed and left: the `responseSchema` field docs of the rest, auth, i18n and storage route ledgers say names resolve against `@objectstack/spec/api` exports. That is still true of every row they hold, and they name no moved symbol. The historical migration-registry entries and `.changeset/*` stock record the past and were not edited. - **CI red on the intermediate push `05383b5e9c`, fixed:** `Type Check · source gates` failed `check:docs` with `content/docs/references/api/package-lifecycle.mdx (out of date)`. That was the pointer edit landing before its page was regenerated. `12822df4fb` carries the generated page, and `check:docs` answers `226 generated files in sync with packages/spec`. ## 1. Export-usage measurement (first deliverable) objectui at `62597c5880`, which **is** the pinned `.objectui-sha` (`62597c588072636e9c30ea35b3d89b1e46fd765d`). Every non-test `import … from '@objectstack/spec/api'` / `import('@objectstack/spec/api')` in objectui: | objectui file | kind | names | declaring module in spec | reaches the datasource/driver tree? | | --- | --- | --- | --- | --- | | `core/src/utils/column-sortability.ts` | value + type | `FIELD_UNSORTABLE_VIRTUAL_TYPE`, `FIELD_SORTABLE_UNPROVISIONED_ANCHOR`; types `FieldSortability`, `ObjectSortability` | `api/sortability.zod.ts` | no (closure 50 modules, no stack/datasource/driver) | | `data-objectstack/src/metadata-client.ts` | value + type | `GetMetaItemLayeredResponseSchema`; types `GetMetaItemLayeredResponse`, `PublishPackageDraftsResponse`, `RuntimeAuthoringIssue` | `api/protocol.zod.ts` | no (closure 79) | | `app-shell/src/views/metadata-admin/clientValidation.ts` | dynamic value | `ApiEndpointSchema` | `api/endpoint.zod.ts` | no (closure 29) | | `plugin-chatbot/src/usePendingActions.ts` | type only | `ApproveAiPendingActionResponse`, `RejectAiPendingActionResponse` | `api/protocol.zod.ts` | no | | `react/src/utils/error-message.ts` | type only | `ApiError` | `api/contract.zod.ts` | no | | `types/src/data.ts` | type only | `ExportJobStatus`, `ExportFormat`, `ImportJobStatus`, `ImportRowResult`, `ImportWriteMode` | `api/export.zod.ts` | no | Re-scan beyond the card's six: two more type-only sites — `data-objectstack/src/index.ts` (`ApiError`) and `types/src/index.ts` (`export type * as API from '@objectstack/spec/api'`, a type-only namespace re-export). The card called all six "value imports"; three of them are type-only and cost a bundle nothing. **Which `./api` names pull the tree.** An esbuild metafile walk of every module `src/api/index.ts` re-exports: exactly one, `package-api.zod.ts`, reached `stack.zod` → `data/datasource.zod` → nine `data/driver/*` modules, through one import (`RecordStagePackageBodySchema`). Inside it, exactly five declarations need that import: `AssembledInstalledPackageSchema`, `InstalledPackageAtEitherStageSchema`, `ListInstalledPackagesResponseSchema`, `GetInstalledPackageResponseSchema`, and `PackageApiContracts` (which names the two responses). **None of them is imported anywhere in objectui** (0 hits across the whole objectui tree; control `GetMetaItemLayeredResponseSchema`: 14 files). Stop conditions: neither fired. No objectui site imports a moved name, so no objectui import changes and no `@object-ui/*` API change is needed for the six sites to keep resolving. One transitive note: `@object-ui/types`'s type-only `API` namespace loses the moved names when the pin bumps. objectui itself reads none of them through it. ## 2. The cut - `src/api/package-api.zod.ts` → the five declarations (and their `X` / `XParsed` types) move verbatim to `src/api/package-api-assembled.zod.ts`. `package-api.zod.ts` no longer imports `../stack.zod`. - New entry **`@objectstack/spec/api-assembled`** (`src/api-assembled/index.ts`, one `export *`), carrying the `browser` condition (its graph still reaches the pg URL grammar, so it gets the existing `swapServerOnlyGrammarArm` twin). `./api` **drops** its `browser` condition: nothing in its graph links a server-only module any more, and `tsup.config.ts` says the conditioned list must equal the poisoned set. - **Name, against the existing subpaths.** Every other subpath is one protocol domain (`./data`, `./api`, …) plus one fine-grained hyphenated entry (`./meta-spelling`). This one is a *packaging* split of the API protocol, named by the property that sets its members apart: they carry the **assembled** package body. `api-` keeps it next to `./api` in every listing. It is not called `server`: the browser-reachability gate still requires it to be bundler-feasible (its browser condition), and the browser SDK's `.d.ts` imports a type from it, so "server-only" would be a claim nothing enforces. - The protocol **category** did not change. `scripts/lib/split-entries.ts` declares `api-assembled` as a split entry of `api`, so JSON Schema ids stay `api/NAME` (the published `json-schema/api/` ids are unchanged; `json-schema.manifest/`, `authorable-surface/`, `declaration-map/` byte-identical). `build-docs` spells the page's import line from the entry that exports it, `declaration-map` joins the split entry's origins into its home, and a missing `json-schema/api-assembled/` is a declared state that expires if the tree stops matching. - In-repo importers of the moved names, all moved here: `packages/client/src/index.ts` (type import), `packages/client/src/return-type-precision.test.ts`, `packages/runtime/src/domains/packages-read-delete-response-conformance.test.ts`, `packages/spec/src/api/package-api.test.ts`. The route-ledger `responseSchema` field (`packages/runtime/src/route-ledger.ts`) now names an export of either API entry, and its resolver test resolves against both. It also pins that the two entries share no name. `examples/**` and `apps/**`: 0 importers. **Does an existing import path stop resolving? Yes.** The 13 names (5 declarations plus their types) no longer resolve from `@objectstack/spec/api`. That is **major** in the ruling's terms. Under the launch-window convention (`check-changeset-no-major`) it ships as `@objectstack/spec` **minor** with a **BREAKING** banner, a FROM → TO table, and one ADR-0087 marker (`registered api-assembled-entry-split`, a new D3 semantic entry; an import path is not metadata, so there is no D2 conversion). `@objectstack/client` gets a `patch`, because its published `.d.ts` now imports the type from the new entry. Runtime publishes nothing changed: its built `dist/` holds 0 occurrences of the edited ledger text, against the positive control `HttpDispatcher` in 4 files. ## 3. Size proof Base `fc6ddb87a4` vs head `12822df4fb` (the consumer probes read byte-identically at `5f845af5c4`), both built from source. esbuild 0.28.2, `platform: browser`, conditions `browser` + `import`, minified, gzip -9, spec resolved through its `exports` map: | probe | base gzip | head gzip | delta | | --- | ---: | ---: | ---: | | objectui `core/src/utils/column-sortability.ts`, bundled as-is | 311,124 | 166,529 | **−46.5%** | | `metadata-client.ts`'s value import, verbatim | 311,182 | 166,616 | −46.5% | | `clientValidation.ts`'s dynamic `import()`, verbatim (pulls the whole namespace) | 391,171 | 327,732 | −16.2% | | whole `./api` namespace (contrast) | 387,421 | 324,465 | −16.3% | | `./api` entry bundle (esm) | 612,813 | 469,800 | −23.3% | `./api` source graph: 171 → 120 modules. The stack, datasource and driver modules went from 11 to 0. `pg-connection-string` is linked 4× in the base node bundle and 0× in the head. For history, #17535 measured the regression as 132,121 → 261,221 on the narrowest consumer. The remaining gap to 166,529 is the rest of `./api` growing since then, not the assembled tree. The `browser-reachable-entries.json` `./api` weight row is **replaced** with these readings, and its ablation was re-taken at `5f845af5c4`: 2 problems (zod ×2), down from 4 (zod ×2 plus `pg-connection-string` ×2). ## 4. Verification (head `12822df4fb`) Gates were derived by `dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `12822df4fb`: 126 families, the same set as round 0. All 126 were run on that head with exit codes recorded, and `--ran` answered `126 derived famil(ies) accounted for — 126 run, 0 NOT-MEASURED (a DERIVED zero …)`. All 126 exited 0; `check:pm-dispatch-gates` took 933.7s (1925 self-test cases). The dist-reading gates ran against a full `turbo run build --filter='./packages/*' --filter='./packages/*/*'` of this head (72 tasks). Highlights: - `check:docs`: `226 generated files in sync with packages/spec`, which is the CI red on `05383b5e9c`, fixed. `check:generated`, `check:api-surface`, `check:export-origins`, `check:declaration-map`, `check:authorable-surface`, `check:browser-reachable-entries` (5 browser-conditioned subpaths, positive control held), `check:entry-nameability`, `check:dual-source-exports`, `check:exported-any`, `check:llms-txt`, `check:skill-examples`: exit 0. - `check:dual-build-cjs-loads`, `check:published-files`, `check:lean-entry-closure`, `check:docs-spec-enumerations`, `check:quick-reference-counts`, `check:type-check-debt`, `check:nul-bytes`: exit 0. - `check-adr-0087-registration --base origin/main`: `.changeset/18576-api-assembled-entry-split.md [BREAKING+bang+clause-②-narrowing] registered api-assembled-entry-split`. `check-changeset-no-major`: `This diff introduces no major bump`. - Tests at this head: `@objectstack/spec` local project 534 files / 15665 passed. Spec repo project 30 of 31 files / 492 tests passed (the 31st is under NOT MEASURED). The client (7) and runtime (17) targeted tests and the spec/client/runtime typechecks were green at `4c6babc4ae`; this round changes no code they compile (comments, prose, a ledger string, one generated page). - **Pin + ablation** (round 0, `5f845af5c4`; this round touches neither the pin nor the graph). `src/api/api-entry-graph.pin.test.ts` turned red, 2 failed and 2 passed, when `src/api/index.ts` re-exported `./package-api-assembled.zod`. Restored blob `404586349b` equals HEAD. - **Reverse verification** (round 0, rebuilt `.d.ts`). `ListInstalledPackagesResponseSchema` from `/api` fails with `TS2724 … Did you mean 'InstallPackageResponseSchema'?`. From `/api-assembled` it compiles with 0 diagnostics. - PR mergeability read at `12822df4fb`: `mergeable: true`. Main moved about 20 commits, touching `packages/spec` (including the generated `src/migrations/registry.ts`) but none of the lines this diff owns, so the branch was not merged. CI's merge ref covers it. NOT MEASURED, declared to CI: `scripts/build-schemas-check-mode.test.ts` (spec repo project). This round it queued three times and never acquired the shared verify lock (3 × 540s, other seats' suites and builds held it). In round 0 it did not finish inside the foreground window. This round's diff does not touch `build-schemas.ts` or anything it reads. ## Acceptance notes - The objectui half (move the six sites nowhere, since none uses a moved name; bump the pin after the spec release) is the spec seat's card at release time, per the ruling. It is not opened here. - `cloud` is NOT MEASURED (not attached). Its consumers of the 13 moved names are unknown. - Main moved during the work. The only overlap with this derivation is `scripts/engine-double-contract.pinned.json` (unrelated to this diff), so the branch was not merged. CI's merge ref covers it. --- _Generated by [Claude Code](https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9b8c74c commit c23cfb3

43 files changed

Lines changed: 1530 additions & 718 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec)!: split the assembled-stage package API declarations off `@objectstack/spec/api` into the new `@objectstack/spec/api-assembled` entry (#18576)
6+
7+
**BREAKING** — five Package API declarations, with their types, are no longer exported from `@objectstack/spec/api`. They are exported, unchanged, from the new entry `@objectstack/spec/api-assembled`.
8+
9+
A `major`-class change — an existing import path stops resolving for these names — recorded as `minor` under the launch-window convention. Maintainer ruling on #18576, batch #145 item 1, letter B, 「同意,其他也同意」.
10+
11+
**Why.** These five declarations embed the ASSEMBLED package body, which reaches the whole metadata vocabulary and, behind it, the datasource declaration and the driver-config validators. While they were declared inside `@objectstack/spec/api`, that tree was part of every bundle of the entry, and the entry ships as one self-contained bundle that a consumer's tree-shaking can recover little of. A browser module that imports two string constants from `@objectstack/spec/api` paid for all of it. Measured on the splitting PR (esbuild 0.28.2, `platform: browser`, conditions `browser` + `import`, minified, gzip -9), for objectui's `@object-ui/core` `column-sortability.ts`, which imports only those two constants: **311,124 → 166,529 bytes gzipped (−46.5%)**. The `./api` entry bundle itself goes from 612,813 to 469,795 bytes gzipped, and its module graph no longer reaches `stack.zod`, the datasource declaration or any driver-config module. `./api` also no longer needs a `browser` export condition, since nothing in its graph links the server-only pg URL grammar any more; the condition moves to `./api-assembled`.
12+
13+
### FROM → TO
14+
15+
| removed from `@objectstack/spec/api` | import instead from |
16+
| --- | --- |
17+
| `AssembledInstalledPackageSchema`, `AssembledInstalledPackage`, `AssembledInstalledPackageParsed` | `@objectstack/spec/api-assembled` |
18+
| `InstalledPackageAtEitherStageSchema`, `InstalledPackageAtEitherStage`, `InstalledPackageAtEitherStageParsed` | `@objectstack/spec/api-assembled` |
19+
| `ListInstalledPackagesResponseSchema`, `ListInstalledPackagesResponse`, `ListInstalledPackagesResponseParsed` | `@objectstack/spec/api-assembled` |
20+
| `GetInstalledPackageResponseSchema`, `GetInstalledPackageResponse`, `GetInstalledPackageResponseParsed` | `@objectstack/spec/api-assembled` |
21+
| `PackageApiContracts` | `@objectstack/spec/api-assembled` |
22+
23+
**The one-line fix: change the import path.**
24+
25+
```ts
26+
// before
27+
import { ListInstalledPackagesResponseSchema } from '@objectstack/spec/api';
28+
// after
29+
import { ListInstalledPackagesResponseSchema } from '@objectstack/spec/api-assembled';
30+
```
31+
32+
The compiler finds every site: `TS2305` ("Module '"@objectstack/spec/api"' has no exported member …"), or `TS2724` with a did-you-mean when a similarly named export exists — measured on the splitting PR, `ListInstalledPackagesResponseSchema` from `/api` answers `TS2724 … Did you mean 'InstallPackageResponseSchema'?`, which is NOT the name you want. Nothing else changes: every schema parses and refuses exactly what it did, `PackageApiContracts` keeps its four entries, and the JSON Schema ids are the same (`json-schema/api/AssembledInstalledPackage.json` and its three siblings are still published under `api/`, and still documented in the API reference). Every other Package API declaration — the two read doors' request schemas, the install / uninstall / upgrade / rollback shapes and `PackageApiErrorCode` — stays on `@objectstack/spec/api`. If you only use those, or any other `/api` contract, you need to do nothing.
33+
34+
⚠️ **Out-of-repo consumers are NOT MEASURED beyond objectui.** Inside this repository the moved names had four importers (the client's type import, one runtime conformance test, the client's return-type pins and the spec's own unit test), all moved in the same PR. objectui at the pinned `.objectui-sha` imports none of the moved names from anywhere; its six browser-shipped files that import `@objectstack/spec/api` keep resolving every name they use, from `/api` itself. `@object-ui/types` re-exports `@objectstack/spec/api` as a type-only `API` namespace, which loses the moved names with this release; objectui itself references none of them through it. The `cloud` repository was not measured.
35+
36+
The ADR-0087 D3 semantic entry `api-assembled-entry-split` carries the judgement: an import path is TypeScript source, not metadata, so there is no source a D2 conversion could rewrite.
37+
38+
Clause-②: yes (narrowing)
39+
40+
<!-- adr-0087: registered api-assembled-entry-split -->
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@objectstack/client': patch
3+
---
4+
5+
fix(client): take `InstalledPackageAtEitherStage` from `@objectstack/spec/api-assembled` (#18576)
6+
7+
`@objectstack/spec` moved the declarations that embed the assembled package body — `InstalledPackageAtEitherStage` among them — off `@objectstack/spec/api` into the new `@objectstack/spec/api-assembled` entry. The client's `packages.list` / `packages.get` return types (and their scoped twins) name that type, so the published declarations now import it from the new entry. The return types are the same type as before; nothing a caller writes changes. It is a type-only import, erased from the client's bundle.

‎content/docs/deployment/troubleshooting.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ import { FieldSchema } from '@objectstack/spec/data';
344344
import { ErrorResponseSchema } from '@objectstack/spec/api';
345345
```
346346

347-
Available subpaths (the `./*` entries of the package's `exports` map, in its order): `data`, `system`, `kernel`, `ai`, `automation`, `api`, `ui`, `contracts`, `integration`, `security`, `studio`, `marketplace`, `qa`, `identity`, `shared`, `meta-spelling`.
347+
Available subpaths (the `./*` entries of the package's `exports` map, in its order): `data`, `system`, `kernel`, `ai`, `automation`, `api`, `api-assembled`, `ui`, `contracts`, `integration`, `security`, `studio`, `marketplace`, `qa`, `identity`, `shared`, `meta-spelling`.
348348

349349
---
350350

‎content/docs/getting-started/quick-reference.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ AI/ML capabilities - agents, skills, tools, MCP exposure, RAG, and cost tracking
126126
| **[Usage](/docs/references/ai/usage)** | `usage.zod.ts` | AIUsageRecord, TokenUsage | AI usage and cost tracking |
127127
| **[Solution Blueprint](/docs/references/ai/solution-blueprint)** | `solution-blueprint.zod.ts` | BlueprintObject, BlueprintApp | Blueprint format for AI app generation |
128128

129-
## API Protocol (17 of 31 schemas)
129+
## API Protocol (17 of 32 schemas)
130130

131131
REST endpoints, real-time subscriptions, and discovery.
132132

‎content/docs/references/api/index.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This section contains all protocol schemas for the api layer of ObjectStack.
2727
<Card href="/docs/references/api/misc" title="Misc" />
2828
<Card href="/docs/references/api/odata" title="Odata" description="Source: packages/spec/src/api/odata.zod.ts" />
2929
<Card href="/docs/references/api/package-api" title="Package Api" description="Source: packages/spec/src/api/package-api.zod.ts" />
30+
<Card href="/docs/references/api/package-api-assembled" title="Package Api Assembled" description="Source: packages/spec/src/api/package-api-assembled.zod.ts" />
3031
<Card href="/docs/references/api/package-lifecycle" title="Package Lifecycle" description="Source: packages/spec/src/api/package-lifecycle.zod.ts" />
3132
<Card href="/docs/references/api/plugin-rest-api" title="Plugin Rest Api" description="Source: packages/spec/src/api/plugin-rest-api.zod.ts" />
3233
<Card href="/docs/references/api/protocol" title="Protocol" description="Source: packages/spec/src/api/protocol.zod.ts" />

‎content/docs/references/api/meta.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"---More---",
3535
"error-code-ledger",
3636
"misc",
37+
"package-api-assembled",
3738
"package-lifecycle",
3839
"sortability"
3940
]

0 commit comments

Comments
 (0)