# Picker Contract (cross-platform single-choice abstraction)

> Native `AskUserQuestion` is a Claude-Code primitive; Copilot CLI has no
> agent-invokable choice picker. This contract defines ONE abstract "ask the
> user to choose" primitive that each supported CLI renders to the best
> mechanism it offers.

## The abstract primitive

`ask_choice(question, options[], { header?, default?, allowFreeText? })`

- `question` - rendered in `outputLanguage`.
- `options[]` - each `{ label (English), description (outputLanguage) }`.
- `default` - recommended option (label or 1-based index); used by autopilot / non-interactive runs.
- `allowFreeText` - when true, a free-text answer is accepted as an extra channel (e.g. the plan-gate's edit request).

Returns the selected option label (or the free-text string when `allowFreeText` and the user typed instead of choosing).

## Step narration (breadcrumb)

A picker chain (account → repo → maturity → dev-context, or the analysis Phase 0 chain) must tell the user where they are. Before each `ask_choice` / `AskUserQuestion` in a multi-step chain, emit ONE narrator line:

`<localized: "Step <i>/<n>: <what this step decides>">`

- `<i>` is the 1-based position, `<n>` the total steps the active flow will run (known up front: e.g. an analysis run with one platform is 5 steps; a Jira-ID input is 3). When the count is genuinely unknown, omit `/<n>` and print `Step <i>: ...`.
- The line renders in `outputLanguage` (it is conversational copy, like `question`). Turkish: `Adim 2/5: hesap secimi`. English: `Step 2/5: account selection`.
- Auto-selected / skipped steps (single account, local-only flow) still print their breadcrumb with the resolution noted (`Step 1/5: account selection (auto: <label>)`), so the chain reads continuously instead of jumping numbers.
- It is a narrator line above the picker, never part of the `question`, `label`, or `header`.

This is the surface that makes the native picker show, step by step, what it is doing. Every picker file (`_account-picker.md`, `_repo-picker.md`, `_dev-context.md`) and the analysis Phase 0 chain reference this contract.

## Per-platform rendering (degradation ladder)

| Platform | Render |
|---|---|
| **Claude Code** | native `AskUserQuestion` (question/description in `outputLanguage`, label English); free-text via the built-in **Other** field |
| **Copilot CLI** | `pipeline/lib/ask-choice.sh` (numbered menu); a future MCP elicitation tool can replace this once host support is broad |

## Universal fallback: `pipeline/lib/ask-choice.sh`

A zero-dependency numbered-menu picker for any shell-capable runtime:

```bash
choice=$(pipeline/lib/ask-choice.sh "Do you approve this plan?" "Approve" "Cancel" "Edit")
```

- Prints the menu + prompt on **stderr**; echoes ONLY the chosen label on **stdout**.
- `ASK_CHOICE_DEFAULT=<label|index>` selects without prompting (autopilot / CI).
- No TTY + no default -> picks the first option and never blocks an automated run.

Enforced by `smoke-ask-choice.sh`.

## Autopilot / non-interactive contract

In autopilot, `ask_choice` resolves to `default` (or the safe first option) without prompting - identical to how the native gates auto-proceed today. A picker is only surfaced for genuinely ambiguous or destructive decisions, matching the maturity-check model.

## Deterministic gates note

Claude Code's `PreToolUse` exit-2 hooks are the HARD blocking gates. Two ship, both needing no run-specific arguments so they are naturally hookable: (1) `pre-commit-check.sh` scans the staged diff on every `git commit` and blocks on a detected secret; (2) `agent-guard.sh` runs on `git commit` + `git push` and blocks AI/assistant attribution in a commit message and force-push to a protected branch (main/master/develop). Both are self-contained, fail-open on internal error, and never execute the inspected command. The recommended hook block ships at `install/templates/claude-hooks.json`; `multi-agent:setup` offers to merge it into `~/.claude/settings.json`. The other deterministic gates (evidence, consensus, intent, learnings) are invoked by the pipeline phases with per-run arguments (a build-log path, the triage JSON, the free-text input), so they are phase-enforced by contract, not OS-hookable.

Copilot CLI has no `PreToolUse` equivalent, so the secret scan there is workflow-enforced (run as a phase step, not OS-blocked) plus a CI smoke-gate step.
