SCHEMA_MIGRATION.md
Rama main · réplica de lectura del repositorio original.
# Schema v2 Migration Guide
## Field Mapping
| v1 | v2 |
|----|----|
| `command: echo hello` | `run: echo hello` |
| multi-line `command: |` | `run: |` |
| `script: |` | `run: |` |
| `shell`, `shell_args`, `shell_packages` | move under `with:` on the `run:` step |
| `exec:` | `action: exec` with `with.command` and `with.args` |
| `type: http` | `action: http.request` |
| `type: ssh` + `command` | `action: ssh.run` with `with.command` |
| `type: postgres` + `command` | `action: postgres.query` with `with.query` |
| `type: sqlite` + `command` | `action: sqlite.query` with `with.query` |
| SQL import config | `action: postgres.import` or `action: sqlite.import` |
| `type: jq` + `command` | `action: jq.filter` with `with.filter` |
| `type: docker` | `action: docker.run` |
| `type: container` | `action: container.run` |
| `type: k8s` or `type: kubernetes` | `action: k8s.run` or `action: kubernetes.run` |
| `call:` + `params:` | `action: dag.run` with `with.dag` and `with.params` |
| `parallel:` with `call:` | `parallel:` with `action: dag.run` |
| `type: router`, `value`, `routes` | `action: router.route` with `with.value` and `with.routes` |
| `type: chat`, `messages`, `llm` | `action: chat.completion` with `with.prompt` or `with.messages` plus LLM keys |
| `type: harness` + prompt in `command` | `action: harness.run` with `with.prompt` |
| `type: template` + `script` | `action: template.render` with `with.template` |
| `type: log` | `action: log.write` |
| `type: mail` | `action: mail.send` |
| `type: archive` | `action: archive.create`, `archive.extract`, or `archive.list` |
| `type: s3` | `action: s3.upload`, `s3.download`, `s3.list`, or `s3.delete` |
| `type: sftp` | `action: sftp.upload` or `sftp.download` |
| `type: redis` | `action: redis.<operation>` |
| `type: noop` | `action: noop` |
| `step_types:` | `actions:` |
| `config:` | `with:` |
## Local Command Steps
Before:
```yaml
steps:
- id: test
command: go test ./...
```
After:
```yaml
steps:
- id: test
run: go test ./...
```
Multi-line command/script before:
```yaml
steps:
- id: build
script: |
set -e
npm ci
npm test
```
After:
```yaml
steps:
- id: build
run: |
set -e
npm ci
npm test
```
Shell settings before:
```yaml
steps:
- id: bash
command: echo "$SHELL"
shell: bash -e
shell_args: [-c]
```
After:
```yaml
steps:
- id: bash
run: echo "$SHELL"
with:
shell: bash -e
shell_args: [-c]
```
Only `shell`, `shell_args`, and `shell_packages` are allowed in `with:` for a
`run:` step.
## Direct Exec
Before:
```yaml
steps:
- id: direct
exec:
command: /usr/bin/python3
args: [-u, app.py, --limit, 10]
```
After:
```yaml
steps:
- id: direct
action: exec
with:
command: /usr/bin/python3
args: [-u, app.py, --limit, 10]
```
## HTTP
Before:
```yaml
steps:
- id: request
type: http
with:
method: POST
url: https://api.example.com/jobs
body: '{"mode":"daily"}'
```
After:
```yaml
steps:
- id: request
action: http.request
with:
method: POST
url: https://api.example.com/jobs
body: '{"mode":"daily"}'
```
## SQL Query
Before:
```yaml
steps:
- id: query
type: postgres
command: SELECT * FROM users
with:
dsn: ${DATABASE_URL}
```
After:
```yaml
steps:
- id: query
action: postgres.query
with:
dsn: ${DATABASE_URL}
query: SELECT * FROM users
```
For SQLite, use `action: sqlite.query`.
## SSH
Before:
```yaml
steps:
- id: deploy
type: ssh
command: cd /srv/app && git pull && systemctl restart app
with:
host: prod.example.com
user: deploy
```
After:
```yaml
steps:
- id: deploy
action: ssh.run
with:
host: prod.example.com
user: deploy
command: cd /srv/app && git pull && systemctl restart app
```
## Child DAGs
Before:
```yaml
steps:
- id: child
call: workflows/process-account
params:
ACCOUNT_ID: acct_123
```
After:
```yaml
steps:
- id: child
action: dag.run
with:
dag: workflows/process-account
params:
ACCOUNT_ID: acct_123
```
Parallel before:
```yaml
steps:
- id: fanout
call: workflows/process-account
params:
ACCOUNT_ID: ${ITEM.account_id}
parallel:
items:
- account_id: acct_1
- account_id: acct_2
```
Parallel after:
```yaml
steps:
- id: fanout
action: dag.run
with:
dag: workflows/process-account
params:
ACCOUNT_ID: ${ITEM.account_id}
parallel:
items:
- account_id: acct_1
- account_id: acct_2
```
`parallel:` currently requires `action: dag.run` or `action: dag.enqueue`.
## Router
Before:
```yaml
steps:
- id: route
type: router
value: ${STATUS}
routes:
success: [deploy]
failed: [notify]
```
After:
```yaml
steps:
- id: route
action: router.route
with:
value: ${STATUS}
routes:
success: [deploy]
failed: [notify]
```
## jq
Before:
```yaml
steps:
- id: pick_name
type: jq
command: .name
script: '{"name":"Alice"}'
```
After:
```yaml
steps:
- id: pick_name
action: jq.filter
with:
filter: .name
data:
name: Alice
```
Use `with.input` for a file path. Do not set both `with.data` and
`with.input`.
## Harness
Before:
```yaml
steps:
- id: review
type: harness
command: Review the current branch and list actionable issues.
with:
provider: codex
```
After:
```yaml
steps:
- id: review
action: harness.run
with:
provider: codex
prompt: Review the current branch and list actionable issues.
```
If the v1 harness used `script:` for stdin, use `with.stdin` in v2.
## Chat
Chat before:
```yaml
steps:
- id: summarize
type: chat
messages:
- role: user
content: Summarize ${REPORT_PATH}
llm:
provider: openai
model: gpt-4.1
```
Chat after:
```yaml
steps:
- id: summarize
action: chat.completion
with:
provider: openai
model: gpt-4.1
messages:
- role: user
content: Summarize ${REPORT_PATH}
```
## Template
Before:
```yaml
steps:
- id: render
type: template
with:
data:
name: Alice
script: |
Hello, {{ .name }}!
```
After:
```yaml
steps:
- id: render
action: template.render
with:
data:
name: Alice
template: |
Hello, {{ .name }}!
```
## `step_types` to `actions`
Before:
```yaml
step_types:
greet:
type: command
description: Print a greeting
input_schema:
type: object
additionalProperties: false
required: [message]
properties:
message:
type: string
template:
command: echo {{ json .input.message }}
steps:
- id: say_hello
type: greet
with:
message: hello
```
After:
```yaml
actions:
greet:
description: Print a greeting
input_schema:
type: object
additionalProperties: false
required: [message]
properties:
message:
type: string
template:
run: echo {{ json .input.message }}
steps:
- id: say_hello
action: greet
with:
message: hello
```
Important differences:
- `actions.<name>.type` is not supported.
- `actions.<name>.template` must contain exactly one of `run` or `action`.
- Legacy execution fields are rejected inside action templates.
- Custom action names may contain dots, such as `slack.notify`.
- Custom action names cannot conflict with builtin action names.
## Mixed Syntax to Avoid
Do not leave v1 execution fields beside v2 fields:
```yaml
steps:
- id: bad
run: echo hello
command: echo legacy
```
```yaml
steps:
- id: bad
action: http.request
type: http
with:
method: GET
url: https://example.com
```
The parser rejects these because `run:` and `action:` cannot be used with
legacy execution fields on the same step.