skip to content
all docs

Postings 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 postings also appear in the default 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 work

terminalhire contribute          # open-source issues ranked by winnability
terminalhire bounties            # first-party postings
terminalhire bounties --priced   # add scraped paid open-source bounties

terminalhire bounties with no flag shows first-party 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 posting, the posted price is the gross amount. The poster saves a card before publishing, but posting, claiming, approving, submitting, and rejecting work do not create a charge. The poster 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>]       # submit — id optional from the claim's worktree
terminalhire claim status [<id>]                  # poster approval or source PR merge
terminalhire claim list [--active]                # your claims + accepted-PR rate
terminalhire claim notes                          # what the poster wrote on your claims
terminalhire claim release <id>                   # give it up

These commands talk to terminalhire.com. If you copied one from the dev deployment or from a web app running on your own machine, it needs a prefix naming that deployment, and your terminal needs a session there too — otherwise you get no-such-claim about a claim that plainly exists in the browser tab in front of you. The pages print the prefix for you; CLI reference lists the variables.

When the poster says something. They can leave a note on your claim, or ask for another pass. Nothing is emailed or pushed to you — if you enrolled with claim --push --keep-updated, your statusline shows 📝 N from your founder, and terminalhire claim notes prints them. Reading them clears the badge. Your agent can run it too, so "what did the poster say?" has an answer without you leaving the terminal.

A note changes nothing about your claim. "Asked for changes" marks it as needing another pass, and the two are labelled differently on purpose.

What the poster sees back. That same enrolment is two-way. While you work a claim on someone's posting, meaningful steps — recording it, starting, attaching a workspace, a state change, fetching the slice, submitting — tell their posting page that you were at the keyboard, so it can say "active 4 minutes ago". It carries no note, no progress and no rating: only a time, only on the posting you claimed, and only to the poster. Without --keep-updated none of it is sent, and claim --push --revoke stops it.

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.

First-party postings are the one exception, and it is deliberate. A posting is paid work on someone's own repository, so the poster 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 poster never saw. For paid posted 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 posted 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 work a claim 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 claim, and if it falls through you just delete the directory.

The flow, start to finish:

terminalhire claim record <id>                       # 1. claim it locally
git worktree add ../claim-<id> -b fix/<id>           # 2. isolated checkout + branch
terminalhire claim attach <id> --worktree ../claim-<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.

First-party postings — the private-repo loop

Work posted on someone's own (often private) repository runs through the same claim verbs, but the mechanics invert: you never fork, and you never push. The platform carries your patch back instead.

How much of the repository reaches you depends on the posting: a set of files the poster listed, a slice we proposed from their tree — or from a file list they pasted, when our App is not on the repo — and they confirmed, or the whole repository through a read credential. claim slice is where you find out which — the listing does not say, and neither does the claim page. On an approval-only posting you do not find out then either: the approval check runs before the tier is considered, so until the poster approves you the answer is that the files are not available to you, with no scope named.

On a whole-repository posting your CLI clones, using a credential scoped to that one repo and read-only — it cannot write, so it is no route to pushing. claim slice uses it for a single fetch and writes it nowhere on disk, leaving a shallow checkout with no remote to fetch against. On the other two tiers you are issued no credential at all and the files arrive as a copy.

terminalhire claim start <id> --watch   # records the claim (server-side too) AND delivers
                                        # your workspace; --watch waits out a pending approval
# … write the fix in the delivered workspace …
terminalhire claim submit <id>          # patch-out over HTTP — no fork, no PR from your account
terminalhire claim runs <id>            # the poster’s own CI verdict on your patch

One command, start to workspace. claim record and claim slice still exist and still work as separate steps; claim start runs the same record path and then the same delivery, so there is nothing extra to learn when a step needs re-running. The command finishes by leaving you in the delivered workspace — your own shell, started there, so the next thing you type runs against the files you were given. exit brings you back. --open <agent> starts that agent there instead, and --stay prints the path and returns.

The delivered workspace carries a .terminalhire/ directory — the poster's brief (BRIEF.md, when they wrote one), how the work is checked (VERIFY.md), and orientation for a coding agent opened there (AGENTS.md). It is excluded from ordinary staging and a patch that force-adds it is refused at submit.

What's different from the open-source flow:

  • claim record registers server-side. For a first-party 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 poster approves you, slice delivery and patch submission open up.
  • claim slice is the repo access, whatever the tier. On a scoped posting you get exactly the files the poster confirmed, bytes included; on a whole-repository posting the same command clones. Either way it is a statement about DELIVERY — what you are given, not a fence around what you may send back. The server checks what a patch is and does — CI config, git internals, lockfiles, install scripts, binary or unreadable content, network calls, dependency changes — and routes anything it flags to the poster. It does not hold a file list to check your patch against. th run is where a file list still binds, and only on a scoped posting: it refuses a diff reaching outside the slice locally, before any container starts.
  • claim submit sends 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 poster'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 runs reports their CI. The poster's own workflow runs against the branch; you read the verdict — passed, failed, or still running — from your terminal.

On the poster's side, each claim links to a signed-in review page. It shows the stored branch and exact commit, provider preview when one was recorded, CI status and failing jobs, the bounded log tail, touched paths, and the claim timeline. Missing evidence says Not recorded; the page does not fill gaps with estimated test counts or local-session claims. The same page records the poster's accept or reject decision and follows the posting's stored payment policy for a reserved payout, acceptance-time payment, or legacy free work.

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 poster 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 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.

Posted work is different from the opt-in snapshot below. Once you claim a first-party posting, the dashboard Overview shows a separate your active 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, poster 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.