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
58 changes: 58 additions & 0 deletions .changeset/workflow-slot-retired.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
"@objectstack/spec": major
"@objectstack/client": major
"@objectstack/metadata-protocol": minor
"@objectstack/runtime": minor
---

refactor(spec,client,metadata-protocol,runtime)!: retire the workflow service slot — declared end to end, implemented nowhere (#4451)

The `workflow` slot was ADR-0078's silently-inert declaration at every layer at
once: a `CoreServiceName` nothing ever registered or resolved (ADR-0115
Evidence 5 — "no code in this repository resolves either slot", verified across
both repositories), an `IWorkflowService` contract with zero implementations, a
`WorkflowProtocol` whose three methods no code ever provided, a discovery
`routes.workflow` field no builder could truthfully populate, and a
`/api/v1/workflow` advertisement for a path no host ever mounted (the
pre-#3586 `DEFAULT_DISPATCHER_ROUTES` already listed it among routes that
never existed). The capability it promised is live elsewhere and has been for
majors: record state machines are enforced by the `state_machine` validation
rule, approvals are first-class flow nodes on the approvals runtime
(ADR-0019), and record-triggered automation is lifecycle hooks +
`record_change` flows (`service-automation`).

FROM → TO:

- `CoreServiceName 'workflow'` / `ServiceRequirementDef.workflow` /
`CORE_SERVICE_PROVIDER['workflow']` → removed; there is no slot to fill.
- `IWorkflowService` (`@objectstack/spec/contracts`) → removed; no
implementation ever existed. Register nothing — use the mechanisms above.
- `WorkflowProtocol` + `GetWorkflowConfigRequest/Response`,
`WorkflowState`, `GetWorkflowStateRequest/Response`,
`WorkflowTransitionRequest/Response` (`@objectstack/spec/api`) → removed,
along with the seven published JSON schemas. Delete the import; nothing
ever answered these shapes.
- Discovery `routes.workflow` / `services.workflow` / `features.workflow`
(metadata-protocol + runtime builders) → absent. A reader keying on them
only ever saw `unavailable` / `false`; delete the read.
- `RouterConfig.mounts.workflow` → removed; there was never a surface to
mount at it.
- `RestApiRouteCategory 'workflow'` → removed; categorize automation-adjacent
routes as `'automation'`.
- `@objectstack/client` re-exports of the four workflow types → removed with
their source. (The `client.workflow.*` methods were already removed earlier
in the v17 cycle — this retires the types they returned.)
- Also removed: the stray `graphql` entry in `CORE_SERVICE_PROVIDER` and the
`graphql: { route: '/graphql' }` discovery entry — `graphql` was never a
`CoreServiceName`, and the dispatcher had already dropped `/graphql` as out
of the product plan (#2462 follow-on).

The retirement kit: the `workflow-service-slot-retired` semantic migration
(major 17) carries this prescription into `spec-changes.json`, the generated
upgrade guide and the `spec_changes` MCP tool. These are TS/API surfaces and a
discovery response field — never stored in stack metadata — so there is no
load-path conversion and nothing for `os migrate meta` to rewrite; the
21 `authorable-surface.json` baseline lines and 7 `json-schema.manifest.json`
entries for the deleted schemas are dropped deliberately in the same change
(the plugin-runtime precedent: a prescription nobody can receive is noise —
nothing parses these shapes any more).
20 changes: 11 additions & 9 deletions content/docs/api/plugin-endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,21 @@ Authenticate with email and password (better-auth's email sign-in route, mounted

The following endpoints become available when the corresponding plugin is installed and registered with the kernel. Use the discovery `services` map to check availability.

### Workflow (`/workflow`) — Plugin Required
### Workflow (`/workflow`) — removed in v17

<Callout type="warn">
Not yet mounted. These routes are declared in the API protocol but the core dispatcher registers no `/workflow` handler and no bundled plugin provides a `workflow` service (only an in-memory dev stub), so they return **404** today. `discovery.services.workflow` reports `unavailable` in a standard install.
There is no workflow endpoint, and there is no `workflow` service slot. The
three routes documented here were declared in the API protocol and served by
nothing — no dispatcher handler, no plugin — so they 404'd for the whole life
of the declaration. The slot, the `WorkflowProtocol` methods behind it and the
discovery fields that reported it were all retired in v17 ([#4451](https://github.com/objectstack-ai/objectstack/issues/4451)).
Use the live mechanisms instead: an object validation rule of type
`state_machine` for lifecycle transitions, an `approval` flow node for human
approval pauses (ADR-0019), and lifecycle hooks / `record_change` flows for
record-triggered automation.
</Callout>

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| GET | `/workflow/:object/config` | Get workflow configuration |
| GET | `/workflow/:object/:recordId/state` | Get record's workflow state |
| POST | `/workflow/:object/:recordId/transition` | Execute state transition |

Approve/reject are **not** workflow routes (ADR-0019): approval is a flow node, and decisions are recorded on the approvals runtime via `POST /approvals/requests/:id/approve` and `POST /approvals/requests/:id/reject`.
Approve/reject were never workflow routes (ADR-0019): approval is a flow node, and decisions are recorded on the approvals runtime via `POST /approvals/requests/:id/approve` and `POST /approvals/requests/:id/reject`.

### Automation (`/automation`) — Plugin Required

Expand Down
46 changes: 30 additions & 16 deletions content/docs/kernel/services-checklist.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ The ObjectStack protocol defines **16 kernel services** registered via the `Core
- ✅ Implemented — the 17 kernel-provided protocol methods (`DataProtocol` 9 + `MetadataProtocol` 8)
- ⚠️ Framework — the slot is filled by the kernel's in-memory fallback: real reads and
writes, no persistence (self-declares `degraded`, ADR-0076 D12)
- ❌ Plugin Required — the remaining 36 methods declared across the other per-domain
- ❌ Plugin Required — the remaining 33 methods declared across the other per-domain
contracts (`analytics` 2, `automation` 1, `packages` 6, `views` 5, `permissions` 3,
`workflow` 3, `realtime` 6, `notification` 7, `i18n` 3). *Declared* is not *routed*:
`realtime` 6, `notification` 7, `i18n` 3). *Declared* is not *routed*:
`packages` is answered kernel-side by the `/packages` dispatcher domain over the
ObjectQL registry, `i18n` has a kernel in-memory fallback, and `views` / `permissions` /
`workflow` have no implementation anywhere — the rest wait on whatever fills the slot
ObjectQL registry, `i18n` has a kernel in-memory fallback, and `views` /
`permissions` have no implementation anywhere — the rest wait on whatever fills
the slot. (`workflow`'s 3 methods were the fourth such group; the whole slot
retired in v17, [#4451](https://github.com/objectstack-ai/objectstack/issues/4451).)
</Callout>

---
Expand Down Expand Up @@ -90,10 +92,12 @@ installable optional package, so they need no remedy (`NO_REMEDY_SLOTS` in the
guard script). And `null` there does **not** always mean "nothing ships" — it
means "no name belongs in an `Install X` sentence", which covers two cases:

- **Nothing provides the slot at all** — `search`, `workflow` (and the retired
`graphql`). Discovery says exactly that rather than naming a plausible
package: `No implementation ships for the '<slot>' slot — register a service
under it to enable`.
- **Nothing provides the slot at all** — `search`. Discovery says exactly that
rather than naming a plausible package: `No implementation ships for the
'<slot>' slot — register a service under it to enable`. (`workflow` and the
never-real `graphql` sat here too until both were retired outright in v17,
[#4451](https://github.com/objectstack-ai/objectstack/issues/4451) — a slot
nothing fills and nothing consumes is better removed than explained.)
- **A provider exists but cannot be installed** — `ai`. `@objectstack/service-ai`
registers the slot in `objectstack-ai/cloud` and is `private: true`.

Expand Down Expand Up @@ -333,13 +337,18 @@ the domain answers **501** with that remedy spelled out, not a generic "install
plugin".
</Callout>

### 6. workflow Service — 3 methods ❌ Nothing ships
`getWorkflowConfig`, `getWorkflowState`, `workflowTransition`
State machine transitions. No package registers the `workflow` slot
(`CORE_SERVICE_PROVIDER.workflow` is `null`). Approve/reject are not workflow
methods — per ADR-0019 they moved to the request-id-based approvals API under
`/api/v1/approvals` (`POST /requests/:id/{approve,reject,recall}`, served by
`@objectstack/plugin-approvals`).
### 6. workflow Service — retired in v17
The slot, its `IWorkflowService` contract and the three `WorkflowProtocol`
methods (`getWorkflowConfig`, `getWorkflowState`, `workflowTransition`) were
removed in [#4451](https://github.com/objectstack-ai/objectstack/issues/4451):
nothing ever registered or resolved the slot (ADR-0115 Evidence 5), no method
ever had an implementation, and no host ever mounted `/api/v1/workflow`. The
three capabilities it named are live elsewhere — state-machine transitions are
an object validation rule of type `state_machine`, approvals are `approval`
flow nodes on the approvals runtime (ADR-0019 — decisions via
`POST /api/v1/approvals/requests/:id/{approve,reject,recall}`, served by
`@objectstack/plugin-approvals`), and record-triggered automation is lifecycle
hooks + `record_change` flows.

### 7. automation Service — 1 method ✅ `@objectstack/service-automation`
`triggerAutomation`
Expand Down Expand Up @@ -502,11 +511,16 @@ a package that cannot be installed is a dead end, which is why
| Slot | State |
|:-------|:------------|
| **ui** | Nothing registers the slot. `ViewProtocol`'s five methods are declared and unrouted; view CRUD runs through `/api/v1/meta`, and `/api/v1/ui/view/:object` is served by the `protocol` service. |
| **workflow** | Nothing ships. `WorkflowProtocol`'s three methods have no implementation and no consumer. |
| **search** | Nothing ships. Contract and engine enum exist in `@objectstack/spec` only. |
| **ai** | Nothing in this repo — `service-ai` (chat, completion, models, conversations) is Cloud/EE. |
| **realtime transport** | The service exists but no WebSocket/SSE route is mounted, so `routes.realtime` is deliberately never advertised. |

The `workflow` slot used to sit in this table ("nothing ships, no consumer").
It was retired outright in v17 (#4451, per ADR-0115 Evidence 5): the
capability lives in `state_machine` validation rules, approval flow nodes
(ADR-0019) and `record_change` flows, so there is nothing left for a slot to
promise.

---

## Plugin Implementation Pattern
Expand Down
5 changes: 2 additions & 3 deletions content/docs/protocol/kernel/http-protocol.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,13 @@ GET /api/v1/discovery HTTP/1.1
"packages": "/api/v1/packages",
"auth": "/api/v1/auth",
"ui": "/api/v1/ui",
"storage": "/api/v1/storage",
"graphql": "/api/v1/graphql"
"storage": "/api/v1/storage"
},
"services": {
"data": { "enabled": true, "status": "available", "route": "/api/v1/data", "provider": "objectql" },
"metadata": { "enabled": true, "status": "available", "route": "/api/v1/meta", "provider": "objectql" },
"auth": { "enabled": true, "status": "available", "route": "/api/v1/auth", "provider": "@objectstack/plugin-auth" },
"workflow": { "enabled": false, "status": "unavailable", "message": "No implementation ships for the 'workflow' slot — register a service under it to enable" },
"search": { "enabled": false, "status": "unavailable", "message": "No implementation ships for the 'search' slot — register a service under it to enable" },
"ai": { "enabled": false, "status": "unavailable", "message": "Provided by @objectstack/service-ai in ObjectStack Cloud/Enterprise — no implementation ships in the open framework" }
},
"locale": {
Expand Down
1 change: 0 additions & 1 deletion content/docs/references/api/discovery.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const result = ApiRoutes.parse(data);
| **storage** | `string` | optional | e.g. /api/v1/storage |
| **analytics** | `string` | optional | e.g. /api/v1/analytics |
| **packages** | `string` | optional | e.g. /api/v1/packages |
| **workflow** | `string` | optional | e.g. /api/v1/workflow |
| **approvals** | `string` | optional | e.g. /api/v1/approvals |
| **realtime** | `string` | optional | e.g. /api/v1/realtime |
| **notifications** | `string` | optional | e.g. /api/v1/notifications |
Expand Down
4 changes: 2 additions & 2 deletions content/docs/references/api/dispatcher.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const result = DispatcherConfig.parse(data);

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **routes** | `{ prefix: string; service: Enum<'metadata' \| 'data' \| 'auth' \| 'file-storage' \| 'search' \| 'cache' \| 'queue' \| 'automation' \| 'analytics' \| 'realtime' \| 'job' \| 'notification' \| 'ai' \| 'i18n' \| 'ui' \| 'workflow'>; authRequired: boolean; criticality: Enum<'required' \| 'core' \| 'optional'>; … }[]` | ✅ | Route-to-service mappings |
| **routes** | `{ prefix: string; service: Enum<'metadata' \| 'data' \| 'auth' \| 'file-storage' \| 'search' \| 'cache' \| 'queue' \| 'automation' \| 'analytics' \| 'realtime' \| 'job' \| 'notification' \| 'ai' \| 'i18n' \| 'ui'>; authRequired: boolean; criticality: Enum<'required' \| 'core' \| 'optional'>; … }[]` | ✅ | Route-to-service mappings |
| **fallback** | `Enum<'404' \| 'proxy' \| 'custom'>` | ✅ | Behavior when no route matches |
| **proxyTarget** | `string` | optional | Proxy target URL when fallback is "proxy" |

Expand Down Expand Up @@ -91,7 +91,7 @@ Route-resolution failure mode emitted in `error.code`
| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **prefix** | `string` | ✅ | URL path prefix for routing (e.g. /api/v1/data) |
| **service** | `Enum<'metadata' \| 'data' \| 'auth' \| 'file-storage' \| 'search' \| 'cache' \| 'queue' \| 'automation' \| 'analytics' \| 'realtime' \| 'job' \| 'notification' \| 'ai' \| 'i18n' \| 'ui' \| 'workflow'>` | ✅ | Target core service name |
| **service** | `Enum<'metadata' \| 'data' \| 'auth' \| 'file-storage' \| 'search' \| 'cache' \| 'queue' \| 'automation' \| 'analytics' \| 'realtime' \| 'job' \| 'notification' \| 'ai' \| 'i18n' \| 'ui'>` | ✅ | Target core service name |
| **authRequired** | `boolean` | ✅ | Whether authentication is required |
| **criticality** | `Enum<'required' \| 'core' \| 'optional'>` | ✅ | Service criticality level for unavailability handling |
| **permissions** | `string[]` | optional | Required permissions for this route namespace |
Expand Down
Loading
Loading