Skip to content

fix(golf): classify remaining CoachHelm no-rounds empty states as expected#807

Merged
njrini99-code merged 5 commits into
mainfrom
fix/golf-empty-state-codes
Jul 12, 2026
Merged

fix(golf): classify remaining CoachHelm no-rounds empty states as expected#807
njrini99-code merged 5 commits into
mainfrom
fix/golf-empty-state-codes

Conversation

@njrini99-code

Copy link
Copy Markdown
Owner

Summary

Sentry alerted at 23:11 UTC tonight on GET /golf/dashboard/coachhelm. Root cause: a player with no completed rounds loading the CoachHelm page fires four sibling expected-empty-state soft failures, and #804 only gave getPlayerShotAnalytics a stable code — getPlayerProfile, getPlayerTrendAnalysis, getPlayerShotContext, and the what-if scenario path (all in coachhelm-data.ts) still log at error severity and reach Sentry.

This extends #804's convention: each empty-state return now carries a stable code (no_completed_rounds / insufficient_rounds / no_rounds_in_period), all registered under EXPECTED_EMPTY_STATE_CODES in the soft-failure observer (info, skipSentry). The ActionError envelope gains an optional code field.

Partner-readable summary

A brand-new player opening the CoachHelm page was being counted as a production error four different ways. It's now correctly treated as a normal empty state, keeping error dashboards meaningful.

Type of change

  • Bug fix
  • Feature / new behavior
  • Security / RLS / auth
  • Database migration
  • CI / tooling / chore
  • Docs only

Area

golf · coachhelm

Risk level

  • Low — adds optional codes to failure envelopes + observer classification; no behavior change for callers (they branch on success only)

Git Activity Timeline note

Stops new players' empty CoachHelm pages from being logged as production errors.

Checklist

  • npm run typecheck passes; eslint --max-warnings 0 clean on touched files
  • observe-action-result suite green (48 tests)
  • No migrations, no RLS changes, no secrets, no UI changes

🤖 Generated with Claude Code

https://claude.ai/code/session_019WJ1Gzjj9MT3UsxhMoMzSV


Generated by Claude Code

…ected

Sentry alerted at 23:11 UTC on /golf/dashboard/coachhelm: a rounds-less
player loading the page fires FOUR sibling soft failures, and #804 only
gave getPlayerShotAnalytics a stable code. getPlayerProfile,
getPlayerTrendAnalysis, getPlayerShotContext, and the what-if scenario
path in coachhelm-data.ts now carry codes on their empty-state returns
(no_completed_rounds / insufficient_rounds / no_rounds_in_period), all
registered under EXPECTED_EMPTY_STATE_CODES — 'info', skipSentry, same
convention as #804. A brand-new player browsing CoachHelm is not a
production error.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WJ1Gzjj9MT3UsxhMoMzSV
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
helmv3 Ignored Ignored Preview Jul 12, 2026 1:39am

Request Review

@supabase

supabase Bot commented Jul 12, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project qmnssrrolpinvwjjnufo because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b0263198-8751-40d9-9752-2c35028be6a7

📥 Commits

Reviewing files that changed from the base of the PR and between 1721489 and 8864372.

📒 Files selected for processing (2)
  • src/app/golf/actions/coachhelm-data.ts
  • src/lib/admin/__tests__/observe-action-result.test.ts

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of missing golf data by clearly distinguishing: no completed rounds, insufficient usable rounds, and no rounds in the selected period.
    • Added stable, machine-readable soft-failure codes so the client can reliably classify empty/incomplete results.
    • Reclassified genuinely empty routine outcomes as informational events for cleaner diagnostics.
  • Tests
    • Extended coverage to verify the new empty-state classifications and that informational soft failures are logged with the expected behavior and metadata.

Walkthrough

Server action errors now include stable codes for routine missing-data conditions. Golf actions distinguish absent rounds and insufficient usable data from completed rounds lacking required score fields, while the observer classifies recognized codes as informational empty states.

Changes

Empty-State Error Classification

