edoxen 0.7.2 → 2.1.1

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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +17 -1
  3. data/.rubocop_todo.yml +1 -1
  4. data/CHANGELOG.md +246 -0
  5. data/README.adoc +308 -153
  6. data/edoxen.gemspec +10 -10
  7. data/lib/edoxen/action.rb +1 -1
  8. data/lib/edoxen/agenda.rb +4 -7
  9. data/lib/edoxen/agenda_item.rb +10 -3
  10. data/lib/edoxen/approval.rb +1 -1
  11. data/lib/edoxen/attendance.rb +16 -10
  12. data/lib/edoxen/body_vocabulary_entry.rb +24 -0
  13. data/lib/edoxen/body_vocabulary_host.rb +29 -0
  14. data/lib/edoxen/cli.rb +117 -120
  15. data/lib/edoxen/component_localization.rb +11 -0
  16. data/lib/edoxen/consideration.rb +2 -2
  17. data/lib/edoxen/contact.rb +24 -0
  18. data/lib/edoxen/contact_identifier.rb +13 -0
  19. data/lib/edoxen/contact_method.rb +16 -0
  20. data/lib/edoxen/decision.rb +69 -0
  21. data/lib/edoxen/decision_collection.rb +10 -0
  22. data/lib/edoxen/decision_date.rb +9 -0
  23. data/lib/edoxen/{resolution_metadata.rb → decision_metadata.rb} +5 -6
  24. data/lib/edoxen/decision_relation.rb +11 -0
  25. data/lib/edoxen/entity_ref.rb +74 -0
  26. data/lib/edoxen/enums.rb +117 -9
  27. data/lib/edoxen/error.rb +1 -1
  28. data/lib/edoxen/extension_attribute.rb +78 -0
  29. data/lib/edoxen/host_ref.rb +1 -1
  30. data/lib/edoxen/link_checker.rb +8 -8
  31. data/lib/edoxen/localization.rb +2 -2
  32. data/lib/edoxen/localization_host.rb +23 -0
  33. data/lib/edoxen/meeting.rb +63 -39
  34. data/lib/edoxen/meeting_collection.rb +1 -2
  35. data/lib/edoxen/meeting_collection_metadata.rb +3 -1
  36. data/lib/edoxen/meeting_component.rb +41 -0
  37. data/lib/edoxen/meeting_extension.rb +25 -0
  38. data/lib/edoxen/meeting_identifier.rb +1 -1
  39. data/lib/edoxen/meeting_localization.rb +6 -1
  40. data/lib/edoxen/meeting_series.rb +21 -0
  41. data/lib/edoxen/minutes.rb +2 -2
  42. data/lib/edoxen/minutes_section.rb +8 -11
  43. data/lib/edoxen/motion.rb +51 -0
  44. data/lib/edoxen/name.rb +29 -0
  45. data/lib/edoxen/officer.rb +19 -0
  46. data/lib/edoxen/officers_host.rb +19 -0
  47. data/lib/edoxen/person.rb +5 -9
  48. data/lib/edoxen/physical_venue.rb +15 -0
  49. data/lib/edoxen/recurrence.rb +27 -0
  50. data/lib/edoxen/recurrence_by_day.rb +10 -0
  51. data/lib/edoxen/reference_data.rb +35 -41
  52. data/lib/edoxen/schema_validator.rb +2 -3
  53. data/lib/edoxen/source_url.rb +3 -3
  54. data/lib/edoxen/structured_identifier.rb +2 -2
  55. data/lib/edoxen/topic.rb +37 -0
  56. data/lib/edoxen/topic_asset.rb +14 -0
  57. data/lib/edoxen/topic_document.rb +16 -0
  58. data/lib/edoxen/venue.rb +80 -0
  59. data/lib/edoxen/venue_validator.rb +56 -0
  60. data/lib/edoxen/version.rb +1 -1
  61. data/lib/edoxen/virtual_venue.rb +15 -0
  62. data/lib/edoxen/vote_record.rb +17 -13
  63. data/lib/edoxen/voting.rb +45 -0
  64. data/lib/edoxen/voting_counts.rb +23 -0
  65. data/lib/edoxen.rb +40 -13
  66. data/schema/edoxen.yaml +792 -145
  67. data/schema/meeting.yaml +600 -142
  68. data/sig/edoxen.rbs +581 -1
  69. metadata +57 -20
  70. data/lib/edoxen/_metadata.rb.deprecated +0 -57
  71. data/lib/edoxen/location.rb +0 -15
  72. data/lib/edoxen/resolution.rb +0 -43
  73. data/lib/edoxen/resolution_collection.rb +0 -10
  74. data/lib/edoxen/resolution_date.rb +0 -11
  75. data/lib/edoxen/resolution_relation.rb +0 -11
  76. data/lib/edoxen/resolution_set.rb +0 -17
  77. data/lib/edoxen/schedule_item.rb +0 -27
  78. data/lib/edoxen/schedule_item_localization.rb +0 -21
data/schema/meeting.yaml CHANGED
@@ -23,22 +23,36 @@ $defs:
23
23
 
