The attorneys' Pool
One Pool for the legal brand's six Services. Attorneys and patent practitioners join, the verifier checks each credential, Tasks are offered one member at a time, conflicts stay private, and tiers come from outcomes.
legal's wordsmatter is a jobasset is a subjectreview is a tasksigner is a membernetwork is a pooloutcome is a end
One Pool serves all six Services. Attorneys and patent practitioners join on their own, and the Pool's verifier is the only writer of what they hold. Every person step posts a Task to it. A member sees a teaser that names no party, a sheet that does, and the packet only once they've cleared conflicts.
cat pools/attorneys.ts roles: {
attorney: { credential: 'bar admission' },
practitioner: { credential: ['patent attorney', 'patent agent'], where: '*' },
watcher: { credential: ['patent attorney', 'patent agent'], where: '*' },
student: { credential: 'law school enrollment' },
scopes: { 'bar admission': 'region', 'patent attorney': '*', 'patent agent': '*', 'law school enrollment': 'region' },
name: 'text: your name as admitted', credential: ['bar admission', 'patent attorney', 'patent agent', 'law school enrollment'],
verify: 'https://verify.example.test/credentials',
coverage: { per_act: 1_000_000, by: 'either', usd: 4 },
sees: { teaser: ['act', 'region', 'fee_usd', 'signals'], sheet: ['parties', 'kind', 'amount'] },
clear: { attest: ['I checked conflicts across my full book, including my firm'], check: ['client', 'applicant', 'recipient'] },
affinity: '30d', rank: ['fit', 'track', 'speed'],
tiers: [{ name: 'reviewer' }, { name: 'signer', after: 20, sendbacks: '5%', on_time: '95%' }, { name: 'counsel', after: 100, sendbacks: '3%', on_time: '98%' }],The whole file, and check after it.
| Key | In this Pool |
|---|---|
roles | attorney holds a bar admission, practitioner and watcher a patent registration, student a law school enrollment. A Service names a role; none names student yet |
scopes | A bar admission and an enrollment count where they're held. A patent registration counts everywhere |
join | Eight fields over three steps, after the email, and resumable |
verify | The only writer of what a member holds, through its own vt_ token |
coverage | $1,000,000 an act. A member's own policy covers their act, and the Pool's covers one who carries none, at $4 an act on the brand's ledger |
sees | The teaser shows the act, the region, the fee and the signals. The sheet adds the parties, the kind and the amount |
clear | The conflicts statement a member attests, and the party inputs checked against their own ledger |
affinity | 30 days of first refusal on follow-on work for the same asset |
rank | Fit, then track record, then speed |
tiers | Reviewer, then signer after 20 reviews, then counsel after 100, each with a send-back and on-time bar |
left out: dispatch | One member at a time, because clear.check names parties |
left out: hold | Three times the step's estimate, at least a day |
left out: fill | The default: raise 10% at 2 hours, widen a tier at 4, then firms at 8 and staff at 12 |
left out: payouts | Pay accrues at the act and is payable two days after it |
Joining is email first and resumable, one group of fields a step:
→ POST /attorneys/join {"email":"grace@example.com"}← 200 mbr_7Hq2 · attorneys · grace@example.com · applying, step 1 of 3: name, credential, number · on
→ POST /attorneys/join {"email":"grace@example.com","answers":{"name":"Grace Hopper","credential":"bar admission","number":"CA-301"}}
← 200 mbr_7Hq2 · attorneys · grace@example.com · applying, step 2 of 3: regions, areas, hours · on
→ POST /attorneys/join {"email":"grace@example.com","answers":{"regions":["US-CA","US-NY"],"areas":["contracts","collections"],"hours":10}}
← 200 mbr_7Hq2 · attorneys · grace@example.com · applying, step 3 of 3: turnaround, min_fee · on
→ POST /attorneys/join {"email":"grace@example.com","answers":{"turnaround":"24h","min_fee":40}}
← 200 mbr_7Hq2 · attorneys · grace@example.com · verified (simulated): bar admission in US-CA, US-NY · on
verified {"credentials":{"bar admission":["US-CA","US-NY"]},"coverage":{"per_act":1000000,"until":"2027-10-25T14:02:11.000Z"},"at":"2026-10-25T14:02:11.000Z","by":"fixture","fresh_until":"2026-11-24T14:02:11.000Z","fresh":true}Only the verifier's own token writes verified facts. The brand's key can't:
→ POST /attorneys/signers/mbr_7Hq2/verify {"facts":{"credentials":{"bar admission":["US-CA","US-NY","US-NJ"]},"coverage":{"per_act":2000000,"until":"2027-10-25T00:00:00Z"}}} Authorization: Bearer sk_test_7Hq2← 401 key_invalid: This door takes the network's verifier token, Authorization: Bearer vt_…, and nothing else.
→ POST /attorneys/signers/mbr_7Hq2/verify {"facts":{"credentials":{"bar admission":["US-CA","US-NY","US-NJ"]},"coverage":{"per_act":2000000,"until":"2027-10-25T00:00:00Z"}}} Authorization: Bearer vt_test_7Hq2
← 200 mbr_7Hq2 · attorneys · grace@example.com · verified: bar admission in US-CA, US-NY, US-NJ · onEach claim is checked in order: credential, region, coverage, availability, separation, conflict, clearance. The answer names the predicate that failed and its cure:
→ POST /reviews/tsk_7Hq2/claim # as mbr_lapsed_credential← 403 ineligible · credential: attorney needs bar admission, and the verifier holds no current one for mbr_lapsed_credential.
cure Renew bar admission; the verifier writes it once it checks.
→ POST /reviews/tsk_7Hq2/claim # as mbr_out_of_region
← 403 ineligible · region: attorney needs bar admission in US-CA, and mbr_out_of_region holds it in ZZ.
cure Add US-CA to your regions at /join; the verifier checks the admission.
→ POST /reviews/tsk_7Hq2/claim # as mbr_lapsed_coverage
← 403 ineligible · coverage: attorney needs $1,000,000 of coverage in force per act, and mbr_lapsed_coverage's policy lapsed on 2026-10-24.
cure Renew the policy; the verifier writes the new term.
→ POST /reviews/tsk_7Hq2/claim # as mbr_eligible
← 200 tsk_7Hq2 · claimed · attorney · advise · US-CA · $50.00 · 45 min · $66.67 an hour · due 2026-10-27 14:02Because clear.check names parties, a Task goes to one member at a time in rank order, each for a window. A pass moves it on at once:
→ POST /matters {"service":"contract-review","contract":"lease.pdf","client":{"name":"Ada Lovelace","email":"signs@example.test"},"client_region":"US-CA","max_usd":99,"key":"lease"}← job_7Hq2 · waiting · on claim · $99.00 held · advise · tsk_7Hq2
# dispatch cascade: offered to mbr_7Hq3 alone, for a window
mbr_7Hq3 GET /tasks → tsk_7Hq2
mbr_7Hq2 GET /tasks → nothing
mbr_eligible GET /tasks → nothing
→ POST /reviews/tsk_7Hq2/pass # as mbr_7Hq3
# a pass moves it on at once: mbr_7Hq2
mbr_7Hq3 GET /tasks → nothing
mbr_7Hq2 GET /tasks → tsk_7Hq2
mbr_eligible GET /tasks → nothing
→ POST /test_clocks/default/advance {"to":"61min"}
# the window ran out: the sandbox's eligible member, last in every cascade
mbr_7Hq3 GET /tasks → nothing
mbr_7Hq2 GET /tasks → nothing
mbr_eligible GET /tasks → tsk_7Hq2
A member's conflicts ledger is theirs. A match keeps the Task from them, and no one learns which entry matched, not even the owner:
→ GET /reviews # as mbr_eligible← 0 Tasks: the cascade skipped a conflicted member
→ POST /reviews/tsk_7Hq2/claim # as mbr_eligible
← 403 ineligible · conflict: A party on this review matches an entry in mbr_eligible's conflicts ledger.
cure None for this Task: your ledger stays yours, and no one learns which entry matched.
→ GET /me/ledger # the owner console, with no member session
← 403 needs_a_person: Only a signer, in their own session at http://localhost:8787/attorneys, does this. A key never can.
# what the owner reads: /owner/tasks no hash of it · /owner/jobs/job_7Hq2 no hash of it · /owner/jobs/job_7Hq2/export no hash of it · /owner/events?limit=1000 no hash of itTiers are computed from outcomes. When no member claims a review in time, the fill ladder raises the fee, then opens it to the tier below:
→ POST /test_clocks/tc_7Hq3/advance {"to":"2h"}# the record: task.posted → task.raised → task.widened → task.claimed → attested
14:02 · task.posted · tsk_7Hq2 · $50.00 · dispatch cascade
16:02 · task.raised · tsk_7Hq2 · $55.00 · raise 10% 2h · funded by the brand
18:02 · task.widened · tsk_7Hq2 · opened reviewer · widen 4h
18:02 · task.claimed · tsk_7Hq2 · mbr_7Hq4 · simulated
ladder raise 10% 2h: raised to $55.00 · widen 4h: opened reviewer · posted $50.00, now $55.00Pool() covers every key, and When no one claims the ladder.