Layer / File(s) Summary
Action error contract and codes
src/app/golf/actions/coachhelm-data.ts
ActionError gains an optional machine-readable code; profile, trend, shot-context, and what-if actions add stable codes and completed-round count checks.【src/app/golf/actions/coachhelm-data.ts:104-109】【src/app/golf/actions/coachhelm-data.ts:300-317】【src/app/golf/actions/coachhelm-data.ts:617-633】【src/app/golf/actions/coachhelm-data.ts:756】【src/app/golf/actions/coachhelm-data.ts:1069-1084】
Expected empty-state classification and validation
src/lib/admin/observe-action-result.ts, src/lib/admin/__tests__/observe-action-result.test.ts
The observer documents and registers the expanded empty-state code set; tests verify classification and informational logging with Sentry skipped.【src/lib/admin/observe-action-result.ts:51-67】【src/lib/admin/tests/observe-action-result.test.ts:76-99】

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GolfAction
  participant RoundData
  participant ActionObserver
  participant ServerLogger
  GolfAction->>RoundData: query completed-round and scoring data
  RoundData-->>GolfAction: empty, insufficient, or unusable results
  GolfAction->>RoundData: count completed rounds when needed
  RoundData-->>GolfAction: completed-round count
  GolfAction-->>ActionObserver: return coded soft failure
  ActionObserver->>ServerLogger: log expected empty state at info severity
