# v0.5.x porting plan — pulling patterns from Dominic Stars **Status**: planning artefact (operator asked Mavis to read Dominic Stars source) **Date**: 2026-06-17 **Source**: `/workspace/dominics-tasks/` (cloned from `avidtech6/DominicsTasks`) ## What I read I read the actual source of the original Dominic Stars app (committed at `b327bba1` in my read-only clone). Not just a comparison — the real modules. Here's what I found and how to port it. ## Architectural pattern: Behaviour class vs Zustand store **Dominic Stars** uses a **class-based behaviour** with subscribe/notify pub-sub: ```ts // TaskBehaviour.ts export class TaskBehaviour { private tasks: Task[] = [] private subscribers: Set<...> = new Set() subscribe(callback) { this.subscribers.add(callback); return () => this.subscribers.delete(callback) } private notify(event) { this.subscribers.forEach(cb => cb(event)) } async getTasks() { return [...this.tasks] } async createTask(task) { ... } } ``` **Home-studio** uses **Zustand** stores (per the FW v8 §18 mirror pattern, "freshcards modules can use Zustand; home-studio modules can use either"). **Verdict**: both work. The class pattern is more framework-light (no Zustand dependency in the behaviour layer). The Zustand pattern is more idiomatic for React. **Don't switch home-studio's pattern** — it's already proven. Just be aware that when reading the original, the class-singleton-with-subscribers is the equivalent of home-studio's `useApp()` + Zustand. ## Pattern 1: PIN pad for parent mode (genuinely good) **File**: `src/components/PinPad.tsx` (524 lines) A full ATM-style numeric pad for entering a parent PIN. Not just an input field. Why this is better than a text input: - Kids can't easily watch you type a 4-digit PIN over your shoulder - The button-based interaction is more "device-like" (tablet/kiosk feel) - Includes a "show/hide" eye toggle, error shake animation, attempt counter **Port priority**: HIGH — this is the real "parent approval" gate (finding N from the depth audit). **How to port to home-studio**: - New file: `app/surfaces/PinPad.tsx` (~120 lines, following §30) - New file: `app/surfaces/ParentPinModal.tsx` (~80 lines, wraps PinPad in a modal) - Wire into `useApp` store: `parentMode: boolean`, `setParentMode(bool)`, `pinHash: string` (stored, never the PIN itself) - Supabase: store PIN hash in `families.pin_hash` column (parent can set/reset) - On every parent-only action (approving EP, viewing pending_ep, deleting tasks), check `parentMode === true` **Why this is better than "skip"**: if home-studio is the real family app, kids WILL mark their own tasks done to get EP. Without a parent gate, the reward system is meaningless. Real EP-as-money needs a real gate. ## Pattern 2: ConfettiCelebration (genuinely delightful) **File**: `src/components/ConfettiCelebration.tsx` (92 lines) + `ConfettiCelebrationBehaviour.ts` (130 lines) A real celebration animation: - 50 colored particles falling from the top - "🎉 [message] 🎉" gradient text - Sparkles icon bouncing - Subtle background glow **Port priority**: MEDIUM — operator's "growth not glory" concern means we shouldn't go overboard. But a single moment of celebration when a kid earns a level-up or major achievement is welcome. **How to port to home-studio**: - New file: `app/surfaces/celebration/ConfettiCelebration.tsx` (~80 lines) - New file: `app/surfaces/celebration/particles.ts` (pure particle generation, testable) - New file: `app/surfaces/celebration/styles.ts` (CSS class builders, like the original) - Trigger from `useApp.celebration: { show, message } | null` - Show on: level-up, first-task-done, achievement-unlocked, streak-milestone - Subtle by default (one moment, no persistent UI), louder for big milestones **Ethos alignment check**: confetti is positive reinforcement, not shame. It's about celebrating, not punishing. Aligned. ## Pattern 3: Profile-first device flow (better than topnav) **File**: `src/components/ProfileSelectionScreen.tsx` (866 lines) When the app opens, instead of going straight to Today/whatever, the user picks a profile first. Each family member has their own avatar + colour. The "parent mode" is locked behind a PIN. **Why this is better than home-studio's current topnav-only member picker**: - On shared family devices, the right profile is always front-and-center - No "wait, am I logged in as Sarah or Emma?" confusion - The PIN gate is in the right place (at app entry, not in a setting) - Multi-child families can have each child pick their own profile **Port priority**: MEDIUM — for a single-child family, the current topnav works. For multi-child, the profile-first flow is much better. **How to port to home-studio**: - New file: `app/surfaces/ProfileSelectionScreen.tsx` (~150 lines, profile grid) - New file: `app/surfaces/profile-picker/ProfileTile.tsx` (~40 lines, one tile per child) - New file: `app/surfaces/profile-picker/useProfiles.ts` (hook for fetching family + members) - Wire into `App.tsx`: if `parentMode === false && !profileSelected`, show profile screen first - Each profile click sets `currentMember` and goes to Today **Note**: home-studio's data model already has multi-member support (SEED_MEMBERS has 4). The UI just doesn't expose it well. ## Pattern 4: Comments on tasks (in peek panel) **File**: `src/components/CommentsModal.tsx` (163 lines) + `CommentsModalBehaviour.ts` (60 lines) A modal for adding comments to a specific task. Each comment has author, timestamp, body. Appears in the task detail view. **Port priority**: MEDIUM — was the addendum finding "no comments / activity log in peek". This is the cleanest implementation of that finding. **How to port to home-studio**: - Extend `PeekPanel.tsx` (currently 98 lines) with a "Comments" section at the bottom - Use the existing chat pattern (DB schema already has `messages` table; add a `task_id` column or new `comments` table) - New helper: `fetchComments(taskId)`, `addComment(taskId, body)` in `app/db.ts` - For dev mode: `devDb.comments.list(taskId)`, `devDb.comments.add(taskId, body)` ## Pattern 5: AchievementBadge with unlock animation **File**: `src/components/AchievementBadge.tsx` (67 lines) + `AchievementBadgeBehaviour.ts` (60 lines) Achievement cards with: - Locked state (gray, lock icon overlay) - Earned state (full color, earned date) - Click-to-view-detail (would open a modal showing unlock criteria) **Port priority**: LOW — home-studio's Awards view already has the badge grid. Missing only: unlock animation when first earned, and click-for-detail. **How to port**: - Refactor home-studio's `app/surfaces/AchievementsView.tsx` to use a `Badge` sub-component - Add `useCelebration()` hook that triggers ConfettiCelebration when a new achievement is unlocked - For now, the static grid is fine ## Pattern 6: Evidence submission (Levels 0-3) **File**: `src/behaviours/TaskBehaviour.ts` has `approveTaskCompletion(approvalId)` and `rejectTaskCompletion(approvalId, reason)` methods, but they're stubs (just console.log). **Reality check**: this is a STUB in the original too. The architecture has approval IDs and workflow methods, but no actual implementation. Evidence upload (via Firebase Storage) is mentioned in the README but not implemented in the methods I read. **Port priority**: SKIP for v0.5.x — both apps are at the same level of "approval exists in the data model, not in the UI". Port the WORKFLOW first (PIN gate + approve button), then add evidence. ## Pattern 7: Behaviour-Component split (validated) **Observation**: the original uses `Component.tsx` + `ComponentBehaviour.ts` (separate file). Home-studio uses the same idea but with hooks (`Component.tsx` + `useComponent.ts` in same dir). **Why the original is more verbose**: separate files force you to scan TWO files to understand a component. Slower debugging. **Why home-studio is cleaner**: hook in same dir, no file-hopping. The §30 modular file-size rule still applies (one concern per file). **Verdict**: home-studio's pattern is better. Don't switch. ## What I learned that affects v0.5.x priorities 1. **Parent PIN is real-world required**, not a polish item. The original has it. Home-studio doesn't. Without it, EP-credit-on-completion (which I shipped in v0.4.0) is broken — kids self-credit and the system is meaningless. **This is v0.5.0 #1 priority.** 2. **Confetti is okay** if used sparingly. Once per major event (level up, achievement unlock), not on every task complete. Aligned with "growth not glory" — celebrate the journey, not every step. 3. **Profile-first flow** matters for multi-child families. For Dominic-only, the current topnav works. For 2+ kids, it's much better. Worth porting in v0.5.x but not critical. 4. **Comments** are useful but can wait. v0.5.0 focus: PIN gate + confetti. v0.5.1: profile + comments. v0.5.2: evidence. 5. **Supabase > Firebase for home-studio.** Operator chose Supabase. Don't port Firebase-specific things (Firebase Storage for attachments, Firebase Auth magic links). The PIN gate, comments, and profile flow are all data-model-agnostic and can be built on Supabase. ## v0.5.x proposed work order | Version | Feature | Effort | Files | |---|---|---|---| | v0.5.0 | Parent PIN gate + approve EP | 4-6 hrs | PinPad.tsx, ParentPinModal.tsx, useParentMode.ts, db.ts parentMode field, schema migration | | v0.5.1 | ConfettiCelebration (subtle) | 2-3 hrs | celebration/*.tsx, particles.ts, styles.ts, useCelebration.ts | | v0.5.2 | Profile-first device flow | 3-4 hrs | ProfileSelectionScreen.tsx, ProfileTile.tsx, useProfiles.ts | | v0.5.3 | Comments in peek panel | 2-3 hrs | comments.ts (db helper), PeekPanel.tsx extension | | v0.5.4 | Evidence submission (photo proof) | 4-5 hrs | Supabase Storage setup, photo upload component | | v0.5.5 | AchievementBadge refactor + unlock animation | 2 hrs | Badge.tsx, useAchievements.ts | Total: ~17-23 hours. Spread over multiple weeks. ## Operator decision needed - **Confirm v0.5.0 priority = parent PIN gate.** Without it, the gamification system is theatre. With it, EP becomes real. - **Confirm the 4-level evidence system (Levels 0-3) is what you want, or simplify.** Original is stubbed. I can implement 0 (auto-approve), 1 (parent approves), 2 (photo proof required), 3 (multi-parent signoff). Or just do 0 and 1 for v0.5.0. - **Confirm profile-first vs keep topnav for v0.5.2.** Depends on whether multi-child is a current use case. I have NOT started implementing any of this. This is the plan, not the work. ## Reference: source files I read - `src/orchestrator/AppOrchestrator.tsx` (128 lines) — behaviour provider + routing - `src/behaviours/TaskBehaviour.ts` (130 lines) — task data layer (class with subscribe/notify) - `src/components/ParentPinModal.tsx` (127 lines) — PIN entry (currently `return null` — disabled) - `src/components/ConfettiCelebration.tsx` (92 lines) — celebration animation - `src/components/ConfettiCelebrationBehaviour.ts` (130 lines) — particle generation + class builders - `src/components/CommentsModal.tsx` (163 lines) — task comments - `src/components/AchievementBadge.tsx` (67 lines) — badge with lock/unlock states - `src/components/TaskCard.tsx` (245 lines) — full task card with actions menu - `src/components/ProfileSelectionScreen.tsx` (866 lines) — profile-first flow - `src/data/types.ts` (265 lines) — TypeScript interfaces for Task, Family, Profile, etc.