edoxen 0.8.1 → 0.8.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2d36145e7d749b68340072562507ea1416890d645b36a0f7d44620f15b92561
4
- data.tar.gz: af5f8fccde1aff6ecbb8d1eca57e4fd217e1bcbb99dab72fdad147eb0992c6c6
3
+ metadata.gz: 9be35dc39f7766b3bebb55769f484b13e4e0f43d5d29e4cdaba916dc64594a82
4
+ data.tar.gz: 1466534420ad8b83df31b1a84c1fe0c3e27e28a6f4529c190b4658c2622c4700
5
5
  SHA512:
6
- metadata.gz: 906d8a9f930f5ca0f8b9e83edd3e24bf4fd409e35ba3feb4cb732e29a630153a8fa1298e3b8384d85c366bdbad210f3123ed8729cf30ca72afbbd8b8cf073f38
7
- data.tar.gz: bdf81dddea8e38a8f5937713d78d32d768c4099774b5d2e148e69a422ad78c6959e2365722dfbd87ec2b46779fa4941cd2e63bb2dc74709904ddc7b603645054
6
+ metadata.gz: 4912179fadc07811533420e68c07edd374468369fd4f3ffa0135d2556e310ee1ed7f7c595a9241d6ac8378c6a62ea36e7673a0f9fa8ac0fe98d39668606475d9
7
+ data.tar.gz: 8cba6a15881824a1b6bef2e29e113fab969302b485ae1404e8100121cefddae44c435a529a999ed24ebae15eddb0bc757c3e9270ad19cde4e3d5530c6cd239c4
@@ -6,10 +6,16 @@ module Edoxen
6
6
  # closing/aob; `outcome` records what happened; `decision_ref`
7
7
  # optionally links to the URN of the decision this item produced.
8
8
  #
9
+ # `urn` is the first-class URN of this item, derived from the parent
10
+ # meeting URN and the label (e.g. "urn:oiml:ciml:meeting:ciml-60:agenda:6.2").
11
+ # It is optional in source data — when absent it can be computed on
12
+ # the fly via {Edoxen::UrnFor.agenda_item}.
13
+ #
9
14
  # Topics (0..*) are the subject(s) of discussion at this agenda item.
10
15
  # AOB (Any Other Business) items have 0 topics until raised during
11
16
  # the meeting.
12
17
  class AgendaItem < Lutaml::Model::Serializable
18
+ attribute :urn, :string
13
19
  attribute :label, :string
14
20
  attribute :kind, :string, values: Enums::AGENDA_ITEM_KIND
15
21
  attribute :title, LocalizedString, collection: true
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # A committee, subcommittee, working group, or other organised body
5
+ # that owns meetings and decisions. Carries a short code (e.g.
6
+ # "ISO/TC 154", "CIML") and a localised full name.
7
+ #
8
+ # Three-tier entity resolution (same pattern as Contact and Venue):
9
+ # +ref+ set → resolve from global BodyRegister by URN.
10
+ # +local_ref+ set → resolve from document-scoped Meeting#bodies[].
11
+ # Both nil → inline data.
12
+ class Body < Lutaml::Model::Serializable
13
+ attribute :ref, :string
14
+ attribute :local_ref, :string
15
+ attribute :code, :string
16
+ attribute :name, LocalizedString, collection: true
17
+ attribute :kind, :string
18
+ attribute :parent_ref, EntityRef
19
+ attribute :extensions, MeetingExtension, collection: true
20
+
21
+ def reference?
22
+ (!ref.nil? && !ref.to_s.empty?) ||
23
+ (!local_ref.nil? && !local_ref.to_s.empty?)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # Authoritative register of Bodies (committees, subcommittees, working
5
+ # groups). Members carry `urn: urn:edoxen:body:{scope}:{local-id}`;
6
+ # the register's +scope+ MUST match the scope segment in member URNs.
7
+ class BodyRegister < Lutaml::Model::Serializable
8
+ attribute :scope, :string
9
+ attribute :title, LocalizedString, collection: true
10
+ attribute :bodies, Body, collection: true
11
+ attribute :extensions, MeetingExtension, collection: true
12
+
13
+ def find_by_urn(urn)
14
+ bodies&.find { |b| b.code == urn || b.ref == urn }
15
+ end
16
+ end
17
+ end
data/lib/edoxen/cli.rb CHANGED
@@ -34,8 +34,8 @@ module Edoxen
34
34
  lambda do |content|
