Skip to content

Commit 6e33b67

Browse files
feat(spec)!: retire agent.lifecycle — a conversation phase is a skill, orchestration is Flow, record transitions are the state_machine rule; the XState StateMachineSchema family leaves with it (#21320) (#21461)
Fixes #21320 Part of #20274 Clause-②: yes (narrowing) ## What this does The maintainer ruled **D (retire)** on objectstack-ai/cloud#2569 (batch #267 item 5, 「其他同意」). This PR carries out that ruling's spec half through the `spec-property-retirement` playbook. **`agent.lifecycle` is retired.** It was parsed and never read. No runtime, in this repository or in cloud, moved an agent through a declared state or refused an undeclared transition. What it reached for is already served elsewhere: - a conversation phase is a **skill** with its own `instructions` and `tools`, selected by `triggerConditions` (ADR-0064); - a multi-step process is a **Flow** (ADR-0019); - a record's status transitions are the `state_machine` **validation rule** (ADR-0020). **The XState `StateMachineSchema` family leaves the package with it,** under the card's scope item 3. The census below shows `agent.lifecycle` was its last authorable consumer. ADR-0020 implementation note 1 had kept the file only for this door. This run **resumed a lost one.** The container restarted mid-flight. The predecessor's eight WIP commits survived, but its census and gate results did not. Everything below was re-measured in this run; nothing from before the restart counts as measured. The last section says what was found done and what this run finished. ## The retirement kit - **Tombstone.** `AgentSchema.lifecycle` is now a `retiredKey()` (`packages/spec/src/ai/agent.zod.ts`). Its prescription names the three destinations and ends with the house `os migrate meta --from 17` sentence. `tsc` refuses the key, because its input type is `never`. - **Form.** The agent form drops its `lifecycle` composite row (`agent.form.ts`). The four `platform-objects` `*.metadata-forms.generated.ts` catalogs lose the row's label and help text in every locale. - **D2 conversion `agent-lifecycle-removed`.** It runs at step 18 with `retiredFromLoadPath`, `retiredAfter` 17.6.0 and order 57, and it sits in identifier order in `MAJOR_18_CONVERSIONS`. It deletes `lifecycle` from every `agents[]` entry, whatever the key holds, with one notice per agent. The delete is lossless. An object's ADR-0057 `lifecycle` block shares the name and is not touched; a pin asserts this. - **Registration.** `RETIRED_KEYS_BY_MAJOR[18]` gains `ai/Agent:lifecycle`. `RETIRED_DEFS_BY_MAJOR[18]` gains the five published defs: `automation/StateMachine`, `StateNode`, `Transition`, `ActionRef` and `GuardRef`. Each is one entry file, written into the generated regions by `gen:migration-registry`. - **D3 entry `agent-lifecycle-retired`.** One entry covers the family. It carries the judgement no conversion can make: which of the three destinations each deleted machine meant. Its `STEP18_RATIONALE` fragment is order 62. - **Family deletion.** `automation/state-machine.zod.ts` and its test are deleted. `./automation` stops re-exporting the module. `StateNodeConfig` leaves the root and `/ai` entries, whose only structural mention of it was the tombstoned key. - **Ledger.** The `liveness/agent.json` row `lifecycle` moves from `experimental` to `dead`, with `verifiedAt` 2026-10-02 and the REMOVED note. The tombstone keeps the key in the walked shape (the `rls.priority` precedent). `state-counts/agent.md` is regenerated. The README's agent row no longer says "autonomy tier experimental": no `agent` row is `experimental` any more (A6). - **Generated baselines.** These are regenerated, not hand-edited: `authorable-surface/{ai,automation}.json` (one new `ai/Agent:lifecycle [RETIRED]` row and 18 family rows gone), `authorable-defaults`, `json-schema.manifest` (five defs gone), `api-surface`, `export-origins`, `declaration-map`, `content/docs/references/**` (the `state-machine` page is gone) and the strictness-ledger counts. - **Pins.** `packages/spec/src/ai/agent-lifecycle-retirement.test.ts` is a `repo`-project test with 14 cases: - the refusal for every value, with the issue `code`, the `path` and the prescription; - the `defineStack` door's ADR-0112 envelope (`STACK_SCHEMA_INVALID` / 422); - the stored-row replay and the boot-door before/after; - idempotence and load-path retirement; - the registration; - the family's runtime absence from `./automation`; - a tree-scoped absence walk over the five roots declared for `@objectstack/spec#test`, with an anti-vacuity matcher case. - **Changeset.** `.changeset/21320-agent-lifecycle-retired.md` bumps `@objectstack/spec` `minor` and `@objectstack/platform-objects` `patch`. It carries a **BREAKING** banner, the FROM → TO table, the one-line fix, `Clause-②: yes (narrowing)` and the ADR-0087 `registered` marker naming both ids. ## The census (measured in this run) **`StateMachineSchema` family consumers at `origin/main` `c2c21f357c`, before this branch's change:** | site | symbol | what it was | disposition | |---|---|---|---| | `src/ai/agent.zod.ts:7`, `:341` | `StateMachineSchema` | **the one authorable consumer**: `AgentSchema.lifecycle` | tombstoned | | `src/data/validation.zod.ts:187` | `StateMachineValidationSchema` | the ADR-0020 `state_machine` rule. It is a **different export** (a flat `{ from: [to] }` table) and never imported the family | unchanged; it is the prescription's destination | | `src/api/protocol.zod.ts:2614` | comment | claimed "`StateMachineSchema` stays authorable on the object". That has been false since ADR-0020 retired `object.stateMachines` | corrected | | `src/automation/state-machine.zod.ts:25` | docblock | named the agent lifecycle as the surviving door | file deleted | | `src/ui/chart.zod.ts:50` | comment | named `StateMachineSchema` as a positive control | updated | | `migrations/entries/semantic/17.ui-interaction-config-family-retired.ts:38`, `17.authoring-schemas-strict-unknown-keys.ts:20` | text | historical witnesses inside released D3 entries | left, because they are dated records | | `src/ai/index.ts:47`, `src/index.ts:147` | `StateNodeConfig` re-export | entry-nameability only; it was mentioned solely through `lifecycle` | removed | | `recursive-schema-input-assertions.ts`, `union-author-message-pins.test.ts`, `type-alias-convention.pin.test.ts` (778 → 773 pins), `sync-retirement.test.ts`, `scripts/export-origins.test.ts`, `scripts/liveness/check-liveness.test.ts` | tests and type probes | their probes and witnesses were the family or `agent.lifecycle` | removed or re-pointed (`FlowSchema`, `tool.outputSchema`) | | `content/docs/automation/workflows.mdx` | `StateMachineConfig` | **taught** the XState type for record lifecycles | rewritten to the `state_machine` rule, `os:check` green | Nothing outside `packages/spec` imports the family: `git grep` over `packages/**`, `examples/**`, `skills/**` and `content/**` finds no import. **`agent.lifecycle` producers and readers (A3):** - **Readers: 0** outside `packages/spec`. The pattern `.lifecycle` / `['lifecycle']` / `"lifecycle"` has 62 hits in `packages/**` and `examples/**`. Every one is an object's ADR-0057 data lifecycle, a service-registration lifecycle, a schema-migration composition or an i18n key path. One hit is agent-related: the form-label pin in `object-lifecycle-panel-echo-decisions.test.ts`, which is re-pointed (A2). - **Producers: 0.** `examples/**` contains no agent definition at all, so its control reading is also 0, and that zero is not a census of agent authors. Seventeen files outside `packages/spec` name `defineAgent` / `AgentSchema`, and none of them authors `lifecycle`. - **objectui at the pin `89cad75d55`:** 0 imports of any family export. The control `FilterCondition` is found in 44 files. `AgentPreview.tsx` draws no `lifecycle`. - **Cloud:** NOT MEASURED here, because this session has no cloud checkout. The card's cloud zero-reader census (cloud `@3aadd908`) is attributed, not re-taken. ## Deviations and conflicts (the reviewer should read these) 1. **This PR touches `skills/**`, so it is Tier H.** The claim's file surface says "No `skills/**` edit"; this breaches it. - `skills/objectstack-ai/references/_index.md` loses one generated line: the transitive dependency `automation/state-machine.zod.ts`. Once `agent.zod.ts` stops importing that file, `check:skill-refs` fails without that change. - Any retirement of `agent.lifecycle` causes this, even one that keeps the family file, because the index is computed from `agent.zod.ts`'s imports. - The generated-surface exception (#11705) does not lift the path. This PR also edits `packages/spec/scripts/**`, which the exception's co-edit fence treats as the generator tree. The edits there are a liveness witness, an export-origins witness, the `undrilled-containers` baseline, and two corrected comments in `build-skill-references.ts` and `lib/skill-map-guards.ts`. - `check-governed-merges.mjs --branch` answers exit 3: **GOVERNED, landing tier H**. - Landing therefore needs the maintainer's hand, or an authorized APPROVED review. 2. **These files are outside the claim's declared file surface.** Each is a consequence of retiring the family: - hand-written docs: `workflows.mdx`, `quick-reference.mdx`, the strictness-ledger prose row and its counts; - `PROTOCOL_MAP.md`, `llms.txt` and `docs-import-surface.baseline.json`; - the spec test, witness and baseline files in the census table; - `vitest.repo-tests.json`; - comment corrections in `api/protocol.zod.ts`, `ui/chart.zod.ts`, `ai/index.ts`, `index.ts` and `automation/index.ts`. ## Verification All of this ran on head `b4e1682e1c`, after merging `origin/main` `53fd35e3e3` through `scripts/pm/os-regen-merge.sh`. That merge includes #21431's generated registry entry, `analytics-row-wildcard-outside-count-refused`. No hunk was hand-resolved, and `check:migration-registry` is green with nothing to regenerate. **Gates.** Every derived gate ran on head `b4e1682e1c`, with each exit code captured before any pipe: - **The derived union.** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 124 families, and all 124 exited 0. The reconciliation `dispatch-gates --ran` reads: `✓ dispatch-gates --ran: 124 derived famil(ies) accounted for — 124 run, 0 NOT-MEASURED (a DERIVED zero — all 124 recorded an exit code and none of them is 3).` - **`check:generated`:** `✓ All 15 generated artifacts are up to date`. `check:migration-registry` reads `✓ src/migrations/registry.ts is current (349 semantic, 245 retired-key, 217 retired-def)`. - **`check:liveness`:** `✓ … state-counts/ is current`. Across the shards: 989 live, 1 experimental, 1 live-elsewhere, 110 dead, 10 planned. - **`check:api-surface`:** `@objectstack/spec public API surface + factory signatures unchanged ✓`, read against the committed snapshot, which carries the removal. - **`check-adr-0087-registration --base origin/main`:** `✓ … 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.` The arm was read as `[BREAKING+bang+clause-②-narrowing] registered agent-lifecycle-removed, agent-lifecycle-retired`. - **`check-changeset-no-major`:** `✓ This diff introduces no major bump.` - **`check-empty-changeset`:** `✓ No empty-frontmatter changeset introduced by this diff`. - **`check:i18n`:** `check-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys).` - **`check:doc-authoring`:** `✓ doc authoring guard: 17266 customer-facing string(s) … clean`. - **`check:nul-bytes`:** `check-nul-bytes: OK (… no raw ASCII control bytes).` **Tests:** | command | head | result | |---|---|---| | `pnpm --filter @objectstack/spec test` | `b4e1682e1c` | 600 files, 17689 passed, 1 todo | | `pnpm --filter @objectstack/spec test:repo` | `d472aaffaf` | 50 files, 877 passed | | `pnpm --filter @objectstack/spec typecheck` | `d472aaffaf` | exit 0; `check:test-typecheck: OK` | | `@objectstack/platform-objects` `vitest run` and `typecheck` | `b4e1682e1c` | 59 files, 949 passed; exit 0 | | `@objectstack/lint` `src/lint-liveness-properties.test.ts` | `b4e1682e1c` | 95 passed | | `@objectstack/dogfood` `test/expression-conformance.test.ts` | `b4e1682e1c` | 7 passed | The `d472aaffaf` rows are the merge commit. Its `packages/spec` tree is byte-identical to `b4e1682e1c`; the only later commit edits one `platform-objects` test. **One red was found and fixed in this run.** The `platform-objects` echo-decisions positive control failed with `expected 659 to be 660` until the count moved with the retired row. **Ablation of the tombstone** ran on the committed head through `scripts/ablation-replace.mjs`, which restores automatically: - **The mutation.** The anchor ` lifecycle: retiredKey(` became ` lifecycle_ablated: retiredKey(`, a bare delete on the strict schema. The anchor count went 1 → 0, and the blob went `80b6593b3953` → `42c1ebe78de2`. - **The prediction:** turns red. - **The observation:** `agent-lifecycle-retirement.test.ts` went to **5 failed / 9 passed (14)**. The prescription, walked-shape, tsc-channel, `defineStack`-envelope and boot-door cases went red. The conversion and absence cases stayed green, as they should, because they do not depend on the tombstone. - **The restore.** The blob after the restore equals HEAD (`80b6593b3953`), and `git diff HEAD` is empty. - **The tsc channel.** The pin's `@ts-expect-error` is live: `tsc -p tsconfig.test.json --listFilesOnly` lists the pin (count 1, and the control `agent-memory-store-retirement.test.ts` also counts 1), and the pin carries no debt entry in `test-typecheck-debt.json`. ## `skills/**` readings - **The changed file.** `skills/objectstack-ai/references/_index.md` goes from 43 to 42 lines. It is generated, and the change is one deleted line. - **The whole package.** The sum of every `SKILL.md` is 4397 lines before and 4397 after. No `SKILL.md` is touched. ## Acceptance notes - **`skills/**` teaching.** No text in `skills/**` teaches `agent.lifecycle` or the XState family (A7). `skills/objectstack-automation/references/state-machines-and-approvals.md` teaches the `state_machine` validation rule, which is the prescription's destination. - **Historical comments left as written.** These comments still describe `StateNodeConfig` as one of `defineStack`'s structural mentions: the nine `scripts/i18n-extract.config.ts` headers, `scripts/check-entry-nameability.ts:97` and `scripts/root-entry-type-nameability.pin.test.ts:16`. They are dated records of #10868 / #11350. - **The authorable-surface anchor.** `authorable-surface.base.json` still lists the family's rows. Only `gen:authorable-surface-base` writes that anchor, and the anchor is allowed to lag. `check:authorable-surface` is green. - **Historical audit files.** `packages/spec/ZOD_SCHEMA_AUDIT_REPORT.md` and `DEVELOPMENT_PLAN.md` still name the deleted file. - **Out-of-repo consumers are NOT MEASURED.** That covers tenant-authored agents and code outside this repository, cloud included, that imports the family's exports. The changeset says so, and the prescription and the D2 replay cover stored agent rows. - **`os lint`.** It reads the unparsed stack, so it now grades an authored `agent.lifecycle` `liveness-dead-property`. This was measured with `lintLivenessProperties` on this tree; the control `tool.outputSchema` still reads `liveness-experimental-property`. The parsing doors refuse the key first. The changeset was corrected to say exactly this. ## Resume record: found done vs finished in this run - **Found done.** The predecessor's WIP commits held the whole kit: `b3b1fe8852`, `4729a31154`, `f1fd8ebb5f`, `fbd4e311ea`, `d41227ffd4`, `9e40a894aa`, `f3f5301451` and `54018d8e6a`, plus a merge. - **Finished in this run:** - re-measured the census, and with it the go-ahead to retire the whole family; - merged `origin/main` twice through `os-regen-merge.sh` (`42bce96be8`, `d472aaffaf`); - corrected the changeset's `os lint` sentence (`d3240440b9`); - fixed a red the predecessor missed: the metadata-form catalog's per-locale positive control in `object-lifecycle-panel-echo-decisions.test.ts` read 660 and is now 659 (`b4e1682e1c`); - ran the whole gate set and the ablation. ## 维护者速读(草稿) **改了什么**:把 agent 元数据上的 `lifecycle`(对话状态机)退役,改为编写时报错并给出处方(会话阶段用 skill + `triggerConditions`,多步流程用 Flow,记录状态流转用 `state_machine` 校验规则);随之删掉它唯一还在用的 XState 风格 `StateMachineSchema` 一族导出。表单、四语种表单文案、台账、生成物、文档、迁移登记(D2 转换 + D3 说明)同步。 **为什么改**:裁决 cloud#2569 定 D(退役)。这个键一直是「声明了但没有任何运行时读取」——写了等于没写,对 AI 编写元数据是陷阱;实测本仓与 objectui 零读取零编写。 **风险与代价(含回滚)**:`@objectstack/spec` minor + BREAKING:写了 `lifecycle` 的 agent 会在 parse 时被拒(D2 转换会把存量数据里的该键无损删除);外部若有代码 import 这一族导出会编译失败(仓外未测量)。本 PR 因生成的 `skills/objectstack-ai/references/_index.md` 少一行而成为 Tier H(人合)。回滚即 revert 本 PR。 **席位意见**:(留空) **你要做的**:审阅通过后在本 PR 上 APPROVE(Tier H),席位随后落地。 --- _Generated by [Claude Code](https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent aa46322 commit 6e33b67

63 files changed

Lines changed: 978 additions & 1255 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/platform-objects': patch
4+
---
5+
6+
feat(spec)!: retire `agent.lifecycle`, the agent conversation state machine, and with it the XState `StateMachineSchema` family — a conversation phase is a skill with `triggerConditions`, orchestration is Flow, record transitions are the `state_machine` validation rule (#21320)
7+
8+
**BREAKING** — `agent.lifecycle` was parsed and never read. No runtime, in this
9+
repository or in the cloud AI runtime that executes agents, moved an agent through a
10+
declared state or refused an undeclared transition, so an authored machine changed
11+
nothing an agent did (ADR-0049 enforce-or-remove). Enforcing it would have meant a
12+
statechart interpreter beside Flow, the two-engine shape ADR-0020 rejected. Authoring
13+
now refuses the key by name, with a prescription, and TypeScript rejects it.
14+
15+
Its value schema had no other authorable door: ADR-0020 had already retired the XState
16+
shape as a record-lifecycle declaration and kept the file only for this key. So the
17+
family leaves the package with it.
18+
19+
### FROM → TO
20+
21+
| before | what to write instead |
22+
| --- | --- |
23+
| `agent.lifecycle` — any value | delete the key. |
24+
| a conversation phase in the machine (its own instructions and tools) | a skill with its own `instructions` and `tools`, selected by its `triggerConditions`, listed in the agent's `skills`. |
25+
| a multi-step process in the machine | a Flow. |
26+
| a record's status transitions in the machine | a `state_machine` validation rule in the object's `validations`: `{ type: 'state_machine', field, transitions: { from: [to, …] } }`. |
27+
| `StateMachineSchema`, `StateNodeSchema`, `TransitionSchema`, `ActionRefSchema`, `GuardRefSchema` and the types `StateMachineConfig`, `StateNode`, `StateNodeConfig`, `Transition`, `ActionRef`, `GuardRef` from `@objectstack/spec/automation` | no replacement: declare the shape your code needs itself, or drop it. For record transitions, `StateMachineValidationSchema` in `@objectstack/spec/data` is the enforced shape. |
28+
| `StateNodeConfig` from `@objectstack/spec` or `@objectstack/spec/ai` | removed with the family; nothing in those entries mentions it any more. |
29+
30+
**The one-line fix: delete `lifecycle`; put phase-scoped instructions and tools in
31+
skills with `triggerConditions`, and orchestration in Flow.** `os migrate meta --from 17`
32+
lists the mechanical edits for existing sources (the `lifecycle` deletion). Where each
33+
deleted machine's intent goes is the author's judgement.
34+
35+
The refusal is a parse error at `lifecycle` naming the key and the fix, and the key
36+
fails `tsc` (its input type is `never`).
37+
38+
### The retirement kit
39+
40+
- **Tombstone.** `lifecycle` is a `retiredKey()` on `AgentSchema` carrying the
41+
prescription; the agent metadata form no longer offers it.
42+
- **D2 conversion `agent-lifecycle-removed`** (step 18, retired from the load path):
43+
it deletes `lifecycle` from every agent, whatever it holds. The delete is lossless,
44+
because no value of it ever changed what an agent did. Stored `sys_metadata` agent
45+
rows and built artifacts replay it; one notice per agent. An object's ADR-0057
46+
`lifecycle` block shares the name and is not touched.
47+
- **D3 entry `agent-lifecycle-retired`** carries the judgement the conversion cannot
48+
make: which of the three destinations each deleted machine meant.
49+
- **`RETIRED_KEYS_BY_MAJOR[18]`** registers `ai/Agent:lifecycle`, and
50+
**`RETIRED_DEFS_BY_MAJOR[18]`** registers the five published defs
51+
`automation/StateMachine`, `automation/StateNode`, `automation/Transition`,
52+
`automation/ActionRef` and `automation/GuardRef`. Their reference page
53+
(`references/automation/state-machine`) is gone.
54+
- **No deprecation window**, per the project's startup-stage posture.
55+
56+
### The liveness ledger
57+
58+
The `agent.lifecycle` row moves `experimental` → `dead` with a REMOVED note
59+
(`verifiedAt` 2026-10-02); the tombstone keeps it in the walked shape. No `agent` row is
60+
`experimental` any more. `os validate` and every other parsing door refuse the key at
61+
parse, before any advisory runs. `os lint` reads the unparsed stack, so it now grades the
62+
key `liveness-dead-property` where it used to say `liveness-experimental-property`.
63+
64+
### `@objectstack/platform-objects`
65+
66+
The agent metadata-form catalogs drop the `lifecycle` row's label and help text in all
67+
four locales.
68+
69+
⚠️ **The out-of-repo consumer population is NOT MEASURED.** `@objectstack/spec` is
70+
published: tenant-authored agents, and code outside this repository importing the
71+
family's exports, were not measured. This repository authors no `agent.lifecycle`
72+
outside `packages/spec` and imports none of the family outside it; the pinned objectui
73+
checkout imports none of the family and reads no `agent.lifecycle`.
74+
75+
Clause-②: yes (narrowing)
76+
77+
<!-- adr-0087: registered agent-lifecycle-removed, agent-lifecycle-retired -->

‎content/docs/automation/workflows.mdx‎

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ ObjectStack no longer has a standalone Salesforce-style Workflow Rule authoring
88
type. Use:
99

1010
- **Flow** for event-triggered or scheduled automation.
11-
- **State machine metadata** for strict lifecycle transitions.
11+
- **A `state_machine` validation rule** for strict lifecycle transitions.
1212
- **Approval nodes** inside Flow for human approval pauses.
1313

1414
This page keeps the historical route but documents the current split.
@@ -60,42 +60,51 @@ registration/runtime.
6060

6161
## State machines for lifecycle constraints
6262

63-
Use `StateMachineSchema` when the core requirement is "this object can only move
64-
through these states by these events."
63+
Use a `state_machine` validation rule when the core requirement is "this record
64+
can only move through these states." It is one of the object's `validations`: a
65+
flat table of each state's allowed next states, enforced by the write path (see
66+
[State Machine](/docs/protocol/objectql/state-machine)).
6567

6668
{/* os:check */}
6769
```typescript
68-
import type { StateMachineConfig } from '@objectstack/spec/automation';
69-
70-
export const caseLifecycle: StateMachineConfig = {
71-
id: 'case_lifecycle',
72-
initial: 'new',
73-
states: {
74-
new: {
75-
on: {
76-
ASSIGN: { target: 'assigned' },
77-
},
78-
},
79-
assigned: {
80-
on: {
81-
RESOLVE: { target: 'resolved', cond: 'has_resolution' },
82-
ESCALATE: { target: 'escalated' },
83-
},
84-
},
85-
escalated: {
86-
on: {
87-
RESOLVE: { target: 'resolved', cond: 'has_resolution' },
70+
import { ObjectSchema, Field } from '@objectstack/spec/data';
71+
72+
export const SupportCase = ObjectSchema.create({
73+
name: 'support_case',
74+
label: 'Support Case',
75+
sharingModel: 'private',
76+
fields: {
77+
status: Field.select({
78+
label: 'Status',
79+
required: true,
80+
options: [
81+
{ label: 'New', value: 'new' },
82+
{ label: 'Assigned', value: 'assigned' },
83+
{ label: 'Escalated', value: 'escalated' },
84+
{ label: 'Resolved', value: 'resolved' },
85+
],
86+
}),
87+
},
88+
validations: [
89+
{
90+
type: 'state_machine',
91+
name: 'case_status_flow',
92+
field: 'status',
93+
events: ['update'],
94+
message: 'Invalid case status transition.',
95+
transitions: {
96+
new: ['assigned'],
97+
assigned: ['resolved', 'escalated'],
98+
escalated: ['resolved'],
99+
resolved: [],
88100
},
89101
},
90-
resolved: {
91-
type: 'final',
92-
},
93-
},
94-
};
102+
],
103+
});
95104
```
96105

97-
State machines describe valid transitions and guards. Use Flow nodes for side
98-
effects around those transitions when you need notifications, record updates, or
106+
The rule declares which transitions are legal. Use Flow nodes for side effects
107+
around those transitions when you need notifications, record updates, or
99108
external calls.
100109

101110
---

‎content/docs/getting-started/quick-reference.mdx‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,14 @@ REST endpoints, real-time subscriptions, and discovery.
151151
| **[Metadata](/docs/references/api/metadata)** | `metadata.zod.ts` | Metadata | API metadata endpoints |
152152
| **[Storage](/docs/references/api/storage)** | `storage.zod.ts` | Storage | API storage operations |
153153

154-
## Automation Protocol (4 of 14 schemas)
154+
## Automation Protocol (3 of 13 schemas)
155155

156-
Flows, state machines, approvals, and integrations.
156+
Flows, approvals, and integrations. A record's lifecycle transitions are a `state_machine` validation rule on the object (see [State Machine](/docs/protocol/objectql/state-machine)).
157157

158158
| Protocol | Source File | Key Schemas | Purpose |
159159
|:---------|:-----------|:------------|:--------|
160160
| **[Flow](/docs/references/automation/flow)** | `flow.zod.ts` | Flow, FlowNode | Visual workflow builder |
161161
| **[Approval](/docs/references/automation/approval)** | `approval.zod.ts` | ApprovalNodeConfig | Flow approval-node config |
162-
| **[State Machine](/docs/references/automation/state-machine)** | `state-machine.zod.ts` | StateMachine | State machine definitions |
163162
| **[Webhook](/docs/references/automation/webhook)** | `webhook.zod.ts` | Webhook | Outbound webhooks |
164163

165164
## Security Protocol (3 of 5 schemas)

‎content/docs/references/ai/agent.mdx‎

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const result = AIModelConfigSchema.parse(data);
4949
| **role** | `string` | ✅ | The persona/role (e.g. "Senior Support Engineer") |
5050
| **instructions** | `string` | ✅ | System Prompt / Prime Directives |
5151
| **model** | `{ provider: Enum<'openai' \| 'azure_openai' \| 'anthropic' \| 'local'>; model: string; temperature: number; maxTokens?: number; … }` | optional | |
52-
| **lifecycle** | `{ id: string; description?: string; contextSchema?: Record<string, any>; initial: string; … }` | optional | [EXPERIMENTAL — not enforced] State machine defining the agent conversation flow and constraints. Parsed but no runtime consumer yet. |
52+
| **lifecycle** | `never` | optional | [REMOVED] `agent.lifecycle` was removed in @objectstack/spec 17.7.0 (ADR-0049 enforce-or-remove) — no runtime ever read it: no agent moved through a declared state and no transition was ever refused. Delete the key. A phase of a conversation is a skill with its own `instructions` and `tools`, selected by its `triggerConditions` (ADR-0064); multi-step process orchestration is a Flow (ADR-0019); a record's status transitions are a `state_machine` validation rule on the object (ADR-0020). Run `os migrate meta --from 17` to list the mechanical edits for existing sources; apply them by hand. |
5353
| **surface** | `Enum<'ask' \| 'build'>` | optional (default: `"ask"`) | Product surface this agent binds ('ask' \| 'build') — ADR-0063 §1 |
5454
| **skills** | `string[]` | optional | Skill names to attach (Agent→Skill→Tool architecture) |
5555
| **tools** | `never` | optional | [REMOVED] `agent.tools` was removed in @objectstack/spec 17 — use `skills`. An agent reaches exactly the tools its surface-compatible skills declare (ADR-0064), so move each reference into a skill: a platform tool by its registered name, or `action_<name>` for one of your own AI-exposed Actions. This is NOT a rename — there is no key the value moves to: the migration DELETES the key and emits a notice naming each tool that was listed, and you re-declare each one in a skill by hand. ADR-0064 itself still reads `Proposed` and is cloud-owned — that scopes its RUNTIME half (tool resolution, which lives in cloud `service-ai`), not this rejection: the authoring invariant binds you here, and ADR-0109 (Accepted — implemented) is the in-repo record that carries it. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
@@ -80,17 +80,6 @@ const result = AIModelConfigSchema.parse(data);
8080
| **maxTokens** | `number` | optional | |
8181
| **topP** | `number` | optional | |
8282

83-
### Nested Shape: `Agent.lifecycle`
84-
85-
| Property | Type | Required | Description |
86-
| :--- | :--- | :--- | :--- |
87-
| **id** | `string` | ✅ | Unique Machine ID |
88-
| **description** | `string` | optional | |
89-
| **contextSchema** | `Record<string, any>` | optional | Zod Schema for the machine context/memory |
90-
| **initial** | `string` | ✅ | Initial State ID |
91-
| **states** | `Record<string, { type: Enum<'atomic' \| 'compound' \| 'parallel' \| 'final' \| 'history'>; entry?: (string \| object)[]; exit?: (string \| object)[]; on?: Record<string, string \| object \| object[]>; … }>` | ✅ | State Nodes |
92-
| **on** | `Record<string, string \| { target?: string; cond?: string \| object; actions?: (string \| object)[]; description?: string } \| { target?: string; cond?: string \| object; actions?: (string \| object)[]; description?: string }[]>` | optional | |
93-
9483
### Nested Shape: `Agent.planning`
9584

9685
| Property | Type | Required | Description |

‎content/docs/references/automation/index.mdx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Automation Protocol — schema reference
33
navTitle: Automation Protocol
4-
description: "The ObjectStack Automation Protocol in 14 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
4+
description: "The ObjectStack Automation Protocol in 13 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
55
---
66

77
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -20,7 +20,6 @@ This section contains all protocol schemas for the automation layer of ObjectSta
2020
<Card href="/docs/references/automation/node-executor" title="Node Executor" description="Source: packages/spec/src/automation/node-executor.zod.ts" />
2121
<Card href="/docs/references/automation/schedule-organization" title="Schedule Organization" description="Source: packages/spec/src/automation/schedule-organization.zod.ts" />
2222
<Card href="/docs/references/automation/schemaless-node-config" title="Schemaless Node Config" description="Source: packages/spec/src/automation/schemaless-node-config.zod.ts" />
23-
<Card href="/docs/references/automation/state-machine" title="State Machine" description="Source: packages/spec/src/automation/state-machine.zod.ts" />
2423
<Card href="/docs/references/automation/time-relative-trigger" title="Time Relative Trigger" description="Source: packages/spec/src/automation/time-relative-trigger.zod.ts" />
2524
<Card href="/docs/references/automation/webhook" title="Webhook" description="Source: packages/spec/src/automation/webhook.zod.ts" />
2625
</Cards>

‎content/docs/references/automation/meta.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"execution",
77
"flow",
88
"node-executor",
9-
"state-machine",
109
"time-relative-trigger",
1110
"---Integration & Data---",
1211
"bpmn-interop",

0 commit comments

Comments
 (0)