Saltar a contenido

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. §1-3 (briefing + stack + repo): contexto de una sola lectura. Léelo entero antes de spawnar agents.
  2. §4-7 (metodología): reglas que TODOS los agents deben respetar (TDD, determinismo, quality gates).
  3. §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 importar internal/engine/*, NO al revés.
  • internal/engine/* puede importar internal/storage/* SOLO vía interfaces.
  • pkg/wfclient/* NO importa nada de internal/*.
  • internal/storage/postgres y internal/storage/inmemory implementan misma interface storage.Store.
  • cmd/* solo importan internal/* y pkg/*.

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:

  1. Happy path: feature funciona con input válido.
  2. Edge cases: nil inputs, empty collections, max/min values.
  3. Error paths: cada error explícito tiene un test.
  4. 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é

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

func TestMain(m *testing.M) {
    goleak.VerifyTestMain(m)
}

Tests parallelos seguros

Cada test crea su propio store y resources. NO globals.

func TestSomething(t *testing.T) {
    t.Parallel()
    // ...
}

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 run zero warnings on new code
  • gosec -severity high zero findings
  • govulncheck zero 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:

  1. Logs guardados en tasks/T-XXX/log.txt
  2. Failure type classified:
  3. BLOCKED_BY_DEP: waiting on T-YYY → re-queue cuando YYY done
  4. BUG_IN_DEP: regresión en T-YYY → bug ticket
  5. SPEC_AMBIGUOUS: needs human → escalate
  6. TIMEOUT: split task, retry
  7. QUALITY_GATE_FAIL: agent loop con feedback
  8. 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


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.