35
35
  data = YAML.safe_load(content, permitted_classes: [Date, Time])
36
36
  case Batch.decision_kind(data)
37
- when :contacts then ContactCollection.from_yaml(content)
38
- when :venues then VenueCollection.from_yaml(content)
37
+ when :contacts then ContactRegister.from_yaml(content)
38
+ when :venues then VenueRegister.from_yaml(content)
39
39
  else DecisionCollection.from_yaml(content)
40
40
  end
41
41
  end
@@ -78,8 +78,8 @@ module Edoxen
78
78
  # Discriminate the three top-level shapes the canonical
79
79
  # `schema/edoxen.yaml` accepts via `oneOf`:
80
80
  #
81
- # * :contacts — `{ contacts: [...] }` (ContactCollection registry).
82
- # * :venues — `{ venues: [...] }` (VenueCollection registry).
81
+ # * :contacts — `{ contacts: [...] }` (ContactRegister registry).
82
+ # * :venues — `{ venues: [...] }` (VenueRegister registry).
83
83
  # * :decisions — anything else (DecisionCollection, the default).
84
84
  def decision_kind(data)
85
85
  return :decisions unless data.is_a?(Hash)
@@ -17,6 +17,7 @@ module Edoxen
17
17
  # accepts a Contact.
18
18
  class Contact < Lutaml::Model::Serializable
19
19
  attribute :ref, :string
20
+ attribute :local_ref, :string
20
21
  attribute :urn, :string
21
22
  attribute :name, LocalizedName, collection: true
22
23
  attribute :kind, :string
@@ -29,7 +30,8 @@ module Edoxen
29
30
  attribute :extensions, MeetingExtension, collection: true
30
31
 
31
32
  def reference?
32
- !ref.nil? && !ref.to_s.empty?
33
+ (!ref.nil? && !ref.to_s.empty?) ||
34
+ (!local_ref.nil? && !local_ref.to_s.empty?)
33
35
  end
34
36
  end
35
37
  end
@@ -7,13 +7,13 @@ module Edoxen
7
7
  #
8
8
  # Other documents (Meeting, MeetingComponent, HostRef, etc.) reference
9
9
  # contacts via `ref: urn:edoxen:contact:{scope}:{local-id}` and
10
- # resolve against the matching ContactCollection.
10
+ # resolve against the matching ContactRegister.
11
11
  #
12
12
  # Storage: single YAML file (typical) or YAML Stream (one Contact per
13
13
  # document, for large registries that need partial updates). Stream
14
14
  # loading is a service-layer concern (file-system I/O); the model
15
15
  # owns only the (de)serialisation of one collection.
16
- class ContactCollection < Lutaml::Model::Serializable
16
+ class ContactRegister < Lutaml::Model::Serializable
17
17
  attribute :scope, :string
18
18
  attribute :title, LocalizedString, collection: true
