ADR-029: The gateway wizard — six asks on the default path
Date: 2026-07-24 Status: accepted Issue: #895 (cross-repo: VytCepas/harbor#11, Harbor J1)
Context
The interactive wizard had grown to ~26 decision points (22 unconditional + up to 4 conditional on common paths). Each one carries an ADR-023 explainer, which made every question informed but the whole flow long: a user scaffolding a standard project answered two dozen prompts to accept two dozen defaults. The Harbor J1 review (2026-07-23) sized the collapse against the real prompt count and required: a short default path, every concern still reachable, contracts preserved (flags, descriptor fields, tier integers), and the security surface echoed, not silently defaulted.
Decision
The default interactive path asks exactly six questions: preset · name · description · language · the Customize gateway · bootstrap. (Target-Python remains conditional on a greenfield Python scaffold, as before.)
- Gateway groups. Every other concern belongs to one of seven groups (delivery & deploy · integrations · dev extras · docs & updates · profile & overlays · memory & lifecycle · project details). The gateway offers the groups once; opening a group runs its pre-collapse choosers unchanged, in the pre-collapse order — their names are stable seams (tests monkeypatch them) and their ADR-023 explainer panels are untouched.
- Unopened ⇒ the chooser's Enter default. A group left closed resolves every concern to exactly the value its chooser's Enter default produced pre-collapse. Flag precedence is unchanged (flag > preset > default); a flag also pins its concern and is annotated at the gateway. An invalid flag value force-opens its group — never crash, never silently drop.
- Informed consent moves to the preview. The full resolution — led by the
security surface (enforcement mode, egress posture, lifecycle gate, MCP
set, agent surfaces, governance) and annotated with compressed why/cost
digests of the choosers' panels — is rendered before the gateway, so
accepting the standard setup is a decision made looking at it. If an opened
group changes anything, the resolution is re-echoed before bootstrap.
safety.allowis noted as deny-by-default and hand-extendable; it is not presented as a wizard decision because the wizard cannot set it. - ADR-024 positioning survives preset pinning. All shipped presets pin
memory_stack, so the ladder chooser never runs on the default path; the echo therefore carries the tier and the positioning line (graph/RAG are opt-in rungs; they pay off at multi-project / monorepo scale). - Preset-seed-skips-prompt is generalized. The precedent (a
governedpreset pre-accepts the governance prompt) now covers preset-pinned memory and lifecycle on the default path.
What deliberately did not change
All 36 CLI flags and the ADR-023 concern/mechanical partition; every
_choose_*_interactive chooser and its explainer; _gather_inputs_interactive's
signature and the cli_overlays seeding contract; ScaffoldInputs; the
non-interactive path; review-cycles semantics (#714 / PR #717) including the
loud drop on lifecycle none; KeyboardInterrupt → exit 130; bootstrap as the
final question (#887).
Consequences
- An Enter-only run produces a
ScaffoldInputsidentical to the pre-collapse Enter-only run — pinned by an equivalence test (test_wizard_gateway.py). - Tests that inject a non-default answer by monkeypatching a group chooser must
also open that group (
_choose_gateway_interactiveis the mockable seam — deliberately not built on_prompt, so pre-collapse ordered answer iterators never feed it by accident). - ADR-023's explain-before-asking standard is met per concern inside opened groups; on the default path the annotated preview is the explanation surface.
- A first-time user sees one annotated table instead of ~20 sequential panels; the deep-dive explainers remain one gateway selection away.