331 lines
22 KiB
Markdown
331 lines
22 KiB
Markdown
# Install UX Hardening — IUH-M01 Session Notes
|
|
|
|
## Session: 2026-04-05 (agent-ad6b6696)
|
|
|
|
### Plan
|
|
|
|
**Manifest schema decision:**
|
|
|
|
- Version 1 JSON at `~/.config/mosaic/.install-manifest.json` (mode 0600)
|
|
- Written by `tools/install.sh` after successful install
|
|
- Fields: version, installedAt, cliVersion, frameworkVersion, mutations{directories, npmGlobalPackages, npmrcLines, shellProfileEdits, runtimeAssetCopies}
|
|
- Uninstall reads it; if missing → heuristic mode (warn user)
|
|
|
|
**File list:**
|
|
|
|
- NEW: `packages/mosaic/src/runtime/install-manifest.ts` — read/write helpers + types
|
|
- NEW: `packages/mosaic/src/runtime/install-manifest.spec.ts` — unit tests
|
|
- NEW: `packages/mosaic/src/commands/uninstall.ts` — command implementation
|
|
- NEW: `packages/mosaic/src/commands/uninstall.spec.ts` — unit tests
|
|
- MOD: `packages/mosaic/src/cli.ts` — register `uninstall` command
|
|
- MOD: `tools/install.sh` — write manifest on success + add `--uninstall` path
|
|
|
|
**Runtime asset list (from mosaic-link-runtime-assets / framework/install.sh):**
|
|
|
|
- `~/.claude/CLAUDE.md` (source: `$MOSAIC_HOME/runtime/claude/CLAUDE.md`)
|
|
- `~/.claude/settings.json` (source: `$MOSAIC_HOME/runtime/claude/settings.json`)
|
|
- `~/.claude/hooks-config.json` (source: `$MOSAIC_HOME/runtime/claude/hooks-config.json`)
|
|
- `~/.claude/context7-integration.md` (source: `$MOSAIC_HOME/runtime/claude/context7-integration.md`)
|
|
- `~/.config/opencode/AGENTS.md` (source: `$MOSAIC_HOME/runtime/opencode/AGENTS.md`)
|
|
- `~/.codex/instructions.md` (source: `$MOSAIC_HOME/runtime/codex/instructions.md`)
|
|
|
|
**Reversal logic:**
|
|
|
|
1. If `.mosaic-bak-<stamp>` exists for a file → restore it
|
|
2. Else if managed copy exists → remove it
|
|
3. Never touch files not in the known list
|
|
|
|
**npmrc reversal:**
|
|
|
|
- Only remove line `@mosaicstack:registry=https://git.mosaicstack.dev/api/packages/mosaicstack/npm/`
|
|
- If manifest has the line, use that as authoritative; else check heuristically
|
|
|
|
**PATH reversal:**
|
|
|
|
- Check install.sh: it does NOT add PATH entries to shell profiles (framework/install.sh migration removes old `$MOSAIC_HOME/bin` PATH entries in v0/v1→v2 migration, but new install does NOT add PATH)
|
|
- ASSUMPTION: No PATH edits in current install (v0.0.24+). Shell profiles not modified by current install.
|
|
- The `$PREFIX/bin` is mentioned in a warning but NOT added to shell profiles by install.sh.
|
|
- shellProfileEdits array will be empty for new installs; heuristic mode also skips it.
|
|
|
|
**Test strategy:**
|
|
|
|
- Unit test manifest read/write with temp dir mocking
|
|
- Unit test command registration
|
|
- Unit test dry-run flag (no actual fs mutations)
|
|
- Unit test --keep-data skips protected paths
|
|
- Unit test heuristic mode warning
|
|
|
|
**Implementation order:**
|
|
|
|
1. install-manifest.ts helpers
|
|
2. install-manifest.spec.ts tests
|
|
3. uninstall.ts command
|
|
4. uninstall.spec.ts tests
|
|
5. cli.ts registration
|
|
6. tools/install.sh manifest writing + --uninstall path
|
|
|
|
ASSUMPTION: No PATH modifications in current install.sh (v0.0.24). Framework v0/v1→v2 migration cleaned old PATH entries but current install does not add new ones.
|
|
ASSUMPTION: `--uninstall` in install.sh handles framework + cli + npmrc only; gateway teardown deferred to `mosaic gateway uninstall`.
|
|
ASSUMPTION: Pi settings.json edits (skills paths) added by framework/install.sh are NOT reversed in this iteration — too risky to touch user Pi config without manifest evidence. Noted as follow-up.
|
|
|
|
---
|
|
|
|
## Session 2 — 2026-04-05 (orchestrator resume)
|
|
|
|
### IUH-M01 completion summary
|
|
|
|
- **PR:** #429 merged as `25cada77`
|
|
- **CI:** green (Woodpecker)
|
|
- **Issue:** #425 closed
|
|
- **Files:** +1205 lines across 4 new + 2 modified + 1 docs
|
|
- **Tests:** 14 new, 170 total passing
|
|
|
|
### Follow-ups captured from worker report
|
|
|
|
1. **Pi settings.json reversal deferred** — worker flagged as too risky without manifest evidence. Future IUH task should add manifest entries for Pi settings mutations. Not blocking M02/M03.
|
|
2. **Pre-existing `cli-smoke.spec.ts` failure** — `@mosaicstack/brain` package entry resolution fails in Vitest. Unrelated to IUH-M01. Worth a separate issue later.
|
|
3. **`pr-create.sh` wrapper bug with multiline bodies** — wrapper evals body args as shell when they contain newlines/paths. Worker fell back to Gitea REST API. Same class of bug I hit earlier with `issue-create.sh`. Worth a tooling-team issue to fix both wrappers.
|
|
|
|
### Mission doc sync
|
|
|
|
cli-unification docs that were archived before the M01 subagent ran did not travel into the M01 PR (they were local, stashed before pull). Re-applying now:
|
|
|
|
- `docs/archive/missions/cli-unification-20260404/` (the old manifest + tasks)
|
|
- `docs/MISSION-MANIFEST.md` (new install-ux-hardening content)
|
|
- `docs/TASKS.md` (new install-ux-hardening content)
|
|
|
|
Committing as `docs: scaffold install-ux-hardening mission + archive cli-unification`.
|
|
|
|
### Next action
|
|
|
|
Delegate IUH-M02 to a sonnet subagent in an isolated worktree.
|
|
|
|
---
|
|
|
|
## Session 3: 2026-04-05 (agent-a6ff34a5) — IUH-M02 Wizard Remediation
|
|
|
|
### Plan
|
|
|
|
**AC-3: Password masking + confirmation**
|
|
|
|
- New `packages/mosaic/src/prompter/masked-prompt.ts` — raw-mode stdin reader that suppresses echo, handles backspace/Ctrl+C/Enter.
|
|
- `bootstrapFirstUser` in `packages/mosaic/src/commands/gateway/install.ts`: replace `rl.question('Admin password...')` with `promptMaskedPassword()`, require confirm pass, keep min-8 validation.
|
|
- Headless path: when `MOSAIC_ASSUME_YES=1` or `!process.stdin.isTTY`, read `MOSAIC_ADMIN_PASSWORD` env var directly.
|
|
|
|
**AC-4a: Hooks preview stage**
|
|
|
|
- New `packages/mosaic/src/stages/hooks-preview.ts` — reads `hooks-config.json` from `state.sourceDir` or `state.mosaicHome`, displays each top-level hook category with name/trigger/command preview, prompts "Install these hooks? [Y/n]", stores result in `state.hooks`.
|
|
- `packages/mosaic/src/types.ts` — add `hooks?: { accepted: boolean; acceptedAt?: string }` to `WizardState`.
|
|
- `packages/mosaic/src/wizard.ts` — insert `hooksPreviewStage` between `runtimeSetupStage` and `skillsSelectStage`; skip if no claude runtime detected.
|
|
|
|
**AC-4b: `mosaic config hooks` subcommands**
|
|
|
|
- Add `hooks` subcommand group to `packages/mosaic/src/commands/config.ts`:
|
|
- `list`: reads `~/.claude/hooks-config.json`, shows hook names and enabled/disabled status
|
|
- `disable <name>`: prefixes matching hook key with `_disabled_` in the JSON
|
|
- `enable <name>`: removes `_disabled_` prefix if present
|
|
|
|
**AC-5: Headless install path**
|
|
|
|
- `runConfigWizard`: detect headless mode (`MOSAIC_ASSUME_YES=1` or `!process.stdin.isTTY`), read env vars with defaults, validate required vars, skip prompts entirely.
|
|
- `bootstrapFirstUser`: detect headless mode, read `MOSAIC_ADMIN_NAME/EMAIL/PASSWORD`, validate, proceed without prompts.
|
|
- Document env vars in `packages/mosaic/README.md` (create if absent).
|
|
|
|
### File list
|
|
|
|
NEW:
|
|
|
|
- `packages/mosaic/src/prompter/masked-prompt.ts`
|
|
- `packages/mosaic/src/prompter/masked-prompt.spec.ts`
|
|
- `packages/mosaic/src/stages/hooks-preview.ts`
|
|
- `packages/mosaic/src/stages/hooks-preview.spec.ts`
|
|
|
|
MODIFIED:
|
|
|
|
- `packages/mosaic/src/types.ts` — extend WizardState
|
|
- `packages/mosaic/src/wizard.ts` — wire hooksPreviewStage
|
|
- `packages/mosaic/src/commands/gateway/install.ts` — masked password + headless path
|
|
- `packages/mosaic/src/commands/config.ts` — add hooks subcommands
|
|
- `packages/mosaic/src/commands/config.spec.ts` — extend tests
|
|
- `packages/mosaic/README.md` — document env vars
|
|
|
|
### Assumptions
|
|
|
|
ASSUMPTION: `hooks-config.json` location is `<sourceDir>/framework/runtime/claude/hooks-config.json` during wizard (sourceDir is package root). Fall back to `<mosaicHome>/runtime/claude/hooks-config.json` for installed config.
|
|
ASSUMPTION: The `hooks` subcommands under `config` operate on `~/.claude/hooks-config.json` (the installed copy), not the package source.
|
|
ASSUMPTION: For the hooks preview stage, the "name" field displayed per hook entry is the top-level event key (e.g. "PostToolUse") plus the matcher from nested hooks array. This is the most user-readable representation given the hooks-config.json structure.
|
|
ASSUMPTION: `config hooks list/enable/disable` use `CLAUDE_HOME` env or `~/.claude` as the target directory for hooks files.
|
|
ASSUMPTION: The headless TTY detection (`!process.stdin.isTTY`) is sufficient; `MOSAIC_ASSUME_YES=1` is an explicit override for cases where stdin is a TTY but the user still wants non-interactive (e.g., scripted installs with piped terminal).
|
|
|
|
---
|
|
|
|
## Session 4 — 2026-04-05 (orchestrator resume) — IUH-M02 closed, delegating IUH-M03
|
|
|
|
### IUH-M02 completion summary
|
|
|
|
- **PR:** #431 merged as `cd8b1f66`
|
|
- **CI:** green (Woodpecker)
|
|
- **Issue:** #426 closed
|
|
- **Acceptance criteria:** AC-3 (password mask), AC-4 (hooks visibility — consent recorded), AC-5 (headless path) all satisfied
|
|
- **New files:** `prompter/masked-prompt.ts`, `stages/hooks-preview.ts` (+ specs)
|
|
- **Modified:** `wizard.ts`, `types.ts` (`state.hooks`), `commands/gateway/install.ts`, `commands/config.ts`
|
|
|
|
### Follow-up captured from M02 agent
|
|
|
|
**Hooks consent is recorded but not enforced.** The `hooks-preview` stage sets `state.hooks.accepted` when the user confirms, but the finalize stage still unconditionally runs `mosaic-link-runtime-assets`, which copies `hooks-config.json` into `~/.claude/` regardless of consent. This is a soft gap — the user sees the prompt and can decline, but declining currently has no effect downstream.
|
|
|
|
Options for addressing:
|
|
|
|
- Fold into IUH-M03 (since M03 touches the finalize/install convergence path anyway)
|
|
- Spin a separate small follow-up issue after M03 lands
|
|
|
|
Leaning toward folding into M03 — the unified first-run flow naturally reworks the finalize→gateway handoff where this gating belongs.
|
|
|
|
### IUH-M03 delegation
|
|
|
|
Now delegating to an **opus** subagent in an isolated worktree. Scope from `/tmp/iuh-m03-body.md`:
|
|
|
|
- Extract `runConfigWizard` → `stages/gateway-config.ts`
|
|
- Extract `bootstrapFirstUser` → `stages/gateway-bootstrap.ts`
|
|
- `runWizard` invokes gateway stages as final stages
|
|
- Drop the 10-minute `$XDG_RUNTIME_DIR/mosaic-install-state.json` session bridge
|
|
- `mosaic gateway install` becomes a thin standalone wrapper for backward-compat
|
|
- `tools/install.sh` single auto-launch entry point
|
|
- **Bonus if scoped:** honor `state.hooks.accepted` in finalize stage so declining hooks actually skips hook install
|
|
|
|
Known tooling caveats to pass to worker:
|
|
|
|
- `issue-create.sh` / `pr-create.sh` wrappers eval multiline bodies as shell — use Gitea REST API fallback with `load_credentials gitea-mosaicstack`
|
|
- Protected `main`: PR-only, squash merge
|
|
- Must run `ci-queue-wait.sh --purpose push|merge` before push/merge
|
|
|
|
---
|
|
|
|
## Session 5: 2026-04-05 (agent-a7875fbd) — IUH-M03 Unified First-Run
|
|
|
|
### Problem recap
|
|
|
|
`mosaic wizard` and `mosaic gateway install` currently run as two separate phases bridged by a fragile 10-minute session file at `$XDG_RUNTIME_DIR/mosaic-install-state.json`. `tools/install.sh` auto-launches both sequentially so the user perceives two wizards stitched together; state is not shared, prompts are duplicated, and if the user walks away the bridge expires.
|
|
|
|
### Design decision — Option A: gateway install becomes terminal stages of `runWizard`
|
|
|
|
Two options on the table:
|
|
|
|
- (A) Extract `runConfigWizard` and `bootstrapFirstUser` into `stages/gateway-config.ts` and `stages/gateway-bootstrap.ts`, append them to `runWizard` as final stages, and make `mosaic gateway install` a thin wrapper that runs the same stages with an ephemeral state seeded from existing config.
|
|
- (B) Introduce a new top-level orchestrator that composes the wizard and gateway install as siblings.
|
|
|
|
**Chosen: Option A.** Rationale:
|
|
|
|
1. The wizard already owns a `WizardState` that threads state across stages — gateway config/bootstrap fit naturally as additional stages without a new orchestration layer.
|
|
2. `mosaic gateway install` as standalone entry point stays idempotent by seeding a minimal `WizardState` and running only the gateway stages, reusing the same functions.
|
|
3. Avoids a parallel state object and keeps the call graph linear; easier to test and to reason about the "one cohesive flow" UX goal.
|
|
4. Option B would leave `runWizard` and the gateway install as siblings that still need to share a state object — equivalent complexity without the narrative simplification.
|
|
|
|
### Scope
|
|
|
|
1. Extend `WizardState` with optional `gateway` slice: `{ tier, port, databaseUrl?, valkeyUrl?, anthropicKey?, corsOrigin, admin?: { name, email, password } }`. The admin password is held in memory only — never persisted to disk as part of the state object.
|
|
2. New `packages/mosaic/src/stages/gateway-config.ts` — pure stage that:
|
|
- Reads existing `.env`/`mosaic.config.json` if present (resume path) and sets state.
|
|
- Otherwise prompts via `WizardPrompter` (interactive) or reads env vars (headless).
|
|
- Writes `.env` and `mosaic.config.json`, starts the daemon, waits for health.
|
|
3. New `packages/mosaic/src/stages/gateway-bootstrap.ts` — pure stage that:
|
|
- Checks `/api/bootstrap/status`.
|
|
- If needsSetup, prompts for admin name/email/password (uses `promptMaskedConfirmed`) or reads env vars (headless); calls `/api/bootstrap/setup`; persists token in meta.
|
|
- If already setup, handles inline token recovery exactly as today.
|
|
4. `packages/mosaic/src/wizard.ts` — append gateway-config and gateway-bootstrap as stages 11 and 12. Remove `writeInstallState` and the `INSTALL_STATE_FILE` constant entirely.
|
|
5. `packages/mosaic/src/commands/gateway/install.ts` — becomes a thin wrapper that builds a minimal `WizardState` with a `ClackPrompter`, then calls `runGatewayConfigStage(...)` and `runGatewayBootstrapStage(...)` directly. Remove the session-file readers/writers. Headless detection is delegated to the stage itself. The wrapper still exposes the `runInstall({host, port, skipInstall})` API so `gateway.ts` command registration is unchanged.
|
|
6. `tools/install.sh` — drop the second `mosaic gateway install` call; `mosaic wizard` now covers end-to-end. Leave `gateway install` guidance for non-auto-launch path so users still know the standalone entry point exists.
|
|
7. **Hooks gating (bonus — folded in):** `finalize.ts` already runs `mosaic-link-runtime-assets`. When `state.hooks?.accepted === false`, set `MOSAIC_SKIP_CLAUDE_HOOKS=1` in the env for the subprocess; teach the script to skip copying `hooks-config.json` when that env var is set. Other runtime assets (CLAUDE.md, settings.json, context7) still get linked.
|
|
|
|
### Files
|
|
|
|
NEW:
|
|
|
|
- `packages/mosaic/src/stages/gateway-config.ts` (+ `.spec.ts`)
|
|
- `packages/mosaic/src/stages/gateway-bootstrap.ts` (+ `.spec.ts`)
|
|
|
|
MODIFIED:
|
|
|
|
- `packages/mosaic/src/types.ts` — extend WizardState with `gateway?:` slice
|
|
- `packages/mosaic/src/wizard.ts` — append gateway stages, remove session-file bridge
|
|
- `packages/mosaic/src/commands/gateway/install.ts` — thin wrapper over stages, remove 10-min bridge
|
|
- `packages/mosaic/src/stages/finalize.ts` — honor `state.hooks.accepted === false` by setting `MOSAIC_SKIP_CLAUDE_HOOKS=1`
|
|
- `packages/mosaic/framework/tools/_scripts/mosaic-link-runtime-assets` — honor `MOSAIC_SKIP_CLAUDE_HOOKS=1`
|
|
- `tools/install.sh` — single unified auto-launch
|
|
|
|
### Assumptions
|
|
|
|
ASSUMPTION: Gateway stages must run **after** `finalizeStage` because finalize writes identity files and links runtime assets that the gateway admin UX may later display — reversed ordering would leave Claude runtime linkage incomplete when the admin token banner prints.
|
|
ASSUMPTION: Standalone `mosaic gateway install` uses a `ClackPrompter` (interactive) by default; the headless path is still triggered by `MOSAIC_ASSUME_YES=1` or non-TTY stdin, and the stage functions detect this internally.
|
|
ASSUMPTION: When `runWizard` reaches the gateway stages, `state.mosaicHome` is authoritative for GATEWAY_HOME resolution if it differs from the default — we set `process.env.MOSAIC_GATEWAY_HOME` before importing gateway modules so the constants resolve correctly.
|
|
ASSUMPTION: Keeping backwards compatibility for `runInstall({host, port, skipInstall})` is enough — no other internal caller exists.
|
|
ASSUMPTION: Removing the session file is safe because the old bridge is at most a 10-minute window; there is no on-disk migration to do.
|
|
|
|
### Test plan
|
|
|
|
- `gateway-config.spec.ts`: fresh install writes .env + mosaic.config.json (mock fs + prompter); resume path reuses existing BETTER_AUTH_SECRET; headless path respects MOSAIC_STORAGE_TIER/MOSAIC_GATEWAY_PORT/etc.
|
|
- `gateway-bootstrap.spec.ts`: calls `/api/bootstrap/setup` with collected creds (mock fetch); handles "already setup" branch; honors headless env vars; persists token via `writeMeta`.
|
|
- Extend existing passing tests — no regressions in `login.spec`, `recover-token.spec`, `rotate-token.spec`.
|
|
- Unified flow integration is covered at the stage-level; no new e2e test infra required.
|
|
|
|
### Delivery cycle
|
|
|
|
plan (this entry) → code → typecheck/lint/format → test → codex review (`~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted`) → remediate → commit → ci-queue-wait push → push → PR → CI green → merge → close #427.
|
|
|
|
### Remediation log (codex review rounds)
|
|
|
|
- **Round 1** — hooks opt-out did not remove an existing managed file; port override ignored on resume; headless errors swallowed. Fixed: hooks cleanup, `portOverride` honored, errors re-thrown.
|
|
- **Round 2** — headless stage failures exited 0; port override on decline-rerun mismatched; no default-path integration test. Fixed: `process.exit(1)` in headless, revert portOverride on decline, add `unified-wizard.test.ts`.
|
|
- **Round 3** — hooks removal too broad (would touch user-owned files); port override written to meta but not .env (drift); wizard swallowed errors. Fixed: `cmp -s` managed-file check, force regeneration when portOverride differs from saved port, re-throw unexpected errors.
|
|
- **Round 4** — port-override regeneration tripped the corrupt-partial-state guard (blocker); headless already-bootstrapped-with-no-local-token path reported failure instead of no-op; hooks byte-equality fragile across template updates. Fixed: introduce `forcePortRegen` flag bypassing the guard (with a dedicated spec test), headless rerun of already-bootstrapped gateway now returns `{ completed: true }` (with spec coverage), hooks cleanup now checks for a stable `"mosaic-managed": true` marker embedded in the template (byte-equality remains as a fallback for legacy installs).
|
|
- Round 5 codex review attempted but blocked by upstream usage limit (quota). Rerun after quota refresh if further findings appear; all round-4 findings are code-covered.
|
|
|
|
---
|
|
|
|
## Session 6 — 2026-04-05 (orchestrator close-out) — MISSION COMPLETE
|
|
|
|
### IUH-M03 completion summary (reported by opus delivery agent)
|
|
|
|
- **PR:** #433 merged as `732f8a49`
|
|
- **CI:** Woodpecker green on final rebased commit `f3d5ef8d`
|
|
- **Issue:** #427 closed with summary comment
|
|
- **Tests:** 219 passing (+15 net new), 24 files
|
|
- **Codex review:** 4 rounds applied and remediated; round 5 blocked by upstream quota — no known outstanding findings
|
|
|
|
### What shipped in M03
|
|
|
|
- NEW stages: `stages/gateway-config.ts`, `stages/gateway-bootstrap.ts` (extracted from the old monolithic `gateway/install.ts`)
|
|
- NEW integration test: `__tests__/integration/unified-wizard.test.ts`
|
|
- `runWizard` now has 12 stages — gateway config + bootstrap are terminal stages 11 & 12
|
|
- 10-minute `$XDG_RUNTIME_DIR/mosaic-install-state.json` session-file bridge **deleted**
|
|
- `mosaic gateway install` rewritten as a thin standalone wrapper invoking the same two stages — backward-compat preserved
|
|
- `WizardState.gateway?` slice carries host/port/tier/admin/adminTokenIssued across stages
|
|
- `tools/install.sh` single unified `mosaic wizard` call — no more two-phase launch
|
|
- **Bonus scoped in:** finalize stage honors `state.hooks.accepted === false` via `MOSAIC_SKIP_CLAUDE_HOOKS=1`; `mosaic-link-runtime-assets` honors the flag; Mosaic-managed detection now uses a stable `"mosaic-managed": true` marker in `hooks-config.json` with byte-equality fallback for legacy installs. **Closes the M02 follow-up.**
|
|
|
|
### Mission status — ALL DONE
|
|
|
|
| AC | Status | PR |
|
|
| ---- | ------ | ---------------------------------------------------- |
|
|
| AC-1 | ✓ | #429 |
|
|
| AC-2 | ✓ | #429 |
|
|
| AC-3 | ✓ | #431 |
|
|
| AC-4 | ✓ | #431 + #433 (gating) |
|
|
| AC-5 | ✓ | #431 |
|
|
| AC-6 | ✓ | #433 |
|
|
| AC-7 | ✓ | #429, #431, #433 all merged, CI green, issues closed |
|
|
|
|
### Follow-ups for future work (not blocking mission close)
|
|
|
|
1. **`pr-ci-wait.sh` vs Woodpecker**: wrapper reports `state=unknown` because Woodpecker doesn't publish to Gitea's combined-status endpoint. Worker used `tea pr` CI glyphs as authoritative. Pre-existing tooling gap — worth a separate tooling-team issue.
|
|
2. **`issue-create.sh` / `pr-create.sh` wrapper `eval` bug with multiline bodies** — hit by M01, M02, M03 workers. All fell back to Gitea REST API. Needs wrapper fix.
|
|
3. **Codex review round 5** — attempted but blocked by upstream quota. Rerun after quota resets to confirm nothing else surfaces.
|
|
4. **Pi settings.json reversal** — deferred from M01; install manifest schema should be extended to track Pi settings mutations for reversal.
|
|
5. **`cli-smoke.spec.ts` pre-existing failure** — `@mosaicstack/brain` resolution in Vitest. Unrelated. Worth a separate issue.
|
|
|
|
### Next steps (orchestrator)
|
|
|
|
1. This scratchpad + MISSION-MANIFEST.md + TASKS.md updates → final docs PR
|
|
2. After merge: create release tag per framework rule (milestone/mission completion = release tag + repository release)
|
|
3. Archive mission docs under `docs/archive/missions/install-ux-hardening-20260405/` once the tag is published
|