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:

# workspace.yaml
version: '2.0'

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

This approach works with any language and toolchain.

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

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

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

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.

FlagDescription
--docs-onlyRun only docs-related gates (skip format/lint/typecheck/test)
--full-testsRun full test suites instead of scoped tests
--full-lintRun full lint pass instead of scoped lint

Commands can be strings or objects with options:

gates:
  execution:
    format: 'dotnet format --verify-no-changes'
    test:
      command: 'dotnet test --no-restore'
      timeout: 300000 # 5 minutes
      continueOnError: false
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'
gates:
  execution:
    preset: 'python'
    # Or custom:
    setup: 'pip install -e ".[dev]"'
    format: 'ruff format --check .'
    lint: 'ruff check . && mypy .'
    test: 'pytest -v'
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'
gates:
  execution:
    preset: 'go'
    # Or custom:
    format: 'test -z "$(gofmt -l .)"'
    lint: 'golangci-lint run'
    typecheck: 'go vet ./...'
    test: 'go test -v ./...'
gates:
  execution:
    preset: 'rust'
    format: 'cargo fmt --check'
    lint: 'cargo clippy -- -D warnings'
    typecheck: 'cargo check'
    test: 'cargo test'
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'
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'
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:

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 gates.execution config automatically. See GitHub Action docs for details.

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