Skip to content

Commit 72b26ed

Browse files
feat(metadata-protocol,spec): importUnmanagedPackage, the unmanaged install mode's copy-once import (ADR-0131 D6, stage S1) (#22879)
Part of #15213. This is stage S1 only, the import verb behind the `unmanaged` install mode; the card stays open for S2 (the `installModes` manifest key with its install door, `--install-mode` and the `installMode` body key), S3 (the authoring docs page) and S4 (the objectui marketplace choice). Clause-②: yes (widening) The widening: two new published schemas in `@objectstack/spec/api` (`ImportUnmanagedPackageRequestSchema`, parsed by its producer, and `ImportUnmanagedPackageResponseSchema`), and two new error codes registered to `@objectstack/metadata-protocol`. Nothing existing is removed or narrowed. Changeset: `.changeset/15213-unmanaged-import-verb.md` (`minor` on both packages). ## What it does `ObjectStackProtocolImplementation.importUnmanagedPackage({ manifest, targetPackageId?, targetName? })` (`packages/metadata-protocol/src/protocol.ts`, beside `duplicatePackage`) copies every metadata item of ONE package body into a NEW writable base in the environment ledger, through `saveMetaItem`, the save door a Studio edit takes. Every copied row is owned by the base, carries `organization_id` NULL, is served `_provenance: 'org'`, and the next save edits it. Nothing is registered under the source id. This is the part of a managed install that the manifest service does, and nothing more. The rulings it carries, as built: - **The spelling is `unmanaged`** (ruling 6108509158, letter B): the codes are `UNMANAGED_INSTALL_*` and the write face is `'package-unmanaged-import'`. - **Q2: the refusal reads the REQUESTED posture.** `resolveTenancyPosture()`; an unrecognised value is read as walled (fail-closed, as the protocol's other posture gate does). `group` / `isolated` answer `UNMANAGED_INSTALL_WALLED_POSTURE` / 409, and `details.posture` names it. - **Q3: a NEW base whose id differs from the source.** `targetPackageId` defaults to the source id plus `-copy` (the Studio duplicate default). The source namespace is kept. The base is installed scope-less (writable) through `installPackage`, never best-effort. A target id naming an installed package is refused (`RESOURCE_CONFLICT` / 409): never into an existing base. - **Q5, the door half: runtime-uncreatable types and code are refused before any write.** The type verdict is the save door's own predicate (`isRuntimeCreateAllowed`, registry-only); `OS_METADATA_WRITABLE` is not consulted, and a test sets it and shows it does not open `job`. Code keys (`functions`, `onEnable`, `plugins`, `devPlugins`, `runtimeModule`, and a plugin artifact's `main` / `runtime` / `packaging` / `integrity`), extensions of another package's items (`objectExtensions`, `picklistExtensions`, `navigationContributions`, `contributes`) and the artifact envelope (`packages`, a nested `manifest`) are refused whole with `UNMANAGED_INSTALL_UNCOPYABLE` / 422, naming each key. The authoring half (`defineStack` / `os validate`) rides S2 with the manifest key. - **Q7: every judgeable refusal before the base exists; any write failure deletes the base.** Order: organization-scoped request, the published request declaration (a key it does not declare is a 400), posture, the body (uncopyable keys, item names against the save door's grammar, duplicate names, the target id), the ADR-0087 D1 handshake, an existing target, an owned namespace (a managed install of the same package included), conversion to today's protocol (`INVALID_METADATA` / 422, `FLOW_CONVERSION_CONFLICT` / 409), and collisions with environment rows or items a managed package ships (`RESOURCE_CONFLICT` / 409, `details.collisions`). A write that fails afterwards runs `deletePackage(base)`, which removes its rows and drops its tables, and the thrown error keeps the failed item's own `code` and `status`, names the item, and says what the rollback did (`details.rolledBack`). A rollback that cannot finish is logged once at `error` with the consequence and the fix. ### What is copied, and what is left to the door The pure half lives in `packages/metadata-protocol/src/package-unmanaged-install.ts`. Copied: the items of every manifest collection (`PLURAL_TO_SINGULAR`, the one map the artifact loader and the authoring lint read) plus the assembled `viewItems`, written in registry `loadOrder`, a view container named by the object it binds (the artifact loader's naming). Every other top-level key is classified in `UNMANAGED_IMPORT_KEY_DISPOSITIONS`, and a test holds that table total against `ManifestSchema` (tombstoned keys excepted) and the stack definition in both directions, so a key either schema gains reddens a test instead of being refused with no reason on record. An unclassified key is refused, never skipped. Two classes are deliberately not this verb's, because a managed install leaves them out of the manifest service too: seed `data` and `translations` are side effects install-local's step 5 applies in either mode (seeds are data, never metadata items), and deployment configuration (`i18n`, `requires`, `datasourceMapping`, …) is read by a stack's boot, by no install. S2's door owns their unmanaged fate; see the acceptance notes. ## Measured before building (the dispatch's mechanism assumptions) - `external-import` is NOT this import's face: it serves `@objectstack/service-datasource`'s "Import as Object" route, which relays `400 EXTERNAL_IMPORT_ERROR`. One new face was needed, as the stage plan said, and both face switches state it (`destructiveChangeRemedy` gets its own 409 clause; `specValidationFindings` keeps the full-prose default, documented). - No request schema for duplicate exists anywhere in spec (zero hits for `DuplicatePackageRequest`). The request schema here is new, and ENFORCED rather than transcribed: the verb parses through it. The module header of `package-lifecycle.zod.ts` says so. - `installModes` / `installMode`: still zero hits in `packages/` and `examples/`; nothing of S2 was needed, and no `runtime` file is edited. ## Tests `packages/metadata-protocol/src/package-unmanaged-install.test.ts`, 40 cases. The verb runs against a stub engine that keeps rows, so the REAL `saveMetaItem`, `installPackage` and `deletePackage` run; its write verbs route through the producer's dispatch predicates, and its `find` honours `limit`. Refusals are asserted as the envelope `resolveThrownHttpError` answers (`code` and `status`), never a bare throw. The stage-0 pins, each built: - every item copied with `package_id` = the base and `organization_id` NULL, nothing under the source id; the response parses through `ImportUnmanagedPackageResponseSchema`; - the registry is handed every copied item stamped `_provenance: 'org'` (a view's registry item; an object's registered schema, under the base id); - a walled requested posture (`isolated`, `group`, an unrecognised value) is refused 409 with ZERO writes and no package; the lit control on `single` imports; - a collision with an environment row, with a managed package's item, an owned namespace and an existing target are each refused before the base is created; - a mid-copy failure (a spec-invalid second item) is thrown as the save door's own `INVALID_METADATA` / 422 and leaves no package, no row, and the object's table dropped; - a follow-up save edits a copied view AND a copied object. ### Ablations (one-shot, not kept as tests) Each leg mutated through `scripts/ablation-replace.mjs` (the anchor must hit exactly once, the write is verified on disk by blob hash, the restore is proven against `HEAD` by blob hash and an empty `git diff HEAD`), inside a wrapper carrying an EXIT/INT/TERM restore trap. The suite imports `src` directly (no `dist` leg). Direction predicted before running: red for all four; observed red for all four, and only where predicted. Run at `b5cfa28ee0`; the two mutated files are byte-identical at this PR's head (`protocol.ts` blob `432f1d7841`, `package-unmanaged-install.ts` blob `352e1b7afc`). | Leg | Mutation | Red | |---|---|---| | A1 | the posture refusal's `throw` removed | the 3 walled-posture cases (37 of 40 still green) | | A2 | the rollback's `deletePackage` call replaced by an empty outcome | the mid-copy failure case (39 of 40 green) | | A3 | the collision refusal disabled | the environment-row and managed-item collision cases (38 of 40 green) | | A4 | the runtime-uncreatable refusal disabled | the 5 per-type cases and the hatch case (34 of 40 green) | ### Gates (local, targeted; CI runs the full farm) - **Derived on this diff** with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` at `d15160d108`: 122 commands, a strict superset of the 84 derived at dispatch (the additions come from the generated docs pages and the gate ledger this diff touches). - **All 122 run at `d15160d108`**, each exit code captured to disk before any pipe: **122 × exit 0**. `--ran` reconciliation over the recorded codes: 122 derived, 122 run, 0 NOT-MEASURED, 0 UNRUN. - An earlier run of the union at `b5cfa28ee0` found two real reds, both in this PR's test double, both fixed in `d15160d108`: `check:objectql-double-limit` (the stub's `find` ignored `limit`) and `check:engine-double-contract` (a new pinned double needs its ledger rows, written by the gate's own `--write`: 3 rows added, 0 lost). Three gates that run read a missing `dist` there (exit 3, not measured); after building the `objectql` and `client-react` closures all three measured green above. - `@objectstack/metadata-protocol`: `typecheck` (`tsc --noEmit`) green at `d15160d108`; the full suite at the post-merge tree `2a26dc7eec`: 225 files passed, 3 skipped, 28,168 tests passed, with one red file, the `lookupArtifactItem(` call-site census, fixed in `b5cfa28ee0` (the new call removed) and re-run green with this PR's suite and both face inventories (4 files, 100 tests). - `@objectstack/spec`: the full `typecheck` chain (`tsc --noEmit`, scripts, test-typecheck) green; every suite under `src/api/` (51 files, 1,595 tests) green; `check:generated` reports all 14 generated artifacts current after the merge. - Not run locally, left to CI: the repo-wide `pnpm lint`, the full test farm, and the merge queue's rebuild. The derivation flags its tree as behind `origin/main` by at least 5 commits, two of which changed gate scripts (`scripts/check-route-envelope.mjs`, `scripts/cross-package-test-inputs.mjs`); CI reads the merged state. Size: 1,566 changed lines (1,553 added, 13 deleted, generated included), 17 files. ## Acceptance notes Out of scope for S1, recorded for the carrier named; none is filed. - **S2 owns seeds and translations for the unmanaged mode.** The verb leaves `data` and `translations` to the door, as install-local's step 5 applies them for a managed install. One difference S2 has to decide on: a managed install rehydrates its translation bundles from its ledger entry at each boot; an unmanaged install writes no ledger entry, so bundles loaded once into the i18n service would not come back after a restart unless S2 carries them some other way. Carrier: S2. - **`installed_from` has no carrier yet.** The card body asks for an informational `installed_from`; the stage-0 report proposed the server-stated write source. The verb does not state a `source` on its `saveMetaItem` calls, because the save door reads any stated `source` as a stored-row rewrite and skips the authored-write rule for it. The answer names the source id and version, which the door can record. Carrier: S2. - **Spec validity is judged at the write, then rolled back, not preflighted.** The stage plan's preflight list does not include a per-item schema parse, and a second copy of the save door's gates in the verb would be a parallel judge. A spec-invalid item therefore creates the base and deletes it again (pinned). If the seat reads Q7's "every judgeable case" as including schema validity, the preflight can parse each item through `getMetadataTypeSchema` before the base. Carrier: the seat, at review. - **Hooks with a function-name `handler` and no `body`** are not judged by the verb: install-local's step 1d refuses them before any mode fork, using the runtime binder this package cannot import. Carrier: S2 (install-local runs 1d before any point where S2 can branch on the mode). - **The managed-collision detail does not name the owning package.** It says `shipped by a managed package`: naming it would add a `lookupArtifactItem(` call site, which `protocol.lookup-artifact-item-call-sites.test.ts` records with a disposition, in a file outside this stage's surface. The namespace refusal does name its owners. Carrier: none. - **Unmeasured:** an item carrying an authored `protection` lock is copied as written, so it may stay locked in the environment ledger; and an exported package whose `viewItems` carry an edited view-container expansion may be refused by the save door's container-expansion predicate on import (then rolled back). Carrier: S2's acceptance run on `examples/app-crm`. - **Observation, no card:** `convertStoredItem`'s legacy-shape notice says "stored row … re-save it" when the import converts an artifact item; the wording describes a stored row, not an import. Carrier: none. --- _Generated by [Claude Code](https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 75b6d6a commit 72b26ed

