Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .changeset/sdk-response-contracts-bound.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
"@objectstack/spec": minor
"@objectstack/client": minor
"@objectstack/runtime": patch
"@objectstack/rest": patch
---

feat(spec,client): bind published response contracts for the 17 unbound client-SDK methods; retire the false `PackageRollbackResponseSchema` (#12038, ruling 1C · 2C · 3A · 4A · 5A)

<!-- adr-0087: registered package-rollback-response-retired -->

**BREAKING** export removal, landing after the v17.0.0 cut (the lockstep
launch-window convention ships it as `minor`; the prescription is registered
under protocol major 18 — `RETIRED_DEFS_BY_MAJOR[18]` `api/PackageRollbackResponse`
plus the D3 semantic entry `package-rollback-response-retired` — where
`os migrate meta` users will look).

FROM → TO:

- `PackageRollbackResponseSchema` / `PackageRollbackResponse` /
`PackageRollbackResponseParsed` → `RollbackToPackageCommitResponseSchema` /
`RollbackToPackageCommitResponse` (`@objectstack/spec/api`). The retired
schema declared a VERSION rollback (`{ success, restoredVersion?,
message? }`) while the live `POST /packages/:id/rollback` route posts
`{ commitId }` and answers the ADR-0067 COMMIT rollback —
`{ success, revertedCommits: string[], failed: [{ commitId, error }] }`.
Read `revertedCommits` / `failed`; there is no `restoredVersion`.
- `PackageApiContracts.rollbackPackage` → *(removed)* — it bound the
wrong-operation schema to the exact live path. No route registration or
SDK generation ever consumed it (zero consumers measured across
objectstack, objectui and cloud; only its own unit test and the #11925
compile-time guard, both updated in this PR).

One-line fix: replace any import of `PackageRollbackResponse(Schema)` with
`RollbackToPackageCommitResponse(Schema)` and read `revertedCommits` /
`failed` instead of `restoredVersion`. `PackageRollbackRequestSchema` stays
published (ruled out of the retirement), bound to no route.

The rest of the change is additive — the recorded five-part maintainer
ruling (2026-08-27) for the 17 client-SDK methods that had no published
response contract:

- **12 describe-only transcriptions** into `@objectstack/spec/api`, each
from the return type its producer already declares inline (no wire byte
changes): `ListDraftsResponseSchema`, `GetMetaDiagnosticsResponseSchema`,
`FindReferencesToMetaResponseSchema`, `RollbackMetaItemResponseSchema`,
`DiffMetaItemResponseSchema`, `ResolvedBookSchema` (authored beside its
interfaces in `system/book.zod.ts`), `DiscardPackageDraftsResponseSchema`,
`ListPackageCommitsResponseSchema` (the `{ commits }` wrapper declared as
the handler's own), `RevertPackageCommitResponseSchema`,
`RollbackToPackageCommitResponseSchema`,
`ReassignOrphanedMetadataResponseSchema`, `DuplicatePackageResponseSchema`.
- **Ruling 1C**: `GetPublishedMetaItemResponseSchema` is deliberately opaque
(`z.unknown()`) — the route answers an arbitrary metadata item body, never
a union frozen against the type registry.
- **Ruling 2C**: `meta.migrateStored` stays UNBOUND, documented at its two
ledger rows and in the SDK — `StoredMigrationReport` lives in
`@objectstack/metadata-protocol`, and a second declaration would drift.
- **Ruling 4A**: `PackageExportManifestSchema` pins the four fixed keys
(`id`, `name`, `version`, `label?`) and stays honestly open for the
registry-derived plural keys.
- **Ruling 5A**: `PackagePublishResultSchema` and the `ResolvedBook` family
are re-exported into `@objectstack/spec/api` (the namespace the
route-ledger resolver searches) — never a second copy.
- The 18 boundable route-ledger rows in `@objectstack/runtime` and
`@objectstack/rest` now name their `responseSchema`, each stating which
surface's envelope it describes; every named schema carries conformance
coverage (the #3877 rule).
- The client SDK binds 16 of the 17 methods to the published payload types,
replaces four invented test mocks with producer-true shapes, and pins the
`unwrapResponse` mis-unwrap hazard so no bound payload can declare both a
boolean `success` and a `data` key.
2 changes: 1 addition & 1 deletion content/docs/getting-started/quick-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ AI/ML capabilities - agents, skills, tools, MCP exposure, RAG, and cost tracking
| **[Usage](/docs/references/ai/usage)** | `usage.zod.ts` | AIUsageRecord, TokenUsage | AI usage and cost tracking |
| **[Solution Blueprint](/docs/references/ai/solution-blueprint)** | `solution-blueprint.zod.ts` | BlueprintObject, BlueprintApp | Blueprint format for AI app generation |

## API Protocol (17 of 29 schemas)
## API Protocol (17 of 31 schemas)

REST endpoints, real-time subscriptions, and discovery.

Expand Down
2 changes: 2 additions & 0 deletions content/docs/references/api/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ This section contains all protocol schemas for the api layer of ObjectStack.
<Card href="/docs/references/api/export" title="Export" description="Source: packages/spec/src/api/export.zod.ts" />
<Card href="/docs/references/api/http-cache" title="Http Cache" description="Source: packages/spec/src/api/http-cache.zod.ts" />
<Card href="/docs/references/api/metadata" title="Metadata" description="Source: packages/spec/src/api/metadata.zod.ts" />
<Card href="/docs/references/api/misc" title="Misc" />
<Card href="/docs/references/api/odata" title="Odata" description="Source: packages/spec/src/api/odata.zod.ts" />
<Card href="/docs/references/api/package-api" title="Package Api" description="Source: packages/spec/src/api/package-api.zod.ts" />
<Card href="/docs/references/api/package-lifecycle" title="Package Lifecycle" description="Source: packages/spec/src/api/package-lifecycle.zod.ts" />
<Card href="/docs/references/api/plugin-rest-api" title="Plugin Rest Api" description="Source: packages/spec/src/api/plugin-rest-api.zod.ts" />
<Card href="/docs/references/api/protocol" title="Protocol" description="Source: packages/spec/src/api/protocol.zod.ts" />
<Card href="/docs/references/api/query-adapter" title="Query Adapter" description="Source: packages/spec/src/api/query-adapter.zod.ts" />
Expand Down
2 changes: 2 additions & 0 deletions content/docs/references/api/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
"storage",
"---More---",
"error-code-ledger",
"misc",
"package-lifecycle",
"sortability"
]
}
82 changes: 82 additions & 0 deletions content/docs/references/api/misc.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: Misc
description: Misc protocol schemas
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}

