feat(concurrency): provider-wide lens budget, drop the model cap
Concurrency was two multiplicative gates in two processes: entrypoint.sh capped MODELS-at-once per provider (GADFLY_PROVIDER_CONCURRENCY) while each model's binary separately capped its own lenses (GADFLY_LENS_CONCURRENCY). A model therefore held its whole model-slot until its LAST lens finished, stalling the next model even with idle lens capacity. Collapse to one throttle: a provider-wide lens budget shared across all of that provider's models. entrypoint now runs every model in a lane at once and seeds a single cross-process permit pool per lane (a dir of N flock files, sized by GADFLY_PROVIDER_LENS_CONCURRENCY -> GADFLY_LENS_CONCURRENCY). Each lens pass (review+recheck) acquires a permit before it runs and releases it after, so a model winding down immediately yields its freed permits to another model's queued lenses. flock auto-releases on process death, so a killed/crashed model can't leak budget. - cmd/gadfly/lenssem.go: the flock permit pool (+ lenssem_test.go). - main.go: runSpecialists holds a shared permit per lens; fanout sized to the budget so a lone model can use all of it. Falls back to the in-process limit when no pool is set (local runs, tests). - entrypoint.sh: drop provider_cap/DEFAULT_CONC; run_lane runs all models and seeds the per-lane pool. - GADFLY_PROVIDER_CONCURRENCY / GADFLY_CONCURRENCY are now ignored; the reusable workflow marks provider_concurrency deprecated and stops forwarding it. Docs (README, CLAUDE.md, examples) updated per the maintenance rule. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -57,7 +57,7 @@ Dockerfile multi-stage; private-module creds via BuildKit secrets ne
|
||||
.gitea/workflows/build-image.yml push main → :latest; tag v* → :<tag>+:latest; PR → build-only
|
||||
.gitea/workflows/review-reusable.yml reusable (workflow_call) review job; resolves swarm config at
|
||||
RUNTIME: consumer `with:` input → owner user-scope var (GADFLY_DEFAULT_MODELS /
|
||||
_SPECIALISTS / _PROVIDER_CONCURRENCY / _PROVIDER_LENS_CONCURRENCY, +
|
||||
_SPECIALISTS / _PROVIDER_LENS_CONCURRENCY, +
|
||||
GADFLY_ENDPOINT_RAGNAROS) → image default. Vars are injected per-run, so editing
|
||||
one var retunes the whole fleet even though long-lived act_runners CACHE this file
|
||||
by ref (a moved tag is NOT re-fetched — only a runtime value or a fresh @<sha>
|
||||
@@ -154,10 +154,19 @@ are actually exercised. OpenAI/Anthropic/Google come from majordomo's abstractio
|
||||
NOT re-pulled, so the job silently runs the previous image. For a run that must use a specific
|
||||
build (e.g. validating a just-pushed fix), pin the consumer stub to the immutable
|
||||
`:sha-<short>` tag the build publishes, not `:latest`.
|
||||
- **Concurrency is per-provider** (`entrypoint.sh`): each provider is a lane, lanes run in
|
||||
parallel, `cap` (from `GADFLY_PROVIDER_CONCURRENCY` else `GADFLY_CONCURRENCY`, default 1) bounds
|
||||
models-at-once within a lane. The review timeout (`GADFLY_TIMEOUT_SECS`) is **per-lens**, not
|
||||
shared across the suite — a slow model can't starve later lenses (the original timeout bug).
|
||||
- **Concurrency is one per-provider lens budget** (`entrypoint.sh` + `cmd/gadfly/lenssem.go`):
|
||||
each provider is a lane, lanes run in parallel, and within a lane ALL of the provider's models
|
||||
run at once — the only throttle is a **provider-wide lens budget** (max lens passes in flight,
|
||||
a lens = one specialist's review+recheck). The budget comes from
|
||||
`GADFLY_PROVIDER_LENS_CONCURRENCY` (`provider=N` map) else `GADFLY_LENS_CONCURRENCY` (default 1).
|
||||
Because models are separate processes, the budget is a **cross-process permit pool**: entrypoint
|
||||
seeds a per-lane dir of N flock files; each model's binary acquires one before a lens pass and
|
||||
releases it after (flock auto-drops on process death). This replaced the old two-level
|
||||
`GADFLY_PROVIDER_CONCURRENCY` MODEL cap × per-model `GADFLY_LENS_CONCURRENCY`, which multiplied
|
||||
and let a model hold its slot through its last lens — stalling the next model with idle lens
|
||||
capacity. Those two model-cap vars are now **ignored**. The review timeout
|
||||
(`GADFLY_TIMEOUT_SECS`) is **per-lens**, not shared across the suite — a slow lens can't starve
|
||||
the others (the original timeout bug).
|
||||
- **Large-PR token burn**: the agent loop re-sends the whole transcript every step, so a giant
|
||||
diff (the old `get_diff` dumped it untruncated, and it was embedded in both the review and
|
||||
recheck task) was re-transmitted ~steps × lenses × passes × models times — a ~250 K-token PR
|
||||
|
||||
Reference in New Issue
Block a user