custodian_unqualified
A subject's custodian ladder leaves the brand of record for a role whose credential the brand doesn't hold.
| Fix | Fails in | Suggests | Exit |
|---|---|---|---|
| Put a role that holds the credential ahead of 'brand' in custodian, or declare the brand's credential in brand.ts. | services/translation.ts:9 · subject.of_record.custodian | subject.of_record.custodian: ['translator', 'brand'] | 1 |
Owned by Of record: the page that explains what this gate holds.
✗ A 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. translation's certified translation names the brand as custodian of record for the translator, and the brand holds no sworn translator: put a role that does first.
at services/translation.ts:9 · subject.of_record.custodian
✗ 1 to fiximport { Service } from 'services.dev'
import rules from '../rules.ts'
export default Service({
name: 'Translation',
input: { document: 'file', to: 'language: a BCP 47 tag, such as fr-CA' },
output: { document: 'file' },
rules,
subject: { kind: 'certified translation', key: 'document', of_record: { role: 'translator', named_at: 'sign', custodian: ['brand'] } },
roles: { translator: { credential: 'sworn translator' }, reviewer: { credential: 'sworn translator' } },
deliver: {
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 59ba02a..e0b0ae5 100644
--- a/services/translation.ts
+++ b/services/translation.ts
@@ -6,7 +6,7 @@ export default Service({
input: { document: 'file', to: 'language: a BCP 47 tag, such as fr-CA' },
output: { document: 'file' },
rules,
- subject: { kind: 'certified translation', key: 'document', of_record: { role: 'translator', named_at: 'sign', custodian: ['brand'] } },
+ subject: { kind: 'certified translation', key: 'document', of_record: { role: 'translator', named_at: 'sign', custodian: ['reviewer', 'brand'] } },
roles: { translator: { credential: 'sworn translator' }, reviewer: { credential: 'sworn translator' } },
deliver: {
translate: 'ai',✓ 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.' },
},
},
})How the legal example passes this gate, in lines read from its files.
roles: {
// …
practitioner: { credential: ['patent attorney', 'patent agent'], where: '*' },subject: {Read them in context: The attorneys' Pool, Provisional patent application.
{
"code": "custodian_unqualified",
"reason": "A subject's custodian ladder leaves the brand of record for a role whose credential the brand doesn't hold.",
"next": "Put a role that holds the credential ahead of 'brand' in custodian, or declare the brand's credential in brand.ts. translation's certified translation names the brand as custodian of record for the translator, and the brand holds no sworn translator: put a role that does first.",
"doc_url": "https://services.dev/docs/reference/check/custodian_unqualified",
"file": "services/translation.ts",
"line": 9,
"path": "subject.of_record.custodian",
"suggest": {
"path": "subject.of_record.custodian",
"value": "['translator', 'brand']"
}
}The whole document check --json prints
{
"ai": {
"cents_per_call": 42,
"simulated": true
},
"mcp": {
"tools": 13,
"tokens": 4490,
"sandbox": 9,
"budget": 8000
},
"files": [
{
"path": "rules.ts",
"lines": 6,
"kind": "rules",
"says": "rules 2026-10 · 1 Service · 1 row"
}
],
"services": [