17 files changed

Lines changed: 1553 additions & 13 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@objectstack/metadata-protocol': minor
3+
'@objectstack/spec': minor
4+
---
5+
6+
`importUnmanagedPackage` copies one package into the environment ledger, editable from then on — the import behind the `unmanaged` install mode (ADR-0131 D6)
7+
8+
Clause-②: yes (widening)
9+
10+
A package installs one of two ways. Managed: the package is the authority, it upgrades, and nothing it ships may be edited. Unmanaged: every metadata item is copied once into this environment, is fully editable, and never follows an upgrade. This release adds the import; no door calls it yet, and no manifest key declares the mode yet.
11+
12+
- **The verb** (`@objectstack/metadata-protocol`). `ObjectStackProtocolImplementation.importUnmanagedPackage({ manifest, targetPackageId?, targetName? })` writes every metadata item of ONE package body into a NEW writable base through the metadata save door: each row is owned by the base, carries no organization, is served as environment-authored (`_provenance: 'org'`), and the next save edits it. The base's id differs from the source (`targetPackageId`, defaulting to `<id>-copy`), it keeps the source namespace so every object name stays valid, and nothing is registered under the source id. The answer names the source, the base, its namespace and every item copied, in write order.
13+
- **All or nothing.** Every refusal that can be judged is raised before the base exists, so a refused import writes nothing: a deployment whose requested tenancy posture walls organizations (`group`, `isolated`, or an unrecognised value); a body carrying what cannot be copied; a malformed name or an unusable target id; a protocol-incompatible package; a target id that names an installed package, or a namespace another package owns (a managed install of the same package included); an item that does not convert to this protocol; a name an environment row or a managed package already holds. A write that fails afterwards deletes the base — its rows and its tables — and the failure is thrown with the item's own code and status, naming the item and what the rollback did.
14+
- **What is copied, and what is refused.** The items of every manifest collection and the assembled `viewItems` are copied. A body carrying a runtime-uncreatable type (`jobs`, `agents`, `apis`, `capabilities`, `picklists`), code (`functions`, `onEnable`, `plugins`, a plugin artifact's `main` / `runtime` / `packaging` / `integrity`), extensions of another package's items (`objectExtensions`, `picklistExtensions`, `navigationContributions`, `contributes`) or an artifact envelope (`packages`) is refused whole; `OS_METADATA_WRITABLE` does not open a type for it. Seed `data` and `translations` are left to the install door, which applies them after the copy as it does for a managed install; deployment configuration (`i18n`, `requires`, `datasourceMapping`, …) is not part of a package install in either mode.
15+
- **Two new error codes** (`@objectstack/spec`, registered to `@objectstack/metadata-protocol`). `UNMANAGED_INSTALL_WALLED_POSTURE` (409) and `UNMANAGED_INSTALL_UNCOPYABLE` (422). Both mean the package can be installed managed instead. A collision, an existing target or an owned namespace answers the standard `RESOURCE_CONFLICT` (409).
16+
- **Two new schemas** (`@objectstack/spec/api`). `ImportUnmanagedPackageRequestSchema` is parsed by the verb, so a request key it does not declare is refused with `400`. `ImportUnmanagedPackageResponseSchema` declares the answer.
17+
18+
Nothing existing changes: no schema, door or code is removed or narrowed.

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const result = ApiErrorSchema.parse(data);
2828

2929
| Property | Type | Required | Description |
3030
| :--- | :--- | :--- | :--- |
31-
| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +321 more>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) |
31+
| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +323 more>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) |
3232
| **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112) |
3333
| **message** | `string` | ✅ | Readable error message |
3434
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
@@ -353,6 +353,8 @@ const result = ApiErrorSchema.parse(data);
353353
* `UNIQUE_VIOLATION`
354354
* `UNKNOWN_KEY`
355355
* `UNKNOWN_NAMESPACE`
356+
* `UNMANAGED_INSTALL_UNCOPYABLE`
357+
* `UNMANAGED_INSTALL_WALLED_POSTURE`
356358
* `UNSUPPORTED`
357359
* `UNSUPPORTED_QUERY_PARAM`
358360
* `UNSUPPORTED_TRANSFORM`

