CLI tool wf — diseño del command surface¶
CLI ergonómica para developers (deploy BPMN, start instance) y operators (replay, inspect, drain). Inspirada en
kubectl,gh,flyctl. Single binary Go, completion shell, output JSON/YAML/table.
Goals¶
- Hello-world < 30 segundos:
wf deploy order.bpmn && wf start order-flow --var amount=100. - Operator-friendly: comandos para incident triage, replay, drain, snapshot inspection.
- Scriptable: salida JSON estable; exit codes coherentes.
- Discoverable:
wf help,wf <cmd> --help, shell completion. - Cross-platform: macOS / Linux / Windows; binary 100% Go.
Anti-goals¶
- ❌ Reemplazar el UI (Operate). El CLI cubre tareas que el UI no hace o son tediosas.
- ❌ TUI interactivo a la
lazygit. Subset opcional, no obligatorio. - ❌ Plugins (todavía). Mantener single binary monolítico.
Estructura de comandos¶
wf <noun> <verb> [args] [flags]
Nouns: Verbs típicos:
process list, get, deploy, undeploy
instance list, get, start, cancel, modify, migrate
job list, get, fail, complete
incident list, get, resolve
message publish, list
variable get, set
decision list, get, evaluate
user list, get, create, delete
tenant list, create, delete
cluster status, info, drain, undrain
snapshot list, create, restore
replay start, status, stop
audit tail, query
Aliases útiles: wf ps = wf instance list, wf logs = wf audit tail.
Comandos esenciales (M1)¶
Authentication¶
# Login (OIDC device flow)
wf login --server https://wf.example.com
# → abre browser, valida, guarda token en ~/.wf/config.yaml
# Context (multi-cluster)
wf context list
wf context use prod
wf context create dev --server http://localhost:8080 --insecure
Config en ~/.wf/config.yaml (mismo esquema que kubeconfig):
contexts:
- name: prod
server: https://wf.example.com
tenant: acme
token-file: ~/.wf/prod.token
- name: dev
server: http://localhost:8080
insecure: true
current-context: dev
Deploy & start¶
# Deploy un BPMN
wf process deploy order-flow.bpmn
# → output:
# Deployed:
# process order-flow v3 key=2251799813685250
# decision credit-check v1 key=2251799813685251 (DMN embebido)
# Deploy con tenant
wf process deploy order-flow.bpmn --tenant acme
# Validate sin deploy (lint)
wf process validate order-flow.bpmn
# List
wf process list
wf process list --tenant acme --version-latest
# Start instance
wf instance start order-flow --var amount=100 --var customer=cust-42
# → key: 2251799813685300, status: ACTIVE
# Start con variables desde archivo
wf instance start order-flow --vars-file vars.json
wf instance start order-flow --vars-file vars.yaml
# Start sincrónico (espera hasta complete)
wf instance start order-flow --vars-file vars.json --await --timeout 30s
# → key: 2251799813685300, status: COMPLETED, output: {...}
Inspect¶
# Instance detail
wf instance get 2251799813685300
wf instance get 2251799813685300 -o yaml
wf instance get 2251799813685300 -o json | jq '.variables'
# Timeline (audit trail de eventos)
wf instance timeline 2251799813685300
# → 14:23:01.123 ProcessInstanceStarted order-flow v3
# 14:23:01.234 ElementActivated StartEvent_1
# 14:23:01.345 ElementCompleted StartEvent_1
# 14:23:01.456 JobCreated charge-payment, key=...
# 14:23:02.789 JobCompleted charge-payment, duration=1.3s
# ...
# Variables actuales
wf variable get 2251799813685300
wf variable get 2251799813685300 --scope local --element Task_charge
# Stuck instances (heuristic)
wf instance list --stuck-since 1h
# → instancias sin progreso en última hora
Incident management¶
wf incident list
wf incident list --active --process order-flow
wf incident get <key>
wf incident resolve <key>
wf incident resolve <key> --update-retries 3
# Bulk resolve (peligroso, requiere --yes)
wf incident list --active --error-type EXTRACT_VALUE_ERROR --output keys | \
xargs -n 1 wf incident resolve --yes
Job operations (worker pov)¶
# Manual job complete (debugging)
wf job complete <job-key> --vars-file output.json
# Manual job fail
wf job fail <job-key> --reason "transient db error" --retries 2
# Activate job (similar a worker pulling)
wf job activate --type send-email --worker manual-test --max 5
Comandos operacionales (M2-M3)¶
Cluster ops¶
wf cluster status
# → 3 nodes (engine-1 leader, engine-2 follower, engine-3 follower)
# postgres-primary: pg-1 (lag 0ms)
# postgres-replica: pg-2 (lag 12ms)
# active instances: 4,231
# jobs in queue: 87
# p95 cmd latency: 8ms
wf cluster info -o json # para monitoring scripts
# Drain (preparar shutdown gracioso)
wf cluster drain --node engine-1 --timeout 5m
# Reverse
wf cluster undrain --node engine-1
Replay & snapshot¶
# Replay (reconstruir state desde command log)
wf replay start --from-time '2026-05-14T10:00:00Z' --to-time '2026-05-14T11:00:00Z'
wf replay status
wf replay stop
# Snapshots (Postgres logical backup + WAL)
wf snapshot list
wf snapshot create --tag manual-pre-deploy
wf snapshot restore <id> --confirm
Replay determinism check¶
wf replay verify --from <commit> --to <commit>
# → re-aplica command log y compara final state con expected
# ✓ 1,234 commands replayed
# ✓ Final state hash matches
# ✗ MISMATCH: instance 2251... at command 567 (saved offset)
Crítico para verificar la invariante de adrs/adr-019-replay-determinism-invariant.
Migration¶
# Migrate instances entre versiones de proceso
wf instance migrate 2251... --target-process-key 2251799813685251 \
--mapping '{"oldTask": "newTask"}'
# Bulk migrate
wf instance list --process-id order-flow --version 2 --output keys | \
wf instance migrate --target-process-key <v3-key> --mapping mapping.json --bulk
Tenant management¶
wf tenant list
wf tenant create acme --description "Acme Corp"
wf tenant delete acme --confirm
wf tenant quota set acme --max-instances 10000 --max-jobs-per-min 1000
Audit¶
wf audit tail
wf audit tail --user paulo@example.com
wf audit query --action 'instance.cancel' --since 24h
wf audit query --resource 'process/order-flow' --output json
Output formats¶
Convención (igual que kubectl):
--output table # default, columnas legibles
--output json # JSON formatted
--output yaml # YAML
--output jsonpath='{.items[*].key}' # extraer fields
--output keys # solo las keys (1 por línea) — útil para xargs
--output go-template=... # template Go
Exit codes:
0 success
1 user error (bad args, validation)
2 network / auth error
3 not found
4 conflict (precondition failed)
5 internal error
Stderr para logs/progreso, stdout para datos. Permite:
Shell completion¶
wf completion bash > /etc/bash_completion.d/wf
wf completion zsh > $(brew --prefix)/share/zsh/site-functions/_wf
wf completion fish | source
wf completion powershell | Out-String | Invoke-Expression
Completion dinámica vía API (process IDs, instance keys, tenants).
Implementación interna¶
Tech stack¶
- Framework: Cobra + Viper (defacto en CNCF).
- HTTP client: stdlib
net/httpcon middleware (auth, retry, trace). - Output: tablas con tablewriter; JSON con
encoding/json. - Streaming: pretty-print con go-pretty (tablas dinámicas).
- Config: YAML en
~/.wf/config.yaml, env varsWF_*como override.
Tamaño del binario¶
- Target: < 30 MB compressed (Brotli) para distribución vía GitHub Releases.
wftyped en Go puro, no requiere libc dinámica.- Build con
-ldflags="-s -w"strip debug; ~12 MB sin compresión.
Cross-platform¶
# Build matrix
GOOS=darwin GOARCH=arm64 go build -o wf-darwin-arm64
GOOS=darwin GOARCH=amd64 go build -o wf-darwin-amd64
GOOS=linux GOARCH=amd64 go build -o wf-linux-amd64
GOOS=linux GOARCH=arm64 go build -o wf-linux-arm64
GOOS=windows GOARCH=amd64 go build -o wf-windows-amd64.exe
Distribución vía:
- Homebrew tap (brew install wf-engine/wf).
- curl | sh install script.
- GitHub Releases (binarios + checksums + cosign signature).
- go install github.com/example/wf-cli/cmd/wf@latest.
- Container (docker run --rm wf-engine/wf:latest wf process list).
Comparativa con alternativas¶
| Tool | Pros | Cons |
|---|---|---|
| zbctl (Zeebe CLI oficial) | Cubre comandos básicos | Sintaxis verbose, no shell completion, output sólo JSON, requiere gRPC client local |
| camunda-cli (community) | Comandos para operate | No oficial, no mantenido |
| kubectl + custom resources | Nativo k8s | Sólo si engine es operator-based; alta fricción developer |
| wf (nosotros) | DX moderno, scriptable, completion, single binary | Build & maintenance overhead |
Casos de uso típicos¶
Developer flow¶
# Iterar en un proceso
$EDITOR order-flow.bpmn
wf process validate order-flow.bpmn # lint
wf process deploy order-flow.bpmn # subir
wf instance start order-flow --vars-file test.json --await # run + esperar
wf instance timeline <key> # ver qué pasó
Operator flow (incident response)¶
# Alerta: incidents en última hora subieron
wf incident list --active --since 1h
wf incident get <key> # ver detalle
wf instance timeline <instance-key> # contexto
# Diagnóstico: variable mal serializada
wf variable get <instance-key>
wf variable set <instance-key> orderTotal=99.99
wf incident resolve <key>
CI/CD flow¶
# .github/workflows/deploy.yml
- run: |
wf login --token ${{ secrets.WF_TOKEN }}
wf context use staging
wf process validate processes/*.bpmn # exit 1 si falla
for f in processes/*.bpmn; do
wf process deploy "$f"
done
Migration flow¶
# Lista instances en v2 que podemos migrar
wf instance list --process-id order-flow --version 2 -o keys > to-migrate.txt
# Migrate en batches de 100
split -l 100 to-migrate.txt batch_
for batch in batch_*; do
xargs -a "$batch" wf instance migrate --target-process-key $V3 --bulk
done
Roadmap¶
- M1: comandos esenciales (deploy, start, list, get, incident).
- M2: cluster ops, drain, snapshot, audit.
- M3: replay verify, migration bulk, completion dinámica.
- M4: TUI interactivo opcional (
wf topestilohtop).
Referencias¶
- analysis/developer-experience — DX targets
- adrs/adr-026-go-as-implementation-language — Go para CLI
- analysis/rest-api-design — REST que el CLI consume
- analysis/observability-deep-dive — métricas que
cluster statusmuestra