railiance-master/schemas/release-unit.schema.json
codex 56fe6bda80 Propose ADR-0011 and the release-unit schema.
Amends ADR-0007 with a fate-group declaration whose claim is the coverage authority. Founder review is RMASTER-WP-0028. release-registry validates against this file and does not copy it.

Assistant: grok
Assistant-Session: 01a15f9f-3d77-7d21-911b-d93c86f9d14f
2026-10-21 21:00:54 +02:00

362 lines
12 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://railiance.dev/schemas/release-unit.schema.json",
"title": "Railiance release-unit declaration",
"description": "Normative shape for registry/release-units/*.yaml, authored by railiance-master under ADR-0011 as an amendment to ADR-0007. Evolves rapp.schema.json. This schema is normative; ADR-0011 cites it and does not restate its fields. release-registry validates against this file and does not carry a copy.",
"type": "object",
"additionalProperties": false,
"required": [
"kind",
"unit_id",
"owner",
"contract_version",
"data_classification",
"criticality",
"composition",
"rollout_contract",
"smoke_contract",
"rollback_contract",
"workloads",
"pins"
],
"properties": {
"kind": { "const": "release-unit" },
"unit_id": {
"$ref": "#/$defs/unitId",
"description": "Canonical fate-group identifier. The coverage claim and the railiance.io/release-unit label both use this value."
},
"owner": {
"$ref": "#/$defs/repoSlug",
"description": "The single repository accountable for rollout. A repository slug, not a catalog group."
},
"contract_version": { "$ref": "#/$defs/semver" },
"data_classification": {
"enum": ["public", "internal", "confidential", "restricted"]
},
"criticality": {
"enum": ["low", "medium", "high", "critical"]
},
"composition": { "$ref": "#/$defs/composition" },
"rollout_contract": { "$ref": "#/$defs/rolloutContract" },
"smoke_contract": { "$ref": "#/$defs/smokeContract" },
"rollback_contract": { "$ref": "#/$defs/rollbackContract" },
"exposure": { "$ref": "#/$defs/exposure" },
"workloads": {
"type": "array",
"minItems": 1,
"description": "Coverage claims. Each entry names one top-level workload by kind, namespace and name. The claim is the coverage authority.",
"items": { "$ref": "#/$defs/workloadClaim" }
},
"part_of": {
"$ref": "#/$defs/unitId",
"description": "Optional containment. The named unit is an umbrella. Rollback fate stays with this unit."
},
"depends_on": {
"type": "array",
"uniqueItems": true,
"description": "Ordering and compatibility between independently rolled units. Data only; this schema grants no deploy right.",
"items": { "$ref": "#/$defs/dependency" }
},
"pins": { "$ref": "#/$defs/pins" }
},
"allOf": [
{
"description": "public exposure requires a grant object, as in rapp.schema.json.",
"if": {
"required": ["exposure"],
"properties": {
"exposure": {
"required": ["posture"],
"properties": { "posture": { "const": "public" } }
}
}
},
"then": {
"properties": {
"exposure": { "required": ["grant"] }
}
}
}
],
"$defs": {
"repoSlug": {
"type": "string",
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
"minLength": 2
},
"unitId": {
"type": "string",
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
"minLength": 2
},
"semver": {
"type": "string",
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$"
},
"notes": {
"type": "array",
"items": { "type": "string", "minLength": 1 }
},
"dnsLabel": {
"type": "string",
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
"maxLength": 63
},
"composition": {
"type": "object",
"additionalProperties": false,
"required": ["purpose", "member_repos"],
"description": "Same composition block as rapp.schema.json. member_repos stay many-to-many across units. Workload claims stay one-to-one.",
"properties": {
"purpose": { "type": "string", "minLength": 12 },
"member_repos": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["repo", "role", "deployables"],
"properties": {
"repo": { "$ref": "#/$defs/repoSlug" },
"role": { "type": "string", "minLength": 3 },
"deployables": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": { "type": "string", "minLength": 1 }
}
}
}
},
"upstream_components": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["name", "source", "version"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"source": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 }
}
}
}
}
},
"rolloutContract": {
"type": "object",
"additionalProperties": false,
"required": ["default_mode"],
"properties": {
"default_mode": { "type": "string", "minLength": 3 },
"commands": {
"type": "array",
"items": { "type": "string", "minLength": 1 }
},
"production_gate": { "type": "string" },
"notes": { "$ref": "#/$defs/notes" }
}
},
"smokeContract": {
"type": "object",
"additionalProperties": false,
"required": ["required"],
"properties": {
"required": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": { "type": "string", "minLength": 1 }
},
"commands": {
"type": "array",
"items": { "type": "string", "minLength": 1 }
},
"notes": { "$ref": "#/$defs/notes" }
}
},
"rollbackContract": {
"type": "object",
"additionalProperties": false,
"required": ["order"],
"properties": {
"order": {
"type": "array",
"minItems": 1,
"items": { "type": "string", "minLength": 1 }
},
"commands": {
"type": "array",
"items": { "type": "string", "minLength": 1 }
},
"fallback": { "type": "string" },
"notes": { "$ref": "#/$defs/notes" }
}
},
"exposure": {
"type": "object",
"additionalProperties": false,
"required": ["posture"],
"description": "Who may reach the listener. Omitted means private (ADR-0008). Distinct from data_classification.",
"properties": {
"posture": { "enum": ["private", "operator", "public"] },
"grant": { "$ref": "#/$defs/exposureGrant" },
"tunnel": { "$ref": "#/$defs/exposureTunnel" },
"binding_admission": {
"enum": ["declared", "installed", "verified", "production-approved", "deprecated"]
}
}
},
"exposureGrant": {
"type": "object",
"additionalProperties": false,
"required": ["reason", "approved_on", "residual_risk_owner"],
"anyOf": [
{ "required": ["hostname"] },
{ "required": ["port"] }
],
"properties": {
"hostname": { "type": "string", "minLength": 1 },
"port": { "type": "integer", "minimum": 1, "maximum": 65535 },
"reason": { "type": "string", "minLength": 1 },
"approved_on": { "type": "string", "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" },
"residual_risk_owner": { "type": "string", "minLength": 1 }
}
},
"exposureTunnel": {
"type": "object",
"additionalProperties": false,
"required": ["name"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"local_port": { "type": "integer", "minimum": 1, "maximum": 65535 }
}
},
"workloadClaim": {
"type": "object",
"additionalProperties": false,
"required": ["kind", "namespace", "name"],
"properties": {
"kind": {
"enum": ["Deployment", "StatefulSet", "DaemonSet", "CronJob", "Job"]
},
"namespace": { "$ref": "#/$defs/dnsLabel" },
"name": { "$ref": "#/$defs/dnsLabel" }
}
},
"dependency": {
"type": "object",
"additionalProperties": false,
"required": ["unit_id"],
"properties": {
"unit_id": { "$ref": "#/$defs/unitId" },
"readiness": {
"type": "string",
"minLength": 1,
"description": "Readiness the depended-on unit must show before this unit rolls. Recorded as data. No controller is implied."
},
"version": {
"type": "string",
"minLength": 1,
"description": "Compatibility constraint on the depended-on unit."
}
}
},
"pins": {
"type": "object",
"additionalProperties": false,
"required": ["resources", "sources"],
"description": "Specification-level subset of an OCM component descriptor. No OCM library is required to read it.",
"properties": {
"component": {
"type": "object",
"additionalProperties": false,
"required": ["name", "version"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 }
}
},
"resources": {
"type": "array",
"items": { "$ref": "#/$defs/resource" }
},
"sources": {
"type": "array",
"items": { "$ref": "#/$defs/source" }
},
"references": {
"type": "array",
"description": "OCM component references. Operational containment and ordering are part_of and depends_on.",
"items": { "$ref": "#/$defs/reference" }
}
}
},
"digest": {
"type": "object",
"additionalProperties": false,
"required": ["hashAlgorithm", "normalisationAlgorithm", "value"],
"properties": {
"hashAlgorithm": { "type": "string", "minLength": 1 },
"normalisationAlgorithm": { "type": "string", "minLength": 1 },
"value": {
"type": "string",
"minLength": 1,
"description": "Digest string as the live object shows it. An image digest keeps the sha256: prefix."
}
}
},
"resource": {
"type": "object",
"additionalProperties": false,
"required": ["name", "type", "version"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"type": {
"type": "string",
"minLength": 1,
"description": "OCM resource type. ociImage and helmChart are the types the drift check reads."
},
"version": { "type": "string", "minLength": 1 },
"relation": { "enum": ["local", "external"] },
"digest": { "$ref": "#/$defs/digest" },
"releaseRevision": {
"type": "string",
"minLength": 1,
"description": "Helm release revision. Compared with the revision annotation on the workload object. Absent from the object, the revision is unobservable. It is not read from a Helm release Secret."
}
},
"allOf": [
{
"if": { "properties": { "type": { "const": "ociImage" } }, "required": ["type"] },
"then": { "required": ["digest"] }
},
{
"if": { "properties": { "type": { "const": "helmChart" } }, "required": ["type"] },
"then": { "required": ["releaseRevision"] }
}
]
},
"source": {
"type": "object",
"additionalProperties": false,
"required": ["name", "type", "version", "digest"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"type": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 },
"digest": { "$ref": "#/$defs/digest" }
}
},
"reference": {
"type": "object",
"additionalProperties": false,
"required": ["name", "componentName", "version"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"componentName": { "type": "string", "minLength": 1 },
"version": { "type": "string", "minLength": 1 }
}
}
}
}