24
24
  # ====================================================================
25
25
  # Reused from schema/edoxen.yaml (re-declared here so this file is
26
- # self-contained — schema/meeting.yaml does not depend on
27
- # schema/edoxen.yaml).
26
+ # self-contained).
28
27
  # ====================================================================
29
28
 
30
29
  StructuredIdentifier:
31
30
  type: object
32
- description: "prefix + number identifier. A Meeting carries 1..*."
31
+ description: "An identifier (prefix + number). A Decision carries 1..* of these."
33
32
  additionalProperties: false
34
33
  required: [prefix, number]
35
34
  properties:
36
35
  prefix: { type: string }
37
36
  number: { type: string }
38
37
 
38
+ EntityRef:
39
+ type: object
40
+ description: |
41
+ Typed cross-reference between entities (v2.1, TODO.refactor/44).
42
+ Exactly one of `urn`, `identifier`, or `local_ref` should be set;
43
+ the gem's `EntityRef#valid?` enforces this in Ruby.
44
+ additionalProperties: false
45
+ properties:
46
+ urn: { type: string }
47
+ identifier: { $ref: "#/$defs/StructuredIdentifier" }
48
+ local_ref: { type: string }
49
+ kind: { type: string }
50
+ role: { type: string }
51
+ note: { type: string }
52
+
39
53
  SourceUrl:
40
54
  type: object
41
- description: "Per-language canonical source URL (PDF, page, register link)."
55
+ description: "Per-language canonical source URL (e.g. one PDF per language)."
42
56
  additionalProperties: false
43
57
  required: [ref]
44
58
  properties:
@@ -50,10 +64,6 @@ $defs:
50
64
  kind:
51
65
  $ref: "#/$defs/SourceUrlKind"
52
66
 
53
- # ====================================================================
54
- # Meeting-side primitive types.
55
- # ====================================================================
56
-
57
67
  DateRange:
58
68
  type: object
59
69
  description: "Start + end date pair for multi-day meetings."
@@ -66,75 +76,137 @@ $defs:
66
76
  type: string
67
77
  format: date
68
78
 
69
- Location:
79
+ ContactMethodKind:
80
+ type: string
81
+ description: "Polymorphic communication channel kind."
82
+ enum: [phone, mobile, fax, email, url, mail, pager, message, other]
83
+
84
+ ContactMethod:
70
85
  type: object
71
- description: "Venue geography. Multi-venue meetings carry one Location per venue."
86
+ description: |
87
+ One polymorphic communication channel — phone, email, fax, url,
88
+ mail, etc. `kind` discriminates the channel; `value` carries the
89
+ address/number; `label` is a free-form display hint ("Office",
90
+ "Front desk"). OCP: new channel kinds are added via the
91
+ ContactMethodKind enum (or `other` + extensions).
72
92
  additionalProperties: false
73
93
  properties:
74
- name: { type: string }
75
- address: { type: string }
76
- link: { type: string }
77
- phone: { type: string }
78
- note: { type: string }
79
- lat: { type: number }
80
- lon: { type: number }
94
+ kind: { $ref: "#/$defs/ContactMethodKind" }
95
+ value: { type: string }
96
+ label: { type: string }
97
+ primary: { type: boolean }
98
+ extensions:
99
+ type: array
100
+ items:
101
+ $ref: "#/$defs/MeetingExtension"
81
102
 
82
- Person:
103
+ ContactIdentifierKind:
104
+ type: string
105
+ description: "Polymorphic external identifier scheme for a Contact."
106
+ enum: [orcid, isni, wikidata, ror, ringgold, github, other]
107
+
108
+ ContactIdentifier:
83
109
  type: object
84
- description: "Identity + role + affiliation + contact."
110
+ description: |
111
+ One polymorphic external identifier — ORCID, ISNI, Wikidata QID,
112
+ ROR, Ringgold, GitHub handle, etc. Replaces the hard-coded `orcid`
113
+ field. OCP: new identifier schemes are added via the
114
+ ContactIdentifierKind enum (or `other` + extensions).
85
115
  additionalProperties: false
86
116
  properties:
87
- name: { type: string }
88
- role: { type: string }
89
- affiliation: { type: string }
90
- email: { type: string }
91
- phone: { type: string }
92
- orcid: { type: string }
117
+ kind: { $ref: "#/$defs/ContactIdentifierKind" }
118
+ value: { type: string }
119
+ extensions:
120
+ type: array
121
+ items:
122
+ $ref: "#/$defs/MeetingExtension"
93
123
 
94
- HostRef:
124
+ Name:
95
125
  type: object
96
- description: "Typed reference to an organization that hosts or co-organizes a meeting."
126
+ description: |
127
+ Structured personal/organisational name (VCARD RFC 6350
128
+ conventions). `formatted` is the pre-built display string (FN);
129
+ the structured components (N) are stored separately for sorting,
130
+ indexing, or locale-aware rendering.
97
131
  additionalProperties: false
98
- required: [ref]
99
132
  properties:
