Files
Ready2Blob/.planning/RETROSPECTIVE.md
T
kawaandClaude Sonnet 4.6 a26608791f chore: complete v1.3 milestone — Backend Expansion
Archives v1.3 milestone (Phase 13: 18 rclone backends), updates PROJECT.md
with all 30 validated requirements, reorganizes ROADMAP.md with all 4 milestones
in collapsed details blocks, adds v1.2 and v1.3 retrospective entries.

Also commits minor CSS token fixes in src/ (text-on-surface-container class
alignment) that were uncommitted from phase 13 execution.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 16:54:45 +02:00

11 KiB
Raw Blame History

Project Retrospective

A living document updated after each milestone. Lessons feed forward into future planning.

Milestone: v1.0 — MVP

Shipped: 2026-03-27 Phases: 4 | Plans: 18 | Timeline: 2 days

What Was Built

  • Backend Schema Registry as single source of truth driving form rendering, Zod validation, and config generation for 3 backends
  • 4 pure generator functions (rclone.conf, Intune install/detection, RMM script) with 61 tests and strict PowerShell encoding/path requirements
  • 4-step multi-step wizard (backend selection → config → deployment options → review/download) with full back-navigation
  • Review and download layer: live preview, security gate, individual file downloads, ZIP bundle, clipboard copy

What Worked

  • Inside-out build order (foundation → generators → wizard UI → download layer): front-loaded high-risk correctness work (PowerShell encoding, SYSTEM-context paths, credential handling) before any UI existed — each phase was independently verifiable
  • Wave 0 TDD with expect.fail stubs: defining the API contract in RED before implementation forced explicit interface design and caught integration issues upfront; named failure messages made debugging fast
  • BACKEND_REGISTRY as single source of truth: zero field-name duplication between form rendering, Zod schemas, and config generation — no drift possible across phases
  • useReducer + Context API with no external state lib: sufficient for a 4-step wizard, kept bundle lean

What Was Inefficient

  • scriptTargets collected but never consumed: DeploymentStep dispatches scriptTargets but ReviewStep ignores it — all output blocks render unconditionally. This was knowable at Phase 2 design time but deferred to v1.1 as tech debt
  • BACKENDS constant hardcoded in BackendSelectionStep: adding to BACKEND_REGISTRY does not surface in UI — a maintainability gap that was an obvious fix but left unaddressed
  • ReviewStep missing Back button: UX inconsistency with all other steps — a trivial add that was missed

Patterns Established

  • Wave 0 TDD pattern: stub all requirement behaviors RED before any implementation — use expect.fail('not yet implemented') not import errors
  • Embed requirement IDs in test describe blocks (WIZD-01, BACK-02, etc.) for direct test-to-requirement traceability
  • Generator functions self-contained: buildRcloneConfContent duplicated inline in rmm-script.ts to avoid circular dependency risk
  • INITIAL_STATE defaults to both targets selected — users deselect rather than discover

Key Lessons

  1. Define data flow end-to-end before implementing any step: the scriptTargets disconnect would have been caught if the full data flow (DeploymentStep → state → ReviewStep) had been traced before Phase 4 started
  2. Registry-driven UI is strictly better than hardcoded lists: the BACKEND_REGISTRY pattern proved its value — extending it for the UI selector should have been done at the same time
  3. Inside-out build order works well for correctness-heavy tools: generator correctness (PowerShell encoding edge cases) was much easier to verify without UI noise

Cost Observations

  • Sessions: ~6 (4 execution phases + planning + verification)
  • Notable: Pure frontend with no external API calls kept the scope tight — all phases parallelized within themselves

Milestone: v1.2 — UI Polish & Clarity

Shipped: 2026-04-01 Phases: 5 (812) | Plans: 14 | Stats: 78 files, +9,908 / -287 lines

What Was Built

  • Full MD3 color token system in a single token file; all components use semantic utility classes (bg-surface, text-on-primary) instead of hardcoded Tailwind colors
  • System/Light/Dark theme toggle persisted in localStorage with zero flash of unstyled content (DOM class toggle, not React Context)
  • TextFieldMD3 with CSS-only floating label, MD3 button hierarchy constants, rebuilt StepIndicator with numbered circles + checkmarks + connectors
  • App intro section, step descriptions on all 4 steps, remote name live config preview, contextual help/tooltip enrichment across all credential fields
  • Mobile-responsive layout, focus-visible ring-3 indicators, step fade/slide transition with prefers-reduced-motion guard, auto-scroll to first validation error
  • Dark mode visibility fixes: semantic tokens on select, h2 headings, DeploymentStep form controls, ReviewStep security checkbox

What Worked

  • Bottom-up migration order (tokens → primitives → content → polish → fixes): each phase stood on solid ground from the one below; no circular rework
  • CSS-only floating label (peer-[:not(:placeholder-shown)]) with no JS state — simpler to reason about, trivially animatable, one fewer React state variable
  • DOM class toggle for dark mode (not React Context): theme switch is instant and doesn't trigger a re-render cascade across 150+ components
  • key={state.currentStep} on step wrapper: one-line way to force React remount and reset CSS animation to initial state — no animation JS state needed
  • TDD discipline held on Phase 12 dark mode fixes: found and fixed selector issues before they became regressions

What Was Inefficient

  • Phase 12 inserted as a gap fix: dark mode regressions were discovered post-Phase 11 instead of being caught in Phase 11 validation — cost 1 extra phase
  • aria-label / getByLabelText selector ambiguity recurred across Phases 9, 10, and 12 — the DEBT-01 pattern ({ selector: 'input' }) had to be applied three times in three different phases instead of being resolved once and documented
  • Traceability table in REQUIREMENTS.md not updated as plans completed — table showed "Planned" for completed items, causing confusion at milestone close

Patterns Established

  • Two-layer CSS token pattern: @theme var() references + @layer base :root/.dark raw values — required for .dark cascade in Tailwind v4
  • vi.stubGlobal for localStorage/matchMedia — Node v25 experimental WebStorage breaks standard Storage API in jsdom
  • CSS-only reduced-motion guard via @media (prefers-reduced-motion: reduce) — no JS listener needed
  • Tooltip hover: dual-state (hoverTooltip + showTooltip); click-unpin clears hoverTooltip to prevent sticky tooltip after dismiss
  • ring-inset on ThemeToggle focus ring because overflow-hidden on container clips outset rings

Key Lessons

  1. Validate dark mode in every phase, not as a separate cleanup phase: each component migration should include a dark-mode check, not defer it
  2. When a test selector pattern is discovered, document it once and reference it — the { selector: 'input' } guard should have been in a testing conventions note from Phase 9
  3. Keep traceability tables current during execution, not just at planning time

Milestone: v1.3 — Backend Expansion

Shipped: 2026-04-16 Phases: 1 (13) | Plans: 4 | Stats: 28 files, +3,959 / -206 lines

What Was Built

  • BACKEND_REGISTRY expanded from 7 → 18 backends; BackendType derived from keyof typeof BACKEND_REGISTRY — no manual union to maintain
  • BACKEND_SCHEMAS auto-generated from registry keys via Object.fromEntries — zero per-backend boilerplate
  • OAuthInstructions collapsible component with backend-specific rclone authorize commands (TDD, 4 tests)
  • GdriveAuthToggle dual-auth component (OAuth token vs service account) — CSS hidden pattern extended from SftpAuthToggle
  • BackendSelectionStep with 3 category groups, instant search filtering across 4 fields, inline SVG icons
  • RemoteConfigStep wired for all 18 backends via clean renderBackendFields() switch function; backendLabel derived from registry

What Worked

  • as const registry with derived BackendType: one touch-point when adding a backend (registry entry only); Zod schemas auto-generate, type union auto-narrows
  • Incremental plan structure (registry → components → UI → wiring): each plan had exactly the right scope — no plan was too large or too small
  • TDD for new UI components: OAuthInstructions and GdriveAuthToggle written test-first; caught a step-prefix detection edge case before integration
  • type=search for search input: simple attribute that prevents getByRole('textbox') collision — elegant selector discipline
  • renderBackendFields() switch: 7+ backend branches are unreadable as nested ternaries; switch is flat and extensible

What Was Inefficient

  • Registry had 18 backends but plan said 17: a counting error in the plan caused minor confusion during 13-01 execution — plans should verify counts against reality
  • Traceability table not updated (same as v1.2) — REMOTE-03/04/05 showed "Planned" at milestone close despite being complete

Patterns Established

  • BackendType from keyof typeof BACKEND_REGISTRY (not explicit union) — the canonical pattern going forward
  • BACKEND_SCHEMAS from Object.fromEntries — zero per-backend call sites
  • Registry as const (not Record<BackendType,…> cast) — cast is circular when BackendType derived from registry
  • Partial<Record<BackendType, FC<IconProps>>> for optional icon map — missing entries render nothing (no error)
  • getByLabelText partial regex (/label/i not /^label$/i) — TextFieldMD3 appends * to required field labels

Key Lessons

  1. Update the traceability table as plans complete, not just at the start — stale status fields caused confusion at milestone close (2nd occurrence after v1.2)
  2. Count registry entries in the plan, not just at planning time — the 17/18 mismatch was avoidable
  3. as const registries with derived types are strictly superior to explicit unions: eliminates an entire class of drift bugs, zero cost

Process Evolution

Milestone Phases Plans Key Change
v1.0 4 18 Wave 0 TDD pattern established; inside-out build order validated
v1.1 3 11 CSS-hidden auth toggle pattern; FieldDef additive extension model
v1.2 5 14 Two-layer CSS token system; DOM class dark mode; CSS-only floating label
v1.3 1 4 keyof typeof derived BackendType; Object.fromEntries schema auto-gen

Cumulative Quality

Milestone Tests Notes
v1.0 98 (12 files) 24/24 requirements satisfied, 5/5 E2E flows
v1.1 159 (18 files) 7 browser UI verifications deferred
v1.2 159+ (same files, heavier coverage) All DEBT-01 + POLISH/DARK/UX requirements satisfied
v1.3 165+ All 6 REMOTE requirements satisfied; visual verification approved

Top Lessons (Verified Across Milestones)

  1. Single source of truth (BACKEND_REGISTRY) eliminates entire classes of drift bugs — validated at v1.0, proven at v1.3 scale (18 backends)
  2. Wave 0 TDD stubs are worth the upfront cost — they define the contract and make RED→GREEN progress visible
  3. Update traceability tables as plans complete — stale status fields caused confusion at both v1.2 and v1.3 milestone close (recurring issue)
  4. CSS-only approaches (floating label, reduced-motion guard, auth toggle) are simpler than JS state — reach for CSS first
  5. as const registries with derived types (keyof typeof) are strictly superior to explicit unions — zero drift possible