## TypeScript Usage

```typescript
import { ResolvedBookSchema, ResolvedEntrySchema, ResolvedGroupSchema } from '@objectstack/spec/api';
import type { ResolvedBook, ResolvedEntry, ResolvedGroup } from '@objectstack/spec/api';

// Validate data
const result = ResolvedBookSchema.parse(data);
```

---

## ResolvedBook

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **name** | `string` | ✅ | The book's machine name. |
| **label** | `string` | optional | The book's display label, when it declares one. |
| **groups** | `{ key: string; label: string; entries: object[] }[]` | ✅ | The resolved groups, in render order. |

### Nested Shape: `ResolvedBook.groups[number]`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **key** | `string` | ✅ | The group's key (from the spine, or `uncategorized`). |
| **label** | `string` | ✅ | The group's display label. |
| **entries** | `{ doc?: string; href?: string; label?: string; description?: string; … }[]` | ✅ | The group's resolved entries, in render order. |


---

## ResolvedEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **doc** | `string` | optional | Doc name, or undefined for an external link / separator. |
| **href** | `string` | optional | External link target, when the entry is a link. |
| **label** | `string` | optional | Display label, when one resolved. |
| **description** | `string` | optional | Doc description, when one resolved. |
| **badge** | `string` | optional | Badge text (e.g. "beta"), when declared. |
| **icon** | `string` | optional | Icon name, when declared. |
| **separator** | `boolean` | optional | True for a `---` separator node. |


---

## ResolvedGroup

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **key** | `string` | ✅ | The group's key (from the spine, or `uncategorized`). |
| **label** | `string` | ✅ | The group's display label. |
| **entries** | `{ doc?: string; href?: string; label?: string; description?: string; … }[]` | ✅ | The group's resolved entries, in render order. |

