edoxen 0.3.0 → 0.5.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.
- checksums.yaml +4 -4
- data/CLAUDE.md +24 -10
- data/README.adoc +170 -31
- data/TODO.meeting-agenda/01-design-decisions.md +132 -0
- data/TODO.meeting-agenda/02-lutaml-canonical.md +61 -0
- data/TODO.meeting-agenda/03-ruby-models.md +73 -0
- data/TODO.meeting-agenda/04-schema.md +34 -0
- data/TODO.meeting-agenda/05-fixtures.md +43 -0
- data/TODO.meeting-agenda/06-specs.md +52 -0
- data/TODO.meeting-agenda/07-cli.md +55 -0
- data/TODO.meeting-agenda/08-docs.md +35 -0
- data/TODO.meeting-agenda/09-verify-and-release.md +41 -0
- data/TODO.meeting-agenda/10-future-enhancements.md +72 -0
- data/TODO.meeting-agenda/README.md +30 -0
- data/lib/edoxen/action.rb +0 -6
- data/lib/edoxen/agenda.rb +26 -0
- data/lib/edoxen/agenda_item.rb +17 -0
- data/lib/edoxen/approval.rb +0 -7
- data/lib/edoxen/cli.rb +199 -95
- data/lib/edoxen/consideration.rb +0 -6
- data/lib/edoxen/date_range.rb +11 -0
- data/lib/edoxen/deadline.rb +10 -0
- data/lib/edoxen/enums.rb +24 -0
- data/lib/edoxen/error.rb +32 -2
- data/lib/edoxen/host_ref.rb +14 -0
- data/lib/edoxen/localization.rb +0 -12
- data/lib/edoxen/location.rb +15 -0
- data/lib/edoxen/meeting.rb +68 -0
- data/lib/edoxen/meeting_collection.rb +26 -0
- data/lib/edoxen/meeting_collection_metadata.rb +11 -0
- data/lib/edoxen/meeting_identifier.rb +0 -5
- data/lib/edoxen/meeting_localization.rb +15 -0
- data/lib/edoxen/meeting_relation.rb +12 -0
- data/lib/edoxen/person.rb +14 -0
- data/lib/edoxen/reference.rb +13 -0
- data/lib/edoxen/resolution.rb +21 -12
- data/lib/edoxen/resolution_collection.rb +0 -5
- data/lib/edoxen/resolution_date.rb +0 -5
- data/lib/edoxen/resolution_metadata.rb +5 -9
- data/lib/edoxen/resolution_relation.rb +0 -6
- data/lib/edoxen/schedule_item.rb +16 -0
- data/lib/edoxen/schema_validator.rb +12 -28
- data/lib/edoxen/source_url.rb +6 -8
- data/lib/edoxen/structured_identifier.rb +0 -5
- data/lib/edoxen/url.rb +0 -6
- data/lib/edoxen/version.rb +1 -1
- data/lib/edoxen.rb +17 -0
- data/schema/edoxen.yaml +7 -0
- data/schema/meeting.yaml +358 -0
- metadata +27 -2
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Phase 6 — Specs
|
|
2
|
+
|
|
3
|
+
Priority: **P0**
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Comprehensive spec coverage. One spec file per new model class, plus
|
|
8
|
+
CLI and schema-sync coverage. Real model instances only — no
|
|
9
|
+
`double()`.
|
|
10
|
+
|
|
11
|
+
## Deliverables — `spec/edoxen/meeting_*_spec.rb` (one per class)
|
|
12
|
+
|
|
13
|
+
For each of the 13 new classes:
|
|
14
|
+
|
|
15
|
+
- Construction with explicit attrs.
|
|
16
|
+
- Round-trip YAML serialization.
|
|
17
|
+
- Enum coverage where applicable (one example per enum value).
|
|
18
|
+
- Real-instance types (e.g., `Meeting#agenda` returns an `Edoxen::Agenda`).
|
|
19
|
+
|
|
20
|
+
## Deliverables — additional specs
|
|
21
|
+
|
|
22
|
+
- `spec/edoxen/meeting_collection_spec.rb` — top-level container,
|
|
23
|
+
round-trips, lookup helpers (`find_by_urn`, `find_by_identifier`).
|
|
24
|
+
- `spec/edoxen/meeting_lookup_spec.rb` — `Meeting#in_language`,
|
|
25
|
+
`Meeting#primary_localization`, `Meeting#find_agenda_item`.
|
|
26
|
+
- `spec/edoxen/schema_meeting_enum_sync_spec.rb` — every enum in
|
|
27
|
+
`schema/meeting.yaml` `$defs` matches the corresponding
|
|
28
|
+
`Edoxen::Enums::*` frozen array.
|
|
29
|
+
- `spec/edoxen/schema_meeting_model_sync_spec.rb` — every Ruby
|
|
30
|
+
Meeting-related class has matching properties/collection flags in
|
|
31
|
+
`schema/meeting.yaml` `$defs`.
|
|
32
|
+
- `spec/edoxen/cli_meetings_spec.rb` — `validate-meetings` and
|
|
33
|
+
`normalize-meetings` subcommands, spawned via `Open3.capture3`
|
|
34
|
+
against the new fixtures.
|
|
35
|
+
- `spec/edoxen/schema_validator_meeting_spec.rb` — SchemaValidator
|
|
36
|
+
constructed with the meeting schema path; happy path + each error
|
|
37
|
+
type (additionalProperties, required, enum, pattern, type).
|
|
38
|
+
|
|
39
|
+
## Style rules
|
|
40
|
+
|
|
41
|
+
- Real model instances only — no `double()`.
|
|
42
|
+
- Each example ≤ 10 lines.
|
|
43
|
+
- Round-trip examples cover every fixture.
|
|
44
|
+
|
|
45
|
+
## Acceptance criteria
|
|
46
|
+
|
|
47
|
+
- [ ] One spec file per new class (13 files).
|
|
48
|
+
- [ ] +5 additional specs (collection, lookup, schema enum sync,
|
|
49
|
+
schema model sync, CLI).
|
|
50
|
+
- [ ] +1 schema_validator spec for the meeting schema path.
|
|
51
|
+
- [ ] `bundle exec rspec` — 0 failures (existing 197 + new ≈ 80+).
|
|
52
|
+
- [ ] `bundle exec rubocop` clean.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Phase 7 — CLI integration
|
|
2
|
+
|
|
3
|
+
Priority: **P1**
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Add `validate-meetings` and `normalize-meetings` subcommands. Reuse
|
|
8
|
+
the existing `Edoxen::Cli::Batch` deep module — no scaffold
|
|
9
|
+
duplication.
|
|
10
|
+
|
|
11
|
+
## Deliverables
|
|
12
|
+
|
|
13
|
+
- `lib/edoxen/cli.rb` — two new subcommands:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
desc "validate-meetings YAML_FILE_PATTERN",
|
|
17
|
+
"Validate Meeting/Agenda YAML file(s) against schema/meeting.yaml."
|
|
18
|
+
|
|
19
|
+
def validate_meetings(pattern)
|
|
20
|
+
validator = SchemaValidator.new(meeting_schema_path)
|
|
21
|
+
Batch.run(self, pattern, header: "🔍 Validating meetings") do |file|
|
|
22
|
+
errors = validator.validate_file(file) + collect_meeting_model_errors(file)
|
|
23
|
+
errors.empty? ? Batch::Result.ok("VALID") : Batch::Result.bad(errors.map(&:to_clickable_format))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc "normalize-meetings YAML_FILE_PATTERN",
|
|
28
|
+
"Round-trip Meeting YAML file(s) through the model (--output DIR | --inplace)."
|
|
29
|
+
|
|
30
|
+
option :output, type: :string
|
|
31
|
+
option :inplace, type: :boolean
|
|
32
|
+
|
|
33
|
+
def normalize_meetings(pattern)
|
|
34
|
+
# Same shape as normalize — uses Batch.run
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- `lib/edoxen/cli.rb` private: `meeting_schema_path`,
|
|
39
|
+
`collect_meeting_model_errors`.
|
|
40
|
+
|
|
41
|
+
## Why separate subcommands
|
|
42
|
+
|
|
43
|
+
- Different root schema, different model parser.
|
|
44
|
+
- `validate` (existing) is for ResolutionCollection; backward
|
|
45
|
+
compatibility preserved.
|
|
46
|
+
- Future: a `validate-all` that auto-detects via `oneOf` if needed.
|
|
47
|
+
|
|
48
|
+
## Acceptance criteria
|
|
49
|
+
|
|
50
|
+
- [ ] `edoxen validate-meetings PATTERN` exits 0 on valid fixtures.
|
|
51
|
+
- [ ] `edoxen validate-meetings PATTERN` exits non-zero + lists
|
|
52
|
+
errors on invalid input.
|
|
53
|
+
- [ ] `edoxen normalize-meetings PATTERN --output DIR` round-trips.
|
|
54
|
+
- [ ] Existing `validate` and `normalize` (for Resolutions) unchanged.
|
|
55
|
+
- [ ] `edoxen help` lists both pairs of commands.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Phase 8 — Documentation
|
|
2
|
+
|
|
3
|
+
Priority: **P1**
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
README + CLAUDE.md reflect the Meeting/Agenda model. The canonical
|
|
8
|
+
edoxen-model README documents the new LUTAML files.
|
|
9
|
+
|
|
10
|
+
## Deliverables — edoxen gem
|
|
11
|
+
|
|
12
|
+
- `README.adoc`:
|
|
13
|
+
- New "Meeting & Agenda model" section with ASCII tree.
|
|
14
|
+
- Quick-start example showing both Resolution and Meeting parsing.
|
|
15
|
+
- Cross-link to the canonical LUTAML files.
|
|
16
|
+
- `CLAUDE.md`:
|
|
17
|
+
- Architecture section mentions `Meeting`, `Agenda`, the meeting
|
|
18
|
+
schema, and the separate CLI subcommands.
|
|
19
|
+
- Audit checklist extended: schema_model_sync_spec covers both
|
|
20
|
+
`schema/edoxen.yaml` and `schema/meeting.yaml`.
|
|
21
|
+
|
|
22
|
+
## Deliverables — edoxen-model
|
|
23
|
+
|
|
24
|
+
- `README.adoc`:
|
|
25
|
+
- File index extended with all new `.lutaml` files.
|
|
26
|
+
- New "Meeting model" section explaining the Meeting/Agenda split,
|
|
27
|
+
URN linking to Resolutions, and the localization pattern.
|
|
28
|
+
- Sample meeting YAML mirroring one of the gem's fixtures.
|
|
29
|
+
|
|
30
|
+
## Acceptance criteria
|
|
31
|
+
|
|
32
|
+
- [ ] Both READMEs updated.
|
|
33
|
+
- [ ] edoxen CLAUDE.md updated.
|
|
34
|
+
- [ ] Sample YAML in edoxen-model README would pass
|
|
35
|
+
`edoxen validate-meetings`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Phase 9 — Verify, PR, merge, release
|
|
2
|
+
|
|
3
|
+
Priority: **P0**
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Land everything via PRs. Verify GHA passes. Merge. Bump edoxen minor
|
|
8
|
+
(new feature) and release.
|
|
9
|
+
|
|
10
|
+
## Branch / PR plan
|
|
11
|
+
|
|
12
|
+
| Repo | Branch | PRs against |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `metanorma/edoxen-model` | `feat/meeting-agenda-model` | main |
|
|
15
|
+
| `metanorma/edoxen` | `feat/meeting-agenda-model` | main |
|
|
16
|
+
|
|
17
|
+
Order: edoxen-model first (canonical), then edoxen (mirror).
|
|
18
|
+
|
|
19
|
+
## Verification gates
|
|
20
|
+
|
|
21
|
+
Per branch, before pushing:
|
|
22
|
+
|
|
23
|
+
- [ ] `bundle exec rspec` — 0 failures.
|
|
24
|
+
- [ ] `bundle exec rubocop` — 0 offenses.
|
|
25
|
+
- [ ] `bundle exec exe/edoxen validate-meetings "spec/fixtures/meetings/*.yaml"` — clean.
|
|
26
|
+
- [ ] `bundle exec exe/edoxen normalize-meetings --output /tmp/out` — round-trips.
|
|
27
|
+
- [ ] No AI attribution in any commit.
|
|
28
|
+
- [ ] No TODOs / FIXMEs in committed source.
|
|
29
|
+
- [ ] No transient files (.DS_Store, .bak, .swp).
|
|
30
|
+
|
|
31
|
+
## Post-merge
|
|
32
|
+
|
|
33
|
+
- [ ] Trigger `gh workflow run release.yml --ref main -f next_version=minor`
|
|
34
|
+
on `metanorma/edoxen`.
|
|
35
|
+
- [ ] Confirm new version appears on rubygems.org.
|
|
36
|
+
- [ ] Confirm tag `vX.Y.Z` pushed.
|
|
37
|
+
|
|
38
|
+
## Why minor (not patch)
|
|
39
|
+
|
|
40
|
+
The new Meeting/Agenda model is an additive feature. No breaking
|
|
41
|
+
change to existing Resolution APIs. Semver minor on the 0.x line.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Phase 10 — Future enhancements (not in this release)
|
|
2
|
+
|
|
3
|
+
Priority: **P2**
|
|
4
|
+
|
|
5
|
+
These are documented as known-future-work; not executed in this pass.
|
|
6
|
+
Each one would be its own PR.
|
|
7
|
+
|
|
8
|
+
## P2.1 — Per-schedule-item localization
|
|
9
|
+
|
|
10
|
+
`ScheduleItemLocalization` carrying per-language `event` and
|
|
11
|
+
`description`. Triggered when a real consumer needs WG meeting titles
|
|
12
|
+
in FR alongside EN.
|
|
13
|
+
|
|
14
|
+
## P2.2 — Meeting series
|
|
15
|
+
|
|
16
|
+
`MeetingSeries` carrying `{identifier, name, description}` with
|
|
17
|
+
`Meeting.series_ref: String` (URN). Triggered when navigation across
|
|
18
|
+
ordinals becomes a real use case (ISO/TC 154 plenary history page).
|
|
19
|
+
|
|
20
|
+
## P2.3 — Contribution model
|
|
21
|
+
|
|
22
|
+
Replace flat `Person.contributors` with a `Contribution` class
|
|
23
|
+
matching the comment in the original draft (`'contributors should
|
|
24
|
+
defer to Contribution model`). Triggered when we need to distinguish
|
|
25
|
+
"presented" vs "authored" vs "reviewed" contributions per meeting.
|
|
26
|
+
|
|
27
|
+
## P2.4 — Auto-detect document type in CLI
|
|
28
|
+
|
|
29
|
+
A single `edoxen validate PATTERN` that auto-detects
|
|
30
|
+
ResolutionCollection vs Meeting via JSON-Schema `oneOf`. Triggered
|
|
31
|
+
when users complain about remembering two subcommand names.
|
|
32
|
+
|
|
33
|
+
## P2.5 — Cross-document link validation
|
|
34
|
+
|
|
35
|
+
A spec/assertion that every `Meeting.resolution_refs[*]` URN has a
|
|
36
|
+
matching `ResolutionCollection` somewhere in the data set, and vice
|
|
37
|
+
versa. Triggered when broken links cause real bugs.
|
|
38
|
+
|
|
39
|
+
## P2.6 — Attendance roster
|
|
40
|
+
|
|
41
|
+
`Meeting.attendance: Attendance[]` carrying per-person attendance
|
|
42
|
+
records (present/absent/apologies + affiliation). Triggered when a
|
|
43
|
+
consumer needs the full attendance record, not just officers.
|
|
44
|
+
|
|
45
|
+
## P2.7 — Vote records
|
|
46
|
+
|
|
47
|
+
`VoteRecord { resolution_ref, person, vote: VoteType }` linked to a
|
|
48
|
+
Meeting. Triggered when ballot-by-ballot transparency becomes a
|
|
49
|
+
requirement.
|
|
50
|
+
|
|
51
|
+
## P2.8 — Minutes narrative
|
|
52
|
+
|
|
53
|
+
A `Minutes` class distinct from `Agenda`, capturing the actual
|
|
54
|
+
narrative of what was said (linked Markdown / Asciidoc blocks per
|
|
55
|
+
agenda item). Triggered when the OIML `meetings/` directory starts
|
|
56
|
+
receiving Bulletin scans.
|
|
57
|
+
|
|
58
|
+
## P2.9 — federation (multi-body)
|
|
59
|
+
|
|
60
|
+
A `Federation` or `JointMeeting` class for meetings organized by
|
|
61
|
+
multiple bodies (e.g., a JWG with TC 154 + TC 307). Triggered when
|
|
62
|
+
real joint-meeting data lands.
|
|
63
|
+
|
|
64
|
+
## P2.10 — IANA / ISO 3166-1 / ISO 639-3 / ISO 15924 reference data
|
|
65
|
+
|
|
66
|
+
Built-in lookup tables to validate `country_code`, `language_code`,
|
|
67
|
+
`script` against the actual standard. Triggered when typo-rate
|
|
68
|
+
becomes painful.
|
|
69
|
+
|
|
70
|
+
## Acceptance criteria
|
|
71
|
+
|
|
72
|
+
This file documents intent only; nothing to land in this release.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# TODO.meeting-agenda — index
|
|
2
|
+
|
|
3
|
+
The work to land a canonical Meeting + Agenda + AgendaItem model
|
|
4
|
+
linkable to Resolutions/Decisions across `edoxen` and `edoxen-model`.
|
|
5
|
+
|
|
6
|
+
## Phases
|
|
7
|
+
|
|
8
|
+
| # | File | Priority | Status |
|
|
9
|
+
|---|---|---|---|
|
|
10
|
+
| 1 | [01-design-decisions.md](01-design-decisions.md) | P0 | done |
|
|
11
|
+
| 2 | [02-lutaml-canonical.md](02-lutaml-canonical.md) | P0 | pending |
|
|
12
|
+
| 3 | [03-ruby-models.md](03-ruby-models.md) | P0 | pending |
|
|
13
|
+
| 4 | [04-schema.md](04-schema.md) | P0 | pending |
|
|
14
|
+
| 5 | [05-fixtures.md](05-fixtures.md) | P0 | pending |
|
|
15
|
+
| 6 | [06-specs.md](06-specs.md) | P0 | pending |
|
|
16
|
+
| 7 | [07-cli.md](07-cli.md) | P1 | pending |
|
|
17
|
+
| 8 | [08-docs.md](08-docs.md) | P1 | pending |
|
|
18
|
+
| 9 | [09-verify-and-release.md](09-verify-and-release.md) | P0 | pending |
|
|
19
|
+
| 10 | [10-future-enhancements.md](10-future-enhancements.md) | P2 | documented |
|
|
20
|
+
|
|
21
|
+
## Execution order
|
|
22
|
+
|
|
23
|
+
Phases 2 → 9 are executed in order. Phase 10 documents known-future-
|
|
24
|
+
work only.
|
|
25
|
+
|
|
26
|
+
## Branches
|
|
27
|
+
|
|
28
|
+
- `metanorma/edoxen-model` ← `feat/meeting-agenda-model` (Phase 2)
|
|
29
|
+
- `metanorma/edoxen` ← `feat/meeting-agenda-model` (Phases 3–8)
|
|
30
|
+
- Phase 9 merges both, then releases `edoxen` as a minor bump.
|
data/lib/edoxen/action.rb
CHANGED
|
@@ -7,11 +7,5 @@ module Edoxen
|
|
|
7
7
|
attribute :type, :string, values: Enums::ACTION_TYPE
|
|
8
8
|
attribute :date_effective, ResolutionDate
|
|
9
9
|
attribute :message, :string
|
|
10
|
-
|
|
11
|
-
key_value do
|
|
12
|
-
map "type", to: :type
|
|
13
|
-
map "date_effective", to: :date_effective
|
|
14
|
-
map "message", to: :message
|
|
15
|
-
end
|
|
16
10
|
end
|
|
17
11
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# The business-order document of a Meeting. Distinct from the
|
|
5
|
+
# timetable (Schedule) — the Agenda orders topics; the Schedule
|
|
6
|
+
# orders time slots.
|
|
7
|
+
#
|
|
8
|
+
# An Agenda may be versioned independently of the Meeting: a draft
|
|
9
|
+
# agenda circulates weeks before; a final agenda at meeting time;
|
|
10
|
+
# an amended agenda if items are added during the meeting. The
|
|
11
|
+
# `status` field captures that lifecycle.
|
|
12
|
+
class Agenda < Lutaml::Model::Serializable
|
|
13
|
+
attribute :identifier, StructuredIdentifier, collection: true
|
|
14
|
+
attribute :status, :string, values: Enums::AGENDA_STATUS
|
|
15
|
+
attribute :source_doc, :string
|
|
16
|
+
attribute :items, AgendaItem, collection: true
|
|
17
|
+
attribute :opening_session, ScheduleItem
|
|
18
|
+
attribute :closing_session, ScheduleItem
|
|
19
|
+
|
|
20
|
+
# Find an agenda item by its label (e.g., "5.2"). Returns nil
|
|
21
|
+
# when no item matches.
|
|
22
|
+
def find_item(label)
|
|
23
|
+
items&.find { |item| item.label == label.to_s }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Edoxen
|
|
4
|
+
# One entry on an Agenda. `label` is the visible identifier (e.g.,
|
|
5
|
+
# "5.2"); `kind` discriminates numbered/unnumbered/header/opening/
|
|
6
|
+
# closing; `outcome` records what happened; `resolution_ref`
|
|
7
|
+
# optionally links to the URN of the resolution this item produced.
|
|
8
|
+
class AgendaItem < Lutaml::Model::Serializable
|
|
9
|
+
attribute :label, :string
|
|
10
|
+
attribute :kind, :string, values: Enums::AGENDA_ITEM_KIND
|
|
11
|
+
attribute :title, :string
|
|
12
|
+
attribute :description, :string
|
|
13
|
+
attribute :references, Reference, collection: true
|
|
14
|
+
attribute :outcome, :string, values: Enums::AGENDA_ITEM_OUTCOME
|
|
15
|
+
attribute :resolution_ref, :string
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/edoxen/approval.rb
CHANGED
|
@@ -8,12 +8,5 @@ module Edoxen
|
|
|
8
8
|
attribute :degree, :string, values: Enums::APPROVAL_DEGREE
|
|
9
9
|
attribute :date, ResolutionDate
|
|
10
10
|
attribute :message, :string
|
|
11
|
-
|
|
12
|
-
key_value do
|
|
13
|
-
map "type", to: :type
|
|
14
|
-
map "degree", to: :degree
|
|
15
|
-
map "date", to: :date
|
|
16
|
-
map "message", to: :message
|
|
17
|
-
end
|
|
18
11
|
end
|
|
19
12
|
end
|