100
- ref: { type: string }
101
- type:
102
- $ref: "#/$defs/HostType"
103
- role: { type: string }
104
- contact:
105
- $ref: "#/$defs/Person"
133
+ formatted: { type: string }
134
+ family: { type: string }
135
+ given: { type: string }
136
+ additional: { type: string }
137
+ prefix: { type: string }
138
+ suffix: { type: string }
139
+ extensions:
140
+ type: array
141
+ items:
142
+ $ref: "#/$defs/MeetingExtension"
106
143
 
107
- ScheduleItemLocalization:
144
+ Contact:
108
145
  type: object
109
- description: "Per-language content for a ScheduleItem."
146
+ description: |
147
+ VCARD-like abstract contact. Generalises Person for cases where
148
+ the contact may be a person, an organisation, a department, a
149
+ role ("Secretariat"), or any other entity with a name and one or
150
+ more communication channels.
110
151
  additionalProperties: false
111
- required: [language_code]
112
152
  properties:
113
- language_code:
114
- type: string
115
- pattern: "^[a-z]{3}$"
116
- script:
117
- type: string
118
- pattern: "^[A-Z][a-z]{3}$"
119
- event: { type: string }
120
- description: { type: string }
153
+ name: { $ref: "#/$defs/Name" }
154
+ kind: { type: string }
155
+ role: { type: string }
156
+ title: { type: string }
157
+ affiliation: { type: string }
158
+ contact_methods:
159
+ type: array
160
+ items:
161
+ $ref: "#/$defs/ContactMethod"
162
+ identifiers:
163
+ type: array
164
+ items:
165
+ $ref: "#/$defs/ContactIdentifier"
166
+ address: { type: string }
167
+ extensions:
168
+ type: array
169
+ items:
170
+ $ref: "#/$defs/MeetingExtension"
121
171
 
122
- ScheduleItem:
172
+ Person:
123
173
  type: object
124
- description: "One entry in the meeting timetable."
174
+ description: |
175
+ A Contact that is specifically an individual human. Inherits all
176
+ Contact fields. The old `email`, `phone`, and `orcid` fields are
177
+ replaced by entries in `contact_methods` (kind=email / kind=phone)
178
+ and `identifiers` (kind=orcid).
125
179
  additionalProperties: false
126
180
  properties:
127
- date:
128
- type: string
129
- format: date
130
- time: { type: string }
131
- event: { type: string }
132
- description: { type: string }
133
- room: { type: string }
134
- localizations:
181
+ name: { $ref: "#/$defs/Name" }
182
+ kind: { type: string }
183
+ role: { type: string }
184
+ title: { type: string }
185
+ affiliation: { type: string }
186
+ contact_methods:
135
187
  type: array
136
188
  items:
137
- $ref: "#/$defs/ScheduleItemLocalization"
189
+ $ref: "#/$defs/ContactMethod"
190
+ identifiers:
191
+ type: array
192
+ items:
193
+ $ref: "#/$defs/ContactIdentifier"
194
+ address: { type: string }
195
+ extensions:
196
+ type: array
197
+ items:
198
+ $ref: "#/$defs/MeetingExtension"
199
+
200
+ HostRef:
201
+ type: object
202
+ description: "Typed reference to a hosting organization."
203
+ additionalProperties: false
204
+ required: [ref]
205
+ properties:
206
+ ref: { type: string }
207
+ type: { $ref: "#/$defs/HostType" }
208
+ role: { type: string }
209
+ contact: { $ref: "#/$defs/Contact" }
138
210
 
139
211
  Attendance:
140
212
  type: object
@@ -142,36 +214,39 @@ $defs:
142
214
  additionalProperties: false
143
215
  required: [person, status]
144
216
  properties:
145
- person:
146
- $ref: "#/$defs/Person"
147
- status:
148
- $ref: "#/$defs/ParticipationStatus"
217
+ person: { $ref: "#/$defs/Person" }
218
+ status: { $ref: "#/$defs/ParticipationStatus" }
219
+ role: { $ref: "#/$defs/AttendanceRole" }
220
+ response: { $ref: "#/$defs/AttendanceResponse" }
149
221
  affiliation: { type: string }
150
- proxy_for:
151
- $ref: "#/$defs/Person"
152
- notes: { type: string }
222
+ proxy_for: { $ref: "#/$defs/Person" }
223
+ notes: { type: string }
224
+ extensions:
225
+ type: array
226
+ items:
227
+ $ref: "#/$defs/MeetingExtension"
153
228
 
154
229
  VoteRecord:
155
230
  type: object
156
- description: "One vote by one person on one resolution at one meeting."
231
+ description: "One vote by one person on one decision/voting."
157
232
  additionalProperties: false
158
- required: [resolution_ref, person, vote]
233
+ required: [person, vote]
159
234
  properties:
