Files
executus/.gitea/workflows/ci.yml
T
steve ea9475da54
executus CI / test (pull_request) Failing after 1m5s
Adversarial Review (Gadfly) / review (pull_request) Successful in 8m18s
P5: light-tier canary — gadfly-shaped reviewer on executus core
examples/reviewer proves the core is sufficient for a static-binary light host
(gadfly's shape) with NO batteries:
- config.Env + model.Configure  -> env-driven model fleet + tier overrides
- model.ParseModelForContext    -> tier resolution + failover
- fanout.Run (PerKey caps)      -> N models x M lenses swarm, per-provider bound
- model.GenerateWith[T]         -> structured findings per (model, lens) cell
- Consolidate                   -> one verdict-led report section per model

Hermetic test runs the full 2x3 swarm against majordomo's fake provider and
asserts the consolidated verdicts. A go list -deps CI check asserts the canary
imports ZERO batteries (the light-tier invariant) — gadfly's go.sum stays free
of gorm/redis/discordgo/sqlite. README + docs updated.

This is the canary; migrating the LIVE gadfly repo onto executus core is a
follow-up (kept separate to not destabilize the active reviewer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 00:22:02 -04:00

129 lines
5.1 KiB
YAML

name: executus CI
# Go library CI: build, vet, race-tested, tidy-clean, plus the executus
# invariant that the CORE module never pulls a host/DB dependency. Mirrors
# majordomo's gates; private-module access (the private majordomo dependency)
# uses the same Gitea credentials gadfly's CI uses.
#
# Required repo secrets:
# REGISTRY_USER / REGISTRY_PASSWORD Gitea creds with read access to the
# private majordomo module.
on:
push:
branches: [main]
tags: ["v*"]
# Docs/example/meta-only changes don't affect the build — skip the run.
# (Path filters are not applied to tag pushes, so v* releases always run.)
paths-ignore:
- "**.md"
- "LICENSE"
- ".gitignore"
- ".dockerignore"
- "examples/**"
- "docs/**"
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- "**.md"
- "LICENSE"
- ".gitignore"
- ".dockerignore"
- "examples/**"
- "docs/**"
workflow_dispatch: {}
concurrency:
group: executus-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
run: |
REPO_URL="https://token:${{ github.token }}@gitea.stevedudenhoeffer.com/${{ github.repository }}.git"
if [ "${{ github.event_name }}" = "pull_request" ]; then
git clone --depth=1 --branch "${{ github.head_ref }}" "$REPO_URL" .
else
git clone --depth=1 --branch "${{ github.ref_name }}" "$REPO_URL" .
fi
- name: Set up Go
run: |
GO_VERSION=$(grep '^go ' go.mod | awk '{print $2}')
curl -sL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar -C /usr/local -xzf -
echo "/usr/local/go/bin" >> $GITHUB_PATH
echo "GOPATH=${HOME}/go" >> $GITHUB_ENV
echo "${HOME}/go/bin" >> $GITHUB_PATH
- name: Configure private module access
env:
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: |
git config --global url."https://${REGISTRY_USER}:${REGISTRY_PASSWORD}@gitea.stevedudenhoeffer.com/".insteadOf "https://gitea.stevedudenhoeffer.com/"
echo "GOFLAGS=-mod=mod" >> $GITHUB_ENV
echo "GONOSUMCHECK=gitea.stevedudenhoeffer.com/*" >> $GITHUB_ENV
echo "GONOSUMDB=gitea.stevedudenhoeffer.com/*" >> $GITHUB_ENV
echo "GOPRIVATE=gitea.stevedudenhoeffer.com/*" >> $GITHUB_ENV
- name: Build
run: go build ./...
- name: Vet
run: go vet ./...
- name: Test (race)
run: go test -race -count=1 -timeout 5m ./...
- name: go mod tidy is clean
run: |
go mod tidy
# go.sum may not exist yet (no external deps), so don't name it as a
# diff path (git errors on a missing path). git status flags both a
# modified go.mod and a freshly-created untracked go.sum.
CHANGES=$(git status --porcelain -- go.mod go.sum)
if [ -n "$CHANGES" ]; then
echo "go.mod/go.sum not tidy:"; echo "$CHANGES"; git diff -- go.mod; exit 1
fi
echo "OK: go.mod/go.sum tidy."
- name: Core stays majordomo+stdlib only
run: |
# The core module must never pull a host/DB dependency. If any of these
# appear in go.sum, a battery leaked into the core import graph.
[ -f go.sum ] || { echo "OK: no external dependencies yet."; exit 0; }
FORBIDDEN='gorm.io|go-redis|redis/go-redis|bwmarrin/discordgo|modernc.org/sqlite|mattn/go-sqlite3|gin-gonic/gin'
if grep -qE "$FORBIDDEN" go.sum; then
echo "ERROR: forbidden dependency in core go.sum:"
grep -E "$FORBIDDEN" go.sum
exit 1
fi
echo "OK: core go.sum is free of host/DB dependencies."
- name: Light-tier canary imports no battery
run: |
# examples/reviewer is gadfly's shape on the CORE only. If it ever
# pulls in a battery (audit/budget/persona/skill/critic/schedule/
# checkpoint/contrib), the light path has regressed.
LEAK=$(go list -deps ./examples/reviewer/... | grep -E 'executus/(audit|budget|persona|skill|critic|schedule|checkpoint|contrib)' || true)
if [ -n "$LEAK" ]; then
echo "ERROR: light-tier canary pulled in a battery:"; echo "$LEAK"; exit 1
fi
echo "OK: examples/reviewer is core-only."
- name: contrib/store (nested SQLite module — isolated from core)
run: |
# contrib/store is a SEPARATE module carrying modernc.org/sqlite; the
# core's `go test ./...` doesn't reach it. Build + test it on its own,
# and confirm it DOES carry the driver the core forbids (proof the
# split works: persistence lives here, not in the core go.sum).
cd contrib/store
go build ./...
go test -race -count=1 -timeout 5m ./...
grep -qE 'modernc.org/sqlite' go.sum || { echo "ERROR: contrib/store should carry the sqlite driver"; exit 1; }
echo "OK: contrib/store builds, tests pass, and owns the SQLite dep."