Skip to content

composer: let the repository picker check out zero, one, or many repos - #105

Merged
hbrooks merged 1 commit into
mainfrom
fix/repository-picker-multi
Aug 10, 2026
Merged

composer: let the repository picker check out zero, one, or many repos#105
hbrooks merged 1 commit into
mainfrom
fix/repository-picker-multi

Conversation

@hbrooks

@hbrooks hbrooks commented Aug 10, 2026

Copy link
Copy Markdown
Member

Summary

The new-session composer's Repository row (agent, then ↑ past "Agent") only ever produced a one-repo checkout, though sandboxes take zero, one, or many. Three separate bugs, the latter two of which would have made fixing the first one invisible.

The resting state lied. The toggle logic was already multi-select, but an empty selection meant "let the server resolve it" while rendering as [x] on the detected repo. So checking a second repo produced a set of one, silently dropping the repo that looked selected, and unchecking everything was indistinguishable from the default. The selection is now null when untouched, and the first toggle seeds from the detected repo, so the checkboxes on screen are the checkout you get. The row reads none for the no-checkout sandbox.

The override key was dead. sandbox: was renamed to environment: in ellipsis#6162, and StrictConfigModel is extra="forbid". Verified against the real Pydantic model:

sandbox:     FAIL -> Extra inputs are not permitted
environment: OK   -> [{'name': 'cli', 'owner': 'ellipsis-dev', 'ref': None}]

So every repo pick from this picker had been rejected at validation. The same stale key sat behind --repo / --cpu / --memory / --timeout on agent session start, which was silently broken too.

The context repo overrode the selection. The server merges the request's repository into the checkout unconditionally, even under an explicit config (run_agent_session.py: "even under an explicit --config, agent in a repo always has that repo"). An empty or api-less selection could therefore never hold, so it is dropped when the selection excludes it — which also moves default-config resolution off that repo's rung, the honest reading of "not this one".

Request building moves out of the startSession callback into applyComposerChoices in lib/sessions.ts, so the three cases are testable without mounting the pane.

The public skill documented the old sandbox: schema (with github: nested under it, now top-level permissions:), which would have had agents generate configs the API rejects.

Test plan

  • 443 tests pass, tsc --noEmit clean, on a worktree branched from origin/main
  • 8 new applyComposerChoices tests: untouched sends no repo override; many repos; empty set; context repo dropped when excluded; environment not sandbox; no mutation of the input request
  • Drove the picker offline in an Ink harness against a fake TTY (deleted after). Checking acme/web yields [x] acme/api [x] acme/web — previously this dropped acme/api. Three checked submits all three with repository retained; unchecking all submits environment.repositories: [] with repository dropped. Closed row reads acme/api, acme/web, acme/infra and none respectively.
  • All three YAML examples in SKILL.md validated against the real AgentConfig / CodeReviewConfig models

Worth a second opinion on: dropping req.repository when the selection excludes it also changes which rung of the defaults ladder resolves the agent. That seemed right — if you uncheck this repo, you probably don't want its repo-default agent either — but it is a behavior change beyond the checkout set.

The Repository row's toggle was already multi-select, but its resting state
lied: an empty selection MEANT "let the server resolve it" while RENDERING as
[x] on the detected repo. Checking a second repo therefore produced a set of
one, silently dropping the repo that looked selected, and unchecking everything
was indistinguishable from the default. The selection is now null when
untouched, and the first toggle seeds from the detected repo, so the checkboxes
on screen are the checkout you get. The row reads "none" for the no-checkout
sandbox.

Two related bugs, either of which would have made that fix invisible:

The override key was dead. The backend renamed sandbox: to environment: and
the config model forbids extra keys, so every repo pick was rejected at
validation. The same stale key sat behind --repo/--cpu/--memory/--timeout on
'session start'.

The context repo overrode the selection. The server merges the request's
`repository` into the checkout unconditionally, even under an explicit config,
so an empty set could never hold; it is dropped when the selection excludes it.

Request building moves to applyComposerChoices so the three cases are testable
without mounting the pane. The public skill documented the old sandbox: schema
(and github: under it, now top-level permissions:), which would have generated
configs the API rejects.
@hbrooks
hbrooks merged commit 849c886 into main Aug 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant