Packaging, rtut check, tests CI job; schemas move into the package (RBT-WP-0009-T02)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 295952@bnt-lap001
Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
This commit is contained in:
tegwick 2026-09-29 21:32:19 +02:00
parent 6d3414d8b7
commit e021fd979f
16 changed files with 163 additions and 10 deletions

View file

@ -0,0 +1,37 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://runbook-tutorials.invalid/schema/attribute-set/v0.1",
"title": "Attribute set",
"type": "object",
"required": ["spec", "id", "title", "attributes"],
"additionalProperties": false,
"properties": {
"spec": {"const": "attribute-set/v0.1"},
"id": {"type": "string", "pattern": "^[a-z0-9]+([.-][a-z0-9]+)*$"},
"title": {"type": "string", "minLength": 3},
"extends": {"type": "array", "items": {"type": "string"}, "description": "Composed first; later entries and this set's own attributes override."},
"attributes": {"$ref": "#/$defs/attributes"}
},
"$defs": {
"attributes": {
"type": "object",
"propertyNames": {"pattern": "^[a-z][a-z0-9_]*$"},
"additionalProperties": {"$ref": "#/$defs/attribute"}
},
"attribute": {
"oneOf": [
{"type": ["string", "integer", "boolean"]},
{
"type": "object",
"additionalProperties": false,
"properties": {
"value": {"type": ["string", "integer", "boolean"]},
"requirement": {"enum": ["required", "optional"]},
"note": {"type": "string"}
},
"minProperties": 1
}
]
}
}
}

View file

@ -0,0 +1,169 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://runbook-tutorials.invalid/schema/project/v0.1",
"title": "Project or project template",
"type": "object",
"required": [
"spec",
"id",
"title",
"runbooks"
],
"additionalProperties": false,
"properties": {
"spec": {
"enum": [
"project/v0.1",
"project-template/v0.1"
]
},
"id": {
"type": "string",
"pattern": "^[a-z0-9]+([.-][a-z0-9]+)*$"
},
"title": {
"type": "string",
"minLength": 3
},
"goal": {
"type": "string"
},
"environment": {
"enum": [
"rehearsal",
"staging",
"production"
]
},
"template": {
"type": "string",
"description": "Id of the template this project was created from."
},
"attribute_sets": {
"type": "array",
"items": {
"type": "string"
}
},
"attributes": {
"$ref": "#/$defs/attributes"
},
"targets": {
"type": "object",
"propertyNames": {
"pattern": "^[a-z][a-z0-9-]*$"
},
"additionalProperties": {
"type": "object",
"additionalProperties": false,
"properties": {
"title": {
"type": "string"
},
"attribute_sets": {
"type": "array",
"items": {
"type": "string"
}
},
"attributes": {
"$ref": "#/$defs/attributes"
}
}
}
},
"runbooks": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"pack"
],
"additionalProperties": false,
"properties": {
"pack": {
"type": "string"
},
"target": {
"type": "string"
},
"after": {
"type": "array",
"items": {
"type": "string"
}
},
"attributes": {
"$ref": "#/$defs/attributes"
}
}
}
},
"description": {
"type": "string",
"description": "What this project is, in its own words. Not inherited from a template."
}
},
"allOf": [
{
"if": {
"properties": {
"spec": {
"const": "project/v0.1"
}
}
},
"then": {
"required": [
"environment"
]
}
}
],
"$defs": {
"attributes": {
"type": "object",
"propertyNames": {
"pattern": "^[a-z][a-z0-9_]*$"
},
"additionalProperties": {
"$ref": "#/$defs/attribute"
}
},
"attribute": {
"oneOf": [
{
"type": [
"string",
"integer",
"boolean"
]
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"value": {
"type": [
"string",
"integer",
"boolean"
]
},
"requirement": {
"enum": [
"required",
"optional"
]
},
"note": {
"type": "string"
}
},
"minProperties": 1
}
]
}
}
}

View file

