Attention model + course-scoping player gate (audit finale) - #965
Conversation
The coach dashboard told three different stories about what needed the
coach: the hero counted actionItems.length (tasks + announcements) and
its copy claimed "approvals" it never counted; pending join-request
approvals were counted by nothing; and announcements (no accept/resolve
step) inflated the "waiting on you" total as if they were backlog.
Add a pure attention-queue module deriving ONE honest count from data the
dashboard already holds (no new query, no RLS): total = actionable tasks
+ pending approvals, announcements excluded. Wire it through:
- coach-signal hero: number is the canonical total (approvals finally
counted, so the "approvals" copy is true), body names only the parts
that exist ("3 tasks and 2 approvals ...").
- ActionItemsPanel: badge counts actionable work only; announcements move
to a clearly-labelled, separately-counted strip and never inflate the
backlog. Row markup extracted to a shared ActionItemRow.
Signals and Team Pulse keep their own genuinely-different metrics.
Tests: attention-queue counts/breakdown/split, coach-signal honest hero,
ActionItemsPanel announcement separation. eslint + vitest green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMdviLDsAg2YYJ8adsM6fg
…players (audit owner-decision) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMdviLDsAg2YYJ8adsM6fg
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Summary by CodeRabbit
WalkthroughThe PR adds a canonical Fairway attention model that excludes announcements from actionable counts, updates dashboard signal copy, and separates announcement rendering. It also restricts golf course mutation controls to team managers with player and coach test coverage. ChangesFairway dashboard attention model
Golf course management permission gates
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant FairwayCoachDashboard
participant attentionQueue
participant coachSignal
FairwayCoachDashboard->>attentionQueue: Build counts from action items and pending approvals
attentionQueue-->>FairwayCoachDashboard: Return canonical AttentionCounts
FairwayCoachDashboard->>coachSignal: Derive signal with AttentionCounts
coachSignal->>attentionQueue: Format task and approval breakdown
attentionQueue-->>coachSignal: Return attention text
coachSignal-->>FairwayCoachDashboard: Return CoachSignal
Possibly related PRs
🚥 Pre-merge checks | ✅ 9 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (9 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.44.1)ast-grep could not parse rule config: /ast-grep-rules/../git/.coderabbit/ast-grep/no-explicit-any.yml 🔧 ESLint
src/components/fairway/pages/dashboard/ActionItemsPanel.test.tsxESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. src/components/fairway/pages/dashboard/FairwayCoachDashboard.tsxESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox. src/components/fairway/pages/dashboard/attention-queue.test.tsESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/components/fairway/pages/dashboard/FairwayCoachDashboard.tsx`:
- Around line 1163-1199: Update the attention-item rendering around the icon and
status pill so announcement items never receive overdue treatment: require
item.type !== 'announcement' alongside item.overdue in both the icon branch and
the Overdue StatusPill branch. Preserve the existing urgent and announcement
rendering for all other cases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 6c1ac62d-528a-47f8-8a81-ee330e93e357
📒 Files selected for processing (10)
src/components/fairway/pages/dashboard/ActionItemsPanel.test.tsxsrc/components/fairway/pages/dashboard/FairwayCoachDashboard.tsxsrc/components/fairway/pages/dashboard/attention-queue.test.tssrc/components/fairway/pages/dashboard/attention-queue.tssrc/components/fairway/pages/dashboard/coach-signal.test.tssrc/components/fairway/pages/dashboard/coach-signal.tssrc/components/golf/courses/CourseDetailDrawer.tsxsrc/components/golf/courses/CourseLibraryClient.tsxsrc/test/golf/components/CourseDetailDrawerPlayerGate.test.tsxsrc/test/golf/components/CourseLibraryClientPlayerGate.test.tsx
| <span className="mt-0.5 shrink-0"> | ||
| {item.overdue ? ( | ||
| <IconAlertCircle size={16} className="text-fw-danger" /> | ||
| ) : item.type === 'announcement' ? ( | ||
| <IconBell size={16} className="text-text-tertiary" /> | ||
| ) : isUrgent ? ( | ||
| <IconAlertCircle size={16} className="text-fw-warning" /> | ||
| ) : ( | ||
| <IconClipboardList size={16} className="text-text-tertiary" /> | ||
| )} | ||
| </span> | ||
| <span className="flex min-w-0 flex-1 flex-col gap-1"> | ||
| <span | ||
| className={cn( | ||
| 'truncate font-fw-sans text-body font-medium', | ||
| item.overdue ? 'text-fw-danger' : 'text-text-primary', | ||
| )} | ||
| > | ||
| {item.title} | ||
| </span> | ||
| <span className="flex flex-wrap items-center gap-2"> | ||
| <span | ||
| className="font-fw-sans text-caption text-text-tertiary" | ||
| suppressHydrationWarning | ||
| > | ||
| {now ? formatRelativeDate(item.date, now) : ''} | ||
| </span> | ||
| {item.overdue ? ( | ||
| <StatusPill tone="danger" dot={false} size="sm"> | ||
| Overdue | ||
| </StatusPill> | ||
| ) : isUrgent ? ( | ||
| <StatusPill tone="warning" dot={false} size="sm"> | ||
| {item.priority === 'urgent' ? 'Urgent' : 'High'} | ||
| </StatusPill> | ||
| ) : null} | ||
| </span> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Guard the overdue treatment against announcement items.
item.overdue is checked before item.type === 'announcement', so an announcement with overdue: true would render the danger icon and "Overdue" pill — contradicting the attention model's rule that announcements are never actionable/urgent (see the doc comment at Line 1210-1215). ActionItem.overdue is untyped against type, so nothing prevents this combination upstream.
🛡️ Proposed guard
- <span className="mt-0.5 shrink-0">
- {item.overdue ? (
+ <span className="mt-0.5 shrink-0">
+ {item.overdue && item.type !== 'announcement' ? (
<IconAlertCircle size={16} className="text-fw-danger" />
) : item.type === 'announcement' ? (
<IconBell size={16} className="text-text-tertiary" />
) : isUrgent ? (
<IconAlertCircle size={16} className="text-fw-warning" />
) : (
<IconClipboardList size={16} className="text-text-tertiary" />
)}
</span>Apply the same guard to the Overdue StatusPill branch below it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/components/fairway/pages/dashboard/FairwayCoachDashboard.tsx` around
lines 1163 - 1199, Update the attention-item rendering around the icon and
status pill so announcement items never receive overdue treatment: require
item.type !== 'announcement' alongside item.overdue in both the icon branch and
the Overdue StatusPill branch. Preserve the existing urgent and announcement
rendering for all other cases.
What
Two audit findings from the Full Product Audit Swarm, integrated together.
1. One canonical coach "needs you" attention count (the deepest finding)
The coach dashboard told three different stories about what needed the coach:
actionItems.length(tasks + announcements) and its copy claimed "approvals" it never counted;New pure
attention-queue.tsderives one honest count from data the dashboard already holds (no new query, no RLS change):total = actionable tasks + pending approvals, announcements excluded. Wired through every surface:coach-signal.ts): the number is the canonical total — approvals are finally counted, so the "approvals" copy is now true — and the body names only the parts that exist ("3 tasks and 2 approvals flagged from your roster this week.").ActionItemRow.2. Course-scoping: hide create/edit/delete controls from players (owner decision: "keep library open, hide player edit")
Players could browse the shared course library (intended) but four mutation controls leaked to player sessions: the library "+ Add course" button and empty-state CTA, plus (on non-library courses) "Edit course", photo upload/replace/remove, "Add tee", and the per-tee "Edit" button. All now gate on the existing
canManageTeamboolean the server page already resolves from session role. Pure UI role-gate, no RLS/server-action change; same conditional path serves desktop and mobile.Gates
npx eslint <10 changed files>→ 0npx tsc --noEmit(full) → 0npx vitest run(full) → 2553 files / 23984 tests pass, 56 skipped, 0 failures(One unrelated test,
FairwayStatsCockpit.tabSwitching, flaked once under full-suite concurrency on the first run and passed deterministically in isolation and on a clean full re-run. It is untouched by this diff.)Follow-up flagged for Nick (not in this PR)
The new-round tee picker (
FairwayCoursePicker.tsx) also lets players add a course, which is the same data-pollution vector this PR closes for the library. Gating it there would be consistent with the "hide player edit" decision, but it would block a player from logging a round on a course that isn't in the coach-curated library — a real UX tradeoff outside the scoped library decision. Left open pending your call.🤖 Generated with Claude Code