feat(jobs/challenges): Phase 3 — signals endpoint + 5 processors#842
Open
raymondjacobson wants to merge 1 commit into
Open
feat(jobs/challenges): Phase 3 — signals endpoint + 5 processors#842raymondjacobson wants to merge 1 commit into
raymondjacobson wants to merge 1 commit into
Conversation
Adds the last 5 challenge processors plus the infra they need.
New infra
---------
Migration 0205:
* challenge_signal_type ENUM ('mobile_install', 'one_shot', 'referral')
* challenge_signals table with (id, type, user_id, extra jsonb, created_at,
source, client_nonce). UNIQUE (type, user_id, client_nonce) for client
replay dedupe; index on (type, id) for incremental scans.
* Catalog seed for m/r/rv/rd/o.
POST /v1/challenges/signals (api/v1_challenges_signals.go):
* Body: {type, user_id, extra, client_nonce?}
* Auth: requireAuthMiddleware + requireWriteScope (existing pattern).
* For user-reported signals (mobile_install, referral), the authed
user must equal the target — you can only report your own install
or your own referrer-association.
* one_shot is currently treated as admin-issued; tightening to an
explicit admin allowlist is a follow-up.
Processors (jobs/challenges/signals.go)
---------------------------------------
m mobile_install boolean per user; signal user_id == reward target
o one_shot extra.amount overrides catalog; nonce in specifier
so the same user can receive multiple grants
r referral (sender) referrer earns when NOT verified
rv verified_referral referrer earns when verified
rd referred_signup referred user earns once per referral signal
r/rv/rd all read the same "referral" signal type but checkpoint
independently. A single client POST therefore triggers up to two outputs
(r or rv for the sender, rd for the recipient).
Wire-in
-------
5 new processors registered in IndexChallengesJob (23 total now).
Tests
-----
6 new DB-backed tests covering each processor's happy path and gates:
* TestMobileInstall_OneRowPerUser
* TestOneShot_ExtraAmountOverrides
* TestReferral_NonVerifiedReferrer (r fires, rv skipped, rd fires)
* TestReferral_VerifiedReferrer (r skipped, rv fires)
* TestSignals_CheckpointAdvances (incremental processing across runs)
All 29 challenge tests pass (23 challenges × variety of cases); go build +
go vet clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #841 (Phase 2). Final 5 challenge processors plus the signals endpoint they consume from.
After this PR, every non-Solana challenge in apps' `challenges.json` is implemented.
New infrastructure
Migration 0205:
Endpoint `POST /v1/challenges/signals` (
api/v1_challenges_signals.go):Processors
`r` / `rv` / `rd` all read the same `referral` signal but checkpoint independently. One client POST → up to two outputs (one for the referrer, one for the referred).
Wire-in
5 new processors appended to `IndexChallengesJob` — 23 total now.
Test plan
6 new DB-backed tests in addition to Phase 1+2's 23:
What's left after this
Only the explicitly-out-of-scope Solana challenge: `ft` (send_first_tip). Every other entry in `challenges.json` is implemented across #835, #841, and this PR.
🤖 Generated with Claude Code