160
- resolution_ref: { type: string }
161
- person:
162
- $ref: "#/$defs/Person"
163
- affiliation: { type: string }
164
- vote:
165
- $ref: "#/$defs/VoteType"
166
- notes: { type: string }
235
+ decision_ref: { type: string }
236
+ voting_ref: { type: string }
237
+ person: { $ref: "#/$defs/Person" }
238
+ affiliation: { type: string }
239
+ vote: { $ref: "#/$defs/VoteType" }
240
+ role: { type: string }
241
+ notes: { type: string }
242
+ extensions:
243
+ type: array
244
+ items:
245
+ $ref: "#/$defs/MeetingExtension"
167
246
 
168
247
  MinutesSection:
169
248
  type: object
170
- description: |
171
- One section of a Meeting's minutes — typically tied to an agenda
172
- item by its `number` field. Carries the narrative as Markdown
173
- (the format the GLM-OCR pipeline emits) plus an optional page
174
- range for provenance back to the source PDF.
249
+ description: "One section of a Meeting's minutes — typically tied to an agenda item by `number`."
175
250
  additionalProperties: false
176
251
  properties:
177
252
  number: { type: string }
@@ -188,12 +263,8 @@ $defs:
188
263
  type: object
189
264
  description: |
190
265
  The narrative record of a Meeting — what was said, by whom, in
191
- what order, with what outcome. Distinct from Agenda (the business
192
- order drafted before the meeting) and from ResolutionCollection
193
- (the formal decisions adopted). The Minutes are the running
194
- record. One Minutes document per language; sections are typically
195
- keyed by agenda-item number so consumers can join
196
- Minutes ↔ AgendaItem ↔ Resolution.
266
+ what order, with what outcome. Distinct from Agenda and from
267
+ DecisionCollection.
197
268
  additionalProperties: false
198
269
  properties:
199
270
  identifier:
@@ -227,7 +298,7 @@ $defs:
227
298
 
228
299
  Reference:
229
300
  type: object
230
- description: "Generic document reference (used by AgendaItem.references)."
301
+ description: "Generic document reference."
231
302
  additionalProperties: false
232
303
  properties:
233
304
  ref: { type: string }
@@ -244,19 +315,26 @@ $defs:
244
315
  additionalProperties: false
245
316
  properties:
246
317
  label: { type: string }
247
- kind:
248
- $ref: "#/$defs/AgendaItemKind"
318
+ kind: { $ref: "#/$defs/AgendaItemKind" }
249
319
  title: { type: string }
250
320
  description: { type: string }
251
321
  references:
252
322
  type: array
253
323
  items:
254
324
  $ref: "#/$defs/Reference"
255
- outcome:
256
- $ref: "#/$defs/AgendaItemOutcome"
257
- resolution_ref:
258
- type: string
259
- description: "URN of the resolution this item produced."
325
+ outcome: { $ref: "#/$defs/AgendaItemOutcome" }
326
+ decision_ref: { type: string }
327
+ topics:
328
+ type: array
329
+ items:
330
+ $ref: "#/$defs/Topic"
331
+ components:
332
+ type: array
333
+ items: { type: string }
334
+ extensions:
335
+ type: array
336
+ items:
337
+ $ref: "#/$defs/MeetingExtension"
260
338
 
261
339
  Agenda:
262
340
  type: object
@@ -269,17 +347,322 @@ $defs:
269
347
  type: array
270
348
  items:
271
349
  $ref: "#/$defs/StructuredIdentifier"
272
- status:
273
- $ref: "#/$defs/AgendaStatus"
274
- source_doc: { type: string }
350
+ status: { $ref: "#/$defs/AgendaStatus" }
351
+ source_doc: { type: string }
275
352
  items:
276
353
  type: array
277
354
  items:
278
355
  $ref: "#/$defs/AgendaItem"
