schema/revl-interchange-v1.schema.json
Rama main · réplica de lectura del repositorio original.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://revl.dev/schema/revl-interchange-v1.schema.json",
"title": "revl composition interchange (manifest + G8 audit)",
"description": "The versioned, tool-consumable verdict a revl gate produces for a composition: what each component provides, requires, and reaches (its G8 boundary surface), plus the declared host-code externs and the per-service distributability verdict. A consumer can read provides/requires/reaches from this document WITHOUT running revl. See docs/interchange-format.md for the compatibility promise.",
"type": "object",
"required": ["schema_version", "kind", "manifest", "boundary"],
"properties": {
"schema_version": {
"description": "MAJOR.MINOR of this interchange format. Same MAJOR is backward compatible: a consumer written for X.m keeps reading X.n (n>m); new MINOR versions only add optional members. A MAJOR bump may remove or re-shape members.",
"type": "string",
"pattern": "^[0-9]+\\.[0-9]+$"
},
"kind": {
"description": "Self-identifying tag so a consumer can tell an interchange document apart from any other JSON. Always the constant below for this format.",
"type": "string",
"enum": ["revl.interchange"]
},
"manifest": { "$ref": "#/$defs/manifest" },
"boundary": {
"description": "Per-component G8 boundary surface, keyed by component name. This is the 'reaches' half of provides/requires/reaches.",
"type": "object",
"additionalProperties": { "$ref": "#/$defs/boundaryEntry" }
},
"externs": {
"description": "Every declared host-code extern in the composition (the trust surface a proof cannot cover).",
"type": "array",
"items": { "$ref": "#/$defs/extern" }
},
"distributability": {
"description": "Per-service verdict on whether the service may cross a process seam (interop-bridge §4), keyed by service name.",
"type": "object",
"additionalProperties": { "$ref": "#/$defs/distributabilityVerdict" }
},
"cardinality": {
"description": "Optional (item 260). Per-component per-capability crossing-count ceiling per activation, keyed by component name. Present only for components that cross an emission boundary, so a crossing-free composition carries an empty object. A capability whose multiplicity is not statically provable (a loop, any recursion, a first-class emitting arrow, a crossing behind a host extern) is `unbounded` with `bound: null` and a `reason`; the component `verdict` rolls the tokens up with `unbounded` dominating.",
"type": "object",
"additionalProperties": { "$ref": "#/$defs/cardinalityEntry" }
},
"gauntlet": {
"description": "Optional. A gauntlet dossier (docs/gauntlet.md) carried in the same envelope. Its proved/tested/claimed/pending sections reuse the manifest+boundary vocabulary defined here, so item 31's dossier composes into this format without a schema change. Left open (additionalProperties) so the dossier can evolve independently.",
"type": "object"
},
"function_specs": {
"description": "Optional (issue #2705). One row per function that states an `ensures` spec, present only when a function states one. `last_result` is the result of the last recorded `revl test --report json` run supplied with `revl audit --spec-results`, or `unrun` when none was recorded (docs/function-specs.md).",
"type": "array",
"items": {
"type": "object",
"required": ["function", "spec", "last_result"],
"additionalProperties": true
}
}
},
"additionalProperties": true,
"$defs": {
"manifest": {
"description": "The linker's composition manifest: the static membership, load order, and each component's provide/require/isolate/intercept surface.",
"type": "object",
"required": ["components", "loadOrder"],
"properties": {
"components": {
"type": "array",
"items": { "$ref": "#/$defs/component" }
},
"loadOrder": {
"description": "Component names in dependency order (providers first). Teardown is the exact reverse (LIFO).",
"type": "array",
"items": { "type": "string" }
},
"templates": {
"description": "Optional. Instance-parametric components: spawn targets instantiated at runtime, each in its own local realm, excluded from the static composition.",
"type": "array",
"items": { "type": "string" }
}
},
"additionalProperties": true
},
"component": {
"type": "object",
"required": ["name", "inject", "provides"],
"properties": {
"name": { "type": "string" },
"file": { "type": "string" },
"inject": {
"description": "Provision keys this component requires (its 'requires').",
"type": "array",
"items": { "type": "string" }
},
"provides": {
"description": "Provision keys this component provides.",
"type": "array",
"items": { "type": "string" }
},
"isolate": {
"description": "Optional. provision-key -> realm name, when a key is bound in a named local realm.",
"type": "object",
"additionalProperties": { "type": "string" }
},
"intercept": {
"description": "Optional. provision-key -> interception metadata for keys this component intercepts.",
"type": "object",
"additionalProperties": { "type": "object" }
}
},
"additionalProperties": true
},
"boundaryEntry": {
"type": "object",
"required": ["emissions", "capabilities", "compensated", "awaits", "externs"],
"properties": {
"emissions": {
"description": "Emission call sites (irreversible boundary crossings) this component performs, as `key.method` labels.",
"type": "array",
"items": { "type": "string" }
},
"capabilities": {
"description": "emission-label -> the capability scopes its declaration carries. A single-element list of \"*\" is a bare emission that promises nothing about where it goes.",
"type": "object",
"additionalProperties": {
"type": "array",
"items": { "type": "string" }
}
},
"compensated": {
"description": "How many of the `emissions` labels above carry a compensating undo. Counted over distinct labels, never over emit steps, so it can never exceed the number of `emissions` and a label compensated at more than one site counts once. Compensation reached through an emitting host extern has no `key.method` label and is therefore absent from this count; it is named in `compensatedHostEmissions` instead.",
"type": "integer",
"minimum": 0
},
"compensatedHostEmissions": {
"description": "Names of emitting host externs this component compensates. They cross the boundary but carry no `key.method` label, so they are absent from both `emissions` and `compensated`. Present only when there is one, so a boundary entry with no host-extern compensation is byte-identical to before this field existed.",
"type": "array",
"items": { "type": "string" }
},
"awaits": {
"description": "Iteration/await boundaries in this component's body.",
"type": "integer",
"minimum": 0
},
"externs": {
"description": "Host-code externs this component reaches, transitively through functions. A first-class dispatch appears as name \"*\" with class \"first-class dispatch\".",
"type": "array",
"items": { "$ref": "#/$defs/extern" }
}
},
"additionalProperties": true
},
"extern": {
"type": "object",
"required": ["name", "backends"],
"properties": {
"name": { "type": "string" },
"class": {
"description": "The extern's classification, one of the four G4 boundary classes: \"pure\" (no observable effect), \"acquire\" (takes a resource that must be released), \"emission\" (an irreversible crossing out of the system), or \"witnessed\" (a reversible host mutation that names its inverse). The pseudo-class \"first-class dispatch\" appears (with name \"*\") for a reach through a value whose target is not statically nameable. May be null when unknown.",
"type": ["string", "null"]
},
"inverse": {
"description": "Issue #2692: the single derived inverse-provenance class of this crossing, one value of the closed vocabulary. \"declared\" is an exact inverse the author declared (`undo ...`); \"generated\" is an exact inverse derived from trusted metadata (reserved: no current declaration produces one); \"compensation\" is a best-effort offset (`compensate ...`), not a restoration; \"forward\" is irreversible but recoverable forward by a re-issue the declaration's idempotency register covers (a deferred emission with `idempotent` or `idempotent(key: ...)`; the register says whether the re-issue is free or fenced); \"irreversible\" is an emission with no inverse, offset or forward claim; \"unknown\" is no provenance fact at all. This version emits it on every entry, `unknown` included, and it is never inferred from a name or a shape. Optional in the schema so a document captured before the field existed still validates; absence there is not `unknown`. See docs/interchange-format.md.",
"type": "string",
"enum": ["declared", "generated", "compensation", "forward",
"irreversible", "unknown"]
},
"capabilities": {
"description": "The declared capability scope of a scoped `emission[...]`/`witnessed[...]` extern, as a sorted list of tokens (e.g. [\"fs\"]). This token — not the extern name — is what a `capability <glob>` policy rule and the register floor key on. Absent for an unscoped extern, whose token is its own name. This is the machine-readable form of the ` [fs]` clause on the human render's `host code:` line.",
"type": "array",
"items": { "type": "string" }
},
"backends": {
"description": "Backend tiers this extern has a body for (sorted), e.g. [\"py\"]. This is the machine-readable form of the tier the human render prints after the class, as in `(witnessed, py)`.",
"type": "array",
"items": { "type": "string" }
}
},
"additionalProperties": true
},
"distributabilityVerdict": {
"type": "object",
"required": ["verdict", "reasons"],
"properties": {
"verdict": {
"type": "string",
"enum": ["transport-safe", "address-space-bound"]
},
"reasons": {
"type": "array",
"items": { "type": "string" }
}
},
"additionalProperties": true
},
"cardinalityEntry": {
"description": "One component's crossing-count ceiling (item 260).",
"type": "object",
"required": ["per_capability", "verdict"],
"properties": {
"per_capability": {
"description": "Capability token -> its per-activation crossing ceiling.",
"type": "object",
"additionalProperties": {
"type": "object",
"required": ["bound", "kind"],
"properties": {
"bound": {
"description": "The proved per-activation maximum, or null when unbounded.",
"type": ["integer", "null"]
},
"kind": {
"type": "string",
"enum": ["bounded", "bounded-symbolic", "unbounded"]
},
"reason": {
"description": "Present when kind is unbounded: why the multiplicity is not statically provable.",
"type": "string"
}
},
"additionalProperties": true
}
},
"verdict": {
"type": "string",
"enum": ["bounded", "bounded-symbolic", "unbounded"]
}
},
"additionalProperties": true
}
}
}