Skip to content

Commit daae7aa

Browse files
os-litantclaude
andauthored
feat(spec,client): declare the search and data.clone route response contracts (#11924) (#12203)
* feat(spec,client): declare the search and data.clone route response contracts (#11924) The maintainer-ruled half of the four undeclared SDK route returns (2026-08-25, decision-inbox batch 8, option A): search + data.clone are stable, server-produced shapes — declared in packages/spec exactly as produced, with conformance coverage on both the producer and the mounted route, honouring #3877's rule that no route-ledger responseSchema row is filled without conformance coverage (both rows filled in this same change). The automation.create / automation.update pair is NOT touched — it returns to the decision inbox as its own card with a consumer-survey reading first. - spec: SearchAllResponseSchema / SearchAllHitSchema + CloneDataResponseSchema in api/protocol.zod.ts (+ z.input type aliases and isomorphism pin lines); regenerated artifacts: api-surface (6 added / 0 removed), export-origins, reference docs, strictness-ledger counts; authorable-surface and json-schema.manifest shards rewritten by gen:schema as expected products. - rest: both ledger rows filled (responseSchema + coverage-naming notes); search-clone-route-schema-conformance.test.ts pins the relay half — bodies parse, answered bare (201 for clone), rows resolve to the very schema objects the suite parses with. - metadata-protocol: search-clone-schema-conformance.test.ts pins the producer half — the real searchAll / cloneData over fixture engines, safeParse plus key-set subset checks at both levels, both searchAll return statements, both snippet branches. - client: search → Promise<SearchAllResponse>; data.clone → clone<T = any>(...): Promise<CloneDataResult<T>> with the new exported CloneDataResult mirror interface; SearchAllHit / SearchAllResponse re-exported; return-type-precision pins extended in both directions. The #8140 SearchResult near-miss guard's assertions are untouched and green — only its prose note is updated (the binding it described as deliberately absent now exists). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV * test(spec): recount the isomorphic-pin self-check for the three #11924 pins (835 -> 838) The pin file asserts its own pin count and recomputes the two prose statements from the file; the three new Iso859/860/861 pins moved the count. Receipt paragraph added per the file's own convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV * chore(spec): regenerate artifacts on the post-merge tree (api-surface, export-origins, docs, strictness-ledger via check:generated --fix) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV * test(rest): keep the new conformance suite off the TEST_DEBT ledger (explicit .js extension; index instead of Array.prototype.at) check:type-check-debt re-measure went 155 -> 157 on @objectstack/rest from this PR's new test file (TS2835 missing extension, TS2550 .at under the package lib). Fixed the two new errors; ledger stays at its recorded 155. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV * chore(client): delete the two closed exported-any-returns entries (ObjectStackClient.search, ObjectStackClient.data.clone) This PR binds real return types to both sites, voiding their DELIBERATE Promise<any> ledger reasons; the shrink-only ledger is judged exactly, so the closed-gap entries must go in the same change (check:exported-any-returns went red on them at CI). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NDGG54XF5gbTLdQzCtnaVV --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent e6ca40e commit daae7aa

16 files changed

Lines changed: 710 additions & 40 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/client': minor
4+
---
5+
6+
Declare the `search` and `data.clone` route response contracts, and bind the SDK to them (#11924)
7+
8+
Two of the four SDK routes #8140 had to leave as deliberate `Promise<any>` holes now have real
9+
contracts. Their shapes were always stable and server-produced — they were declared inline on the
10+
implementation (`@objectstack/metadata-protocol`'s `searchAll` / `cloneData`), reachable from no
11+
spec export — and per the maintainer ruling on #11924 they are now declared in `@objectstack/spec`
12+
exactly as produced, with conformance coverage on both the producer and the mounted route (#3877:
13+
no route-ledger `responseSchema` row is filled without conformance coverage; both rows are filled
14+
as part of this change).
15+
16+
**`@objectstack/spec` (additive):**
17+
18+
- `SearchAllResponseSchema` / `SearchAllHitSchema` (+ `SearchAllResponse` / `SearchAllHit` types,
19+
`@objectstack/spec/api`) — the WHOLE body of `GET /api/v1/search`, answered bare:
20+
`{ query, hits, totalObjects, totalHits, truncated }` with hits of
21+
`{ object, id, title, snippet?, record }`. ⚠️ Deliberately distinct from `SearchResult` /
22+
`SearchHit` in `@objectstack/spec/contracts`, which type the per-object `ISearchService.search`
23+
(hits of `score` / `document`) — reaching for that same-named neighbour was the near-miss trap
24+
#8140 left a compile-time guard against, and the guard stands unchanged.
25+
- `CloneDataResponseSchema` (+ `CloneDataResponse`, `@objectstack/spec/api`) — the whole 201 body
26+
of `POST /data/:object/:id/clone`: `{ object, id, sourceId, record }`, `CreateDataResponse`'s
27+
structural sibling plus `sourceId` (`id` is the NEW record's, `sourceId` the copied record's).
28+
No `droppedFields` member — unlike `createData`, the clone producer emits none.
29+
30+
**`@objectstack/client` (return-type narrowing, same nature as the #8140 batch):** `search` is
31+
now `Promise<SearchAllResponse>` and `data.clone` is `clone<T = any>(…): Promise<CloneDataResult<T>>`
32+
(a new exported interface mirroring `CloneDataResponseSchema`, beside `CreateDataResult`). Nothing
33+
changes at runtime — no request, response, unwrapping or error path is touched — but code that
34+
compiled against the previous `any` returns (arbitrary property reads, assignments to unrelated
35+
types) can stop compiling; in particular a result assigned to the per-object `SearchResult` is now
36+
refused at compile time, which is the trap the erasure used to hide.
37+
38+
The `automation.create` / `automation.update` pair is explicitly NOT declared here — it returns to
39+
the decision inbox with a consumer-survey reading per the same ruling.

content/docs/references/api/protocol.mdx

Lines changed: 46 additions & 2 deletions
Large diffs are not rendered by default.

content/docs/references/index.mdx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Protocol Reference
3-
description: Every schema published by @objectstack/spec — 1585 schemas across 14 protocol modules
3+
description: Every schema published by @objectstack/spec — 1588 schemas across 14 protocol modules
44
---
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -20,7 +20,7 @@ counts are sums of the rows they head. Regenerate with
2020
| Module | Pages | Schemas | Description |
2121
| :--- | ---: | ---: | :--- |
2222
| [AI Protocol](/docs/references/ai) | 11 | 66 | Agents, tools, skills, RAG and knowledge sources, model registry, conversations. |
23-
| [API Protocol](/docs/references/api) | 29 | 420 | REST contracts, endpoints, routing, realtime, batch, discovery. |
23+
| [API Protocol](/docs/references/api) | 29 | 423 | REST contracts, endpoints, routing, realtime, batch, discovery. |
2424
| [Automation Protocol](/docs/references/automation) | 13 | 68 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. |
2525
| [Cloud Protocol](/docs/references/cloud) | 11 | 94 | Environments, packages and versions, marketplace, developer portal, tenancy. |
2626
| [Data Protocol](/docs/references/data) | 29 | 166 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. |
@@ -33,7 +33,7 @@ counts are sums of the rows they head. Regenerate with
3333
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
3434
| [System Protocol](/docs/references/system) | 36 | 288 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3535
| [UI Protocol](/docs/references/ui) | 16 | 152 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
36-
| **Total** | **199** | **1585** | 14 protocol modules |
36+
| **Total** | **199** | **1588** | 14 protocol modules |
3737

3838
---
3939

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

6262
## API Protocol
6363

64-
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **29 pages, 420 schemas**
64+
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **29 pages, 423 schemas**
6565

6666
REST contracts, endpoints, routing, realtime, batch, discovery.
6767

@@ -86,7 +86,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
8686
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
8787
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageRollbackResponse`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
8888
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
89-
| [`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`, `CreateAiConversationRequest`, `CreateDataRequest`, `CreateDataResponse`, `CreateManyDataRequest`, `CreateManyDataResponse`, `DeleteDataRequest`, `DeleteDataResponse`, `DeleteManyDataRequest`, `DeleteManyDataResponse`, `DeleteMetaItemRequest`, `DeleteMetaItemResponse`, `DisablePackageRequest`, `DisablePackageResponse`, `EnablePackageRequest`, `EnablePackageResponse`, `FindDataRequest`, `FindDataResponse`, `GetDataRequest`, `GetDataResponse`, `GetDiscoveryRequest`, `GetDiscoveryResponse`, `GetEffectivePermissionsRequest`, `GetEffectivePermissionsResponse`, `GetFieldLabelsRequest`, `GetFieldLabelsResponse`, `GetLocalesRequest`, `GetLocalesResponse`, `GetMetaItemCachedRequest`, `GetMetaItemCachedResponse`, `GetMetaItemLayeredRequest`, `GetMetaItemLayeredResponse`, `GetMetaItemRequest`, `GetMetaItemResponse`, `GetMetaItemsRequest`, `GetMetaItemsResponse`, `GetMetaTypesRequest`, `GetMetaTypesResponse`, `GetNotificationPreferencesRequest`, `GetNotificationPreferencesResponse`, `GetObjectPermissionsRequest`, `GetObjectPermissionsResponse`, `GetPackageRequest`, `GetPackageResponse`, `GetPresenceRequest`, `GetPresenceResponse`, `GetTranslationsRequest`, `GetTranslationsResponse`, `GetUiViewRequest`, `GetUiViewResponse`, `HttpFindQueryParams`, `InstallPackageRequest`, `InstallPackageResponse`, `ListAiConversationsRequest`, `ListAiConversationsResponse`, `ListAiPendingActionsRequest`, `ListAiPendingActionsResponse`, `ListNotificationsRequest`, `ListNotificationsResponse`, `ListPackagesRequest`, `ListPackagesResponse`, `MarkAllNotificationsReadRequest`, `MarkAllNotificationsReadResponse`, `MarkNotificationsReadRequest`, `MarkNotificationsReadResponse`, `Notification`, `NotificationPreferences`, `PublishMetaItemRequest`, `PublishMetaItemResponse`, `PublishPackageDraftsResponse`, `RealtimeConnectRequest`, `RealtimeConnectResponse`, `RealtimeDisconnectRequest`, `RealtimeDisconnectResponse`, `RealtimeSubscribeRequest`, `RealtimeSubscribeResponse`, `RealtimeUnsubscribeRequest`, `RealtimeUnsubscribeResponse`, `RegisterDeviceRequest`, `RegisterDeviceResponse`, `RejectAiPendingActionResponse`, `RuntimeAuthoringIssue`, `SaveMetaItemRequest`, `SaveMetaItemResponse`, `SetPresenceRequest`, `SetPresenceResponse`, `UninstallPackageRequest`, `UninstallPackageResponse`, `UnregisterDeviceRequest`, `UnregisterDeviceResponse`, `UpdateAiConversationRequest`, `UpdateDataRequest`, `UpdateDataResponse`, `UpdateManyDataRequest`, `UpdateManyDataResponse`, `UpdateNotificationPreferencesRequest`, `UpdateNotificationPreferencesResponse`, `ValidateDataIssue`, `ValidateDataRequest`, `ValidateDataResponse` |
89+
| [`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`, `DisablePackageRequest`, `DisablePackageResponse`, `EnablePackageRequest`, `EnablePackageResponse`, `FindDataRequest`, `FindDataResponse`, `GetDataRequest`, `GetDataResponse`, `GetDiscoveryRequest`, `GetDiscoveryResponse`, `GetEffectivePermissionsRequest`, `GetEffectivePermissionsResponse`, `GetFieldLabelsRequest`, `GetFieldLabelsResponse`, `GetLocalesRequest`, `GetLocalesResponse`, `GetMetaItemCachedRequest`, `GetMetaItemCachedResponse`, `GetMetaItemLayeredRequest`, `GetMetaItemLayeredResponse`, `GetMetaItemRequest`, `GetMetaItemResponse`, `GetMetaItemsRequest`, `GetMetaItemsResponse`, `GetMetaTypesRequest`, `GetMetaTypesResponse`, `GetNotificationPreferencesRequest`, `GetNotificationPreferencesResponse`, `GetObjectPermissionsRequest`, `GetObjectPermissionsResponse`, `GetPackageRequest`, `GetPackageResponse`, `GetPresenceRequest`, `GetPresenceResponse`, `GetTranslationsRequest`, `GetTranslationsResponse`, `GetUiViewRequest`, `GetUiViewResponse`, `HttpFindQueryParams`, `InstallPackageRequest`, `InstallPackageResponse`, `ListAiConversationsRequest`, `ListAiConversationsResponse`, `ListAiPendingActionsRequest`, `ListAiPendingActionsResponse`, `ListNotificationsRequest`, `ListNotificationsResponse`, `ListPackagesRequest`, `ListPackagesResponse`, `MarkAllNotificationsReadRequest`, `MarkAllNotificationsReadResponse`, `MarkNotificationsReadRequest`, `MarkNotificationsReadResponse`, `Notification`, `NotificationPreferences`, `PublishMetaItemRequest`, `PublishMetaItemResponse`, `PublishPackageDraftsResponse`, `RealtimeConnectRequest`, `RealtimeConnectResponse`, `RealtimeDisconnectRequest`, `RealtimeDisconnectResponse`, `RealtimeSubscribeRequest`, `RealtimeSubscribeResponse`, `RealtimeUnsubscribeRequest`, `RealtimeUnsubscribeResponse`, `RegisterDeviceRequest`, `RegisterDeviceResponse`, `RejectAiPendingActionResponse`, `RuntimeAuthoringIssue`, `SaveMetaItemRequest`, `SaveMetaItemResponse`, `SearchAllHit`, `SearchAllResponse`, `SetPresenceRequest`, `SetPresenceResponse`, `UninstallPackageRequest`, `UninstallPackageResponse`, `UnregisterDeviceRequest`, `UnregisterDeviceResponse`, `UpdateAiConversationRequest`, `UpdateDataRequest`, `UpdateDataResponse`, `UpdateManyDataRequest`, `UpdateManyDataResponse`, `UpdateNotificationPreferencesRequest`, `UpdateNotificationPreferencesResponse`, `ValidateDataIssue`, `ValidateDataRequest`, `ValidateDataResponse` |
9090
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
9191
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
9292
| [`realtime-shared.zod.ts`](/docs/references/api/realtime-shared) | `BasePresence`, `PresenceStatus`, `RealtimeRecordAction` |

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ directory rather than per file.
257257
| Dir | Sites |
258258
|---|---|
259259
| `ai/` | 77 |
260-
| `api/` | 413 |
260+
| `api/` | 416 |
261261
| `cloud/` | 83 |
262262
| `identity/` | 32 |
263263
| `integration/` | 10 |

packages/client/exported-any-returns.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@
6262
"ObjectStackClient.auth.accounts.unlink": "#12104 — no return annotation; `return res.json()`, and lib.dom declares `Response.json(): Promise<any>`. Invisible to every grep #8140's census and #11925 used: the method names neither `any` nor `Promise` nor `unwrapResponse`. Bind the contract the route actually answers, minding the envelope.",
6363
"ObjectStackClient.automation.trigger": "#12104 — no return annotation; `return res.json()`, and lib.dom declares `Response.json(): Promise<any>`. Invisible to every grep #8140's census and #11925 used: the method names neither `any` nor `Promise` nor `unwrapResponse`. Bind the contract the route actually answers, minding the envelope.",
6464
"ObjectStackClient.automation.create": "#11924 — DELIBERATE `Promise<any>`: `POST /automation` ends `deps.success(body)`, echoing the caller's own unvalidated bytes, and `IAutomationService.registerFlow` returns nothing, so the service contract has no return shape to relay. This needs a DECISION (keep echoing, or answer the registered `FlowParsed`), not an annotation.",
65-
"ObjectStackClient.automation.update": "#11924 — DELIBERATE `Promise<any>`: `PUT /automation/:name` ends `deps.success(definition)` where `definition = body.definition ?? body`. Same missing contract as `automation.create`, and the two should be answered together since they are one route class.",
66-
"ObjectStackClient.search": "#11924 — DELIBERATE `Promise<any>`: `GET /api/v1/search` relays `protocol.searchAll(...)` verbatim and that shape is declared only as an inline annotation in `@objectstack/metadata-protocol`, which `packages/client` does not depend on. ⛔ `SearchResult` from `@objectstack/spec/contracts` is the VERIFIED near-miss: it contracts the per-object `ISearchService.search`, whose hits carry `score`/`document`, not this route's `object`/`title`/`snippet`/`record`. Binding it would typecheck and ship a false declaration; `return-type-precision.test.ts` holds a compile-time guard on that mismatch.",
67-
"ObjectStackClient.data.clone": "#11924 — DELIBERATE `Promise<any>`: `POST /data/:object/:id/clone` answers `{ object, id, sourceId, record }`, stable and server-produced but declared in no spec module. It is the structural sibling of this package's own `CreateDataResult<T>` plus `sourceId`, which is exactly what makes minting that equivalence inside a consumer the wrong move — it would create an undeclared second contract."
65+
"ObjectStackClient.automation.update": "#11924 — DELIBERATE `Promise<any>`: `PUT /automation/:name` ends `deps.success(definition)` where `definition = body.definition ?? body`. Same missing contract as `automation.create`, and the two should be answered together since they are one route class."
6866
}
6967
}

0 commit comments

Comments
 (0)