279
- opening_session:
280
- $ref: "#/$defs/ScheduleItem"
281
- closing_session:
282
- $ref: "#/$defs/ScheduleItem"
356
+ extensions:
357
+ type: array
358
+ items:
359
+ $ref: "#/$defs/MeetingExtension"
360
+
361
+ # ====================================================================
362
+ # Topic, Venue, Recurrence, MeetingExtension (shared with edoxen.yaml).
363
+ # ====================================================================
364
+
365
+ TopicDocument:
366
+ type: object
367
+ description: "Text-bearing document about a Topic."
368
+ additionalProperties: false
369
+ properties:
370
+ identifier: { type: string }
371
+ title: { type: string }
372
+ version: { type: string }
373
+ status: { type: string }
374
+ url: { type: string }
375
+ format: { type: string }
376
+ language_code:
377
+ type: string
378
+ pattern: "^[a-z]{3}$"
379
+ extensions:
380
+ type: array
381
+ items:
382
+ $ref: "#/$defs/MeetingExtension"
383
+
384
+ TopicAsset:
385
+ type: object
386
+ description: "Non-text resource about a Topic."
387
+ additionalProperties: false
388
+ properties:
389
+ identifier: { type: string }
390
+ title: { type: string }
391
+ kind: { type: string }
392
+ url: { type: string }
393
+ format: { type: string }
394
+ extensions:
395
+ type: array
396
+ items:
397
+ $ref: "#/$defs/MeetingExtension"
398
+
399
+ Topic:
400
+ type: object
401
+ description: "The subject of discussion at a Meeting."
402
+ additionalProperties: false
403
+ properties:
404
+ identifier: { type: string }
405
+ urn: { type: string }
406
+ title: { type: string }
407
+ description: { type: string }
408
+ status: { $ref: "#/$defs/TopicStatus" }
409
+ resumption_of: { type: string }
410
+ documents:
411
+ type: array
412
+ items:
413
+ $ref: "#/$defs/TopicDocument"
414
+ assets:
415
+ type: array
416
+ items:
417
+ $ref: "#/$defs/TopicAsset"
418
+ references:
419
+ type: array
420
+ items:
421
+ $ref: "#/$defs/Reference"
422
+ motions:
423
+ type: array
424
+ items: { type: string }
425
+ decisions:
426
+ type: array
427
+ items: { type: string }
428
+ extensions:
429
+ type: array
430
+ items:
431
+ $ref: "#/$defs/MeetingExtension"
432
+
433
+ Venue:
434
+ type: object
435
+ description: |
436
+ Polymorphic place where a Meeting happens. `kind` discriminates
437
+ physical vs virtual; all fields from both subtypes live here as
438
+ optional siblings. Validators (Edoxen::VenueValidator) enforce
439
+ that fields match `kind`.
440
+ additionalProperties: false
441
+ properties:
442
+ kind: { $ref: "#/$defs/VenueKind" }
443
+ name: { type: string }
444
+ label: { type: string }
445
+ description: { type: string }
446
+ capacity: { type: integer }
447
+ url: { type: string }
448
+ contact_methods:
449
+ type: array
450
+ items:
451
+ $ref: "#/$defs/ContactMethod"
452
+ unlocode:
453
+ type: string
454
+ pattern: "^[A-Z]{2}[A-Z0-9]{3}$"
455
+ iata_code:
456
+ type: string
457
+ pattern: "^[A-Z]{3}$"
458
+ address: { type: string }
459
+ city: { type: string }
460
+ country_code:
461
+ type: string
462
+ pattern: "^[A-Z]{2}$"
463
+ lat: { type: number }
464
+ lon: { type: number }
465
+ building: { type: string }
466
+ floor: { type: string }
467
+ room: { type: string }
468
+ access_notes: { type: string }
469
+
470
+ uri: { type: string }
471
+ features:
472
+ type: array
473
+ items:
474
+ $ref: "#/$defs/VirtualFeature"
475
+ passcode: { type: string }
476
+ meeting_id: { type: string }
477
+ dial_in_numbers:
478
+ type: array
479
+ items: { type: string }
480
+ waiting_room: { type: boolean }
481
+ registration_required: { type: boolean }
482
+
483
+ extensions:
484
+ type: array
485
+ items:
486
+ $ref: "#/$defs/MeetingExtension"
487
+
488
+ RecurrenceByDay:
489
+ type: object
490
+ description: "BYDAY part of a recurrence. `ordinal` is null for 'every', +1 for first, -1 for last."
491
+ additionalProperties: false
492
+ properties:
493
+ ordinal: { type: integer }
494
+ weekday: { type: string }
495
+
496
+ Recurrence:
497
+ type: object
498
+ description: "Structured ISO 8601-2 §13 recurrence."
499
+ additionalProperties: false
500
+ properties:
501
+ freq: { $ref: "#/$defs/RecurrenceFreq" }
502
+ interval: { type: integer }
503
+ count: { type: integer }
504
+ until: { type: string, format: date-time }
505
+ by_day:
506
+ type: array
507
+ items:
508
+ $ref: "#/$defs/RecurrenceByDay"
509
+ by_month_day:
510
+ type: array
511
+ items: { type: integer }
512
+ by_month:
513
+ type: array
514
+ items: { type: integer }
515
+ by_week_no:
516
+ type: array
517
+ items: { type: integer }
518
+ by_year_day:
519
+ type: array
520
+ items: { type: integer }
521
+ by_hour:
522
+ type: array
523
+ items: { type: integer }
524
+ by_minute:
525
+ type: array
526
+ items: { type: integer }
527
+ by_second:
528
+ type: array
529
+ items: { type: integer }
530
+ by_set_pos:
531
+ type: array
532
+ items: { type: integer }
533
+ week_start: { type: string }
534
+ extensions:
535
+ type: array
536
+ items:
537
+ $ref: "#/$defs/MeetingExtension"
538
+
539
+ ExtensionAttribute:
540
+ type: object
541
+ description: |
542
+ One typed key/value pair within a MeetingExtension. Polymorphic on
543
+ value type so consumers don't re-parse strings back into Int/Float/
544
+ Bool/Date (v2.1 tighten, TODO.refactor/47).
545
+ additionalProperties: false
546
+ properties:
547
+ key: { type: string }
548
+ type: { type: string, enum: [string, integer, float, boolean, date, datetime] }
549
+ value: { type: string }
550
+ integer_value: { type: integer }
551
+ float_value: { type: number }
552
+ boolean_value: { type: boolean }
553
+ date_value: { type: string, format: date }
554
+ date_time_value: { type: string, format: date-time }
555
+
556
+ MeetingExtension:
557
+ type: object
558
+ description: |
559
+ Profile-specific extension. Adopters register their namespace via
560
+ `profile` and discriminate via `kind`. Field semantics tightened
561
+ v2.1 (TODO.refactor/47): `kind` is the in-profile discriminator,
562
+ `ref` is the URN of an external profile document, and the
563
+ recursive `extensions[]` slot was removed (YAGNI — use dotted
564
+ keys in `attributes[]` for nesting).
565
+ additionalProperties: false
566
+ properties:
567
+ profile: { type: string }
568
+ kind: { type: string }
569
+ ref: { type: string }
570
+ attributes:
571
+ type: array
572
+ items:
573
+ $ref: "#/$defs/ExtensionAttribute"
574
+
575
+ Officer:
576
+ type: object
577
+ description: "A person holding a structural role in a Meeting."
578
+ additionalProperties: false
579
+ properties:
580
+ role: { $ref: "#/$defs/OfficerRole" }
581
+ person: { $ref: "#/$defs/Person" }
582
+ term_start: { type: string, format: date }
583
+ term_end: { type: string, format: date }
584
+ extensions:
585
+ type: array
586
+ items:
587
+ $ref: "#/$defs/MeetingExtension"
588
+
589
+ ComponentLocalization:
590
+ type: object
591
+ description: "Per-language content for a MeetingComponent."
592
+ additionalProperties: false
593
+ required: [language_code]
594
+ properties:
595
+ language_code:
596
+ type: string
597
+ pattern: "^[a-z]{3}$"
598
+ script:
599
+ type: string
600
+ pattern: "^[A-Z][a-z]{3}$"
601
+ title: { type: string }
602
+ description: { type: string }
603
+
604
+ MeetingComponent:
605
+ type: object
606
+ description: "Flat sub-event of a Meeting."
607
+ additionalProperties: false
608
+ properties:
609
+ identifier: { type: string }
610
+ urn: { type: string }
611
+ kind: { $ref: "#/$defs/ComponentKind" }
612
+ body_type: { type: string }
613
+ title: { type: string }
614
+ description: { type: string }
615
+ starts_at: { type: string, format: date-time }
616
+ ends_at: { type: string, format: date-time }
617
+ time_label: { type: string }
618
+ venue_refs:
619
+ type: array
620
+ items: { type: string }
621
+ officers:
622
+ type: array
623
+ items:
624
+ $ref: "#/$defs/Officer"
625
+ agenda_ref: { type: string }
626
+ minutes_ref: { type: string }
627
+ attendance_refs:
628
+ type: array
629
+ items: { type: string }
630
+ localizations:
631
+ type: array
632
+ items:
633
+ $ref: "#/$defs/ComponentLocalization"
634
+ extensions:
635
+ type: array
636
+ items:
637
+ $ref: "#/$defs/MeetingExtension"
638
+
639
+ MeetingSeries:
640
+ type: object
641
+ description: "Parent of recurring Meeting instances."
642
+ additionalProperties: false
643
+ properties:
644
+ identifier:
645
+ type: array
646
+ items:
647
+ $ref: "#/$defs/StructuredIdentifier"
648
+ urn: { type: string }
649
+ name: { type: string }
650
+ description: { type: string }
651
+ recurrence: { $ref: "#/$defs/Recurrence" }
652
+ term: { type: string }
653
+ contact: { $ref: "#/$defs/Contact" }
654
+ hosts:
655
+ type: array
656
+ items:
657
+ $ref: "#/$defs/HostRef"
658
+ kind: { type: string }
659
+ meeting_refs:
660
+ type: array
661
+ items: { type: string }
662
+ extensions:
663
+ type: array
664
+ items:
665
+ $ref: "#/$defs/MeetingExtension"
283
666
 
