Appendix A - 9. Aggregate-compute protection (`lint-all` host mutex)
The Structure of Aggregate-compute protection (lint-all host mutex) — its shape at a glance:
A whole-sweep singleton mutex caps the host to one running sweep, and an orchestrator-side in-flight count caps dispatched sweeps to one — a coarser instrument than the per-call semaphore over the pieces.
flowchart LR
Briefs[Sweep-class briefs] --> Count{One in flight?}
Count -->|already one| Wait([Hold dispatch])
Count -->|none| Run[Aggregate sweep]
Run --> Mutex{{Host mutex, cap 1}}
Mutex -->|second run| Block([Refuse / wait])
Accessible description: sweep-class briefs are capped to one in flight by an orchestrator-side count, and the aggregate sweep acquires a one-per-host mutex; a second run on the same host is refused, so the heaviest job never overlaps itself.
Projected from the catalogue entry mediators-and-resource-locks / Aggregate-compute protection (lint-all host mutex).
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — A one-per-host mutex on lint-all (the aggregate lint sweep) plus a one-in-flight declaration per orchestrator, so the single heaviest compute job cannot run twice on a machine or be triggered by many agents at once.
Motivation
lint-all is the single heaviest compute in the system; it fans out over the whole tree. Two concurrent runs, or many agents each triggering one, melt the host. The failure is host exhaustion / OOM from aggregate work that individually looks fine, and it recurs whenever more than one lint-all-class job is set in motion at once.
Applicability
- A host mutex with a hard timeout and fail-loud behavior.
- A
compute-classdeclaration on briefs so the orchestrator can count in-flight aggregate work. - Orchestrator-side in-flight accounting: the one-at-a-time rule is partly discipline, not purely mechanical.
- The role gate to stop the wrong role from triggering the sweep.
Structure
The Structure diagram appears at the top of this page.
Sample Code
A per-call semaphore over the pieces still lets two whole sweeps overlap; bounding an aggregate needs a coarser instrument — a singleton mutex over the sweep as an indivisible unit, plus an orchestrator-side in-flight count.
import fcntl, sys
def run_aggregate_sweep(lock_path: str, do_sweep, timeout_s=1800):
with open(lock_path, "w") as lock:
try:
fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) # one sweep per host, cap 1
except BlockingIOError:
sys.exit("another aggregate sweep holds the host lock — refusing to melt the machine")
return do_sweep() # indivisible: the whole tree, one at a time
def may_dispatch_sweep(in_flight_sweeps: int) -> bool:
return in_flight_sweeps == 0 # orchestrator-side: only one sweep-class brief in flight at a time
Consequences
- Serializes the heaviest gate. Only one sweep at a time is the point, but it means lint-all is a throughput chokepoint under a busy fleet.
- The in-flight rule leans on orchestrator discipline. The host mutex is mechanical; the "one-in-flight-per-orchestrator" half depends on honest
compute-classdeclaration. - Bypass and mis-declaration are holes.
ADA_TOOL_LINT_ALL_NO_MUTEX=1, or a brief that under- declares its compute-class, slips past.
Example use within DocAble
- The aggregate-lint host instance-lock (hard cap 1800s).
- The
compute-classbrief declaration + one-in-flight discipline. - The role-enforcement gate refusing
sonnet-active.
Related Patterns
- Consumer — reads the role from role-typed-dispatch: the role-enforcement gate is how it refuses the wrong caller.
- Counterpart — the brief-side
compute-classdeclaration is the soft-ish orchestrator half that the hard host mutex backstops. - See also (sibling) — test-serializer, build-serializer: the finer-grained mediators. This one rations the aggregate they cannot.