Loading
🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Conventional Commits ❓ Inconclusive HEAD commit subject matches the regex, but the actual PR title isn’t available here, so the title half can’t be verified. Provide the PR title or GitHub PR metadata so the title can be checked against the regex.
✅ Passed checks (11 passed)
Check name Status Explanation
Title check ✅ Passed Conventional Commits is used and the golf scope matches the CoachHelm empty-state fix.
Description check ✅ Passed The description directly describes the CoachHelm empty-state error-code changes in coachhelm-data.ts and the observer.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
No Service-Role In Client Bundles ✅ Passed Added hunks only set action error codes and observer contract; no changed lines in touched files reference SUPABASE_SERVICE_ROLE_KEY or create a service-role client.
Rls Coverage On New Tables ✅ Passed No migration files were changed; HEAD^..HEAD only touches src/lib/admin/observe-action-result.ts, so the RLS-on-new-tables check is not applicable.
Auth Check In Server Actions ✅ Passed PASS: The only changed action file exports thin wrappers; all Supabase reads in impls occur after supabase.auth.getUser() (e.g. 274→287, 574→587, 694→712, 882→894, 997→1009).
Sport-Prefixed Table Names ✅ Passed All changed Supabase queries in coachhelm-data.ts use golf_* tables (286-288, 323-369, 587-589, 712-730, 820-825, 895-896, 1009-1011, 1060-1082); observe-action-result has no queries.
No Destructive Writes ✅ Passed PASS: coachhelm-data.ts only does SELECT-based reads and returns codes (270-300, 570-600, 689-723, 992-1036); observe-action-result.ts only classifies codes (57-67).
No Edits To Historical Migrations ✅ Passed PASS: merge-base diff only touches src/app/golf/actions/coachhelm-data.ts and src/lib/admin/observe-action-result.ts; no supabase/migrations/** files changed.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/golf-empty-state-codes
  • 🛠️ helm safety pass
  • 🛠️ dashboard ux pass
  • 🛠️ rls test pass

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


Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR reclassifies expected CoachHelm empty states so new-player pages do not create production-error noise. The main changes are:

  • Adds an optional stable code field to CoachHelm action errors.
  • Tags remaining CoachHelm no-rounds and insufficient-rounds returns with expected empty-state codes.
  • Registers those codes in the soft-failure observer with info-level, skip-Sentry handling.
  • Adds tests for the new observer classification behavior.

Confidence Score: 4/5

This is close, but the scoring-data case should be fixed before merging.

  • The intended no-rounds empty states are now routed through the expected observer path.
  • The profile branch now separates true no-rounds from completed rounds with missing scores.
  • The trend and what-if branches can still label missing score_to_par data as expected insufficient data.

src/app/golf/actions/coachhelm-data.ts

Important Files Changed

Filename Overview
src/app/golf/actions/coachhelm-data.ts Adds expected empty-state codes to CoachHelm action failures, with one remaining data-quality case that can be mislabeled as insufficient data.
src/lib/admin/observe-action-result.ts Extends the expected empty-state code set and documents the global classification contract.
src/lib/admin/tests/observe-action-result.test.ts Adds observer tests for the new CoachHelm empty-state codes and skip-Sentry routing.

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/app/golf/actions/coachhelm-data.ts:610-611
**Missing Score Guard**

`insufficient_rounds` is now treated as an expected empty state for every observed action. This branch counts only completed rounds where `score_to_par` is already populated, so a player with 3+ completed rounds whose score-to-par values failed to populate can still return `insufficient_rounds`. That logs as info with Sentry skipped instead of surfacing the scoring-data failure. The same check is needed before the what-if branch returns the same code.

Reviews (2): Last reviewed commit: "fix(golf): distinguish unscored complete..." | Re-trigger Greptile

Comment thread src/app/golf/actions/coachhelm-data.ts
…_STATE_CODES

Greptile on #807: classification is by code globally, so a future real
failure reusing one of these codes would silently skip Sentry. Spell that
contract out where the set is defined.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WJ1Gzjj9MT3UsxhMoMzSV

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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/app/golf/actions/coachhelm-data.ts`:
- Around line 299-300: Update the rounds handling around the completed-round
query and the roundsData empty check to distinguish players with no completed
rounds from players whose completed rounds lack usable scores. Only assign the
globally expected `no_completed_rounds` code when the completed-round query
itself is empty; return a separate data-quality failure without that code when
completed rounds exist but roundsData is empty after score filtering.

In `@src/lib/admin/observe-action-result.ts`:
- Around line 57-67: Extend the tests around the existing expected-empty-state
assertions in observe-action-result.test.ts to cover no_rounds_in_period,
no_completed_rounds, and insufficient_rounds. Verify each code takes the
expected empty-state classification and exercises the info/skipSentry path,
preserving the existing coverage for previously registered codes.
🪄 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: ae24a807-b43e-44f7-bd99-0dda0ad01866

📥 Commits

Reviewing files that changed from the base of the PR and between 784138e and 1721489.

📒 Files selected for processing (2)
  • src/app/golf/actions/coachhelm-data.ts
  • src/lib/admin/observe-action-result.ts

Comment thread src/app/golf/actions/coachhelm-data.ts
Comment thread src/lib/admin/observe-action-result.ts
… new empty-state codes

getPlayerProfile's round query filters total_score IS NOT NULL, so an
empty result was ambiguous: the globally-silenced no_completed_rounds
code could also fire when completed rounds exist but all lack scores —
a data-quality failure that must stay visible per the
EXPECTED_EMPTY_STATE_CODES contract. The empty branch now runs a
head-count of completed rounds and only attaches the code when the
player truly has none.

Also adds regression coverage for the three newly registered codes
(no_rounds_in_period, no_completed_rounds, insufficient_rounds)
asserting the info/skipSentry classification path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WJ1Gzjj9MT3UsxhMoMzSV
@njrini99-code njrini99-code marked this pull request as ready for review July 12, 2026 01:34
@njrini99-code njrini99-code enabled auto-merge July 12, 2026 01:34
@njrini99-code njrini99-code disabled auto-merge July 12, 2026 01:35
@njrini99-code njrini99-code enabled auto-merge July 12, 2026 01:35
@njrini99-code njrini99-code disabled auto-merge July 12, 2026 01:35
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Classify CoachHelm no-rounds empty states via stable codes (skip Sentry)

🐞 Bug fix 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add stable empty-state codes to CoachHelm data actions to prevent Sentry noise.
• Distinguish true data-quality issues from genuine no-rounds/insufficient-rounds states.
• Extend soft-failure observer + tests to treat these codes as info/skipSentry globally.
Diagram

graph TD
  UI["CoachHelm UI"] --> Actions["coachhelm-data actions"] --> Env["ActionResult {success:false, code}"] --> Obs["observeActionSoftFailure"] --> Dec{"Code in EXPECTED_EMPTY_STATE_CODES?"}
  Dec -->|"yes"| Info["logServerEvent (info, skipSentry)"]
  Dec -->|"no"| Err["logServerError (warning/error)"] --> Sentry{{"Sentry"}}
  subgraph Legend
    direction LR
    _ui["UI"] ~~~ _svc["Server module"] ~~~ _dec{"Decision"} ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Namespace empty-state codes per action (e.g., coachhelm.no_completed_rounds)
  • ➕ Reduces risk of global code collisions causing unrelated failures to skip Sentry
  • ➕ Makes it easier to audit ownership/usage of codes across the repo
  • ➖ More verbose codes and migration overhead for any existing references
  • ➖ Doesn’t eliminate the need for a global allowlist, just changes identifiers
2. Centralize codes into a typed enum/const module shared across actions
  • ➕ Avoids stringly-typed codes and typos across call sites
  • ➕ Provides a single discoverable place for code documentation and intended semantics
  • ➖ Slightly more upfront refactor; may require touching multiple actions to adopt consistently
  • ➖ Can be overkill if only a few actions use codes

Recommendation: Current approach is good for an urgent Sentry-noise fix: it adds stable codes where the empty states occur and registers them in the observer with an explicit global-silence contract. If code reuse becomes a concern, prefer namespacing + a shared typed constants module to reduce collision risk while keeping the observer logic unchanged.

Files changed (3) +61 / -6

Bug fix (2) +36 / -6
coachhelm-data.tsAttach stable empty-state codes to CoachHelm data action failures +23/-4

Attach stable empty-state codes to CoachHelm data action failures

• Extends the action error envelope with an optional 'code' field for observer classification. Adds 'no_completed_rounds', 'no_rounds_in_period', and 'insufficient_rounds' codes to the relevant empty-state returns, and introduces an extra query in getPlayerProfile to distinguish true no-rounds from completed rounds missing scores (data-quality issue).

src/app/golf/actions/coachhelm-data.ts

observe-action-result.tsRegister CoachHelm empty-state codes and document global classification contract +13/-2

Register CoachHelm empty-state codes and document global classification contract

• Adds the new CoachHelm empty-state codes to 'EXPECTED_EMPTY_STATE_CODES' and expands comments to clarify that code-based classification is global (a listed code silences Sentry everywhere). Updates inline docs describing which actions use which codes.

src/lib/admin/observe-action-result.ts

Tests (1) +25 / -0
observe-action-result.test.tsTest that new CoachHelm empty-state codes log as info and skip Sentry +25/-0

Test that new CoachHelm empty-state codes log as info and skip Sentry

• Adds coverage that the new CoachHelm codes are recognized as expected empty states. Verifies 'observeActionSoftFailure' routes these envelopes to 'logServerEvent' at 'info' severity with 'skipSentry: true'.

src/lib/admin/tests/observe-action-result.test.ts

Comment on lines 610 to 611
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Missing Score Guard

insufficient_rounds is now treated as an expected empty state for every observed action. This branch counts only completed rounds where score_to_par is already populated, so a player with 3+ completed rounds whose score-to-par values failed to populate can still return insufficient_rounds. That logs as info with Sentry skipped instead of surfacing the scoring-data failure. The same check is needed before the what-if branch returns the same code.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/golf/actions/coachhelm-data.ts
Line: 610-611

Comment:
**Missing Score Guard**

`insufficient_rounds` is now treated as an expected empty state for every observed action. This branch counts only completed rounds where `score_to_par` is already populated, so a player with 3+ completed rounds whose score-to-par values failed to populate can still return `insufficient_rounds`. That logs as info with Sentry skipped instead of surfacing the scoring-data failure. The same check is needed before the what-if branch returns the same code.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a35da6f — both insufficient_rounds sites (trend analysis and the what-if scenario path) now run the same unfiltered completed-round head-count as the profile fix: the globally-silenced code only attaches when the player truly has fewer than 3 completed rounds; 3+ completed rounds lacking score data returns an uncoded data-quality error that stays visible to Sentry. Follow-up 8864372 also guards all three count queries' own error, so a failed count can't fall through to the silenced path either.


Generated by Claude Code

@qodo-code-review

qodo-code-review Bot commented Jul 12, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 98 rules

Grey Divider


Action required

1. Missing intelligence_dashboard registry mapping ✗ Dismissed 📘 Rule violation ⚙ Maintainability
Description
The new soft-failure context uses feature: 'intelligence_dashboard', but there is no corresponding
entry (or explicit gap marker) for intelligence_dashboard in memory/registry.yml. This violates
the feature-awareness registry requirement and can break ownership/mapping workflows that depend on
the registry.
Code

src/lib/admin/tests/observe-action-result.test.ts[89]

+      { action: 'getPlayerProfile', sport: 'golf', feature: 'intelligence_dashboard', source: 'server_action' },
Relevance

⭐⭐⭐ High

Registry updates for new feature identifiers were previously requested/partially accepted (PR296).

PR-#296

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The test change introduces a clearly named feature identifier intelligence_dashboard in an
action-observability context, but memory/registry.yml’s features: section does not define an
intelligence_dashboard entry or gap marker alongside other feature keys.

Rule 1519254: Update feature registry mappings when touching unmapped features
src/lib/admin/tests/observe-action-result.test.ts[82-90]
memory/registry.yml[1-110]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new feature identifier `intelligence_dashboard` is referenced in code, but `memory/registry.yml` does not contain a mapping (or explicit awareness-gap marker) for it.

## Issue Context
The PR adds/uses `{ feature: 'intelligence_dashboard' }` in the soft-failure observer context. Compliance requires that any feature identifier touched in code is mapped in `memory/registry.yml` or explicitly marked as an unmapped awareness gap.

## Fix Focus Areas
- memory/registry.yml[1-120]
- src/lib/admin/__tests__/observe-action-result.test.ts[82-90]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Count query error silenced ✓ Resolved 🐞 Bug ☼ Reliability
Description
In getPlayerProfileImpl, the fallback completed-rounds count query ignores the Supabase error;
if that query fails, completedCount is null and the code returns `{ code: 'no_completed_rounds'
}. Since observeActionSoftFailure` treats that code as an expected empty state (info +
skipSentry), genuine backend failures can be hidden from Sentry/error dashboards.
Code

src/app/golf/actions/coachhelm-data.ts[R305-313]

+      const { count: completedCount } = await supabase
+        .from('golf_rounds')
+        .select('id', { count: 'exact', head: true })
+        .eq('player_id', playerId)
+        .eq('status', 'completed');
+      if ((completedCount ?? 0) > 0) {
+        return { success: false, error: 'Completed rounds exist but are missing score data' };
+      }
+      return { success: false, error: 'No completed rounds found for this player', code: 'no_completed_rounds' };
Relevance

⭐⭐⭐ High

Team accepted limiting skipSentry to known handled cases; avoids silencing real backend failures
(PR208).

PR-#208

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The fallback count query in getPlayerProfileImpl does not read/handle Supabase’s error, and it
returns code: 'no_completed_rounds' when completedCount is null/0. The observer classifies any
envelope carrying that code as an expected empty state and sets skipSentry for non-error
severities, so misclassification suppresses Sentry for real failures.

src/app/golf/actions/coachhelm-data.ts[284-314]
src/lib/admin/observe-action-result.ts[52-68]
src/lib/admin/observe-action-result.ts[70-83]
src/lib/admin/observe-action-result.ts[112-166]
PR-#208

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getPlayerProfileImpl` runs a secondary Supabase query to distinguish “brand-new player” from “completed rounds with missing score data”, but it destructures only `count` and ignores the query’s `error`. If that query fails (RLS, network, PostgREST error, etc.), `completedCount` can be `null` and the function incorrectly returns the globally-silenced empty-state code `no_completed_rounds`, which suppresses Sentry.

## Issue Context
Empty-state classification is global by `code`; any action returning a code in `EXPECTED_EMPTY_STATE_CODES` is logged at `info` with `skipSentry: true`.

## Fix Focus Areas
- src/app/golf/actions/coachhelm-data.ts[299-314]
- src/lib/admin/observe-action-result.ts[52-68]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Unneeded exact count query ✗ Dismissed 🐞 Bug ➹ Performance
Description
getPlayerProfileImpl uses count: 'exact' to decide only whether any completed rounds exist (`>
0`), which is more DB work than necessary. Switching to an existence check (e.g.,
select('id').limit(1) or a non-exact count) avoids a full exact count on the empty-state path.
Code

src/app/golf/actions/coachhelm-data.ts[R305-309]

+      const { count: completedCount } = await supabase
+        .from('golf_rounds')
+        .select('id', { count: 'exact', head: true })
+        .eq('player_id', playerId)
+        .eq('status', 'completed');
Relevance

⭐⭐ Medium

No direct precedent; performance/query-tuning suggestions often rejected, including count-related
ones (PR787).

PR-#787

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code requests an exact count and then immediately checks only > 0, so an existence query would
produce the same branching behavior with less DB work.

src/app/golf/actions/coachhelm-data.ts[299-313]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The fallback query uses an exact count even though the control flow only needs a boolean “any completed rounds exist”. Exact counts can be materially more expensive than existence queries.

## Issue Context
This query runs only when the primary rounds query returns an empty set, i.e., on the empty-state path (brand-new players or data-quality issues).

## Fix Focus Areas
- src/app/golf/actions/coachhelm-data.ts[299-313]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread src/lib/admin/__tests__/observe-action-result.test.ts
Comment thread src/app/golf/actions/coachhelm-data.ts Outdated
Comment thread src/app/golf/actions/coachhelm-data.ts Outdated
claude added 2 commits July 12, 2026 01:38
Same contract violation as the getPlayerProfile fix in aeda380, in the
two insufficient_rounds sites: getPlayerTrendAnalysis and the what-if
scenario path both filter score_to_par IS NOT NULL, so a player with 3+
completed rounds whose scores never populated would be silenced as an
expected empty state. Both branches now head-count completed rounds and
only attach the globally-silenced code when the player truly has fewer
than 3; otherwise they return an uncoded data-quality error that stays
visible to Sentry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WJ1Gzjj9MT3UsxhMoMzSV
… states

Qodo caught that the three fallback head-count queries ignored the
Supabase error: a failed count leaves count null, which fell through to
the globally-silenced empty-state code — hiding a genuine backend
failure from Sentry. All three guards now return an uncoded failure
when the count query errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019WJ1Gzjj9MT3UsxhMoMzSV
@njrini99-code njrini99-code merged commit 3237633 into main Jul 12, 2026
43 of 44 checks passed
@njrini99-code njrini99-code deleted the fix/golf-empty-state-codes branch July 12, 2026 01:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants