From 74ff477ff5e93bada7185bde9262546734c3bd1f Mon Sep 17 00:00:00 2001 From: delchev Date: Sat, 15 Aug 2026 22:19:38 +0300 Subject: [PATCH 1/2] spec: an unrecognised key is an authoring error, never ignored A typed mapping normally drops a key it does not know, and that silence is the worst failure this format can have: the file is accepted, generation succeeds, the application deploys, and the only symptom is that the promise the author wrote is absent at runtime - with every step reporting success. A case slip is the same failure and is the hardest to see by eye. Adds the normative rule to Authoring rules: an unrecognised key must be reported naming the key, where it appears and the nearest declared name; key names are case-sensitive; maps whose keys come from the model being described are validated against that model instead. Sharpens the seeds rule the same way - a row key is a field name, a *to-one* relation name (a collection has no column to set) or the `stage` marker, and what accepting it silently costs is spelled out: a dropped NOT NULL foreign key makes the import skip every row. --- versions/1.2.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/versions/1.2.md b/versions/1.2.md index f321557..b6f252b 100644 --- a/versions/1.2.md +++ b/versions/1.2.md @@ -204,6 +204,21 @@ seeds: - **No type tags.** Blocked by the safe parser. - **Quote unquoted braces in scalars.** `to: {member.email}` is parsed by YAML as an object, not a string - write `to: member.email`. Braces are only for `{...}` interpolation inside `subject` / `body` text. - **An event-binding key is `event:`, never `on:`** - YAML 1.1 resolves a bare `on` (and `off` / `yes` / `no`) to a boolean. An action key is `do:`. +- **Only the keys this specification declares exist, and they are case-sensitive.** An invented key, or a case slip (`Required:` for `required:`), is an authoring error - never a key that is accepted and ignored. + +#### Unrecognised keys + +A typed mapping normally drops a key it does not know. That silence is the worst failure this format can have: the file is accepted, generation succeeds, the application deploys, and the only symptom is that the promise the author wrote is absent at runtime - with every step of the pipeline reporting success. The rule is therefore the same one the format applies to a reference it cannot resolve. + +> **Normative.** +> A conforming generator MUST report a key it does not recognise as an authoring error rather than +> ignoring it, and the report MUST name the key, where it appears, and - where one exists - the +> nearest declared name. Key names are **case-sensitive**: a key differing from a declared one only +> in case is unrecognised, and the report SHOULD say so, since it is the slip hardest to see by eye. +> This applies equally to a [seed row](#seeds), whose keys are the target entity's own names rather +> than this specification's. A map whose keys are drawn from the model being described (a `map:` +> projection, a relation's `where:`, a widget's `at:`) is validated against that model, not against +> this vocabulary. ## Entities & fields @@ -1538,7 +1553,7 @@ Generates a seed-import descriptor + CSV per seed. Two shapes: - **`file: data/.csv`** — an authored CSV under a `data/` subfolder, right for bulk nomenclatures and prepopulated demo data. A foreign key is set by the relation name (`Country: 34`). > **Normative.** -> Row keys must match a field or relation name **exactly** (case-sensitive). A key matching neither is an authoring error — a silently dropped column becomes a NOT NULL failure at import time. +> Row keys must match a field name, a **to-one** relation name (a collection has no column to set), or the `stage` marker below, **exactly** (case-sensitive). A key matching none of those is an authoring error, reported with the nearest declared name — see [unrecognised keys](#unrecognised-keys). Accepting it would drop the column, and a dropped NOT NULL foreign key makes the import skip **every** row: a nomenclature that imports as zero rows, behind a fully green pipeline. A seed with `language: ` is a **translation** seed: it fills the per-language values of a `multilingual: true` entity, carrying the base row's `id` plus the translatable fields only. From ceecb34adcac4aa15741c239c7d861850edb0437 Mon Sep 17 00:00:00 2001 From: delchev Date: Sun, 16 Aug 2026 01:29:02 +0300 Subject: [PATCH 2/2] spec: a step argument is recognised per step kind An argument declared on a kind that does not read it (a decision's `if` on a user task, a boundary `timeout` on a service task) is the same silent drop as an invented one - the step reads nothing - so it must be reported the same way, naming the kind that does read it. Also names the blocks that are a fixed vocabulary despite being written as a mapping (trigger, abortOn, an event binding, a step's args and what nests inside them), and adds a delegate's injected `fields:` to the free-form list. --- versions/1.2.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/versions/1.2.md b/versions/1.2.md index b6f252b..89324c3 100644 --- a/versions/1.2.md +++ b/versions/1.2.md @@ -217,8 +217,19 @@ A typed mapping normally drops a key it does not know. That silence is the worst > in case is unrecognised, and the report SHOULD say so, since it is the slip hardest to see by eye. > This applies equally to a [seed row](#seeds), whose keys are the target entity's own names rather > than this specification's. A map whose keys are drawn from the model being described (a `map:` -> projection, a relation's `where:`, a widget's `at:`) is validated against that model, not against -> this vocabulary. +> projection, a relation's `where:`, a widget's `at:`, a delegate's injected `fields:`) is validated +> against that model, not against this vocabulary. + +Being written as a mapping does not make a block free-form. A process [`trigger:`](#processes), an +[`abortOn:`](#aborton--cancel-the-instance-on-a-terminal-status), a glue [`event:`](#notifications) +binding, a step's [`args:`](#processes) and the blocks nested inside them are each a fixed +vocabulary, and a key outside it is unrecognised like any other. + +> **Normative.** +> A step's `args:` are recognised **per step kind**: an argument declared on a kind that does not +> read it (a decision's `if` on a user task, a boundary `timeout` on a service task) MUST be reported +> like an unrecognised one, and the report SHOULD name the kind that does read it. This is the same +> failure and not a lesser one - the step reads nothing, so the argument does nothing. ## Entities & fields