check’s gates

The running jobjob_7Hq2 · delivered · document.fr-CA.pdf · $49.00 charged

Every gate npx services.dev check runs before you sell, the code its --json output prints, and what fixes it. A failing gate exits 1.

check runs 40 gates on every Service before you sell; a failing gate exits 1 and prints its code, why and the fix in --json. 30 have their own page: the file, what check prints and the fix as git prints it.

GateMeansFix
description_too_longThe tool description is over 30 words, so agents skim past it.Shorten description.
price_unstatedThe tool description omits the price or when it's charged.Keep the price sentence in it.
agent_cannot_buyAn agent couldn't order, wait for and accept a job.Run npx services.dev dev and read its job.
toolset_too_largeThe buyer's MCP toolset is over its token budget, so an agent's context fills with tools.Move Services to a brand of their own, or rows to the sandbox toolset: the order tools already fold into one.
instructions_too_longThe MCP server's instructions are over 120 words.Move Services to a brand of their own: the instructions name each Service that charges at its own time or waits on a party.
GateMeansFix
step_unreachableA step no edge reaches never runs.Fix its after, or route there with next.
review_after_attestA person attests work no review has passed: the review runs after the signature.Move the review ahead of the step that signs, so the person signs work the review has already passed.
release_unprovenprice.release names no step that runs.Name a step that runs.
GateMeansFix
clock_unownedA clock's owner can never resolve: a role the Service doesn't have, or of_record with no member of record on its subject. It would run vacant, with no one to answer for the date.Name a role the Service has, 'brand' or 'buyer', or give the subject an of_record.
clock_due_unreadableA clock's due function can't give a date for the Service's own example order: it throws, or returns something that isn't a date. On a real order the clock would never open, and the owner console would alarm.Return a civil date such as '2027-04-15' from what the order has, or nothing until it has it.
GateMeansFix
price_below_costA delivered job could lose money: payout, nested Services and the AI cap add up to more than the price.Raise price.usd, or lower payout.
upgrade_below_payoutThe approved price adds less than the person's payout.Raise price.approved past the payout.
rail_conflictA role's pay.rail contradicts the rail of the row for its act.Leave pay.rail out, so the row's rail holds, or make the two agree.
rush_uncappedA role is paid from the buyer, its Pool's fill ladder raises the fee, and nothing caps what the buyer pays for it.Declare policy.max_rush: the most an order lets the ladder add, in dollars. Or pay the role from the price.
fill_underwaterThe Pool's fill_cap lets the ladder raise a brand-funded fee past what the job leaves: a job filled at the cap loses money.Lower the Pool's fill_cap, or leave it out so the default holds: the lower of 50% and what the margin leaves.
GateMeansFix
reserved_act_unstaffedA step performs a reserved act, and no role that does it holds the credential its row reserves it to.Give the step to a role that holds the credential, or set the role's credential to one that meets it.
act_unruledA step names an act the rules table has no row for, under its Service or shared. There is no fallback.Add a row for the act to the rules table, with its position, or name an act the table has.
rule_unreadThe rules table binds a name nothing reads: a key that names no Service file, or a row no step performs that is neither an advice class nor refused.Rename it to what it means, or take it out.
where_unresolvedA reserved row reads where from a party's region, and the Service takes no input that names it, so an order could never say where the act happens.Add the input the row reads, such as client_region: 'jurisdiction: such as US-CA', or set the role's where.
region_unqualifiedA rules row's regions entry isn't an ISO 3166 code: a bare state such as NY is no country, and CA is Canada.Write the subdivision as US-NY, or a country such as US for every state.
self_unruledprice.self prices the client performing reserved acts themselves, and no reserved row the Service performs allows self.Mark the rows the client may perform with self: true, or take price.self out.
advice_without_adoptThe rules reserve an advice class for the Service, and its talk names no role from a Pool that holds the row's credential to adopt held answers.Set talk: { adopt: '<role>' } to a role from a Pool whose credential meets the advice row, or turn talk off with talk: false.
client_decision_gatedA rules row reserves a decision to the client, and the step isn't an ask to the client, or a guard ahead of it refuses what the client would decide.Make the step an ask to the client party, and turn the guard into a signal: the client sees every offer.
sheet_without_consentA Pool's sheet names the job's parties to the members who read it, and the client hasn't consented before the first Task posts.Add a consent ask to the client ahead of the first role step: { ask: 'client', to: 'consent' }.
custodian_unqualifiedA subject's custodian ladder leaves the brand of record for a role whose credential the brand doesn't hold.Put a role that holds the credential ahead of 'brand' in custodian, or declare the brand's credential in brand.ts.
pool_role_unknownA Service's role names a Pool that has no role by that name, so no member could ever be offered its Task.Name the role as the Pool does, or add it to the Pool's roles.
fill_bypasses_reservedA fill rung could offer a reserved act to someone who doesn't hold its credential.Take the rung out, or add a staff role that holds the credential.
brand_credential_missingA rules row names a credential the brand holds, and the Brand doesn't declare it.Declare it in brand.ts, under Brand({ credentials }), with the verifier that keeps it fresh.
GateMeansFix
surface_driftA surface serves a call with no row, or lacks a live row.Add the row, or render it.
service_id_reservedA Service's file name, or a portal's, takes a word services.dev's own routes use.Rename the file, or the role.
word_collisionA Brand.words value takes a generic word, a word an operation's id is spelled with or a path services.dev already serves, or makes two rows render the same, so two things would read the same.Choose a word of your own for it.
npx services.dev check --json
{
  "ai": {
    "cents_per_call": 42,
    "simulated": true
  },
  "mcp": {
    "tools": 13,
    "tokens": 4525,
    "sandbox": 9,
    "budget": 8000
  },
  "services": [
    {
      "path": "services/store-listing.ts",
      "lines": 5,
      "steps": "draft · ai → review · ai · adversarial · then a person approves",
      "ok": false,
      "price": "$29 per job, or $30 with a person's approval, within 48 hours, charged on delivery.",
      "who": "AI delivers unless the buyer pays for a person's approval.",
      "agent": {
FieldTypeAlwaysMeans
codestringyesThe gate's code, the word an agent branches on.
reasonstringyesWhat the gate means, the same words on every failure of it.
nextstringyesWhat to change: the gate's fix, then what this run found, naming the step, row or file.
doc_urlstringyesThe gate's page: the failing file, what check printed and the fix.
filestringnoThe file at fault, from the brand's folder: rules.ts, brand.ts, a Pool's file or the Service's own.
linenumbernoThe line in that file, when check can find it.
pathstringnoWhere in the definition, such as rules.services['translation'].sign or deliver.choose.
suggest{ path: string; value: string }noA value that fixes it, to write at its path.

Nine more read the ledger, the brand's fee policy, a door's terms and the deployed brand:

GateFails when
ai_unfundeda Service paid only on an outcome runs AI with no budget that funds it
fee_policya price uses a fee form the brand's fee policy forbids
member_chargeda ledger line charges a member to work
charter_weakeneda Pool's keys, an import or a door's terms would remove or narrow a line of the member charter
pay_below_flooran importing role pays below the minimum the exporter publishes for it: importing
party_missingan ask names a party no input supplies
breaking_changecheck --against live finds a removed field or a newly required input
not_regenerablea generated surface was edited by hand, so the definition can't regenerate it
learning_without_usea learning key is set where no step reads what it learns