‎content/docs/references/api/error-code-ledger.mdx‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,8 @@ const result = ErrorCode.parse(data);
520520
* `UNIQUE_VIOLATION`
521521
* `UNKNOWN_KEY`
522522
* `UNKNOWN_NAMESPACE`
523+
* `UNMANAGED_INSTALL_UNCOPYABLE`
524+
* `UNMANAGED_INSTALL_WALLED_POSTURE`
523525
* `UNSUPPORTED`
524526
* `UNSUPPORTED_QUERY_PARAM`
525527
* `UNSUPPORTED_TRANSFORM`

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

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ through the `{ success, data }` envelope (`http-dispatcher.ts`), so each
2222
schema declares the `data` payload, envelope-free — the same convention as
2323
`PublishPackageDraftsResponseSchema` and its ledger row.
2424

25+
One pair is the exception: `ImportUnmanagedPackageRequestSchema` /
26+
`ImportUnmanagedPackageResponseSchema` (ADR-0131 D6) declare a protocol verb
27+
no route serves yet, and the request half is parsed by its producer rather
28+
than transcribed — see their own docblocks.
29+
2530
`packages.publish`'s contract is NOT here: its producer
2631
(`MetadataManager.publishPackage`) already has an exact published schema,
2732
`PackagePublishResultSchema` in `@objectstack/spec/system` — re-exported
@@ -43,8 +48,8 @@ authored after that retirement per the ruling's sequencing (3A).
4348
## TypeScript Usage
4449