284
667
  # ====================================================================
285
668
  # Meeting side.
@@ -291,12 +674,9 @@ $defs:
291
674
  additionalProperties: false
292
675
  required: [source, destination, type]
293
676
  properties:
294
- source:
295
- $ref: "#/$defs/StructuredIdentifier"
296
- destination:
297
- $ref: "#/$defs/StructuredIdentifier"
298
- type:
299
- $ref: "#/$defs/MeetingRelationType"
677
+ source: { $ref: "#/$defs/StructuredIdentifier" }
678
+ destination: { $ref: "#/$defs/StructuredIdentifier" }
679
+ type: { $ref: "#/$defs/MeetingRelationType" }
300
680
 
301
681
  MeetingLocalization:
302
682
  type: object
@@ -325,37 +705,36 @@ $defs:
325
705
  minItems: 1
326
706
  items:
327
707
  $ref: "#/$defs/StructuredIdentifier"
328
- urn: { type: string }
329
- ordinal: { type: integer }
330
- type:
331
- $ref: "#/$defs/MeetingType"
332
- status:
333
- $ref: "#/$defs/MeetingStatus"
334
- year: { type: integer }
708
+ urn: { type: string }
709
+ ordinal: { type: integer }
710
+ series_ref: { type: string }
711
+ type: { $ref: "#/$defs/MeetingType" }
712
+ status: { $ref: "#/$defs/MeetingStatus" }
713
+ visibility: { $ref: "#/$defs/Visibility" }
714
+ body_type: { type: string }
335
715
 