@ -0,0 +1,643 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://runbook-tutorials.invalid/schema/runbook-pack/v0.2",
"title": "Runbook pack manifest v0.2",
"type": "object",
"required": [
"spec",
"id",
"title",
"owner",
"engine"
],
"additionalProperties": false,
"properties": {
"spec": {
"const": "runbook-pack/v0.2"
},
"id": {
"type": "string",
"pattern": "^[a-z0-9]+([.-][a-z0-9]+)*$"
},
"title": {
"type": "string",
"minLength": 3
},
"owner": {
"type": "string",
"minLength": 1
},
"outcome": {
"type": "string"
},
"exercise_status": {
"type": "string",
"deprecated": true,
"description": "Deprecated and ignored. Maturity is derived from run history (docs/maturity-and-projects.md); old packs that still carry this field keep loading."
},
"engine": {
"enum": [
"native",
"legacy-command"
]
},
"confirm_phrase": {
"type": "string",
"minLength": 8,
"description": "Typed by the operator to confirm an irreversible step."
},
"timeout": {
"$ref": "#/$defs/timeout",
"description": "Default for auto-confirm runs."
},
"legacy": {
"type": "object",
"required": [
"command"
],
"additionalProperties": false,
"properties": {
"command": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"cwd": {
"type": "string"
},
"url": {
"type": "string"
},
"note": {
"type": "string"
}
}
},
"framing": {
"type": "array",
"items": {
"type": "string"
},
"description": "Things to keep in mind; shown first; not gates."
},
"actors": {
"type": "array",
"items": {
"$ref": "#/$defs/actor"
}
},
"datapoints": {
"type": "array",
"items": {
"$ref": "#/$defs/datapoint"
}
},
"stages": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/stage"
}
},
"artefacts": {
"type": "array",
"items": {
"$ref": "#/$defs/artefact"
}
},
"ownership": {
"type": "array",
"items": {
"$ref": "#/$defs/ownership"
}
}
},
"allOf": [
{
"if": {
"properties": {
"engine": {
"const": "native"
}
}
},
"then": {
"required": [
"stages",
"framing"
]
}
},
{
"if": {
"properties": {
"engine": {
"const": "legacy-command"
}
}
},
"then": {
"required": [
"legacy"
]
}
}
],
"$defs": {
"id": {
"type": "string",
"pattern": "^[a-z][a-z0-9-]*$"
},
"dpid": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$"
},
"timeout": {
"type": "object",
"required": [
"seconds"
],
"additionalProperties": false,
"properties": {
"seconds": {
"type": "integer",
"minimum": 1
},
"on_timeout": {
"enum": [
"stop",
"push-through"
],
"default": "stop"
}
}
},
"actor": {
"type": "object",
"required": [
"id",
"role",
"description"
],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/$defs/id"
},
"role": {
"type": "string"
},
"description": {
"type": "string"
},
"privileges": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"datapoint": {
"type": "object",
"required": [
"id",
"label",
"type"
],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/$defs/dpid"
},
"label": {
"type": "string"
},
"type": {
"enum": [
"string",
"text",
"enum",
"path",
"bool",
"int"
]
},
"values": {
"type": "array",
"items": {
"type": "string"
}
},
"default": {
"type": [
"string",
"integer",
"boolean"
]
},
"pattern": {
"type": "string"
},
"help": {
"type": "string"
},
"dimensions": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"ask_in": {
"$ref": "#/$defs/id"
},
"produced_by": {
"$ref": "#/$defs/id"
},
"confirmed_by": {
"$ref": "#/$defs/id"
},
"secret": {
"const": false
}
}
},
"check": {
"type": "object",
"additionalProperties": false,
"properties": {
"done_when": {
"type": "string",
"minLength": 3
},
"command": {
"type": "string"
},
"expect": {
"enum": [
"manual",
"exit-0",
"output-contains"
]
},
"contains": {
"type": "string"
},
"datapoints_ok": {
"type": "array",
"items": {
"$ref": "#/$defs/dpid"
},
"minItems": 1
}
},
"anyOf": [
{
"required": [
"done_when"
]
},
{
"required": [
"datapoints_ok"
]
}
]
},
"alternative": {
"type": "object",
"required": [
"id",
"title",
"verify"
],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/$defs/id"
},
"title": {
"type": "string"
},
"command": {
"type": "string"
},
"reversible": {
"type": "boolean"
},
"rollback": {
"type": "string"
},
"verify": {
"$ref": "#/$defs/check"
},
"recommended_when": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"idempotent": {
"enum": [
"idempotent",
"non-idempotent",
"conditional"
]
},
"idempotent_note": {
"type": "string"
}
}
},
"step": {
"type": "object",
"required": [
"id",
"title",
"actor",
"owner"
],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/$defs/id"
},
"title": {
"type": "string"
},
"actor": {
"$ref": "#/$defs/id"
},
"owner": {
"type": "string"
},
"description": {
"type": "string"
},
"command": {
"type": "string"
},
"reversible": {
"type": "boolean",
"default": true
},
"rollback": {
"type": "string"
},
"confirm_phrase": {
"type": "string",
"minLength": 8
},
"verify": {
"$ref": "#/$defs/check"
},
"requires": {
"type": "array",
"items": {
"type": "string"
}
},
"uses": {
"type": "array",
"items": {
"$ref": "#/$defs/dpid"
}
},
"produces": {
"type": "array",
"items": {
"type": "string"
}
},
"evidence": {
"type": "array",
"items": {
"type": "string"
}
},
"one_of": {
"type": "array",
"minItems": 2,
"items": {
"$ref": "#/$defs/alternative"
}
},
"timeout": {
"$ref": "#/$defs/timeout"
},
"downstream_risk": {
"type": "string"
},
"auto_confirm": {
"enum": [
"allowed",
"manual-only"
],
"default": "allowed"
},
"idempotent": {
"enum": [
"idempotent",
"non-idempotent",
"conditional"
],
"description": "Is re-running this step after a partial or complete earlier attempt safe? Omit if unknown. Values are words, never yes/no: YAML reads unquoted yes/no as booleans."
},
"idempotent_note": {
"type": "string",
"description": "Required when idempotent is conditional: under what condition."
}
},
"anyOf": [
{
"required": [
"verify"
]
},
{
"required": [
"one_of"
]
}
]
},
"gate": {
"type": "object",
"required": [
"id",
"title",
"goal",
"checks"
],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/$defs/id"
},
"title": {
"type": "string"
},
"goal": {
"type": "string",
"minLength": 3
},
"blocking": {
"type": "boolean",
"default": true
},
"checks": {
"type": "array",
"minItems": 1,
"items": {
"$ref": "#/$defs/check"
}
},
"waivable": {
"type": "object",
"required": [
"by",
"needs_reason"
],
"additionalProperties": false,
"properties": {
"by": {
"$ref": "#/$defs/id"
},
"needs_reason": {
"const": true
}
}
},
"timeout": {
"$ref": "#/$defs/timeout"
},
"downstream_risk": {
"type": "string"
}
}
},
"stage": {
"type": "object",
"required": [
"id",
"title",
"goal"
],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/$defs/id"
},
"title": {
"type": "string"
},
"goal": {
"type": "string",
"minLength": 3
},
"framing": {
"type": "array",
"items": {
"type": "string"
}
},
"requires": {
"type": "array",
"items": {
"type": "string"
}
},
"steps": {
"type": "array",
"items": {
"$ref": "#/$defs/step"
}
},
"gates": {
"type": "array",
"items": {
"$ref": "#/$defs/gate"
}
},
"stages": {
"type": "array",
"items": {
"$ref": "#/$defs/stage"
}
}
},
"anyOf": [
{
"required": [
"steps"
]
},
{
"required": [
"stages"
]
}
]
},
"artefact": {
"type": "object",
"required": [
"id",
"title",
"location",
"produced_by"
],
"additionalProperties": false,
"properties": {
"id": {
"$ref": "#/$defs/id"
},
"title": {
"type": "string"
},
"location": {
"type": "object",
"required": [
"datapoint"
],
"additionalProperties": false,
"properties": {
"datapoint": {
"$ref": "#/$defs/dpid"
},
"path": {
"type": "string"
}
}
},
"produced_by": {
"$ref": "#/$defs/id"
},
"lives_until": {
"$ref": "#/$defs/id"
}
}
},
"ownership": {
"type": "object",
"required": [
"concern",
"owner"
],
"additionalProperties": false,
"properties": {
"concern": {
"type": "string"
},
"owner": {
"type": "string"
}
}
}
}
}

