okyline — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited okyline (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Okyline is a declarative language for describing and validating JSON structures using inline constraints on field names. A schema is a valid JSON document made of real example values: the example gives the type, the key carries the constraints.
MANDATORY: read the reference files BEFORE producing an Okyline schema:
references/syntax-reference.md - complete syntax of constraintsreferences/internal-references.md - $defs, references (&Name), $entriesreferences/conditional-directives.md - if conditional logicreferences/expression-language.md - if $compute is necessaryreferences/virtual-fields.md - if $field (virtual fields) is necessaryreferences/external-imports.md - ONLY if $deps/$import are explicitly requested or already present in a loaded schemaNever generate a schema based solely on this SKILL.md file. The examples here are a summary, not an exhaustive reference.
Since 1.8.0 the engine checks a schema when it is loaded, not only when data is validated. A schema that carries any of the following is rejected, even if the same text loaded under 1.7.0:
$required, $forbidden, $atLeastOne, $mutuallyExclusive, $exactlyOne or $allOrNone must be declared in the same object. $additionalProperties: true does not help.❌ "$exactlyOne": ["email", "phon"] when the object declares phone ✅ "$exactlyOne": ["email", "phone"]
❌ "$requiredIf active('true')": [...] on a boolean ❌ age('18') on an integer ❌ code(12) on a string ✅ active(true), age(18), code('12')
❌ "$appliedIf status('LEGACY')": {...} when $STATUS is ACTIVE,INACTIVE ✅ use a value of $STATUS, or add LEGACY to it
❌ "$requiredIf address('X')": [...] ❌ "$requiredIf items(>0)": [...] ✅ "$requiredIf items(_ListOfObject_)": [...]
❌ "$requiredIf lines.total(>0)": [...] ❌ "$requiredIf parent.x('A')": [...] written in the root object ✅ write the directive inside the list element: "lines|[*]": [{ ..., "$requiredIf total(>0)": [...] }]
$required, $forbidden, the four structural groups, $requiredIf* / $forbiddenIf*, $else and $notExist. Nothing else.❌ "$appliedIf type('B')": { "$ref": "&Business" } ❌ { "$additionalProperties": true } in a branch ✅ declare the fields directly in the branch
❌ "tags|[0,10] -> {2,20}": [] ❌ "tags|[*] -> !": [] ✅ "tags|[0,10] -> {2,20}": ["eco"] ✅ "tags|[0,10]": [] (any element accepted)
{} or [], everything below comes from the base.❌ "address|$amend @": { "city|@": "Paris" } ✅ "address|$amend @": {}
matches() with a format that is not written '$Name' or does not exist.❌ round(x, 2, "NEAREST") ❌ matches(ref, fmt) ❌ length() ✅ round(x, 2, "HALF_EVEN") ✅ matches(ref, '$Email') ✅ length(ref)
(...) blocks on one field, $oneOf and $anyOf on the same field.❌ "$appliedIf type('B')": { "name|@ {2,100}": "…" } when name is declared at the parent level ✅ "$appliedIf type('B')": { "name|$amend {2,100}": "…" }
"fieldName | constraints | label": exampleValueA label without constraints needs the empty middle slot:
❌ "buyer|Client" → "Client" parsed as a constraint ✅ "buyer| |Client" → "Client" is the label
A key starting with $ is a directive, unless the first non-blank character after the initial word is a |: "$oid|@ {24}" is a field named $oid (MongoDB). A name starting with @ needs no such mark: "@type|@": "Person" (JSON-LD).
{
"$oky": {
"name|@ {2,50}|User name": "Alice",
"email|@ ~$Email~": "[email protected]",
"age|(18..120)": 30
}
}| Symbol | Meaning | Example | |
|---|---|---|---|
@ | Required field | `"name\ | @": "Alice"` |
? | Nullable (can be null) | `"middle\ | ?": "John"` |
{min,max} | String length | `"code\ | {5,10}": "ABC123"` |
(min..max) | Numeric range | `"age\ | (18..65)": 30` |
(>0) (>=0) (<100) (<=100) | One-sided bound | `"quantity\ | (>0)": 5` |
('a','b') | Enum values | `"status\ | ('ACTIVE','INACTIVE')": "ACTIVE"` |
($NAME) | Nomenclature reference | `"status\ | @ ($STATUS)": "ACTIVE"` |
~pattern~ | Regex (searched, not anchored: write ^…$) | `"zip\ | ~^[0-9]{5}$~": "75001"` |
~$Name~ | Named or built-in format | `"email\ | ~$Email~": "[email protected]"` |
[min,max] [*] [1,*] | Array size | `"tags\ | [1,5]": ["eco"]` |
-> constraints | Constraints on each element | `"tags\ | [*] -> {2,10}": ["eco"]` |
-> ! | Unique elements (by # keys for objects) | `"codes\ | [*] -> !": ["A","B"]` |
# | Key field, for object uniqueness | `"id\ | #": 123` |
[~pattern~:max] [*:*] | Map: key pattern, max entries | `"i18n\ | [~^[a-z]{2}$~:10]": {"en": "Hi"}` |
&Name | Reference to a definition, as the value | "address": "&Address" |
Rules of thumb:
(0..), (..100), (1..*) → ✅ (>=0), (<=100).(0..99999999) → ✅ (>=0).[1,] → ✅ [1,*].(...) block per field: put extra bounds inside the compute when a field carries (%Name).$Date, $DateTime, $Time, $Email, $Uri, $Uuid, $Ipv4, $Ipv6, $Hostname. A $format of the same name replaces the built-in.42 → Integer, 3.14 → Number, "text" → String, true → Boolean, {…} → Object, […] → List.? with a real value: "middleName|?": "Marie".78.00 becomes 78 and is inferred Integer. Quote them: "amount": "78.00" → Number. 45.5 and 10 are unaffected.$obj is a list of examples for a single value, not a list. "payment|@ $obj $oneOf": [ {…}, {…} ] is one object matching one variant; without $obj it is a list.? versus absence| Meaning | When to use | |
|---|---|---|
no @ | the field may be absent | optional fields in most APIs (FHIR, REST) |
? | the field may be explicitly `null` | only when null is a meaningful value |
// ❌ ? for a merely optional field (common mistake)
"meta|?|Resource metadata": { ... }
// ✅ optional = no @, never ?
"meta| |Resource metadata": { ... }
// ✅ ? only when null carries meaning
"active|?|Explicitly unknown status": trueFor legacy producers that send null instead of omitting a field, declare "$nullAsAbsentIfUndeclared": true at the root: a null on a field without ? is then treated as absent. Default false.
When to use `$defs` + `&Name`: recursion (the only way), a structure repeated identically (Address, Period), a template specialized per usage with $override/$amend, or an explicit request. Otherwise inline in $oky; do not over-abstract.
Which conditional mechanism:
| Need | Mechanism | Example | |
|---|---|---|---|
| A required when B has a value | $requiredIf | "$requiredIf status('ACTIVE')": ["email"] | |
| A forbidden when B has a value | $forbiddenIf | "$forbiddenIf status('CLOSED')": ["lastLogin"] | |
| A required when B exists / is absent | $requiredIfExist / $requiredIfNotExist | "$requiredIfExist shipping": ["address"] | |
| Different fields per value of B | $appliedIf switch | "$appliedIf paymentMethod": { "('CARD')": {...}, "('PAYPAL')": {...} } | |
| Extra fields when a condition holds | $appliedIf simple | `"$appliedIf status('ACTIVE')": { "workDays\ | @": 20 }` |
| Exactly one of N present | $exactlyOne | "$exactlyOne": ["email", "phone"] | |
| At most one of N | $mutuallyExclusive | "$mutuallyExclusive": ["optionA", "optionB"] | |
| At least one of N | $atLeastOne | "$atLeastOne": ["email", "phone", "fax"] | |
| All or none of a group | $allOrNone | "$allOrNone": ["street", "city", "zip"] | |
| Condition on a computed value of the field | compute as operand | "$requiredIf amount(%IsHigh)": ["approver"] | |
| Condition on a derived value not in the data | $field + $appliedIf | "$field tier": "%ComputeTier" then "$appliedIf tier('GOLD')": {...} | |
| Validation depends on the runtime type | type guard | "$appliedIf data(_String_)": {...} | |
Condition on null | null literal | "$requiredIf status(null)": ["fallback"] |
Sibling presence versus value structure: $exactlyOne / $mutuallyExclusive constrain which sibling fields are present; $oneOf / $anyOf constrain the shape of one field's value.
`$compute` versus `$field`: $compute alone validates a field against a rule ("total|(%CheckTotal)": 120.0) or serves as a condition operand (amount(%IsHigh)). Introduce $field only when a directive needs a derived value that exists in no field.
$compute in shortparent. and root. go up."total|(%CheckTotal)": 120.0 attaches the check to a field; the expression must yield a boolean. %Name references another compute; %Name(args) calls a parameterized one.$decimalScale decimals (default 6) after each operation, and == compares at that scale exactly. To compare an amount in cents, round it: total == round(sum(lines, qty * price), 2).->: "lines|[1,*] (%OneFocal)"; a compute placed after -> runs per element.See references/expression-language.md.
Collected from real generation errors, most frequent first. Each ❌ has been produced by a model at least once.
? means "may be null", absence needs nothing:❌ "meta|?|Resource metadata": { ... } ✅ "meta| |Resource metadata": { ... }
?:❌ "name|?": null ✅ "name|?": "Charles"
❌ "$requiredIf status('LEGACY')": ["reason"] with "status|@ ($STATUS)" and no LEGACY in $STATUS ❌ "$exactlyOne": ["email", "phon"] when the object declares phone ✅ declare the field, or use a value the field can take
❌ "permis|@ ('A','B','C')[]": ["B"] ✅ "permis|@ -> ('A','B','C')": ["B"] ✅ "permis|@ [1,5] -> ('A','B','C')": ["B"]
❌ "tags|{2,20}": ["eco"] ✅ "tags|[1,10] -> {2,20}": ["eco"]
❌ "codes|[*]!": ["A","B"] ✅ "codes|[*] -> !": ["A","B"]
❌ "amount|@": 800.00 ✅ "amount|@": "800.00"
(%Name), every value constraint goes inside the compute:❌ "montantTTC|@ (>=0) (%LigneTTC)": 4320 ✅ "montantTTC|@ (%LigneTTC)": 4320 with "LigneTTC": "montantTTC >= 0 && montantTTC == montantNetHT + montantTVA"
❌ "price|(0..)": 29.99 ❌ "price|(0..99999999)": 29.99 ✅ "price|(>=0)": 29.99
❌ "acheteur|Client": "…" ✅ "acheteur| |Client": "…"
❌ "address": "Address" (a plain string example) ❌ "children|[*] -> &Node": [] ✅ "address": "&Address" ✅ "children|[*]": ["&Node"] ✅ "children|[1,10] -> {2,50}": ["&Node"]
$obj the field is a list:❌ "payment|@ $oneOf": [ {…}, {…} ] for a single payment ✅ "payment|@ $obj $oneOf": [ {…}, {…} ]
❌ "insurance|[1,*] -> ! (%FocalInsurance)" (after -> the compute runs on each element, not on the list) ✅ "insurance|[1,*] (%FocalInsurance) -> !"
❌ { "$ref": "&Person", "name|@ {1,50}": "John" } when Person declares name ✅ { "$ref": "&Person", "name|$amend @": "John" }
❌ "address|$amend @": { "city|@": "Paris" } ✅ "address|$amend @": {}
$defs inside $oky (root level only); object-level $ref to a scalar definition; object-level cycles (A includes B includes A); $ref, $remove or $additionalProperties inside an $appliedIf branch.❌ "$id": "personne-vehicules" ✅ "$id": "personne.vehicules"
\d → \\d); no ^…$ when full coverage is meant (a pattern is searched, not anchored).parent. and root. only when needed.❌ "tags|[*] -> {2,20}": [] ✅ "tags|[*] -> {2,20}": ["eco"] (or "tags|[*]": [] if the elements are untyped on purpose)
"email|@ ~$Email~": "[email protected]" // required email
"name|@ {2,50}": "Alice" // required string 2-50
"discount|(0..100)": 15 // optional number in range
"status|@ ($STATUS)": "ACTIVE" // enum from nomenclature
"tags|@ [1,10] -> {2,20}!": ["eco", "bio"] // 1-10 unique strings, 2-20 chars each
"middleName|@ ?{1,50}": "Marie" // required but nullable
"translations|[~^[a-z]{2}$~:10]": {"en": "Hello"} // map with pattern keys
"address|@": "&Address" // reference, required
"items|@ [1,100]": ["&OrderItem"] // list of a referenced type
"period| |Validity period": "&Period" // reference with a label
"$ref": "&Auditable" // object-level inclusion
"email|$amend @": "[email protected]" // adapt an inherited field, keep its blocks
"code|$override @ ($MARITAL_STATUS)": "M" // replace an inherited field
"address|$amend @": {} // adapt an inherited object: key only, value empty
"$oid|@ {24}": "507f1f77bcf86cd799439011" // a data field named $oid
"$compute": { "RateCheck(cat)": "category != cat || rate > 0" }
"rate|(%RateCheck('S'))": 20 // parameterized compute
"$requiredIf amount(%IsHigh)": ["approver"] // compute as condition operand{
"$version": "1.0.0",
"$id": "ecommerce.order",
"$title": "E-commerce order",
"$oky": {
"orderId|# ~^ORD-[0-9]{8}$~|Order identifier": "ORD-20250107",
"createdAt|@ ~$DateTime~|Creation date": "2025-01-07T14:30:00Z",
"customer|@|Customer info": {
"id|# (>0)": 42,
"email|@ ~$Email~": "[email protected]",
"phone|? ~$Phone~|Optional phone": "+33612345678",
"type|@ ($CUSTOMER_TYPE)": "PREMIUM",
"$appliedIf type": {
"('PREMIUM')": { "loyaltyPoints|@ (>=0)": 1500, "discountRate|@ (0..30)": 15 },
"('BUSINESS')": { "companyName|@ {2,100}": "Acme Corp", "vatNumber|@ ~$VatNumber~": "FR12345678901" }
}
},
"shipping|@|Shipping address": "&Address",
"billing|?|Billing if different": "&Address",
"lines|@ [1,50] -> !|Order lines": [
{
"sku|# ~$Sku~": "PRD-00123",
"name|@ {2,200}": "Wireless Headphones",
"quantity|@ (1..999)": 2,
"unitPrice|@ (>0)": 79.99,
"lineTotal|(%LineTotal)": 159.98,
"category|($CATEGORY)": "ELECTRONICS"
}
],
"payment|@": {
"method|@ ($PAYMENT_METHOD)": "CARD",
"status|@ ($PAYMENT_STATUS)": "PAID",
"$requiredIf status('PAID')": ["paidAt", "transactionId"],
"$forbiddenIf status('PENDING')": ["paidAt", "transactionId"],
"paidAt|~$DateTime~": "2025-01-07T14:32:00Z",
"transactionId|~$TransactionId~": "TXN-A1B2C3D4E5F6"
},
"amounts|@|Amounts": {
"subtotal|@ (%ValidSubtotal)": 159.98,
"shippingCost|@ (>=0)": 5.99,
"discount|@ (>=0)": "24.00",
"tax|@ (>=0)": 28.39,
"total|@ (%ValidTotal)": 170.36
},
"status|@ ($ORDER_STATUS)": "CONFIRMED",
"tags|? [0,10] -> {1,30}!|Optional tags": ["gift", "express"]
},
"$defs": {
"Address": {
"street|@ {5,200}": "123 Main Street",
"city|@ {2,100}": "Paris",
"postalCode|@ ~^[0-9]{5}$~": "75001",
"country|@ ~^[A-Z]{2}$~": "FR"
}
},
"$format": {
"Phone": "^\\+[0-9]{11,14}$",
"VatNumber": "^[A-Z]{2}[0-9]{9,12}$",
"TransactionId": "^TXN-[A-Z0-9]{12}$",
"Sku": "^[A-Z]{3}-[0-9]{5}$"
},
"$nomenclature": {
"CUSTOMER_TYPE": "STANDARD,PREMIUM,BUSINESS",
"CATEGORY": "ELECTRONICS,CLOTHING,HOME,FOOD,OTHER",
"PAYMENT_METHOD": "CARD,PAYPAL,TRANSFER,CRYPTO",
"PAYMENT_STATUS": "PENDING,PAID,FAILED,REFUNDED",
"ORDER_STATUS": "DRAFT,CONFIRMED,SHIPPED,DELIVERED,CANCELLED"
},
"$compute": {
"LineTotal": "lineTotal == round(quantity * unitPrice, 2)",
"ValidSubtotal": "subtotal > 0 && subtotal == round(sum(parent.lines, lineTotal), 2)",
"ValidTotal": "total > 0 && total == round(subtotal + shippingCost + tax - discount, 2)"
}
}Only $oky is required. Recommended order, for readability only (no rule imposes one):
{
"$id": "my.schema",
"$version": "1.0.0",
"$state": "DRAFT",
"$title": "My Schema",
"$description": "Schema description",
"$additionalProperties": false,
"$sequence": false,
"$decimalScale": 6,
"$oky": { ... },
"$entries": { "Create": "&Item", "Update": "&ItemPatch" },
"$defs": { ... },
"$format": { "Code": "^[A-Z]{3}-\\d{4}$" },
"$compute": { "Total": "price * quantity" },
"$nomenclature": { "STATUS": "ACTIVE,INACTIVE" }
}^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)*$. No hyphen."DRAFT" (default), "DRAFT-FINAL" or "FINAL". Omit unless the lifecycle matters.6.&Name references. A library with no root document writes "$oky": null and is validated through its entries.| after a $nomenclature, $format or $compute key adds a label: "STATUS | Account status": "ACTIVE,INACTIVE"."$atLeastOne": ["email", "phone"]
"$mutuallyExclusive": ["deceasedBoolean", "deceasedDateTime"]
"$exactlyOne": ["diagnosisCode", "diagnosisRef"]
"$allOrNone": ["street", "city", "zip"]
// several groups of the same directive: add a suffix (structural groups only)
"$mutuallyExclusive_deceased": ["deceasedBoolean", "deceasedDateTime"],
"$mutuallyExclusive_birth": ["multipleBirthBoolean", "multipleBirthInteger"]Attach the compute to the array field, before ->, so that it runs once on the whole collection:
{
"$oky": {
"insurance|@ [1,*] (%FocalInsurance) -> !": [{ "id|#": "INS-1", "focal|@": true }]
},
"$compute": { "FocalInsurance": "countIf(insurance, focal) == 1" }
}$oky wrapper present; $defs, $format, $nomenclature, $compute at root level@; ? only where null is meaningful; no null example[...], element constraints and ! after ->.00 are quoted(...) block per field; a (%Name) compute carries every value bound inside the expression| |Label when there is no constraint&; a container compute is placed before ->$id without hyphen; no $okylineVersion$compute is attached to a field or used as a condition operand^…$ where full coverage is meant~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.