336
- date_range:
337
- $ref: "#/$defs/DateRange"
338
-
339
- committee: { type: string }
340
- committee_group: { type: string }
716
+ date_range: { $ref: "#/$defs/DateRange" }
717
+ recurrence: { $ref: "#/$defs/Recurrence" }
341
718
 
342
719
  venues:
343
720
  type: array
344
721
  items:
345
- $ref: "#/$defs/Location"
346
- general_area: { type: string }
722
+ $ref: "#/$defs/Venue"
723
+ general_area: { type: string }
347
724
  city:
348
725
  type: string
349
726
  pattern: "^[A-Z]{2}[A-Z0-9]{3}$"
350
- description: "UN/LOCODE of the host city (5-char: 2-letter ISO 3166-1 country + 3-char location, e.g. FRPAR, HKHKG, THCNM)."
351
727
  country_code:
352
728
  type: string
353
729
  pattern: "^[A-Z]{2}$"
354
- virtual: { type: boolean }
355
730
 
356
- chair: { $ref: "#/$defs/Person" }
357
- secretary: { $ref: "#/$defs/Person" }
358
- host: { type: string }
731
+ committee: { type: string }
732
+ committee_group: { type: string }
733
+
734
+ officers:
735
+ type: array
736
+ items:
737
+ $ref: "#/$defs/Officer"
359
738
  hosts:
360
739
  type: array
361
740
  items:
@@ -365,15 +744,16 @@ $defs:
365
744
  type: array
366
745
  items:
367
746
  $ref: "#/$defs/SourceUrl"
368
- landing_url: { type: string }
369
- registration_url: { type: string }
747
+ landing_url: { type: string }
748
+ registration_url: { type: string }
749
+ note: { type: string }
750
+ contact: { $ref: "#/$defs/Contact" }
370
751
 
371
- agenda:
372
- $ref: "#/$defs/Agenda"
373
- schedule:
752
+ agenda: { $ref: "#/$defs/Agenda" }
753
+ components:
374
754
  type: array
375
755
  items:
376
- $ref: "#/$defs/ScheduleItem"
756
+ $ref: "#/$defs/MeetingComponent"
377
757
  deadlines:
378
758
  type: array
379
759
  items:
@@ -382,15 +762,24 @@ $defs:
382
762
  type: array
383
763
  items:
384
764
  $ref: "#/$defs/Attendance"
385
- vote_records:
386
- type: array
387
- items:
388
- $ref: "#/$defs/VoteRecord"
389
765
  minutes:
390
766
  type: array
391
767
  items:
392
768
  $ref: "#/$defs/Minutes"
393
769
 
770
+ decisions:
771
+ type: array
772
+ items:
773
+ $ref: "#/$defs/StructuredIdentifier"
774
+ motions:
775
+ type: array
776
+ items:
777
+ $ref: "#/$defs/StructuredIdentifier"
778
+ votings:
779
+ type: array
780
+ items:
781
+ $ref: "#/$defs/StructuredIdentifier"
782
+
394
783
  localizations:
395
784
  type: array
396
785
  items:
@@ -399,9 +788,10 @@ $defs:
399
788
  type: array
400
789
  items:
401
790
  $ref: "#/$defs/MeetingRelation"
402
- resolution_refs:
791
+ extensions:
403
792
  type: array
404
- items: { type: string }
793
+ items:
794
+ $ref: "#/$defs/MeetingExtension"
405
795
 
406
796
  MeetingCollectionMetadata:
407
797
  type: object
@@ -410,6 +800,22 @@ $defs:
410
800
  properties:
411
801
  title: { type: string }
412
802
  source: { type: string }
803
+ body_vocabulary:
804
+ type: array
805
+ items:
806
+ $ref: "#/$defs/BodyVocabularyEntry"
807
+
808
+ BodyVocabularyEntry:
809
+ type: object
810
+ description: |
811
+ One entry in a per-dataset body_vocabulary list. Maps a free-form
812
+ body_type to a short canonical value. SSOT for body_type ->
813
+ canonical_type resolution within the declaring collection.
814
+ additionalProperties: false
815
+ properties:
816
+ body_type: { type: string }
817
+ canonical_type: { type: string }
818
+ definition: { type: string }
413
819
 
414
820
  MeetingCollection:
415
821
  type: object
@@ -425,14 +831,21 @@ $defs:
425
831
  $ref: "#/$defs/Meeting"
426
832
 
427
833
  # ====================================================================
428
- # Enums. Each value-list must equal the matching Edoxen::Enums::*
429
- # frozen array; spec/edoxen/schema_meeting_enum_sync_spec.rb enforces.
834
+ # Enums.
430
835
  # ====================================================================
431
836
 
