Imported from shftwst/faff (
plugin/skills/faffter-noon-concurrency-sequential/SKILL.md). Install upstream withnpx skills add shftwst/faff --skill faffter-noon-concurrency-sequential. Copyright stays with the author.
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 # the default — explicit for clarity
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
When standalone, Read the sibling faff/references/kernel.md (the shared kernel) first, then the lane references this executor consumes — faff/references/build.md (Worktree policy) and faff/references/l4.md (the concurrency contract, Mechanism slot, and Evaluator lane).
The concurrency slot contract is fixed in the gateway — see faff/references/l4.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 (partition + per-issue build action + run ledger) and the seven obligations — (1) build every partition issue, (2) serialise within a collision group and require a dependency blocker to have merged (else park the dependent), (3) record every terminal outcome to the run ledger using the fixed buckets, (4) never weaken the merge gate — AC + CI-green + review pass, plus the L4 per-issue code-blind holdout meets-spec, (5) bracket every graft dispatch with integrity-digest custody (snapshot, verify on return, tamper/unavailable parks the unit), (6) persist + verify the returned per-issue AC/review evidence, parking the unit on a mismatch, (7) run the merge above the cut — for each pr-ready member the dispatcher, not the lane, runs the merge locus and records the resulting bucket. 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: falseand 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 aBuildDispatchin its prompt —{ issues, run_id, run_dir, session_id, mode_signal: "autonomous", model, effort, constraints? }(an independent is a one-elementissues; a collision group is the ordered chain) — since a subagent does not inherit shell env reliably. Stamp the foreground-to-terminal clause into the prompt: "Foreground-to-terminal: run every gate/test/review step in the foreground; never end your turn without returning the TerminalToken per issue (or the sanctioned retry-later hold)." — the dispatched build's own turn contract, at its highest-salience surface. Stamp the voice clause: "House voice: read the# Writing stylesection ofAGENTS.mdat the repo root (worktree included) and apply it to all durable prose you write — specs, PR bodies, commit messages, tracker comments. File or section absent → skip this instruction." — so the PR bodies, commit messages, and park comments the build writes read in the house voice (gateway → Producer dispatch → Voice clause).constraints(optional): immediately before dispatching a unit that is being RE-dispatched after a Sentrycorrectintervention, consume against the re-baselined run-grain custody baseline (obligation 5's Class-A re-baseline sequence, which namescorrective author— the chain stays open through thecorrecthandling, so the baseline the orchestrator holds in context already folds in that trusted write): runfaff corrective check --run-dir <run_dir> --issue <issue> --manifest -, piping the context-held baseline over stdin — never a pre-authoring raw manifest (that reads the just-authored artifact as(added)and false-refuses a legitimate input) and never a fresh snapshot taken at consumption (a hollow rubber-stamp with no untrusted dispatch spanned). Branch on the returneddisposition/mandateexactly as today:disposition:"trusted"or"custody-trusted"withmandate:"narrowed"→ stamp itsconstraintsobject ({forbid_surfaces, thresholds, descope}) onto that unit'sBuildDispatch;disposition:"channel-D"or"refuse"⇒ no constraints stamped, today's dispatch byte-for-byte (refuseadditionally surfaces the custody gap in the run log — a proven or uncomputable verify, never silently treated as absence).mandate:"empty"or"indeterminate"→ park the unit, never dispatch, regardless of disposition. An ordinary (non-re-dispatch) unit skips this check entirely.modelis the per-lane build model: resolvefaff config get models.buildonce 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 everyBuildDispatch; a resolved token is passed as the Agent-toolmodelparameter, whileinherit(the default) means omit themodelparameter entirely — today's dispatch, byte-for-byte. Per-issue routing (opt-in): whenmodels.build_by_confidenceis 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'sBuildDispatch. A collision-chain is one subagent = onemodelparam, so resolve it to the model for its most-demanding member — the lowest retained confidence in the chain (mediumoutrankshigh) — 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;inheritstill omits the param. Record the resolved per-issue model on theBuildDispatch/ run log so it is never silent. Tier-keyed routing (opt-in, outranks confidence-only routing): whenmodels.build_by_tieroreffort.build_by_tieris configured, resolve both the model and the effort per issue instead —faff models build-for --tier <tier> --confidence <confidence>andfaff effort build-for --tier <tier>(the tier rides the partition entry alongside confidence, so still no new tracker read) — stamping both resolved values into that unit'sBuildDispatch. A collision-chain resolves to its most-demanding member's tier — the highest tier in the chain (complexoutranksstandardoutranksmechanical), mirroring the confidence rule's lowest-wins shape. Record both resolved values on theBuildDispatch/ run log, never silent. Either matcher absent ⇒ the confidence/scalar path above, unchanged.effortis the per-lane build effort: resolvefaff config get effort.buildonce per run at queue-assembly (fail-loud on an invalid token — exit 2 naming the legal set) and stamp it into everyBuildDispatch; a resolved level is passed as the dispatch's reasoning-effort arg whileinherit(default) omits it — byte-for-byte today. It is also the value the build subagent tags onto itsdata.effortevent. The subagent runsfaff-graftautonomously in its own context (it does its own worktree add, build, Step-7.5 gates, review, CI wait), stops at PR-ready (the merge no longer runs in-lane — obligation 7), then returns aTerminalToken{ issue, outcome, pr }(unchanged — the token stays a scheduling primitive;outcomeispr-readyon a clean build) plus, per obligation 6, anEvidenceReturnalongside it — for a chain, one token + oneEvidenceReturnper member. This executor runs the merge for eachpr-readymember (obligation-7 placement below). 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.
Explicit review target. Because each dispatched build runs in its own worktree while the harness session's ambient cwd stays at the main checkout, every dispatch also stamps: "Explicit review target: before any ad-hoc code-review fork, resolve this build's target via faff review-target --resolve --issue <issue> and pass it explicitly — never rely on an ambient-cwd default. faff review-target --guard --issue <issue> is the pre-review backstop: a non-zero exit means the ambient cwd is on the wrong branch — do not proceed with an un-targeted review." This governs only an ad-hoc review fork; the review slot's own diff computation is already cwd-independent and unchanged by this.
Integrity-digest bracket (obligation 5 — placement here). This executor opens at most one bracket at a time — the chain is the same shared one obligation 5 defines, just never overlapping here. Per unit: snapshot at the top of step 2, immediately before the dispatch (after any pre-dispatch orchestrator write such as the constraints corrective check), holding the manifest in context; then verify as the first action of step 3, immediately on return, before the token is reconciled or anything is recorded, and the step-3 ledger/event write follows through the gateway's mandatory re-baseline sequence like any other trusted-side write. Foreground-blocking does not make this executor exempt from a mid-bracket write landing unbidden: the detached sentry poller's sentry abort runs in another process on its own schedule and can write the ledger while a dispatch is still in flight, so a re-baseline is sometimes needed here too — never assume zero. On exit 0 proceed to reconcile-then-record as above and discard the manifest; on exit 1 (tamper) or exit 2 (verification unavailable) run the shared park — do not reconcile the unit's run-dir evidence. Member set, the chain, the write sequence, exit outcomes, and park mechanics are all the gateway's — see obligation 5.
Per-issue evidence persist + verify (obligation 6 — placement here). Runs as the second action of step 3, immediately after the obligation-5 verify above and before reconcile-then-record: for the unit's member, take the lane's returned EvidenceReturn (the sibling field it returned alongside the token — the token stays {issue,outcome,pr}) and run the obligation-6 point check on ac-checklist.json / review-verdict.json — presence-agreement, then raw-bytes leaf-sha256 identity for each present file. On agreement, persist the returned body to the run-dir path (the orchestrator-authored copy) and append the evidence_persisted ledger entry through the same re-baseline sequence the step-3 write already uses; on a mismatch, run the exit-1 park, unit-scoped (this executor builds one unit at a time, so there is no wave to halt regardless). The check, the EvidenceReturn shape, the raw-bytes rule, the persist, and the honest scope are all the gateway's — see obligation 6.
Merge above the cut (obligation 7 — placement here). A clean build returns pr-ready, not shipped — the merge no longer runs in the lane. Once at pass start, before the first dispatch, this executor emits the build-lane boundary (faff lane-boundary emit --run-dir <run_dir> --lane build) so the run reads dispatched and custody is required for every merge; a non-zero emit parks the pass before any dispatch. As the third action of step 3, immediately after the obligation-6 persist+verify and before recording the terminal bucket, this executor runs the merge locus for a pr-ready member against the dispatcher-persisted ac-checklist.json / review-verdict.json: assert the floor, run the ADR/PRDR-collision guard, produce the per-issue detective-custody verdict over obligation 5's held run-grain baseline and thread --custody-verdict/--custody-verdict-sha256 into the ship call (a tamper/unavailable verdict parks the unit, never merges), invoke slots.ship (--pr/--issue/--run-dir/--level; git-only --local) and route its faff-contract:delivery-outcome, and on shipped run the post-merge tail — then record the resulting shipped / pr-open / parked bucket. Because builds are strictly serial here, each merge sees main as the prior merge left it — no rebase step is needed (obligation 7's rebase note is the parallel executor's). Any other returned token records as today, no merge. The boundary emit, the custody verdict, the floor, the routing, the ship handoff, and the fixed bucket completion are all the gateway's — see obligation 7.
outcome (the recorded ledger bucket) is one of the five build buckets (shipped / superseded / pr-open / parked / errored) — a clean build's returned token is pr-ready, which this executor completes to shipped / pr-open / parked via obligation 7 (never recorded raw); superseded is graft's build-time premise-superseded close-path (deliverables already on main, issue moved Done, no PR — the superseded-done return); routed-out (queue-assembly verdict gate) and unreached-budget (budget fire) are orchestrator dispositions written to the ledger, never values a build subagent returns; nor does a build subagent ever return claimed-by-peer (resolved pre-dispatch, never admitted). shipped / pr-open imply pr != null (a superseded outcome has no PR — its evidence is <run-dir>/<issue>/supersession.json).
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 — never a hand-rolled ledger edit — so the tick's atomic replace of the dedicated heartbeat file is race-free and the in-build ticks from faff-graft's long sub-steps land on the owning run. No concurrent writers of the run ledger, no race — runcheck never sees a live build as abandoned. This preserves the owner stamp across the whole graft lifecycle.
Because only one unit runs at a time and this executor merges it (obligation 7) or records its park before the next dispatch, every later build sees main exactly as the prior merge 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: 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 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 seven 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-graftdecides its terminal state. TerminalTokenstays{ issue, outcome, pr }— no token/spend field, ever. The build subagent never measures its own token spend (its worktree cwd would mis-resolve the transcript root, and an in-subagent measure would return the whole-run cumulative, not its delta) and never writesevents.jsonl(the orchestrator is the sole writer — an unlockedseqline-count can't tolerate a second writer). Per-subagent four-class spend is captured by the orchestrator's ownissue-outcome --tokensemission on return, pinned to the run'smeasure_root+ session id — not by widening this token. This orchestrator-sole-writer posture forevents.jsonl/run-ledger.jsonis the evidence class of gateway → Run-artifact write authority;events.jsonlkeeps its prefix-preserving carve-out (Decision 5).