4550
```typescript
46-
import { DiscardPackageDraftsResponseSchema, DuplicatePackageResponseSchema, ListPackageCommitsResponseSchema, PackageExportManifestSchema, PackagePublishResultSchema, ReassignOrphanedMetadataResponseSchema, RevertPackageCommitResponseSchema, RollbackToPackageCommitResponseSchema } from '@objectstack/spec/api';
47-
import type { DiscardPackageDraftsResponse, DuplicatePackageResponse, ListPackageCommitsResponse, PackageExportManifest, PackagePublishResult, ReassignOrphanedMetadataResponse, RevertPackageCommitResponse, RollbackToPackageCommitResponse } from '@objectstack/spec/api';
51+
import { DiscardPackageDraftsResponseSchema, DuplicatePackageResponseSchema, ImportUnmanagedPackageRequestSchema, ImportUnmanagedPackageResponseSchema, ListPackageCommitsResponseSchema, PackageExportManifestSchema, PackagePublishResultSchema, ReassignOrphanedMetadataResponseSchema, RevertPackageCommitResponseSchema, RollbackToPackageCommitResponseSchema } from '@objectstack/spec/api';
52+
import type { DiscardPackageDraftsResponse, DuplicatePackageResponse, ImportUnmanagedPackageRequest, ImportUnmanagedPackageResponse, ListPackageCommitsResponse, PackageExportManifest, PackagePublishResult, ReassignOrphanedMetadataResponse, RevertPackageCommitResponse, RollbackToPackageCommitResponse } from '@objectstack/spec/api';
4853

4954
// Validate data
5055
const result = DiscardPackageDraftsResponseSchema.parse(data);
@@ -112,6 +117,42 @@ const result = DiscardPackageDraftsResponseSchema.parse(data);
112117
| **error** | `string` | ✅ | Why copying it failed. |
113118

114119

120+
---
121+
122+
## ImportUnmanagedPackageRequest
123+
124+
### Properties
125+
126+
| Property | Type | Required | Description |
127+
| :--- | :--- | :--- | :--- |
128+
| **manifest** | `Record<string, any>` | ✅ | The package to import: ONE package's assembled body, flattened — its manifest fields beside its metadata collections (`objects`, `views`, …), the shape the install door hands the manifest service. An artifact carrying `packages` is refused: import each package body on its own. |
129+
| **targetPackageId** | `string` | optional | Id of the NEW writable base the items are written into, in reverse-domain form. It must differ from the source id and name no installed package. Defaults to `<manifest.id>-copy`. |
130+
| **targetName** | `string` | optional | Display name of the new base. Defaults to the source package's `name`. |
131+
132+
133+
---
134+
135+
## ImportUnmanagedPackageResponse
136+
137+
### Properties
138+
139+
| Property | Type | Required | Description |
140+
| :--- | :--- | :--- | :--- |
141+
| **sourcePackageId** | `string` | ✅ | The id of the package the items were copied from. |
142+
| **sourceVersion** | `string` | optional | The version the imported body declared, when it declared one. |
143+
| **targetPackageId** | `string` | ✅ | The new writable base that owns every copied item. Nothing is registered under the source id. |
144+
| **namespace** | `string` | ✅ | The base's namespace — the SOURCE package's, kept so every copied object name stays valid. |
145+
| **copiedCount** | `number` | ✅ | How many items were copied. |
146+
| **copied** | `{ type: string; name: string }[]` | ✅ | Every item that was copied, in the order it was written. |
147+
148+
### Nested Shape: `ImportUnmanagedPackageResponse.copied[number]`
149+
150+
| Property | Type | Required | Description |
151+
| :--- | :--- | :--- | :--- |
152+
| **type** | `string` | ✅ | Metadata type of the copied item. |
153+
| **name** | `string` | ✅ | Name of the copied item. |
154+
155+
115156
---
116157

117158
## ListPackageCommitsResponse

‎content/docs/references/index.mdx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Protocol reference — every schema by module
33
navTitle: Protocol Reference
4-
description: Every schema published by @objectstack/spec — 1520 schemas across 14 protocol modules
4+
description: Every schema published by @objectstack/spec — 1522 schemas across 14 protocol modules
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -21,7 +21,7 @@ counts are sums of the rows they head. Regenerate with
2121
| Module | Pages | Schemas | Description |
2222
| :--- | ---: | ---: | :--- |
2323
| [AI Protocol](/docs/references/ai) | 12 | 68 | Agents, tools, skills, RAG and knowledge sources, model registry, conversations. |
24-
| [API Protocol](/docs/references/api) | 32 | 432 | REST contracts, endpoints, routing, realtime, batch, discovery. |
24+
| [API Protocol](/docs/references/api) | 32 | 434 | REST contracts, endpoints, routing, realtime, batch, discovery. |
2525
| [Automation Protocol](/docs/references/automation) | 13 | 70 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. |
2626
| [Data Protocol](/docs/references/data) | 29 | 176 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
2727
| [Identity Protocol](/docs/references/identity) | 5 | 27 | Users and accounts, organizations, positions, SCIM provisioning. |
@@ -34,7 +34,7 @@ counts are sums of the rows they head. Regenerate with
3434
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3535
| [System Protocol](/docs/references/system) | 35 | 278 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3636
| [UI Protocol](/docs/references/ui) | 16 | 166 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
37-
| **Total** | **196** | **1520** | 14 protocol modules |
37+
| **Total** | **196** | **1522** | 14 protocol modules |
3838

3939
---
4040

@@ -63,7 +63,7 @@ Agents, tools, skills, RAG and knowledge sources, model registry, conversations.
6363

6464
## API Protocol
6565

66-
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **32 pages, 432 schemas**
66+
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **32 pages, 434 schemas**
6767

6868
REST contracts, endpoints, routing, realtime, batch, discovery.
6969

@@ -89,7 +89,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
8989
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
9090
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `ListInstalledPackagesRequest`, `PackageApiErrorCode`, `PackageInstallBody`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
9191
| [`package-api-assembled.zod.ts`](/docs/references/api/package-api-assembled) | `AssembledInstalledPackage`, `GetInstalledPackageResponse`, `InstalledPackageAtEitherStage`, `ListInstalledPackagesResponse` |
92-
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
92+
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ImportUnmanagedPackageRequest`, `ImportUnmanagedPackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
9393
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
9494
| [`protocol.zod.ts`](/docs/references/api/protocol) | `AiAgentCapabilities`, `AiAgentChatRequest`, `AiAgentSummary`, `AiAgentsResponse`, `AiChatRequest`, `AiChatResponse`, `AiCompleteRequest`, `AiConversation`, `AiMessage`, `AiModelsResponse`, `AiPendingAction`, `AiPendingActionStatus`, `AiStreamChunk`, `ApproveAiPendingActionResponse`, `AuditMetaItemRequest`, `AuditMetaItemResponse`, `AutomationActionsResponse`, `AutomationTriggerRequest`, `AutomationTriggerResponse`, `BatchDataRequest`, `BatchDataResponse`, `CheckPermissionRequest`, `CheckPermissionResponse`, `CloneDataResponse`, `CreateAiConversationRequest`, `CreateDataRequest`, `CreateDataResponse`, `CreateManyDataRequest`, `CreateManyDataResponse`, `DeleteDataRequest`, `DeleteDataResponse`, `DeleteManyDataRequest`, `DeleteManyDataResponse`, `DeleteMetaItemRequest`, `DeleteMetaItemResponse`, `DiffMetaItemResponse`, `DisablePackageRequest`, `DisablePackageResponse`, `EnablePackageRequest`, `EnablePackageResponse`, `FindDataRequest`, `FindDataResponse`, `FindReferencesToMetaResponse`, `GetDataRequest`, `GetDataResponse`, `GetDiscoveryRequest`, `GetDiscoveryResponse`, `GetEffectivePermissionsRequest`, `GetEffectivePermissionsResponse`, `GetFieldLabelsRequest`, `GetFieldLabelsResponse`, `GetLocalesRequest`, `GetLocalesResponse`, `GetMetaDiagnosticsResponse`, `GetMetaItemCachedRequest`, `GetMetaItemCachedResponse`, `GetMetaItemLayeredRequest`, `GetMetaItemLayeredResponse`, `GetMetaItemRequest`, `GetMetaItemResponse`, `GetMetaItemsRequest`, `GetMetaItemsResponse`, `GetMetaTypesRequest`, `GetMetaTypesResponse`, `GetNotificationPreferencesRequest`, `GetNotificationPreferencesResponse`, `GetObjectPermissionsRequest`, `GetObjectPermissionsResponse`, `GetPackageRequest`, `GetPackageResponse`, `GetPresenceRequest`, `GetPresenceResponse`, `GetPublishedMetaItemResponse`, `GetTranslationsRequest`, `GetTranslationsResponse`, `GetUiViewRequest`, `GetUiViewResponse`, `HistoryMetaItemRequest`, `HistoryMetaItemResponse`, `HttpFindQueryParams`, `InstallPackageRequest`, `InstallPackageResponse`, `ListAiConversationsRequest`, `ListAiConversationsResponse`, `ListAiPendingActionsRequest`, `ListAiPendingActionsResponse`, `ListDraftsResponse`, `ListNotificationsRequest`, `ListNotificationsResponse`, `ListPackagesRequest`, `ListPackagesResponse`, `MarkAllNotificationsReadRequest`, `MarkAllNotificationsReadResponse`, `MarkNotificationsReadRequest`, `MarkNotificationsReadResponse`, `MetadataConflictError`, `Notification`, `NotificationPreferences`, `PublishMetaItemRequest`, `PublishMetaItemResponse`, `PublishPackageDraftsResponse`, `RealtimeConnectRequest`, `RealtimeConnectResponse`, `RealtimeDisconnectRequest`, `RealtimeDisconnectResponse`, `RealtimeSubscribeRequest`, `RealtimeSubscribeResponse`, `RealtimeUnsubscribeRequest`, `RealtimeUnsubscribeResponse`, `RegisterDeviceRequest`, `RegisterDeviceResponse`, `RejectAiPendingActionResponse`, `RollbackMetaItemResponse`, `RuntimeAuthoringIssue`, `SaveMetaItemRequest`, `SaveMetaItemResponse`, `SearchAllHit`, `SearchAllPageHit`, `SearchAllResponse`, `SetPresenceRequest`, `SetPresenceResponse`, `UninstallPackageRequest`, `UninstallPackageResponse`, `UnregisterDeviceRequest`, `UnregisterDeviceResponse`, `UpdateAiConversationRequest`, `UpdateDataRequest`, `UpdateDataResponse`, `UpdateManyDataRequest`, `UpdateManyDataResponse`, `UpdateNotificationPreferencesRequest`, `UpdateNotificationPreferencesResponse`, `ValidateDataIssue`, `ValidateDataRequest`, `ValidateDataResponse` |
9595
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |

‎docs/audits/2026-07-unknown-key-strictness-ledger.counts/api.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ hand-patch a number here** — fix the code or the verdict and regenerate.
1919

2020
| Dir | Sites |
2121
|---|---|
22-
| `api/` | 434 |
22+
| `api/` | 437 |

0 commit comments

Comments
 (0)