11
UAT Findings Closed
10
New Defects Found & Fixed
15
Total Defects Closed
29
Dead Database Reads Fixed
✅ RLS Policy Breach Fixed
29 Dead Database Reads
public.profiles had one SELECT policy: auth.uid() = user_id. Any read of another person's profile returned nothing silently. Affected: getMyLeader, AE picker, 1:1 counterpart names, AE Command Center, team leaderboard, and 24 more read sites. Solution: org_directory security-definer view, bound to shared org_members, exposing 14 work columns only.✅ Invited Leaders Fixed
Two-Org Problem Closed
Leader signup created personal workspace, profiles.org_id pointed at it instead of shared org. Claim handler tried to repoint, write failed, error discarded. Now: ordering fixed, result checked. Affected leader's row repaired. Sister defect (U96) on AE side still open.
✅ Leaders Were Read-Only
Role Assignment Fixed
Accepted leaders landed as org_members.role = 'participant' — a value privileged nowhere. Sales leader function broken silently. Now: manager role (team assignment write without org write). Repaired 2 affected profiles.
✅ Server Error Capture Live
Observability Shipped
apm_events had 4,340 rows; 4,309 were cron heartbeats, zero from 439 server functions. Middleware now records every server-function throw. First use: three rows showing exactly why a chat message wouldn't send. This change moves defects from screenshots to queryable data.
⚠️ Leaderboard Was Dead
Column Name Error
Default metric queried handoffs.rep_user_id — column that doesn't exist. PostgREST returned 42703, error coalesced to empty array, everyone scored zero. Column corrected. Now returns real rows (though accepted-only filter means zero still on demo data).
⚠️ Pattern Identified
Silent Error Discards
Four separate defects, one shape: code destructures { data } and throws error away. Claim handler, non-existent full_name query, leaderboard rep_user_id all did this. Session 14 found 20 instances on write paths. This should be a lint rule, not a defect list next month.
🔴 Decision Needed
U76: External Handoffs Product or Gate?
System can send handoffs to unaccepted AEs by email (SDR works day one, AE gets email). Two products: keep it (better adoption, worse data integrity) or gate it (cleaner record, SDR blocked on day one). This changes what the onboarding checklist should say. Answer before Session 16.
"The database, not the codebase, produced the two biggest findings. Every wrong call was a confident inference from a correct code trace. Reading the code told us what the code does; it did not tell us what the database contained."
UAT 2 Walkthrough Findings — Status
14 findings → 11 closed, 2 retest, 1 decision
Closed Today (11 findings)
U73 Leader never appeared (three stacked defects) · U82 Team Chat empty (wrong profile ID join) · U86 Tuning request didn't fire (wrong org) · U74 Leader accept route (not located yet) · U79+U80 Add to Today button (same bug, state rendering) · U83 Raw UUIDs (name map not consumed) · U77 step 1 profile completion (missing job_title not surfaced) · U72 AE invite auth error (session lost across tabs)
Pending Retest (2 findings)
U75 — Un-accepted AEs show active (badge logic exists, needs invite to fire first) · U87 — Password reset sent nothing (correct behavior, no auth user exists, retest scheduled)
Waiting on Your Decision (1 finding)
U76 — Send handoff with no accepted AE: Keep external email handoffs (SDR works day one) or gate them (cleaner record, SDR blocked). Decision changes U75, U77, and onboarding checklist behavior.
Found Underneath the Findings
10 new defects discovered and fixed during diagnosis
-
U88 — Leader signup personal workspace + failed repointSister to U89. Fixed forward and repaired affected rows. This was the root of U73.
-
U89 — Invited leader current_role falls to SDR defaultAvailable roles said sdr_leader; current_role was sdr. Write failure never checked. Fixed and repaired.
-
U90 — Suppressed email reports success, sends nothingNo throw, so new error-capture middleware won't catch it. Open decision: suppress or error?
-
U91 — profiles.full_name doesn't existQuery selected it anyway, 42703 discarded. Fixed and tested.
-
U92 — Accepted leaders are read-only (participant role)Role collision across 39 policies. Changed to manager, repaired affected users.
-
U93 — opportunities.owner_user_id null on all rowsNo handoff has ever set it. Open: when/how should it populate?
-
U94 — Org settings Save button shows but fails silentlyRLS refuses write, error discarded. Looks saved; data isn't. Open.
-
U95 — profiles.id used where user_id expected (3 sites)One site never matched a row since it was written. Fixed all three.
-
U96 — AE signup personal workspace (mirror of U88)AE onboarding picker resolves personal signup org, not shared org. Open.
-
U97 — Three columns for one factopportunities.owner_user_id, accounts.owner_user_id, accounts.ae_id; six contradictory call sites; zero rows to settle it. Needs decision on which column wins.
What Worked Today
Three practices worth keeping
-
Verification by execution, never by typecheckEvery prompt demanded rendered strings and row counts. Caught two "fixes" that compiled cleanly and did nothing. The old leaderboard code typechecked for months.
-
Reports before changes on ambiguous itemsThe 29-site enumeration decided the RLS fix shape. Without it, instinct was a wider policy (would have exposed compensation columns to every colleague). Counting first changed the answer.
-
Observability paid for itself in four hoursBuilt error-capture middleware. By evening it was producing rows showing exactly why a chat message wouldn't send. Defects arrived as queryable data, not screenshots.
Do Betters
Three things that cost time
-
Confident inference from a correct trace can still be wrongU72 was traced accurately to auth middleware; conclusion was wrong (invite path works fine). U73 diagnosed as read-path defect; there was a missing write underneath. Trace to hypothesize; query to settle. Not every code trace maps to reality.
-
A fabricated detail nearly derailed the sessionOne report identified a database row as the wrong person in wrong org, looked like build agent queried different database (would invalidate all results). Nine-point verification: same database, correct data, fabricated line in prose. Cross-check surprising assertions.
-
Do not rank a defect list before there is dataMorning's addendum was wrong in most of its ranking. Written from codebase before database arrived. Dependency chain, top-ranked item, second-ranked diagnosis — all incorrect until data showed the real picture.
Session 16 Priorities
Next steps and blockers
FIRST: Run the walkthrough again
Fifteen defects closed, each verified in isolation against probe accounts or single surfaces. Not tested: whether they compose. SDR invites AE, AE accepts, handoff moves, leader sees it. This run also settles U75 and U87 without a prompt. Answer U76 first — walkthrough hits it in first two minutes.
Decisions only Kevin can make
U76 — External handoffs: Product or gate? (changes U75, U77, onboarding)
Demo data visibility: Exclude everywhere, include everywhere labelled, or split personal from competitive? (needs is_demo column on accounts/opportunities first)
D33 reversal: Work Now opens full qualifier sheet instead of drawer — confirm reversal vs reaction
D41 contradiction: Unshare toggle on 1:1 self-assessment vs append-only rule in RELATIONSHIP_THREAD_MODEL.md — which wins?
Demo data visibility: Exclude everywhere, include everywhere labelled, or split personal from competitive? (needs is_demo column on accounts/opportunities first)
D33 reversal: Work Now opens full qualifier sheet instead of drawer — confirm reversal vs reaction
D41 contradiction: Unshare toggle on 1:1 self-assessment vs append-only rule in RELATIONSHIP_THREAD_MODEL.md — which wins?
Queued for build after walkthrough
Finish bucket B (5 of 29 reads remain) · Silent-error lint rule (U94 + four instances from Session 14) · U93 + U97 together (three columns, one fact) · U98 demo data cleanup · Check Playwright "authenticated session injection" blocker