@runbooks/schema 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -0
- package/dist/agent-skills.test.d.ts +1 -0
- package/dist/agent-skills.test.js +71 -0
- package/dist/capabilities.d.ts +40 -0
- package/dist/capabilities.js +211 -0
- package/dist/capabilities.test.d.ts +1 -0
- package/dist/capabilities.test.js +119 -0
- package/dist/content-classes.test.d.ts +1 -0
- package/dist/content-classes.test.js +77 -0
- package/dist/generate.cli.d.ts +2 -0
- package/dist/generate.cli.js +27 -0
- package/dist/generate.d.ts +54 -0
- package/dist/generate.js +133 -0
- package/dist/generate.test.d.ts +1 -0
- package/dist/generate.test.js +85 -0
- package/dist/generated.d.ts +101 -0
- package/dist/generated.js +9 -0
- package/dist/generated.ts +82 -0
- package/dist/hash.d.ts +36 -0
- package/dist/hash.js +102 -0
- package/dist/hash.test.d.ts +1 -0
- package/dist/hash.test.js +54 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +61 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +236 -0
- package/dist/interpolation.d.ts +11 -0
- package/dist/interpolation.js +34 -0
- package/dist/node.d.ts +41 -0
- package/dist/node.js +111 -0
- package/dist/p1.test.d.ts +1 -0
- package/dist/p1.test.js +87 -0
- package/dist/run-report.test.d.ts +1 -0
- package/dist/run-report.test.js +145 -0
- package/dist/served.d.ts +36 -0
- package/dist/served.js +128 -0
- package/dist/stats.d.ts +17 -0
- package/dist/stats.js +23 -0
- package/dist/stats.test.d.ts +1 -0
- package/dist/stats.test.js +32 -0
- package/dist/validate.d.ts +16 -0
- package/dist/validate.js +46 -0
- package/dist/validate.test.d.ts +1 -0
- package/dist/validate.test.js +61 -0
- package/dist/vocabularies.test.d.ts +1 -0
- package/dist/vocabularies.test.js +77 -0
- package/package.json +50 -0
- package/spec/v1/schema/attestation.json +62 -0
- package/spec/v1/schema/p0.json +383 -0
- package/spec/v1/schema/p1.json +408 -0
- package/spec/v1/schema/record.json +908 -0
- package/spec/v1/schema/run-report-envelope.json +25 -0
- package/spec/v1/schema/run-report.json +121 -0
|
@@ -0,0 +1,908 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://runbooks.directory/spec/v1/schema/record.json",
|
|
4
|
+
"$defs": {
|
|
5
|
+
"stepId": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"pattern": "^[a-z][a-z0-9_]{0,31}$",
|
|
8
|
+
"description": "Unique within a version. Referenced by on_fail, branches and rollback_ref."
|
|
9
|
+
},
|
|
10
|
+
"toolRef": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^(mcp:[a-z0-9][a-z0-9-]*|cli:[a-z0-9][a-z0-9._-]*|iam:[a-z0-9-]+:[A-Za-z0-9*]+)$",
|
|
13
|
+
"description": "Same grammar as runbook.capabilities. A step may name a tool absent from capabilities[]: the schema permits it and the linter rejects it as a blocking T2 error (RUNBOOK.md 12). Schema and linter have different jobs."
|
|
14
|
+
},
|
|
15
|
+
"title": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 3,
|
|
18
|
+
"maxLength": 48,
|
|
19
|
+
"description": "An imperative verb phrase (RUNBOOK.md 18.6): 'Capture current lag', not 'Lag problem'. The 48-character cap is what keeps a node label to two lines."
|
|
20
|
+
},
|
|
21
|
+
"step": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"required": [
|
|
24
|
+
"id",
|
|
25
|
+
"kind",
|
|
26
|
+
"title"
|
|
27
|
+
],
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"properties": {
|
|
30
|
+
"id": {
|
|
31
|
+
"$ref": "#/$defs/stepId"
|
|
32
|
+
},
|
|
33
|
+
"kind": {
|
|
34
|
+
"enum": [
|
|
35
|
+
"check",
|
|
36
|
+
"action",
|
|
37
|
+
"decision",
|
|
38
|
+
"wait",
|
|
39
|
+
"escalate"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"title": {
|
|
43
|
+
"$ref": "#/$defs/title"
|
|
44
|
+
},
|
|
45
|
+
"tool": {
|
|
46
|
+
"$ref": "#/$defs/toolRef"
|
|
47
|
+
},
|
|
48
|
+
"command": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"description": "May interpolate {{inputs.<name>}}, and only names declared in runbook.inputs. The reference check is semantic, not structural, so it lives in the linter."
|
|
51
|
+
},
|
|
52
|
+
"expect": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 3
|
|
55
|
+
},
|
|
56
|
+
"assert": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"additionalProperties": false,
|
|
59
|
+
"minProperties": 1,
|
|
60
|
+
"description": "A machine-checkable postcondition, required by R2 supervision. `expect` stays prose for the reader; this is what a supervisor evaluates. A step with only prose is unevaluable at R2 - a first-class outcome in which the supervisor halts and asks a human rather than guessing (docs/decisions/Q12-expect-evaluability.md).",
|
|
61
|
+
"properties": {
|
|
62
|
+
"exit_code": {
|
|
63
|
+
"type": "integer",
|
|
64
|
+
"minimum": 0,
|
|
65
|
+
"maximum": 255
|
|
66
|
+
},
|
|
67
|
+
"stdout_matches": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "Regular expression."
|
|
70
|
+
},
|
|
71
|
+
"stdout_not_matches": {
|
|
72
|
+
"type": "string"
|
|
73
|
+
},
|
|
74
|
+
"stderr_empty": {
|
|
75
|
+
"type": "boolean"
|
|
76
|
+
},
|
|
77
|
+
"json_path": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"additionalProperties": false,
|
|
80
|
+
"required": [
|
|
81
|
+
"path",
|
|
82
|
+
"equals"
|
|
83
|
+
],
|
|
84
|
+
"properties": {
|
|
85
|
+
"path": {
|
|
86
|
+
"type": "string"
|
|
87
|
+
},
|
|
88
|
+
"equals": {}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"on_fail": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"pattern": "^(escalate:)?([a-z][a-z0-9_]{0,31}|end:(success|failed|aborted))$"
|
|
96
|
+
},
|
|
97
|
+
"question": {
|
|
98
|
+
"type": "string",
|
|
99
|
+
"minLength": 3
|
|
100
|
+
},
|
|
101
|
+
"branches": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"minProperties": 2,
|
|
104
|
+
"additionalProperties": {
|
|
105
|
+
"$ref": "#/$defs/stepId"
|
|
106
|
+
},
|
|
107
|
+
"description": "Label to step id. Two or more is structural and checked here; that one of them is a default is graph invariant 3, checked by the linter."
|
|
108
|
+
},
|
|
109
|
+
"risk": {
|
|
110
|
+
"enum": [
|
|
111
|
+
"read-only",
|
|
112
|
+
"reversible-write",
|
|
113
|
+
"destructive",
|
|
114
|
+
"irreversible"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
"requires_approval": {
|
|
118
|
+
"type": "boolean"
|
|
119
|
+
},
|
|
120
|
+
"rollback_ref": {
|
|
121
|
+
"type": "string",
|
|
122
|
+
"pattern": "^(rb_[0-9A-HJKMNP-TV-Z]{26}|[a-z][a-z0-9_]{0,31})$",
|
|
123
|
+
"description": "A step in this runbook, or another runbook. Cross-runbook resolution is not in v1; until then a rollback_ref to another runbook is documentation for a human, not a route a supervisor can take."
|
|
124
|
+
},
|
|
125
|
+
"duration": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"pattern": "^[0-9]+(s|m|h)$"
|
|
128
|
+
},
|
|
129
|
+
"until": {
|
|
130
|
+
"type": "string",
|
|
131
|
+
"minLength": 3
|
|
132
|
+
},
|
|
133
|
+
"timeout": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"pattern": "^[0-9]+(s|m|h)$"
|
|
136
|
+
},
|
|
137
|
+
"retry": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"additionalProperties": false,
|
|
140
|
+
"required": [
|
|
141
|
+
"max"
|
|
142
|
+
],
|
|
143
|
+
"properties": {
|
|
144
|
+
"max": {
|
|
145
|
+
"type": "integer",
|
|
146
|
+
"minimum": 1,
|
|
147
|
+
"maximum": 20
|
|
148
|
+
},
|
|
149
|
+
"target": {
|
|
150
|
+
"$ref": "#/$defs/stepId"
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"description": "The only permitted cycle, and it must be finite: an unbounded loop in an autonomous agent costs money and causes incidents (invariant 6)."
|
|
154
|
+
},
|
|
155
|
+
"to": {
|
|
156
|
+
"type": "string",
|
|
157
|
+
"minLength": 2
|
|
158
|
+
},
|
|
159
|
+
"next": {
|
|
160
|
+
"type": "string",
|
|
161
|
+
"pattern": "^([a-z][a-z0-9_]{0,31}|end:(success|failed|aborted))$",
|
|
162
|
+
"description": "The next step, or an explicit terminal. Invariant 7 requires every terminal to be explicit: a graph may not end by running out of next node."
|
|
163
|
+
},
|
|
164
|
+
"x-layout": {
|
|
165
|
+
"type": "object",
|
|
166
|
+
"additionalProperties": false,
|
|
167
|
+
"required": [
|
|
168
|
+
"x",
|
|
169
|
+
"y"
|
|
170
|
+
],
|
|
171
|
+
"properties": {
|
|
172
|
+
"x": {
|
|
173
|
+
"type": "number"
|
|
174
|
+
},
|
|
175
|
+
"y": {
|
|
176
|
+
"type": "number"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"description": "An authoring-time position from the constructor. Accepted so that a draft laid out by hand can be submitted, and stripped on publish: auto-layout is canonical, the catalog stores no positions, and every reader of a version sees the same graph (docs/decisions/Q8-layout.md). It never participates in content_hash, so re-laying-out a graph is not a new version."
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"allOf": [
|
|
183
|
+
{
|
|
184
|
+
"description": "A check without expect and on_fail cannot be evaluated or routed, so a supervisor would have to improvise both (invariant 4).",
|
|
185
|
+
"if": {
|
|
186
|
+
"properties": {
|
|
187
|
+
"kind": {
|
|
188
|
+
"const": "check"
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"required": [
|
|
192
|
+
"kind"
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
"then": {
|
|
196
|
+
"properties": {
|
|
197
|
+
"expect": {
|
|
198
|
+
"type": "string"
|
|
199
|
+
},
|
|
200
|
+
"on_fail": {
|
|
201
|
+
"type": "string"
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"required": [
|
|
205
|
+
"expect",
|
|
206
|
+
"on_fail"
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"description": "An action must state its risk. Unstated risk defaults to nothing safe.",
|
|
212
|
+
"if": {
|
|
213
|
+
"properties": {
|
|
214
|
+
"kind": {
|
|
215
|
+
"const": "action"
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"required": [
|
|
219
|
+
"kind"
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
"then": {
|
|
223
|
+
"properties": {
|
|
224
|
+
"risk": {
|
|
225
|
+
"type": "string"
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"required": [
|
|
229
|
+
"risk"
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"description": "Invariant 5: a destructive or irreversible action is always paired - an approval gate before it, or a rollback after it. This is the one rule the whole product exists to enforce, so it is structural rather than advisory.",
|
|
235
|
+
"if": {
|
|
236
|
+
"properties": {
|
|
237
|
+
"kind": {
|
|
238
|
+
"const": "action"
|
|
239
|
+
},
|
|
240
|
+
"risk": {
|
|
241
|
+
"enum": [
|
|
242
|
+
"destructive",
|
|
243
|
+
"irreversible"
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
"required": [
|
|
248
|
+
"kind",
|
|
249
|
+
"risk"
|
|
250
|
+
]
|
|
251
|
+
},
|
|
252
|
+
"then": {
|
|
253
|
+
"anyOf": [
|
|
254
|
+
{
|
|
255
|
+
"properties": {
|
|
256
|
+
"requires_approval": {
|
|
257
|
+
"const": true
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
"required": [
|
|
261
|
+
"requires_approval"
|
|
262
|
+
]
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
"properties": {
|
|
266
|
+
"rollback_ref": {
|
|
267
|
+
"type": "string"
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
"required": [
|
|
271
|
+
"rollback_ref"
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"description": "A decision needs a question and at least two branches.",
|
|
279
|
+
"if": {
|
|
280
|
+
"properties": {
|
|
281
|
+
"kind": {
|
|
282
|
+
"const": "decision"
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
"required": [
|
|
286
|
+
"kind"
|
|
287
|
+
]
|
|
288
|
+
},
|
|
289
|
+
"then": {
|
|
290
|
+
"properties": {
|
|
291
|
+
"question": {
|
|
292
|
+
"type": "string"
|
|
293
|
+
},
|
|
294
|
+
"branches": {
|
|
295
|
+
"type": "object"
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
"required": [
|
|
299
|
+
"question",
|
|
300
|
+
"branches"
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"description": "A wait must be bounded: either a duration, or a condition with a timeout. An unbounded wait is a hang with better manners.",
|
|
306
|
+
"if": {
|
|
307
|
+
"properties": {
|
|
308
|
+
"kind": {
|
|
309
|
+
"const": "wait"
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
"required": [
|
|
313
|
+
"kind"
|
|
314
|
+
]
|
|
315
|
+
},
|
|
316
|
+
"then": {
|
|
317
|
+
"anyOf": [
|
|
318
|
+
{
|
|
319
|
+
"properties": {
|
|
320
|
+
"duration": {
|
|
321
|
+
"type": "string"
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
"required": [
|
|
325
|
+
"duration"
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
"properties": {
|
|
330
|
+
"until": {
|
|
331
|
+
"type": "string"
|
|
332
|
+
},
|
|
333
|
+
"timeout": {
|
|
334
|
+
"type": "string"
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
"required": [
|
|
338
|
+
"until",
|
|
339
|
+
"timeout"
|
|
340
|
+
]
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"description": "Escalation is an exit from the workflow, so it must say where it goes.",
|
|
347
|
+
"if": {
|
|
348
|
+
"properties": {
|
|
349
|
+
"kind": {
|
|
350
|
+
"const": "escalate"
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
"required": [
|
|
354
|
+
"kind"
|
|
355
|
+
]
|
|
356
|
+
},
|
|
357
|
+
"then": {
|
|
358
|
+
"properties": {
|
|
359
|
+
"to": {
|
|
360
|
+
"type": "string"
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
"required": [
|
|
364
|
+
"to"
|
|
365
|
+
]
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
]
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
"title": "Runbook, as the catalog serves it",
|
|
372
|
+
"description": "A published record: the authored document plus what the build concluded about it. Validate /v1/runbooks/{publisher}/{slug}.json against this; validate a document you are about to submit against p0.json or p1.json, which forbid the fields the catalog assigns.",
|
|
373
|
+
"allOf": [
|
|
374
|
+
{
|
|
375
|
+
"title": "Runbook, profile P0",
|
|
376
|
+
"description": "Frontmatter for a P0 (prose) runbook. A superset of Agent Skills: `name` and `description` keep their Agent Skills meaning at the top level, and everything this specification adds lives under the single `runbook` key. See docs/decisions/Q2-frontmatter-namespacing.md.",
|
|
377
|
+
"type": "object",
|
|
378
|
+
"required": [
|
|
379
|
+
"name",
|
|
380
|
+
"description",
|
|
381
|
+
"runbook"
|
|
382
|
+
],
|
|
383
|
+
"additionalProperties": true,
|
|
384
|
+
"properties": {
|
|
385
|
+
"name": {
|
|
386
|
+
"type": "string",
|
|
387
|
+
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
|
|
388
|
+
"maxLength": 64,
|
|
389
|
+
"description": "Agent Skills field. ASCII slug, never reused (§9)."
|
|
390
|
+
},
|
|
391
|
+
"description": {
|
|
392
|
+
"type": "string",
|
|
393
|
+
"minLength": 16,
|
|
394
|
+
"maxLength": 1024,
|
|
395
|
+
"description": "Agent Skills field. What the procedure does and when to reach for it."
|
|
396
|
+
},
|
|
397
|
+
"runbook": {
|
|
398
|
+
"type": "object",
|
|
399
|
+
"additionalProperties": false,
|
|
400
|
+
"required": [
|
|
401
|
+
"schema_version",
|
|
402
|
+
"profile",
|
|
403
|
+
"domain",
|
|
404
|
+
"targets",
|
|
405
|
+
"trigger",
|
|
406
|
+
"execution",
|
|
407
|
+
"risk",
|
|
408
|
+
"capabilities",
|
|
409
|
+
"publisher",
|
|
410
|
+
"lang",
|
|
411
|
+
"license",
|
|
412
|
+
"source",
|
|
413
|
+
"content_hash",
|
|
414
|
+
"trust"
|
|
415
|
+
],
|
|
416
|
+
"properties": {
|
|
417
|
+
"schema_version": {
|
|
418
|
+
"const": "v1"
|
|
419
|
+
},
|
|
420
|
+
"id": {
|
|
421
|
+
"type": "string",
|
|
422
|
+
"pattern": "^rb_[0-9A-HJKMNP-TV-Z]{26}$",
|
|
423
|
+
"description": "ULID assigned by the catalog. Immutable, survives renames (§9). Absent in an authored document."
|
|
424
|
+
},
|
|
425
|
+
"semver": {
|
|
426
|
+
"type": "string",
|
|
427
|
+
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z.-]+)?$",
|
|
428
|
+
"description": "The version this snapshot is. Assigned by the catalog alongside `id` and `content_hash`; absent in an authored document, present in everything the catalog serves."
|
|
429
|
+
},
|
|
430
|
+
"content_hash": {
|
|
431
|
+
"type": "string",
|
|
432
|
+
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
433
|
+
"description": "What this version is (RUNBOOK.md 9). Agent pins point at it and attestations bind to it. Computed over the document with this field and x-layout excluded, so it cannot be part of what it hashes."
|
|
434
|
+
},
|
|
435
|
+
"profile": {
|
|
436
|
+
"enum": [
|
|
437
|
+
"P0",
|
|
438
|
+
"P1",
|
|
439
|
+
"P2"
|
|
440
|
+
]
|
|
441
|
+
},
|
|
442
|
+
"min_runtime_profile": {
|
|
443
|
+
"enum": [
|
|
444
|
+
"R0",
|
|
445
|
+
"R1",
|
|
446
|
+
"R2"
|
|
447
|
+
],
|
|
448
|
+
"description": "Lowest client runtime profile permitted to follow this runbook (§13.3). A client refuses a document whose minimum exceeds what it enforces."
|
|
449
|
+
},
|
|
450
|
+
"domain": {
|
|
451
|
+
"enum": [
|
|
452
|
+
"incident-response",
|
|
453
|
+
"data-pipelines",
|
|
454
|
+
"llm-ops",
|
|
455
|
+
"agent-workflows",
|
|
456
|
+
"cloud-infra",
|
|
457
|
+
"databases",
|
|
458
|
+
"ci-cd",
|
|
459
|
+
"security"
|
|
460
|
+
],
|
|
461
|
+
"description": "Closed list, changed only by RFC (§4). Navigation and URLs, not classification."
|
|
462
|
+
},
|
|
463
|
+
"targets": {
|
|
464
|
+
"type": "array",
|
|
465
|
+
"minItems": 1,
|
|
466
|
+
"uniqueItems": true,
|
|
467
|
+
"items": {
|
|
468
|
+
"type": "string",
|
|
469
|
+
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
"trigger": {
|
|
473
|
+
"enum": [
|
|
474
|
+
"alert",
|
|
475
|
+
"symptom",
|
|
476
|
+
"scheduled",
|
|
477
|
+
"manual",
|
|
478
|
+
"agent-invoked"
|
|
479
|
+
]
|
|
480
|
+
},
|
|
481
|
+
"execution": {
|
|
482
|
+
"enum": [
|
|
483
|
+
"human-only",
|
|
484
|
+
"human-with-agent",
|
|
485
|
+
"agent-autonomous"
|
|
486
|
+
]
|
|
487
|
+
},
|
|
488
|
+
"risk": {
|
|
489
|
+
"enum": [
|
|
490
|
+
"read-only",
|
|
491
|
+
"reversible-write",
|
|
492
|
+
"destructive",
|
|
493
|
+
"irreversible"
|
|
494
|
+
]
|
|
495
|
+
},
|
|
496
|
+
"blast_radius": {
|
|
497
|
+
"enum": [
|
|
498
|
+
"resource",
|
|
499
|
+
"service",
|
|
500
|
+
"cluster",
|
|
501
|
+
"tenant",
|
|
502
|
+
"global"
|
|
503
|
+
]
|
|
504
|
+
},
|
|
505
|
+
"duration": {
|
|
506
|
+
"enum": [
|
|
507
|
+
"<5m",
|
|
508
|
+
"5-30m",
|
|
509
|
+
">30m"
|
|
510
|
+
]
|
|
511
|
+
},
|
|
512
|
+
"capabilities": {
|
|
513
|
+
"type": "array",
|
|
514
|
+
"uniqueItems": true,
|
|
515
|
+
"description": "A privilege request, not a description (§2). A client MUST check it against its own allowlist before execution (§12).",
|
|
516
|
+
"items": {
|
|
517
|
+
"type": "string",
|
|
518
|
+
"pattern": "^(mcp:[a-z0-9][a-z0-9-]*|cli:[a-z0-9][a-z0-9._-]*|iam:[a-z0-9-]+:[A-Za-z0-9*]+)$"
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
"applies_to": {
|
|
522
|
+
"type": "object",
|
|
523
|
+
"additionalProperties": {
|
|
524
|
+
"type": "string"
|
|
525
|
+
},
|
|
526
|
+
"description": "Version ranges per target, e.g. {\"postgres\": \">=14 <17\"}."
|
|
527
|
+
},
|
|
528
|
+
"publisher": {
|
|
529
|
+
"type": "string",
|
|
530
|
+
"pattern": "^(std|[a-z0-9][a-z0-9-]{0,38}|github:[A-Za-z0-9][A-Za-z0-9-]{0,38}|domain:[a-z0-9.-]+\\.[a-z]{2,})$",
|
|
531
|
+
"description": "Namespace (§7). Claimed, not granted, except `std` which is ours."
|
|
532
|
+
},
|
|
533
|
+
"lang": {
|
|
534
|
+
"type": "string",
|
|
535
|
+
"pattern": "^[a-z]{2,3}(-[A-Za-z0-9]{2,8})*$",
|
|
536
|
+
"default": "en"
|
|
537
|
+
},
|
|
538
|
+
"title_en": {
|
|
539
|
+
"type": "string",
|
|
540
|
+
"maxLength": 200
|
|
541
|
+
},
|
|
542
|
+
"summary_en": {
|
|
543
|
+
"type": "string",
|
|
544
|
+
"maxLength": 1024
|
|
545
|
+
},
|
|
546
|
+
"translation_of": {
|
|
547
|
+
"type": "string",
|
|
548
|
+
"pattern": "^rb_[0-9A-HJKMNP-TV-Z]{26}$"
|
|
549
|
+
},
|
|
550
|
+
"derived_from": {
|
|
551
|
+
"type": "string",
|
|
552
|
+
"pattern": "^rb_[0-9A-HJKMNP-TV-Z]{26}$",
|
|
553
|
+
"description": "The catalog record this was adapted from, when the original is itself in the catalog. Attribution for an adaptation is its source (RUNBOOK.md 10.2), which every record carries; this is the edge that lets a reader see both and keeps deduplication from counting one procedure twice. Requiring it of an adaptation whose original is not in the catalog would be requiring a link to nothing."
|
|
554
|
+
},
|
|
555
|
+
"content_class": {
|
|
556
|
+
"enum": [
|
|
557
|
+
"ingested",
|
|
558
|
+
"adapted",
|
|
559
|
+
"submitted",
|
|
560
|
+
"curated"
|
|
561
|
+
],
|
|
562
|
+
"description": "How this record got here (RUNBOOK.md 15). `adapted` means the procedure derives from somebody else's document under a licence that allows it, whether a machine segmented it or a person rewrote it by hand. It is the one class with a rule of its own: it does not publish without a human confirming the step segmentation and the risk labelling, because an unreviewed machine adaptation is what that rule is guarding against. Its attribution is `source`, which is mandatory for every record."
|
|
563
|
+
},
|
|
564
|
+
"license": {
|
|
565
|
+
"type": "string",
|
|
566
|
+
"minLength": 2,
|
|
567
|
+
"description": "SPDX identifier. Mandatory: an undetectable licence means the record is not ingested at all (§10.2)."
|
|
568
|
+
},
|
|
569
|
+
"keywords": {
|
|
570
|
+
"type": "array",
|
|
571
|
+
"uniqueItems": true,
|
|
572
|
+
"items": {
|
|
573
|
+
"type": "string",
|
|
574
|
+
"maxLength": 40
|
|
575
|
+
},
|
|
576
|
+
"description": "The pressure valve for anything that is not a facet: uncontrolled, unguaranteed, not filterable (§4)."
|
|
577
|
+
},
|
|
578
|
+
"inputs": {
|
|
579
|
+
"type": "object",
|
|
580
|
+
"description": "Declared parameters. Interpolation may reference only these, and a missing required input aborts a run rather than interpolating empty (§5).",
|
|
581
|
+
"additionalProperties": {
|
|
582
|
+
"type": "object",
|
|
583
|
+
"additionalProperties": false,
|
|
584
|
+
"required": [
|
|
585
|
+
"type"
|
|
586
|
+
],
|
|
587
|
+
"properties": {
|
|
588
|
+
"type": {
|
|
589
|
+
"enum": [
|
|
590
|
+
"string",
|
|
591
|
+
"number",
|
|
592
|
+
"boolean"
|
|
593
|
+
]
|
|
594
|
+
},
|
|
595
|
+
"required": {
|
|
596
|
+
"type": "boolean",
|
|
597
|
+
"default": false
|
|
598
|
+
},
|
|
599
|
+
"default": {},
|
|
600
|
+
"description": {
|
|
601
|
+
"type": "string",
|
|
602
|
+
"maxLength": 200
|
|
603
|
+
},
|
|
604
|
+
"secret": {
|
|
605
|
+
"type": "boolean",
|
|
606
|
+
"default": false,
|
|
607
|
+
"description": "The value is a credential and is redacted wherever the run is shown to a human - an approval request above all (RUNBOOK.md 13, NG3). An approval channel is somewhere a secret should never arrive."
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
"steps": {
|
|
613
|
+
"type": "array",
|
|
614
|
+
"description": "Permitted but unconstrained at P0, which is prose. P1 requires it and constrains every item (spec/v1/schema/p1.json). It is declared here so that each profile is a strict superset of the one below: a P1 document must also be a valid P0 document, or every facet, hub and search filter would need a second code path."
|
|
615
|
+
},
|
|
616
|
+
"source": {
|
|
617
|
+
"type": "object",
|
|
618
|
+
"additionalProperties": false,
|
|
619
|
+
"required": [
|
|
620
|
+
"url",
|
|
621
|
+
"fetched_at",
|
|
622
|
+
"upstream_state"
|
|
623
|
+
],
|
|
624
|
+
"description": "Provenance. A record without it does not validate (§10).",
|
|
625
|
+
"properties": {
|
|
626
|
+
"url": {
|
|
627
|
+
"type": "string",
|
|
628
|
+
"format": "uri"
|
|
629
|
+
},
|
|
630
|
+
"commit": {
|
|
631
|
+
"type": "string",
|
|
632
|
+
"pattern": "^[0-9a-f]{7,40}$"
|
|
633
|
+
},
|
|
634
|
+
"fetched_at": {
|
|
635
|
+
"type": "string",
|
|
636
|
+
"format": "date"
|
|
637
|
+
},
|
|
638
|
+
"upstream_state": {
|
|
639
|
+
"enum": [
|
|
640
|
+
"current",
|
|
641
|
+
"changed",
|
|
642
|
+
"gone"
|
|
643
|
+
]
|
|
644
|
+
},
|
|
645
|
+
"upstream_hash": {
|
|
646
|
+
"type": "string",
|
|
647
|
+
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
648
|
+
"description": "The hash of the upstream document as it was when this record was made. What a reindex compares against: without it, 'has upstream changed' can only be answered by re-deriving the record, and a derivation that differs for our own reasons would read as the source having moved. Never part of content_hash — noticing that a source moved is not a new version of this record."
|
|
649
|
+
},
|
|
650
|
+
"checked_at": {
|
|
651
|
+
"type": "string",
|
|
652
|
+
"format": "date",
|
|
653
|
+
"description": "When the upstream was last looked at, which is not when the record was made. A marker with no date is a claim about the present made at an unknown time."
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
"trust": {
|
|
658
|
+
"enum": [
|
|
659
|
+
"T0",
|
|
660
|
+
"T1",
|
|
661
|
+
"T2",
|
|
662
|
+
"T3",
|
|
663
|
+
"T4"
|
|
664
|
+
],
|
|
665
|
+
"description": "Computed by the catalog at build time from evidence plus a clock, never asserted by the record. Forbidden in an authored document (p0.json); present in every served one."
|
|
666
|
+
},
|
|
667
|
+
"attestations": {
|
|
668
|
+
"type": "array",
|
|
669
|
+
"items": {
|
|
670
|
+
"type": "object",
|
|
671
|
+
"required": [
|
|
672
|
+
"issuer",
|
|
673
|
+
"issued_at",
|
|
674
|
+
"content_hash",
|
|
675
|
+
"status",
|
|
676
|
+
"self_attested",
|
|
677
|
+
"counts"
|
|
678
|
+
],
|
|
679
|
+
"additionalProperties": false,
|
|
680
|
+
"properties": {
|
|
681
|
+
"issuer": {
|
|
682
|
+
"type": "string"
|
|
683
|
+
},
|
|
684
|
+
"issued_at": {
|
|
685
|
+
"type": "string",
|
|
686
|
+
"format": "date"
|
|
687
|
+
},
|
|
688
|
+
"content_hash": {
|
|
689
|
+
"type": "string",
|
|
690
|
+
"pattern": "^sha256:[0-9a-f]{64}$"
|
|
691
|
+
},
|
|
692
|
+
"status": {
|
|
693
|
+
"enum": [
|
|
694
|
+
"valid",
|
|
695
|
+
"invalid",
|
|
696
|
+
"unverifiable"
|
|
697
|
+
]
|
|
698
|
+
},
|
|
699
|
+
"self_attested": {
|
|
700
|
+
"type": "boolean"
|
|
701
|
+
},
|
|
702
|
+
"counts": {
|
|
703
|
+
"type": "boolean"
|
|
704
|
+
},
|
|
705
|
+
"why": {
|
|
706
|
+
"type": "string"
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
},
|
|
711
|
+
"profile_verified": {
|
|
712
|
+
"not": {},
|
|
713
|
+
"description": "Forbidden. There is no self-certification of any kind. See RUNBOOK.md 19 on why there is no score."
|
|
714
|
+
},
|
|
715
|
+
"relations": {
|
|
716
|
+
"type": "array",
|
|
717
|
+
"description": "Edges to other records (§22). A relation is authored: unlike an attestation, it is the author saying how this procedure stands to another, and only they can say it. Every reference must resolve to a record this catalog holds, or the record does not publish.",
|
|
718
|
+
"items": {
|
|
719
|
+
"type": "object",
|
|
720
|
+
"additionalProperties": false,
|
|
721
|
+
"required": [
|
|
722
|
+
"kind",
|
|
723
|
+
"ref"
|
|
724
|
+
],
|
|
725
|
+
"properties": {
|
|
726
|
+
"kind": {
|
|
727
|
+
"enum": [
|
|
728
|
+
"requires",
|
|
729
|
+
"rollback_of",
|
|
730
|
+
"escalates_to",
|
|
731
|
+
"part_of"
|
|
732
|
+
],
|
|
733
|
+
"description": "requires: this cannot run until that one has. rollback_of: this undoes that one. escalates_to: when this hands off, it hands off to that one. part_of: this is a step of that larger procedure (§18.6's decomposition)."
|
|
734
|
+
},
|
|
735
|
+
"ref": {
|
|
736
|
+
"type": "string",
|
|
737
|
+
"pattern": "^[a-z0-9][a-z0-9-]{0,38}/[a-z0-9][a-z0-9-]{0,63}(@[0-9]+\\.[0-9]+\\.[0-9]+)?$",
|
|
738
|
+
"description": "publisher/slug, optionally pinned with @semver. `requires` and `rollback_of` must pin, because they assert what another record does and that is only true of a version. `part_of` and `escalates_to` must not, because they are structural and a pinned structure rots."
|
|
739
|
+
},
|
|
740
|
+
"why": {
|
|
741
|
+
"type": "string",
|
|
742
|
+
"maxLength": 280,
|
|
743
|
+
"description": "One sentence for a reader. Optional, and the only free text in a relation."
|
|
744
|
+
},
|
|
745
|
+
"step": {
|
|
746
|
+
"type": "string",
|
|
747
|
+
"pattern": "^[a-z][a-z0-9_-]{0,31}$",
|
|
748
|
+
"description": "`part_of` only: the step of the parent this record expands (§18.6). Without it a decomposition is a claim of membership with no place in the parent's procedure, and a reader cannot tell which of twelve steps this is the detail of."
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
"trust_evidence": {
|
|
754
|
+
"type": "object",
|
|
755
|
+
"required": [
|
|
756
|
+
"level",
|
|
757
|
+
"date",
|
|
758
|
+
"issuer",
|
|
759
|
+
"self_attested"
|
|
760
|
+
],
|
|
761
|
+
"additionalProperties": false,
|
|
762
|
+
"description": "Why the level reads as it does (§18.5): a badge without a date and an issuer is a claim nobody can check.",
|
|
763
|
+
"properties": {
|
|
764
|
+
"level": {
|
|
765
|
+
"enum": [
|
|
766
|
+
"T0",
|
|
767
|
+
"T1",
|
|
768
|
+
"T2",
|
|
769
|
+
"T3",
|
|
770
|
+
"T4"
|
|
771
|
+
]
|
|
772
|
+
},
|
|
773
|
+
"date": {
|
|
774
|
+
"type": "string",
|
|
775
|
+
"format": "date"
|
|
776
|
+
},
|
|
777
|
+
"issuer": {
|
|
778
|
+
"type": "string"
|
|
779
|
+
},
|
|
780
|
+
"self_attested": {
|
|
781
|
+
"type": "boolean"
|
|
782
|
+
},
|
|
783
|
+
"expires_on": {
|
|
784
|
+
"type": "string",
|
|
785
|
+
"format": "date"
|
|
786
|
+
},
|
|
787
|
+
"reasons": {
|
|
788
|
+
"type": "array",
|
|
789
|
+
"items": {
|
|
790
|
+
"type": "string"
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
"signature": {
|
|
796
|
+
"type": "object",
|
|
797
|
+
"required": [
|
|
798
|
+
"status",
|
|
799
|
+
"key_id"
|
|
800
|
+
],
|
|
801
|
+
"additionalProperties": false,
|
|
802
|
+
"description": "What checking the publisher's signature concluded. `unverifiable` states are published as themselves: a signature nobody could check must not look like one that passed, nor like a record nobody signed.",
|
|
803
|
+
"properties": {
|
|
804
|
+
"status": {
|
|
805
|
+
"enum": [
|
|
806
|
+
"valid",
|
|
807
|
+
"invalid",
|
|
808
|
+
"unknown-key",
|
|
809
|
+
"revoked"
|
|
810
|
+
]
|
|
811
|
+
},
|
|
812
|
+
"key_id": {
|
|
813
|
+
"type": "string"
|
|
814
|
+
},
|
|
815
|
+
"why": {
|
|
816
|
+
"type": "string"
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
"allOf": [
|
|
822
|
+
{
|
|
823
|
+
"description": "Non-English content must carry an English title and summary, or clustering splits one topic into language clusters (RUNBOOK.md 16). ajv strict mode requires each conditional branch to define the properties it names.",
|
|
824
|
+
"if": {
|
|
825
|
+
"properties": {
|
|
826
|
+
"lang": {
|
|
827
|
+
"type": "string",
|
|
828
|
+
"not": {
|
|
829
|
+
"const": "en"
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
"required": [
|
|
834
|
+
"lang"
|
|
835
|
+
]
|
|
836
|
+
},
|
|
837
|
+
"then": {
|
|
838
|
+
"properties": {
|
|
839
|
+
"title_en": {
|
|
840
|
+
"type": "string",
|
|
841
|
+
"maxLength": 200
|
|
842
|
+
},
|
|
843
|
+
"summary_en": {
|
|
844
|
+
"type": "string",
|
|
845
|
+
"maxLength": 1024
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
"required": [
|
|
849
|
+
"title_en",
|
|
850
|
+
"summary_en"
|
|
851
|
+
]
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
]
|
|
855
|
+
},
|
|
856
|
+
"body": {
|
|
857
|
+
"type": "string",
|
|
858
|
+
"description": "The markdown body of the SKILL.md this record came from, carried so it participates in content_hash. Changing the prose of a procedure is a change to the procedure."
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
"if": {
|
|
864
|
+
"type": "object",
|
|
865
|
+
"properties": {
|
|
866
|
+
"runbook": {
|
|
867
|
+
"type": "object",
|
|
868
|
+
"properties": {
|
|
869
|
+
"profile": {
|
|
870
|
+
"const": "P1"
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
"then": {
|
|
877
|
+
"type": "object",
|
|
878
|
+
"required": [
|
|
879
|
+
"runbook"
|
|
880
|
+
],
|
|
881
|
+
"properties": {
|
|
882
|
+
"runbook": {
|
|
883
|
+
"type": "object",
|
|
884
|
+
"required": [
|
|
885
|
+
"profile",
|
|
886
|
+
"steps"
|
|
887
|
+
],
|
|
888
|
+
"properties": {
|
|
889
|
+
"profile": {
|
|
890
|
+
"enum": [
|
|
891
|
+
"P1",
|
|
892
|
+
"P2"
|
|
893
|
+
]
|
|
894
|
+
},
|
|
895
|
+
"steps": {
|
|
896
|
+
"type": "array",
|
|
897
|
+
"minItems": 1,
|
|
898
|
+
"items": {
|
|
899
|
+
"$ref": "#/$defs/step"
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
]
|
|
908
|
+
}
|