MVP Implementation Plan — hyper-detallado para Claude Code¶
Plan ejecutable para construir el MVP. Diseñado para alimentar a Claude Code: tasks atómicas con TDD, criterios de aceptación deterministas, DAG de dependencias, toolchain completo de static analysis. Cada task es realizable por un agent en un solo turn.
0. Cómo usar este documento¶
Este plan tiene tres niveles de zoom:
- §1-3 (briefing + stack + repo): contexto de una sola lectura. Léelo entero antes de spawnar agents.
- §4-7 (metodología): reglas que TODOS los agents deben respetar (TDD, determinismo, quality gates).
- §8 (task catalog): ~150 tasks atómicas con spec ejecutable. Esto es lo que pasás a Claude Code.
Workflow Claude Code recomendado¶
1. Cargá este archivo + ADRs relevantes en el context.
2. Identificá la "wave" (fase) actual: T-XXX en estado pending sin dependencies bloqueadas.
3. Spawn un agent por task atómica disponible (típicamente 5-15 en paralelo).
4. Cada agent: lee la task spec, escribe tests primero (RED), implementa (GREEN), refactoriza.
5. CI gate: lint zero, tests pass, coverage >= threshold, SAST clean.
6. Merge automático si todos los gates verdes; PR review si rojo.
7. Repetir con la siguiente wave.
Las tasks están en formato YAML-like estructurado dentro de markdown, parseables por LLM o por scripts.
1. Scope del MVP (M1)¶
Lo que SÍ entra al MVP¶
| Capability | Cobertura |
|---|---|
| BPMN 2.0 core | 28 elementos (ver analysis/bpmn-coverage-matrix M1) |
| Engine | Single-node, command-sourcing, replay determinism |
| Persistence | PostgreSQL único state store |
| REST API | 25 endpoints core (start, complete, list, get, modify) |
| Worker SDK | Go SDK con OTel built-in |
| CLI | wf con 12 comandos esenciales |
| Webapp | Tasklist completo + Process Inspector minimal |
| Auth | OIDC single IdP, RBAC 3 roles |
| Observability | OTel, 50+ métricas, structured logs |
| Migrations | golang-migrate, schemas Postgres |
| Connectors | HTTP/REST outbound básico |
| Expression engine | CEL + FEEL-lite shim |
| Compensation | NO en M1 (M2) |
| Multi-instance | NO en M1 (M2) |
| HA | NO en M1, single-node intencionalmente |
Target operacional M1¶
- Throughput: 100 instances/sec sostenido.
- Latency p99 E2E (W1 workload): < 1s.
- Availability: 99% (single-node + daily backups).
- DX: hello-world < 30 min desde clone.
Out of scope (defer a M2+)¶
- Compensation, multi-instance, event subprocess.
- Signal events.
- DMN decisions.
- gRPC API.
- Kubernetes operator.
- Multi-region.
- Patroni HA.
2. Tech stack definitivo¶
Lenguajes y runtimes¶
| Stack | Versión target | Rationale |
|---|---|---|
| Go | 1.22+ | adrs/adr-026-go-as-implementation-language |
| TypeScript | 5.4+ | Webapp frontend |
| Node | 20 LTS | Frontend toolchain |
| Postgres | 16.x | adrs/adr-002-postgresql-as-state-store |
Librerías Go (engine + CLI + SDK)¶
// Core
github.com/jackc/pgx/v5 // Postgres driver, fastest in Go
github.com/jackc/pgx/v5/pgxpool // Connection pool
github.com/golang-migrate/migrate/v4 // Migrations
// HTTP / REST
github.com/go-chi/chi/v5 // Router, stdlib-friendly
github.com/go-chi/render // JSON responses
github.com/swaggo/swag // OpenAPI generation
// CLI
github.com/spf13/cobra // CLI framework
github.com/spf13/viper // Config
// Observability
go.opentelemetry.io/otel // Tracing
go.opentelemetry.io/otel/sdk
go.opentelemetry.io/otel/exporters/otlp/otlptrace
go.opentelemetry.io/otel/metric
github.com/prometheus/client_golang // Prometheus metrics
log/slog // stdlib structured logs
// BPMN parsing
github.com/nu7hatch/gouuid // UUID v4
encoding/xml // stdlib (BPMN is XML)
// Expression engine
github.com/google/cel-go // CEL evaluator
// Auth
github.com/coreos/go-oidc/v3 // OIDC client
github.com/golang-jwt/jwt/v5 // JWT validation
// Testing
github.com/stretchr/testify // Assertions
github.com/testcontainers/testcontainers-go // Integration tests
go.uber.org/goleak // Goroutine leak detection
github.com/leanovate/gopter // Property-based tests
pgregory.net/rapid // Alternative PBT (better shrinking)
Librerías TypeScript (webapps)¶
{
"react": "18.x",
"react-router-dom": "6.x",
"@tanstack/react-query": "5.x",
"zustand": "4.x",
"vite": "5.x",
"vitest": "1.x",
"@playwright/test": "1.x",
"bpmn-js": "17.x",
"monaco-editor": "0.45.x",
"openapi-typescript": "6.x",
"tailwindcss": "3.x"
}
Infrastructure¶
| Tool | Use |
|---|---|
| Docker / Docker Compose | Dev environment |
| GitHub Actions | CI/CD |
| Helm | k8s deployment templates |
| pgBackRest | Postgres backups |
3. Estructura del repo (monorepo)¶
wf-engine/
├── cmd/
│ ├── engine/ # Engine binary
│ │ └── main.go
│ ├── wf/ # CLI binary
│ │ └── main.go
│ └── migrate/ # Migration runner
│ └── main.go
├── internal/ # No public API
│ ├── api/
│ │ ├── rest/ # HTTP handlers
│ │ ├── auth/ # OIDC, RBAC
│ │ └── middleware/ # Logging, tracing, rate limit
│ ├── engine/
│ │ ├── command/ # Command types
│ │ ├── event/ # Event types
│ │ ├── processor/ # StreamProcessor
│ │ ├── elements/ # 28 BPMN element processors
│ │ ├── expression/ # CEL + FEEL-lite
│ │ ├── timer/ # Timer scheduler
│ │ ├── variable/ # Variable scoping
│ │ └── replay/ # Replay state machine
│ ├── storage/
│ │ ├── postgres/ # Postgres adapter
│ │ └── inmemory/ # In-memory for tests
│ ├── observability/
│ │ ├── metrics/ # Prometheus
│ │ ├── tracing/ # OTel
│ │ └── logging/ # slog setup
│ └── parser/
│ ├── bpmn/ # BPMN XML parser
│ └── validator/ # Deploy-time validation
├── pkg/ # Public API
│ └── wfclient/ # Go SDK
│ ├── client.go
│ ├── worker.go
│ ├── ops.go
│ └── wfclienttest/ # Test utilities
├── webapp/ # TypeScript frontend
│ ├── tasklist/
│ ├── inspector/
│ └── shared/
├── migrations/ # SQL migrations
│ ├── 000001_initial.up.sql
│ └── ...
├── deploy/
│ ├── docker-compose.yml # Dev environment
│ ├── helm/
│ └── k8s/
├── docs/
│ ├── api/ # OpenAPI spec
│ ├── adr/ # Symlink a wiki ADRs
│ └── runbook/
├── scripts/
│ ├── dev/ # Dev tooling
│ ├── ci/ # CI helpers
│ └── benchmark/ # Load test scripts
├── tests/
│ ├── scenario/ # E2E scenario tests
│ ├── load/ # k6 scripts
│ └── fixtures/
│ └── bpmn/ # Sample BPMN files
├── .github/
│ └── workflows/
├── go.mod
├── go.sum
├── Makefile
├── Dockerfile
├── .golangci.yml
├── .sonar-project.properties
└── README.md
Module boundaries¶
Reglas estrictas (enforced via lint):
internal/api/*puede importarinternal/engine/*, NO al revés.internal/engine/*puede importarinternal/storage/*SOLO vía interfaces.pkg/wfclient/*NO importa nada deinternal/*.internal/storage/postgresyinternal/storage/inmemoryimplementan misma interfacestorage.Store.cmd/*solo importaninternal/*ypkg/*.
Verificación: go-arch-lint o tests custom en internal/architecture_test.go.
4. Metodología TDD obligatoria¶
Disciplina red-green-refactor¶
Cada task DEBE seguir:
1. RED: escribe el test, ejecuta, verifica que FALLA por la razón correcta.
2. GREEN: implementa lo mínimo para que el test pase.
3. REFACTOR: limpia el código manteniendo tests verdes.
4. COMMIT: cada paso es un commit separado (atomic, reversible).
Estructura del test file¶
// internal/engine/processor/start_instance_test.go
package processor_test
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/example/wf-engine/internal/engine/processor"
"github.com/example/wf-engine/internal/storage/inmemory"
)
func TestStartInstance_ValidProcess_CreatesInstance(t *testing.T) {
// GIVEN
store := inmemory.New()
proc := processor.New(store, processor.WithClock(fixedClock(t)))
ctx := context.Background()
procDef := loadBPMN(t, "testdata/order-flow.bpmn")
require.NoError(t, proc.Deploy(ctx, procDef))
// WHEN
instance, err := proc.StartInstance(ctx, processor.StartCmd{
ProcessID: "order-flow",
Variables: map[string]any{"amount": 99.99},
TenantID: "test-tenant",
})
// THEN
require.NoError(t, err)
require.NotZero(t, instance.Key)
require.Equal(t, "ACTIVE", instance.State)
require.Equal(t, "order-flow", instance.ProcessID)
// AND audit log was written
events := store.GetEvents(ctx, instance.Key)
require.Len(t, events, 2) // PROCESS_INSTANCE_CREATED + ELEMENT_ACTIVATED
}
func TestStartInstance_UnknownProcess_ReturnsNotFound(t *testing.T) {
store := inmemory.New()
proc := processor.New(store)
_, err := proc.StartInstance(context.Background(), processor.StartCmd{
ProcessID: "non-existent",
})
require.ErrorIs(t, err, processor.ErrProcessNotFound)
}
Naming conventions de tests¶
Test<Subject>_<Scenario>_<ExpectedOutcome>:
TestStartInstance_ValidProcess_CreatesInstance✓TestStartInstance_UnknownProcess_ReturnsNotFound✓TestStartInstance_DuplicateUniquenessKey_ReturnsExisting✓
Anti-patterns:
- TestStartInstance ✗ (no scenario)
- TestStartInstance1, TestStartInstance2 ✗
- TestEngine ✗ (too broad)
Tests por categoría (obligatorias)¶
Cada package debe tener tests en estas 4 categorías:
- Happy path: feature funciona con input válido.
- Edge cases: nil inputs, empty collections, max/min values.
- Error paths: cada error explícito tiene un test.
- Invariants: properties holdthat must hold across all inputs (PBT).
Mínimo 4 tests por función pública.
Coverage thresholds (CI gate)¶
| Module | Statement coverage | Branch coverage |
|---|---|---|
internal/engine/* |
95% | 90% |
internal/api/* |
90% | 85% |
internal/storage/postgres |
85% | 75% |
internal/parser/* |
95% | 90% |
pkg/wfclient/* |
90% | 85% |
cmd/* |
70% | — |
Comando: go test -cover -coverprofile=cover.out ./...
Tool para branch coverage: gobco o config de gocover-cobertura.
Mutation testing¶
Para módulos críticos (engine, parser, expressions):
go install github.com/avito-tech/go-mutesting/cmd/go-mutesting@latest
go-mutesting ./internal/engine/processor/...
Target: > 75% mutation kill rate. Mutations sobrevivientes = test gap.
5. Determinismo obligatorio¶
Por qué¶
- adrs/adr-019-replay-determinism-invariant: replay del command log debe producir mismo state.
- Sin determinismo, tests son flaky → falsos positivos → tooling de Claude Code falla.
Reglas¶
Reloj inyectable¶
type Clock interface {
Now() time.Time
}
// Production
type realClock struct{}
func (realClock) Now() time.Time { return time.Now() }
// Tests
type fixedClock struct{ t time.Time }
func (f *fixedClock) Now() time.Time { return f.t }
func (f *fixedClock) Advance(d time.Duration) { f.t = f.t.Add(d) }
NUNCA llamar time.Now() directamente en code paths.
RNG con seed explícito¶
type IDGenerator struct {
rng *rand.Rand
}
func New(seed int64) *IDGenerator {
return &IDGenerator{rng: rand.New(rand.NewSource(seed))}
}
// Tests usan seed fijo
gen := NewIDGenerator(42)
IDs deterministicos¶
Para entities con ID server-generated: derivar del command + position en el log, NO de uuid.New().
// Deterministic key: based on partition + position
key := (int64(partition) << 51) | int64(position)
Sort estable¶
Map iteration en Go no es deterministic. Antes de processing:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
// process m[k]
}
Goroutine leak detection¶
Tests parallelos seguros¶
Cada test crea su propio store y resources. NO globals.
Invariantes testeables (property-based)¶
import "pgregory.net/rapid"
func TestReplayDeterminism(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
commands := rapid.SliceOf(genCommand()).Draw(t, "commands")
state1 := replay(commands)
state2 := replay(commands)
require.Equal(t, hash(state1), hash(state2))
})
}
func TestCommandLogMonotonic(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
cmds := genCommandSequence().Draw(t, "cmds")
positions := applyCommands(cmds)
for i := 1; i < len(positions); i++ {
require.Greater(t, positions[i], positions[i-1])
}
})
}
Mínimo 5 invariantes PBT por módulo crítico.
6. Static analysis toolchain¶
Stack completo Go¶
# .golangci.yml — golangci-lint config
run:
go: '1.22'
timeout: 5m
tests: true
linters:
enable:
# Correctness
- errcheck # Unchecked errors
- govet # Standard vet
- staticcheck # All static analysis
- ineffassign # Inefficient assignments
- unused # Unused identifiers
- gosimple # Simplifications
- unconvert # Unnecessary conversions
- asasalint # ...args of type any pass to vararg
# Performance
- prealloc # Preallocate slices
- bodyclose # http.Response.Body closed
- rowserrcheck # sql.Rows.Err checked
- sqlclosecheck # sql.Rows / Stmt closed
# Style
- gofmt
- goimports
- revive # Replacement for golint
- misspell # Common misspellings
- whitespace
- wsl # Whitespace linter
- gocritic # 100+ checks
# Security
- gosec # Security
- bidichk # Bidirectional Unicode
- exportloopref # Loop variable references
# Complexity
- gocyclo # Cyclomatic complexity
- cognitive # Cognitive complexity (preferred)
- funlen # Function length
- lll # Line length
# Bugs
- errorlint # Wrapped errors checked correctly
- exhaustive # Exhaustive switches
- nilerr # Returning nil after non-nil err
- nilnil # Returning both nil and nil error
- contextcheck # Context propagation
linters-settings:
gocyclo:
min-complexity: 10
cognitive:
min-complexity: 15
funlen:
lines: 60
statements: 40
lll:
line-length: 120
errcheck:
exclude-functions:
- (io.Closer).Close
- fmt.Fprintln
issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0
CI gate: golangci-lint run exit 0.
Security-focused¶
# Security-specific linting
go install github.com/securego/gosec/v2/cmd/gosec@latest
gosec -severity high -confidence high ./...
# Vulnerability scanning (Go modules)
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
# Static security with semgrep (multi-language)
docker run --rm -v "${PWD}:/src" returntocorp/semgrep --config=auto
# Specific rulesets:
# p/golang
# p/owasp-top-ten
# p/sql-injection
# p/jwt
SonarQube / SonarCloud¶
# .sonar-project.properties
sonar.projectKey=example_wf-engine
sonar.organization=example
sonar.host.url=https://sonarcloud.io
sonar.sources=cmd,internal,pkg
sonar.tests=.
sonar.test.inclusions=**/*_test.go
sonar.exclusions=**/vendor/**,**/testdata/**,**/*_mock.go
sonar.go.coverage.reportPaths=cover.out
sonar.go.golangci-lint.reportPaths=golangci-report.xml
# Quality gates
sonar.qualitygate.wait=true
Métricas que SonarQube monitorea: - Bugs: 0 (gate fail si > 0) - Vulnerabilities: 0 - Security hotspots: revisar 100% - Code smells: per-module budget - Duplications: < 3% - Coverage: thresholds de §4 - Technical debt ratio: < 5% - Cognitive complexity: < 15 por function
Quality Gate "WF-Engine" (custom):
Coverage on new code: >= 90%
Duplications on new code: < 3%
Maintainability rating: A
Reliability rating: A
Security rating: A
Security review rating: A
Container & supply chain¶
# Container vulnerability scanning
trivy image example/wf-engine:latest
# SBOM generation
syft example/wf-engine:latest -o spdx-json > sbom.json
# CVE scan over SBOM
grype sbom:sbom.json
# Sign images
cosign sign --key cosign.key example/wf-engine:latest
# Verify signature
cosign verify --key cosign.pub example/wf-engine:latest
Dependency review¶
# Audit go modules
go mod tidy
go mod verify
go list -m -u all # check updates
# License compliance
go install github.com/google/go-licenses@latest
go-licenses report ./... > LICENSES_THIRD_PARTY.md
go-licenses check --disallowed_types=forbidden ./...
# Dependency graph audit
go install github.com/loov/goda@latest
goda graph "./internal/engine/...:all"
TypeScript stack¶
// .eslintrc.json
{
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/strict",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:security/recommended-legacy",
"plugin:sonarjs/recommended",
"plugin:jsx-a11y/recommended"
],
"rules": {
"complexity": ["error", 10],
"max-lines-per-function": ["error", 50],
"no-console": "error",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/strict-boolean-expressions": "error"
}
}
# Audit npm dependencies
npm audit --audit-level=high
pnpm audit # if using pnpm
# Better: socket.dev / snyk
snyk test
SQL linting¶
# sqlfluff for migrations
pip install sqlfluff
sqlfluff lint --dialect postgres migrations/*.sql
sqlfluff fix --dialect postgres migrations/*.sql
BPMN validation¶
# Custom validator
go run ./cmd/wf process validate tests/fixtures/bpmn/*.bpmn
# Schema validation
xmllint --schema schemas/BPMN20.xsd tests/fixtures/bpmn/order-flow.bpmn
Resumen herramientas¶
| Tool | Layer | When |
|---|---|---|
golangci-lint |
Go static | Every commit, CI gate |
gosec |
Go security | CI, weekly deep scan |
govulncheck |
Go CVEs | CI, daily |
semgrep |
Multi-lang security | CI, daily |
SonarQube |
Quality + security | CI, PR gate |
trivy |
Container CVE | Build pipeline |
syft + grype |
SBOM + CVE | Release pipeline |
cosign |
Artifact signing | Release pipeline |
go-licenses |
License compliance | CI weekly |
ESLint + sonarjs |
TS static | Pre-commit |
sqlfluff |
SQL lint | Pre-commit migrations |
mutation-testing |
Test quality | Weekly |
gosec+semgrep rules custom |
SAST | Per-feature security review |
OWASP ZAP (DAST) |
API security | Pre-release |
7. CI/CD pipeline¶
Pipeline stages¶
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
# Stage 1: Fast feedback (< 3 min)
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version: '1.22' }
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
- name: gofmt check
run: test -z "$(gofmt -l .)"
- name: SQL lint
run: pip install sqlfluff && sqlfluff lint migrations/
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- run: go test -race -coverprofile=cover.out -tags=test_inmemory ./...
- uses: codecov/codecov-action@v4
# Stage 2: Deeper validation (5-10 min)
integration-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env: { POSTGRES_PASSWORD: test }
ports: [5432:5432]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- run: make migrate-up
- run: go test -tags=integration -timeout=10m ./...
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: gosec -severity high ./...
- run: govulncheck ./...
- uses: returntocorp/semgrep-action@v1
with: { config: p/owasp-top-ten,p/golang }
sonarqube:
runs-on: ubuntu-latest
needs: [unit-tests]
steps:
- uses: actions/checkout@v4
- uses: SonarSource/sonarqube-scan-action@v2
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- uses: SonarSource/sonarqube-quality-gate-action@v1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# Stage 3: Heavy (only on main / release)
scenario-tests:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: go test -tags=scenario -timeout=20m ./tests/scenario/...
load-tests:
if: github.event_name == 'schedule' # nightly
runs-on: self-hosted
steps:
- run: k6 run tests/load/w1-baseline.js
container-build:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: [unit-tests, security, sonarqube]
steps:
- uses: actions/checkout@v4
- uses: docker/build-push-action@v6
- run: trivy image example/wf-engine:${{ github.sha }}
- run: cosign sign --key ${{ secrets.COSIGN_KEY }} example/wf-engine:${{ github.sha }}
Pre-commit hooks¶
# .pre-commit-config.yaml
repos:
- repo: https://github.com/dnephin/pre-commit-golang
rev: v0.5.1
hooks:
- id: go-fmt
- id: go-imports
- id: golangci-lint
- id: go-unit-tests
- repo: https://github.com/sqlfluff/sqlfluff
rev: 3.0.0
hooks:
- id: sqlfluff-lint
args: [--dialect, postgres]
- repo: https://github.com/streetsidesoftware/cspell-cli
rev: v8.0.0
hooks:
- id: cspell
Branch protection (GitHub)¶
main branch:
- Require pull request before merging
- Require approvals: 1 (Claude Code can self-approve via repo automation)
- Require status checks to pass:
* lint
* unit-tests
* integration-tests
* security
* sonarqube quality gate
- Require branches to be up to date before merging
- Require linear history
- Restrict who can push to matching branches
8. Task catalog¶
Formato de cada task¶
- id: T-001
title: "Short imperative phrase"
phase: P0 | P1 | P2 | ...
estimate: 2h | 4h | 1d | 2d # max 2d, else split
parallelizable: yes | no
dependencies: [T-000, T-002]
files_create:
- path/to/new/file.go
files_modify:
- path/to/existing/file.go
acceptance_criteria:
- "given X, when Y, then Z (linkable test name)"
- "command `make X` exits 0"
- "coverage on new code >= threshold"
test_files:
- path/to/file_test.go
tdd_steps:
1. Write failing test for case A
2. Implement minimal code
3. Add test case B
4. ...
static_analysis:
- golangci-lint clean
- gosec no high-severity findings
notes: "Reference [[link]] for context"
Phase P0: Foundation (Week 0, ~20 tasks)¶
- id: T-001
title: "Initialize monorepo skeleton"
phase: P0
estimate: 2h
parallelizable: no
dependencies: []
files_create:
- go.mod
- go.sum
- Makefile
- .gitignore
- .golangci.yml
- .sonar-project.properties
- README.md
- LICENSE # Apache 2.0
acceptance_criteria:
- "go mod tidy exits 0"
- "make help shows all available targets"
- "directory structure matches §3 exactly"
static_analysis:
- golangci-lint --version succeeds
notes: "Use Go 1.22; module path github.com/example/wf-engine"
- id: T-002
title: "Set up GitHub Actions CI skeleton"
phase: P0
estimate: 3h
parallelizable: no
dependencies: [T-001]
files_create:
- .github/workflows/ci.yml
- .github/workflows/release.yml
- .github/CODEOWNERS
- .github/PULL_REQUEST_TEMPLATE.md
acceptance_criteria:
- "PR opens trigger lint job"
- "lint job exits 0 on empty repo"
notes: "Reference §7 pipeline spec"
- id: T-003
title: "Add Docker Compose dev environment"
phase: P0
estimate: 4h
parallelizable: yes
dependencies: [T-001]
files_create:
- deploy/docker-compose.yml
- deploy/docker-compose.dev.yml
- deploy/init-db.sql
acceptance_criteria:
- "docker compose up -d postgres exits 0"
- "psql connects to localhost:5432"
- "make dev-up brings up all services"
notes: "Include: postgres 16, otel-collector, prometheus, jaeger"
- id: T-004
title: "Install pre-commit hooks"
phase: P0
estimate: 2h
parallelizable: yes
dependencies: [T-001]
files_create:
- .pre-commit-config.yaml
- scripts/dev/install-hooks.sh
acceptance_criteria:
- "scripts/dev/install-hooks.sh succeeds"
- "commit with bad format fails pre-commit"
- id: T-005
title: "Create initial Postgres schema migrations"
phase: P0
estimate: 1d
parallelizable: no
dependencies: [T-003]
files_create:
- migrations/000001_initial.up.sql
- migrations/000001_initial.down.sql
- cmd/migrate/main.go
acceptance_criteria:
- "migrate up creates 12 tables (processes, instances, jobs, ...)"
- "migrate down drops all tables cleanly"
- "psql can describe all tables"
test_files:
- cmd/migrate/main_test.go
tdd_steps:
1. Write test: migrate up, verify tables exist
2. Write empty up.sql, fail
3. Add table definitions one by one
4. Add down.sql, verify clean rollback
static_analysis:
- sqlfluff lint migrations/
- id: T-006
title: "Set up structured logging with slog"
phase: P0
estimate: 3h
parallelizable: yes
dependencies: [T-001]
files_create:
- internal/observability/logging/logger.go
- internal/observability/logging/logger_test.go
acceptance_criteria:
- "logger produces valid JSON output"
- "trace_id propagated from context"
- "log level filtered correctly"
test_files:
- internal/observability/logging/logger_test.go
- id: T-007
title: "Set up OpenTelemetry tracing"
phase: P0
estimate: 4h
parallelizable: yes
dependencies: [T-006]
files_create:
- internal/observability/tracing/tracer.go
- internal/observability/tracing/tracer_test.go
- id: T-008
title: "Set up Prometheus metrics"
phase: P0
estimate: 3h
parallelizable: yes
dependencies: [T-006]
files_create:
- internal/observability/metrics/metrics.go
- internal/observability/metrics/metrics_test.go
- id: T-009
title: "Add storage.Store interface"
phase: P0
estimate: 3h
parallelizable: yes
dependencies: [T-001]
files_create:
- internal/storage/store.go
- internal/storage/store_test.go
- internal/storage/contract_test.go # shared contract tests for all implementations
acceptance_criteria:
- "interface Store defines: Begin, Commit, Rollback, all CRUD methods"
- "contract_test.go exports TestStoreContract(t, factory)"
- id: T-010
title: "Implement in-memory storage adapter"
phase: P0
estimate: 1d
parallelizable: no
dependencies: [T-009]
files_create:
- internal/storage/inmemory/store.go
- internal/storage/inmemory/store_test.go
acceptance_criteria:
- "TestStoreContract passes against inmemory.Store"
- "thread-safe (race detector clean)"
- id: T-011
title: "Implement Postgres storage adapter"
phase: P0
estimate: 2d
parallelizable: no
dependencies: [T-009, T-005]
files_create:
- internal/storage/postgres/store.go
- internal/storage/postgres/store_test.go
- internal/storage/postgres/queries.sql
acceptance_criteria:
- "TestStoreContract passes (using testcontainers)"
- "all queries < 5ms p99 on empty DB"
- "connection pool reuse verified"
- id: T-012
title: "Add config loading (viper)"
phase: P0
estimate: 3h
parallelizable: yes
dependencies: [T-001]
files_create:
- internal/config/config.go
- internal/config/config_test.go
- config/example.yaml
acceptance_criteria:
- "loads YAML config from path"
- "env vars override YAML"
- "validates required fields"
# ... T-013 to T-020 cover: error types, retry helpers,
# expression engine wrapper (cel-go), BPMN parser scaffolding,
# command/event base types, deterministic ID generator
Phase P1: Core engine (Weeks 1-3, ~40 tasks)¶
- id: T-021
title: "Implement Command interface and core command types"
phase: P1
estimate: 4h
parallelizable: no
dependencies: [T-020]
files_create:
- internal/engine/command/command.go
- internal/engine/command/types.go
- internal/engine/command/command_test.go
acceptance_criteria:
- "Command interface defined with Marshal/Unmarshal"
- "types: CreateInstance, CompleteJob, FailJob, SetVariable, PublishMessage"
- "MarshalBinary/UnmarshalBinary round-trip preserves all fields"
tdd_steps:
1. Write round-trip test for CreateInstance
2. Implement Command interface
3. Add other command types incrementally
- id: T-022
title: "Implement Event interface and core event types"
phase: P1
estimate: 4h
parallelizable: yes
dependencies: [T-021]
similar_to: T-021
- id: T-023
title: "Implement command log (append-only Postgres)"
phase: P1
estimate: 1d
parallelizable: no
dependencies: [T-021, T-011]
files_create:
- internal/engine/log/log.go
- internal/engine/log/log_test.go
acceptance_criteria:
- "Append returns monotonic position"
- "Read returns commands in append order"
- "concurrent Append safe"
- "ON CONFLICT DO NOTHING dedupes commands"
property_tests:
- "TestLogMonotonic: positions strictly increasing across N concurrent appends"
- "TestLogReplay: read sequence = append sequence"
- id: T-024
title: "Implement StreamProcessor skeleton (read-process-write loop)"
phase: P1
estimate: 1d
parallelizable: no
dependencies: [T-023, T-022]
files_create:
- internal/engine/processor/stream.go
- internal/engine/processor/stream_test.go
acceptance_criteria:
- "given log with N commands, processor applies all in order"
- "processor is single-threaded (no race conditions detected)"
- "processor crash mid-batch: replay produces identical state"
- id: T-025
title: "Implement deterministic ID generator"
phase: P1
estimate: 3h
parallelizable: yes
dependencies: [T-024]
files_create:
- internal/engine/idgen/idgen.go
- internal/engine/idgen/idgen_test.go
acceptance_criteria:
- "ID = (partition << 51) | position"
- "given same seed: deterministic sequence"
- "no collisions across 1M generations"
# ... T-026 to T-040 cover:
# - VariableStorage (scoped variables with snapshot)
# - Replay state machine
# - Process definition deployment + parsing
# - BPMN XML parser (basic)
# - Process cache with LRU eviction
# - Expression evaluation (CEL bridge)
# - Job activation with SKIP LOCKED
# - Job complete / fail / activate
# - Timer scheduler
# - Incident creation / resolution
# - Single-tenant scope (multi-tenant in P5)
Phase P2: BPMN M1 elements (Weeks 4-7, ~30 tasks)¶
- id: T-061
title: "StartEvent (none) processor"
phase: P2
estimate: 4h
parallelizable: yes
dependencies: [T-040]
files_create:
- internal/engine/elements/start_event.go
- internal/engine/elements/start_event_test.go
acceptance_criteria:
- "given process with NoneStartEvent, when CreateInstance, then token at start event"
- "token transitions to outgoing flow after StartEvent_COMPLETED"
test_fixtures:
- tests/fixtures/bpmn/none-start-only.bpmn
- id: T-062
title: "ServiceTask processor + job creation"
phase: P2
estimate: 1d
parallelizable: yes
dependencies: [T-061]
acceptance_criteria:
- "service task creates job with type from taskDefinition"
- "job has process_instance_key, element_id, retries"
- "input mapping copies variables to job"
- id: T-063
title: "EndEvent (none) processor"
phase: P2
estimate: 3h
parallelizable: yes
dependencies: [T-061]
acceptance_criteria:
- "EndEvent terminates branch"
- "all branches completed → process COMPLETED"
- id: T-064
title: "ExclusiveGateway (XOR) processor"
phase: P2
estimate: 6h
parallelizable: yes
dependencies: [T-062]
acceptance_criteria:
- "evaluates conditions in flow order"
- "default flow taken if no condition matches"
- "exactly one outgoing flow activated"
test_fixtures:
- tests/fixtures/bpmn/xor-gateway.bpmn
# ... T-065 to T-090 cover:
# - ParallelGateway (split + join)
# - SequenceFlow with conditions
# - UserTask processor
# - ReceiveTask processor
# - SendTask processor
# - Timer Start Event
# - Message Start Event
# - Timer Intermediate Catch Event
# - Message Intermediate Catch Event
# - Boundary Timer Event (interrupting)
# - Boundary Error Event (interrupting)
# - Boundary Message Event
# - Embedded Subprocess
# - Call Activity
# - Error End Event
# - Terminate End Event
# - Message End Event
# - Input/Output Mapping
# - Variable propagation
Phase P3: REST API (Weeks 8-10, ~25 tasks)¶
- id: T-091
title: "REST router skeleton + middleware stack"
phase: P3
estimate: 4h
parallelizable: no
dependencies: [T-040, T-008]
files_create:
- internal/api/rest/router.go
- internal/api/rest/router_test.go
- internal/api/middleware/logging.go
- internal/api/middleware/tracing.go
- internal/api/middleware/auth.go
- internal/api/middleware/ratelimit.go
acceptance_criteria:
- "/healthz returns 200"
- "/readyz reflects DB connectivity"
- "middleware order: trace -> log -> auth -> ratelimit -> handler"
- id: T-092
title: "POST /api/v1/processes (deploy)"
phase: P3
estimate: 6h
parallelizable: yes
dependencies: [T-091]
acceptance_criteria:
- "multipart upload BPMN file"
- "validates BPMN, returns 400 with details if invalid"
- "returns 201 with process key + version on success"
- "OpenAPI spec generated"
test_files:
- internal/api/rest/processes_test.go
# ... T-093 to T-115 cover all 25 endpoints
# Each endpoint: handler + test + OpenAPI annotation
Phase P4: Worker SDK Go (Weeks 11-12, ~15 tasks)¶
- id: T-121
title: "wfclient.Client basic setup"
phase: P4
estimate: 4h
parallelizable: no
dependencies: [T-115]
files_create:
- pkg/wfclient/client.go
- pkg/wfclient/client_test.go
acceptance_criteria:
- "New returns initialized client"
- "Close releases resources"
- "thread-safe"
- id: T-122
title: "wfclient.RegisterWorker + JobHandler dispatch"
phase: P4
estimate: 1d
parallelizable: no
dependencies: [T-121]
# ... T-123 to T-135 cover:
# - Streaming activation (HTTP/2 long-poll)
# - BPMNError / IncidentError helpers
# - OpenTelemetry instrumentation
# - Graceful shutdown
# - Testing utilities (wfclienttest)
Phase P5: CLI (Weeks 13-14, ~12 tasks)¶
Reference analysis/cli-tool-design. Tasks T-141 to T-152 cover:
- wf skeleton with cobra
- 12 essential commands
- Config management (~/.wf/config.yaml)
- Shell completion generation
- Output formatters (table, json, yaml)
Phase P6: Webapps (Weeks 15-20, ~30 tasks)¶
Reference analysis/operate-tasklist-mvp-detailed. Tasks T-161 to T-190 cover: - Vite + React + Tailwind scaffolding - OIDC login flow - Tasklist list view - Tasklist detail + JSON Schema form rendering - Tasklist complete action - Process Inspector basics - BPMN viewer (bpmn-js integration) - E2E tests (Playwright)
Phase P7: Hardening (Weeks 21-24, ~25 tasks)¶
- id: T-201
title: "Implement REST API idempotency-key support"
phase: P7
dependencies: [T-115]
reference: "[analysis/idempotency-and-deduplication](<../../analysis/idempotency-and-deduplication.md>)"
- id: T-202
title: "Add backpressure middleware (token bucket)"
reference: "[concepts/backpressure-rest-strategy](<../../concepts/backpressure-rest-strategy.md>)"
- id: T-203
title: "Add audit log with hash chain"
reference: "[adrs/adr-025-audit-logging-mandatory](<../../adrs/adr-025-audit-logging-mandatory.md>)"
- id: T-204
title: "Add Postgres RLS policies"
reference: "[adrs/adr-024-postgres-rls-defense-in-depth](<../../adrs/adr-024-postgres-rls-defense-in-depth.md>)"
# ... T-205 to T-225: security hardening, performance tuning,
# replay-determinism verification suite, chaos drills
Phase P8: Production (Weeks 25-26)¶
- id: T-226
title: "Helm chart with values for prod/staging/dev"
- id: T-227
title: "Disaster recovery runbook (executable)"
reference: "[analysis/disaster-recovery-runbook](<../../analysis/disaster-recovery-runbook.md>)"
- id: T-228
title: "k6 load test suite (W1-W6)"
reference: "[analysis/performance-testing-methodology](<../../analysis/performance-testing-methodology.md>)"
- id: T-229
title: "Quickstart docs (< 30 min hello world)"
- id: T-230
title: "Migration tooling from Camunda 8"
reference: "[analysis/migration-from-camunda-8](<../../analysis/migration-from-camunda-8.md>)"
9. Quality gates by phase¶
| Phase | Test coverage | SonarQube | Security | Performance |
|---|---|---|---|---|
| P0 | n/a (scaffolding) | Pass A | gosec clean | n/a |
| P1 | 95% engine | Pass A | gosec + govulncheck | unit < 1ms |
| P2 | 95% elements | Pass A | gosec + govulncheck | W1 < 1s p99 |
| P3 | 90% API | Pass A | + OWASP ZAP | < 50ms p99 |
| P4 | 90% SDK | Pass A | gosec + govulncheck | < 100ms job pickup |
| P5 | 70% CLI | Pass A | gosec | < 200ms cmd |
| P6 | 80% UI | Pass B | + Snyk | Lighthouse > 90 |
| P7 | 95% engine | Pass A | full pen test | W1 100 inst/s |
| P8 | 90% all | Pass A | full audit | M1 targets met |
10. Definition of Done¶
Una task se considera DONE solo si:
- Tests escritos PRIMERO (visible en commit history como RED commit antes de GREEN)
- Tests cubren happy + edge + error + invariant paths
-
go test -race ./<package>/...exit 0 - Coverage on new code >= threshold (§4)
-
golangci-lint runzero warnings on new code -
gosec -severity highzero findings -
govulncheckzero new vulns - SonarQube quality gate pass
- No goroutine leaks (
goleak) - No determinism violations (reproducer test passes)
- Docs: godoc on all exported types
- Code review: 1 approval (Claude Code self-review puede contar si pasa todos los gates)
- No breaking API changes (or documented in CHANGELOG)
- Integration test (if cross-component)
CI no permite merge sin todos los gates verdes.
11. Agent orchestration con Claude Code¶
Wave-based execution¶
Claude Code procesa tasks por "waves" (DAG depth):
Wave 0 (P0): T-001 → T-012 (run T-002, T-003, T-004, T-006-T-009 en paralelo)
Wave 1 (P0 + P1): T-013 to T-025
Wave 2 (P1): T-026 to T-040 (la mayoría paralelizable)
Wave 3 (P2): T-061 to T-090 (cada BPMN element es task independiente)
Wave 4 (P3): T-091 to T-115 (cada endpoint en paralelo)
Wave 5 (P4 + P5): SDK + CLI en paralelo
Wave 6 (P6): Webapps
Wave 7 (P7): Hardening (mucha paralelización)
Wave 8 (P8): Production prep
Spawn template¶
# Pseudo-código de orquestación
for wave in waves:
available_tasks = [t for t in wave if all_deps_met(t)]
parallel_agents = min(len(available_tasks), 15) # cap
for task in available_tasks[:parallel_agents]:
spawn_agent(
description=f"Implement {task.id}: {task.title}",
prompt=render_task_prompt(task, references=[
"wiki/analysis/mvp-implementation-plan-detailed.md",
*task.references,
]),
isolation="worktree", # cada task en branch propia
run_in_background=True,
)
wait_for_all_complete()
run_integration_tests() # verifica que tasks no rompieron entre sí
merge_to_main() # auto si gates verdes
Prompt template para spawnar agents¶
You are implementing task T-XXX from the MVP Implementation Plan.
## Task
{title}: {description}
## Context to read FIRST
1. wiki/analysis/mvp-implementation-plan-detailed.md (sections 4-6 mandatory)
2. {referenced ADRs}
3. {referenced concept pages}
## Acceptance criteria (TEST-DRIVEN)
{criteria}
## Files to create
{files_create}
## TDD workflow (mandatory)
1. Write tests in {test_file} for ALL acceptance criteria
2. Run tests, verify they FAIL with informative errors
3. Implement minimal code to pass tests
4. Run tests, verify GREEN
5. Run full static analysis: `make check`
6. Commit each step separately (RED, GREEN, REFACTOR)
## Quality gates
- golangci-lint zero warnings on new code
- gosec no high-severity findings
- coverage on new code >= {threshold}
- no goroutine leaks
- no determinism violations
## Stop conditions
- If a test cannot pass after 3 reasonable attempts: stop, document, report blocker
- If acceptance criteria require API change: stop, propose change in writing
- If task scope expands > 50%: stop, split into sub-tasks
## Report
- List of commits made
- Test count + coverage %
- Static analysis output
- Any deviation from spec
Failure handling¶
Si un agent falla:
- Logs guardados en
tasks/T-XXX/log.txt - Failure type classified:
BLOCKED_BY_DEP: waiting on T-YYY → re-queue cuando YYY doneBUG_IN_DEP: regresión en T-YYY → bug ticketSPEC_AMBIGUOUS: needs human → escalateTIMEOUT: split task, retryQUALITY_GATE_FAIL: agent loop con feedback- Auto-retry max 3 veces con context expandido.
Parallel safety¶
- Cada agent trabaja en su propia git worktree (
isolation: "worktree"). - Tasks que tocan el mismo archivo: forzar secuencial (DAG declaration).
- Integration test después de merge: detecta regresiones cruzadas.
12. Continuous metrics dashboard¶
Para visualizar progreso en tiempo real:
Total tasks: 230
Completed: 34 ████░░░░░░░░░░░░░░░░ 15%
In progress: 12 ░░░░░░░░░░░░░░░░░░░░
Blocked: 3
Failed: 2
Current wave: Wave 3 (P2 BPMN elements)
Velocity: ~12 tasks/week (target: 10)
ETA M1: Week 24 (vs plan 26) — ahead by 2 weeks
Quality:
Coverage: 91.3% (target 95% engine, 90% rest)
SonarQube: Pass A
Open bugs: 0
Tech debt: 2.1%
Security: 0 high, 1 medium, 3 low
Velocity by phase:
P0: ████████████████████ 100% (20/20)
P1: ███████████████░░░░░ 75% (30/40)
P2: ██░░░░░░░░░░░░░░░░░░ 10% (3/30)
...
Implementar como GitHub Project + custom GitHub Action que actualiza badge en README.
13. Riesgos identificados y mitigation¶
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Agent loops on flaky test | Med | High | Strict determinism rules §5; fail fast |
| BPMN spec ambiguity | High | Med | Human escalation; pre-define edge cases in task |
| Postgres performance bottleneck | Med | High | Load tests in P7 con baseline en P3 |
| Static analysis false positives | High | Low | Whitelist mantenido; review weekly |
| Cross-task merge conflicts | Med | Med | Worktree isolation; small tasks; integration test |
| Coverage thresholds blocking | Med | Med | Allow exceptions con justification; track via PR labels |
| Replay determinism regression | Low | Critical | T-200+ verification suite, runs on every CI |
| Scope creep agent | High | High | Stop conditions in prompt; scope <50% growth allowed |
14. Referencias cruzadas¶
- analysis/blueprint-plataforma-simplificada — spec arquitectónico
- analysis/implementation-roadmap-concrete — roadmap 26 semanas original
- analysis/bpmn-coverage-matrix — qué elementos en M1
- analysis/rest-api-design — endpoints REST detallados
- analysis/worker-sdk-go-design — SDK API
- analysis/cli-tool-design — CLI commands
- analysis/operate-tasklist-mvp-detailed — webapps spec
- analysis/process-testing-framework — testing levels
- analysis/idempotency-and-deduplication — dedup
- analysis/disaster-recovery-runbook — DR
- adrs/index — todas las ADRs
- analysis/language-choice — por qué Go
- analysis/performance-testing-methodology — load tests
Apéndice A: Comandos comunes (Makefile)¶
# Build
build: ## Build all binaries
go build -o bin/engine ./cmd/engine
go build -o bin/wf ./cmd/wf
go build -o bin/migrate ./cmd/migrate
# Test
test: ## Run unit tests
go test -race -short ./...
test-integration: ## Run integration tests (needs Docker)
go test -race -tags=integration -timeout=10m ./...
test-scenario: ## Run E2E scenario tests
go test -race -tags=scenario -timeout=20m ./tests/scenario/...
test-load: ## Run load tests
k6 run tests/load/w1-baseline.js
cover: ## Generate coverage report
go test -race -coverprofile=cover.out ./...
go tool cover -html=cover.out -o cover.html
# Quality
lint: ## Run golangci-lint
golangci-lint run
sec: ## Run security scans
gosec -severity high ./...
govulncheck ./...
check: lint test sec ## All quality checks
# Migrations
migrate-up: ## Apply migrations
migrate -database "$$DATABASE_URL" -path migrations up
migrate-down: ## Rollback last migration
migrate -database "$$DATABASE_URL" -path migrations down 1
# Dev
dev-up: ## Start dev environment
docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.dev.yml up -d
dev-down: ## Stop dev environment
docker compose -f deploy/docker-compose.yml -f deploy/docker-compose.dev.yml down
# Release
release-build: ## Build release artifacts
goreleaser build --clean
release-test: ## Test release process locally
goreleaser release --snapshot --clean
Apéndice B: Plantilla de PR¶
## Task
Implements T-XXX: <title>
## Changes
- Added `internal/engine/processor/start_instance.go`
- Added tests covering 4 acceptance criteria
## Acceptance criteria
- [x] Given valid process, when StartInstance, then instance created
- [x] Given unknown process, returns ErrProcessNotFound
- [x] Given duplicate uniqueness key, returns existing
- [x] Replay determinism: T-019 invariant test passes
## Quality gates
- [x] Tests written first (see commit history)
- [x] Coverage on new code: 96% (target 95%)
- [x] golangci-lint: 0 warnings
- [x] gosec: 0 findings
- [x] govulncheck: 0 vulns
- [x] No goroutine leaks
- [x] SonarQube: Pass A
## TDD evidence
- Commit `abc1234`: RED — added failing test
- Commit `def5678`: GREEN — minimal implementation
- Commit `ghi9012`: REFACTOR — extracted helper
## Deviations
None.
## Related
- [adrs/adr-005-stream-processing-command-sourcing](<../../adrs/adr-005-stream-processing-command-sourcing.md>)
Apéndice C: Tools cheat sheet¶
# Quality (run before commit)
golangci-lint run --fix # auto-fix what's possible
gosec ./... # security
govulncheck ./... # CVEs
go test -race -short ./... # fast feedback
# Coverage with branch coverage
gobco ./... # branch coverage
go tool cover -func=cover.out | tail -1
# Mutation testing
go-mutesting ./internal/engine/...
# Property-based
go test -tags=pbt -run TestProperties ./...
# Determinism check (replay)
go test -run TestReplayDeterminism -count=100 -race ./...
# Performance regression
go test -bench=. -benchmem -count=5 ./internal/engine/... | benchstat baseline.txt -
# Container scan
trivy image example/wf-engine:dev
syft example/wf-engine:dev -o spdx-json | grype
# SQL lint
sqlfluff lint --dialect postgres migrations/
# License audit
go-licenses check --disallowed_types=forbidden ./...
# Architecture verification
go-arch-lint check # if using go-arch-lint
Total estimado: 230 tasks, 26 semanas, 3-4 ingenieros (o equivalente agent-hours).
Output deseado al final: workflow engine drop-in replacement de Camunda 8 para feature subset M1, con quality gates verdes en todos los stages, listo para producción staging.