Skip to content

chore(api): drop the unused v_challenge_disbursements view#873

Merged
rickyrombo merged 1 commit into
mainfrom
mp/drop-v-challenge-disbursements
May 30, 2026
Merged

chore(api): drop the unused v_challenge_disbursements view#873
rickyrombo merged 1 commit into
mainfrom
mp/drop-v-challenge-disbursements

Conversation

@rickyrombo
Copy link
Copy Markdown
Contributor

Stacked on #872. Merge and migrate only after #872's binary is deployed.

#872 migrates every API read off v_challenge_disbursements, leaving it unused. This PR removes it:

  • migration 0208DROP VIEW IF EXISTS v_challenge_disbursements;
  • deletes the ddl/views/v_challenge_disbursements.sql definition
  • regenerates sql/01_schema.sql (clean −23-line, view-only) and the migration tracker

Why split from #872

0208 drops the view while the old binary still references it. Shipping the read migrations first (#872) means that by the time this migration runs, no running code touches the view — no missing-relation errors during a rolling deploy.

Rollout

  1. Merge + deploy fix(api): key challenge disbursement reads by specifier, not recipient #872 (binary no longer reads the view; view still present).
  2. Confirm rollout complete.
  3. Merge this; 0208 drops the view.

Note

The deleted ddl/views file still had the pre-0204 case-sensitive join, and since views/ applies after migrations/ it was silently reverting 0204's LOWER(users.wallet) fix — so the deployed view was the broken one. Dropping it removes that footgun (and #872 inlines the correct LOWER join where the resolution is still needed).

🤖 Generated with Claude Code

rickyrombo added a commit that referenced this pull request May 29, 2026
#872)

## Problem

`v_challenge_disbursements` exposes `sol_reward_disbursements` in the
legacy `challenge_disbursements` shape and resolves `user_id` by **INNER
JOIN**ing `users` on the recipient wallet. Any disbursement whose
recipient doesn't resolve to a current user (wallet migrations,
deactivated users, case mismatches) is silently dropped from the view.

Most callers only ask the identity-independent question *"has this
`(challenge_id, specifier)` been disbursed?"* — and a reward is
disbursed exactly once per `(challenge_id, specifier)` on-chain,
regardless of recipient. For those callers, a dropped row makes a
**paid** reward look **unpaid**:

- **`/users/{id}/challenges`** — showed `is_complete=true,
is_disbursed=false`, so the client offered an already-paid reward as
claimable; the claim then failed ("No rewards to claim" / on-chain
"specifier already used"). This was the original bug.
- **`/challenges/undisbursed`** (HTTP handler **and** the sqlc query
used by the claim path) — same: a dropped disbursement makes a paid
specifier claimable, and the claim fails on-chain. This is the exact
failure mode migration `0204` documents.
- **`/challenges/info`** — `weekly_pool_remaining` overstated by any
dropped disbursement.
- **coins redeem** — the double-redeem guard counts 0 and allows a
second redemption.

## Fix

All specifier/challenge-keyed callers now read
`sol_reward_disbursements` directly by `(challenge_id, specifier)`,
aligning them with how the reward manager dedupes disbursements
on-chain. `is_disbursed` on `/users/{id}/challenges` is now keyed by
specifier rather than the querying user.

The one caller that genuinely needs the recipient `user_id` —
**`/challenges/disbursements`** (admin listing) — inlines the `users`
join, using `LOWER(users.wallet) = recipient_eth_address` per migration
`0204` (the stale `ddl/views` file had the pre-`0204` case-sensitive
join and, because `views/` applies after `migrations/`, was silently
reverting the fix — so the deployed view was the broken one; inlining
fixes that too).

The view is now unused but **retained in this PR** so the binary can
deploy before the view is dropped. The drop is a stacked follow-up:
**#873**.

## Why reads, not a data migration

`user_challenges` is indexer-derived; a manual reattribution would be
reverted on reindex and doesn't address the read inconsistency. Keying
reads by specifier is self-healing for past and future occurrences.
(Attribution in `user_challenges` is intentionally left unchanged.)

## Tests

- New `TestUserChallengesDisbursedToDifferentUser`: a disbursement
attributed (by wallet) to a different user now reports
`is_disbursed=true` for the completer.
- Existing `TestGetChallengeDisbursements` covers the inlined join
across all sorts/filters incl. `challenge_user_id`.
- Full `./api` + `./api/dbv1` suites pass.

## Rollout
1. Merge + deploy this (reads no longer touch the view; view still
present).
2. Then merge #873 to drop the view.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Base automatically changed from mp/challenges-is-disbursed-by-specifier to main May 29, 2026 17:10
@rickyrombo rickyrombo force-pushed the mp/drop-v-challenge-disbursements branch from 693f6c9 to 3665a0a Compare May 29, 2026 18:47
All API reads have been migrated off v_challenge_disbursements (#872), so the
compatibility view has no remaining consumers. Drop it via migration 0212,
remove the ddl/views definition, and update the schema dump.

Deploy ordering: merge/migrate this only after #872's binary is rolled out,
so no running code references the dropped view.

Note: the removed ddl/views file still carried the pre-0204 case-sensitive
join and, because views/ applies after migrations/, was silently reverting
the 0204 LOWER(users.wallet) fix. Dropping it removes that footgun.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@rickyrombo rickyrombo force-pushed the mp/drop-v-challenge-disbursements branch from 3665a0a to 4491cce Compare May 29, 2026 23:56
@rickyrombo rickyrombo merged commit 0a02041 into main May 30, 2026
5 checks passed
@rickyrombo rickyrombo deleted the mp/drop-v-challenge-disbursements branch May 30, 2026 00:17
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.

1 participant