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

`1481e83`→[main](/content/gh/entireio/cli/commits/main/index.html)·

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

- cmd/entire/cli/review

- Mcmd.go+30/-5

- Mconfigure_test.go+30

- Mpicker.go+191/-70

```go
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

```go
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

```go
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
}
```
