deliver

The running jobjob_7Hq2 · working · $49.00 held

Who does the work on each job. Leave it out and AI does it, in one step. Name steps and each can be AI, your code, a person, a party or an outside system.

deliver answers one question: who does the work on each job.

Leave it out. The four-line Translation has one AI step, briefed by services/translation.md, and the review every AI job gets:

Run
npx services.dev check
magic values: none
  steps   draft · ai → review · ai · adversarial

Name the steps, and each one says who does it:

grow/review.ts
  deliver: {
    translate: 'ai',
    harden: { review: 'adversarial', lenses: ['meaning', 'terms'], rounds: 2 },
  },

On the Service

KeyWrittenLeft outDoes
deliverDeliver'ai', briefed by services/<id>.mdWho does the work: 'ai', a person or a role word, a URL, your function, or named steps in order.

The step kinds

WrittenDoes
'ai', 'person', a role word, a URL, your function, another Service'ai', a person, a URL, your function, or another of your Services. A person is 'person', or a role word such as 'interpreter': the role names their app, /interpreters. Once you write roles, a role word is one of them.
{ by, … }Work: AI, a person, your code, a URL or another Service proposes output.
{ review, … }Review: a step that hardens the step before it (adversarial) or extends it, "yes, and" (affirmative).
{ ask, to, … }Ask: a party acts, through a hosted single-use link or the customer portal, with no account. The job waits on: 'party', and the step's within pauses. Every ask is an object, ask_…, with its own link.
{ submit, to, receipt, … }Submit: send to a system you don't control, and keep its receipt. The send goes through the gate, so a guard can stop it, and the job waits on: 'outside' for the answer. A rejection is a transition: the job moves to the repair step rejected names, never an error answer.

A step is work, a review, an ask or a submit. check reads each one aloud, and the record prints who did it.

Work proposes output: AI from its brief, your code, another Service, or a person. A role word makes a step a person's, and the job waits in the delivery portal for that role:

Run
npx services.dev check
magic values: none
  steps   prep · ai → call · interpreter → recap · ai → review · ai · adversarial

A review, an ask and a submit each have their page: review steps, asks and sending to outside systems.

One run of Grow 4's Translation, with all four:

Run
npx services.dev jobs wait job_7Hq2
magic values: metadata.sandbox.outside = { reject: [missing_certificate] }
  record  glossary.terms · answered | glossary · buyer · answer · clock paused 2h | translate · ai | harden · ai · adversarial · 2 lenses · pass | sign · person · sworn translator · * · mbr_7Hq2 | file · the registry · rejected · missing_certificate · repairing at sign

A step stays AI's until the job says a person should take it. escalate: { to, when } names the role and the condition: a function of the job, read as the job reaches the step, or 'at_risk' or 'late', read on the deadline's tick while the step is open. When it holds, the step is the role's: its Task posts to the role's Pool, and the job waits on a claim, with the fill ladder running if no one takes it. A role with a span keeps the steps after it too.

The Deal Service hands the booking to a setter on a warm reply, and the meeting, with every step after it, to a closer from $50,000 or a buying committee:

