| name | faffter-noon-concurrency-sequential |
| description | Default `concurrency` executor — runs faff-beep-boop's build pass sequentially, one faff-graft at a time: no worktree contention, no merge races. The safe zero-config default. Runs as a configured slot, not the user `/` menu. |
| user-invocable | false |
| judgement_seam | none |
faffter-noon-concurrency-sequential
The default executor for the concurrency slot. Runs /faff-beep-boop's build pass sequentially — one /faff-graft at a time — over the partition that conflict analysis produced. The safe, zero-contention default: no worktree juggling, no merge races, no concurrency cap to tune. Swap to faffter-dark-concurrency-parallel when you want speed and your project can absorb concurrent worktrees.
slots:
concurrency: faffter-noon-concurrency-sequential
When it runs
Invoked by /faff-beep-boop's build pass (full pipeline step 6, and the explicit-list build loop) as the configured concurrency skill — the default when the slot is unset. It is a mechanism slot (no paired adaptor): it executes the build pass, it does not produce or translate anything.
The slot contract
The concurrency slot contract is fixed in the gateway — see the sibling faff/SKILL.md → Mechanism slot (concurrency) → The concurrency slot contract. It is the authoritative definition for every occupant (this default and any third-party executor): the input (the conflict-analysis partition + per-issue build action + run ledger) and the four obligations — (1) build every partition issue, (2) serialise within a collision group and require a dependency blocker to have merged (park the dependent otherwise), (3) record every terminal outcome to the run ledger using the fixed buckets (mapping pr-open-for-human → pr-open), (4) never weaken the merge gate — AC + CI-green + review pass, plus, under the L4 lights-out signal, the per-issue code-blind holdout meets-spec (the FAFF-311 fourth floor condition every merging lane must assert). This skill refers back to that contract; the recap here is non-normative and the gateway wins on any conflict. What follows is only how this default discharges it.
How the default runs it
Strictly one issue at a time, no worktree concurrency:
- Order the work: independents first (in the order beep-boop supplied — already priority → chainable-unlock-value, reframed by any methodology), then each collision group as a contiguous block (members in listed order).
- For each unit in that flattened order — an independent issue, or a whole collision group as one serial chain — dispatch a build subagent (Agent/Task tool) with
run_in_background: false and block awaiting its terminal token before starting the next. The foreground pin is load-bearing, not decorative: background is the tool default, so an omitted posture ends the orchestrator's turn instead of blocking — an idle unattended parent then gets reaped, killing the in-flight build (gateway → Producer dispatch for the background-by-default why). Pass it a BuildDispatch in its prompt — { issues, run_id, run_dir, session_id, mode_signal: "autonomous", model, effort } (an independent is a one-element issues; a collision group is the ordered chain) — since a subagent does not inherit shell env reliably. model is the per-lane build model (FAFF-315): resolve faff config get models.build once per run at queue-assembly time (fail-loud on an invalid token — the CLI exits 2 naming the legal set; never dispatch on a silent fallback) and stamp the value into every BuildDispatch; a resolved token is passed as the Agent-tool model parameter, while inherit (the default) means omit the model parameter entirely — today's dispatch, byte-for-byte. Per-issue routing (FAFF-334, opt-in): when models.build_by_confidence is configured, resolve the build model per issue at dispatch instead — for each unit, faff models build-for <that issue's retained confidence> (the confidence is carried on the partition entry the orchestrator annotates at assembly, so no new tracker read) — and stamp the per-issue token into that unit's BuildDispatch. A collision-chain is one subagent = one model param, so resolve it to the model for its most-demanding member — the lowest retained confidence in the chain (medium outranks high) — so no chained member is under-served by a cheaper model chosen for an easier sibling. Matcher absent ⇒ the once-per-run scalar resolution above, byte-for-byte; inherit still omits the param. Record the resolved per-issue model on the BuildDispatch / run log so it is never silent. effort is the per-lane build effort (FAFF-416): resolve faff config get effort.build once per run at queue-assembly (fail-loud on an invalid token — exit 2 naming the legal set) and stamp it into every BuildDispatch; a resolved level is passed as the dispatch's reasoning-effort arg while inherit (default) omits it — byte-for-byte today. It is also the value the build subagent tags onto its FAFF-415 data.effort event. The subagent runs faff-graft autonomously in its own context (it does its own worktree add, build, Step-7.5 gates, review, CI wait, merge), then returns only a TerminalToken{ issue, outcome, pr } — for a chain, one token per member. That subagent context is discarded on return; the orchestrator re-absorbs only the token + on-disk artifacts, never the build's working set. Never run the build inline via the Skill tool — inline dispatch accretes the build's working set into the orchestrator, the very thing isolation exists to prevent.
- Reconcile, then record. Read the unit's
.faff/runs/<run-id>/ISSUE-XX/ artifacts + git ground truth (PR / CI / merge state) and write the terminal outcome to the run ledger yourself — the token is the subagent's claim; disk + git are the truth on any disagreement (per "verify subagent claims against ground truth"). Never parse the subagent's free-text transcript for the outcome. A subagent that returns no token triggers the same ground-truth reconciliation, not a blind park.
- When the list is exhausted, return to beep-boop.
outcome is one of the existing six ledger buckets (shipped / pr-open / parked / errored / routed-out / unreached-budget) — a build subagent never returns claimed-by-peer (resolved pre-dispatch, never admitted). shipped / pr-open imply pr != null.
Heartbeat ownership follows the single active writer. Because the orchestrator blocks awaiting the subagent, exactly one of them is active at any instant: between dispatches the orchestrator refreshes the heartbeat; during a dispatched build the subagent does (it has run_dir from the BuildDispatch). Both refresh through the single sanctioned faff heartbeat "$run_dir" write path (FAFF-234) — never a hand-rolled ledger edit — so the field-merge is atomic and the in-build ticks from faff-graft's long sub-steps land on the owning ledger. No concurrent writers, no race — runcheck never sees a live build as abandoned. This preserves the FAFF-205 owner stamp across the whole graft lifecycle.
Because only one build runs at a time and each subagent merges (or parks) before the next starts, every later build sees main exactly as the prior build left it — there is no merge race to manage and no rebase step needed. The one case to handle: a dependent whose in-group blocker terminated unmerged is parked, not built (obligation 2) — it can't build on a main that's missing its dependency. Throughput is the cost; safety and simplicity are the payoff.
Resume-from-ledger. An admitted issue with no outcomes entry (compaction mid-build, or a stall mid-review) is re-dispatched as a fresh build subagent; graft re-attaches idempotently to its existing worktree/branch/PR. Nothing build-specific is ever held in orchestrator context, so resumability is unchanged. The re-attach is CHEAP (FAFF-329): the re-dispatched graft reads the per-issue review-progress checkpoint and resumes the review at the right phase — skipping a completed Phase-1 (diff-identity-guarded) and a completed Phase-2 — instead of re-running the slow adversarial second-opinion from scratch. All review compute still runs inside the (re-dispatched) subagent — the orchestrator never invokes review-call.mjs nor runs a review phase itself (the FAFF-201 isolation the checkpoint preserves, not breaks).
Rules
- This is the minimum executor. A richer occupant (
faffter-dark-concurrency-parallel) may run independents concurrently — but must still honour the four slot-contract obligations above.
- No WIP cap applies to autonomous runs (the WIP cap is a human-flow concept — see the methodology slot). Sequential execution is a safety/simplicity choice, not a throttle.
- It never parks an issue for being "later in the queue" — sequencing is not deferral. Each issue runs;
/faff-graft decides its terminal state.