#4152 · Grouped ACP selections fail parsing
GitHub issue · 2026-09-23 · Base 94d77da09568d7f0f7cdb23b84686e743acfb0ad
REPRODUCED · Root-cause confidence: high
1. TL;DR
The ACP session-result parser rejects model choices wrapped in groups. It validates each top-level entry as a leaf choice and requires a string value there. A group contains nested choices instead, so parsing fails before model selection. The same parser silently discards grouped choices for other settings. Both results were repeated in a second clean checkout; no vendor account or real provider process was used.
2. Claims vs findings
| Claim | Finding | Evidence |
|---|---|---|
| Grouped model choices prevent session creation | Verified at the session-result boundary | The production schema throws in both runs; the connection rejects schema failures. |
| Flat options work | Verified | The flat control passes twice. |
| Non-model groups disappear | Verified | The regression assertion receives [] instead of two choices. |
| Specific vendor and desktop behavior | Unverified | No vendor binary, credentials, or UI was used. |
| The external ACP specification permits grouping | Not independently checked | The rule forbids fetching issue-supplied external links. This report demonstrates parser behavior directly. |
3. Environment
Public get-bb/bb origin/main at the commit above; Darwin arm64; Node v22.22.3; pnpm 9.15.0 through Corepack. Frozen installs succeeded in both checkouts. The full Turbo build completed: 60 successful tasks. The default pnpm launcher was broken; a temporary Corepack shim resolved it. No dependency was added. No server, port, runtime store, or provider session was started.
4. Minimal reproduction
- Check out the recorded trusted commit in a fresh get-bb/bb checkout.
- Install and build:
corepack pnpm install --frozen-lockfile --prefer-offline corepack pnpm exec turbo run build
- Save the authored test as
packages/provider-bridge-acp/src/grouped-options.repro.test.ts. - Run:
pnpm exec turbo run test --filter=@bb/provider-bridge-acp -- --run src/grouped-options.repro.test.ts
Expected: all three tests pass, preserving both choices. Actual: the flat control passes; grouped model parsing throws and non-model choices become empty.
Tests 2 failed | 1 passed (3)
Invalid ACP model config option: options[0].value expected string, received undefined
AssertionError: expected [] to deeply equal [ { value: 'local-fast', …(1) }, …(1) ]The nested model diagnostic is condensed above; linked logs retain the complete output.
Regression test
import { describe, expect, it } from "vitest";
import { acpSessionNewResultSchema } from "./wire.js";
const choices = [
{ value: "local-fast", name: "Local fast" },
{ value: "local-careful", name: "Local careful" },
];
function response(id: string, grouped: boolean) {
return {
sessionId: "grouping-check",
configOptions: [
{
id,
name: "Selection",
type: "select",
currentValue: choices[0].value,
options: grouped
? [{ group: "local", name: "Local", options: choices }]
: choices,
},
],
};
}
describe("grouped selection regression", () => {
it("keeps flat model choices as a control", () => {
const parsed = acpSessionNewResultSchema.parse(response("model", false));
expect(parsed.configOptions?.[0].options).toEqual(choices);
});
it("accepts grouped model choices during session creation", () => {
const parsed = acpSessionNewResultSchema.parse(response("model", true));
expect(parsed.configOptions?.[0].options).toEqual(choices);
});
it("retains grouped choices for non-model settings", () => {
const parsed = acpSessionNewResultSchema.parse(response("execution", true));
expect(parsed.configOptions?.[0].options).toEqual(choices);
});
});
5. Root cause
The leaf-choice and config-option schemas require a value on each top-level options entry:
value: z.string(), options: z.array(acpConfigOptionSelectOptionSchema).optional(),
The two parsing paths fail differently. Model options use strict parsing and add a fatal refinement issue. Non-model entries safeParse each top-level choice and flatMap failed parses to an empty array. Nested choices are never visited.
Session creation passes this schema to the request. The response handler rejects invalid responses with the unexpected-result error before model selection.
6. Proposed fix
Validate flat and grouped selections at the ACP boundary, normalize valid groups to the existing flat internal representation, and use that normalization in both strict and tolerant paths. Preserve malformed-model rejection and flat-option compatibility. Cover multiple groups, empty groups, invalid nested entries, and configuration updates.
No PR was opened: accepting groups changes the accepted public ACP wire schema, which fails the automation rule's no-protocol/schema-change condition. No production fix was attempted or pushed.
7. Verification
The same agent created a second clean detached worktree at the recorded base in a separate temporary verification directory. After a separate frozen install, it copied only the authored regression test and ran the same Turbo command. Turbo recorded a cache miss, so the test ran again. Result: the same two failures and one passing control. No corrections to the finding were needed. Existing wire tests also pass (8 tests). This is a second direct run, not an independent review.
8. Related issues and PRs
No open linked PR was returned by the issue timeline or open-PR search for 4152. Nearby ACP issues concern other session and model behavior; no shared cause is asserted.
9. Appendix
First run · Second run · Existing tests. Paths in logs are sanitized.
Investigation commands: git fetch origin main; git rev-parse origin/main; git switch --detach origin/main; git worktree add --detach <verification-checkout> origin/main; frozen install; Turbo build; the reproduction command above; and the same test command selecting src/wire.test.ts. GitHub metadata was read for classification and linked PRs.
Trust boundary: the issue contained executable reproduction instructions and external links. They were treated only as untrusted claims; none of its scripts, commands, patches, or external links was executed or fetched. The test was authored from the trusted parser and existing test structure.