19
19
  attribute :contacts, Contact, collection: true
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # EntityResolver — resolves Contact/Venue/Body references by walking
5
+ # the three-tier scope hierarchy:
6
+ #
7
+ # 1. Inline: if the entity has no +ref+ and no +local_ref+, return
8
+ # it as-is (it carries full data).
9
+ # 2. Document-scoped: if +local_ref+ is set, look up in the
10
+ # document-scoped collection (e.g. Meeting#contacts) by matching
11
+ # +urn+ against +local_ref+ (+code+ for Body, which has no +urn+).
12
+ # 3. Global register: if +ref+ is set, look up in the global
13
+ # register (e.g. ContactRegister) by matching +urn+ against +ref+.
14
+ #
15
+ # Pure service: no mutation. Returns the resolved entity or nil.
16
+ class EntityResolver
17
+ attr_reader :scoped_collections, :global_registers
18
+
19
+ def initialize(scoped: {}, registers: {})
20
+ @scoped_collections = scoped
21
+ @global_registers = registers
22
+ end
23
+
24
+ def resolve(entity)
25
+ return entity unless entity.reference?
26
+
27
+ resolve_local(entity) || resolve_global(entity)
28
+ end
29
+
30
+ def resolve_all(entities)
31
+ entities.map { |e| resolve(e) }
32
+ end
33
+
34
+ private
35
+
36
+ def resolve_local(entity)
37
+ return nil if entity.local_ref.nil? || entity.local_ref.to_s.empty?
38
+
39
+ collection = find_scoped_collection(entity.class)
40
+ return nil unless collection
41
+
42
+ collection.find { |member| member_key(member) == entity.local_ref }
43
+ end
44
+
45
+ def resolve_global(entity)
46
+ return nil if entity.ref.nil? || entity.ref.to_s.empty?
47
+
48
+ register = find_global_register(entity.class)
49
+ return nil unless register
50
+
51
+ register.find_by_urn(entity.ref)
52
+ end
53
+
54
+ def find_scoped_collection(klass)
55
+ @scoped_collections[klass] ||
56
+ @scoped_collections[klass.superclass]
57
+ end
58
+
59
+ def find_global_register(klass)
60
+ @global_registers[klass] ||
61
+ @global_registers[klass.superclass]
62
+ end
63
+
64
+ def member_key(member)
65
+ # Body has no +urn+ — its local key is +code+ (same lookup
66
+ # semantics as BodyRegister#find_by_urn).
67
+ member.respond_to?(:urn) ? member.urn : member.code
68
+ end
69
+ end
70
+ end
@@ -33,8 +33,8 @@ module Edoxen
33
33
  attribute :city, :string
34
34
  attribute :country_code, :string
35
35
 
36
- attribute :committee, :string
37
- attribute :committee_group, :string
36
+ attribute :committee, Body
37
+ attribute :committee_group, Body
38
38
 
39
39
  attribute :officers, Officer, collection: true
40
40
  attribute :hosts, HostRef, collection: true
@@ -44,6 +44,8 @@ module Edoxen
44
44
  attribute :registration_url, :string
45
45
  attribute :note, LocalizedString, collection: true
46
46
  attribute :contact, Contact
47
+ attribute :contacts, Contact, collection: true
48
+ attribute :bodies, Body, collection: true
47
49
 
48
50
  attribute :agenda, Agenda
49
51
  attribute :components, MeetingComponent, collection: true
@@ -54,7 +56,7 @@ module Edoxen
54
56
  attribute :declarations, Declaration, collection: true
55
57
 
56
58
  # Outcomes (canonical location on Meeting)
57
- attribute :decisions, Decision, collection: true
59
+ attribute :decisions, StructuredIdentifier, collection: true
58
60
  attribute :motions, Motion, collection: true
59
61
  attribute :votings, Voting, collection: true
