diff --git a/envelope/SPEC.md b/envelope/SPEC.md index 725c7a2..9a1de38 100644 --- a/envelope/SPEC.md +++ b/envelope/SPEC.md @@ -1,4 +1,4 @@ -# Assertion Envelope — Specification v1.0 +# Assertion Envelope — Specification v1.1 **Status**: stable. Declared v1.0 upon the first real federated exchange between two distinct jurisdictions — openathle → Prismagram, endorsed @@ -84,7 +84,7 @@ fields (§7). | 5 | `statement` | string | REQUIRED. What is asserted, in one sentence or few | | 6 | `evidence` | list of `{kind, ref}` | REQUIRED, MAY be empty. A conflict without evidence asks to be believed; evidence names where the fact lives | | 7 | `provenance` | string | REQUIRED. One of §6. Transported verbatim (§2.2) | -| 8 | `confidence` | number | REQUIRED. `0.0`–`1.0`. Transported verbatim. Interpretation policies — decay, half-life, thresholds — are consumer-local readings and MUST NOT modify the transported value | +| 8 | `confidence` | number | REQUIRED. `0.0`–`1.0`. Transported verbatim. Interpretation policies — decay, half-life, thresholds — are consumer-local readings and MUST NOT modify the transported value. A consumer that *exposes* such a reading declares it (field 15, §4.1) | | 9 | `endorsement` | object or null | REQUIRED. `{by, at, rite}`; `rite` is one of `pr_approval`, `ui`, `batch`. `null` means: no human has endorsed this — which is a fact, not a defect | | 10 | `jurisdiction` | string | REQUIRED. MUST equal the prefix of `id`. Lowercase ASCII, no separators | | 11 | `at` | string | REQUIRED. RFC 3339 timestamp of the asserted state | @@ -97,6 +97,21 @@ fields (§7). assertion; `endorsement` records the proven event. The two are irreducible: a declared status is not an approval (ADR-070 §3.2). +### 4.1 Declared readings (v1.1) + +| # | Field | Type | Rule | +|---|---|---|---| +| 15 | `confidence_reading` | object | OPTIONAL. Present only when the sender exposes a value it has itself derived from `confidence`. Keys: `policy` (string, the reading's name), `version` (string), `value` (number `0.0`–`1.0`), `as_of` (RFC 3339 timestamp). All four REQUIRED when the object is present | + +A decayed confidence is a reading, not a fact. Two auditors applying two decay +functions to the same verbatim value, on two dates, would disagree without +either being wrong — and neither could say why. This field makes the reading +quotable: the verbatim value stays in field 8, the reading stands beside it, +named, versioned and dated. + +It is OPTIONAL because a consumer that never exposes a derived value never owes +one; it is not optional for those who do (§9). + ## 5. Classes `invariant`, `step_realized`, `transition`, `traversal`, `stressor`, @@ -132,6 +147,10 @@ a standard field is a contract change under §8. declared stable, nothing more. Consumers MUST treat `envelope: "0.1"` messages as v1.0 messages; the schema accepts both values. This equivalence is specific to this pair and will not be repeated across future MAJORs. +- **v1.1 adds field 15 `confidence_reading`** (§4.1) and the consumer rule that + governs it (§9). Additive and ignorable: a v1.0 consumer that meets the field + preserves it and moves on, exactly as §8 requires of any unknown field. No + existing field, class, provenance or rule changed. - At most **two published versions per year**. Deprecation is announced in this file at least one version before removal. - Consumers MUST accept any message whose `envelope` shares their MAJOR and @@ -155,7 +174,10 @@ MUST NOT emit an endorsement it did not witness as a dated event; MUST NOT emit `measured` provenance for anything a machine did not measure. **Consumer** — MUST NOT mutate `id`, `provenance` or `confidence` of a -received assertion; MUST NOT reject messages carrying unknown classes or +received assertion; MUST, when it presents or relays a confidence value it has +derived from the transported one, carry the verbatim value unchanged **and** +declare the applied reading in `confidence_reading` (§4.1) — an undeclared +derived value is not conformant, because nobody can reproduce it; MUST NOT reject messages carrying unknown classes or unknown fields (preserve, expose, move on); MUST record received assertions append-only; MAY annotate, reference and aggregate received assertions under its own identity, and MUST NOT present the result as the origin's. @@ -163,6 +185,7 @@ its own identity, and MUST NOT present the result as the origin's. ## 10. What this contract does not do It does not say how assertions are stored, projected, displayed or decayed — -those are jurisdiction-local. It does not transport source documents: sealing +those are jurisdiction-local. It says only that a decay, once shown, names +itself (§4.1): the contract governs what is claimed, never how it is computed. It does not transport source documents: sealing and encrypting source payloads is storage, not exchange. It does not decide who is right: it keeps both parties quotable, at their own risk. diff --git a/envelope/conformance/producer/invalid/reading-without-version.json b/envelope/conformance/producer/invalid/reading-without-version.json new file mode 100644 index 0000000..0493a7f --- /dev/null +++ b/envelope/conformance/producer/invalid/reading-without-version.json @@ -0,0 +1,21 @@ +{ + "reason": "A declared reading without its version is unreproducible: two auditors on two dates could not tell whether they read alike. All four keys are required when the object is present (SPEC §4.1).", + "message": { + "envelope": "1.1", + "id": "temoin/EVI-999", + "class": "measure", + "subject": "x", + "statement": "y", + "evidence": [], + "provenance": "measured", + "confidence": 1.0, + "endorsement": null, + "jurisdiction": "temoin", + "at": "2026-09-19T08:00:00Z", + "lifecycle": "proposed", + "relations": {}, + "deadline": null, + "falsification": null, + "confidence_reading": { "policy": "half-life-by-class", "value": 0.87, "as_of": "2026-09-19T08:00:00Z" } + } +} diff --git a/envelope/conformance/producer/valid/declared-reading.json b/envelope/conformance/producer/valid/declared-reading.json new file mode 100644 index 0000000..716e689 --- /dev/null +++ b/envelope/conformance/producer/valid/declared-reading.json @@ -0,0 +1,23 @@ +{ + "envelope": "1.1", + "id": "temoin/EVI-129", + "class": "measure", + "subject": "INV-011", + "statement": "Detected mishoused on 8 September, reinforced on 10 — the loop closed.", + "evidence": [{ "kind": "coupe", "ref": "openathle/MES-INV-011-2" }], + "provenance": "measured", + "confidence": 1.0, + "endorsement": null, + "jurisdiction": "temoin", + "at": "2026-09-10T12:12:20.466Z", + "lifecycle": "proposed", + "relations": {}, + "deadline": null, + "falsification": null, + "confidence_reading": { + "policy": "half-life-by-class", + "version": "1", + "value": 0.87, + "as_of": "2026-09-19T08:00:00Z" + } +} diff --git a/envelope/schema/assertion.schema.json b/envelope/schema/assertion.schema.json index a34d729..d6f0481 100644 --- a/envelope/schema/assertion.schema.json +++ b/envelope/schema/assertion.schema.json @@ -1,8 +1,8 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://prismagram.com/published-language/envelope/1.0/assertion.schema.json", - "title": "Assertion Envelope v0.1", - "description": "Producer contract for envelope v0.1 messages (SPEC.md §4). One cross-field rule cannot be expressed here and is enforced by the conformance runner: `jurisdiction` MUST equal the prefix of `id`.", + "title": "Assertion Envelope v1.1", + "description": "Producer contract for envelope v1.1 messages (SPEC.md §4). One cross-field rule cannot be expressed here and is enforced by the conformance runner: `jurisdiction` MUST equal the prefix of `id`.", "type": "object", "required": [ "envelope", @@ -25,9 +25,10 @@ "envelope": { "enum": [ "0.1", - "1.0" + "1.0", + "1.1" ], - "description": "Version of the contract. v1.0 is substantively identical to v0.1 (SPEC §8): both values validate. A message that does not declare its version is not versioned (A1 §3)." + "description": "Version of the contract. v1.0 is substantively identical to v0.1 (SPEC §8): both values validate. v1.1 adds the optional declared reading (§4.1). A message that does not declare its version is not versioned (A1 §3)." }, "id": { "type": "string", @@ -178,6 +179,40 @@ "minLength": 1 } ] + }, + "confidence_reading": { + "type": "object", + "description": "OPTIONAL (SPEC §4.1, v1.1). Present only when the sender exposes a value it derived from `confidence`. A derived value shown without this object is not conformant: nobody could reproduce it.", + "required": [ + "policy", + "version", + "value", + "as_of" + ], + "additionalProperties": false, + "properties": { + "policy": { + "type": "string", + "minLength": 1, + "description": "Name of the reading applied — not the value it produced." + }, + "version": { + "type": "string", + "minLength": 1, + "description": "Version of that reading. Two auditors on two dates must be able to tell whether they read alike." + }, + "value": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "The derived value. The verbatim one stays in `confidence`, untouched." + }, + "as_of": { + "type": "string", + "format": "date-time", + "description": "When the reading was taken. A decay without a date is not reproducible." + } + } } }, "patternProperties": { diff --git a/package.json b/package.json index d21e3f8..60c01fb 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,9 @@ { "name": "published-language", - "version": "0.1.0", + "version": "1.1.0", "private": true, "type": "module", - "description": "Contrat d'échange inter-juridictions : grammaire source (grammar/) et enveloppe d'assertion (envelope/), sous un seul numéro de version (ADR-061/A1.2 §2.6, décision 6). Privé jusqu'à l'extraction publique décrite dans OPENING.md.", + "description": "Contrat d'échange inter-juridictions : grammaire source (grammar/) et enveloppe d'assertion (envelope/), sous un seul numéro de version (ADR-061/A1.2 §2.6, décision 6). Public, développé ici par pull request endossée (temoin/DEC-014).", "license": "Apache-2.0", "scripts": { "conformance": "node envelope/conformance/run.mjs",