client_decision_gated
A 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.
| Fix | Fails in | Suggests | Exit |
|---|---|---|---|
| Make the step an ask to the client party, and turn the guard into a signal: the client sees every offer. | services/translation.ts:12 · deliver.choose | deliver.choose: { ask: 'client', to: 'decide', choices: [...] } | 1 |
Owned by Two sovereigns: the page that explains what this gate holds.
✗ A 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. choose is the client's decision alone under rules 2026-10, and choose is done by ai: make it an ask to client.
at services/translation.ts:12 · deliver.choose
✗ 1 to fiximport { Service } from 'services.dev'
import rules from '../rules.ts'
export default Service({
name: 'Translation',
description: 'A document, translated and signed.',
input: { document: 'file', to: 'language: a BCP 47 tag, such as fr-CA', client: 'party' },
output: { document: 'file' },
rules,
roles: { translator: { credential: 'sworn translator' } },
deliver: {
choose: 'ai',
translate: 'ai',
harden: { review: 'adversarial', lenses: ['meaning', 'terms'] },
sign: { by: 'translator', stamp: true },
},
price: 89,
})diff --git a/services/translation.ts b/services/translation.ts
index f1b7df0..8bf1bf0 100644
--- a/services/translation.ts
+++ b/services/translation.ts
@@ -9,7 +9,7 @@ export default Service({
rules,
roles: { translator: { credential: 'sworn translator' } },
deliver: {
- choose: 'ai',
+ choose: { ask: 'client', to: 'decide', choices: ['formal', 'plain'] },
translate: 'ai',
harden: { review: 'adversarial', lenses: ['meaning', 'terms'] },
sign: { by: 'translator', stamp: true },✓ ready to sell · 1 Service · it keeps at least 80% of every jobimport { Rules } from 'services.dev'
export default Rules({
version: '2026-10',
services: {
translation: {
sign: { reserved: 'sworn translator', where: '*', position: 'A sworn translator signs a translation filed with a registry.' },
choose: { client: true, position: 'Which register a translation takes is the client\'s decision.' },
},
},
})How the legal example passes this gate, in lines read from its files.
'demand-letter': {
// …
settle: { client: true, position: 'Whether to settle, and for how much, is the client\'s decision alone.' },deliver: {
// …
settle: { ask: 'client', to: 'decide', choices: ['accept', 'counter', 'close the file'], when: job => job.output.settlement !== undefined },Read them in context: The rules table, Demand letter.
{
"code": "client_decision_gated",
"reason": "A 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.",
"next": "Make the step an ask to the client party, and turn the guard into a signal: the client sees every offer. choose is the client's decision alone under rules 2026-10, and choose is done by ai: make it an ask to client.",
"doc_url": "https://services.dev/docs/reference/check/client_decision_gated",
"file": "services/translation.ts",
"line": 12,
"path": "deliver.choose",
"suggest": {
"path": "deliver.choose",
"value": "{ ask: 'client', to: 'decide', choices: [...] }"
}
}The whole document check --json prints
{
"ai": {
"cents_per_call": 42,
"simulated": true
},
"mcp": {
"tools": 13,
"tokens": 4529,
"sandbox": 9,
"budget": 8000
},
"files": [
{
"path": "rules.ts",
"lines": 7,
"kind": "rules",
"says": "rules 2026-10 · 1 Service · 2 rows"
}
],
"services": [