SPEC-067-A: Demand-Driven Sourcing

Implements: ADR-067 · Status: Living · Updated: 2026-06-17

One deliverable: a new opt-in --if-pool-empty flag on swarm/sourcing.sh (SPEC-062-A) that makes the sourcing runner demand-driven — it sources only when there are no problems left to solve, and otherwise no-ops. No new files; this extends the existing runner and its hermetic self-test. This spec is the contract for the flag’s CLI, behaviour, and quality bar; everything not restated here is unchanged from SPEC-062-A.

1. CLI surface (delta from SPEC-062-A §1)

./swarm/sourcing.sh --if-pool-empty [--cycles <N>] [--theme <name>] [--max-goals <N>] [--dry-run]
Flag Meaning
--if-pool-empty Demand-driven gate. Before each cycle, source only if the prove pool is empty (zero goals/<slug>.aisp carry status≜open); otherwise stop the loop with exit 0 — no Claude call, no PR. Default off (every existing invocation is unchanged). Composes with --cycles, --theme, --max-goals, --dry-run.

2. “Problems to solve” — open_goal_count

open_goal_count <goals-dir>   # default: goals

Prints the count of goal records that still represent unsolved work: every <goals-dir>/*.aisp whose text contains a status≜open field line. Properties:

3. Gate placement and loop behaviour

The gate lives at the top of the cycle loop in main, after the preflight (SPEC-062-A §4) has synced main to origin/main:

for (( i = 1; i <= cycles; i++ )); do
  if [ "$IF_POOL_EMPTY" -eq 1 ]; then
    open_n="$(open_goal_count goals)"
    if [ "$open_n" -gt 0 ]; then
      log "--if-pool-empty: $open_n open goal(s) still to solve — skipping sourcing (no PR)"
      break                       # exit 0 (overall stays 0); nothing-to-do
    fi
    log "--if-pool-empty: prove pool empty — sourcing to replenish the backlog"
  fi
  ...run_cycle...
done

4. Interaction with other flags

Combination Behaviour
--if-pool-empty alone One cycle: source iff the pool is empty.
--if-pool-empty --cycles N Up to N cycles, each gated; stops early once the pool is non-empty.
--if-pool-empty --dry-run Gate evaluated against the local goals/; if empty, prints the assembled prompt (no call/PR); if non-empty, logs the skip and exits 0 without printing a prompt.
--if-pool-empty --theme/--max-goals Unchanged; these parameterise the cycle that runs when the gate opens.

5. Exit codes (unchanged from SPEC-062-A §5)

0 ok / nothing-to-do (incl. the gated no-op) · 1 cycle fail · 2 config · 3 infra. supervise.sh’s next_action policy wraps sourcing.sh unchanged.

6. Quality bar (SPEC-007-A, enforced by agent-lint.yml)

No workflow change — sourcing.sh is already in agent-lint. The bar is the same and must stay green:

7. Out of scope (deferred)