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
49 changes: 49 additions & 0 deletions .changeset/retirement-sentence-states-what-migrate-meta-does.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
"@objectstack/lint": patch
---

fix(spec): the retirement prescriptions state what `os migrate meta` actually does (#9529)

Every `retiredKey()` prescription whose surface an ADR-0087 conversion covers
closed with a maintainer-ruled sentence (2026-08-09, #6856):

> Run `os migrate meta --from N` to rewrite existing sources automatically.

The command has never rewritten an authored source file. It replays the
conversion chain over the loaded stack **in memory**, prints the attributed
mechanical change list (`Applied N mechanical change(s)`, one line per site as
`path: from → to (conversionId)`), and writes exactly one file — the `--out`
JSON snapshot, when you ask for it. Every write site in
`packages/cli/src/commands/migrate/meta.ts` is that snapshot; there is no
`--write` / `--fix` / in-place flag. So an author who followed the prescription
got the chain replayed, a printed diff and optionally a JSON document in a shape
their per-artifact `.ts` modules are not written in — and then still edited every
file by hand, with nothing in the message saying so.

Under the maintainer's ruling of 2026-08-18 the sentence is withdrawn in favour
of an honest one, class-wide:

> Run `os migrate meta --from N` to list the mechanical edits for existing
> sources; apply them by hand.

The partial-value conversions keep their two-clause shape, reworded the same way
(`… to list the mechanical edits for the \`1y\` case; the other durations are
reported for you to re-state.`). Behaviour is unchanged in both packages — this
is message text only, and no accept/reject verdict moves.

The claim is withdrawn from every shipped site, not only the canonical sentence:
the variant phrasings in tombstone and conversion-registry prose ("rewrites
author sources", "rewrites it for you", "only `os migrate meta` rewrites
sources") go with it, as do the upgrade-path statements in the hand-written docs
(`upgrading.mdx` now carries the same "does not rewrite your source files" fact
the `objectstack-upgrade` skill already told operators). The class-wide pin
`packages/spec/src/shared/retired-key-migrate-sentence.test.ts` moves in
lockstep and now holds **both** directions: the new sentence is required where a
prescription names the command, and the withdrawn claim is a hard failure
wherever it reappears — including in a prescription that spells the bare command
without `--from N`, which the sentence-shape check alone would not have seen.

The in-place AST codemod that would make the original claim true is commissioned
separately for v18 (#9591); when it lands, the sentence may be restored by
editing that one pin in the same PR.
2 changes: 1 addition & 1 deletion content/docs/automation/flows.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -1104,7 +1104,7 @@ on, and every removed `ETL*` name fails at `tsc`.
<Callout type="warn">
**17.0.0 breaking:** the base delay here was `retryDelayMs` and is now
`backoffMs` — same value, same meaning. `retryDelayMs` is rejected with the
rename; `os migrate meta --from 16` rewrites it for you.
rename; `os migrate meta --from 16` lists the edit for you to apply.
</Callout>

| Property | Type | Description |
Expand Down
4 changes: 2 additions & 2 deletions content/docs/data-modeling/fields.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -376,8 +376,8 @@ server enforces `requiredWhen` on submit and ignores writes to fields whose
`readonlyWhen` predicate is `TRUE`, so client-side affordances are not the
only guard. Use `requiredWhen`. The deprecated `conditionalRequired` alias was removed in
protocol 17 (#3855): authoring it is rejected with an error naming the
replacement, not silently stripped, and `os migrate meta --from 16` rewrites
existing sources automatically.
replacement, not silently stripped, and `os migrate meta --from 16` lists the
mechanical edits for existing sources.

#### Who the lock applies to — and the derived-field write path

Expand Down
2 changes: 1 addition & 1 deletion content/docs/protocol/objectui/actions.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,7 +51,7 @@ The `type` field selects how an action is dispatched. The complete enum is **`sc
| `api` | Call an API endpoint (`method` defaults to `POST`). | Yes |
| `form` | Open a FormView by name, routed to `/_console/forms/:name` **inside the console shell**; the submit lands on the created record. | Yes |

`target` is the canonical binding for every non-`script` type, and since protocol 17 it is the **only** one. The deprecated `execute` alias was removed (#3855): authoring it is rejected with an error naming the replacement, not silently stripped. Run `os migrate meta --from 16` to rewrite existing sources automatically — the removal also ships in the machine-readable change manifest (`spec-changes.json`), which composes across however many majors you are jumping.
`target` is the canonical binding for every non-`script` type, and since protocol 17 it is the **only** one. The deprecated `execute` alias was removed (#3855): authoring it is rejected with an error naming the replacement, not silently stripped. Run `os migrate meta --from 16` to list the mechanical edits for existing sources, then apply them by hand — the removal also ships in the machine-readable change manifest (`spec-changes.json`), which composes across however many majors you are jumping.

### Script Actions

Expand Down
4 changes: 2 additions & 2 deletions content/docs/references/ai/agent.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -53,8 +53,8 @@ const result = AIModelConfigSchema.parse(data);
| **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 (liveness #1878/#1893). |
| **surface** | `Enum<'ask' \| 'build'>` | optional (default: `"ask"`) | Product surface this agent binds ('ask' \| 'build') — ADR-0063 §1 |
| **skills** | `string[]` | optional | Skill names to attach (Agent→Skill→Tool architecture) |
| **tools** | `never` | optional | [REMOVED] `agent.tools` was removed in @objectstack/spec 17 (#3894) — 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. Run `os migrate meta --from 16` to rewrite existing sources automatically. |
| **knowledge** | `never` | optional | [REMOVED] `agent.knowledge` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — declaring knowledge sources/indexes on an agent never scoped retrieval: the `search_knowledge` tool takes `sourceIds` from the LLM's tool-call arguments, not from the agent record. Delete the block. Restrict retrieval at the knowledge-service / source level (per-source permissions), and describe intended grounding in `instructions` so the model asks for the right sources. Run `os migrate meta --from 16` to rewrite existing sources automatically. |
| **tools** | `never` | optional | [REMOVED] `agent.tools` was removed in @objectstack/spec 17 (#3894) — 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. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **knowledge** | `never` | optional | [REMOVED] `agent.knowledge` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — declaring knowledge sources/indexes on an agent never scoped retrieval: the `search_knowledge` tool takes `sourceIds` from the LLM's tool-call arguments, not from the agent record. Delete the block. Restrict retrieval at the knowledge-service / source level (per-source permissions), and describe intended grounding in `instructions` so the model asks for the right sources. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **active** | `boolean` | optional (default: `true`) | |
| **access** | `string[]` | optional | Who can chat with this agent |
| **permissions** | `string[]` | optional | Required permission-set capabilities |
Expand Down
2 changes: 1 addition & 1 deletion content/docs/references/ai/skill.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -38,7 +38,7 @@ const result = SkillSchema.parse(data);
| **surface** | `Enum<'ask' \| 'build' \| 'both'>` | optional (default: `"ask"`) | Agent surface this skill binds to ('ask' \| 'build' \| 'both') — ADR-0063 §3; read by the cloud agent runtime only |
| **instructions** | `string` | optional | LLM instructions when skill is active — also served as an MCP prompt (#3905) |
| **tools** | `string[]` | ✅ | Tool names belonging to this skill (supports trailing wildcard, e.g. `action_*`) — bound by the cloud agent runtime only |
| **triggerPhrases** | `never` | optional | [REMOVED] `skill.triggerPhrases` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — phrases were never matched against the user's message; skill activation is `triggerConditions` (AND of context field/operator/value) intersected with the agent's `skills[]`, plus explicit /skill-name pinning. Delete the key. Put routing intent in `triggerConditions`; describe intent in `description`/`instructions` for the LLM. Run `os migrate meta --from 16` to rewrite existing sources automatically. |
| **triggerPhrases** | `never` | optional | [REMOVED] `skill.triggerPhrases` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — phrases were never matched against the user's message; skill activation is `triggerConditions` (AND of context field/operator/value) intersected with the agent's `skills[]`, plus explicit /skill-name pinning. Delete the key. Put routing intent in `triggerConditions`; describe intent in `description`/`instructions` for the LLM. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **triggerConditions** | `{ field: string; operator: Enum<'eq' \| 'neq' \| 'in' \| 'not_in' \| 'contains'>; value: string \| string[] }[]` | optional | Programmatic activation conditions — evaluated by the cloud agent runtime only |
| **active** | `boolean` | optional (default: `true`) | Whether the skill is enabled |
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this skill. |
Expand Down
4 changes: 2 additions & 2 deletions content/docs/references/api/automation-api.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -92,12 +92,12 @@ const result = AutomationApiErrorCode.parse(data);
| **errorMessage** | `string` | optional | Message carried on AutomationResult for every terminal run (not only screen flows); the screen-flow UI shows it as a toast instead of the raw error. |
| **version** | `integer` | optional (default: `1`) | Version number |
| **status** | `Enum<'draft' \| 'active' \| 'obsolete' \| 'invalid'>` | optional (default: `"draft"`) | Deployment status |
| **template** | `never` | optional | [REMOVED] `flow.template` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — no designer or engine path ever read it, so flagging a flow as a template/subflow did nothing. Delete the key. Shared logic is invoked via a subflow NODE referencing the flow by name. Run `os migrate meta --from 16` to rewrite existing sources automatically. |
| **template** | `never` | optional | [REMOVED] `flow.template` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — no designer or engine path ever read it, so flagging a flow as a template/subflow did nothing. Delete the key. Shared logic is invoked via a subflow NODE referencing the flow by name. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **type** | `Enum<'autolaunched' \| 'record_change' \| 'schedule' \| 'screen' \| 'api'>` | ✅ | Flow type |
| **variables** | `{ name: string; type: string; isInput?: boolean; isOutput?: boolean; … }[]` | optional | Flow variables |
| **nodes** | `{ id: string; type: string; label: string; config?: Record<string, any>; … }[]` | ✅ | Flow nodes |
| **edges** | `{ id: string; source: string; target: string; condition?: string \| object; … }[]` | ✅ | Flow connections |
| **active** | `never` | optional | [REMOVED] `flow.active` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — it never had an effect: the engine arms flows from `status`, and `active: false` did NOT stop a flow (worse, the default read as disabled while the engine treated unset as enabled). Delete the key. Use `status: 'obsolete'` (or 'invalid') to unbind and disable a flow, `status: 'active'` to arm it. Run `os migrate meta --from 16` to rewrite existing sources automatically. |
| **active** | `never` | optional | [REMOVED] `flow.active` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — it never had an effect: the engine arms flows from `status`, and `active: false` did NOT stop a flow (worse, the default read as disabled while the engine treated unset as enabled). Delete the key. Use `status: 'obsolete'` (or 'invalid') to unbind and disable a flow, `status: 'active'` to arm it. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **runAs** | `Enum<'system' \| 'user'>` | optional (default: `"user"`) | Execution identity for the run: system = elevated (bypasses RLS), user = the triggering user (RLS-respecting). A run with no trigger user has no identity to scope to, so under user its data operations are REFUSED — declare system to make the elevation explicit. This covers schedule/time-relative/api triggers AND any record-change flow fired by a write that carried no user. |
| **errorHandling** | `{ strategy?: Enum<'fail' \| 'retry' \| 'continue'>; maxRetries?: integer; backoffMs?: integer; backoffMultiplier?: number; … }` | optional | Flow-level error handling configuration |
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this flow. |
Expand Down
2 changes: 1 addition & 1 deletion content/docs/references/api/rest-server.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -156,7 +156,7 @@ const result = BatchEndpointsConfigSchema.parse(data);
| **enableOpenApi** | `boolean` | optional (default: `true`) | Enable OpenAPI 3.1 spec & docs viewer endpoints |
| **enableProjectScoping** | `boolean` | optional (default: `false`) | Enable project-scoped routing for data/meta/AI APIs |
| **projectResolution** | `Enum<'required' \| 'optional' \| 'auto'>` | optional (default: `"auto"`) | Project ID resolution strategy |
| **requireAuth** | `never` | optional | [REMOVED] `api.requireAuth` was removed in @objectstack/spec 17 (#3963). Anonymous access to object data is now always denied — auth is a kernel concern, not a deployment posture. Delete the key. To publish something publicly, declare it: a public form view (`sharing.allowAnonymous`), a share link, or `book.audience: 'public'` — each derives its own narrow authorization instead of opening the whole data plane. Run `os migrate meta --from 16` to rewrite existing sources automatically. |
| **requireAuth** | `never` | optional | [REMOVED] `api.requireAuth` was removed in @objectstack/spec 17 (#3963). Anonymous access to object data is now always denied — auth is a kernel concern, not a deployment posture. Delete the key. To publish something publicly, declare it: a public form view (`sharing.allowAnonymous`), a share link, or `book.audience: 'public'` — each derives its own narrow authorization instead of opening the whole data plane. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |
| **documentation** | `{ enabled: boolean; title: string; description?: string; version?: string; … }` | optional | OpenAPI/Swagger documentation config |
| **responseFormat** | `{ envelope: boolean; includeMetadata: boolean; includePagination: boolean }` | optional | Response format options |

Expand Down
2 changes: 1 addition & 1 deletion content/docs/references/automation/control-flow.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -149,7 +149,7 @@ const result = FlowRegionSchema.parse(data);
| **backoffMultiplier** | `number` | optional (default: `1`) | Exponential backoff multiplier; 1 (the default) keeps the delay flat |
| **maxRetryDelayMs** | `integer` | optional (default: `30000`) | Ceiling for a single backoff delay (ms) |
| **jitter** | `boolean` | optional (default: `false`) | Randomize each delay within [50%, 100%] of its computed value — spreads a thundering herd of simultaneous retries |
| **retryDelayMs** | `never` | optional | [REMOVED] `retryDelayMs` was removed in @objectstack/spec 17.0.0 (#4661, #4964) — the retry policy now has ONE spelling for its base delay across every surface that carries it: `job.retryPolicy`, a `try_catch` node's `retry` and `flow.errorHandling`. Rename the key to `backoffMs`; the value (milliseconds before the first retry) is unchanged. Run `os migrate meta --from 16` to rewrite existing sources automatically. |
| **retryDelayMs** | `never` | optional | [REMOVED] `retryDelayMs` was removed in @objectstack/spec 17.0.0 (#4661, #4964) — the retry policy now has ONE spelling for its base delay across every surface that carries it: `job.retryPolicy`, a `try_catch` node's `retry` and `flow.errorHandling`. Rename the key to `backoffMs`; the value (milliseconds before the first retry) is unchanged. Run `os migrate meta --from 16` to list the mechanical edits for existing sources; apply them by hand. |


---
Expand Down
Loading
Loading