Adds a third review harness alongside the in-process majordomo loop and the claude-code CLI shell-out: the OpenCode CLI (opencode.ai) driving an ollama-cloud model, selected by an opencode/<model> spec.
The point is to benchmark gadfly's boutique executus harness against a freely-available agentic harness on the same model — run ollama-cloud/glm-5.2 (majordomo loop) and opencode/glm-5.2 (OpenCode) side by side and compare findings. This is the wired, no-proxy version of the README's existing "alternate backends" idea.
How it works
Read-only via config, not a flag. OpenCode has no --append-system-prompt, so each pass generates an OpenCode config — the lens system prompt as a gadfly agent's prompt with edit/bash denied at both global and agent level, plus a gadfly ollama-cloud provider — and injects it via OPENCODE_CONFIG_CONTENT. That env var is the highest-precedence config source in the container, so a reviewed repo's own opencode.json can't re-enable edits on the reviewer.
Spec forms:opencode/<model> (wrapped in the generated provider), the open-code/ alias, opencode/<provider>/<model> pass-through to OpenCode's own registry/auth, and bare opencode. Model ids are taken verbatim, so colon-bearing ollama ids like qwen3-coder:480b-cloud survive (no :thinking suffix like claude-code).
Auth reuses OLLAMA_CLOUD_API_KEY (mapped to OLLAMA_API_KEY, referenced as {env:OLLAMA_API_KEY} in config — never a literal secret). Knobs mirror GADFLY_CLAUDE_*: GADFLY_OPENCODE_BIN/MODEL/BASE_URL/EXTRA_ARGS. openCodeEnv() forwards OLLAMA_API_KEY (the inverse of claudeEnv) but still withholds the Gitea/findings/Anthropic secrets.
main.go engine selection is now a switch (claude-code / opencode / majordomo); the auto-select path uses a type-check instead of a boolean so a shell-out engine can never hit the *majordomoEngine assertion. auto selection and delegate_investigation stay majordomo-only and are skipped for opencode (the CLI does its own legwork).
Dockerfile — bundles opencode-ai (npm auto-selects its musl build on alpine) with a best-effort version check + provider pre-warm that never fails the shared image build.
README.md, examples/opencode.yml (new), examples/README.md, CLAUDE.md, scripts/run.sh, entrypoint.sh, .gitea/workflows/review-reusable.yml — docs/comments per the maintenance rules.
Verification
go build, go test ./..., go vet, gofmt all clean.
Ran the actual built binary end-to-end with a fake opencode CLI: confirmed main.go routes opencode/… to the new engine, emits the correct argv (run --agent gadfly --model gadfly/qwen3-coder:480b-cloud … <task-last>), injects the expected config (env-ref API key, both-level deny, real system prompt), and consolidates output into review markdown.
Not yet verified (needs the built image / a real run)
Docker image build — needs registry secrets. npm publishes musl builds of opencode-ai (opencode-linux-x64-musl, etc.) that it auto-selects on alpine, so the musl risk is small, but confirm with docker run --entrypoint opencode <img> --version.
A real-model run — that OpenCode's --agent + deny behaves non-interactively as expected and its default text output captures cleanly. Documented honestly as "newly wired, lightly tested" in the README.
To put it on the live fleet: add an opencode/<model> entry to the GADFLY_DEFAULT_MODELS user-scope var (runtime retune, no repo change), pinning consumers to the fresh :sha-<short> since :latest is runner-cached.
## What
Adds a third review harness alongside the in-process majordomo loop and the claude-code CLI shell-out: the **OpenCode CLI** ([opencode.ai](https://opencode.ai)) driving an **ollama-cloud** model, selected by an `opencode/<model>` spec.
The point is to **benchmark gadfly's boutique executus harness against a freely-available agentic harness on the same model** — run `ollama-cloud/glm-5.2` (majordomo loop) and `opencode/glm-5.2` (OpenCode) side by side and compare findings. This is the wired, no-proxy version of the README's existing "alternate backends" idea.
## How it works
- **Read-only via config, not a flag.** OpenCode has no `--append-system-prompt`, so each pass generates an OpenCode config — the lens system prompt as a `gadfly` agent's prompt with `edit`/`bash` denied at both global and agent level, plus a `gadfly` ollama-cloud provider — and injects it via `OPENCODE_CONFIG_CONTENT`. That env var is the highest-precedence config source in the container, so **a reviewed repo's own `opencode.json` can't re-enable edits** on the reviewer.
- **Spec forms:** `opencode/<model>` (wrapped in the generated provider), the `open-code/` alias, `opencode/<provider>/<model>` pass-through to OpenCode's own registry/auth, and bare `opencode`. Model ids are taken **verbatim**, so colon-bearing ollama ids like `qwen3-coder:480b-cloud` survive (no `:thinking` suffix like claude-code).
- **Auth reuses `OLLAMA_CLOUD_API_KEY`** (mapped to `OLLAMA_API_KEY`, referenced as `{env:OLLAMA_API_KEY}` in config — never a literal secret). Knobs mirror `GADFLY_CLAUDE_*`: `GADFLY_OPENCODE_BIN/MODEL/BASE_URL/EXTRA_ARGS`. `openCodeEnv()` forwards `OLLAMA_API_KEY` (the inverse of `claudeEnv`) but still withholds the Gitea/findings/Anthropic secrets.
- `main.go` engine selection is now a switch (claude-code / opencode / majordomo); the auto-select path uses a type-check instead of a boolean so a shell-out engine can never hit the `*majordomoEngine` assertion. `auto` selection and `delegate_investigation` stay majordomo-only and are skipped for opencode (the CLI does its own legwork).
## Files
- **`cmd/gadfly/opencode.go`** (new) — the engine.
- **`cmd/gadfly/opencode_test.go`** (new) — 12 tests mirroring `engine_test.go`.
- **`cmd/gadfly/main.go`**, **`engine.go`** — engine selection switch + doc.
- **`Dockerfile`** — bundles `opencode-ai` (npm auto-selects its musl build on alpine) with a best-effort version check + provider pre-warm that never fails the shared image build.
- **`README.md`**, **`examples/opencode.yml`** (new), **`examples/README.md`**, **`CLAUDE.md`**, **`scripts/run.sh`**, **`entrypoint.sh`**, **`.gitea/workflows/review-reusable.yml`** — docs/comments per the maintenance rules.
## Verification
- `go build`, `go test ./...`, `go vet`, `gofmt` all clean.
- Ran the **actual built binary** end-to-end with a fake `opencode` CLI: confirmed `main.go` routes `opencode/…` to the new engine, emits the correct argv (`run --agent gadfly --model gadfly/qwen3-coder:480b-cloud … <task-last>`), injects the expected config (env-ref API key, both-level deny, real system prompt), and consolidates output into review markdown.
### Not yet verified (needs the built image / a real run)
- **Docker image build** — needs registry secrets. npm publishes musl builds of `opencode-ai` (`opencode-linux-x64-musl`, etc.) that it auto-selects on alpine, so the musl risk is small, but confirm with `docker run --entrypoint opencode <img> --version`.
- **A real-model run** — that OpenCode's `--agent` + deny behaves non-interactively as expected and its default text output captures cleanly. Documented honestly as "newly wired, lightly tested" in the README.
To put it on the live fleet: add an `opencode/<model>` entry to the `GADFLY_DEFAULT_MODELS` user-scope var (runtime retune, no repo change), pinning consumers to the fresh `:sha-<short>` since `:latest` is runner-cached.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add a third review harness alongside the in-process majordomo loop and the
claude-code CLI shell-out: the OpenCode CLI (opencode.ai) driving an ollama-cloud
model, selected by an "opencode/<model>" spec. The goal is to benchmark gadfly's
boutique executus harness against a freely-available agentic harness on the SAME
model (e.g. "ollama-cloud/glm-5.2" vs "opencode/glm-5.2").
OpenCode has no --append-system-prompt flag, so the lens system prompt and the
read-only discipline are delivered through a generated config injected via
OPENCODE_CONFIG_CONTENT: a "gadfly" agent whose prompt is the system prompt with
edit/bash denied at both the global and agent level, plus a "gadfly" ollama-cloud
provider. That env var is the highest-precedence config source in the container,
so a reviewed repo's own opencode.json can't re-enable edits on the reviewer.
Spec forms: "opencode/<model>" (wrapped in the generated provider), the
"open-code/" alias, "opencode/<provider>/<model>" pass-through to OpenCode's own
registry, and bare "opencode". Model ids are taken verbatim so colon-bearing
ollama ids (qwen3-coder:480b-cloud) survive. Auth reuses OLLAMA_CLOUD_API_KEY
(mapped to OLLAMA_API_KEY, referenced as {env:OLLAMA_API_KEY} in config, never a
literal secret). Knobs mirror GADFLY_CLAUDE_*: GADFLY_OPENCODE_BIN/MODEL/BASE_URL/
EXTRA_ARGS. openCodeEnv() forwards OLLAMA_API_KEY (the inverse of claudeEnv) but
still withholds the Gitea/findings/Anthropic secrets.
main.go engine selection is now a switch (claude-code / opencode / majordomo), and
the auto-select path uses a type-check instead of a boolean so a shell-out engine
can never hit the *majordomoEngine assertion. auto-select and delegate_investigation
stay majordomo-only and are skipped for opencode (the CLI does its own legwork).
Dockerfile bundles opencode-ai (npm auto-selects its musl build on alpine) with a
best-effort version check + provider pre-warm that never fails the shared image
build. README/examples/CLAUDE.md/scripts updated per the maintenance rules.
Tests: new opencode_test.go mirrors engine_test.go (spec/model/args/config/env-
filter + stub-CLI runtime tests). Verified end-to-end with a fake opencode CLI:
correct argv, injected config, and consolidated markdown output.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
gadfly's self-review adversarial-review.yml calls review-reusable.yml but was
never updated for the Gitea >= 1.27 breaking change (go-gitea#37478): a called
workflow no longer receives the caller's workflow_dispatch inputs in
github.event, so a manual "review PR #N" dispatch reached the reusable with an
empty PR. Commit 64d34bd added the pr_number workflow_call input and mort's
caller threads it; this brings gadfly's own caller in line so the dogfood path
works on the same Gitea version.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
Adds a third review harness alongside the in-process majordomo loop and the claude-code CLI shell-out: the OpenCode CLI (opencode.ai) driving an ollama-cloud model, selected by an
opencode/<model>spec.The point is to benchmark gadfly's boutique executus harness against a freely-available agentic harness on the same model — run
ollama-cloud/glm-5.2(majordomo loop) andopencode/glm-5.2(OpenCode) side by side and compare findings. This is the wired, no-proxy version of the README's existing "alternate backends" idea.How it works
--append-system-prompt, so each pass generates an OpenCode config — the lens system prompt as agadflyagent's prompt withedit/bashdenied at both global and agent level, plus agadflyollama-cloud provider — and injects it viaOPENCODE_CONFIG_CONTENT. That env var is the highest-precedence config source in the container, so a reviewed repo's ownopencode.jsoncan't re-enable edits on the reviewer.opencode/<model>(wrapped in the generated provider), theopen-code/alias,opencode/<provider>/<model>pass-through to OpenCode's own registry/auth, and bareopencode. Model ids are taken verbatim, so colon-bearing ollama ids likeqwen3-coder:480b-cloudsurvive (no:thinkingsuffix like claude-code).OLLAMA_CLOUD_API_KEY(mapped toOLLAMA_API_KEY, referenced as{env:OLLAMA_API_KEY}in config — never a literal secret). Knobs mirrorGADFLY_CLAUDE_*:GADFLY_OPENCODE_BIN/MODEL/BASE_URL/EXTRA_ARGS.openCodeEnv()forwardsOLLAMA_API_KEY(the inverse ofclaudeEnv) but still withholds the Gitea/findings/Anthropic secrets.main.goengine selection is now a switch (claude-code / opencode / majordomo); the auto-select path uses a type-check instead of a boolean so a shell-out engine can never hit the*majordomoEngineassertion.autoselection anddelegate_investigationstay majordomo-only and are skipped for opencode (the CLI does its own legwork).Files
cmd/gadfly/opencode.go(new) — the engine.cmd/gadfly/opencode_test.go(new) — 12 tests mirroringengine_test.go.cmd/gadfly/main.go,engine.go— engine selection switch + doc.Dockerfile— bundlesopencode-ai(npm auto-selects its musl build on alpine) with a best-effort version check + provider pre-warm that never fails the shared image build.README.md,examples/opencode.yml(new),examples/README.md,CLAUDE.md,scripts/run.sh,entrypoint.sh,.gitea/workflows/review-reusable.yml— docs/comments per the maintenance rules.Verification
go build,go test ./...,go vet,gofmtall clean.opencodeCLI: confirmedmain.goroutesopencode/…to the new engine, emits the correct argv (run --agent gadfly --model gadfly/qwen3-coder:480b-cloud … <task-last>), injects the expected config (env-ref API key, both-level deny, real system prompt), and consolidates output into review markdown.Not yet verified (needs the built image / a real run)
opencode-ai(opencode-linux-x64-musl, etc.) that it auto-selects on alpine, so the musl risk is small, but confirm withdocker run --entrypoint opencode <img> --version.--agent+ deny behaves non-interactively as expected and its default text output captures cleanly. Documented honestly as "newly wired, lightly tested" in the README.To put it on the live fleet: add an
opencode/<model>entry to theGADFLY_DEFAULT_MODELSuser-scope var (runtime retune, no repo change), pinning consumers to the fresh:sha-<short>since:latestis runner-cached.🤖 Generated with Claude Code
Add a third review harness alongside the in-process majordomo loop and the claude-code CLI shell-out: the OpenCode CLI (opencode.ai) driving an ollama-cloud model, selected by an "opencode/<model>" spec. The goal is to benchmark gadfly's boutique executus harness against a freely-available agentic harness on the SAME model (e.g. "ollama-cloud/glm-5.2" vs "opencode/glm-5.2"). OpenCode has no --append-system-prompt flag, so the lens system prompt and the read-only discipline are delivered through a generated config injected via OPENCODE_CONFIG_CONTENT: a "gadfly" agent whose prompt is the system prompt with edit/bash denied at both the global and agent level, plus a "gadfly" ollama-cloud provider. That env var is the highest-precedence config source in the container, so a reviewed repo's own opencode.json can't re-enable edits on the reviewer. Spec forms: "opencode/<model>" (wrapped in the generated provider), the "open-code/" alias, "opencode/<provider>/<model>" pass-through to OpenCode's own registry, and bare "opencode". Model ids are taken verbatim so colon-bearing ollama ids (qwen3-coder:480b-cloud) survive. Auth reuses OLLAMA_CLOUD_API_KEY (mapped to OLLAMA_API_KEY, referenced as {env:OLLAMA_API_KEY} in config, never a literal secret). Knobs mirror GADFLY_CLAUDE_*: GADFLY_OPENCODE_BIN/MODEL/BASE_URL/ EXTRA_ARGS. openCodeEnv() forwards OLLAMA_API_KEY (the inverse of claudeEnv) but still withholds the Gitea/findings/Anthropic secrets. main.go engine selection is now a switch (claude-code / opencode / majordomo), and the auto-select path uses a type-check instead of a boolean so a shell-out engine can never hit the *majordomoEngine assertion. auto-select and delegate_investigation stay majordomo-only and are skipped for opencode (the CLI does its own legwork). Dockerfile bundles opencode-ai (npm auto-selects its musl build on alpine) with a best-effort version check + provider pre-warm that never fails the shared image build. README/examples/CLAUDE.md/scripts updated per the maintenance rules. Tests: new opencode_test.go mirrors engine_test.go (spec/model/args/config/env- filter + stub-CLI runtime tests). Verified end-to-end with a fake opencode CLI: correct argv, injected config, and consolidated markdown output. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>Pull request closed