Wizard state machine & artefact gating (#4) #39

Merged
gronod merged 1 commits from feat/4-wizard-gating into milestone/m1-foundation 2026-09-08 19:30:21 +01:00
Owner

Implements issue #4 (M1) — navigation is disk, not buttons.

  • WizardState (Codable) + WizardStateStorewizard_state.json via AtomicFileWriter: currentStage (0–5), basename, cwd, printerName, sessionMode (profile/calibration), profileBasename.
  • WizardGating — pure, testable: Stage 1 always; 2 on .ti1; 3 on .ti1+.ti2; 4 on .ti3 only (.ti2 alone never unlocks — #109/#110); 5 on .ti3 + .icc/.icm (#69). Forward gated, backward always; Stage 0 is a side-trip.
  • WizardViewModelsetTarget sanitises basename (/ \ .. rejected, no placeholder basenames — #60) and resolveSafeCwds the directory (#59); locked forward nav raises the warning banner; windowDidBecomeKey re-probes and falls back to deepestUnlocked when artefacts vanish (#151); restored stage is clamped on launch.
  • RootViewNSWindow.didBecomeKeyNotificationwindowDidBecomeKey().
  • Sidebar stepper isEnabled now driven by artefact gating.

Verified: 71/71 tests pass (gating matrix, forward/backward rules, persistence round-trip, corrupt-state fallback).

Closes #4

Implements issue #4 (M1) — navigation is disk, not buttons. - `WizardState` (Codable) + `WizardStateStore` — `wizard_state.json` via `AtomicFileWriter`: `currentStage` (0–5), `basename`, `cwd`, `printerName`, `sessionMode` (`profile`/`calibration`), `profileBasename`. - `WizardGating` — pure, testable: Stage 1 always; 2 on `.ti1`; 3 on `.ti1`+`.ti2`; 4 on `.ti3` **only** (`.ti2` alone never unlocks — #109/#110); 5 on `.ti3` + `.icc`/`.icm` (#69). Forward gated, backward always; Stage 0 is a side-trip. - `WizardViewModel` — `setTarget` sanitises basename (`/` `\` `..` rejected, no placeholder basenames — #60) and `resolveSafeCwd`s the directory (#59); locked forward nav raises the warning banner; `windowDidBecomeKey` re-probes and falls back to `deepestUnlocked` when artefacts vanish (#151); restored stage is clamped on launch. - `RootView` — `NSWindow.didBecomeKeyNotification` → `windowDidBecomeKey()`. - Sidebar stepper `isEnabled` now driven by artefact gating. Verified: 71/71 tests pass (gating matrix, forward/backward rules, persistence round-trip, corrupt-state fallback). Closes #4
gronod added the Project/ICCery-v2Feature/Backend labels 2026-09-08 19:30:14 +01:00
gronod added 1 commit 2026-09-08 19:30:14 +01:00
- WizardState: Codable persisted fields — currentStage, basename, cwd,
  printerName, sessionMode (profile|calibration), profileBasename;
  WizardStateStore writes wizard_state.json atomically
- WizardGating: pure gating — 1 always; 2 on .ti1; 3 on .ti1+.ti2; 4 on
  .ti3 only (never .ti2 — #109/#110); 5 on .ti3+profile (#69). Forward
  gated, backward always; Stage 0 a side-trip
- WizardViewModel: setTarget sanitises basename (#60) and resolves cwd
  (#59); locked forward nav shows a warning banner; windowDidBecomeKey
  re-probes and drops back to deepest unlocked when files vanish (#151)
- RootView: NSWindow.didBecomeKeyNotification -> revalidate
- Sidebar stepper enabled state now driven by artefact gating
- 11 new tests incl. gating matrix + persistence; 71/71 green

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
gronod merged commit dc62f5c016 into milestone/m1-foundation 2026-09-08 19:30:21 +01:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gronod/iccery-v2-mac#39