Make review crew slot-based (arbitrary workers, incl. duplicate agent+model) · Entire

Make review crew slot-based (arbitrary workers, incl. duplicate agent+model)

1481e83→main·

dipree·1mo ago·3 files·+251 added/-75 removed

Reframe crew configuration around worker slots rather than "per agent". A slot is an agent + model; you can define any number, including the same agent multiple times on different OR identical models (e.g. 5 Claude slots). The data model already supported this (profile.Agents is keyed by an arbitrary worker name, disambiguated by workerIDForAgentModel) — this change updates the two config surfaces that were still agent-keyed.

Picker: replace the per-agent model grid with a slot list — seeded with one default-model slot per launchable agent, then add/remove/duplicate slots (each add = pick agent → pick model/Default/Custom).

Scripted: add repeatable --set-slot agent[=model]. --set-agents stays as the one-default-slot-per-agent shorthand (keeps --set-model targeting). Duplicate slots are allowed and become distinct workers.

Sessions

Changes

3

var setMaster string
var setTask string
var setModels []string
var setSlots []string

cmd := &cobra.Command{
    Use: "review",

--set-master   with --configure: master agent that writes the final report
  --set-task     with --configure: the profile's canonical task text
  --set-model    with --configure: per-worker model as agent=model (repeatable)
  --set-slot     with --configure: a worker slot as agent[=model] (repeatable;
                  the same agent/model may repeat to run it multiple times)
  --edit         re-open the advanced review profile skill picker
  --findings     browse local review findings
  --agent NAME   run only one worker from the selected profile
}

Test Cases

TestBuildConfiguredProfile_FromSlots_AllowsDuplicateAgents

func TestBuildConfiguredProfile_FromSlots_AllowsDuplicateAgents(t *testing.T) {
    deps := configureTestDeps("claude-code", "codex")
    profile, err := buildConfiguredProfile(
        context.Background(),
        "general",
        reviewConfigureOptions{
            Slots: []string{"claude-code=opus", "claude-code=sonnet", "claude-code", "claude-code"},
        },
        &settings.EntireSettings{},
        deps,
    )

if err != nil {
        t.Fatalf("buildConfiguredProfile: %v", err)
    }
    
    // Four distinct workers: two different models + two identical default slots.
    if len(profile.Agents) != 4 {
        t.Fatalf("agents = %d, want 4: %#v", len(profile.Agents), profile.Agents)
    }

models := map[string]int{}
    for _, cfg := range profile.Agents {
        if cfg.Agent != "claude-code" {
            t.Errorf("worker agent = %q, want claude-code", cfg.Agent)
        }
        models[cfg.Model]++
    }
    if models["opus"] != 1 || models["sonnet"] != 1 || models[""] != 2 {
        t.Errorf("model distribution = %#v, want opus:1 sonnet:1 default:2", models)
    }
}

Building the Crew Profile

func buildCrewProfile(ctx context.Context, profileName string, slots []crewSlot) settings.ReviewProfileConfig {
    profile := settings.ReviewProfileConfig{
        Task:   profileTask(profileName, settings.ReviewProfileConfig{}),
        Agents: map[string]settings.ReviewConfig{},
        Agents: make(map[string]settings.ReviewConfig, len(slots)),
    }
    for _, s := range slots {
        cfg := defaultReviewAgentConfig(profileName, s.agent) 
        // Set Agent explicitly so the worker is valid even when the agent has no default skills/prompt.
        cfg.Agent = s.agent
        cfg.Model = s.model
        profile.Agents[workerIDForAgentModel(s.agent, s.model, profile.Agents)] = cfg
    }
    return profile
}