diff --git a/content/docs/protocol/objectql/state-machine.mdx b/content/docs/protocol/objectql/state-machine.mdx index 85fbf53d96..4b69171dd2 100644 --- a/content/docs/protocol/objectql/state-machine.mdx +++ b/content/docs/protocol/objectql/state-machine.mdx @@ -124,7 +124,7 @@ A `state_machine` rule has no per-transition guard. To gate a transition on a pr Because the transition table is data, both UIs and Agents can ask "from this state, what's legal next?" instead of parsing a formula. - **In code**, `legalNextStates(objectSchema, field, currentState)` from `@objectstack/objectql` returns the declared next states, `[]` when the state has no outgoing edges (an explicit `[]` **or** a state the table never mentions — introspection does not distinguish the two, enforcement does), or `null` when no `state_machine` rule governs the field. -- **Over HTTP**, `GET /api/v1/meta/object/:name/state/:field?from=:state` returns `{ object, field, from, next }`, where `next` is the legal-next list (or `null`). +- **Over HTTP**, `GET /api/v1/meta/object/:name/state/:field?from=:state` returns `{ object, field, from, next }`, where `next` is the legal-next list, or `null` for **either** of two reasons: no `state_machine` rule governs the field, **or** `?from=` was omitted (no `from` ⇒ no transition table to answer with). A `null` from a call that passed no `from` is therefore not evidence that the field has no state machine — re-ask with `?from=`. When an AI Agent or Flow tries to update `status` to `approved` while the record is in `draft`, the write fails with a `ValidationError` (field error code `invalid_transition`) — the AI-mistake protection the rule exists for. There is no automatic injection of per-state AI instructions into the prompt; the guardrail is the enforced transition table. diff --git a/skills/objectstack-automation/SKILL.md b/skills/objectstack-automation/SKILL.md index 29f96704d6..47d9094d1e 100644 --- a/skills/objectstack-automation/SKILL.md +++ b/skills/objectstack-automation/SKILL.md @@ -279,7 +279,8 @@ Notes: transition, e.g. `previous.status != 'escalated' && record.status == 'escalated'`. - **Introspection:** `GET /api/v1/meta/object/:name/state/:field?from=:state` returns the legal next states so UIs/agents can read the transition table - instead of hard-coding it (`next: null` when no FSM governs the field). + instead of hard-coding it (`next: null` = no FSM governs the field, **or** + `?from=` was omitted — always pass `from`). - Predicate conditions in sibling rules evaluate against the merged record in the **`record.`** CEL scope (bare field names do not resolve).