432
837
  MeetingType:
433
838
  type: string
434
839
  description: "Edoxen::Enums::MEETING_TYPE."
435
- enum: [plenary, working_group, task_group, ad_hoc, joint, conference_session]
840
+ enum: [plenary, working_group, task_group, ad_hoc, joint, general_assembly, committee, subcommittee, conference, workshop, seminar, webinar, hearing, markup, board_meeting, annual_general_meeting, other]
841
+
842
+ MeetingTypeCanonical:
843
+ type: string
844
+ description: |
845
+ Edoxen::Enums::MEETING_TYPE_CANONICAL — short abstract set (v2.1,
846
+ TODO.refactor/46). No `other` escape — bodies pick the closest
847
+ fit and use `body_type` for body-specific terminology.
848
+ enum: [plenary, governing, working, advisory]
436
849
 
437
850
  MeetingStatus:
438
851
  type: string
@@ -447,12 +860,12 @@ $defs:
447
860
  AgendaItemKind:
448
861
  type: string
449
862
  description: "Edoxen::Enums::AGENDA_ITEM_KIND."
450
- enum: [numbered, unnumbered, header, opening, closing]
863
+ enum: [numbered, unnumbered, header, opening, closing, aob]
451
864
 
452
865
  AgendaItemOutcome:
453
866
  type: string
454
867
  description: "Edoxen::Enums::AGENDA_ITEM_OUTCOME."
455
- enum: [discussed, resolved, deferred, adopted, withdrawn]
868
+ enum: [discussed, resolved, deferred, adopted, withdrawn, carried, negatived]
456
869
 
457
870
  HostType:
458
871
  type: string
@@ -462,19 +875,64 @@ $defs:
462
875
  MeetingRelationType:
463
876
  type: string
464
877
  description: "Edoxen::Enums::MEETING_RELATION_TYPE."
465
- enum: [continues_from, continues_to, joint_with, supersedes, superseded_by, rescheduled_from, rescheduled_to]
878
+ enum: [continues_from, continues_to, joint_with, supersedes, superseded_by, rescheduled_from, rescheduled_to, parent_of, child_of, sibling_of, depends_on, finish_to_start, finish_to_finish, start_to_start, start_to_finish]
466
879
 
467
880
  SourceUrlKind:
468
881
  type: string
469
882
  description: "Edoxen::Enums::SOURCE_URL_KIND."
470
- enum: [agenda_pdf, minutes_pdf, resolutions_pdf, report_pdf, register_url, landing_page]
883
+ enum: [agenda_pdf, minutes_pdf, decisions_pdf, report_pdf, register_url, landing_page]
471
884
 
472
885
  ParticipationStatus:
473
886
  type: string
474
887
  description: "Edoxen::Enums::PARTICIPATION_STATUS."
475
888
  enum: [present, absent, apologies, observer, excused]
476
889
 
890
+ AttendanceRole:
891
+ type: string
892
+ description: "Edoxen::Enums::ATTENDANCE_ROLE."
893
+ enum: [chair, required, optional, non_participant]
894
+
895
+ AttendanceResponse:
896
+ type: string
897
+ description: "Edoxen::Enums::ATTENDANCE_RESPONSE."
898
+ enum: [pending, confirmed, declined, tentative, delegated]
899
+
477
900
  VoteType:
478
901
  type: string
479
902
  description: "Edoxen::Enums::VOTE_TYPE."
480
903
  enum: [affirmative, negative, abstain, absent, not_applicable]
904
+
905
+ TopicStatus:
906
+ type: string
907
+ description: "Edoxen::Enums::TOPIC_STATUS."
908
+ enum: [open, under_discussion, decided, deferred, withdrawn]
909
+
910
+ VenueKind:
911
+ type: string
912
+ description: "Edoxen::Enums::VENUE_KIND."
913
+ enum: [physical, virtual]
914
+
915
+ VirtualFeature:
916
+ type: string
917
+ description: "Edoxen::Enums::VIRTUAL_FEATURE."
918
+ enum: [audio, video, chat, phone, screen, feed]
919
+
920
+ Visibility:
921
+ type: string
922
+ description: "Edoxen::Enums::VISIBILITY."
923
+ enum: [public, private, confidential]
924
+
925
+ ComponentKind:
926
+ type: string
927
+ description: "Edoxen::Enums::COMPONENT_KIND."
928
+ enum: [track, session, debate, breakout, bof, plenary_session, working_group_session, committee_of_the_whole, keynote, address, statement, question_time, opening, closing, break, reception, registration, networking, other]
929
+
930
+ OfficerRole:
931
+ type: string
932
+ description: "Edoxen::Enums::OFFICER_ROLE."
933
+ enum: [chair, vice_chair, deputy_chair, secretary, treasurer, parliamentarian, presiding_officer, sergeant_at_arms, other]
934
+
935
+ RecurrenceFreq:
936
+ type: string
937
+ description: "Edoxen::Enums::RECURRENCE_FREQ."
938
+ enum: [secondly, minutely, hourly, daily, weekly, monthly, yearly]