Files
stack/docs/scratchpads/install-ux-v2-20260405.md
Jarvis d6117f1a4a
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
docs: scaffold install-ux-v2 mission + archive install-ux-hardening
Parent mission install-ux-hardening-20260405 shipped at mosaic-v0.0.25
but a real-run test uncovered a critical bootstrap regression plus a
cluster of first-run UX failings. New mission install-ux-v2-20260405
closes the regression as a hotfix (M01 → mosaic-v0.0.26), polishes
CORS/skill installer UX (M02), then rethinks the flow around a
drill-down main menu with a provider-first intent intake and real
quick-start (M03 → mosaic-v0.0.27).

Linked issues: #436 (M01), #437 (M02), #438 (M03)
Archives install-ux-hardening-20260405 under docs/archive/missions/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 16:23:51 -05:00

110 lines
6.7 KiB
Markdown

# Install UX v2 — Orchestrator Scratchpad
## Session 1 — 2026-04-05 (orchestrator scaffold)
### Trigger
Real-run testing of `@mosaicstack/mosaic@0.0.25` (fresh install of the release we just shipped from the parent mission `install-ux-hardening-20260405`) surfaced a critical regression and a cluster of UX failings. User feedback verbatim:
> The skill/additional feature installation section of install.sh is unsable
> The "quick-start" is asking way too many questions. This process should be much faster to get a quick start.
> The installater should have a main menu that allows for a drill-down install approach.
> "Plugins" — Install Recommended Plugins / Custom
> "Providers" — …
> The gateway port is not prefilling with 14242 for default
> What is the CORS origin for? Is that the webUI that isn't working yet? Maybe we should ask for the fqdn/hostname instead? There must be a better way to handle this.
Plus the critical bug, reproduced verbatim:
```
◇ Admin email
│ jason@woltje.com
Admin password (min 8 chars): ****************
Confirm password: ****************
▲ Bootstrap failed (400): {"message":["property email should not exist","property password should not exist"],"error":"Bad Request","statusCode":400}
✔ Wizard complete.
✔ Install manifest written: /home/jarvis/.config/mosaic/.install-manifest.json
✔ Done.
```
Note the `✔ Wizard complete` and `✔ Done` lines **after** the 400. That's a second bug — failure didn't propagate in interactive mode.
### Diagnosis — orchestrator pre-scope
To avoid handing workers a vague prompt, pre-identified the concrete fix sites:
**Bug 1 (critical) — DTO class erasure.** `apps/gateway/src/admin/bootstrap.controller.ts:16`:
```ts
import type { BootstrapSetupDto, BootstrapStatusDto, BootstrapResultDto } from './bootstrap.dto.js';
```
`import type` erases the class at runtime. `@Body() dto: BootstrapSetupDto` then has no runtime metatype — `design:paramtypes` reflects `Object`. Nest's `ValidationPipe` with `whitelist: true` + `forbidNonWhitelisted: true` receives a plain Object metatype, treats every incoming property as non-whitelisted, and 400s with `"property email should not exist", "property password should not exist"`.
**One-character fix:** drop the `type` keyword on the `BootstrapSetupDto` import. `BootstrapStatusDto` and `BootstrapResultDto` are fine as type-only imports because they're used only in return type positions, not as `@Body()` metatypes.
Must be covered by an **integration test that binds through Nest**, not a controller unit test that imports the DTO directly — the unit test path would pass even with `import type` because it constructs the pipe manually. An e2e test with `@nestjs/testing` + `supertest` against the real `/api/bootstrap/setup` endpoint is the right guard.
**Bug 2 — interactive silent failure.** `packages/mosaic/src/wizard.ts:147-150`:
```ts
if (!bootstrapResult.completed && headlessRun) {
prompter.warn('Admin bootstrap failed in headless mode — aborting wizard.');
process.exit(1);
}
```
The guard is `&& headlessRun`. In interactive mode, `completed: false` is silently swallowed and the wizard continues to the success lines. Fix: propagate failure in both modes. Decision for the worker — either `throw` or `process.exit(1)` with a clear error.
**Bug 3 — port prefill.** `packages/mosaic/src/stages/gateway-config.ts:77-88`:
```ts
const raw = await p.text({
message: 'Gateway port',
defaultValue: defaultPort.toString(),
...
});
```
The stage is passing `defaultValue`. Either the `WizardPrompter.text` adapter is dropping it, or the underlying `@clack/prompts` call expects `initialValue` (which actually prefills the buffer) vs `defaultValue` (which is used only if the user submits an empty string). Worker should verify the adapter and likely switch to `initialValue` semantics so the user sees `14242` in the field.
**Bug 4 — Pi SDK copy gap.** The `"What is Mosaic?"` intro text enumerates Claude Code, Codex, and OpenCode but never mentions Pi SDK, which is the actual agent runtime behind those frontends. Purely a copy edit — find the string, add Pi SDK.
### Mission shape
Three milestones, three tracks, different tiers:
1. **IUV-M01 Hotfix** (sonnet) — the four bugs above + release `mosaic-v0.0.26`. Small, fast, unblocks the 0.0.25 happy path.
2. **IUV-M02 UX polish** (sonnet) — CORS origin → FQDN/hostname abstraction; diagnose and rework the skill installer section. Diagnostic-heavy.
3. **IUV-M03 Provider-first intelligent flow** (opus) — the big one: drill-down main menu, Quick Start path that's actually quick, provider-first natural-language intake with agent self-naming (OpenClaw-style). Architectural.
Sequencing: strict. M01 ships first as a hotfix release (mosaic-v0.0.26). M02 is diagnostic-heavy and can share groundwork with M03 but ships separately for clean release notes. M03 is the architectural anchor and lands last as `mosaic-v0.0.27`.
### Open design questions (to be resolved by workers, not pre-decided)
- M01: does `process.exit(1)` vs `throw` matter for how `tools/install.sh` surfaces the error? Worker should check the install.sh call site and pick the behavior that surfaces cleanly.
- M03: what LLM call powers the intent intake, and what's the offline fallback? Options: (a) reuse the provider the user is configuring (chicken-and-egg — provider setup hasn't happened yet), (b) a bundled deterministic "advisor" that hard-codes common intents, (c) require a provider key up-front before intake. Design doc (IUV-03-01) must resolve.
- M03: is the "agent self-naming" persistent across all future `mosaic` invocations, or a per-session nickname? Probably persistent — lives in `~/.config/mosaic/agent.json` or similar. Worker to decide + document.
### Non-goals for this mission
- No GUI / web UI
- No registry / pipeline migration
- No multi-user / multi-tenant onboarding
- No rework of `mosaic uninstall` (stable from parent mission)
### Known tooling caveats (carry forward from parent mission)
- `issue-create.sh` / `pr-create.sh` wrappers have an `eval` bug with multiline bodies — use Gitea REST API fallback with `load_credentials gitea-mosaicstack`
- `pr-ci-wait.sh` reports `state=unknown` against Woodpecker (combined-status endpoint gap) — use `tea pr` glyphs or poll the commit status endpoint directly
- Protected `main`, squash-merge only, PR-required
- CI queue guard before push/merge: `~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push|merge`
### Next action
1. Create Gitea issues for M01, M02, M03
2. Open the mission-scaffold docs PR (same pattern as parent mission's PR #430)
3. After merge, delegate IUV-M01 to a sonnet subagent in an isolated worktree with the concrete fix-site pointers above