### Nested Shape: `ResolvedGroup.entries[number]`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **doc** | `string` | optional | Doc name, or undefined for an external link / separator. |
| **href** | `string` | optional | External link target, when the entry is a link. |
| **label** | `string` | optional | Display label, when one resolved. |
| **description** | `string` | optional | Doc description, when one resolved. |
| **badge** | `string` | optional | Badge text (e.g. "beta"), when declared. |
| **icon** | `string` | optional | Icon name, when declared. |
| **separator** | `boolean` | optional | True for a `---` separator node. |


---

41 changes: 2 additions & 39 deletions content/docs/references/api/package-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ DELETE /api/v1/packages/:packageId — Uninstall a package
## TypeScript Usage

```typescript
import { GetInstalledPackageRequestSchema, GetInstalledPackageResponseSchema, ListInstalledPackagesRequestSchema, ListInstalledPackagesResponseSchema, PackageApiErrorCode, PackageInstallRequestSchema, PackageInstallResponseSchema, PackagePathParamsSchema, PackageRollbackRequestSchema, PackageRollbackResponseSchema, PackageUpgradeRequestSchema, PackageUpgradeResponseSchema, ResolveDependenciesRequestSchema, ResolveDependenciesResponseSchema, UninstallPackageApiRequestSchema, UninstallPackageApiResponseSchema, UploadArtifactRequestSchema, UploadArtifactResponseSchema } from '@objectstack/spec/api';
import type { GetInstalledPackageRequest, GetInstalledPackageResponse, ListInstalledPackagesRequest, ListInstalledPackagesResponse, PackageApiErrorCode, PackageInstallRequest, PackageInstallResponse, PackagePathParams, PackageRollbackRequest, PackageRollbackResponse, PackageUpgradeRequest, PackageUpgradeResponse, ResolveDependenciesRequest, ResolveDependenciesResponse, UninstallPackageApiRequest, UninstallPackageApiResponse, UploadArtifactRequest, UploadArtifactResponse } from '@objectstack/spec/api';
import { GetInstalledPackageRequestSchema, GetInstalledPackageResponseSchema, ListInstalledPackagesRequestSchema, ListInstalledPackagesResponseSchema, PackageApiErrorCode, PackageInstallRequestSchema, PackageInstallResponseSchema, PackagePathParamsSchema, PackageRollbackRequestSchema, PackageUpgradeRequestSchema, PackageUpgradeResponseSchema, ResolveDependenciesRequestSchema, ResolveDependenciesResponseSchema, UninstallPackageApiRequestSchema, UninstallPackageApiResponseSchema, UploadArtifactRequestSchema, UploadArtifactResponseSchema } from '@objectstack/spec/api';
import type { GetInstalledPackageRequest, GetInstalledPackageResponse, ListInstalledPackagesRequest, ListInstalledPackagesResponse, PackageApiErrorCode, PackageInstallRequest, PackageInstallResponse, PackagePathParams, PackageRollbackRequest, PackageUpgradeRequest, PackageUpgradeResponse, ResolveDependenciesRequest, ResolveDependenciesResponse, UninstallPackageApiRequest, UninstallPackageApiResponse, UploadArtifactRequest, UploadArtifactResponse } from '@objectstack/spec/api';

// Validate data
const result = GetInstalledPackageRequestSchema.parse(data);
Expand Down Expand Up @@ -287,43 +287,6 @@ Rollback package request
| **rollbackCustomizations** | `boolean` | optional (default: `true`) | Whether to restore pre-upgrade customizations |


---

## PackageRollbackResponse

Rollback package response

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **success** | `boolean` | ✅ | Operation success status |
| **error** | `{ code: Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| …>; declaredCode?: string; message: string; userMessage?: string; … }` | optional | Error details if success is false |
| **meta** | `{ timestamp: string; duration?: number; requestId?: string; traceId?: string }` | optional | Response metadata |
| **data** | `{ success: boolean; restoredVersion?: string; message?: string }` | ✅ | |

### Nested Shape: `PackageRollbackResponse.error`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| …>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) |
| **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, #9106) |
| **message** | `string` | ✅ | Readable error message |
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim (#9934). Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution (#3821) for anything unmarked. Status-agnostic; never replaces `message`. |
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |
| **requestId** | `string` | optional | Request ID for tracking |

### Nested Shape: `PackageRollbackResponse.data`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **success** | `boolean` | ✅ | Whether the rollback succeeded |
| **restoredVersion** | `string` | optional | Restored version |
| **message** | `string` | optional | Rollback status message |


---

## PackageUpgradeRequest
Expand Down
Loading
Loading