Skip to content

Gates

Gates are automated quality checks that must pass before a WU can be completed. They replace manual code review with consistent, automated enforcement.

Traditional review:

  • Human bottleneck (waiting for reviewers)
  • Inconsistent (different reviewers, different standards)
  • Slow feedback (review happens after code is written)

Gates:

  • Instant (run automatically)
  • Consistent (same checks every time)
  • Fast feedback (run locally before pushing)

Define your gate commands in workspace.yaml under software_delivery.gates:

# workspace.yaml
version: '2.0'

software_delivery:
  gates:
    execution:
      setup: 'pnpm install'
      format: 'pnpm format:check'
      lint: 'pnpm lint'
      typecheck: 'pnpm typecheck'
      test: 'pnpm test'

Projects with manual database deploy steps can add an explicit migration-state verifier:

software_delivery:
  gates:
    commands:
      migration_verify: 'pnpm db:preflight'

When migration_verify is configured, pnpm gates and pnpm wu:prep run it only when the working diff touches schema or migration paths such as db/schema/**, prisma/schema.prisma, supabase/schema.sql, or migration directories.

Use this for commands that check whether the target database is up to date. Do not use it to run migrations automatically.

This approach works with any language and toolchain.

wu:prep can enforce an extra proof step: if a WU changes code, it must also touch at least one automated test file in the same diff. This policy is configured under software_delivery.gates.tdd_diff_evidence.

software_delivery:
  gates:
    tdd_diff_evidence:
      mode: block
      applies_to_types:
        - feature
        - bug
      exempt_paths:
        - '.github/workflows/**'
        - '**/*.yml'
        - '**/*.yaml'

