Skip to content

Capacity + integrity

LumenFlow treats capacity and integrity as first-class orchestration state — not as something every agent has to re-derive from git status. This page covers the knobs you set and the states the reconciler emits.

By default, LumenFlow will launch every eligible WU it finds — subject to each lane’s configured WIP limit per wave. Every wave builder (orchestrate:initiative planning, the checkpoint wave and its reconciliation) resolves a lane the way wu:claim does: a lane that sets neither wip_limit nor lock_policy (or sets lock_policy: none) is not capped, and a lane with wip_limit: N launches at most N WUs at once, minus the WUs already holding its lock (in_progress, plus blocked under lock_policy: all). The lane-fit preflight flags a lane only when a wave holds more WUs than its wip_limit. When you want to limit concurrency (context budget, API limits, reviewer bandwidth), cap the orchestrator:

# Cap concurrent active workers at 2 for one invocation.
pnpm orchestrate:initiative -i <initiative-id> -c --max-active-workers 2

You can make the cap permanent in workspace.yaml:

software_delivery:
  orchestration:
    max_active_workers: 2

The CLI flag wins over the config when both are present. A value of 0 means “pause launches entirely” — existing work keeps running; nothing new gets handed off.

  • WUs that would otherwise be eligible receive launchBlockedBy: ['capacity'].
  • The next_safe_actions list in status.json surfaces a wait action with "Queued until worker capacity frees (remaining capacity: N)."
  • When remaining capacity is 0 and there are queued WUs, the reconciler emits an aggregate wait action naming everything queued.

Main-checkout contamination is when a delegated WU’s declared code_paths overlap with files that are dirty in the root checkout (not in the WU’s worktree). That is a strong signal something has been edited outside the worktree boundary — the single most common source of lost work.

LumenFlow runs git status --porcelain=v1 in the project root and intersects the dirty set against each WU’s code_paths on every orchestrate:initiative -c and orchestrate:init-status call.

If any overlap is found:

  • The WU’s orchestration_state becomes contaminated.
  • status.json sets blocked_by_integrity: true.
  • The reconciler’s first next_safe_action is recover_wu for the contaminated WU.
  • buildCheckpointWave refuses to advance further — even the eligible waves that are logically independent get queued behind recover_wu.

A code_path that ends in / is treated as a directory. Any dirty file under that directory counts as contamination of that code_path:

# Work unit file
code_paths:
  - app/components/ # directory
  - app/config.ts # single file

A stalled worktree is one that is still on disk but has not produced any activity (checkpoint, signal, or claim update) in a long time. The default threshold is 4 hours; override via workspace.yaml:

software_delivery:
  orchestration:
    stall_threshold_hours: 8 # allow longer-running worktrees

A WU with no worktree on disk is never stalled — that is the needs_relaunch branch instead.

The detector looks at (in order, taking the most recent):

  1. The latest shared-memory checkpoint for the WU.
  2. The latest signal broadcast for the WU.
  3. The WU’s claimed_at timestamp from its YAML spec.

If all three are older than the threshold and a worktree still exists, the WU transitions to stalled with a recover_wu action. A human or agent can then inspect the worktree, merge in-flight work, or block the WU with a concrete reason.

A WU’s blocked_by and dependencies fields both name predecessors that must complete first — they are a union, not a precedence order: either field naming a predecessor is enough to block. LumenFlow evaluates this through one canonical graph adapter shared by orchestrate:initiative dry-run planning, orchestrate:init-status, and orchestrate:monitor’s execution summary, so all three surfaces agree on which WUs are actually launchable.

A WU is eligible only when every predecessor named by its blocked_by/ dependencies union is done. Otherwise:

  • A predecessor that exists but is not yet done reports blocked, naming the predecessor(s) still outstanding.
  • A predecessor id that does not resolve to any known WU fails closed: the WU reports blocked with a missing-reference diagnostic rather than defaulting to eligible.
  • A blocked_by/dependencies cycle also fails closed, reporting blocked with a cycle diagnostic.

Real activity evidence (an active worktree, in_progress status, or delegation pickup/completion) always wins over the dependency graph — work that has genuinely started is never retroactively reclassified as blocked.

Multi-hop chains resolve without a separate traversal: each WU only checks its own direct predecessors, but a predecessor that is itself blocked is (by definition) not yet done, so every WU downstream of a blocked predecessor also reports blocked — and the whole chain becomes eligible wave-by-wave as each predecessor completes, in step with lane WIP/lock policy.

# With one work unit blocked and several others blocked_by that chain:
pnpm orchestrate:init-status --initiative <initiative-id>
pnpm orchestrate:initiative --initiative <initiative-id> --dry-run
# Both report the downstream work units as deferred/blocked -- neither
# surfaces them as an eligible launch action.

orchestrate:monitor builds one fresh evidence snapshot for its execution summaries. Those summaries share the same memory, signal, delegation, stamp, and worktree observations instead of reloading them for each initiative. Watch mode builds a new snapshot on the next poll, so it continues to observe new filesystem and Git state without keeping a process-wide cache.

With --wu <wu-id>, the monitor keeps that WU’s complete initiative context for dependency checks but limits worktree status and activity reads to the selected WU. Without --wu, worktree scanning remains workspace-wide.

Informational signal text does not by itself block execution, even if it mentions a failed gate or timeout. Signal-based blockers require an explicitly blocking protocol type (delegation_failure today). A WU’s canonical blocked status, unresolved dependencies, and integrity findings continue to block independently.

With both detectors wired in, reconciliation stops advancing whenever integrity is suspect, even if there is free capacity and dependencies are clear:

$ pnpm orchestrate:initiative -i <initiative-id> -c

[orchestrate:initiative] Loaded 7 WU(s)
Progress: [██████████████░░░░░░] 70%
  Done: 4/7
  Active: 1
  Pending: 0
  Blocked: 2   # contaminated + stalled

Wave -1 manifest: null
Available capacity: 2

Next Safe Actions:
  - recover_wu <wu-id>: Recover contaminated work before advancing.
  - recover_wu <wu-id>: Recover or inspect stalled delegated work.

Unsafe to advance while orchestration integrity issues remain.

The orchestrator will keep refusing to launch new work until those two recover_wu actions are resolved.

  • State machine — where contaminated, stalled, needs_relaunch, and queued_by_capacity sit in the vocabulary.
  • Lifecycle — the reconciliation phase that fires these detectors.
  • Control-plane SDK — the shared types that record capacity + integrity state.