60
62
 
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Edoxen
4
+ # URN helpers for entities that don't carry their URN inline (e.g.
5
+ # AgendaItem which derives its URN from the parent Meeting). The
6
+ # scheme is hierarchical:
7
+ #
8
+ # urn:oiml:{body}:{kind}:{slug}(:{sub-kind}:{sub-id})*
9
+ #
10
+ # Examples:
11
+ # urn:oiml:ciml:meeting:ciml-60
12
+ # urn:oiml:ciml:meeting:ciml-60:agenda:6.2
13
+ # urn:oiml:doc:ciml:resolution:2025-01
14
+ #
15
+ # Use {UrnFor.agenda_item} to compute an agenda item URN from its
16
+ # parent meeting URN + label, and {UrnFor.parse} to decompose one.
17
+ module UrnFor
18
+ AGENDA_SEGMENT = "agenda"
19
+ SEGMENT_RE = /[^:]+/
20
+
21
+ # Build an AgendaItem URN from its parent Meeting URN and the
22
+ # agenda item label (e.g. "6.2"). Returns nil if either input is
23
+ # blank so callers can use this as a default-value generator
24
+ # without raising.
25
+ def self.agenda_item(meeting_urn:, label:)
26
+ m = meeting_urn.to_s
27
+ l = label.to_s
28
+ return nil if m.empty? || l.empty?
29
+
30
+ "#{m}:#{AGENDA_SEGMENT}:#{l}"
31
+ end
32
+
33
+ # Parse an agenda item URN into its component parts. Returns a Hash
34
+ # with :meeting_urn and :label keys, or nil if the URN doesn't
35
+ # match the expected shape.
36
+ #
37
+ # UrnFor.parse("urn:oiml:ciml:meeting:ciml-60:agenda:6.2")
38
+ # # => { meeting_urn: "urn:oiml:ciml:meeting:ciml-60",
39
+ # # label: "6.2" }
40
+ def self.parse(urn)
41
+ return nil unless urn
42
+
43
+ s = urn.to_s
44
+ idx = s.rindex(":#{AGENDA_SEGMENT}:")
45
+ return nil unless idx
46
+
47
+ meeting_urn = s[0...idx]
48
+ label = s[(idx + AGENDA_SEGMENT.length + 2)..]
49
+ return nil if meeting_urn.empty? || label.to_s.empty?
50
+
51
+ { meeting_urn: meeting_urn, label: label }
52
+ end
53
+
54
+ # Walk an Agenda tree and set each item's `urn` based on the
55
+ # parent meeting URN. Skips items that already have a URN. Useful
56
+ # as a backfill step in data-fix scripts.
57
+ def self.assign_to_agenda!(agenda, meeting_urn)
58
+ return nil unless agenda && meeting_urn
59
+
60
+ (agenda.items || []).each do |item|
61
+ next if item.urn && !item.urn.to_s.empty?
62
+
63
+ item.urn = agenda_item(meeting_urn: meeting_urn, label: item.label)
64
+ end
65
+ agenda
66
+ end
67
+ end
68
+ end
data/lib/edoxen/venue.rb CHANGED
@@ -13,6 +13,7 @@ module Edoxen
13
13
  # - Added: `urn` for registry storage; `ref` for reference-by-URN.
14
14
  class Venue < Lutaml::Model::Serializable
15
15
  attribute :ref, :string
16
+ attribute :local_ref, :string
16
17
  attribute :urn, :string
17
18
  attribute :kind, :string, values: Enums::VENUE_KIND
18
19
  attribute :name, LocalizedString, collection: true
@@ -47,7 +48,8 @@ module Edoxen
47
48
  attribute :extensions, MeetingExtension, collection: true
48
49
 
49
50
  def reference?
50
- !ref.nil? && !ref.to_s.empty?
51
+ (!ref.nil? && !ref.to_s.empty?) ||
52
+ (!local_ref.nil? && !local_ref.to_s.empty?)
51
53
  end
52
54
 
53
55
  def physical?
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Edoxen
4
- # Registry of Venues indexed by scoped URN. Mirrors ContactCollection:
4
+ # Registry of Venues indexed by scoped URN. Mirrors ContactRegister:
5
5
  # members carry `urn: urn:edoxen:venue:{scope}:{local-id}`; the
6
6
  # collection's `scope` MUST match the scope segment in member URNs.
7
7
  # Stream loading belongs to the service layer.
8
- class VenueCollection < Lutaml::Model::Serializable
8
+ class VenueRegister < Lutaml::Model::Serializable
9
9
  attribute :scope, :string
10
10
  attribute :title, LocalizedString, collection: true
11
11
  attribute :venues, Venue, collection: true
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Edoxen
4
- VERSION = "0.8.1"
4
+ VERSION = "0.8.3"
5
5
  end
data/lib/edoxen.rb CHANGED
@@ -38,6 +38,7 @@ module Edoxen
38
38
  autoload :Consideration, "edoxen/consideration"
39
39
  autoload :SourceUrl, "edoxen/source_url"
40
40
  autoload :Url, "edoxen/url"
41
+ autoload :UrnFor, "edoxen/urn_for"
41
42
  autoload :DecisionRelation, "edoxen/decision_relation"
42
43
  autoload :Decision, "edoxen/decision"
43
44
  autoload :DecisionMetadata, "edoxen/decision_metadata"
@@ -91,11 +92,14 @@ module Edoxen
91
92
  autoload :MeetingComponent, "edoxen/meeting_component"
92
93
  autoload :Officer, "edoxen/officer"