Defaults come from software_delivery.methodology.testing:

  • tdd sets software_delivery.gates.tdd_diff_evidence.mode: block
  • test-after sets software_delivery.gates.tdd_diff_evidence.mode: off
  • none sets software_delivery.gates.tdd_diff_evidence.mode: off
  • applies_to_types defaults to feature and bug
  • exempt_paths defaults to []
  • test_file_patterns defaults to TS/JS globs (**/*.test.{ts,tsx,js,jsx,mjs}, **/*.spec.{ts,tsx,js,jsx,mjs}, **/__tests__/**, **/*.test-utils.*, **/*.mock.*)
  • code_file_extensions defaults to TS/JS extensions (.ts, .tsx, .js, .jsx, .cjs, .mts, .cts)

mode supports block, warn, and off. wu:prep only blocks when the mode is block; teams that want the policy disabled can set warn or off and still document their intent in config.

Configuring for non-TS/JS toolchains (WU-2905)

Section titled “Configuring for non-TS/JS toolchains (WU-2905)”

test_file_patterns and code_file_extensions make the gate language-agnostic. Override either field to teach the gate which files count as tests vs production code in your workspace. Overrides replace the defaults — supply only your toolchain’s patterns.

C# (xUnit / NUnit / MSTest):

software_delivery:
  gates:
    tdd_diff_evidence:
      test_file_patterns:
        - '**/*Tests.cs'
        - '**/*.Tests.cs'
        - '**/Test*.cs'
      code_file_extensions:
        - '.cs'

Python (pytest / unittest):

software_delivery:
  gates:
    tdd_diff_evidence:
      test_file_patterns:
        - '**/test_*.py'
        - '**/*_test.py'
        - '**/tests/**/*.py'
      code_file_extensions:
        - '.py'

Go:

software_delivery:
  gates:
    tdd_diff_evidence:
      test_file_patterns:
        - '**/*_test.go'
      code_file_extensions:
        - '.go'

Use this gate when you want changed-test evidence for specific WU types or runtime paths. It is not a requirement to force every team into test-first development.

For one-off exceptions, document the reason in the WU notes with:

tdd-exception: <reason>

The Software Delivery pack also exposes a native delivery_review gate for completion review. This capability is public and vendor-agnostic:

  • It lives under software_delivery.gates.delivery_review
  • It is activated per runtime under software_delivery.agents.clients.<client>.features.delivery_review
  • It runs through native gate execution and wu:prep, not through a vendor-specific skill path
  • It does not depend on lumenflow-cloud or any hosted control plane
software_delivery:
  gates:
    delivery_review:
      enabled: true
      skip_types:
        - documentation
        - process

  agents:
    defaultClient: codex-cli
    clients:
      claude-code:
        features:
          delivery_review:
            enabled: true
            auto_run: true
      codex-cli:
        features:
          delivery_review:
            enabled: true
            auto_run: false
      cursor:
        features:
          delivery_review:
            enabled: false

gates.delivery_review controls whether the capability exists for the workspace. Each client feature block controls whether that runtime opts in and whether wu:prep should auto-run it.

pnpm gates runs delivery_review whenever both the workspace gate and the active client feature are enabled. pnpm wu:prep runs it only when the same config is enabled and the active client has auto_run: true.

delivery_review produces a stable JSON artifact at .lumenflow/artifacts/delivery-review/<WU-ID>.json. Hosts and products can consume the result without assuming a specific vendor runtime.

type DeliveryReviewVerdict = 'PASS' | 'FAIL' | 'PARTIAL';

interface DeliveryReviewResult {
  wuId: string;
  verdict: DeliveryReviewVerdict;
  summary: string;
  findings: Array<{
    severity: 'critical' | 'high' | 'medium' | 'low';
    title: string;
    detail: string;
    acceptanceCriteriaRefs?: string[];
    fileRefs?: string[];
  }>;
  acceptanceCriteria: Array<{
    criterion: string;
    status: 'satisfied' | 'unclear' | 'not_satisfied';
    evidence?: string[];
  }>;
  metadata: {
    runtimeClient?: string;
    startedAt: string;
    completedAt: string;
  };
}

Verdict behavior:

  • PASS means the review found sufficient delivery evidence
  • PARTIAL means the review completed with non-blocking uncertainty or lower-severity findings
  • FAIL means the review found blocking gaps or risks and gates fail

The native review inspects the current WU spec, changed files, acceptance criteria, and delivery risks. It is intentionally separate from wu:verify, which keeps its existing lifecycle meaning.

Define pattern-triggered commands alongside your standard gates:

software_delivery:
  gates:
    execution:
      format: 'pnpm format:check'
      lint: 'pnpm lint'
      typecheck: 'pnpm typecheck'
      test: 'pnpm test'

    conditional_commands:
      - trigger_patterns:
          - 'supabase/migrations/**'
          - 'supabase/schema.sql'
        command: 'pnpm db:verify'
        severity: error
        guidance: 'Apply pending migrations locally before verifying database state.'
        guidance_ref: 'docs/db-verification-guide.md'

      - trigger_patterns:
          - 'prisma/migrations/**'
          - 'prisma/schema.prisma'
        command: 'npm run prisma:validate'
        severity: warn
FieldTypeRequiredDescription
trigger_patternsstring[]YesGlob patterns matched against changed files
commandstringYesShell command to execute when patterns match
severitystringNoerror (default, blocks gates), warn, or off (skip)
guidancestringNoActionable text shown when the command fails
guidance_refstringNoFile path whose content is appended to guidance

How it works:

  1. When pnpm gates or wu:prep runs, changed files are compared against each command’s trigger_patterns using glob matching
  2. Only commands with matching patterns execute — unmatched commands are silently skipped
  3. If a matching command fails with severity error, gates fail. With severity warn, a warning is logged but gates continue

Registering via the CLI (Constraint-9 compatible):

workspace.yaml must not be edited by hand. Two sanctioned paths exist:

  1. gate:conditional (recommended, per-rule) — mirrors gate:co-change:

    pnpm gate:conditional --add --name db-verify \
      --trigger "supabase/migrations/**" \
      --trigger "supabase/schema.sql" \
      --command "pnpm db:verify" \
      --severity error \
      --guidance "Apply pending migrations locally before verifying database state."
    
    pnpm gate:conditional --list          # human-readable
    pnpm gate:conditional --list --json   # machine-readable
    
    pnpm gate:conditional --edit --name db-verify --severity warn
    pnpm gate:conditional --remove --name db-verify

    The name field is how --remove/--edit address a specific entry. It is optional in the underlying schema (existing unnamed entries continue to work) but required for CLI-managed entries.

  2. config:set --json-value (escape hatch) — writes the whole array verbatim when you need a shape the flags above don’t cover:

    pnpm config:set --key software_delivery.gates.conditional_commands \
      --json-value '[{"name":"db-verify","trigger_patterns":["supabase/migrations/**"],"command":"pnpm db:verify","severity":"error"}]'

Both paths validate against ConditionalCommandConfigSchema and commit atomically via micro-worktree.

For common languages, use a preset to get sensible defaults:

software_delivery:
  gates:
    execution:
      preset: 'python'
      # Override specific commands
      lint: 'mypy . && ruff check .'

Available presets: node, python, go, rust, dotnet, java, ruby, php

Path-scoped tests.unit execution is preset-aware. When the active preset supports scoped execution, wu:prep can use the current WU’s tests.unit entries to narrow the test gate. When a preset does not support path-scoped execution, such as dotnet, LumenFlow falls back to the configured default test command for that preset instead of attempting a JavaScript-specific runner.

PresetFormatLintTypecheckTest
nodeprettier --check .eslint .tsc --noEmitnpm test
pythonruff format --check .ruff check .mypy .pytest
gogofmt -l .golangci-lintgo vet ./...go test
rustcargo fmt --checkcargo clippycargo checkcargo test
dotnetdotnet format --verifydotnet build-dotnet test
javaspotless:checkcheckstylemvn compilemvn test
rubyrubocoprubocop-rspec
phpphp-cs-fixerphpstan-phpunit
# Run all gates
pnpm gates

# Output
> Format check... pass
> Lint check... pass
> Type check... pass
> Test suite... pass
> All gates passed!

If any gate fails, wu:prep fails and you fix issues in the worktree before completion.

For migration verification failures, the expected fix is:

  1. Apply the pending migrations using your project’s normal process
  2. Re-run the configured verification command manually if needed
  3. Re-run pnpm wu:prep --id WU-XXX
FlagDescription
--docs-onlyRun only docs-related gates (skip format/lint/typecheck/test)
--full-testsForce the default incremental/full test flow instead of scoped tests.unit execution
--full-lintRun full lint pass instead of scoped lint

Commands can be strings or objects with options:

software_delivery:
  gates:
    execution:
      format: 'dotnet format --verify-no-changes'
      test:
        command: 'dotnet test --no-restore'
        timeout: 300000 # 5 minutes
        continueOnError: false
software_delivery:
  gates:
    execution:
      preset: 'node'
      # Or custom:
      setup: 'pnpm install --frozen-lockfile'
      format: 'pnpm prettier --check .'
      lint: 'pnpm eslint . --max-warnings 0'
      typecheck: 'pnpm tsc --noEmit'
      test: 'pnpm vitest run'
software_delivery:
  gates:
    execution:
      preset: 'python'
      # Or custom:
      setup: 'pip install -e ".[dev]"'
      format: 'ruff format --check .'
      lint: 'ruff check . && mypy .'
      test: 'pytest -v'
software_delivery:
  gates:
    execution:
      preset: 'dotnet'
      # Or custom:
      setup: 'dotnet restore'
      format: 'dotnet format --verify-no-changes'
      lint: 'dotnet build --no-restore -warnaserror'
      test: 'dotnet test --no-restore'
software_delivery:
  gates:
    execution:
      preset: 'go'
      # Or custom:
      format: 'test -z "$(gofmt -l .)"'
      lint: 'golangci-lint run'
      typecheck: 'go vet ./...'
      test: 'go test -v ./...'
software_delivery:
  gates:
    execution:
      preset: 'rust'
      format: 'cargo fmt --check'
      lint: 'cargo clippy -- -D warnings'
      typecheck: 'cargo check'
      test: 'cargo test'
software_delivery:
  gates:
    execution:
      preset: 'java'
      # Or custom (Maven):
      format: 'mvn spotless:check'
      lint: 'mvn checkstyle:check'
      typecheck: 'mvn compile -DskipTests'
      test: 'mvn test'
      # Or Gradle:
      # format: './gradlew spotlessCheck'
      # lint: './gradlew checkstyleMain'
      # typecheck: './gradlew compileJava'
      # test: './gradlew test'
software_delivery:
  gates:
    execution:
      preset: 'ruby'
      # Or custom:
      setup: 'bundle install'
      format: 'bundle exec rubocop --format simple --fail-level W'
      lint: 'bundle exec rubocop'
      test: 'bundle exec rspec'
software_delivery:
  gates:
    execution:
      preset: 'php'
      # Or custom:
      setup: 'composer install'
      format: 'vendor/bin/php-cs-fixer fix --dry-run --diff'
      lint: 'vendor/bin/phpstan analyse'
      test: 'vendor/bin/phpunit'

Each gate maps to a policy rule in the Software Delivery Pack:

GatePolicy IDTrigger
Format checksoftware-delivery.gate.formaton_completion
Lintsoftware-delivery.gate.linton_completion
Type checksoftware-delivery.gate.typecheckon_completion
Testsoftware-delivery.gate.teston_completion

When wu:prep or wu:done runs, the kernel evaluates these policies. A deny from any gate makes the completion decision final — the deny-wins invariant applies. The result is recorded in the evidence store for audit.

When you wu:claim:

  • Worktree is created
  • Gates status is “pending”

Run pnpm gates frequently:

# Quick feedback loop
pnpm gates
# Fix issues
pnpm gates
# All green? Continue

When you wu:prep:

  1. Gates run automatically in the worktree
  2. If any fail, the WU stays in_progress until you fix and rerun wu:prep

When you wu:done:

  1. The WU merges to main
  2. The stamp is created
  3. The worktree is cleaned up
pnpm wu:prep --id WU-042

> Running gates...
> Format check... FAILED
>   src/utils/validation.ts - needs formatting
>
> Fix the issues above before completing.

Fix and retry:

pnpm prettier --write src/utils/validation.ts
pnpm wu:prep --id WU-042
# Gates pass, then complete from main:
cd /path/to/main && pnpm wu:done --id WU-042

Skip specific gates when needed:

pnpm gates --skip-test

Or in configuration:

software_delivery:
  gates:
    execution:
      format: 'pnpm format:check'
      lint: 'pnpm lint'
      # No typecheck or test for this project

Use the LumenFlow Gates GitHub Action:

# .github/workflows/gates.yml
name: Gates
on: [pull_request]
jobs:
  gates:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hellmai/lumenflow-gates@v1
        with:
          token: ${{ secrets.LUMENFLOW_TOKEN }}

The action reads your software_delivery.gates.execution config automatically. See GitHub Action docs for details.

If no software_delivery.gates.execution config is present, LumenFlow falls back to auto-detecting your project type based on files present and uses preset defaults.