Bounties and claims
A lightweight way to take an open-source task to a merged PR — claim it, work it in an isolated worktree, and track it from claim to merge.
The lead surface is contribute: credential-building issues
ranked by winnability — how likely a maintainer is to merge your PR. First-party
founder bounties also appear in the default bounty surface because someone posted
them here and is waiting to pay. Scraped paid open-source bounties stay opt-in
behind --priced; the claim lifecycle below works for both.
Finding a bounty
terminalhire contribute # open-source issues ranked by winnability
terminalhire bounties # first-party founder bounties
terminalhire bounties --priced # add scraped paid open-source bounties
terminalhire bounties with no flag shows first-party founder postings.
Pass --priced to add scraped paid open-source supply, with payout, effort,
source repo, and a link straight to the source platform. Terminalhire takes
no cut from those scraped or open-source bounties.
For a first-party founder bounty, the posted price is the gross amount. The founder saves a card before publishing, but posting, claiming, approving, submitting, and rejecting work do not create a charge. The founder is charged only when they explicitly accept completed work. After Stripe confirms the charge, the developer receives 90%, TerminalHire retains a 10% platform fee, and TerminalHire absorbs Stripe's processing fee.
A saved card is not escrow and does not guarantee that a later charge will succeed. If payment fails or needs more authentication, the work remains unaccepted and no payout is recorded. Everything that surfaces is held to the same trust standard as the rest of the feed: only tasks from projects with a real track record, not a scraped pile of abandoned issues.
The claim lifecycle
Claiming is a commitment, so the flow is preview-first:
terminalhire claim preview <id|issueUrl> # read-only — see what you'd be taking
terminalhire claim record <id|issueUrl> # claim it locally
terminalhire claim attach <id> --worktree … --branch … # link it to your working branch
terminalhire claim submit <id> [--worktree <path>] # mark it submitted
terminalhire claim status [<id>] # founder approval or source PR merge
terminalhire claim list [--active] # your claims + accepted-PR rate
terminalhire claim release <id> # give it up
If a review says revise, fix its blockers and rerun the review gate before
marking the claim ready with terminalhire claim update <id> ready. There is no
separate claim re-review command. The ready transition is your explicit
post-review attestation; it clears the old revise verdict so the next
claim submit <id> is accepted.
An MCP host can call claim_preview and claim_record for the first two steps.
The latter records intent in the same local ledger. MCP has no start, attach, or
submit tool: forking, worktree creation, pushing, and opening a PR remain
human-invoked CLI actions.
Claim state lives locally. Preview/record make public GitHub reads for issue
freshness, contention, and repository policy; recording an open-source claim
sends no developer profile or claim data. claim start and claim submit are
separate CLI paths that can write to GitHub after their own human-facing gates.
Founder postings are the one exception, and it is deliberate. A founder
bounty is paid work on someone's own repository, so the founder has to know who
took it — a claim nobody can see is a claim nobody can pay. Recording one
registers it with terminalhire: your GitHub identity, the posting, and nothing
else. The registration is fail-closed, so if it cannot reach the server it
records nothing rather than leaving you holding a claim the founder never saw.
For paid founder work, a successful claim also stores an encrypted push-only
token and enables the existing score-free claim sync. That lets claim status
report a later approval without a refresh monitor running. The claim output
names this setup; revoke it any time with
terminalhire claim --push --revoke. With no stored token, claim status
still prints local state and the command that completes setup.
Everything else about the claim still lives on your machine, and open-source
claims are untouched by this — they never contact terminalhire at all.
Paid founder work also requires a payout-ready Stripe account before the server records your claim. Open the dashboard’s Payouts card, choose Set up payouts, and complete Stripe’s hosted onboarding. TerminalHire stores the connected-account ID and readiness state, not your bank, identity, or tax details. Free and grandfathered claims do not use this gate.
Once you have an active claim, contribute and bounties --priced mark the
row you already claimed with a ● claimed by you badge and swap its call to
action to terminalhire claim status <id>, so you can jump straight to polling
your PR instead of re-claiming it. The badge reads the same local claims ledger
and disappears when a claim reaches a terminal state (merged or abandoned).
Working a claim — the worktree flow
The --worktree and --branch flags above exist because the safe way to do a
bounty is in an isolated git worktree —
a throwaway checkout on its own branch, separate from whatever you're already
working on. Your main work never mixes with the bounty, and if the claim falls
through you just delete the directory.
The flow, start to finish:
terminalhire claim record <id> # 1. claim it locally
git worktree add ../bounty-<id> -b fix/<id> # 2. isolated checkout + branch
terminalhire claim attach <id> --worktree ../bounty-<id> --branch fix/<id>
# 3. do the work in that directory, review your own diff before you trust it
terminalhire claim submit <id> # 4. push + open PR (from anywhere)
Once a claim is attached, submit runs from anywhere — it already knows the
recorded worktree and pushes from there, so you don't have to cd into it or
retype --worktree (pass --worktree <path> only to override, and a path that
doesn't match the recorded one is still refused). If the recorded worktree has
been moved or deleted, submit tells you to re-attach rather than pushing the
wrong thing.
PR body: if a PR-BODY.md file sits at the worktree root, submit
auto-detects it and uses it as the PR description (the always-on AI-assistance
disclosure is still appended). --body-file <path> overrides it; --no-body
skips the auto-detect and falls back to a minimal Closes #N body. The confirm
card shows which body source will be posted before you approve.
No fork yet? If no remote points at your fork of the upstream repo, submit
offers — interactively, after a y/N confirm — to run gh repo fork and add
your fork as a remote called fork, then continues. (It never auto-forks under
--yes; creating a repo on your account always asks first.)
submit is deliberately strict, because a sloppy PR under your GitHub identity is
permanent and it's your reputation on the line. It refuses unless the claim is
marked ready, the recorded branch matches, the tree is clean, and it has a
fork to push to — it pushes to your fork and opens the PR upstream, never
pushes to the upstream repo directly, always asks before pushing, and never
force-pushes.
One hard rule worth knowing up front: if the target repo's contribution policy prohibits AI-generated or AI-assisted contributions, don't claim it — that work isn't mergeable there, and routing around the policy is never the answer. Release the claim instead.
Founder postings — the private-repo loop
A bounty posted by a founder on their own (often private) repository works
through the same claim verbs, but the mechanics invert: you never fork,
never clone, and never hold a credential to the founder's repo. The platform
delivers a scoped slice of files to you, and carries your patch back.
terminalhire claim record <id> # registers the claim ON THE SERVER too
terminalhire claim slice <id> # fetch the files the founder shared, with contents
# … write the fix against the slice …
terminalhire claim submit <id> # patch-out over HTTP — no fork, no PR from your account
terminalhire claim runs <id> # the founder's own CI verdict on your patch
What's different from the open-source flow:
claim recordregisters server-side. For a founder posting the claim is bound to your verified GitHub identity on the server — that registration is what a later paid credential rests on, and it's why the server refuses any request that tries to name a claimant in the body. On an approval-only posting the claim then sits in an approval-pending state: nothing is wrong and there is nothing to retry; once the founder approves you, slice delivery and patch submission open up.claim sliceis the repo access. You get exactly the files the founder confirmed, bytes included — not a clone. A patch touching anything outside that slice is refused by the server, so the slice is also the honest statement of scope.claim submitsends a patch, not a PR. The CLI builds a unified diff from your worktree and submits it over HTTP; the platform applies it to a branch on the founder's repo with your commit authorship. This call always asks for a fresh browser verification (a single-use proof), by design — it is the one action that writes to someone else's repository under your name.claim runsreports their CI. The founder's own workflow runs against the branch; you read the verdict — passed, failed, or still running — from your terminal.
Contention is expected here too: an open-mode posting can hold claims from several developers at once, and the first accepted patch wins. Registering does not lock anyone else out.
The paid credential depends on the payment standing. An accepted claim shows on your public profile as paid work for as long as the money stays with us. If the founder wins a card dispute, or the charge is refunded, the entry stops showing: we will not vouch for money that went back. An open dispute hides it until the card network decides, and a dispute that closes in our favour brings it back. The posting stays in your own settlement view throughout, so you can tell a hidden entry from a lost one.
Who else is racing this issue (contention)
A day-sized issue is often something more than one person is eyeing, so both
claim status and the submit confirm card surface the open PRs already
referencing the issue — the people you may be racing to merge:
⚠ contention: 2 open PR(s) reference this issue — Fix the widget race
- #128 by @someone (opened 3h ago) https://github.com/acme/widget/pull/128
- #131 by @another (opened 12m ago) https://github.com/acme/widget/pull/131 [NEW]
tip: if scopes overlap, comment on the ISSUE comparing scope — generous + compatible wins triage.
- On
claim status, a PR that showed up since your last check is flagged[NEW](the very first check flags nothing — it has no prior snapshot to compare against). That snapshot is local-only — like the rest of your claim state it never leaves your machine. - On
submit, the same list appears on the confirm card as a last-look nudge before you push. It's advisory only — it never blocks the submit or adds a second confirm; if the check can't reach GitHub it's simply omitted rather than shown as "0". - The nudge is deliberate: if your scope overlaps a competing PR, a quick, generous comment on the issue comparing scope is usually what wins triage — maintainers merge the contribution that's easiest to say yes to.
The full playbook — when to stake a claim before you start, how to pace so a claim never reads as abandoned, and what losing a race gracefully looks like — is in The social layer.
Known limitation: contention is detected by matching #<issue-number> in a
PR's title or body, so a competing PR that never mentions the issue number won't
be counted. Treat the number as a floor, not a guarantee that you're alone.
Why the accepted-PR rate matters
A merged PR from a bounty claim isn't just a paycheck — it's exactly the kind of third-party-attested contribution your Proof of Work credential is built from. Your accepted-PR rate is an honest signal of follow-through: how often a claim turns into a merge, not just an attempt.
Where it shows up
terminalhire bounties also feeds the ambient spinner while you work in
Claude Code — see Claude Code surfaces — and the
same feed is reachable from any MCP-connected editor.
On the web, the dashboard has a dedicated Contributions tab that lists the entire open-source pool (bounties and contribution cards), not just the items that match your profile: everything that overlaps your skill tags comes first, ranked best-fit, and the rest of the pool follows as a browsable tail — matched on-device, like everything else on the dashboard. Job matches stay on their own Matches tab.
Founder-posted work is different from the opt-in snapshot below. Once you claim a
founder posting, the dashboard Overview shows a separate Founder work card
automatically. It is server-authoritative and shows the current claim state plus the
latest event. Before approval it identifies the posting only by its opaque
founder/b-… handle — the repository name, title, spec, founder identity, and event
notes are not sent to that view.
Show your claims on the dashboard (opt-in)
Claims live on your machine. If you want them visible on your dashboard too, you can push a snapshot — explicitly, and only after you've seen exactly what's in it:
terminalhire claim --push # consented snapshot → dashboard Claims card
terminalhire claim --push --revoke # delete every pushed claim from the server
claim --push requires a linked session (terminalhire login first), shows you
the payload, and waits for your consent before sending. The snapshot is
score-free by construction — it carries only the claim's kind, repo, state,
PR link, merged flag, and timestamps. No match scores, no profile data, nothing
about how the claim was ranked ever leaves your machine.
The push is a one-time snapshot, not a live sync: re-run claim --push whenever
you want the dashboard to reflect your latest state, and --push --revoke
hard-deletes the server copy from this machine at any time.
One thing does stay current on its own: for a pushed claim that already carries a PR link, whether that PR has been merged refreshes automatically from public GitHub when you open your dashboard — so a claim flips to merged without a fresh push once the maintainer merges. Only the merge state of an already-shared PR is refreshed this way; everything else still updates only when you push.