Run: Tasks and Lanes

The Run pillar covers three commands: fledge run (task runner), fledge watch (file watcher), and fledge lanes (pipelines).

Running Tasks

fledge run works immediately in any project. No config needed. It detects your stack from marker files and provides sensible defaults:

fledge run test     # auto-detects Rust/Node/Go/Python/Ruby/Java/Swift
fledge run build
fledge run lint
fledge run --list   # see what's available

Auto-Detection

ProjectDetected byDefault tasks
RustCargo.tomlbuild, test, lint, fmt
Node.jspackage.jsontest, build, lint, dev (if scripts exist)
Gogo.modbuild, test, lint
Pythonpyproject.toml / setup.pytest, lint, fmt
RubyGemfiletest, lint
SwiftPackage.swiftbuild, test
Gradlebuild.gradlebuild, test
Mavenpom.xmlbuild, test

For Node.js projects, fledge also detects your package manager (npm, bun, yarn, pnpm) from lockfiles.

Config Mode

When you want full control, generate a fledge.toml:

fledge run --init

This creates a config file pre-filled with detected tasks. Once fledge.toml exists, it takes full precedence. No mixing with auto-detection. You can also override the detected language with --lang:

fledge run test --lang swift

Watching for Changes

fledge watch re-runs a task automatically when files change:

fledge watch test            # re-run tests on save
fledge watch build           # rebuild on change

It watches the project directory, ignoring .git/, target/, node_modules/, and other common build directories. The debounce interval defaults to 500ms and can be changed with --debounce <MS>.

Lanes

Lanes let you chain tasks into named pipelines. Define them in fledge.toml, run them with fledge lanes run ci. They support parallel groups and configurable failure behavior.

Quick Start

Already have tasks in fledge.toml? Generate lanes automatically:

fledge lanes init

This looks at your project type and creates sensible defaults. Then just run one:

fledge lanes run ci

Defining Lanes

Lanes go in fledge.toml alongside your tasks:

[tasks]
fmt = "cargo fmt --check"
lint = "cargo clippy -- -D warnings"
test = "cargo test"
build = "cargo build"

[lanes.ci]
description = "Full CI pipeline"
steps = ["fmt", "lint", "test", "build"]

[lanes.check]
description = "Quick quality check"
steps = [
  { parallel = ["fmt", "lint"] },
  "test"
]

Lane Options

FieldTypeDefaultWhat it does
descriptionstring(no description)Shows up when listing lanes
stepsarrayrequiredOrdered list of steps
fail_fastbooltrueStop on first failure vs. run everything and report

Step Types

You can mix these freely in a lane:

Task References

Just name a task from your [tasks] section. Dependencies (deps) get resolved automatically.

steps = ["lint", "test", "build"]

Inline Commands

One-off shell commands without cluttering your task list:

steps = [
  "test",
  { run = "cargo build --release" },
  { run = "echo 'Build complete'" },
]

Parallel Groups

Run multiple items at the same time. Everything in the group finishes before the next step starts. Items can be task references or inline commands.

steps = [
  { parallel = ["fmt", "lint"] },
  "test",
  "build"
]

Here fmt and lint run concurrently, then test, then build.

You can mix task references and inline commands in a parallel group:

steps = [
  { parallel = ["lint", { run = "echo checking..." }, "fmt"] },
  "test"
]

Step Options

Table-form steps accept four optional fields: when, timeout, retries, and retry_delay. They work on task references ({ task = "name" }), inline commands ({ run = "..." }), and parallel groups ({ parallel = [...] }).

OptionTypeDefaultWhat it does
whenstringalways runSkip the step unless an env-var condition is met. See forms below.
timeoutinteger (seconds)unlimitedPer-attempt deadline. The whole process tree is killed on exceed.
retriesinteger0Retry attempts after failure. Total attempts = retries + 1.
retry_delayinteger (seconds)1Sleep between retry attempts. Set 0 for immediate retry.
[lanes.release]
description = "Build and ship"
steps = [
  { task = "test", when = "!SKIP_TESTS" },
  { task = "build", timeout = 120 },
  { run = "scripts/publish.sh", retries = 3, retry_delay = 5 },
  { task = "deploy", when = "CI=true,DEPLOY_ENV=production", timeout = 60 },
]

Conditional steps with when

The when string supports four condition forms. Multiple comma-separated conditions are AND’d.

FormMeaning
VARRun when VAR is set and non-empty
VAR=valueRun when VAR equals value exactly
!VARRun when VAR is unset or empty
!VAR=valueRun when VAR does not equal value
VAR1,VAR2=xAND: every condition must hold

Skipped steps are visible in the output (⏭ Step N name (skipped: when 'X' not met)) and in JSON output ("skipped": true, "reason": "...").

Per-step timeouts

timeout sets a per-attempt deadline. On exceed, fledge sends SIGKILL to the process group on Unix or TerminateJobObject on Windows, so multi-statement shells (sh -c "a && b", cmd /c "a & b") don’t leak grandchildren. If the step has retries, each attempt gets a fresh deadline.

Retries with retry_delay

retries re-runs the entire step on failure. retry_delay controls the sleep between attempts (default 1s). retry_delay = 0 retries immediately — useful when you want to absorb a flake without slowing down the lane.

# Immediate-retry flake mitigation
{ run = "curl https://api.example.com/health", retries = 5, retry_delay = 0 }

Resuming with --from

fledge lanes run <name> --from <step> skips every step before the target. Useful for “I already ran lint and test, just resume from build.”

fledge lanes run ci --from build      # by step name
fledge lanes run ci --from 3          # by 1-based index

Resume is stateless — no run history is persisted. Skipped steps appear in the output (⏭ Step N (skipped by --from)) and in JSON output ("skipped": true, "reason": "--from"). Targeting a parallel-group step by name doesn’t work; use the index instead.