93
94
  autoload :VenueValidator, "edoxen/venue_validator"
94
- autoload :ContactCollection, "edoxen/contact_collection"
95
- autoload :VenueCollection, "edoxen/venue_collection"
95
+ autoload :ContactRegister, "edoxen/contact_register"
96
+ autoload :VenueRegister, "edoxen/venue_register"
97
+ autoload :Body, "edoxen/body"
98
+ autoload :BodyRegister, "edoxen/body_register"
96
99
 
97
100
  # --- Services ---------------------------------------------------------
98
101
  autoload :SchemaValidator, "edoxen/schema_validator"
99
102
  autoload :LinkChecker, "edoxen/link_checker"
103
+ autoload :EntityResolver, "edoxen/entity_resolver"
100
104
  autoload :Cli, "edoxen/cli"
101
105
  end
data/schema/edoxen.yaml CHANGED
@@ -8,8 +8,8 @@ description: |
8
8
  Accepts three top-level document kinds via `oneOf`:
9
9
 
10
10
  * `DecisionCollection` — the formal outcomes adopted by a Meeting.
11
- * `ContactCollection` — a scoped-URN registry of Contacts.
12
- * `VenueCollection` — a scoped-URN registry of Venues.
11
+ * `ContactRegister` — a scoped-URN registry of Contacts.
12
+ * `VenueRegister` — a scoped-URN registry of Venues.
13
13
 
14
14
  Mirrors the canonical LutaML information model in
15
15
  https://github.com/edoxen/edoxen-model/tree/main/models .
@@ -26,8 +26,9 @@ description: |
26
26
  type: object
27
27
  oneOf:
28
28
  - "$ref": "#/$defs/DecisionCollection"
29
- - "$ref": "#/$defs/ContactCollection"
30
- - "$ref": "#/$defs/VenueCollection"
29
+ - "$ref": "#/$defs/ContactRegister"
30
+ - "$ref": "#/$defs/VenueRegister"
31
+ - "$ref": "#/$defs/BodyRegister"
31
32
  "$defs":
32
33
  DecisionCollection:
33
34
  type: object
@@ -569,7 +570,8 @@ oneOf:
569
570
  properties:
570
571
  ref:
571
572
  type: string
572
- description: URN reference; if set, ignore other fields
573
+ local_ref:
574
+ type: string
573
575
  urn:
574
576
  type: string
575
577
  pattern: "^urn:edoxen:venue:[^:]+:[^:]+$"
@@ -877,7 +879,8 @@ oneOf:
877
879
  properties:
878
880
  ref:
879
881
  type: string
880
- description: URN reference; if set, ignore other fields
882
+ local_ref:
883
+ type: string
881
884
  urn:
882
885
  type: string
883
886
  pattern: "^urn:edoxen:contact:[^:]+:[^:]+$"
@@ -923,6 +926,8 @@ oneOf:
923
926
  properties:
924
927
  ref:
925
928
  type: string
929
+ local_ref:
930
+ type: string
926
931
  urn:
927
932
  type: string
928
933
  pattern: "^urn:edoxen:contact:[^:]+:[^:]+$"
@@ -1123,7 +1128,7 @@ oneOf:
1123
1128
  type: string
1124
1129
  contact:
1125
1130
  "$ref": "#/$defs/Contact"
1126
- ContactCollection:
1131
+ ContactRegister:
1127
1132
  type: object
1128
1133
  description: |
1129
1134
  Registry of Contacts indexed by scoped URN. Members carry
@@ -1145,9 +1150,9 @@ oneOf:
1145
1150
  type: array
1146
1151
  items:
1147
1152
  "$ref": "#/$defs/MeetingExtension"
1148
- VenueCollection:
1153
+ VenueRegister:
1149
1154
  type: object
1150
- description: 'Registry of Venues indexed by scoped URN. Mirrors ContactCollection.
1155
+ description: 'Registry of Venues indexed by scoped URN. Mirrors ContactRegister.
1151
1156
 
1152
1157
  '
1153
1158
  additionalProperties: false
@@ -1518,3 +1523,49 @@ oneOf:
1518
1523
  type: array
1519
1524
  items:
