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]>
112 lines
5.8 KiB
YAML
112 lines
5.8 KiB
YAML
# Drop this in ANY Gitea repo at .gitea/workflows/adversarial-review.yml to turn
|
|
# Gadfly on. The image holds all the logic; this stub just forwards the event
|
|
# context. Advisory only — it never blocks a merge.
|
|
#
|
|
# Per-repo setup (no code changes needed):
|
|
# secret OLLAMA_CLOUD_API_KEY your Ollama Cloud key
|
|
# var OLLAMA_REVIEW_MODELS (optional) comma-separated model ids
|
|
# var GADFLY_ALLOWED_USERS (optional) who may "@gadfly review"; empty =
|
|
# any repo collaborator
|
|
# GITEA_TOKEN is provided automatically; comments post as the gitea-actions user.
|
|
|
|
name: Adversarial Review (Gadfly)
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, reopened, ready_for_review]
|
|
issue_comment:
|
|
types: [created]
|
|
workflow_dispatch:
|
|
inputs:
|
|
pr_number:
|
|
description: "PR number to review"
|
|
required: true
|
|
|
|
permissions:
|
|
contents: read
|
|
issues: write
|
|
pull-requests: write
|
|
|
|
concurrency:
|
|
group: gadfly-${{ github.event.issue.number || github.event.pull_request.number || github.event.inputs.pr_number }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
review:
|
|
# Security: only trusted users may trigger a secret-bearing run via a PR
|
|
# comment (pull_request + workflow_dispatch are already trusted). Replace the
|
|
# username(s) below with your maintainers — keep them in sync with
|
|
# GADFLY_ALLOWED_USERS (the in-container belt-and-suspenders check).
|
|
if: >-
|
|
github.event_name != 'issue_comment'
|
|
|| github.actor == 'your-username'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
steps:
|
|
- uses: docker://gitea.stevedudenhoeffer.com/steve/gadfly:v1
|
|
env:
|
|
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
OLLAMA_CLOUD_API_KEY: ${{ secrets.OLLAMA_CLOUD_API_KEY }}
|
|
OLLAMA_REVIEW_MODELS: ${{ vars.OLLAMA_REVIEW_MODELS }}
|
|
GADFLY_ALLOWED_USERS: ${{ vars.GADFLY_ALLOWED_USERS }}
|
|
# Specialist suite (optional). Empty = default suite
|
|
# (security,correctness,maintainability,performance,error-handling).
|
|
# csv to choose; "all" for everything; or define custom ones via a repo
|
|
# .gadfly.yml / GADFLY_SPECIALIST_<NAME>. See README "Specialists".
|
|
GADFLY_SPECIALISTS: ${{ vars.GADFLY_SPECIALISTS }}
|
|
# Concurrency (optional; default 1 = fully sequential per provider). The
|
|
# ONE throttle is a per-provider LENS BUDGET: the max lens passes (a lens =
|
|
# one specialist's review+recheck) in flight at once for a provider, shared
|
|
# across ALL that provider's models — every model in a lane runs at once and
|
|
# its lenses draw from the shared budget. Raise it to overlap lenses; set it
|
|
# per provider with GADFLY_PROVIDER_LENS_CONCURRENCY:
|
|
# GADFLY_PROVIDER_LENS_CONCURRENCY: "ollama-cloud=3,m1=1"
|
|
# (The old GADFLY_PROVIDER_CONCURRENCY model cap was removed and is ignored.)
|
|
# GADFLY_LENS_CONCURRENCY: ${{ vars.GADFLY_LENS_CONCURRENCY }}
|
|
# GADFLY_PROVIDER_LENS_CONCURRENCY: ${{ vars.GADFLY_PROVIDER_LENS_CONCURRENCY }}
|
|
# Live status board (optional; ON by default): one consolidated comment
|
|
# showing every model's per-lens progress as it runs. Disable with
|
|
# GADFLY_STATUS_BOARD=0; tune the refresh with GADFLY_STATUS_POLL_SECS.
|
|
# GADFLY_STATUS_BOARD: ${{ vars.GADFLY_STATUS_BOARD }}
|
|
# --- Models & providers (optional; default = Ollama Cloud) ----------
|
|
# Gadfly is majordomo-powered, so it can target other backends. Set a
|
|
# provider for bare model ids; point at a different endpoint with a
|
|
# base URL; supply a key (or the provider's standard env var). Examples:
|
|
#
|
|
# Local Ollama daemon (no key):
|
|
# GADFLY_PROVIDER: ollama
|
|
# GADFLY_MODELS: qwen2.5-coder:7b
|
|
# # GADFLY_BASE_URL: http://my-ollama-host:11434 # if not localhost
|
|
#
|
|
# OpenAI-compatible endpoint (incl. local Ollama's /v1):
|
|
# GADFLY_PROVIDER: openai
|
|
# GADFLY_BASE_URL: http://localhost:11434/v1
|
|
# GADFLY_MODELS: qwen2.5-coder:7b
|
|
#
|
|
# OpenAI / Anthropic / Google (supported via majordomo, UNTESTED — see README):
|
|
# GADFLY_PROVIDER: openai # then set OPENAI_API_KEY below
|
|
# OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
#
|
|
# Named endpoint aliases (reference as name/model in GADFLY_MODELS).
|
|
# vars/secrets aren't auto-exposed, so map each one explicitly:
|
|
# GADFLY_ENDPOINT_BIGBOX: ${{ vars.GADFLY_ENDPOINT_BIGBOX }} # "ollama|http://192.168.1.50:11434"
|
|
# GADFLY_MODELS: bigbox/qwen2.5-coder:7b
|
|
GADFLY_PROVIDER: ${{ vars.GADFLY_PROVIDER }}
|
|
GADFLY_BASE_URL: ${{ vars.GADFLY_BASE_URL }}
|
|
GADFLY_MODELS: ${{ vars.GADFLY_MODELS }}
|
|
# --- Findings telemetry (optional; OFF by default) ------------------
|
|
# Set GADFLY_FINDINGS_URL to a gadfly-reports store base URL to POST each run +
|
|
# its findings for model-quality tracking. Advisory only: failures are
|
|
# logged to stderr and never affect the review. Add a bearer token if
|
|
# the store requires auth. (GADFLY_REPO / GADFLY_PR are derived for you.)
|
|
# GADFLY_FINDINGS_URL: ${{ vars.GADFLY_FINDINGS_URL }}
|
|
# GADFLY_FINDINGS_TOKEN: ${{ secrets.GADFLY_FINDINGS_TOKEN }}
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
PR: ${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
|
|
PR_BRANCH: ${{ github.head_ref }}
|
|
IS_DRAFT: ${{ github.event.pull_request.draft }}
|
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
|
COMMENT_ID: ${{ github.event.comment.id }}
|
|
ACTOR: ${{ github.actor }}
|