Appearance
Epic 21 — Bounty Hunter
Producers post referral bounties for hard-to-find talent. Any user can claim by referring a match. CasTars released if the referred talent is hired. Referenced in spec "AFTER THOUGHTS #7". Distinct from the milestone-based bounty in Epic 13 (CasTars).
BE-BOUNTY-001 — Bounty Hunter model and API
- [x] Done (built 2026-06-14 —
src/services/bountyHunter, migrations20260614175547_bounty_hunter+20260614235340_bounty_project_and_job; escrow viaESCROW_HELDledger source + guarded-flip idempotency)
Files:
- Edit:
prisma/schema.prisma— addTalentBounty,BountyClaimmodels - Run:
pnpm db:migrate - Edit:
src/graphql/schema/index.ts - Create:
src/graphql/resolvers/bounty.ts - Create:
src/services/bounty/index.ts
Schema:
prisma
model TalentBounty {
id String @id @default(cuid())
producerId String
producer ProducerProfile @relation(...)
description String // "Need a bilingual skateboarder, 18-22, based in NYC"
rewardStars Int // CasTars attached as reward
status BountyStatus @default(OPEN) // OPEN | CLAIMED | EXPIRED | CANCELLED
expiresAt DateTime?
claims BountyClaim[]
createdAt DateTime @default(now())
@@map("talent_bounties")
}
model BountyClaim {
id String @id @default(cuid())
bountyId String
bounty TalentBounty @relation(...)
claimantId String // user who referred
talentId String? // TalentProfile referred (if on platform)
externalLink String? // external artist link (if not on platform)
status ClaimStatus @default(PENDING) // PENDING | SHORTLISTED | HIRED | REJECTED
createdAt DateTime @default(now())
@@map("bounty_claims")
}
enum BountyStatus { OPEN CLAIMED EXPIRED CANCELLED }
enum ClaimStatus { PENDING SHORTLISTED HIRED REJECTED }Key logic:
- Producer posts bounty with
rewardStars— CasTars are escrowed (deducted from producer balance immediately, held in aESCROWledger entry). - Any user claims with
claimBounty(bountyId, talentId?, externalLink?)— generates a unique "Bounty Link." - When producer marks the referred talent as
HIRED(viaupdateApplicationStatus), the service auto-releases escrowed CasTars to the claimant's balance. - If bounty expires with no hire, escrowed CasTars are refunded to producer.
Mutations: createBounty(description, rewardStars, expiresAt?), claimBounty(bountyId, talentId?, externalLink?), cancelBounty(bountyId)
Queries: openBounties(first, after): BountyConnection!, myBounties: [TalentBounty!]!, myClaims: [BountyClaim!]!
FE-BOUNTY-001 — Bounty Hunter UI
- [x] Done (built 2026-06-14 —
apps/app/src/pages/bounty,BOUNTY_HUNTERflag + notifications; E2Ebounty-hunter.spec.ts)
Files:
- Create:
apps/app/src/pages/bounty/BountiesPage.tsx— browse open bounties - Create:
apps/app/src/pages/bounty/CreateBountyPage.tsx— producer posts a bounty - Create:
apps/app/src/hooks/useBounties.ts - Edit:
apps/app/src/App.tsx— add/bountiesroute (visible in bottom nav as "Bounty Hunters" tab, already shown in Figma designs)
Description:
- Browse page: list of open bounties with description, reward (CasTars), time remaining, claim count. "Claim" button → opens modal to tag a talent from platform or paste an external link. On claim: shows unique shareable "Bounty Link."
- Create page (producer): description field, reward slider (min 500 CasTars), optional expiry date picker. Shows current CasTars balance and escrow warning. "Post Bounty" confirms and escrows the reward.
- My Claims tab: claimant sees their submitted claims with status chips (Pending / Shortlisted / Hired / Rejected). Shows CasTars earned when status = Hired.