Porting api.forsale

The running jobjob_7Hq5 · won · msa-northwind.pdf · $11,100.00 charged, held to 2027-03-09

Every api.forsale route, matched to the operation the generated sales brand serves it with, adapted to a new shape, or decided, and each of the sales kernel's 76 behavior tests matched to the line that carries it. Then the kernel's money path and api.forsale's own probe suite, passing against the generated brand.

sales's wordsdeal is a jobaccount is a subjectgig is a taskcloser is a memberseller is a clientseller is a buyer

api.forsale was built by hand: a deal state machine, a seller's mandate naming who works each role, a gate that refuses a close below the floor, and settlement by role. The sales example generates the same business from three files. This page holds the two side by side, the acceptance test for Money and Outcome.

MeasureOfResultRead it
The sales kernel's behavior tests: core, gig-result, gig-lifecycle, sdk and demo, at 52fa49b76 tests59 ported as they are · 10 adapted · 7 decidedIts behavior tests
api.forsale's routes, the kernel's and the front door's22 routes12 served as its clients call them · 6 adapted · 4 decidedRoute by route
The kernel's money path, its numbers unchanged10 rows10 passed · 0 failedThe suite
api.forsale's own probe suite10 probes10 passed · 8 unchanged, 2 rewritten for a door that sellsThe suite

The kernel's money path, then api.forsale's probes. Two probes expected the door's answers from before it sold, a free price and a stub OFFER, and read the brand's own now:

Run
npx services.dev verify --suite https://api.forsale/verify   # api.forsale's own probe suite, two probes rewritten for a door that sells
magic values: metadata.sandbox.proof = "paid", metadata.sandbox.members = {"close":[{"closed_value":50000},{"closed_value":74000}]}
api-forsale@2 · 10 probes · from https://api.forsale/verify · against http://localhost:8787
✓ keyless-ok              GET /deals                                200 OK
✓ known-empty-1           GET /deals?stage=none                     200 EMPTY
✓ known-empty-2           GET /deals?role=none                      200 EMPTY
✓ known-forbidden-1       GET /deals?scope=tenant                   403 BLOCKED
✓ known-forbidden-2       GET /deals?scope=settlement               403 BLOCKED
✓ pricing                 GET /pricing                              200 · model outcome: 5% of the closed value at won · rewritten from model free
✓ offer                   GET /offer                                402 OFFER · orders with a scoped payment token · rewritten from offer-stub
✓ card                    GET /.well-known/agents.json              200
✓ openapi                 GET /openapi.json                         200
✓ llms                    GET /llms.txt                             200

10 passed · 0 failed · 8 probes unchanged, 2 rewritten from the door's answers before it sold

port.ts reads each api.forsale route, finds the operation that serves it, and calls the running brand. A resource's .md, .json and .html twins count as one route.

