Five linear phases. Each phase is independently verifiable. No advance until the gate clears — including phase 5's bead-fix loop.
Confirm perform-dev branch online. Run seed-perform-dev-coach1-dual-role.sh (idempotent — already shipped). Patch athlete-5 (existing UUID 70792af0…) in place to account_status='parent_managed' + is_minor=true + DOB <13y. Re-run get_team_athlete_trends after patch and confirm row counts hold. Start Vite dev server in tmux against https://crmlprsypfztkqvueoez.supabase.co.
Presume scripts/evaluations/load_all_frameworks_v2.sh local has already loaded the 37 CSV files across 10 sports into public.evaluation_framework_metadata + public.evaluation_criteria on perform-dev. Assert: SELECT count(*) FROM evaluation_framework_metadata > 0 and matching evaluation_criteria rows for the team's sport. Stamp the team's framework: confirm teams.sport_type matches a loaded framework and that future event inserts will default events.sport_framework to a real framework (not the 'SHOT' COALESCE fallback). If the team isn't bound to a loadable framework, file as bead #0 before anything else.
Run seed-perform-dev-events.sh (11 events: 3 past · 2 today · 2 tomorrow · 4 future · 4 event_types). Run populate-historical-evaluations edge function (weeksOfHistory=4, eventsPerWeek=3, completion=85%) so trends compute, framework gaps populate, and FeedbackRAGQueue has owed feedback. Backfill coach_feedback text on ~70% of past response rows. Run intensity-aware score patch: intensity=3 → all three scores, intensity=0 → NULL all.
Login as coach1. Switch role to coach → walk Spotlight / Events (4 modes) / History (Team + Athlete) / Club. Switch role to player → walk Spotlight / Events / History / Club. Switch role to parent, pick the minor child → walk Spotlight / Events / History / Club. For each tab capture: empty / loading / populated / one sheet open. ~75 base tab-state frames; ~125 sheet/modal overlays.
Per captured frame: detect tall screenshots (height > 4000px) → split into 3000px sections. Targeted crops at +150% zoom on header / sub-nav / card row / sheet header / drag-handle area. Apply -auto-level -adaptive-sharpen 0x1.5 for legibility. Pragmatic bar (locked): block on structural defects (dead buttons, wrong copy, broken layout, missing data, "Player" instead of "Athlete", wrong brand-pillar accent for the persona). LOW-severity polish (1-2px spacing nits, contrast AA-but-not-AAA, font-weight micro-drift) batched into ONE perform-polish-pass bead per persona — ship after every cell is structurally green.
Every non-impeccable finding (UI defect, dead button, broken eval submit, RPC failure, wrong copy, wrong pillar accent): bd create --type=bug with screenshot + crop + zone + persona×tab×eval-kind address. Group siblings under one parent bead. Spin off a focused agent (superstar-engineer for full-stack, focused-repository-analyzer for diagnosis-only, backend-developer for RPC/RLS) to investigate and fix the issue into the same feat/perform-redesign branch (no PRs, atomic commits per the project rule). After the fix commits, re-run the affected matrix cell + its eval-flow row in browser-harness. Mark the cell green only when the fix sticks. Halt rule (locked): stop when the matrix is all-green for three consecutive walks. Stricter than the default 2-walk rule — catches intermittent regressions that only surface on the second re-render.
One auth identity. profile.privileges = ['member','coach','player','parent']. RoleSwitchService writes profiles.preferred_role + localStorage shot_current_role. Perform.tsx switches the rendered sub-page; the URL stays at /perform.
Solid grey = synchronous render path. Dashed clay = role decision branch driven by RoleSwitchService.
Phases 1-2 (frameworks + team) already seeded on perform-dev. Phase 3 onward is what this mission writes. Triggers fan out: every events insert + event_participants insert auto-creates evaluation rows. Coach feedback is the only manual PATCH.
Green nodes = already on perform-dev. White = inserts. Dark = auto-triggered cascade. Clay = manual PATCH. The fan-out from Phase 3b → Phase 4 is the load-bearing trigger — no app-code is involved.
4 tabs × 3 personas × 4 base states = 48 base cells. Add ~11 sheet-kind overlays per persona and the modal interrupts and the count lands near 200. The "states to verify" column is the minimum frames captured per cell — empty / loading / populated / one card-tap.
| Persona | Tab | Surfaces & sub-components | Sheet kinds opened | States |
|---|---|---|---|---|
| COACH | Spotlight | FeedbackRAGQueue · NextEventCard · TopMoverCard · AssessmentList · AnnouncementsDigest · 3-CTA row (Create Event / Announce / Eval) | feedback event improver assessment announcement evals-outstanding | 10 |
| COACH | Events | 4-mode toggle (Week/List/Month/Past) · EventFilterCard (team + event_type chips) · DayHeader · EventCard · EventListRow · PastEventsAccordion | event | 12 |
| COACH | History | Team mode: Match Record · Framework Gaps · Athlete summary · Athlete mode: chip-rail picker · drill-down EvaluationFocusInlineSnapshot | — | 8 |
| COACH | Club | ClubTeamSwitcher pill · TeamRosterTab · AdminActionCard grid (Settings / Intensity / Framework / Clubs) | team | 5 |
| ATHLETE | Spotlight | AthleteStoryFeed D2: coach-feedback card · self-eval-due card (48h gate) · next-event card · streak badge · My Development CTA | feedback self-eval event | 7 |
| ATHLETE | Events | 2-mode toggle (Week/Month) · RsvpSummaryCard · DayHeader (purple accent) · EventCard · PastEventsAccordion | event | 8 |
| ATHLETE | History | PerformanceHistoryViewer (premium-gated): chart · SeasonSelector · EventTypeFilter · PerformanceInsights · CategoryDrillDown | — | 6 |
| ATHLETE | Club | SafeRosterList header · roster grid (display_name + avatar + position + jersey, NO PII) | — | 4 |
| PARENT | Spotlight | ChildIdentityCard · NextEventCard · ParentEvalOwed banner · ChildEvaluationHistory · "View Development" CTA · ParentTeamSwitcher | parent-eval event | 7 |
| PARENT | Events | 2-mode toggle · ChildSelectorChip · RsvpSummaryCard (child) · DayHeader (gold accent) · EventCard · EvalOwedCard · PastEventsAccordion | event-parent | 9 |
| PARENT | History | PerformanceHistoryViewer scoped to activeUserId · viewerRole='guardian' · same drill-down as athlete | — | 6 |
| PARENT | Club | Header (child + club + team) · SafeRosterList | — | 4 |
| TOTAL · per-cell average ×4 frames | ~86 | |||
| + ~11 sheet overlays × 3 personas × 4 sheet-states = ~125 sheet/modal overlays + edge cases | ~125 | |||
| GRAND TOTAL — frames the mission captures | ~200 | |||
Persona-pill colour matches the brand-pillar accent used inside that persona's UI (teal / purple / gold). The mission only marks a cell green when every state for that cell renders impeccably.
The matrix above counts tab-level frames. Evaluations are flow-based — each one is a multi-step submit interaction that crosses Spotlight (entry banner) → sheet (form) → submit → success → reflect back on Spotlight/History. Every cell below also has to go green for the persona-tab cell above to be considered done.
| Persona | Eval kind | Entry points | Submission states to verify | DB column written |
|---|---|---|---|---|
| ATHLETE | Pre-eval (self · question_pre · answers_pre) |
Spotlight self-eval-due card (48h gate) · Events eval-owed banner on a future event |
empty form · partial fill · all sliders set · submit pending · success · past-deadline locked · validation error on empty submit | evaluation_responses.pre_score · evaluations.pre_submitted_at |
| ATHLETE | Post-eval (self · question_post · answers_post) |
Spotlight reflect-prompt card (post-event window) · Events past-event card · History past-event row | empty · partial · submit · submitted · reflect/comments composed · past window locked | evaluation_responses.post_score · evaluations.post_submitted_at |
| ATHLETE | Coach feedback view (read-only, gated by share_coach_feedback) |
Spotlight feedback card · History per-event drill |
new unread badge · marked-seen state · "no feedback shared" empty · share_coach_feedback=false (hidden) | evaluation_responses.coach_score read · evaluations.coach_submitted_at read |
| PARENT | Parent-eval (on behalf of minor child) |
Spotlight parent-eval-owed banner · Events EvalOwedCard on child's event |
empty · partial · all set · submit · submitted · "consent_revoked" gated (banner suppressed) · child not parent_managed (banner suppressed) | evaluation_responses.pre_score (shared row · RLS via parent_relationships.verified_at) |
| PARENT | Coach feedback for child (read-only via guardian role) |
Spotlight ChildEvaluationHistory row tap · History drill with viewerRole='guardian' | populated card · "no feedback shared" empty · share_coach_feedback=false (hidden) · child-deselected placeholder | read only (no write) — bound by evaluations.is_parent_of RPC |
| COACH | Coach scoring (per athlete · per event · per question) |
Spotlight FeedbackRAGQueue row tap · evals-outstanding sheet list · Event sheet → "view evaluations" |
empty (NULL scores) · partial scoring · all sliders set · save draft · publish · share_coach_feedback toggle on/off · RAG dot updates (red→amber→green) · validation on incomplete | evaluation_responses.coach_score · coach_evaluator_name · evaluations.coach_submitted_at |
| COACH | Coach feedback compose (free-text per athlete) |
eval-row sheet from FeedbackRAGQueue · per-athlete drill |
empty · text entered · character limit hint · save-publish · share-or-keep-private toggle · already-published shows as "edit" | evaluation_responses.coach_feedback · share_coach_feedback |
| COACH | Bulk eval review (event-level grid) |
Event sheet → "View evaluations" drill (5 athletes × question grid) | all NULL · mixed (some scored, some pending) · all scored · in-session vs evaluating vs completed event-status variants | multi-row PATCH via get_responses_by_evaluation_ids RPC |
| EVAL FLOWS · sum of submission states | ~52 | |||
Three principles for the eval walk: (1) every kind exercised at least once with valid data — submit succeeds, DB row updates, sheet closes, UI reflects; (2) the corresponding error path exercised — empty submit, past-deadline, consent-revoked; (3) RAG-status semantics verified across the FeedbackRAGQueue after each coach scoring run.
Cross-persona invariant the walk MUST check: a coach publishes feedback for athlete-3 with share_coach_feedback=true → switch to athlete role → that feedback appears in athlete's Spotlight; switch to athlete-3's parent (coach1 acts as parent of athlete-5 patched minor; for athlete-3 use the role-switch surrogate where applicable) → feedback appears in ChildEvaluationHistory. Same coach toggles share_coach_feedback=false → both views update to "no feedback shared". This is the load-bearing visibility-gate test.
Browser-harness captures full-frame. /media-processing handles the lens. /impeccable reads the lens output and produces verdicts.
Frame in. Lens applied. Verdict out. Defect routes to bead-create-fix-revalidate. No cell ships orange or red.
profile.preferred_role + localStorage + page contents all flipped. If any layer lags, file as the first bead.get_team_athlete_trends, get_event_responses, get_responses_by_evaluation_ids).parent_managed may collide with existing evaluations and break trend computation for that athlete.get_team_athlete_trends after the patch and confirm row counts.restart_daemon() then re-walk only the affected cell — the matrix tracks progress per-cell so resume is trivial..impeccable.md at the repo root: typography hierarchy (≥1.25 ratio), WCAG-AA contrast on text, brand-pillar accents per persona, "Athlete" not "Player", no orphan loaders, no dead buttons, drag-handle visible, sheet header non-duplicated.severity: field. Each loop pulls highest severity first. Polish only after every cell is structurally green.Stevie reviewed the 3 open questions and locked the three knobs below before mission generation. All three flow into the /make-a-goal mega-prompt.
70792af0… directly: account_status='parent_managed', is_minor=true, date_of_birth < 13y, parent_id=coach1. Reuses every seeded event + evaluation row. Re-run get_team_athlete_trends immediately after the patch to confirm trend math still computes; if it breaks, that's bead #1.perform-polish-pass bead per persona. Polish wave only after every structural cell is green.