@cloudhal/core-contracts 0.3.4 → 0.3.5

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.
@@ -1,639 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://cloudhal.dev/schemas/v1/release.schema.json",
4
- "title": "Release",
5
- "description": "A scheduled content change set (RFC-0008): node versions pinned by id, rev and content hash, applied on top of whatever is live at the release's time. A single scheduled publish is a one-item release. Each item records the version that was live when the release was pinned (`base`); if a pinned node or its live base changed before the release runs, the release is held as a conflict instead of overwriting anything. A person's approval is bound to `hash` (`rl1:`), which digests the items only, so a person may move an approved release without re-approving it while an agent's move or any content change voids the approval. Releases live in D1 next to the graph; they are not graph nodes, never enter a snapshot and are not part of site bundles.",
6
- "type": "object",
7
- "additionalProperties": false,
8
- "required": [
9
- "schemaVersion",
10
- "id",
11
- "siteId",
12
- "items",
13
- "state",
14
- "hash",
15
- "createdBy",
16
- "createdAt",
17
- "updatedAt",
18
- "rev"
19
- ],
20
- "properties": {
21
- "schemaVersion": {
22
- "$ref": "#/$defs/SchemaVersion"
23
- },
24
- "id": {
25
- "$ref": "#/$defs/ReleaseId"
26
- },
27
- "siteId": {
28
- "$ref": "#/$defs/Id"
29
- },
30
- "title": {
31
- "type": "string",
32
- "description": "Optional name shown in Studio (\"Election night\"). A one-item release without a title is shown under its entry's or page's title.",
33
- "minLength": 1,
34
- "maxLength": 200,
35
- "pattern": "^[^\\x00-\\x1F\\x7F\\u202A-\\u202E\\u2066-\\u2069]+$"
36
- },
37
- "items": {
38
- "type": "array",
39
- "description": "The pinned changes, at most one per node. A release never contains the site node: site settings, domains and structure are published the ordinary way.",
40
- "items": {
41
- "$ref": "#/$defs/ReleaseItem"
42
- },
43
- "minItems": 1,
44
- "maxItems": 100
45
- },
46
- "when": {
47
- "$ref": "#/$defs/ReleaseWhen"
48
- },
49
- "state": {
50
- "$ref": "#/$defs/ReleaseState"
51
- },
52
- "hash": {
53
- "$ref": "#/$defs/ReleaseHash"
54
- },
55
- "approval": {
56
- "$ref": "#/$defs/ReleaseApproval"
57
- },
58
- "outcome": {
59
- "$ref": "#/$defs/ReleaseOutcome"
60
- },
61
- "createdBy": {
62
- "$ref": "#/$defs/Actor"
63
- },
64
- "createdAt": {
65
- "$ref": "#/$defs/Timestamp"
66
- },
67
- "updatedAt": {
68
- "$ref": "#/$defs/Timestamp"
69
- },
70
- "rev": {
71
- "$ref": "#/$defs/Revision"
72
- },
73
- "metadata": {
74
- "$ref": "#/$defs/Metadata"
75
- }
76
- },
77
- "$defs": {
78
- "Actor": {
79
- "type": "object",
80
- "additionalProperties": false,
81
- "required": [
82
- "kind",
83
- "id"
84
- ],
85
- "properties": {
86
- "kind": {
87
- "$ref": "#/$defs/ActorKind"
88
- },
89
- "id": {
90
- "$ref": "#/$defs/Id"
91
- },
92
- "name": {
93
- "type": "string",
94
- "maxLength": 200
95
- }
96
- }
97
- },
98
- "ActorKind": {
99
- "type": "string",
100
- "description": "Who performed an action. Humans and agents share one actor model so the policy engine treats them uniformly.",
101
- "enum": [
102
- "human",
103
- "agent",
104
- "system"
105
- ]
106
- },
107
- "Id": {
108
- "type": "string",
109
- "description": "Prefixed, URL-safe identifier, e.g. site_01HZX8K3Q9. The prefix names the entity kind; agents and the MCP server pass these as opaque handles.",
110
- "pattern": "^[a-z]+_[A-Za-z0-9]{6,32}$",
111
- "minLength": 8,
112
- "maxLength": 48
113
- },
114
- "LocalDateTime": {
115
- "type": "string",
116
- "description": "A wall-clock date and time without an offset, to the minute (YYYY-MM-DDTHH:mm), as a person entered it; always paired with a TimeZone (RFC-0008).",
117
- "pattern": "^[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])T(?:[01][0-9]|2[0-3]):[0-5][0-9]$"
118
- },
119
- "Metadata": {
120
- "type": "object",
121
- "description": "Free-form string-to-string map for extensions. Keys should be namespaced (e.g. cloudhal.source).",
122
- "additionalProperties": {
123
- "type": "string"
124
- }
125
- },
126
- "ReleaseApproval": {
127
- "description": "Why the release may run, discriminated by `kind`.",
128
- "oneOf": [
129
- {
130
- "type": "object",
131
- "title": "ReleasePersonApproval",
132
- "description": "A person approved these items (bound to `hash`) for `executeAt`, either by deciding an agent's request (`approvalId`) or by scheduling it themselves. The person's rights are checked again when the release runs.",
133
- "additionalProperties": false,
134
- "required": [
135
- "kind",
136
- "by",
137
- "at"
138
- ],
139
- "properties": {
140
- "kind": {
141
- "type": "string",
142
- "enum": [
143
- "person"
144
- ]
145
- },
146
- "by": {
147
- "$ref": "#/$defs/ReleasePerson"
148
- },
149
- "at": {
150
- "$ref": "#/$defs/Timestamp"
151
- },
152
- "executeAt": {
153
- "$ref": "#/$defs/Timestamp"
154
- },
155
- "approvalId": {
156
- "$ref": "#/$defs/Id"
157
- }
158
- }
159
- },
160
- {
161
- "type": "object",
162
- "title": "ReleaseAutoApproval",
163
- "description": "An agent key in `auto` mode scheduled a low-risk release without a person (ADR-0015). Valid only while the release runs before `windowEnds`: the end of the key's elevation, or 24 hours after scheduling, whichever comes first.",
164
- "additionalProperties": false,
165
- "required": [
166
- "kind",
167
- "by",
168
- "keyId",
169
- "at",
170
- "windowEnds"
171
- ],
172
- "properties": {
173
- "kind": {
174
- "type": "string",
175
- "enum": [
176
- "auto"
177
- ]
178
- },
179
- "by": {
180
- "$ref": "#/$defs/Actor"
181
- },
182
- "keyId": {
183
- "type": "string",
184
- "pattern": "^[A-Za-z0-9_-]{6,64}$"
185
- },
186
- "at": {
187
- "$ref": "#/$defs/Timestamp"
188
- },
189
- "windowEnds": {
190
- "$ref": "#/$defs/Timestamp"
191
- }
192
- }
193
- }
194
- ]
195
- },
196
- "ReleaseConflict": {
197
- "type": "object",
198
- "description": "A node whose live version was not the pinned base when the release ran. `expected` absent: the node was not expected to be live; `found` absent: it was not live.",
199
- "additionalProperties": false,
200
- "required": [
201
- "nodeId"
202
- ],
203
- "properties": {
204
- "nodeId": {
205
- "$ref": "#/$defs/Id"
206
- },
207
- "expected": {
208
- "$ref": "#/$defs/ReleaseNodeVersion"
209
- },
210
- "found": {
211
- "$ref": "#/$defs/ReleaseNodeVersion"
212
- }
213
- }
214
- },
215
- "ReleaseHash": {
216
- "type": "string",
217
- "description": "`rl1:` and the lower-case hex SHA-256 of the release's canonical item lines (site id, then one line per item sorted by node id: kind, node id, target and base versions). The prefix keeps it apart from the graph state hash and from comment bindings (`cm1:`); approvals store it as their state.",
218
- "pattern": "^rl1:[0-9a-f]{64}$"
219
- },
220
- "ReleaseId": {
221
- "type": "string",
222
- "description": "A release id: `rel_` and 6 to 32 letters or digits.",
223
- "pattern": "^rel_[A-Za-z0-9]{6,32}$"
224
- },
225
- "ReleaseItem": {
226
- "description": "One pinned change, discriminated by `kind`.",
227
- "oneOf": [
228
- {
229
- "type": "object",
230
- "title": "ReleaseSetItem",
231
- "description": "Make `target` the live version of the node. `base` is the version that was live when the item was pinned; absent when the node was not live (a new entry or page).",
232
- "additionalProperties": false,
233
- "required": [
234
- "kind",
235
- "nodeId",
236
- "nodeKind",
237
- "target"
238
- ],
239
- "properties": {
240
- "kind": {
241
- "type": "string",
242
- "enum": [
243
- "set"
244
- ]
245
- },
246
- "nodeId": {
247
- "$ref": "#/$defs/Id"
248
- },
249
- "nodeKind": {
250
- "$ref": "#/$defs/ReleaseNodeKind"
251
- },
252
- "target": {
253
- "$ref": "#/$defs/ReleaseNodeVersion"
254
- },
255
- "base": {
256
- "$ref": "#/$defs/ReleaseNodeVersion"
257
- }
258
- }
259
- },
260
- {
261
- "type": "object",
262
- "title": "ReleaseRemoveItem",
263
- "description": "Take the node out of the live snapshot. `base` is the live version that is removed.",
264
- "additionalProperties": false,
265
- "required": [
266
- "kind",
267
- "nodeId",
268
- "nodeKind",
269
- "base"
270
- ],
271
- "properties": {
272
- "kind": {
273
- "type": "string",
274
- "enum": [
275
- "remove"
276
- ]
277
- },
278
- "nodeId": {
279
- "$ref": "#/$defs/Id"
280
- },
281
- "nodeKind": {
282
- "$ref": "#/$defs/ReleaseNodeKind"
283
- },
284
- "base": {
285
- "$ref": "#/$defs/ReleaseNodeVersion"
286
- }
287
- }
288
- }
289
- ]
290
- },
291
- "ReleaseNodeKind": {
292
- "type": "string",
293
- "description": "Kinds of graph node a release may carry. The site node is never one of them.",
294
- "enum": [
295
- "page",
296
- "contentEntry",
297
- "taxonomy",
298
- "term",
299
- "redirect",
300
- "menu",
301
- "asset"
302
- ]
303
- },
304
- "ReleaseNodeVersion": {
305
- "type": "object",
306
- "description": "One version of a node: its revision and the SHA-256 of its canonical JSON, whose body the blob store keeps under `nodes/<hash>.json`.",
307
- "additionalProperties": false,
308
- "required": [
309
- "rev",
310
- "hash"
311
- ],
312
- "properties": {
313
- "rev": {
314
- "$ref": "#/$defs/Revision"
315
- },
316
- "hash": {
317
- "type": "string",
318
- "pattern": "^[0-9a-f]{64}$"
319
- }
320
- }
321
- },
322
- "ReleaseOutcome": {
323
- "description": "What happened when the release was due, discriminated by `kind` (it matches the terminal `state`).",
324
- "oneOf": [
325
- {
326
- "type": "object",
327
- "title": "ReleasePublishedOutcome",
328
- "description": "Applied: `snapshotId` is the new live snapshot (reason `scheduled`), composed from `baseSnapshotId` and the items. `purge` says how the edge cache followed.",
329
- "additionalProperties": false,
330
- "required": [
331
- "kind",
332
- "runId",
333
- "at",
334
- "snapshotId",
335
- "baseSnapshotId",
336
- "latenessSeconds",
337
- "purge"
338
- ],
339
- "properties": {
340
- "kind": {
341
- "type": "string",
342
- "enum": [
343
- "published"
344
- ]
345
- },
346
- "runId": {
347
- "$ref": "#/$defs/Id"
348
- },
349
- "at": {
350
- "$ref": "#/$defs/Timestamp"
351
- },
352
- "snapshotId": {
353
- "$ref": "#/$defs/Id"
354
- },
355
- "baseSnapshotId": {
356
- "$ref": "#/$defs/Id"
357
- },
358
- "latenessSeconds": {
359
- "type": "integer",
360
- "minimum": 0
361
- },
362
- "purge": {
363
- "type": "string",
364
- "enum": [
365
- "tags",
366
- "everything",
367
- "deferred",
368
- "failed",
369
- "none"
370
- ]
371
- }
372
- }
373
- },
374
- {
375
- "type": "object",
376
- "title": "ReleaseConflictOutcome",
377
- "description": "Held: these nodes changed since the release was pinned. Nothing was published.",
378
- "additionalProperties": false,
379
- "required": [
380
- "kind",
381
- "at",
382
- "conflicts"
383
- ],
384
- "properties": {
385
- "kind": {
386
- "type": "string",
387
- "enum": [
388
- "conflict"
389
- ]
390
- },
391
- "runId": {
392
- "$ref": "#/$defs/Id"
393
- },
394
- "at": {
395
- "$ref": "#/$defs/Timestamp"
396
- },
397
- "conflicts": {
398
- "type": "array",
399
- "items": {
400
- "$ref": "#/$defs/ReleaseConflict"
401
- },
402
- "minItems": 1,
403
- "maxItems": 100
404
- }
405
- }
406
- },
407
- {
408
- "type": "object",
409
- "title": "ReleaseFailedOutcome",
410
- "description": "Refused at run time; nothing was published.",
411
- "additionalProperties": false,
412
- "required": [
413
- "kind",
414
- "at",
415
- "code"
416
- ],
417
- "properties": {
418
- "kind": {
419
- "type": "string",
420
- "enum": [
421
- "failed"
422
- ]
423
- },
424
- "runId": {
425
- "$ref": "#/$defs/Id"
426
- },
427
- "at": {
428
- "$ref": "#/$defs/Timestamp"
429
- },
430
- "code": {
431
- "type": "string",
432
- "enum": [
433
- "dependency_missing",
434
- "invalid_snapshot",
435
- "site_missing",
436
- "node_body_missing",
437
- "store_error"
438
- ]
439
- },
440
- "message": {
441
- "type": "string",
442
- "maxLength": 500,
443
- "pattern": "^[^\\x00-\\x1F\\x7F\\u202A-\\u202E\\u2066-\\u2069]*$"
444
- }
445
- }
446
- },
447
- {
448
- "type": "object",
449
- "title": "ReleaseMissedOutcome",
450
- "description": "The scheduler reached the release more than an hour after its time; nothing was published. A person may publish it now.",
451
- "additionalProperties": false,
452
- "required": [
453
- "kind",
454
- "at",
455
- "latenessSeconds"
456
- ],
457
- "properties": {
458
- "kind": {
459
- "type": "string",
460
- "enum": [
461
- "missed"
462
- ]
463
- },
464
- "at": {
465
- "$ref": "#/$defs/Timestamp"
466
- },
467
- "latenessSeconds": {
468
- "type": "integer",
469
- "minimum": 0
470
- }
471
- }
472
- },
473
- {
474
- "type": "object",
475
- "title": "ReleaseExpiredOutcome",
476
- "description": "No valid approval when the release was due: never approved, the approver can no longer publish it, the agent's `auto` window ended, or its creator left.",
477
- "additionalProperties": false,
478
- "required": [
479
- "kind",
480
- "at",
481
- "reason"
482
- ],
483
- "properties": {
484
- "kind": {
485
- "type": "string",
486
- "enum": [
487
- "expired"
488
- ]
489
- },
490
- "at": {
491
- "$ref": "#/$defs/Timestamp"
492
- },
493
- "reason": {
494
- "type": "string",
495
- "enum": [
496
- "unapproved",
497
- "approval_lost",
498
- "auto_window_ended",
499
- "creator_removed"
500
- ]
501
- }
502
- }
503
- },
504
- {
505
- "type": "object",
506
- "title": "ReleaseCanceledOutcome",
507
- "description": "Withdrawn by a person or an agent; its nodes are unlocked.",
508
- "additionalProperties": false,
509
- "required": [
510
- "kind",
511
- "at",
512
- "by"
513
- ],
514
- "properties": {
515
- "kind": {
516
- "type": "string",
517
- "enum": [
518
- "canceled"
519
- ]
520
- },
521
- "at": {
522
- "$ref": "#/$defs/Timestamp"
523
- },
524
- "by": {
525
- "$ref": "#/$defs/Actor"
526
- }
527
- }
528
- }
529
- ]
530
- },
531
- "ReleasePerson": {
532
- "type": "object",
533
- "description": "A staff person (`user_…`). Agents never appear here.",
534
- "additionalProperties": false,
535
- "required": [
536
- "id"
537
- ],
538
- "properties": {
539
- "id": {
540
- "type": "string",
541
- "pattern": "^user_[A-Za-z0-9]{6,32}$"
542
- },
543
- "name": {
544
- "type": "string",
545
- "maxLength": 200
546
- }
547
- }
548
- },
549
- "ReleaseState": {
550
- "type": "string",
551
- "description": "Lifecycle. `draft`: proposed or being assembled, nothing locked. `awaiting_approval`: waiting for a person; its nodes are locked. `scheduled`: approved (or allowed by `auto` mode) and locked until it runs. `running`: claimed by the scheduler. `published`: applied; `outcome` names the snapshot. `conflict`: a pinned node or its live base changed; held, nothing published. `missed`: the scheduler reached it more than an hour late; nothing published. `expired`: no valid approval at its time. `failed`: refused at run time (e.g. a dependency is missing). `canceled`: withdrawn.",
552
- "enum": [
553
- "draft",
554
- "awaiting_approval",
555
- "scheduled",
556
- "running",
557
- "published",
558
- "conflict",
559
- "missed",
560
- "expired",
561
- "failed",
562
- "canceled"
563
- ]
564
- },
565
- "ReleaseWhen": {
566
- "description": "When the release runs, discriminated by `kind`. Absent while a draft has no time yet.",
567
- "oneOf": [
568
- {
569
- "type": "object",
570
- "title": "ReleaseAt",
571
- "description": "At an instant. `at` is what the scheduler compares with the clock; when a person entered a wall-clock time, `localTime` keeps it and `at` is recomputed from it and `timeZone` before the release runs, never earlier than the wall-clock time.",
572
- "additionalProperties": false,
573
- "required": [
574
- "kind",
575
- "at",
576
- "timeZone"
577
- ],
578
- "properties": {
579
- "kind": {
580
- "type": "string",
581
- "enum": [
582
- "at"
583
- ]
584
- },
585
- "at": {
586
- "$ref": "#/$defs/Timestamp"
587
- },
588
- "timeZone": {
589
- "$ref": "#/$defs/TimeZone"
590
- },
591
- "localTime": {
592
- "$ref": "#/$defs/LocalDateTime"
593
- }
594
- }
595
- },
596
- {
597
- "type": "object",
598
- "title": "ReleaseOnApproval",
599
- "description": "As soon as it is approved (a gate-only change such as setting an expiry on live content, or \"publish when approved\").",
600
- "additionalProperties": false,
601
- "required": [
602
- "kind"
603
- ],
604
- "properties": {
605
- "kind": {
606
- "type": "string",
607
- "enum": [
608
- "on-approval"
609
- ]
610
- }
611
- }
612
- }
613
- ]
614
- },
615
- "Revision": {
616
- "type": "integer",
617
- "description": "Monotonic per-node revision used for optimistic locking (compare-and-swap on write).",
618
- "minimum": 0
619
- },
620
- "SchemaVersion": {
621
- "type": "integer",
622
- "description": "Version of this contract. Readers reject unknown versions; migrations move documents forward one version at a time.",
623
- "enum": [
624
- 1
625
- ]
626
- },
627
- "Timestamp": {
628
- "type": "string",
629
- "description": "RFC 3339 / ISO 8601 instant in UTC.",
630
- "format": "date-time"
631
- },
632
- "TimeZone": {
633
- "type": "string",
634
- "description": "IANA time zone name from the tz database (RFC-0008), e.g. Europe/Istanbul, America/Argentina/Buenos_Aires, Etc/GMT+3 or UTC. Wall-clock times a person enters are kept with their zone, so the instant is recomputed when governments change the rules. The pattern bounds the shape only; writers also check the name against the runtime's Intl zone list.",
635
- "pattern": "^(?:UTC|[A-Z][A-Za-z_-]+(?:/[A-Za-z0-9_+-]+){1,2})$",
636
- "maxLength": 64
637
- }
638
- }
639
- }