View file

@ -0,0 +1,294 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://runbook-tutorials.invalid/schema/runbook-pack/v0.1",
"title": "Runbook pack manifest",
"type": "object",
"required": [
"spec",
"id",
"title",
"owner",
"engine"
],
"additionalProperties": false,
"properties": {
"spec": {
"const": "runbook-pack/v0.1"
},
"id": {
"type": "string",
"pattern": "^[a-z0-9]+([.-][a-z0-9]+)*$"
},
"title": {
"type": "string",
"minLength": 3
},
"owner": {
"type": "string",
"minLength": 1,
"description": "Repository that owns the procedure"
},
"outcome": {
"type": "string"
},
"exercise_status": {
"type": "string",
"deprecated": true,
"description": "Deprecated and ignored. Maturity is derived from run history (docs/maturity-and-projects.md); old packs that still carry this field keep loading."
},
"engine": {
"enum": [
"native",
"legacy-command"
]
},
"legacy": {
"type": "object",
"required": [
"command"
],
"additionalProperties": false,
"properties": {
"command": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
},
"cwd": {
"type": "string"
},
"url": {
"type": "string"
},
"note": {
"type": "string"
}
}
},
"parameters": {
"type": "array",
"items": {
"$ref": "#/$defs/parameter"
}
},
"prerequisites": {
"type": "array",
"items": {
"$ref": "#/$defs/prerequisite"
}
},
"steps": {
"type": "array",
"items": {
"$ref": "#/$defs/step"
}
},
"threat_checks": {
"type": "array",
"items": {
"type": "string"
}
},
"ownership": {
"type": "array",
"items": {
"$ref": "#/$defs/ownership"
}
}
},
"allOf": [
{
"if": {
"properties": {
"engine": {
"const": "native"
}
}
},
"then": {
"required": [
"steps",
"threat_checks"
]
}
},
{
"if": {
"properties": {
"engine": {
"const": "legacy-command"
}
}
},
"then": {
"required": [
"legacy"
]
}
}
],
"$defs": {
"parameter": {
"type": "object",
"required": [
"id",
"label",
"type"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$"
},
"label": {
"type": "string"
},
"type": {
"enum": [
"string",
"text",
"enum",
"path",
"bool",
"int"
]
},
"values": {
"type": "array",
"items": {
"type": "string"
}
},
"default": {
"type": [
"string",
"integer",
"boolean"
]
},
"pattern": {
"type": "string"
},
"help": {
"type": "string"
},
"secret": {
"const": false,
"description": "Parameters are never secret. Secrets are entered by the operator into the tool that owns them, never into the engine."
}
}
},
"prerequisite": {
"type": "object",
"required": [
"text",
"owner"
],
"additionalProperties": false,
"properties": {
"text": {
"type": "string"
},
"owner": {
"type": "string"
}
}
},
"check": {
"type": "object",
"required": [
"done_when"
],
"additionalProperties": false,
"properties": {
"done_when": {
"type": "string",
"minLength": 3
},
"command": {
"type": "string"
},
"expect": {
"enum": [
"manual",
"exit-0",
"output-contains"
]
},
"contains": {
"type": "string"
}
}
},
"step": {
"type": "object",
"required": [
"id",
"title",
"owner",
"verify"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"pattern": "^[a-z][a-z0-9-]*$"
},
"title": {
"type": "string"
},
"owner": {
"type": "string"
},
"description": {
"type": "string"
},
"command": {
"type": "string",
"description": "Template; {{param}} placeholders only"
},
"risk": {
"enum": [
"read-only",
"changes-state",
"attended",
"destructive"
],
"default": "read-only"
},
"verify": {
"$ref": "#/$defs/check"
},
"rollback": {
"type": "string"
},
"evidence": {
"type": "array",
"items": {
"type": "string"
},
"description": "Names of non-secret facts recorded when the step completes"
}
}
},
"ownership": {
"type": "object",
"required": [
"concern",
"owner"
],
"additionalProperties": false,
"properties": {
"concern": {
"type": "string"
},
"owner": {
"type": "string"
}
}
}
}
}