feat: dynamic auto specialist selection + worker-tier delegation
Build & push image / build-and-push (push) Successful in 33s

Two Phase-2 swarm upgrades:

- auto.go: GADFLY_SPECIALISTS=auto routes the review — a selector model
  (GADFLY_SELECTOR_MODEL, else the review model) reads the changed files + PR
  description and picks the smallest relevant lens set from the catalog, and may
  propose ad-hoc lenses for gaps (e.g. migrations). Structured output via
  majordomo.Generate[T]; capped + de-duped; falls back to the default suite.
- delegate.go: GADFLY_WORKER_MODEL adds a delegate_investigation tool so the
  reviewer offloads mechanical legwork (trace callers, gather usages) to a cheap
  worker sub-agent that returns an evidence-cited digest — the top model reasons
  over summaries, not raw file dumps. Workers get an fs-only toolbox (no
  sub-delegation). Unset = off.

resolveSpecialists now also returns the registry + an auto flag. Docs (README
Specialists + config table, CLAUDE.md, main.go header) + tests updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Steve Dudenhoeffer
2026-06-25 19:35:59 -04:00
co-authored by Claude Opus 4.8
parent 7809d1b93d
commit 4b8f9aa39b
9 changed files with 370 additions and 26 deletions
+22 -5
View File
@@ -28,7 +28,7 @@ func eq(a, b []string) bool {
func TestResolveSpecialists_DefaultSuite(t *testing.T) {
t.Setenv("GADFLY_SPECIALISTS", "")
specs, errs := resolveSpecialists(t.TempDir())
specs, _, _, errs := resolveSpecialists(t.TempDir())
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
@@ -39,7 +39,7 @@ func TestResolveSpecialists_DefaultSuite(t *testing.T) {
func TestResolveSpecialists_EnvSelection(t *testing.T) {
t.Setenv("GADFLY_SPECIALISTS", "security, tests")
specs, errs := resolveSpecialists(t.TempDir())
specs, _, _, errs := resolveSpecialists(t.TempDir())
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
@@ -48,9 +48,26 @@ func TestResolveSpecialists_EnvSelection(t *testing.T) {
}
}
func TestResolveSpecialists_AutoFlag(t *testing.T) {
t.Setenv("GADFLY_SPECIALISTS", "auto")
specs, registry, auto, errs := resolveSpecialists(t.TempDir())
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if !auto {
t.Error("expected auto=true for GADFLY_SPECIALISTS=auto")
}
if specs != nil {
t.Errorf("auto mode should return nil specs, got %v", names(specs))
}
if len(registry) == 0 {
t.Error("auto mode should still return the registry catalog")
}
}
func TestResolveSpecialists_UnknownNameErrors(t *testing.T) {
t.Setenv("GADFLY_SPECIALISTS", "security,bogus")
specs, errs := resolveSpecialists(t.TempDir())
specs, _, _, errs := resolveSpecialists(t.TempDir())
if len(errs) == 0 {
t.Fatal("expected an error for unknown specialist")
}
@@ -62,7 +79,7 @@ func TestResolveSpecialists_UnknownNameErrors(t *testing.T) {
func TestResolveSpecialists_EnvCustomDefinition(t *testing.T) {
t.Setenv("GADFLY_SPECIALIST_MIGRATIONS", "Review DB migrations for destructive ops.")
t.Setenv("GADFLY_SPECIALISTS", "migrations")
specs, errs := resolveSpecialists(t.TempDir())
specs, _, _, errs := resolveSpecialists(t.TempDir())
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
@@ -83,7 +100,7 @@ define:
t.Fatal(err)
}
t.Setenv("GADFLY_SPECIALISTS", "") // let the file drive selection
specs, errs := resolveSpecialists(dir)
specs, _, _, errs := resolveSpecialists(dir)
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}