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
91 changes: 91 additions & 0 deletions .changeset/rest-api-endpoint-handler-status-retired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
"@objectstack/spec": minor
---

feat(spec): retire `RestApiEndpoint.handlerStatus` and the Route Coverage Report shapes — declared, never read; the 501 they described comes from the endpoint executor (#13823, ADR-0049)

<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->

**BREAKING** accept-set narrowing and 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_KEYS_BY_MAJOR[18]` for the key, `RETIRED_DEFS_BY_MAJOR[18]` for the
three defs, plus the D3 semantic entry
`rest-api-endpoint-handler-status-retired` — where `os migrate meta` users
will look). Maintainer ruling 2026-09-01 on #13823 (director decision batch
#27, verbatim 「同意」): remove; enforce excluded.

`handlerStatus` (`implemented` / `stub` / `planned`) was an authorable key on
`RestApiEndpointSchema` whose docstring promised that a `stub` handler
"returns 501 Not Implemented", and **nothing read it**. Measured at the
retirement base (`origin/main` a9b2be0b0, 2026-09-02, `skills/**` and tests
excluded): the only identifier hits were the declaration, its re-declaration
on `RouteCoverageEntrySchema` and a docblock saying adapters SHOULD warn on
it. The 501 it described has a different cause — every
`DispatcherErrorCode.enum.NOT_IMPLEMENTED` site (`runtime/src/endpoint-executor.ts`
×3, `runtime/src/api-mapping.ts`, `runtime/src/api-endpoint-step.ts`) is the
declarative-endpoint executor refusing a target or mapping it cannot serve,
and none consults the key. So an author who wrote `handlerStatus: 'stub'`
expecting a 501 got an ordinarily served route, and the declaration reported
progress to nobody: `RouteCoverageReportSchema`, the only shape that would
have carried it outward, had zero constructors in objectstack, objectui
(pinned sha) and cloud.

FROM → TO:

- `handlerStatus: 'implemented' | 'stub' | 'planned'` on a `RestApiEndpoint`
→ *(removed key)* — tombstoned with `retiredKey()` (the schema is not
`.strict()`, so a bare deletion would be a silent strip): authoring it is
now a `tsc` error and a parse error carrying the prescription at path
`handlerStatus`, for every former value including the documented default
`'implemented'` (prose only — the key never carried a Zod `.default()`, so
no built artifact materialised it and there is no residue window).
- `HandlerStatusSchema` / `HandlerStatus` → *(removed — no replacement)*. The
enum's only two carriers leave in this same change; an exported value
schema with no consumer reads as a capability (#3950).
- `RouteCoverageEntrySchema` / `RouteCoverageEntry` and
`RouteCoverageReportSchema` / `RouteCoverageReport` → *(removed — no
replacement)*. No adapter, dispatcher or registrar ever constructed the
report; it was a shape with no producer.

One-line fix: delete the key — nothing served changes, because nothing ever
read it. An endpoint that has no handler yet is simply not registered. Route
readiness that IS measured is untouched: the discovery payload's per-service
`status` / `handlerReady` (`api/discovery.zod.ts`) and the CI-asserted route
ledger (`packages/runtime/src/route-ledger.ts`). A declared-but-unbuilt route
answering 501 instead of 404 is a new capability the ruling explicitly
excluded (zero pull); if it is ever wanted it re-declares fresh under its own
ruling, executor first.

The retirement kit:

- key tombstone at the declaration (`api/RestApiEndpoint:handlerStatus` in
`RETIRED_KEYS_BY_MAJOR[18]`; the surface baseline line carries `[RETIRED]`)
- whole-def deletions `api/HandlerStatus`, `api/RouteCoverageEntry`,
`api/RouteCoverageReport` in `RETIRED_DEFS_BY_MAJOR[18]` (manifest keys
deliberately removed; the #4725 gate adjudicated them)
- deliberately NO D2 conversion: nothing in the tree parses
`RestApiEndpointSchema` outside its own unit tests — a REST API plugin route
registration is not a stack collection member and never a `sys_metadata`
row — so the conversion chain has no seam that would ever see one (the
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
prescription, and for the same reason the tombstone carries no
`os migrate meta` sentence
- pin tests (`api/plugin-rest-api.handler-status-retirement.test.ts`): all
three former values refused at path `handlerStatus` with the prescription,
through the route-registration embed too; a well-formed endpoint without the
key still parses and grows no `handlerStatus` property; the shipped default
route registrations still parse; zero holders for all 6 retired export names
on every public entry; the carrier schemas survive; the registrations under
major 18 are present
- no liveness-ledger row moves: `RestApiEndpointSchema` is outside the walked
population (not a registered metadata type and not in `SPEC_ONLY_SCHEMAS`),
so the verdict is recorded here, in the schema and in the D3 entry
- teaching sweep: `content/docs/**`, `examples/**`, `packages/*/README.md` and
`packages/create-objectstack/**` carry no hand-written mention; the generated
reference page regenerates. The published skill `skills/objectstack-api`
taught the key at this PR's base (a9b2be0b0); `main` already dropped that
teaching in the #14448 optimization flight (bae4088f3 — zero `handlerStatus`
/ Handler Status / RouteCoverage hits under `skills/` at 99b8faf8f, with
`RestApiEndpoint` still hitting the skill as the live control), so no
skills-lane follow-up is needed
69 changes: 4 additions & 65 deletions content/docs/references/api/plugin-rest-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,8 +60,8 @@ no plugin code at all.
## TypeScript Usage

```typescript
import { ErrorHandlingConfigSchema, HandlerStatusSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, RouteCoverageEntrySchema, RouteCoverageReportSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, HandlerStatus, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, RouteCoverageEntry, RouteCoverageReport, ValidationMode } from '@objectstack/spec/api';
import { ErrorHandlingConfigSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, ValidationMode } from '@objectstack/spec/api';

// Validate data
const result = ErrorHandlingConfigSchema.parse(data);
Expand All@@ -87,17 +87,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **redactFields** | `string[]` | optional | Field names to redact from error details |


---

## HandlerStatus

### Allowed Values

* `implemented`
* `stub`
* `planned`


---

## OpenApiGenerationConfig
Expand DownExpand Up@@ -197,7 +186,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |


---
Expand DownExpand Up@@ -372,7 +361,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |

### Nested Shape: `RestApiRouteRegistration.middleware[number]`

Expand All@@ -394,56 +383,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **tags** | `string[]` | optional | OpenAPI tags |


---

## RouteCoverageEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| 'automation' \| 'ui' \| 'realtime' \| 'notification' \| 'ai' \| 'i18n'>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## RouteCoverageReport

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **timestamp** | `string` | ✅ | ISO 8601 timestamp |
| **adapter** | `string` | ✅ | Adapter name (e.g. "hono", "express", "nextjs") |
| **summary** | `{ total: integer; implemented: integer; stub: integer; planned: integer }` | ✅ | |
| **entries** | `{ path: string; method: Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>; category: Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>; handlerStatus: Enum<'implemented' \| 'stub' \| 'planned'>; … }[]` | ✅ | Per-endpoint coverage entries |

### Nested Shape: `RouteCoverageReport.summary`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **total** | `integer` | ✅ | Total declared endpoints |
| **implemented** | `integer` | ✅ | Endpoints with real handlers |
| **stub** | `integer` | ✅ | Endpoints with stub handlers (501) |
| **planned** | `integer` | ✅ | Endpoints not yet implemented |

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

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## ValidationMode
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1593 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1590 schemas across 14 protocol modules
---

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

---

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

## API Protocol

**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 440 schemas**
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 437 schemas**

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

Expand All@@ -87,7 +87,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
| [`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`, `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` |
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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
91 changes: 91 additions & 0 deletions .changeset/rest-api-endpoint-handler-status-retired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
"@objectstack/spec": minor
---

feat(spec): retire `RestApiEndpoint.handlerStatus` and the Route Coverage Report shapes — declared, never read; the 501 they described comes from the endpoint executor (#13823, ADR-0049)

<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->

**BREAKING** accept-set narrowing and 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_KEYS_BY_MAJOR[18]` for the key, `RETIRED_DEFS_BY_MAJOR[18]` for the
three defs, plus the D3 semantic entry
`rest-api-endpoint-handler-status-retired` — where `os migrate meta` users
will look). Maintainer ruling 2026-09-01 on #13823 (director decision batch
#27, verbatim 「同意」): remove; enforce excluded.

`handlerStatus` (`implemented` / `stub` / `planned`) was an authorable key on
`RestApiEndpointSchema` whose docstring promised that a `stub` handler
"returns 501 Not Implemented", and **nothing read it**. Measured at the
retirement base (`origin/main` a9b2be0b0, 2026-09-02, `skills/**` and tests
excluded): the only identifier hits were the declaration, its re-declaration
on `RouteCoverageEntrySchema` and a docblock saying adapters SHOULD warn on
it. The 501 it described has a different cause — every
`DispatcherErrorCode.enum.NOT_IMPLEMENTED` site (`runtime/src/endpoint-executor.ts`
×3, `runtime/src/api-mapping.ts`, `runtime/src/api-endpoint-step.ts`) is the
declarative-endpoint executor refusing a target or mapping it cannot serve,
and none consults the key. So an author who wrote `handlerStatus: 'stub'`
expecting a 501 got an ordinarily served route, and the declaration reported
progress to nobody: `RouteCoverageReportSchema`, the only shape that would
have carried it outward, had zero constructors in objectstack, objectui
(pinned sha) and cloud.

FROM → TO:

- `handlerStatus: 'implemented' | 'stub' | 'planned'` on a `RestApiEndpoint`
→ *(removed key)* — tombstoned with `retiredKey()` (the schema is not
`.strict()`, so a bare deletion would be a silent strip): authoring it is
now a `tsc` error and a parse error carrying the prescription at path
`handlerStatus`, for every former value including the documented default
`'implemented'` (prose only — the key never carried a Zod `.default()`, so
no built artifact materialised it and there is no residue window).
- `HandlerStatusSchema` / `HandlerStatus` → *(removed — no replacement)*. The
enum's only two carriers leave in this same change; an exported value
schema with no consumer reads as a capability (#3950).
- `RouteCoverageEntrySchema` / `RouteCoverageEntry` and
`RouteCoverageReportSchema` / `RouteCoverageReport` → *(removed — no
replacement)*. No adapter, dispatcher or registrar ever constructed the
report; it was a shape with no producer.

One-line fix: delete the key — nothing served changes, because nothing ever
read it. An endpoint that has no handler yet is simply not registered. Route
readiness that IS measured is untouched: the discovery payload's per-service
`status` / `handlerReady` (`api/discovery.zod.ts`) and the CI-asserted route
ledger (`packages/runtime/src/route-ledger.ts`). A declared-but-unbuilt route
answering 501 instead of 404 is a new capability the ruling explicitly
excluded (zero pull); if it is ever wanted it re-declares fresh under its own
ruling, executor first.

The retirement kit:

- key tombstone at the declaration (`api/RestApiEndpoint:handlerStatus` in
`RETIRED_KEYS_BY_MAJOR[18]`; the surface baseline line carries `[RETIRED]`)
- whole-def deletions `api/HandlerStatus`, `api/RouteCoverageEntry`,
`api/RouteCoverageReport` in `RETIRED_DEFS_BY_MAJOR[18]` (manifest keys
deliberately removed; the #4725 gate adjudicated them)
- deliberately NO D2 conversion: nothing in the tree parses
`RestApiEndpointSchema` outside its own unit tests — a REST API plugin route
registration is not a stack collection member and never a `sys_metadata`
row — so the conversion chain has no seam that would ever see one (the
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
prescription, and for the same reason the tombstone carries no
`os migrate meta` sentence
- pin tests (`api/plugin-rest-api.handler-status-retirement.test.ts`): all
three former values refused at path `handlerStatus` with the prescription,
through the route-registration embed too; a well-formed endpoint without the
key still parses and grows no `handlerStatus` property; the shipped default
route registrations still parse; zero holders for all 6 retired export names
on every public entry; the carrier schemas survive; the registrations under
major 18 are present
- no liveness-ledger row moves: `RestApiEndpointSchema` is outside the walked
population (not a registered metadata type and not in `SPEC_ONLY_SCHEMAS`),
so the verdict is recorded here, in the schema and in the D3 entry
- teaching sweep: `content/docs/**`, `examples/**`, `packages/*/README.md` and
`packages/create-objectstack/**` carry no hand-written mention; the generated
reference page regenerates. The published skill `skills/objectstack-api`
taught the key at this PR's base (a9b2be0b0); `main` already dropped that
teaching in the #14448 optimization flight (bae4088f3 — zero `handlerStatus`
/ Handler Status / RouteCoverage hits under `skills/` at 99b8faf8f, with
`RestApiEndpoint` still hitting the skill as the live control), so no
skills-lane follow-up is needed
69 changes: 4 additions & 65 deletions content/docs/references/api/plugin-rest-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,8 +60,8 @@ no plugin code at all.
## TypeScript Usage

```typescript
import { ErrorHandlingConfigSchema, HandlerStatusSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, RouteCoverageEntrySchema, RouteCoverageReportSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, HandlerStatus, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, RouteCoverageEntry, RouteCoverageReport, ValidationMode } from '@objectstack/spec/api';
import { ErrorHandlingConfigSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, ValidationMode } from '@objectstack/spec/api';

// Validate data
const result = ErrorHandlingConfigSchema.parse(data);
Expand All@@ -87,17 +87,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **redactFields** | `string[]` | optional | Field names to redact from error details |


---

## HandlerStatus

### Allowed Values

* `implemented`
* `stub`
* `planned`


---

## OpenApiGenerationConfig
Expand DownExpand Up@@ -197,7 +186,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |


---
Expand DownExpand Up@@ -372,7 +361,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |

### Nested Shape: `RestApiRouteRegistration.middleware[number]`

Expand All@@ -394,56 +383,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **tags** | `string[]` | optional | OpenAPI tags |


---

## RouteCoverageEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| 'automation' \| 'ui' \| 'realtime' \| 'notification' \| 'ai' \| 'i18n'>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## RouteCoverageReport

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **timestamp** | `string` | ✅ | ISO 8601 timestamp |
| **adapter** | `string` | ✅ | Adapter name (e.g. "hono", "express", "nextjs") |
| **summary** | `{ total: integer; implemented: integer; stub: integer; planned: integer }` | ✅ | |
| **entries** | `{ path: string; method: Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>; category: Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>; handlerStatus: Enum<'implemented' \| 'stub' \| 'planned'>; … }[]` | ✅ | Per-endpoint coverage entries |

### Nested Shape: `RouteCoverageReport.summary`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **total** | `integer` | ✅ | Total declared endpoints |
| **implemented** | `integer` | ✅ | Endpoints with real handlers |
| **stub** | `integer` | ✅ | Endpoints with stub handlers (501) |
| **planned** | `integer` | ✅ | Endpoints not yet implemented |

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

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## ValidationMode
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1593 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1590 schemas across 14 protocol modules
---

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

---

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

## API Protocol

**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 440 schemas**
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 437 schemas**

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

Expand All@@ -87,7 +87,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
| [`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`, `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` |
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
91 changes: 91 additions & 0 deletions .changeset/rest-api-endpoint-handler-status-retired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
"@objectstack/spec": minor
---

feat(spec): retire `RestApiEndpoint.handlerStatus` and the Route Coverage Report shapes — declared, never read; the 501 they described comes from the endpoint executor (#13823, ADR-0049)

<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->

**BREAKING** accept-set narrowing and 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_KEYS_BY_MAJOR[18]` for the key, `RETIRED_DEFS_BY_MAJOR[18]` for the
three defs, plus the D3 semantic entry
`rest-api-endpoint-handler-status-retired` — where `os migrate meta` users
will look). Maintainer ruling 2026-09-01 on #13823 (director decision batch
#27, verbatim 「同意」): remove; enforce excluded.

`handlerStatus` (`implemented` / `stub` / `planned`) was an authorable key on
`RestApiEndpointSchema` whose docstring promised that a `stub` handler
"returns 501 Not Implemented", and **nothing read it**. Measured at the
retirement base (`origin/main` a9b2be0b0, 2026-09-02, `skills/**` and tests
excluded): the only identifier hits were the declaration, its re-declaration
on `RouteCoverageEntrySchema` and a docblock saying adapters SHOULD warn on
it. The 501 it described has a different cause — every
`DispatcherErrorCode.enum.NOT_IMPLEMENTED` site (`runtime/src/endpoint-executor.ts`
×3, `runtime/src/api-mapping.ts`, `runtime/src/api-endpoint-step.ts`) is the
declarative-endpoint executor refusing a target or mapping it cannot serve,
and none consults the key. So an author who wrote `handlerStatus: 'stub'`
expecting a 501 got an ordinarily served route, and the declaration reported
progress to nobody: `RouteCoverageReportSchema`, the only shape that would
have carried it outward, had zero constructors in objectstack, objectui
(pinned sha) and cloud.

FROM → TO:

- `handlerStatus: 'implemented' | 'stub' | 'planned'` on a `RestApiEndpoint`
→ *(removed key)* — tombstoned with `retiredKey()` (the schema is not
`.strict()`, so a bare deletion would be a silent strip): authoring it is
now a `tsc` error and a parse error carrying the prescription at path
`handlerStatus`, for every former value including the documented default
`'implemented'` (prose only — the key never carried a Zod `.default()`, so
no built artifact materialised it and there is no residue window).
- `HandlerStatusSchema` / `HandlerStatus` → *(removed — no replacement)*. The
enum's only two carriers leave in this same change; an exported value
schema with no consumer reads as a capability (#3950).
- `RouteCoverageEntrySchema` / `RouteCoverageEntry` and
`RouteCoverageReportSchema` / `RouteCoverageReport` → *(removed — no
replacement)*. No adapter, dispatcher or registrar ever constructed the
report; it was a shape with no producer.

One-line fix: delete the key — nothing served changes, because nothing ever
read it. An endpoint that has no handler yet is simply not registered. Route
readiness that IS measured is untouched: the discovery payload's per-service
`status` / `handlerReady` (`api/discovery.zod.ts`) and the CI-asserted route
ledger (`packages/runtime/src/route-ledger.ts`). A declared-but-unbuilt route
answering 501 instead of 404 is a new capability the ruling explicitly
excluded (zero pull); if it is ever wanted it re-declares fresh under its own
ruling, executor first.

The retirement kit:

- key tombstone at the declaration (`api/RestApiEndpoint:handlerStatus` in
`RETIRED_KEYS_BY_MAJOR[18]`; the surface baseline line carries `[RETIRED]`)
- whole-def deletions `api/HandlerStatus`, `api/RouteCoverageEntry`,
`api/RouteCoverageReport` in `RETIRED_DEFS_BY_MAJOR[18]` (manifest keys
deliberately removed; the #4725 gate adjudicated them)
- deliberately NO D2 conversion: nothing in the tree parses
`RestApiEndpointSchema` outside its own unit tests — a REST API plugin route
registration is not a stack collection member and never a `sys_metadata`
row — so the conversion chain has no seam that would ever see one (the
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
prescription, and for the same reason the tombstone carries no
`os migrate meta` sentence
- pin tests (`api/plugin-rest-api.handler-status-retirement.test.ts`): all
three former values refused at path `handlerStatus` with the prescription,
through the route-registration embed too; a well-formed endpoint without the
key still parses and grows no `handlerStatus` property; the shipped default
route registrations still parse; zero holders for all 6 retired export names
on every public entry; the carrier schemas survive; the registrations under
major 18 are present
- no liveness-ledger row moves: `RestApiEndpointSchema` is outside the walked
population (not a registered metadata type and not in `SPEC_ONLY_SCHEMAS`),
so the verdict is recorded here, in the schema and in the D3 entry
- teaching sweep: `content/docs/**`, `examples/**`, `packages/*/README.md` and
`packages/create-objectstack/**` carry no hand-written mention; the generated
reference page regenerates. The published skill `skills/objectstack-api`
taught the key at this PR's base (a9b2be0b0); `main` already dropped that
teaching in the #14448 optimization flight (bae4088f3 — zero `handlerStatus`
/ Handler Status / RouteCoverage hits under `skills/` at 99b8faf8f, with
`RestApiEndpoint` still hitting the skill as the live control), so no
skills-lane follow-up is needed
69 changes: 4 additions & 65 deletions content/docs/references/api/plugin-rest-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,8 +60,8 @@ no plugin code at all.
## TypeScript Usage

```typescript
import { ErrorHandlingConfigSchema, HandlerStatusSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, RouteCoverageEntrySchema, RouteCoverageReportSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, HandlerStatus, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, RouteCoverageEntry, RouteCoverageReport, ValidationMode } from '@objectstack/spec/api';
import { ErrorHandlingConfigSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, ValidationMode } from '@objectstack/spec/api';

// Validate data
const result = ErrorHandlingConfigSchema.parse(data);
Expand All@@ -87,17 +87,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **redactFields** | `string[]` | optional | Field names to redact from error details |


---

## HandlerStatus

### Allowed Values

* `implemented`
* `stub`
* `planned`


---

## OpenApiGenerationConfig
Expand DownExpand Up@@ -197,7 +186,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |


---
Expand DownExpand Up@@ -372,7 +361,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |

### Nested Shape: `RestApiRouteRegistration.middleware[number]`

Expand All@@ -394,56 +383,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **tags** | `string[]` | optional | OpenAPI tags |


---

## RouteCoverageEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| 'automation' \| 'ui' \| 'realtime' \| 'notification' \| 'ai' \| 'i18n'>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## RouteCoverageReport

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **timestamp** | `string` | ✅ | ISO 8601 timestamp |
| **adapter** | `string` | ✅ | Adapter name (e.g. "hono", "express", "nextjs") |
| **summary** | `{ total: integer; implemented: integer; stub: integer; planned: integer }` | ✅ | |
| **entries** | `{ path: string; method: Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>; category: Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>; handlerStatus: Enum<'implemented' \| 'stub' \| 'planned'>; … }[]` | ✅ | Per-endpoint coverage entries |

### Nested Shape: `RouteCoverageReport.summary`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **total** | `integer` | ✅ | Total declared endpoints |
| **implemented** | `integer` | ✅ | Endpoints with real handlers |
| **stub** | `integer` | ✅ | Endpoints with stub handlers (501) |
| **planned** | `integer` | ✅ | Endpoints not yet implemented |

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

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## ValidationMode
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1593 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1590 schemas across 14 protocol modules
---

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

---

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

## API Protocol

**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 440 schemas**
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 437 schemas**

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

Expand All@@ -87,7 +87,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
| [`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`, `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` |
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
91 changes: 91 additions & 0 deletions .changeset/rest-api-endpoint-handler-status-retired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
"@objectstack/spec": minor
---

feat(spec): retire `RestApiEndpoint.handlerStatus` and the Route Coverage Report shapes — declared, never read; the 501 they described comes from the endpoint executor (#13823, ADR-0049)

<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->

**BREAKING** accept-set narrowing and 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_KEYS_BY_MAJOR[18]` for the key, `RETIRED_DEFS_BY_MAJOR[18]` for the
three defs, plus the D3 semantic entry
`rest-api-endpoint-handler-status-retired` — where `os migrate meta` users
will look). Maintainer ruling 2026-09-01 on #13823 (director decision batch
#27, verbatim 「同意」): remove; enforce excluded.

`handlerStatus` (`implemented` / `stub` / `planned`) was an authorable key on
`RestApiEndpointSchema` whose docstring promised that a `stub` handler
"returns 501 Not Implemented", and **nothing read it**. Measured at the
retirement base (`origin/main` a9b2be0b0, 2026-09-02, `skills/**` and tests
excluded): the only identifier hits were the declaration, its re-declaration
on `RouteCoverageEntrySchema` and a docblock saying adapters SHOULD warn on
it. The 501 it described has a different cause — every
`DispatcherErrorCode.enum.NOT_IMPLEMENTED` site (`runtime/src/endpoint-executor.ts`
×3, `runtime/src/api-mapping.ts`, `runtime/src/api-endpoint-step.ts`) is the
declarative-endpoint executor refusing a target or mapping it cannot serve,
and none consults the key. So an author who wrote `handlerStatus: 'stub'`
expecting a 501 got an ordinarily served route, and the declaration reported
progress to nobody: `RouteCoverageReportSchema`, the only shape that would
have carried it outward, had zero constructors in objectstack, objectui
(pinned sha) and cloud.

FROM → TO:

- `handlerStatus: 'implemented' | 'stub' | 'planned'` on a `RestApiEndpoint`
→ *(removed key)* — tombstoned with `retiredKey()` (the schema is not
`.strict()`, so a bare deletion would be a silent strip): authoring it is
now a `tsc` error and a parse error carrying the prescription at path
`handlerStatus`, for every former value including the documented default
`'implemented'` (prose only — the key never carried a Zod `.default()`, so
no built artifact materialised it and there is no residue window).
- `HandlerStatusSchema` / `HandlerStatus` → *(removed — no replacement)*. The
enum's only two carriers leave in this same change; an exported value
schema with no consumer reads as a capability (#3950).
- `RouteCoverageEntrySchema` / `RouteCoverageEntry` and
`RouteCoverageReportSchema` / `RouteCoverageReport` → *(removed — no
replacement)*. No adapter, dispatcher or registrar ever constructed the
report; it was a shape with no producer.

One-line fix: delete the key — nothing served changes, because nothing ever
read it. An endpoint that has no handler yet is simply not registered. Route
readiness that IS measured is untouched: the discovery payload's per-service
`status` / `handlerReady` (`api/discovery.zod.ts`) and the CI-asserted route
ledger (`packages/runtime/src/route-ledger.ts`). A declared-but-unbuilt route
answering 501 instead of 404 is a new capability the ruling explicitly
excluded (zero pull); if it is ever wanted it re-declares fresh under its own
ruling, executor first.

The retirement kit:

- key tombstone at the declaration (`api/RestApiEndpoint:handlerStatus` in
`RETIRED_KEYS_BY_MAJOR[18]`; the surface baseline line carries `[RETIRED]`)
- whole-def deletions `api/HandlerStatus`, `api/RouteCoverageEntry`,
`api/RouteCoverageReport` in `RETIRED_DEFS_BY_MAJOR[18]` (manifest keys
deliberately removed; the #4725 gate adjudicated them)
- deliberately NO D2 conversion: nothing in the tree parses
`RestApiEndpointSchema` outside its own unit tests — a REST API plugin route
registration is not a stack collection member and never a `sys_metadata`
row — so the conversion chain has no seam that would ever see one (the
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
prescription, and for the same reason the tombstone carries no
`os migrate meta` sentence
- pin tests (`api/plugin-rest-api.handler-status-retirement.test.ts`): all
three former values refused at path `handlerStatus` with the prescription,
through the route-registration embed too; a well-formed endpoint without the
key still parses and grows no `handlerStatus` property; the shipped default
route registrations still parse; zero holders for all 6 retired export names
on every public entry; the carrier schemas survive; the registrations under
major 18 are present
- no liveness-ledger row moves: `RestApiEndpointSchema` is outside the walked
population (not a registered metadata type and not in `SPEC_ONLY_SCHEMAS`),
so the verdict is recorded here, in the schema and in the D3 entry
- teaching sweep: `content/docs/**`, `examples/**`, `packages/*/README.md` and
`packages/create-objectstack/**` carry no hand-written mention; the generated
reference page regenerates. The published skill `skills/objectstack-api`
taught the key at this PR's base (a9b2be0b0); `main` already dropped that
teaching in the #14448 optimization flight (bae4088f3 — zero `handlerStatus`
/ Handler Status / RouteCoverage hits under `skills/` at 99b8faf8f, with
`RestApiEndpoint` still hitting the skill as the live control), so no
skills-lane follow-up is needed
69 changes: 4 additions & 65 deletions content/docs/references/api/plugin-rest-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,8 +60,8 @@ no plugin code at all.
## TypeScript Usage

```typescript
import { ErrorHandlingConfigSchema, HandlerStatusSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, RouteCoverageEntrySchema, RouteCoverageReportSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, HandlerStatus, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, RouteCoverageEntry, RouteCoverageReport, ValidationMode } from '@objectstack/spec/api';
import { ErrorHandlingConfigSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, ValidationMode } from '@objectstack/spec/api';

// Validate data
const result = ErrorHandlingConfigSchema.parse(data);
Expand All@@ -87,17 +87,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **redactFields** | `string[]` | optional | Field names to redact from error details |


---

## HandlerStatus

### Allowed Values

* `implemented`
* `stub`
* `planned`


---

## OpenApiGenerationConfig
Expand DownExpand Up@@ -197,7 +186,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |


---
Expand DownExpand Up@@ -372,7 +361,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |

### Nested Shape: `RestApiRouteRegistration.middleware[number]`

Expand All@@ -394,56 +383,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **tags** | `string[]` | optional | OpenAPI tags |


---

## RouteCoverageEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| 'automation' \| 'ui' \| 'realtime' \| 'notification' \| 'ai' \| 'i18n'>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## RouteCoverageReport

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **timestamp** | `string` | ✅ | ISO 8601 timestamp |
| **adapter** | `string` | ✅ | Adapter name (e.g. "hono", "express", "nextjs") |
| **summary** | `{ total: integer; implemented: integer; stub: integer; planned: integer }` | ✅ | |
| **entries** | `{ path: string; method: Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>; category: Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>; handlerStatus: Enum<'implemented' \| 'stub' \| 'planned'>; … }[]` | ✅ | Per-endpoint coverage entries |

### Nested Shape: `RouteCoverageReport.summary`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **total** | `integer` | ✅ | Total declared endpoints |
| **implemented** | `integer` | ✅ | Endpoints with real handlers |
| **stub** | `integer` | ✅ | Endpoints with stub handlers (501) |
| **planned** | `integer` | ✅ | Endpoints not yet implemented |

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

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## ValidationMode
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1593 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1590 schemas across 14 protocol modules
---

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

---

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

## API Protocol

**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 440 schemas**
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 437 schemas**

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

Expand All@@ -87,7 +87,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
| [`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`, `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` |
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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
91 changes: 91 additions & 0 deletions .changeset/rest-api-endpoint-handler-status-retired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
"@objectstack/spec": minor
---

feat(spec): retire `RestApiEndpoint.handlerStatus` and the Route Coverage Report shapes — declared, never read; the 501 they described comes from the endpoint executor (#13823, ADR-0049)

<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->

**BREAKING** accept-set narrowing and 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_KEYS_BY_MAJOR[18]` for the key, `RETIRED_DEFS_BY_MAJOR[18]` for the
three defs, plus the D3 semantic entry
`rest-api-endpoint-handler-status-retired` — where `os migrate meta` users
will look). Maintainer ruling 2026-09-01 on #13823 (director decision batch
#27, verbatim 「同意」): remove; enforce excluded.

`handlerStatus` (`implemented` / `stub` / `planned`) was an authorable key on
`RestApiEndpointSchema` whose docstring promised that a `stub` handler
"returns 501 Not Implemented", and **nothing read it**. Measured at the
retirement base (`origin/main` a9b2be0b0, 2026-09-02, `skills/**` and tests
excluded): the only identifier hits were the declaration, its re-declaration
on `RouteCoverageEntrySchema` and a docblock saying adapters SHOULD warn on
it. The 501 it described has a different cause — every
`DispatcherErrorCode.enum.NOT_IMPLEMENTED` site (`runtime/src/endpoint-executor.ts`
×3, `runtime/src/api-mapping.ts`, `runtime/src/api-endpoint-step.ts`) is the
declarative-endpoint executor refusing a target or mapping it cannot serve,
and none consults the key. So an author who wrote `handlerStatus: 'stub'`
expecting a 501 got an ordinarily served route, and the declaration reported
progress to nobody: `RouteCoverageReportSchema`, the only shape that would
have carried it outward, had zero constructors in objectstack, objectui
(pinned sha) and cloud.

FROM → TO:

- `handlerStatus: 'implemented' | 'stub' | 'planned'` on a `RestApiEndpoint`
→ *(removed key)* — tombstoned with `retiredKey()` (the schema is not
`.strict()`, so a bare deletion would be a silent strip): authoring it is
now a `tsc` error and a parse error carrying the prescription at path
`handlerStatus`, for every former value including the documented default
`'implemented'` (prose only — the key never carried a Zod `.default()`, so
no built artifact materialised it and there is no residue window).
- `HandlerStatusSchema` / `HandlerStatus` → *(removed — no replacement)*. The
enum's only two carriers leave in this same change; an exported value
schema with no consumer reads as a capability (#3950).
- `RouteCoverageEntrySchema` / `RouteCoverageEntry` and
`RouteCoverageReportSchema` / `RouteCoverageReport` → *(removed — no
replacement)*. No adapter, dispatcher or registrar ever constructed the
report; it was a shape with no producer.

One-line fix: delete the key — nothing served changes, because nothing ever
read it. An endpoint that has no handler yet is simply not registered. Route
readiness that IS measured is untouched: the discovery payload's per-service
`status` / `handlerReady` (`api/discovery.zod.ts`) and the CI-asserted route
ledger (`packages/runtime/src/route-ledger.ts`). A declared-but-unbuilt route
answering 501 instead of 404 is a new capability the ruling explicitly
excluded (zero pull); if it is ever wanted it re-declares fresh under its own
ruling, executor first.

The retirement kit:

- key tombstone at the declaration (`api/RestApiEndpoint:handlerStatus` in
`RETIRED_KEYS_BY_MAJOR[18]`; the surface baseline line carries `[RETIRED]`)
- whole-def deletions `api/HandlerStatus`, `api/RouteCoverageEntry`,
`api/RouteCoverageReport` in `RETIRED_DEFS_BY_MAJOR[18]` (manifest keys
deliberately removed; the #4725 gate adjudicated them)
- deliberately NO D2 conversion: nothing in the tree parses
`RestApiEndpointSchema` outside its own unit tests — a REST API plugin route
registration is not a stack collection member and never a `sys_metadata`
row — so the conversion chain has no seam that would ever see one (the
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
prescription, and for the same reason the tombstone carries no
`os migrate meta` sentence
- pin tests (`api/plugin-rest-api.handler-status-retirement.test.ts`): all
three former values refused at path `handlerStatus` with the prescription,
through the route-registration embed too; a well-formed endpoint without the
key still parses and grows no `handlerStatus` property; the shipped default
route registrations still parse; zero holders for all 6 retired export names
on every public entry; the carrier schemas survive; the registrations under
major 18 are present
- no liveness-ledger row moves: `RestApiEndpointSchema` is outside the walked
population (not a registered metadata type and not in `SPEC_ONLY_SCHEMAS`),
so the verdict is recorded here, in the schema and in the D3 entry
- teaching sweep: `content/docs/**`, `examples/**`, `packages/*/README.md` and
`packages/create-objectstack/**` carry no hand-written mention; the generated
reference page regenerates. The published skill `skills/objectstack-api`
taught the key at this PR's base (a9b2be0b0); `main` already dropped that
teaching in the #14448 optimization flight (bae4088f3 — zero `handlerStatus`
/ Handler Status / RouteCoverage hits under `skills/` at 99b8faf8f, with
`RestApiEndpoint` still hitting the skill as the live control), so no
skills-lane follow-up is needed
69 changes: 4 additions & 65 deletions content/docs/references/api/plugin-rest-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,8 +60,8 @@ no plugin code at all.
## TypeScript Usage

```typescript
import { ErrorHandlingConfigSchema, HandlerStatusSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, RouteCoverageEntrySchema, RouteCoverageReportSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, HandlerStatus, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, RouteCoverageEntry, RouteCoverageReport, ValidationMode } from '@objectstack/spec/api';
import { ErrorHandlingConfigSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, ValidationMode } from '@objectstack/spec/api';

// Validate data
const result = ErrorHandlingConfigSchema.parse(data);
Expand All@@ -87,17 +87,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **redactFields** | `string[]` | optional | Field names to redact from error details |


---

## HandlerStatus

### Allowed Values

* `implemented`
* `stub`
* `planned`


---

## OpenApiGenerationConfig
Expand DownExpand Up@@ -197,7 +186,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |


---
Expand DownExpand Up@@ -372,7 +361,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |

### Nested Shape: `RestApiRouteRegistration.middleware[number]`

Expand All@@ -394,56 +383,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **tags** | `string[]` | optional | OpenAPI tags |


---

## RouteCoverageEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| 'automation' \| 'ui' \| 'realtime' \| 'notification' \| 'ai' \| 'i18n'>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## RouteCoverageReport

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **timestamp** | `string` | ✅ | ISO 8601 timestamp |
| **adapter** | `string` | ✅ | Adapter name (e.g. "hono", "express", "nextjs") |
| **summary** | `{ total: integer; implemented: integer; stub: integer; planned: integer }` | ✅ | |
| **entries** | `{ path: string; method: Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>; category: Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>; handlerStatus: Enum<'implemented' \| 'stub' \| 'planned'>; … }[]` | ✅ | Per-endpoint coverage entries |

### Nested Shape: `RouteCoverageReport.summary`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **total** | `integer` | ✅ | Total declared endpoints |
| **implemented** | `integer` | ✅ | Endpoints with real handlers |
| **stub** | `integer` | ✅ | Endpoints with stub handlers (501) |
| **planned** | `integer` | ✅ | Endpoints not yet implemented |

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

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## ValidationMode
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1593 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1590 schemas across 14 protocol modules
---

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

---

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

## API Protocol

**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 440 schemas**
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 437 schemas**

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

Expand All@@ -87,7 +87,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
| [`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`, `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` |
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
91 changes: 91 additions & 0 deletions .changeset/rest-api-endpoint-handler-status-retired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
"@objectstack/spec": minor
---

feat(spec): retire `RestApiEndpoint.handlerStatus` and the Route Coverage Report shapes — declared, never read; the 501 they described comes from the endpoint executor (#13823, ADR-0049)

<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->

**BREAKING** accept-set narrowing and 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_KEYS_BY_MAJOR[18]` for the key, `RETIRED_DEFS_BY_MAJOR[18]` for the
three defs, plus the D3 semantic entry
`rest-api-endpoint-handler-status-retired` — where `os migrate meta` users
will look). Maintainer ruling 2026-09-01 on #13823 (director decision batch
#27, verbatim 「同意」): remove; enforce excluded.

`handlerStatus` (`implemented` / `stub` / `planned`) was an authorable key on
`RestApiEndpointSchema` whose docstring promised that a `stub` handler
"returns 501 Not Implemented", and **nothing read it**. Measured at the
retirement base (`origin/main` a9b2be0b0, 2026-09-02, `skills/**` and tests
excluded): the only identifier hits were the declaration, its re-declaration
on `RouteCoverageEntrySchema` and a docblock saying adapters SHOULD warn on
it. The 501 it described has a different cause — every
`DispatcherErrorCode.enum.NOT_IMPLEMENTED` site (`runtime/src/endpoint-executor.ts`
×3, `runtime/src/api-mapping.ts`, `runtime/src/api-endpoint-step.ts`) is the
declarative-endpoint executor refusing a target or mapping it cannot serve,
and none consults the key. So an author who wrote `handlerStatus: 'stub'`
expecting a 501 got an ordinarily served route, and the declaration reported
progress to nobody: `RouteCoverageReportSchema`, the only shape that would
have carried it outward, had zero constructors in objectstack, objectui
(pinned sha) and cloud.

FROM → TO:

- `handlerStatus: 'implemented' | 'stub' | 'planned'` on a `RestApiEndpoint`
→ *(removed key)* — tombstoned with `retiredKey()` (the schema is not
`.strict()`, so a bare deletion would be a silent strip): authoring it is
now a `tsc` error and a parse error carrying the prescription at path
`handlerStatus`, for every former value including the documented default
`'implemented'` (prose only — the key never carried a Zod `.default()`, so
no built artifact materialised it and there is no residue window).
- `HandlerStatusSchema` / `HandlerStatus` → *(removed — no replacement)*. The
enum's only two carriers leave in this same change; an exported value
schema with no consumer reads as a capability (#3950).
- `RouteCoverageEntrySchema` / `RouteCoverageEntry` and
`RouteCoverageReportSchema` / `RouteCoverageReport` → *(removed — no
replacement)*. No adapter, dispatcher or registrar ever constructed the
report; it was a shape with no producer.

One-line fix: delete the key — nothing served changes, because nothing ever
read it. An endpoint that has no handler yet is simply not registered. Route
readiness that IS measured is untouched: the discovery payload's per-service
`status` / `handlerReady` (`api/discovery.zod.ts`) and the CI-asserted route
ledger (`packages/runtime/src/route-ledger.ts`). A declared-but-unbuilt route
answering 501 instead of 404 is a new capability the ruling explicitly
excluded (zero pull); if it is ever wanted it re-declares fresh under its own
ruling, executor first.

The retirement kit:

- key tombstone at the declaration (`api/RestApiEndpoint:handlerStatus` in
`RETIRED_KEYS_BY_MAJOR[18]`; the surface baseline line carries `[RETIRED]`)
- whole-def deletions `api/HandlerStatus`, `api/RouteCoverageEntry`,
`api/RouteCoverageReport` in `RETIRED_DEFS_BY_MAJOR[18]` (manifest keys
deliberately removed; the #4725 gate adjudicated them)
- deliberately NO D2 conversion: nothing in the tree parses
`RestApiEndpointSchema` outside its own unit tests — a REST API plugin route
registration is not a stack collection member and never a `sys_metadata`
row — so the conversion chain has no seam that would ever see one (the
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
prescription, and for the same reason the tombstone carries no
`os migrate meta` sentence
- pin tests (`api/plugin-rest-api.handler-status-retirement.test.ts`): all
three former values refused at path `handlerStatus` with the prescription,
through the route-registration embed too; a well-formed endpoint without the
key still parses and grows no `handlerStatus` property; the shipped default
route registrations still parse; zero holders for all 6 retired export names
on every public entry; the carrier schemas survive; the registrations under
major 18 are present
- no liveness-ledger row moves: `RestApiEndpointSchema` is outside the walked
population (not a registered metadata type and not in `SPEC_ONLY_SCHEMAS`),
so the verdict is recorded here, in the schema and in the D3 entry
- teaching sweep: `content/docs/**`, `examples/**`, `packages/*/README.md` and
`packages/create-objectstack/**` carry no hand-written mention; the generated
reference page regenerates. The published skill `skills/objectstack-api`
taught the key at this PR's base (a9b2be0b0); `main` already dropped that
teaching in the #14448 optimization flight (bae4088f3 — zero `handlerStatus`
/ Handler Status / RouteCoverage hits under `skills/` at 99b8faf8f, with
`RestApiEndpoint` still hitting the skill as the live control), so no
skills-lane follow-up is needed
69 changes: 4 additions & 65 deletions content/docs/references/api/plugin-rest-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,8 +60,8 @@ no plugin code at all.
## TypeScript Usage

```typescript
import { ErrorHandlingConfigSchema, HandlerStatusSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, RouteCoverageEntrySchema, RouteCoverageReportSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, HandlerStatus, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, RouteCoverageEntry, RouteCoverageReport, ValidationMode } from '@objectstack/spec/api';
import { ErrorHandlingConfigSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, ValidationMode } from '@objectstack/spec/api';

// Validate data
const result = ErrorHandlingConfigSchema.parse(data);
Expand All@@ -87,17 +87,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **redactFields** | `string[]` | optional | Field names to redact from error details |


---

## HandlerStatus

### Allowed Values

* `implemented`
* `stub`
* `planned`


---

## OpenApiGenerationConfig
Expand DownExpand Up@@ -197,7 +186,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |


---
Expand DownExpand Up@@ -372,7 +361,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |

### Nested Shape: `RestApiRouteRegistration.middleware[number]`

Expand All@@ -394,56 +383,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **tags** | `string[]` | optional | OpenAPI tags |


---

## RouteCoverageEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| 'automation' \| 'ui' \| 'realtime' \| 'notification' \| 'ai' \| 'i18n'>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## RouteCoverageReport

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **timestamp** | `string` | ✅ | ISO 8601 timestamp |
| **adapter** | `string` | ✅ | Adapter name (e.g. "hono", "express", "nextjs") |
| **summary** | `{ total: integer; implemented: integer; stub: integer; planned: integer }` | ✅ | |
| **entries** | `{ path: string; method: Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>; category: Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>; handlerStatus: Enum<'implemented' \| 'stub' \| 'planned'>; … }[]` | ✅ | Per-endpoint coverage entries |

### Nested Shape: `RouteCoverageReport.summary`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **total** | `integer` | ✅ | Total declared endpoints |
| **implemented** | `integer` | ✅ | Endpoints with real handlers |
| **stub** | `integer` | ✅ | Endpoints with stub handlers (501) |
| **planned** | `integer` | ✅ | Endpoints not yet implemented |

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

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## ValidationMode
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1593 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1590 schemas across 14 protocol modules
---

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

---

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

## API Protocol

**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 440 schemas**
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 437 schemas**

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

Expand All@@ -87,7 +87,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
| [`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`, `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` |
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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
91 changes: 91 additions & 0 deletions .changeset/rest-api-endpoint-handler-status-retired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
"@objectstack/spec": minor
---

feat(spec): retire `RestApiEndpoint.handlerStatus` and the Route Coverage Report shapes — declared, never read; the 501 they described comes from the endpoint executor (#13823, ADR-0049)

<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->

**BREAKING** accept-set narrowing and 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_KEYS_BY_MAJOR[18]` for the key, `RETIRED_DEFS_BY_MAJOR[18]` for the
three defs, plus the D3 semantic entry
`rest-api-endpoint-handler-status-retired` — where `os migrate meta` users
will look). Maintainer ruling 2026-09-01 on #13823 (director decision batch
#27, verbatim 「同意」): remove; enforce excluded.

`handlerStatus` (`implemented` / `stub` / `planned`) was an authorable key on
`RestApiEndpointSchema` whose docstring promised that a `stub` handler
"returns 501 Not Implemented", and **nothing read it**. Measured at the
retirement base (`origin/main` a9b2be0b0, 2026-09-02, `skills/**` and tests
excluded): the only identifier hits were the declaration, its re-declaration
on `RouteCoverageEntrySchema` and a docblock saying adapters SHOULD warn on
it. The 501 it described has a different cause — every
`DispatcherErrorCode.enum.NOT_IMPLEMENTED` site (`runtime/src/endpoint-executor.ts`
×3, `runtime/src/api-mapping.ts`, `runtime/src/api-endpoint-step.ts`) is the
declarative-endpoint executor refusing a target or mapping it cannot serve,
and none consults the key. So an author who wrote `handlerStatus: 'stub'`
expecting a 501 got an ordinarily served route, and the declaration reported
progress to nobody: `RouteCoverageReportSchema`, the only shape that would
have carried it outward, had zero constructors in objectstack, objectui
(pinned sha) and cloud.

FROM → TO:

- `handlerStatus: 'implemented' | 'stub' | 'planned'` on a `RestApiEndpoint`
→ *(removed key)* — tombstoned with `retiredKey()` (the schema is not
`.strict()`, so a bare deletion would be a silent strip): authoring it is
now a `tsc` error and a parse error carrying the prescription at path
`handlerStatus`, for every former value including the documented default
`'implemented'` (prose only — the key never carried a Zod `.default()`, so
no built artifact materialised it and there is no residue window).
- `HandlerStatusSchema` / `HandlerStatus` → *(removed — no replacement)*. The
enum's only two carriers leave in this same change; an exported value
schema with no consumer reads as a capability (#3950).
- `RouteCoverageEntrySchema` / `RouteCoverageEntry` and
`RouteCoverageReportSchema` / `RouteCoverageReport` → *(removed — no
replacement)*. No adapter, dispatcher or registrar ever constructed the
report; it was a shape with no producer.

One-line fix: delete the key — nothing served changes, because nothing ever
read it. An endpoint that has no handler yet is simply not registered. Route
readiness that IS measured is untouched: the discovery payload's per-service
`status` / `handlerReady` (`api/discovery.zod.ts`) and the CI-asserted route
ledger (`packages/runtime/src/route-ledger.ts`). A declared-but-unbuilt route
answering 501 instead of 404 is a new capability the ruling explicitly
excluded (zero pull); if it is ever wanted it re-declares fresh under its own
ruling, executor first.

The retirement kit:

- key tombstone at the declaration (`api/RestApiEndpoint:handlerStatus` in
`RETIRED_KEYS_BY_MAJOR[18]`; the surface baseline line carries `[RETIRED]`)
- whole-def deletions `api/HandlerStatus`, `api/RouteCoverageEntry`,
`api/RouteCoverageReport` in `RETIRED_DEFS_BY_MAJOR[18]` (manifest keys
deliberately removed; the #4725 gate adjudicated them)
- deliberately NO D2 conversion: nothing in the tree parses
`RestApiEndpointSchema` outside its own unit tests — a REST API plugin route
registration is not a stack collection member and never a `sys_metadata`
row — so the conversion chain has no seam that would ever see one (the
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
prescription, and for the same reason the tombstone carries no
`os migrate meta` sentence
- pin tests (`api/plugin-rest-api.handler-status-retirement.test.ts`): all
three former values refused at path `handlerStatus` with the prescription,
through the route-registration embed too; a well-formed endpoint without the
key still parses and grows no `handlerStatus` property; the shipped default
route registrations still parse; zero holders for all 6 retired export names
on every public entry; the carrier schemas survive; the registrations under
major 18 are present
- no liveness-ledger row moves: `RestApiEndpointSchema` is outside the walked
population (not a registered metadata type and not in `SPEC_ONLY_SCHEMAS`),
so the verdict is recorded here, in the schema and in the D3 entry
- teaching sweep: `content/docs/**`, `examples/**`, `packages/*/README.md` and
`packages/create-objectstack/**` carry no hand-written mention; the generated
reference page regenerates. The published skill `skills/objectstack-api`
taught the key at this PR's base (a9b2be0b0); `main` already dropped that
teaching in the #14448 optimization flight (bae4088f3 — zero `handlerStatus`
/ Handler Status / RouteCoverage hits under `skills/` at 99b8faf8f, with
`RestApiEndpoint` still hitting the skill as the live control), so no
skills-lane follow-up is needed
69 changes: 4 additions & 65 deletions content/docs/references/api/plugin-rest-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,8 +60,8 @@ no plugin code at all.
## TypeScript Usage

```typescript
import { ErrorHandlingConfigSchema, HandlerStatusSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, RouteCoverageEntrySchema, RouteCoverageReportSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, HandlerStatus, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, RouteCoverageEntry, RouteCoverageReport, ValidationMode } from '@objectstack/spec/api';
import { ErrorHandlingConfigSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, ValidationMode } from '@objectstack/spec/api';

// Validate data
const result = ErrorHandlingConfigSchema.parse(data);
Expand All@@ -87,17 +87,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **redactFields** | `string[]` | optional | Field names to redact from error details |


---

## HandlerStatus

### Allowed Values

* `implemented`
* `stub`
* `planned`


---

## OpenApiGenerationConfig
Expand DownExpand Up@@ -197,7 +186,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |


---
Expand DownExpand Up@@ -372,7 +361,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |

### Nested Shape: `RestApiRouteRegistration.middleware[number]`

Expand All@@ -394,56 +383,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **tags** | `string[]` | optional | OpenAPI tags |


---

## RouteCoverageEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| 'automation' \| 'ui' \| 'realtime' \| 'notification' \| 'ai' \| 'i18n'>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## RouteCoverageReport

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **timestamp** | `string` | ✅ | ISO 8601 timestamp |
| **adapter** | `string` | ✅ | Adapter name (e.g. "hono", "express", "nextjs") |
| **summary** | `{ total: integer; implemented: integer; stub: integer; planned: integer }` | ✅ | |
| **entries** | `{ path: string; method: Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>; category: Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>; handlerStatus: Enum<'implemented' \| 'stub' \| 'planned'>; … }[]` | ✅ | Per-endpoint coverage entries |

### Nested Shape: `RouteCoverageReport.summary`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **total** | `integer` | ✅ | Total declared endpoints |
| **implemented** | `integer` | ✅ | Endpoints with real handlers |
| **stub** | `integer` | ✅ | Endpoints with stub handlers (501) |
| **planned** | `integer` | ✅ | Endpoints not yet implemented |

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

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## ValidationMode
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1593 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1590 schemas across 14 protocol modules
---

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

---

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

## API Protocol

**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 440 schemas**
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 437 schemas**

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

Expand All@@ -87,7 +87,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
| [`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`, `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` |
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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
91 changes: 91 additions & 0 deletions .changeset/rest-api-endpoint-handler-status-retired.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
---
"@objectstack/spec": minor
---

feat(spec): retire `RestApiEndpoint.handlerStatus` and the Route Coverage Report shapes — declared, never read; the 501 they described comes from the endpoint executor (#13823, ADR-0049)

<!-- adr-0087: registered rest-api-endpoint-handler-status-retired -->

**BREAKING** accept-set narrowing and 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_KEYS_BY_MAJOR[18]` for the key, `RETIRED_DEFS_BY_MAJOR[18]` for the
three defs, plus the D3 semantic entry
`rest-api-endpoint-handler-status-retired` — where `os migrate meta` users
will look). Maintainer ruling 2026-09-01 on #13823 (director decision batch
#27, verbatim 「同意」): remove; enforce excluded.

`handlerStatus` (`implemented` / `stub` / `planned`) was an authorable key on
`RestApiEndpointSchema` whose docstring promised that a `stub` handler
"returns 501 Not Implemented", and **nothing read it**. Measured at the
retirement base (`origin/main` a9b2be0b0, 2026-09-02, `skills/**` and tests
excluded): the only identifier hits were the declaration, its re-declaration
on `RouteCoverageEntrySchema` and a docblock saying adapters SHOULD warn on
it. The 501 it described has a different cause — every
`DispatcherErrorCode.enum.NOT_IMPLEMENTED` site (`runtime/src/endpoint-executor.ts`
×3, `runtime/src/api-mapping.ts`, `runtime/src/api-endpoint-step.ts`) is the
declarative-endpoint executor refusing a target or mapping it cannot serve,
and none consults the key. So an author who wrote `handlerStatus: 'stub'`
expecting a 501 got an ordinarily served route, and the declaration reported
progress to nobody: `RouteCoverageReportSchema`, the only shape that would
have carried it outward, had zero constructors in objectstack, objectui
(pinned sha) and cloud.

FROM → TO:

- `handlerStatus: 'implemented' | 'stub' | 'planned'` on a `RestApiEndpoint`
→ *(removed key)* — tombstoned with `retiredKey()` (the schema is not
`.strict()`, so a bare deletion would be a silent strip): authoring it is
now a `tsc` error and a parse error carrying the prescription at path
`handlerStatus`, for every former value including the documented default
`'implemented'` (prose only — the key never carried a Zod `.default()`, so
no built artifact materialised it and there is no residue window).
- `HandlerStatusSchema` / `HandlerStatus` → *(removed — no replacement)*. The
enum's only two carriers leave in this same change; an exported value
schema with no consumer reads as a capability (#3950).
- `RouteCoverageEntrySchema` / `RouteCoverageEntry` and
`RouteCoverageReportSchema` / `RouteCoverageReport` → *(removed — no
replacement)*. No adapter, dispatcher or registrar ever constructed the
report; it was a shape with no producer.

One-line fix: delete the key — nothing served changes, because nothing ever
read it. An endpoint that has no handler yet is simply not registered. Route
readiness that IS measured is untouched: the discovery payload's per-service
`status` / `handlerReady` (`api/discovery.zod.ts`) and the CI-asserted route
ledger (`packages/runtime/src/route-ledger.ts`). A declared-but-unbuilt route
answering 501 instead of 404 is a new capability the ruling explicitly
excluded (zero pull); if it is ever wanted it re-declares fresh under its own
ruling, executor first.

The retirement kit:

- key tombstone at the declaration (`api/RestApiEndpoint:handlerStatus` in
`RETIRED_KEYS_BY_MAJOR[18]`; the surface baseline line carries `[RETIRED]`)
- whole-def deletions `api/HandlerStatus`, `api/RouteCoverageEntry`,
`api/RouteCoverageReport` in `RETIRED_DEFS_BY_MAJOR[18]` (manifest keys
deliberately removed; the #4725 gate adjudicated them)
- deliberately NO D2 conversion: nothing in the tree parses
`RestApiEndpointSchema` outside its own unit tests — a REST API plugin route
registration is not a stack collection member and never a `sys_metadata`
row — so the conversion chain has no seam that would ever see one (the
`kernel/Manifest:loading` disposition); the D3 semantic entry carries the
prescription, and for the same reason the tombstone carries no
`os migrate meta` sentence
- pin tests (`api/plugin-rest-api.handler-status-retirement.test.ts`): all
three former values refused at path `handlerStatus` with the prescription,
through the route-registration embed too; a well-formed endpoint without the
key still parses and grows no `handlerStatus` property; the shipped default
route registrations still parse; zero holders for all 6 retired export names
on every public entry; the carrier schemas survive; the registrations under
major 18 are present
- no liveness-ledger row moves: `RestApiEndpointSchema` is outside the walked
population (not a registered metadata type and not in `SPEC_ONLY_SCHEMAS`),
so the verdict is recorded here, in the schema and in the D3 entry
- teaching sweep: `content/docs/**`, `examples/**`, `packages/*/README.md` and
`packages/create-objectstack/**` carry no hand-written mention; the generated
reference page regenerates. The published skill `skills/objectstack-api`
taught the key at this PR's base (a9b2be0b0); `main` already dropped that
teaching in the #14448 optimization flight (bae4088f3 — zero `handlerStatus`
/ Handler Status / RouteCoverage hits under `skills/` at 99b8faf8f, with
`RestApiEndpoint` still hitting the skill as the live control), so no
skills-lane follow-up is needed
69 changes: 4 additions & 65 deletions content/docs/references/api/plugin-rest-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,8 +60,8 @@ no plugin code at all.
## TypeScript Usage

```typescript
import { ErrorHandlingConfigSchema, HandlerStatusSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, RouteCoverageEntrySchema, RouteCoverageReportSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, HandlerStatus, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, RouteCoverageEntry, RouteCoverageReport, ValidationMode } from '@objectstack/spec/api';
import { ErrorHandlingConfigSchema, OpenApiGenerationConfigSchema, RequestValidationConfigSchema, ResponseEnvelopeConfigSchema, RestApiEndpointSchema, RestApiPluginConfigSchema, RestApiRouteCategory, RestApiRouteRegistrationSchema, ValidationMode } from '@objectstack/spec/api';
import type { ErrorHandlingConfig, OpenApiGenerationConfig, RequestValidationConfig, ResponseEnvelopeConfig, RestApiEndpoint, RestApiPluginConfig, RestApiRouteCategory, RestApiRouteRegistration, ValidationMode } from '@objectstack/spec/api';

// Validate data
const result = ErrorHandlingConfigSchema.parse(data);
Expand All@@ -87,17 +87,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **redactFields** | `string[]` | optional | Field names to redact from error details |


---

## HandlerStatus

### Allowed Values

* `implemented`
* `stub`
* `planned`


---

## OpenApiGenerationConfig
Expand DownExpand Up@@ -197,7 +186,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |


---
Expand DownExpand Up@@ -372,7 +361,7 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **rateLimit** | `string` | optional | Rate limit policy name |
| **cacheable** | `boolean` | optional (default: `false`) | Whether response can be cached |
| **cacheTtl** | `integer` | optional | Cache TTL in seconds |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | optional | Handler implementation status: implemented (default if omitted), stub, or planned |
| **handlerStatus** | `never` | optional | [REMOVED] `RestApiEndpoint.handlerStatus` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — nothing ever read it: no registrar, dispatcher or adapter consulted the key, so an endpoint declared `stub` or `planned` was served exactly like an `implemented` one, and the `501 NOT_IMPLEMENTED` its docstring promised is raised by the declarative-endpoint executor for a target it cannot serve, never from this field. Delete the key. An endpoint that has no handler yet is simply not registered; a declared-but-unbuilt route answering 501 is not a platform capability (ruling record, 2026-09-01). |

### Nested Shape: `RestApiRouteRegistration.middleware[number]`

Expand All@@ -394,56 +383,6 @@ const result = ErrorHandlingConfigSchema.parse(data);
| **tags** | `string[]` | optional | OpenAPI tags |


---

## RouteCoverageEntry

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| 'automation' \| 'ui' \| 'realtime' \| 'notification' \| 'ai' \| 'i18n'>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## RouteCoverageReport

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **timestamp** | `string` | ✅ | ISO 8601 timestamp |
| **adapter** | `string` | ✅ | Adapter name (e.g. "hono", "express", "nextjs") |
| **summary** | `{ total: integer; implemented: integer; stub: integer; planned: integer }` | ✅ | |
| **entries** | `{ path: string; method: Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>; category: Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>; handlerStatus: Enum<'implemented' \| 'stub' \| 'planned'>; … }[]` | ✅ | Per-endpoint coverage entries |

### Nested Shape: `RouteCoverageReport.summary`

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **total** | `integer` | ✅ | Total declared endpoints |
| **implemented** | `integer` | ✅ | Endpoints with real handlers |
| **stub** | `integer` | ✅ | Endpoints with stub handlers (501) |
| **planned** | `integer` | ✅ | Endpoints not yet implemented |

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

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **path** | `string` | ✅ | Full URL path (e.g. /api/v1/analytics/query) |
| **method** | `Enum<'GET' \| 'POST' \| 'PUT' \| 'DELETE' \| 'PATCH' \| 'HEAD' \| 'OPTIONS'>` | ✅ | HTTP method (GET, POST, etc.) |
| **category** | `Enum<'discovery' \| 'metadata' \| 'data' \| 'batch' \| 'permission' \| 'analytics' \| …>` | ✅ | Route category |
| **handlerStatus** | `Enum<'implemented' \| 'stub' \| 'planned'>` | ✅ | Handler status |
| **service** | `string` | ✅ | Target service name |
| **healthCheckPassed** | `boolean` | optional | Whether the health check probe succeeded |


---

## ValidationMode
Expand Down
10 changes: 5 additions & 5 deletions content/docs/references/index.mdx
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
---
title: Protocol Reference
description: Every schema published by @objectstack/spec — 1593 schemas across 14 protocol modules
description: Every schema published by @objectstack/spec — 1590 schemas across 14 protocol modules
---

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

---

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

## API Protocol

**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 440 schemas**
**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **31 pages, 437 schemas**

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

Expand All@@ -87,7 +87,7 @@ REST contracts, endpoints, routing, realtime, batch, discovery.
| [`odata.zod.ts`](/docs/references/api/odata) | `ODataConfig`, `ODataError`, `ODataFilterFunction`, `ODataMetadata`, `ODataQuery`, `ODataResponse` |
| [`package-api.zod.ts`](/docs/references/api/package-api) | `GetInstalledPackageRequest`, `GetInstalledPackageResponse`, `ListInstalledPackagesRequest`, `ListInstalledPackagesResponse`, `PackageApiErrorCode`, `PackageInstallRequest`, `PackageInstallResponse`, `PackagePathParams`, `PackageRollbackRequest`, `PackageUpgradeRequest`, `PackageUpgradeResponse`, `ResolveDependenciesRequest`, `ResolveDependenciesResponse`, `UninstallPackageApiRequest`, `UninstallPackageApiResponse`, `UploadArtifactRequest`, `UploadArtifactResponse` |
| [`package-lifecycle.zod.ts`](/docs/references/api/package-lifecycle) | `DiscardPackageDraftsResponse`, `DuplicatePackageResponse`, `ListPackageCommitsResponse`, `PackageExportManifest`, `PackagePublishResult`, `ReassignOrphanedMetadataResponse`, `RevertPackageCommitResponse`, `RollbackToPackageCommitResponse` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `HandlerStatus`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `RouteCoverageEntry`, `RouteCoverageReport`, `ValidationMode` |
| [`plugin-rest-api.zod.ts`](/docs/references/api/plugin-rest-api) | `ErrorHandlingConfig`, `OpenApiGenerationConfig`, `RequestValidationConfig`, `ResponseEnvelopeConfig`, `RestApiEndpoint`, `RestApiPluginConfig`, `RestApiRouteCategory`, `RestApiRouteRegistration`, `ValidationMode` |
| [`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`, `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` |
| [`query-adapter.zod.ts`](/docs/references/api/query-adapter) | `ODataQueryAdapter`, `OperatorMapping`, `QueryAdapterConfig`, `QueryAdapterTarget`, `RestQueryAdapter` |
| [`realtime.zod.ts`](/docs/references/api/realtime) | `RealtimeConfig`, `RealtimeEvent`, `RealtimeEventType`, `RealtimePresence`, `Subscription`, `SubscriptionEvent`, `TransportProtocol` |
Expand Down
Loading
Loading