MarkMeans
✓The generated operation serves the route as api.forsale's clients call it
adaptedThe operation exists with another shape, and the row says what a client changes
decisionAnother generated surface serves it, or it stays the brand's own, and the row says which
Run
node -e "import('./port.ts')"   # each api.forsale route: its mark, the operation, and what the sales brand answered
magic values: none
✓         POST /deals                    service.order                                402 OFFER
✓         GET /deals/:id                 job.get                                      404 EMPTY not_found
decision  POST /deals/:id/advance        step.propose                                 a generated surface · the gate advances the graph and no caller drives it: AI proposes each step, and a closer proposes in their own session
✓         POST /deals/:id/signals        job.signal                                   404 EMPTY not_found
adapted   POST /deals/:id/gig-result     task.propose · task.complete · task.release  POST /gigs/:id/propose, /negotiate, /complete and /release, in the closer's own session on closers.deals: one call per act, never a callback the seller's key could send
adapted   GET /deals/:id/settlement      job.ledger                                   GET /owner/deals/:id: the owner's read, with the ledger; the seller reads the charge on GET /deals/:id
adapted   POST /deals/:id/reengage       job.again                                    POST /deals/:id/again: a new deal with from: on its record, from any ended deal
adapted   POST /sell                     service.order                                POST /deals, then GET /deals/:id with wait: one order, and the same answer whether AI closes or a closer does
✓         GET /deals                     job.list                                     200 EMPTY
adapted   GET /gigs                      task.feed                                    GET /gigs on closers.deals, in a member's session: the teasers, with pay, before a claim
✓         GET /offer                     service.offer                                402 OFFER
✓         GET /pricing                   brand.pricing                                200 application/json
✓         GET /openapi.json              brand.openapi                                200 application/json
✓         GET /llms.txt                  brand.llms                                   200 text/markdown
✓         GET /.well-known/agents.json   brand.card                                   200 application/json
✓         GET /index.json                brand.home                                   200 application/json
✓         POST /mcp                      brand.mcp                                    200 initialize
✓         GET /mcp                       brand.mcp                                    405 · POST only, as MCP over HTTP asks
adapted   POST /api/waitlist             service.waitlist                             POST /deal/waitlist { email }: one waitlist per Service, which a not_live answer names
decision  GET /icp.json                  public/icp.json                              kept as a brand route · a file of the brand's own in public/, served as it is
KernelDecisionWhy
POST /deals/:id/advancenever a call: the gateAI proposes each step, and a closer proposes in their own session; no caller drives the graph
A commission set per deal, clamped to the seller's capthe closer's 10% is the brand's, in one lineA gig's pay is fixed at post, and a closer reads one rate on every teaser
/, /health and POST /demo/resetthe brand's own routes, and test modeA test key and test clocks are the sandbox, so nothing needs resetting
The setter's and nurturer's fees, charged to the sellerfunded from api.forsale's 5%The seller pays nothing on effort: who funds the setter
The one-way door: a deal a person touched stays a person's for every rolea closer's span, and her first call on a revived stepEach step's escalate is its own, so a deal a setter booked under humans_from still meets with AI. An order that wants a person on every role sets setter: 'person' with humans_from: 0

flows.ts reads each of the kernel's tests and names the line that carries it:

Run
node -e "import('./flows.ts')"   # the sales kernel at 52fa49b: 76 tests in core, gig-result, gig-lifecycle, sdk, demo
magic values: none
76 tests · 59 ✓ · 10 adapted · 7 decision
## ✓ · 59
## adapted · 10
## decision · 7

The adapted rows, whole:

captured/sales-flows.txt · magic values: none
$ node -e "import('./flows.ts')" -- --mark adapted
  core#the one-way door: a human-track deal stays human for every subsequent role
    → deal.ts roles.closer.span, revive: { first_call: '24h' } · a closer's span stays hers from the meeting to the end, and a revived step comes back to her first; a deal a setter booked under humans_from still meets with AI, since each step's escalate is its own, so an order that wants a person on every role sets setter: 'person' with humans_from: 0
  core#fees settled at gig fulfillment appear as a line item in the all-in economics
    → deal.ts roles.setter.pay · the setter's $250 is api.forsale's cost, debited from its balance at the held meeting, so the seller pays nothing on effort; from: 'buyer' runs the kernel's way
  gig-result#refuses to re-engage a deal that is not closed_lost
    → POST /deals/:id/again · again takes any ended deal, a won one for a renewal; a live deal answers not_terminal
  sdk#createDeal throws GateRefusedError with the violations payload on an out-of-bounds mandate
    → the SDK · BLOCKED comes back as a typed value, policy_invalid with its fill list, not a thrown error
  sdk#getDeal throws NotFoundError for an unknown id
    → the SDK · not_found, as a typed value
  sdk#settlement throws ConflictError on a deal that is not closed_won
    → the deal's ledger · a lost deal reads +$0.00; there is no settlement call to refuse

port.ts is a list: each route of the brand you're porting, the operation that serves it, and its mark. The run above calls every row against the generated brand and prints what it answered, so a ✓ is an answer, never a claim. flows.ts does the same for the brand's tests. One row of each mark:

sales · port.ts
export default [
  { route: 'POST /deals/:id/signals', op: 'job.signal', mark: '✓' },
  { route: 'POST /deals/:id/reengage', op: 'job.again', mark: 'adapted', moved: 'POST /deals/:id/again: a new deal with from: on its record, from any ended deal' },
  { route: 'POST /deals/:id/advance', op: 'step.propose', mark: 'decision', decision: 'a generated surface', why: 'the gate advances the graph and no caller drives it: AI proposes each step, and a closer proposes in their own session' },
] as const satisfies readonly Port[]

What the brand gets shows the same operations on every surface.