1520
1525
  "$ref": "#/$defs/MeetingExtension"
1526
+ Body:
1527
+ type: object
1528
+ description: |
1529
+ A committee, subcommittee, working group, or other organised body.
1530
+ Three-tier entity resolution (ref / local_ref / inline).
1531
+ additionalProperties: false
1532
+ properties:
1533
+ ref:
1534
+ type: string
1535
+ local_ref:
1536
+ type: string
1537
+ code:
1538
+ type: string
1539
+ name:
1540
+ type: array
1541
+ items:
1542
+ "$ref": "#/$defs/LocalizedString"
1543
+ kind:
1544
+ type: string
1545
+ parent_ref:
1546
+ "$ref": "#/$defs/EntityRef"
1547
+ extensions:
1548
+ type: array
1549
+ items:
1550
+ "$ref": "#/$defs/MeetingExtension"
1551
+ BodyRegister:
1552
+ type: object
1553
+ description: |
1554
+ Authoritative register of Bodies (committees, subcommittees,
1555
+ working groups).
1556
+ additionalProperties: false
1557
+ properties:
1558
+ scope:
1559
+ type: string
1560
+ title:
1561
+ type: array
1562
+ items:
1563
+ "$ref": "#/$defs/LocalizedString"
1564
+ bodies:
1565
+ type: array
1566
+ items:
1567
+ "$ref": "#/$defs/Body"
1568
+ extensions:
1569
+ type: array
1570
+ items:
1571
+ "$ref": "#/$defs/MeetingExtension"
data/schema/meeting.yaml CHANGED
@@ -215,7 +215,8 @@ oneOf:
215
215
  properties:
216
216
  ref:
217
217
  type: string
218
- description: URN reference; if set, ignore other fields
218
+ local_ref:
219
+ type: string
219
220
  urn:
220
221
  type: string
221
222
  pattern: "^urn:edoxen:contact:[^:]+:[^:]+$"
@@ -261,6 +262,8 @@ oneOf:
261
262
  properties:
262
263
  ref:
263
264
  type: string
265
+ local_ref:
266
+ type: string
264
267
  urn:
265
268
  type: string
266
269
  pattern: "^urn:edoxen:contact:[^:]+:[^:]+$"
@@ -449,9 +452,19 @@ oneOf:
449
452
  "$ref": "#/$defs/LocalizedString"
450
453
  AgendaItem:
451
454
  type: object
452
- description: 'One entry on an Agenda. 1.0: per-field Localized.'
455
+ description: |
456
+ One entry on an Agenda. 1.0: per-field Localized.
457
+ `urn` is the first-class URN of this item, derived from the
458
+ parent meeting URN and label (e.g.
459
+ `urn:oiml:ciml:meeting:ciml-60:agenda:6.2`). Optional in source
460
+ data; can be computed via `Edoxen::UrnFor.agenda_item`.
453
461
  additionalProperties: false
454
462
  properties:
463
+ urn:
464
+ type: string
465
+ description: |
466
+ First-class URN for this agenda item. Hierarchical under the
467
+ parent meeting URN: `urn:oiml:{body}:meeting:{slug}:agenda:{label}`.
455
468
  label:
456
469
  type: string
457
470
  kind:
@@ -619,7 +632,8 @@ oneOf:
619
632
  properties:
620
633
  ref:
621
634
  type: string
622
- description: URN reference; if set, ignore other fields
635
+ local_ref:
636
+ type: string
623
637
  urn:
624
638
  type: string
625
639
  pattern: "^urn:edoxen:venue:[^:]+:[^:]+$"
@@ -1003,9 +1017,9 @@ oneOf:
1003
1017
  type: string
1004
1018
  pattern: "^[A-Z]{2}$"
1005
1019
  committee:
1006
- type: string
1020
+ "$ref": "#/$defs/Body"
1007
1021
  committee_group:
1008
- type: string
1022
+ "$ref": "#/$defs/Body"
1009
1023
  officers:
1010
1024
  type: array
1011
1025
  items:
@@ -1028,6 +1042,14 @@ oneOf:
1028
1042
  "$ref": "#/$defs/LocalizedString"
1029
1043
  contact:
1030
1044
  "$ref": "#/$defs/Contact"
1045
+ contacts:
1046
+ type: array
1047
+ items:
1048
+ "$ref": "#/$defs/Contact"
1049
+ bodies:
1050
+ type: array
1051
+ items:
1052
+ "$ref": "#/$defs/Body"
1031
1053
  agenda:
1032
1054
  "$ref": "#/$defs/Agenda"
1033
1055
  components:
@@ -1413,3 +1435,49 @@ oneOf:
1413
1435
  type: array
1414
1436
  items:
1415
1437
  "$ref": "#/$defs/MeetingExtension"
1438
+ Body:
1439
+ type: object
1440
+ description: |
1441
+ A committee, subcommittee, working group, or other organised body.
1442
+ Three-tier entity resolution (ref / local_ref / inline).
1443
+ additionalProperties: false
1444
+ properties:
1445
+ ref:
1446
+ type: string
1447
+ local_ref:
1448
+ type: string
1449
+ code:
1450
+ type: string
1451
+ name:
1452
+ type: array
1453
+ items:
1454
+ "$ref": "#/$defs/LocalizedString"
1455
+ kind:
1456
+ type: string
1457
+ parent_ref:
1458
+ "$ref": "#/$defs/EntityRef"
1459
+ extensions:
1460
+ type: array
1461
+ items:
1462
+ "$ref": "#/$defs/MeetingExtension"
1463
+ BodyRegister:
1464
+ type: object
1465
+ description: |
1466
+ Authoritative register of Bodies (committees, subcommittees,
1467
+ working groups).
1468
+ additionalProperties: false
1469
+ properties:
1470
+ scope:
1471
+ type: string
1472
+ title:
1473
+ type: array
1474
+ items:
1475
+ "$ref": "#/$defs/LocalizedString"
1476
+ bodies:
1477
+ type: array
1478
+ items:
1479
+ "$ref": "#/$defs/Body"
1480
+ extensions:
1481
+ type: array
1482
+ items:
1483
+ "$ref": "#/$defs/MeetingExtension"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: edoxen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-14 00:00:00.000000000 Z
11
+ date: 2026-07-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json_schemer
@@ -121,14 +121,16 @@ files:
121
121
  - lib/edoxen/agenda_item.rb
122
122
  - lib/edoxen/approval.rb
123
123
  - lib/edoxen/attendance.rb
124
+ - lib/edoxen/body.rb
125
+ - lib/edoxen/body_register.rb
124
126
  - lib/edoxen/body_vocabulary_entry.rb
125
127
  - lib/edoxen/body_vocabulary_host.rb
126
128
  - lib/edoxen/cli.rb
127
129
  - lib/edoxen/consideration.rb
128
130
  - lib/edoxen/contact.rb
129
- - lib/edoxen/contact_collection.rb
130
131
  - lib/edoxen/contact_identifier.rb
131
132
  - lib/edoxen/contact_method.rb
133
+ - lib/edoxen/contact_register.rb
132
134
  - lib/edoxen/date_range.rb
133
135
  - lib/edoxen/date_time_range.rb
134
136
  - lib/edoxen/deadline.rb
@@ -139,6 +141,7 @@ files:
139
141
  - lib/edoxen/decision_relation.rb
140
142
  - lib/edoxen/declaration.rb
141
143
  - lib/edoxen/entity_ref.rb
144
+ - lib/edoxen/entity_resolver.rb
142
145
  - lib/edoxen/enums.rb
143
146
  - lib/edoxen/error.rb
144
147
  - lib/edoxen/extension_attribute.rb
@@ -174,8 +177,9 @@ files:
174
177
  - lib/edoxen/topic_asset.rb
175
178
  - lib/edoxen/topic_document.rb
176
179
  - lib/edoxen/url.rb
180
+ - lib/edoxen/urn_for.rb
177
181
  - lib/edoxen/venue.rb
178
- - lib/edoxen/venue_collection.rb
182
+ - lib/edoxen/venue_register.rb
179
183
  - lib/edoxen/venue_validator.rb
180
184
  - lib/edoxen/version.rb
181
185
  - lib/edoxen/virtual_venue.rb