Run
cat services/deal.ts
magic values: none
const setter = job => job.policy.setter === 'person' || (job.policy.setter === 'on_warm' && job.signals.warm)
const person = job => job.input.value >= job.policy.humans_from || job.signals.committee || job.signals.person
    book: { by: 'ai', escalate: { to: 'setter', when: setter } },
    meet: { by: 'ai', escalate: { to: 'closer', when: person }, when: job => job.input.motion !== 'B2A',

check prints each escalation on the step it moves, and the ledger counts the role's pay only on jobs that escalate.

Steps run in the order you write them, unless a step says otherwise. Branches, loops, joins and parallel steps are on the step graph. The step that last committed is the job's stage, so the steps are the state machine and you never declare one.

No step writes to the job directly. AI, your code, a person and an outside system each propose, and the gate commits or refuses. A person's work meets the same checks as the AI's.

OpAPICLIMCPSDKSiteDocsApp
job.approvePOST /:portal/jobs/:id/approve, POST /jobs/:id/approve, POST /jobs/:id/sign, POST /owner/jobs/:id/approvejobs approve <job>never: a person actsnever: a person acts8 error pagesowner console, delivery portal
ask.getGET /asks/:idasks get <ask>never: a person actsnever: a person acts1 error pagehosted link, owner console
ask.completePOST /asks/:idnever: a person actsnever: a person acts6 error pageshosted link
ask.reissuePOST /asks/:id/reissue, POST /asks/:id/linkasks reissue <ask>reissue_askasks.reissue()3 error pagescustomer portal, owner console

Objects: ask_, rcp_. Events: job.paused, step.refused, ask.opened, item.done, ask.reminded, ask.done, ask.expired, ask.voided, ask.relinked, consent.given, job.ended, submission.sent, receipt.accepted, receipt.rejected, receipt.timed_out, review.done.

Operations has every column of every row.

CodeCategoryHTTPWhy
needs_a_personblocked403A person approves and signs, in the app. A key or a tool never can.
not_waitingconflict409The job isn't waiting on a person.
reservedblocked403The rules reserve this act to a credential the person who attested doesn't hold. Nothing was charged.
check gateWhy
step_unreachableA step no edge reaches never runs.
WrittenWhat TypeScript says
input: { document: 'file' }, output: { document: 'file' }, deliver: { translate: translation }, price: 99Property 'to' is missing in type '{ readonly document: FileRef; }' but required in type '{ readonly document: FileRef; readonly to: string; }'.

The lines that use it in the example brands, as their files write them, and what check read aloud for each file.

legal · services/contract-review.ts · 16 lines
  deliver: {
    consent: { ask: 'client', to: 'consent', expires: '7d' },
    flag: 'ai',
    attack: { review: 'adversarial' },
    advise: { by: 'attorney', stamp: true, est: '45min', checklist: ['every flag is addressed', 'the memo answers the goals'] },
    send: { by: 'ai' },
  },
npx services.dev check · captured/legal-walk.txt
services/contract-review.ts · 16 lines · $99 per job, within 48 hours, charged on acceptance.
  steps   consent · client · consent → flag · ai → attack · ai · adversarial → advise · attorney · client · stamped · 2-item checklist → send · ai

The legal example runs every one of its files, captured.

tax · services/notice-response.ts · 19 lines
  deliver: {
    read: { by: 'ai', cite: 'source' },
    poa: { ask: 'owner', to: 'grant', after: 'respond.claim', what: 'Form 2848', names: { claimant: 'caf' }, scope: job => ({ acts: ['represent', 'send'], matters: [{ tax: 'income', form: '1120', periods: job.input.tax_years }], for: '365d' }), how: 'esign', capacity: 'officer', withdrawn: ['accepted', 'for'] },
    respond: { by: 'representative', act: 'represent', needs: 'poa', stamp: true, est: '1h', drafts: { misapplied: 'a transfer request citing the receipt', penalty: 'a request to abate', extend: 'a Form 872 limited to the issues and a date' } },
    send: { submit: 'https://correspondence.example.test/replies', to: 'the agency', receipt: 'receipt', needs: 'poa' },
  },
npx services.dev check · captured/tax-check.txt
services/notice-response.ts · 19 lines · $299 per job, or $99 if you answer it yourself, or 25% of a penalty abated, $99 at least, within 48 hours, charged on send's receipt.
  steps   read · ai · cites source → match · code · cites each payment's receipt, and the period it was for → classify · code · agree to pay, the rest to respond → review · ai · adversarial → respond · representative · * · stamped → poa · owner · grant · Form 2848, after respond.claim → send · the agency → pay · Treasury · routed

The tax example runs every one of its files, captured.

sales · services/deal.ts · 42 lines
  deliver: {
    source: 'ai',
    qualify: { by: 'ai', next: job => (job.output.fit ? 'book' : 'lost') },
    book: { by: 'ai', escalate: { to: 'setter', when: setter } },
    meet: { by: 'ai', escalate: { to: 'closer', when: person }, when: job => job.input.motion !== 'B2A',
      guard: { held: (m, job) => m.attended(job.input.account) >= 10 || 'no one from the account stayed 10 minutes' } },
    propose: { by: 'ai' }, negotiate: { by: 'ai' },
    close: { by: 'ai', next: job => (job.output.closed_value ? 'won' : 'lost'),
      guard: { closed_value: (v, job) => v >= job.input.value * job.policy.floor || 'below the floor you set' } },
    nurture: { by: 'nurturer', routed: true, next: job => (job.output.revived ? job.record.interrupted : 'lost') },
  },
npx services.dev check · captured/sales-check.txt
services/deal.ts · 42 lines · nothing on effort; 5% of closed_value at won, proven by the account's payment, held 60 days by bank, 120 by card.
  steps   source · ai → qualify · ai → book · ai · setter when warm → meet · ai · closer from humans_from, committee or person · skipped for B2A · guard held: the account attends 10 minutes → propose · ai · the closer's span → negotiate · ai · the closer's span → close · ai · the closer's span · guard closed_value at least value × floor → nurture · nurturer · routed

The sales example runs every one of its files.