Failure Behavior

Default is fail_fast = true. Pipeline stops on the first failure.

[lanes.ci]
description = "Stop on first failure"
steps = ["lint", "test", "build"]

Set fail_fast = false when you want the full picture:

[lanes.audit]
description = "Run everything, report all failures"
fail_fast = false
steps = ["lint", "test", "security-check", "license-check"]

Step Timing

Every step prints its elapsed time, and the lane summary shows total time:

▶️ Lane: ci, Full CI pipeline
  ▶️ Running parallel: fmt, lint
  ✔ Step 1 done (245ms)
  ▶️ Running task: test
  ✔ Step 2 done (1.032s)
  ▶️ Running task: build
  ✔ Step 3 done (3.456s)
✅ Lane ci completed (3 steps in 4.733s)

This helps identify slow steps in your pipeline without any extra tooling.

Task Configuration

Tasks are the building blocks that lanes and fledge run execute. Define them in fledge.toml.

Short Form

[tasks]
lint = "cargo clippy"

Full Form

[tasks.build]
cmd = "cargo build --release"
description = "Build release binary"
deps = ["lint"]
env = { RUST_LOG = "info" }
dir = "crates/core"
FieldTypeWhat it does
cmdstringShell command to run
descriptionstringShows up when listing tasks
depsarrayTasks to run first (resolved recursively)
envtableEnvironment variables for this task
dirstringWorking directory (relative to project root)

Lane Examples

CI Pipeline

[lanes.ci]
description = "Full CI pipeline"
steps = [
  { parallel = ["fmt", "lint"] },
  "test",
  "build"
]

Release

[lanes.release]
description = "Build and package a release"
steps = [
  "test",
  { run = "cargo build --release" },
  { run = "strip target/release/my-app" },
  { run = "tar -czf release.tar.gz -C target/release my-app" },
]

Full Audit

[lanes.audit]
description = "All quality checks"
fail_fast = false
steps = [
  "lint",
  "test",
  { run = "cargo audit" },
  { run = "cargo deny check" },
]

Real CI with conditional deploy and flake retries

[lanes.ci]
description = "Lint, test, build, and deploy on main"
steps = [
  { parallel = ["fmt", "lint"] },
  { task = "test", timeout = 300 },
  "build",
  { task = "deploy", when = "CI=true,BRANCH=main", timeout = 120, retries = 2, retry_delay = 5 },
]

Auto-Generated Defaults

fledge lanes init detects your project type:

ProjectHow it’s detectedWhat you get
RustCargo.tomlci (fmt, lint, test, build), check (parallel fmt+lint, test)
Node.jspackage.jsonci (lint, test, build), check (parallel lint+test)
Gogo.modci (fmt, lint, test, build), check (parallel fmt+lint, test)
Pythonpyproject.tomlci (fmt, lint, test), check (parallel fmt+lint, test)

Lanes CLI

fledge lanes run ci                   # run a lane
fledge lanes run ci --dry-run         # preview the plan
fledge lanes run ci --from build      # resume from a step (by name or 1-based index)
fledge lanes run ci --json            # machine-readable per-step results
fledge lanes list                     # list lanes
fledge lanes list --json
fledge lanes init                     # generate defaults
fledge lanes search                   # find community lanes
fledge lanes search rust              # search with keyword
fledge lanes import owner/repo        # import lanes from GitHub
fledge lanes import owner/repo@v1.0.0 # pin to a version
fledge lanes publish --org MyOrg      # publish lanes to GitHub
fledge lanes create my-lanes          # scaffold a new lane repo
fledge lanes validate                 # validate lane definitions
fledge lanes validate --strict        # treat warnings as errors
fledge lanes validate --json          # machine-readable output

Community Lane Registry

Share and discover lanes via GitHub. Repos with the fledge-lane topic are discoverable through fledge lanes search.

Official Examples

CorvidLabs/fledge-lanes is the official collection of language-specific lane examples. Each subdirectory contains a fully-documented fledge.toml.

LanguageImport command
Rustfledge lanes import CorvidLabs/fledge-lanes/rust
Pythonfledge lanes import CorvidLabs/fledge-lanes/python
Node/TypeScriptfledge lanes import CorvidLabs/fledge-lanes/node-typescript
Gofledge lanes import CorvidLabs/fledge-lanes/go

Creating Lanes

Use fledge lanes create to scaffold a ready-to-publish lane repo:

fledge lanes create my-lanes

This creates a directory with a starter fledge.toml containing example tasks and lanes, a README, and a .gitignore. Edit the lanes, then validate and publish:

fledge lanes validate ./my-lanes     # check for errors
fledge lanes publish ./my-lanes      # push to GitHub (validates first)

Publishing Lanes

  1. Create a repo with a fledge.toml containing your lanes and tasks (or use fledge lanes create)
  2. Validate with fledge lanes validate (publish does this automatically)
  3. Publish with fledge lanes publish (sets the fledge-lane topic automatically)
  4. Others can find it with fledge lanes search and import it

Importing Lanes

fledge lanes import CorvidLabs/fledge-lanes

This fetches the remote repo’s fledge.toml, extracts its lanes and any required tasks, and merges them into your local fledge.toml. Existing lanes with the same name are skipped (not overwritten).

You can pin to a specific branch or tag:

fledge lanes import CorvidLabs/fledge-lanes@v1.0.0

Tips

  • Start with fledge lanes init and customize from there.
  • Use parallel groups for independent checks. Linting and formatting don’t need to wait for each other.
  • Keep fail_fast = true for CI. No point building if tests fail.
  • Use fail_fast = false for audit lanes where you want the full report.
  • Inline commands are great for one-off steps that don’t need to be named tasks.