From 79da1bfde3da20047de945c41d3106fe8280db91 Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Sat, 27 Jun 2026 22:05:31 -0400 Subject: [PATCH] feat(reusable): ship the curated swarm as the default config consumers inherit Make the reusable workflow's input defaults BE the standard Gadfly swarm so a consumer subscribes by just calling it (no `with:` block) and inherits: - models: 3 strong cloud (minimax-m3, glm-5.2, deepseek-v4-pro) + Claude Code (sonnet, opus, opus:max) - specialists: the 5-lens default suite (security, correctness, maintainability, performance, error-handling) - provider_concurrency: ollama-cloud=3,claude-code=3 (all three claudes at once) - timeout_minutes default 45 -> 90 (5 lenses x 2 passes over a slow lane) The default is opinionated (needs OLLAMA_CLOUD_API_KEY + CLAUDE_CODE_OAUTH_TOKEN); consumers override `models:` for cloud-only / other providers. gadfly's own caller is slimmed to inherit (only allowed_users remains). examples/reusable.yml keeps a cloud-only `models:` override so a public copy works with just the Ollama key. README/CLAUDE.md updated. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/adversarial-review.yml | 22 ++++++--------- .gitea/workflows/review-reusable.yml | 37 +++++++++++++++---------- CLAUDE.md | 8 ++++-- README.md | 3 +- examples/reusable.yml | 21 +++++++++----- 5 files changed, 53 insertions(+), 38 deletions(-) diff --git a/.gitea/workflows/adversarial-review.yml b/.gitea/workflows/adversarial-review.yml index 27dc5db..c384d3e 100644 --- a/.gitea/workflows/adversarial-review.yml +++ b/.gitea/workflows/adversarial-review.yml @@ -1,10 +1,10 @@ -# Gadfly reviewing its OWN PRs — now a thin CALLER of the reusable workflow -# (.gitea/workflows/review-reusable.yml), dogfooding the Phase-4 "subscribe" -# path. The reusable holds the image pin + env plumbing; this file holds only -# the triggers, the actor gate, and gadfly's specific swarm config. +# Gadfly reviewing its OWN PRs — a thin CALLER of the reusable workflow +# (.gitea/workflows/review-reusable.yml), dogfooding the "subscribe" path. The +# reusable holds the image pin, env plumbing, AND the default swarm; this file +# holds only the triggers, the actor gate, secret forwarding, and allow-list. # -# Advisory only — never blocks a merge. Fleet: 6 cloud + Claude Code -# (sonnet, opus, opus:max) competitors. +# Advisory only — never blocks a merge. It inherits the default swarm: 3 cloud +# models + Claude Code (sonnet, opus, opus:max), 5-lens suite, 3 claudes at once. name: Adversarial Review (Gadfly) @@ -48,11 +48,7 @@ jobs: GADFLY_FINDINGS_URL: ${{ secrets.GADFLY_FINDINGS_URL }} GADFLY_FINDINGS_TOKEN: ${{ secrets.GADFLY_FINDINGS_TOKEN }} with: - models: "minimax-m3:cloud,glm-5.2:cloud,glm-5.1:cloud,deepseek-v4-pro:cloud,nemotron-3-super:cloud,qwen3-coder:480b-cloud,claude-code/sonnet,claude-code/opus,claude-code/opus:max" - specialists: "security,correctness,error-handling" - provider_concurrency: "ollama-cloud=3,claude-code=2" - provider_lens_concurrency: "ollama-cloud=3" - timeout_secs: "600" - max_steps: "14" + # Inherit the default swarm (3 cloud + Claude Code sonnet/opus/opus:max, + # 5-lens suite, all three claudes concurrent) from review-reusable.yml. + # Only the consumer-specific allow-list is set here. allowed_users: "steve,fizi,dazed" - timeout_minutes: 90 diff --git a/.gitea/workflows/review-reusable.yml b/.gitea/workflows/review-reusable.yml index fdb0921..68a96e9 100644 --- a/.gitea/workflows/review-reusable.yml +++ b/.gitea/workflows/review-reusable.yml @@ -10,11 +10,13 @@ # secrets: # forward ONLY what the reviewer needs # OLLAMA_CLOUD_API_KEY: ${{ secrets.OLLAMA_CLOUD_API_KEY }} # CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} -# with: { models: "...", allowed_users: "..." } # all optional +# with: { allowed_users: "..." } # config inputs are optional (see below) # -# Inputs are all optional and default to "" — an empty env value makes the -# image/entrypoint use its own built-in default, so the caller only sets what it -# wants to override. Secrets are DECLARED below (workflow_call.secrets) so a +# Inputs ship the DEFAULT swarm (see the inputs block): 3 cloud models + the +# Claude Code engine, 5-lens suite, all three claudes concurrent. A consumer +# inherits it by omitting `with:` entirely, or overrides any field (e.g. +# `models:` for a cloud-only / different-provider setup; "" falls back to the +# image's built-in default). Secrets are DECLARED below (workflow_call.secrets) so a # caller forwards only the credentials the reviewer actually uses — least # privilege — rather than `secrets: inherit`, which leaks every caller secret # (registry/deploy/db creds) into this workflow. `secrets: inherit` still works @@ -30,21 +32,28 @@ name: Gadfly review (reusable) on: workflow_call: + # Inputs ship the DEFAULT Gadfly swarm so a consumer can just call this + # workflow (no `with:` block) and inherit it. The default is opinionated — + # 3 strong cloud models + the Claude Code engine (sonnet/opus/opus:max), the + # 5-lens suite, all three claudes concurrent. It needs OLLAMA_CLOUD_API_KEY + # and CLAUDE_CODE_OAUTH_TOKEN; a consumer with only one (or a different + # provider) overrides `models:` (and forwards just the secrets it uses). + # Set any input to "" to fall back to the image/entrypoint built-in default. inputs: - models: { type: string, default: "" } # GADFLY_MODELS (csv) - specialists: { type: string, default: "" } # GADFLY_SPECIALISTS + models: { type: string, default: "minimax-m3:cloud,glm-5.2:cloud,deepseek-v4-pro:cloud,claude-code/sonnet,claude-code/opus,claude-code/opus:max" } # GADFLY_MODELS (csv) + specialists: { type: string, default: "security,correctness,maintainability,performance,error-handling" } # GADFLY_SPECIALISTS (5-lens default suite) provider: { type: string, default: "" } # GADFLY_PROVIDER base_url: { type: string, default: "" } # GADFLY_BASE_URL - provider_concurrency: { type: string, default: "" } # GADFLY_PROVIDER_CONCURRENCY - provider_lens_concurrency: { type: string, default: "" } # GADFLY_PROVIDER_LENS_CONCURRENCY - timeout_secs: { type: string, default: "" } # GADFLY_TIMEOUT_SECS (per lens) - max_steps: { type: string, default: "" } # GADFLY_MAX_STEPS + provider_concurrency: { type: string, default: "ollama-cloud=3,claude-code=3" } # GADFLY_PROVIDER_CONCURRENCY (all 3 claudes at once) + provider_lens_concurrency: { type: string, default: "ollama-cloud=3" } # GADFLY_PROVIDER_LENS_CONCURRENCY + timeout_secs: { type: string, default: "600" } # GADFLY_TIMEOUT_SECS (per lens) + max_steps: { type: string, default: "14" } # GADFLY_MAX_STEPS worker_model: { type: string, default: "" } # GADFLY_WORKER_MODEL - allowed_users: { type: string, default: "" } # GADFLY_ALLOWED_USERS + allowed_users: { type: string, default: "" } # GADFLY_ALLOWED_USERS (consumer-specific; set in your stub) trigger_phrase: { type: string, default: "" } # GADFLY_TRIGGER_PHRASE - # Job wall-clock cap. 45 > 30 as a default: a multi-model swarm or a slow - # lens (e.g. claude-code with extended thinking) can exceed 30 minutes. - timeout_minutes: { type: number, default: 45 } + # Job wall-clock cap. 90 as a default: the 5-lens suite across a slow lane + # (claude-code with extended thinking) over two passes can run long. + timeout_minutes: { type: number, default: 90 } # Declared so callers can forward ONLY the secrets the reviewer needs # (least privilege) instead of `secrets: inherit`, which would hand this # workflow every secret in the caller's repo (registry/deploy/db creds the diff --git a/CLAUDE.md b/CLAUDE.md index 7d284c6..cb463b7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -46,9 +46,11 @@ entrypoint.sh container brains: trigger gating, PR clone, model loop (t used to live in workflow YAML) Dockerfile multi-stage; private-module creds via BuildKit secrets never reach the final image .gitea/workflows/build-image.yml push main → :latest; tag v* → :+:latest; PR → build-only -.gitea/workflows/review-reusable.yml reusable (workflow_call) review job; consumers subscribe with - an ~8-line caller forwarding only the secrets the reviewer needs (Phase 4). gadfly's own - adversarial-review.yml is a thin caller of it (dogfoods the path). +.gitea/workflows/review-reusable.yml reusable (workflow_call) review job; ships the DEFAULT swarm as + input defaults (3 cloud + Claude Code sonnet/opus/opus:max, 5-lens suite, + claude-code=3) so consumers inherit it by omitting `with:`. Consumers subscribe + with an ~8-line caller forwarding only the secrets the reviewer needs (Phase 4); + gadfly's own adversarial-review.yml is a thin caller of it (dogfoods the path). examples/ copy-paste consumer stub workflows for different providers ``` diff --git a/README.md b/README.md index 2f0bd92..577e574 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,8 @@ it. Drop one file in your repo and set a couple of secrets/vars: 1. Copy a stub from [`examples/`](examples/) to `.gitea/workflows/adversarial-review.yml` in your repo. Two flavors: the slim [`reusable.yml`](examples/reusable.yml) — a tiny caller of Gadfly's **reusable workflow** (`uses: steve/gadfly/.gitea/workflows/review-reusable.yml@…`, - forwarding only the secrets the reviewer needs), best when you take the defaults — or the full self-contained + forwarding only the secrets the reviewer needs), which ships a **default swarm** (3 cloud models + + the Claude Code engine, 5-lens suite) you inherit by omitting `with:` or override per-input — or the full self-contained [`adversarial-review.yml`](examples/adversarial-review.yml) (Ollama Cloud default, with inline notes for every provider / local Ollama / OpenAI-compatible / endpoint aliases). See the [examples index](examples/README.md). diff --git a/examples/reusable.yml b/examples/reusable.yml index 312376f..a582053 100644 --- a/examples/reusable.yml +++ b/examples/reusable.yml @@ -5,7 +5,12 @@ # workflow, which holds the image pin + all the env plumbing. You only declare # the triggers, the comment-trigger actor gate, and any overrides you want. # -# Needs: secret OLLAMA_CLOUD_API_KEY (the default Ollama Cloud provider). +# The reusable ships a DEFAULT swarm: 3 cloud models + the Claude Code engine +# (sonnet/opus/opus:max), 5-lens suite. That default needs BOTH +# OLLAMA_CLOUD_API_KEY and CLAUDE_CODE_OAUTH_TOKEN. This example overrides +# `models:` to a cloud-only set so it works with just OLLAMA_CLOUD_API_KEY — +# delete that override (and forward the Claude token) to inherit the full default. +# # Forward ONLY the secrets the reviewer uses (least privilege) — see the # `secrets:` block below. GITEA_TOKEN is automatic. `secrets: inherit` also works # but hands the reusable EVERY secret in your repo (registry/deploy/db creds the @@ -13,9 +18,8 @@ # Gadfly commit (not @main) so a push there can't change what runs with your # secrets. # -# Prefer this when you're happy with the defaults. For custom named endpoints -# (GADFLY_ENDPOINT_) or a provider the reusable doesn't map, use the full -# stub in adversarial-review.yml instead. +# For custom named endpoints (GADFLY_ENDPOINT_) or a provider the reusable +# doesn't map, use the full stub in adversarial-review.yml instead. name: Adversarial Review (Gadfly) @@ -56,7 +60,10 @@ jobs: # GADFLY_FINDINGS_URL: ${{ secrets.GADFLY_FINDINGS_URL }} # GADFLY_FINDINGS_TOKEN: ${{ secrets.GADFLY_FINDINGS_TOKEN }} with: - # All optional — omit to take Gadfly's defaults. Examples: - # models: "qwen3-coder:480b-cloud,gpt-oss:120b-cloud" - # specialists: "security,correctness,error-handling" + # Cloud-only override so this works with just OLLAMA_CLOUD_API_KEY. Delete + # this line (and forward CLAUDE_CODE_OAUTH_TOKEN above) to inherit the full + # default swarm (3 cloud + Claude Code sonnet/opus/opus:max, 5 lenses). + models: "minimax-m3:cloud,glm-5.2:cloud,deepseek-v4-pro:cloud" + # Other inputs inherit the default (5-lens suite, concurrency, 90-min cap); + # override any of them here (specialists, provider, base_url, timeout_secs…). allowed_users: "your-username"