feat: add scout opportunities and claims API (#44) #59
No reviewers
Labels
No Label
ai-changes-requested
ai-failed
ai-merged
ai-pr-opened
ai-reviewed
ai-reviewing
ai-reviewing
ai-working
bug
enhancement
evolution
performance
phase-0
phase-1a
phase-1b
phase-1c
phase-1d
security
tech-debt
todo
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: cal/paper-dynasty-database#59
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/scout-opportunities-claims"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
scout_opportunityandscout_claimPeewee models with BigIntegerField timestamps and JSON card_ids storagePOST/GET/DELETE /api/v2/scout_opportunities— supportsclaimed(bool),expired_before(ms timestamp), andopener_team_idquery filtersPOST/GET/DELETE /api/v2/scout_claims— supportsscout_opportunity_idandclaimed_by_team_idquery filters(scout_opportunity_id, claimed_by_team_id)to prevent duplicate claims at DB levelpaperdynasty_dev)Closes #44
Test plan
POST /api/v2/scout_opportunitiescreates a record and returns it withidGET /api/v2/scout_opportunities?claimed=false&expired_before=<ts>returns unclaimed expired opportunitiesPOST /api/v2/scout_claimscreates a claim and returns it withidcard_idsis returned as a JSON array (not a string) in GET responses🤖 Generated with Claude Code
8f4e539b97to37439626edAI Code Review — REQUEST_CHANGES
Files Reviewed
app/db_engine.py(modified — new models)app/main.py(modified — router registration)app/routers_v2/scout_claims.py(added)app/routers_v2/scout_opportunities.py(added)migrations/2026-03-04_add_scout_opportunities_claims.sql(added)Findings
🔴 Security — BLOCKING
[1] Bearer token logged in plaintext — both new routers repeat the exact pattern fixed by PRs #7 and #8:
Appears in:
scout_claims.py—post_scout_claimanddelete_scout_claimscout_opportunities.py—post_scout_opportunityanddelete_scout_opportunityRemove
{token}from the log message entirely. Just use a generic warning with no token value.🔴 Correctness — BLOCKING
[2]
raise HTTPException(status_code=200, ...)in delete handlers — PR #47 was merged specifically to fix this pattern. Both new delete handlers reintroduce it:scout_claims.py:raise HTTPException(status_code=200, detail=f"Scout claim {claim_id} has been deleted")scout_opportunities.py: same patternReplace with
return {"message": f"Scout claim {claim_id} has been deleted"}(and same for opportunities).[3]
except Exceptioninstead ofexcept peewee.DoesNotExist— PR #48 was merged to fix broad exception catching across all routers. The new files reintroduce it in four places:scout_claims.py—get_one_scout_claim(line ~41) anddelete_scout_claim(line ~65)scout_opportunities.py—get_one_scout_opportunity(line ~56) anddelete_scout_opportunity(line ~96)Replace with
except peewee.DoesNotExist:.🟡 Style & Conventions
[4] Unused import in
scout_claims.py:ScoutOpportunityis imported fromdb_enginebut never used in that module. Remove it.[5]
ScoutOpportunity.opener_teamFK missingon_deletein Peewee model — the SQL migration correctly hasON DELETE CASCADE, but the model definition omits it:The Peewee model and migration are inconsistent; SQLite dev tables won't enforce the cascade.
Suggestions (non-blocking)
card_ids = CharField()defaults to max_length=255. For 26 cards with 5-digit IDs this is borderline (~185 chars). ConsiderTextField()/TEXTto avoid future truncation surprises.claimed=FalseusesNOT IN (subquery). Fine for current scale, but a LEFT JOIN orNOT EXISTSapproach would be more efficient on large tables.Verdict: REQUEST_CHANGES
The domain model and API design are solid. However, three patterns explicitly fixed in recent merged PRs (#7, #8, #47, #48) are reintroduced here: plaintext token logging (security),
raise HTTPException(status_code=200)(correctness), andexcept Exception(exception specificity). Fix these before merging.Automated review by Claude PR Reviewer