openehr-rails 0.4.1 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0bdb58aed10113d7fae2a48ef3819f0d999d8f9b5bc03adc8647eef8b357371
4
- data.tar.gz: 78416c8a3208456cd7cc3d33a66354b623404a29044a78f94020d5098ac9faa0
3
+ metadata.gz: 69d9f917d3eb32a92e7d47aeb3f43bf8992a6a44f8ceb49320a1e5a6130ecebf
4
+ data.tar.gz: 1f39015dbd51241ea4dca624bd89e988a82bf62ef3a02964e54a343b6e8ccbf1
5
5
  SHA512:
6
- metadata.gz: 4e2a343f8b63041b84dc4e364bb1f2d6a6bf4c586d377b43cd5e79c082dc7cb90c114084ddb325e3468d118b8d5fafef8c2171126d056ab02cf21282f2e0e2af
7
- data.tar.gz: 6f90fd8358d753c85bcc3271830804e47837191c40b115058c75f1f53aae9995bc5f9e60919337df08d9367c81483fae3df5eec0b73084b0e94dc3277432ce17
6
+ metadata.gz: 5acec88b6d3dee1ab32167080639a81052911eee90043ae09474e083fa0c92e081da69dade909848da04743cffc96603e887765a8efaff29d6172f873e93a074
7
+ data.tar.gz: 1c134c5a1147f54ee8628b62efdcfcb867f257f634a5094a4f0aacb2af460ac28cfcaa011457017610262686437041ec85045186f9c05acb034ed4080e6ec793
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something behaves incorrectly
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Summary
10
+
11
+ <!-- One or two sentences: what's wrong. -->
12
+
13
+ ## Environment
14
+
15
+ - `openehr-rails` version: <!-- e.g. 0.4.1 -->
16
+ - `openehr` version: <!-- e.g. ~> 2.3 -->
17
+ - Rails version: <!-- e.g. 8.1 -->
18
+ - Ruby version: <!-- e.g. 3.3, 3.4, 4.0 -->
19
+ - If the report involves an OPT/ADL file: the generating tool and its
20
+ version (e.g. Better Archetype Designer / Ocean Template Designer 2.6 /
21
+ ADL Workbench / LinkEHR / HMC).
22
+
23
+ ## Reproduction
24
+
25
+ <!-- Minimal runnable code (a generator invocation, a snippet against a model, an
26
+ .opt/.adl fixture) that reproduces the bug from a clean checkout. -->
27
+
28
+ ```ruby
29
+ ```
30
+
31
+ ## Expected vs Actual
32
+
33
+ - Expected:
34
+ - Actual:
35
+
36
+ ## Root cause
37
+
38
+ <!-- file:line, if known. Leave blank if not yet investigated -- explore/plan happens
39
+ after filing, not before. -->
40
+
41
+ ## Proposed fix
42
+
43
+ <!-- Optional at filing time; fill in once a plan exists. -->
44
+
45
+ ## Acceptance criteria
46
+
47
+ <!-- Spec-verifiable. E.g.: -->
48
+ - [ ] A reproduction spec for this bug goes red on the current code, then green after
49
+ the fix (see CLAUDE.md's "Ticket-driven workflow": bug = red-first).
@@ -0,0 +1 @@
1
+ blank_issues_enabled: true
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: Enhancement
3
+ about: Propose new behavior or a change to existing behavior
4
+ title: ''
5
+ labels: enhancement
6
+ assignees: ''
7
+ ---
8
+
9
+ ## Motivation
10
+
11
+ <!-- Why this is needed -- what's painful or missing today, and for whom. -->
12
+
13
+ ## Current behavior
14
+
15
+ <!-- What happens now. file:line references if known. -->
16
+
17
+ ## Proposed behavior
18
+
19
+ <!-- What should happen instead/additionally. -->
20
+
21
+ ## Acceptance criteria
22
+
23
+ <!-- Spec-verifiable. E.g.: -->
24
+ - [ ] A spec for the new behavior goes red on the current code, then green once
25
+ implemented (see CLAUDE.md's "Ticket-driven workflow": enhancement = red-first).
26
+
27
+ ## Compatibility notes
28
+
29
+ <!-- Surface this change touches -- check all that apply, and say how: -->
30
+ - [ ] Runtime behavior
31
+ - [ ] Public API
32
+ - [ ] Install-time dependency (gemspec, supported Ruby/Rails versions)
33
+ - [ ] Generator output (`lib/generators/**/templates/` -- migrations/scaffolding
34
+ shipped to and expanded inside host applications; counts as shipped product, see
35
+ CLAUDE.md)
36
+
37
+ <!-- semver impact, if known: -->
@@ -18,7 +18,7 @@ jobs:
18
18
  env:
19
19
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails_${{ matrix.rails }}.gemfile
20
20
  steps:
21
- - uses: actions/checkout@v4
21
+ - uses: actions/checkout@v7
22
22
  - uses: ruby/setup-ruby@v1
23
23
  with:
24
24
  ruby-version: ${{ matrix.ruby }}
@@ -31,7 +31,7 @@ jobs:
31
31
  runs-on: ubuntu-latest
32
32
  needs: spec
33
33
  steps:
34
- - uses: actions/checkout@v4
34
+ - uses: actions/checkout@v7
35
35
  - uses: ruby/setup-ruby@v1
36
36
  with:
37
37
  ruby-version: '4.0'
@@ -49,7 +49,7 @@ jobs:
49
49
  runs-on: ubuntu-latest
50
50
  needs: spec
51
51
  steps:
52
- - uses: actions/checkout@v4
52
+ - uses: actions/checkout@v7
53
53
  - uses: ruby/setup-ruby@v1
54
54
  with:
55
55
  ruby-version: '4.0'
@@ -12,20 +12,22 @@ jobs:
12
12
  ci:
13
13
  uses: ./.github/workflows/ci.yml
14
14
 
15
- release:
16
- name: Release to RubyGems
15
+ build:
16
+ name: Build gem artifact
17
17
  needs: ci
18
18
  runs-on: ubuntu-latest
19
- permissions:
20
- id-token: write # required for RubyGems Trusted Publishing (OIDC)
21
- contents: read
22
19
  steps:
23
- - uses: actions/checkout@v4
20
+ - uses: actions/checkout@v7
24
21
  - uses: ruby/setup-ruby@v1
25
22
  with:
26
23
  ruby-version: '4.0'
27
24
  bundler-cache: true
28
25
  - name: release:check (clean tree, sibling-file tracking, gemspec validity)
29
26
  run: bundle exec rake release:check
30
- - name: Release
31
- uses: rubygems/release-gem@v1
27
+ - name: Build gem
28
+ run: bundle exec rake build
29
+ - name: Upload gem artifact
30
+ uses: actions/upload-artifact@v7
31
+ with:
32
+ name: gem
33
+ path: pkg/*.gem
data/CHANGELOG.md CHANGED
@@ -7,8 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.6.0] - 2026-08-26
11
+
12
+ ### Added
13
+ - `OpenehrRails::Fhir::FshGenerator` renders one FHIR Shorthand profile per
14
+ openEHR entry, including metadata, archetype codes, value constraints,
15
+ component slicing, quantity units, fixed terminology codes, and required
16
+ external value-set bindings. Observation-mapped output is Sushi-verified;
17
+ multi-leaf non-Observation entries have a known structural gap tracked as #33.
18
+
19
+ ### Changed
20
+ - `FshGenerator` no longer depends on `active_support/core_ext/string`; the
21
+ three String helpers it used are now small plain-Ruby private methods,
22
+ verified byte-identical output. Restores the Rails-independence this class
23
+ was designed for (see `anlage`'s `docs/design/fsh-plan.md`), so it can move
24
+ wholesale to a future satellite gem without a Rails dependency riding along.
25
+
26
+ ## [0.5.0] - 2026-08-25
27
+
28
+ ### Added
29
+ - `FieldExtractor` fields now always include `value_set_uri` for external
30
+ `C_CODE_REFERENCE` value sets and `code_bindings` for ontology-level fixed-code
31
+ bindings. `OpenehrRails::Opt::Parser` temporarily enriches OPT terminology objects
32
+ with `term_bindings` until `skoba/openehr-ruby#31` is available upstream.
33
+
34
+ ### Fixed
35
+ - `ProfileGenerator` now emits a required FHIR `binding.valueSet` for
36
+ `DV_CODED_TEXT` fields constrained by `C_CODE_REFERENCE`; these fields previously
37
+ received no binding. Existing local-code-list bindings remain strength-only. Host
38
+ applications must regenerate cached `app/fhir/profiles/*.json` files to receive
39
+ the new bindings.
40
+ - For multi-alternative `value` constraints, `FieldExtractor` now prefers a
41
+ `C_CODE_REFERENCE`-backed alternative. This can change the extracted `rm_type` and
42
+ scaffold column behavior from `DV_TEXT` to `DV_CODED_TEXT` for affected templates.
43
+
44
+ ### Changed
45
+ - Raised the `openehr` dependency floor to 2.3.1 because 2.3.0 cannot parse
46
+ `C_CODE_REFERENCE` constraints (`skoba/openehr-ruby#30`).
47
+
10
48
  ## [0.4.1] - 2026-08-22
11
49
 
50
+ Errata: this release includes minor-level changes (Ruby >= 3.3 requirement,
51
+ OpenehrRails namespace consolidation) and should be treated as a minor release
52
+ when upgrading from 0.4.0.
53
+
12
54
  ### Fixed
13
55
  - `FieldExtractor` now resolves embedded-archetype element terminology in the nearest
14
56
  enclosing `C_ARCHETYPE_ROOT`, preventing both silent at-code collisions with the outer
data/CLAUDE.md CHANGED
@@ -16,12 +16,111 @@ Follow the TDD framework as advocated by t-wada:
16
16
  - Write tests for areas of concern first
17
17
  - opt files must not be changed automatically.
18
18
 
19
+ ## Ticket-driven workflow
20
+
21
+ - A change touching runtime behavior, public API, or install-time dependencies requires
22
+ a GitHub Issue filed before work starts — no code-change commit without one. Changes
23
+ limited to docs, CI, or dev-tooling config are Issue-optional.
24
+ - **This repo's surface for that rule is not just `lib/`**: `lib/generators/**/templates/`
25
+ (the migrations and scaffolding code shipped to, and expanded inside, host
26
+ applications) is shipped product too. Changes there are Issue-required and subject to
27
+ the same semver judgment as any other runtime-behavior change.
28
+ - Write Acceptance criteria in a spec-verifiable form.
29
+ - Three resolution kinds, signaled in the PR body and in spec comments:
30
+ - **bug** — a reproduction spec goes red first, then gets fixed green.
31
+ - **enhancement** — a new-behavior spec goes red first, then gets implemented green.
32
+ - **pin/hardening** — fixing an existing property in place, where red isn't possible.
33
+ Mark this `regression pin` in a spec comment instead of staging a fake red.
34
+ - 1 issue = 1 branch = 1 PR (reaffirms the existing convention); the PR closes with
35
+ `Fixes #N`. A `docs/design/` plan doc opens with the issue number.
36
+
19
37
  ## Working with implementation agents (e.g. Codex)
20
38
 
21
39
  Codex delivers only working-tree changes; it does not commit. Claude Code reviews the
22
40
  diff, then commits, recording the implementer in a commit message trailer (e.g.
23
41
  `Implemented-by: Codex`).
24
42
 
43
+ ## Release convention
44
+
45
+ Before tagging, make the final semver determination from the actual content of
46
+ `[Unreleased]`, not from a pre-assigned version number. If the instructed version number
47
+ contradicts the actual content, stop instead of tagging and ask for re-arbitration.
48
+
49
+ ## Verification
50
+
51
+ - **Verify against the repo before recording a fact in it**, even when a prompt or an
52
+ earlier report already stated it as true - a premise that went unverified once tends
53
+ to get repeated, not corrected, if the next write also skips checking (e.g. "CI is
54
+ unconfigured" repeated across two turns before anyone ran `gh run list`).
55
+ - **The document a gate report points to must be pushed**, not just committed locally,
56
+ before the report is sent - a local-only SHA is unverifiable by anyone reading the
57
+ report. (Added 2026-08-25, after a gate report cited two docs-only commits - the #30
58
+ issue-filing log and its design doc - that were still local-only `master` commits,
59
+ not yet on `origin/master`.)
60
+ - **After a git command appears to lose a file, search git's own storage
61
+ (`git stash show`/`stash@{n}^3`, `git reflog`, `git fsck --unreachable`) before
62
+ reconstructing content from memory.** Reconstruction from a model's own memory of
63
+ a file it recently read is a last resort, and if used, the result must be
64
+ independently diffed against the recovered original before trusting it - matching
65
+ by eye is not enough. (Added 2026-08-25, after a `git stash push -u` with a
66
+ multi-pathspec argument printed a pathspec error for one untracked file and that
67
+ file appeared to vanish from both the working tree and the stash's summary output;
68
+ it was in fact captured in the stash's untracked-files commit the whole time - the
69
+ error was cosmetic. The file was reconstructed from the session's own recent read
70
+ before that was confirmed, and only verified byte-identical against the actual
71
+ stashed copy afterward - the right outcome, but by the wrong order of operations.)
72
+
73
+ ## Repository-context-dependent commands confirm their target explicitly
74
+
75
+ A command whose target (repository, branch, or resumed session) is decided by
76
+ ambient state - cwd, current branch, or session history - rather than an
77
+ explicit argument, must have that target pinned before it runs; never assume
78
+ the shell or session is still where an earlier step left it.
79
+
80
+ - If the tool has an explicit target option, always use it: `git` takes a `cd`
81
+ to the intended directory on the same command line (or `-C <path>`); `gh`
82
+ takes `-R <owner>/<repo>` (or `--repo`) on every invocation.
83
+ - If the tool has no such option (e.g. `codex exec`, `codex exec resume`),
84
+ print `pwd` immediately before the call and confirm it names the intended
85
+ repository first.
86
+ - Before adopting a new repository-context-dependent command for the first
87
+ time, decide how this principle applies to it before using it.
88
+
89
+ (Generalized 2026-08-24, consolidating this repo's prior narrower
90
+ `checkout`/`pull` rule with `openehr-ruby`'s branch-confirmation rule, after a
91
+ third incident of the same class surfaced the need for one shared principle
92
+ covering non-git tools too. Three incidents on record: (1) this repo,
93
+ 2026-08-22 - a mistaken `checkout`/`pull` ran against the wrong repo, caught
94
+ and self-reported immediately, no lasting effect; (2) `openehr-ruby`,
95
+ 2026-08-23 - a docs-only commit intended for `master` landed on a
96
+ checked-out PR feature branch instead; (3) `anlage`, 2026-08-24 - `codex exec
97
+ resume --last`, run after cwd had silently drifted back to `openehr-ruby`,
98
+ resumed an unrelated stale session in the wrong repo instead of the intended
99
+ one; Codex itself detected the mismatch and made no changes, so there was no
100
+ lasting effect, but the near-miss is what prompted this generalization. See
101
+ `openehr-ruby`'s own copy of this rule and its `docs/backlog.md` entry
102
+ logging the underlying structural fix under consideration - one
103
+ worktree/session per repo instead of per-command vigilance.)
104
+
105
+ ## Cross-repository implementation work needs its own authorization gate
106
+
107
+ The moment it becomes clear that the actual implementation target for a piece of
108
+ work is a repository other than this one, stop before starting that work and insert
109
+ a gate report confirming the implementation target, rather than sliding from
110
+ read-only cross-repo reference (already permitted for design/evidence-gathering)
111
+ into cross-repo write work on the strength of that same permission. Correctly
112
+ targeting a command once you've decided to cross the boundary (the previous
113
+ section's `cd`/`-C`/`-R` discipline) is a different concern from being authorized to
114
+ cross it at all - following the operational convention precisely does not retroactively
115
+ authorize the crossing.
116
+
117
+ (Added 2026-08-26, alongside the `#32`/`#33` FSH-generator work continuation, to
118
+ name explicitly a distinction that the existing repository-context rule left
119
+ implicit: it governs *how* to target a cross-repo command correctly, not *whether*
120
+ crossing into implementation work on another repository has actually been
121
+ authorized for the task at hand. A matching line is planned for `anlage`'s own
122
+ `CLAUDE.md` in a later batch - not yet added there as of this entry.)
123
+
25
124
  ## Project Overview
26
125
 
27
126
  This is `openehr-rails`, a Rails engine gem that turns an openEHR Operational Template (`.opt`, ADL2/XML) into a working Rails resource in one command: `rails generate openehr:scaffold path/to/template.opt --fhir` emits a model, migration, controller, views, i18n locale, and (with `--fhir`) HL7 FHIR R5 `StructureDefinition` profiles. Generated models persist both as typed columns (for forms/search) and as full openEHR RM data (canonical JSON + a typed node graph with immutable-append versioning), and are queryable via a growing AQL surface. A mountable admin engine (`/openehr`) provides template upload/management, runtime scaffolding, and a FHIR R5 facade. Legacy ADL-archetype-only generators (model/controller/migration/helper/assets/i18n/template/template_model, based on `Openehr::Generators::ArchetypedBase`) have been removed — OPT is the only supported input format for scaffolding.
@@ -69,6 +168,28 @@ This is `openehr-rails`, a Rails engine gem that turns an openEHR Operational Te
69
168
  - `spec/openehr_rails/{opt,rm,fhir}/`, `spec/openehr_rails/*_spec.rb` - runtime library specs.
70
169
  - `spec/models/openehr_template_spec.rb`, `spec/unit/opt_parser_spec.rb` - registry model and parser specs.
71
170
  - OPT fixtures live in `spec/generators/templates/` and `spec/templates/`; do not hand-edit an existing `.opt` fixture (add a new one instead) — **opt files must not be changed automatically.**
171
+ - Fixtures fall into four kinds; each fixture's leading comment must say which kind it is:
172
+ - **real** — a genuine artifact (CKM export, Archetype Designer output, a real host-app
173
+ template), used as-is.
174
+ - **reduced** — a trimmed-down real artifact; the comment must name the real source it
175
+ was reduced from.
176
+ - **synthetic** — hand-authored, not derived from any real artifact. real/reduced are
177
+ preferred by default; synthetic is only for structural test cases whose reproduction
178
+ conditions can't be controlled with a real artifact. The leading comment must say it's
179
+ synthetic and cite its design authority (e.g. a design doc section). Archetype
180
+ IDs/at-codes should use self-evidently invented names that can't be mistaken for real
181
+ ones — don't rename an existing fixture to fix this after the fact; its at-codes/
182
+ archetype IDs are reference anchors other specs/docs already point to, and freezing
183
+ those anchors takes priority.
184
+ - **security** — built to exercise an attack/abuse case; the comment must say it is not
185
+ a clinical artifact.
186
+ - A fixture's provenance comment must describe its lineage as measured (checked against
187
+ the actual design/implementation record), not as instructed — if an instructed lineage
188
+ doesn't match what actually went into the fixture, write it to match reality instead.
189
+ - Example: `spec/templates/lab_result_report_reduced.opt` is **synthetic** (design
190
+ authority: `docs/design/fix-terminology-scope-plan.md` §4; lineage confirmed
191
+ 2026-08-22) — its filename says "reduced" for historical reasons, but per this
192
+ convention its actual kind is synthetic; the name stays as-is (reference anchor).
72
193
 
73
194
  ## Development Notes
74
195
 
data/docs/backlog.md ADDED
@@ -0,0 +1,157 @@
1
+ # Backlog
2
+
3
+ Non-blocking follow-ups noted during work on the upstream sprint queue. Not scheduled;
4
+ pick up when the relevant gate opens or when convenient alongside other work in the same
5
+ area. No code changes accompany entries here — this file is a record only.
6
+
7
+ ## CI status (verified, not a follow-up item)
8
+
9
+ Both `.github/workflows/ci.yml` (`rspec` matrix on Ruby 3.3/3.4/4.0 x Rails
10
+ 7.2/8.0/8.1, plus `demo-smoke` and `application template smoke test` jobs; triggers
11
+ on `push` to `master`, all `pull_request`s, and `workflow_call`) and
12
+ `.github/workflows/release.yml` (triggers on `v*` tags, reuses `ci.yml` via
13
+ `workflow_call`, then runs a RubyGems release job) exist on `master` — introduced at
14
+ `412712d` (2026-08-12) and `2263298` (2026-08-13) respectively — and were confirmed
15
+ actually running, not just present:
16
+
17
+ - PR #26 (`fix/field-extractor-terminology-scope`): `pull_request` runs `32549486071`
18
+ and `32550157259` (2026-08-22, both success), post-merge `master` `push` run
19
+ `32550193330` (success). The merge landed (03:53:04Z) about 1m43s before the final
20
+ PR run finished (03:54:47Z) — CI ran and passed, but this doesn't demonstrate
21
+ merge-blocking enforcement.
22
+ - Tag `v0.4.1`: `release.yml` run `32550344344` (2026-08-22T03:56:26Z) — all 11 reused
23
+ CI jobs green, but the run's overall conclusion is **failure**: the `Release to
24
+ RubyGems` job's `Release` step (`rubygems/release-gem@v1`) fails at "Configure
25
+ trusted publishing credentials" ("No trusted publisher configured for this workflow
26
+ found on https://rubygems.org for audience rubygems.org"); the actual gem-push
27
+ sub-steps are skipped, so CI never attempted to publish. rubygems.org's 0.4.1
28
+ listing was published by the human running `gem push` manually — no harm to the
29
+ actual release, only to the workflow run's color. Same failure shape on every tag
30
+ to date: v0.3.0 (runs `31655140867`, `31655495221`, `31655778244`) and v0.4.0 (run
31
+ `31660884406`), both failure. **This is structural, not incidental**: as long as
32
+ `release.yml` keeps a live RubyGems-publish step while the actual publish stays a
33
+ manual human `gem push` (the current, intended operating model), every future tag
34
+ push will reproduce the same red run. See "Release automation" below.
35
+ - Most recent `master` push at the time of this record (`4055ec3`): CI run
36
+ `32569774700`, success.
37
+ - **Tag `v0.5.0` (2026-08-25, #30): `release.yml` run
38
+ [32831263811](https://github.com/skoba/openehr-rails/actions/runs/32831263811) --
39
+ overall conclusion **success**, all 12 jobs green (9 reused `ci.yml` spec-matrix
40
+ jobs, `demo smoke`, `application template smoke test`, `Build gem artifact`).**
41
+ First fully-green tag-push run on record -- confirms the "Release automation fix
42
+ landed (2026-08-23, PR #28)" note below actually fixed the structural red
43
+ described above, not just that the workflow file changed (`v0.4.1`/`v0.4.0`/`v0.3.0`
44
+ were all red at the now-removed RubyGems-publish step; `v0.5.0` has no such step to
45
+ fail at). Downloaded the run's `gem` artifact and independently built
46
+ `pkg/openehr-rails-0.5.0.gem` locally at the same tagged commit (`69db63f`): both
47
+ 197632 bytes, identical sha256
48
+ `e07815bd1c86736403cbb558fec869fbe04666f695e6cc12a41dad9be77230e6`. RubyGems
49
+ publish (human `gem push`, per "Release automation" below) confirmed complete via
50
+ `https://rubygems.org/api/v1/versions/openehr-rails.json`: version `0.5.0`,
51
+ `created_at: 2026-08-25T09:26:15.342Z`, published `sha` field matches the same
52
+ sha256 above exactly -- local build, CI artifact, and the published gem are all
53
+ byte-identical.
54
+
55
+ Recorded here after this file previously claimed "this repo currently has no CI
56
+ workflow gating pull requests" — wrong on both existence and execution. This file is
57
+ the primary record for openehr-rails CI/release facts; openehr-ruby's own
58
+ `docs/backlog.md` defers to it rather than duplicating (see that file's 2026-08-23
59
+ correction, commit `bf17be7`) — do not duplicate this record back into openehr-ruby.
60
+
61
+ **Release automation fix landed (2026-08-23, PR #28, `Fixes #27`)**: `release.yml`'s
62
+ RubyGems-publish job is gone; tag pushes now run `ci` + `rake build` +
63
+ `actions/upload-artifact` only (see "Release automation" below for the item this
64
+ closes). Also bumped `actions/checkout` v4 -> v7 to clear the Node 20 deprecation
65
+ warning noted in the v0.4.1 entry above. Verified, not just green: PR #28's own
66
+ `pull_request` run `32611109196` — all 11 `ci.yml` jobs success, and the Node 20
67
+ deprecation annotation is gone from every one of those 11 jobs (checked via the
68
+ GitHub check-runs annotations API, not just the run log — each job's annotations
69
+ array is empty, `[]`). Post-merge `master` `push` run `32611237453` — success. The
70
+ renamed `build` job in `release.yml` (with `actions/checkout@v7` and the new
71
+ `actions/upload-artifact@v7` step) is tag-triggered only and was not exercised by
72
+ either of those runs; per PR #28's own body, no test tag was pushed to verify it —
73
+ that verification is deferred to the next real release (>= 0.5.0), not simulated.
74
+
75
+ ## Release automation
76
+
77
+ **Done (2026-08-23, PR #28)** — kept below for the original rationale; see the CI
78
+ status entry above for verification details.
79
+
80
+ - **Unify the release path**: remove `release.yml`'s RubyGems-publish job/step and
81
+ change tag-push handling to CI + `gem build` + artifact upload only, so the workflow
82
+ matches how releases are actually done today (human-gated `gem push`) and stops
83
+ going red on every tag for a step that was never meant to run automatically.
84
+ Automatic publishing via RubyGems Trusted Publishing can be re-evaluated when the
85
+ project's planned December public release and external-contributor model are
86
+ actually being designed — revisit the tag-before-inventory convention (`CLAUDE.md`'s
87
+ "Release convention" section) together with that decision at the same time, since
88
+ automatic publishing changes what "ready to tag" needs to mean. Timing: before the
89
+ next release (>= 0.5.0, per the "Versioning" item below). Out of scope for this
90
+ docs-only pass — implementing the workflow change goes through an Issue (once
91
+ ticket-driven work applies to it) plus the normal explore → plan → approval gate,
92
+ not a direct docs commit.
93
+
94
+ ## From #25 / PR #26 (FieldExtractor terminology scope fix, 2026-08-22)
95
+
96
+ - **Multi-level nesting regression test (CLUSTER in CLUSTER)**: the 0.4.1 fix and its
97
+ regression spec (`spec/openehr_rails/opt/field_extractor_embedded_archetype_spec.rb`)
98
+ cover one level of embedding (entry -> embedded CLUSTER). The underlying walk in
99
+ `FieldExtractor#collect_elements` is recursive and should handle a CLUSTER embedded
100
+ inside another embedded CLUSTER the same way, but this is currently unverified by any
101
+ test. Add a fixture/spec covering two levels of `C_ARCHETYPE_ROOT` nesting (openehr-ruby's
102
+ `spec/lib/openehr/opt_parser/eReferral.opt` already has a real two-level case:
103
+ `OBSERVATION.lab_test` -> `OBSERVATION.imaging` -> `CLUSTER.imaging`, per
104
+ `docs/design/fix-terminology-scope-plan.md` section 4). Suitable for a follow-on PR, not
105
+ urgent.
106
+
107
+ ## Compatibility
108
+
109
+ - **STRICT-incompatible fixture: 1 known case** — `spec/templates/lab_result_report_reduced.opt`'s
110
+ leading comment uses `--` as an em dash (a double hyphen, invalid inside an XML comment);
111
+ under STRICT-mode XML parsing this file would fail to parse at all. See
112
+ openehr-ruby#36. Not fixed now — the current form is a live reproduction case for #36; if
113
+ #36 moves toward making STRICT the default, this comment's punctuation is the prerequisite
114
+ fix on this repo's side.
115
+
116
+ ## Fixture conventions
117
+
118
+ - **Licensed terminology-code literals in spec expectations: keep minimal, cite the
119
+ source fixture's file:line.** Adopted from Anlage's C2 firewall precedent (SNOMED
120
+ CT is a licensed terminology; reproducing its codes verbatim in more places than
121
+ necessary widens exposure for no test-coverage benefit). Applies to SNOMED CT
122
+ specifically; **LOINC is exempt** (permissively licensed). Current count (#30,
123
+ 2026-08-25): **2 SNOMED literal occurrences** in spec expectation code, both the
124
+ same code value `60621009`, both traceable to the same source fixture line --
125
+ `spec/openehr_rails/opt/field_extractor_binding_spec.rb:40` and
126
+ `spec/openehr_rails/opt/parser_term_bindings_spec.rb:28`, both citing
127
+ `spec/templates/bmi_calculation_without_uid.opt:1689` (and the identical
128
+ `spec/generators/templates/bmi_calculation.opt:1692`). No new SNOMED literal was
129
+ introduced by #30 -- reused the code already present in these existing fixtures.
130
+
131
+ ## Versioning
132
+
133
+ - **Next release must be >= 0.5.0, regardless of its own content**, to retroactively
134
+ acknowledge in the version series that 0.4.1 was substantively a minor release (see the
135
+ errata in `CHANGELOG.md`'s `[0.4.1]` entry and the release convention added to
136
+ `CLAUDE.md`). Applies even if the next release's own changes would otherwise only
137
+ warrant a patch bump.
138
+
139
+ ## December public release prep
140
+
141
+ - **Issue templates: done (2026-08-23, #29)** — `.github/ISSUE_TEMPLATE/` (bug_report,
142
+ enhancement, config.yml) is in place, along with `CLAUDE.md`'s "Ticket-driven
143
+ workflow" section.
144
+ - **demo_assets README**: deferred to December prep. Note: no prior record of this
145
+ deferral was found anywhere in this repo's docs (`docs/`, `CLAUDE.md`,
146
+ `demo_assets/`) at the time of writing this entry — `demo_assets/` currently has no
147
+ README at all, only `demo_seed.rb` and `templates/`. Recording it here now as the
148
+ first record, not as confirmation of an earlier one.
149
+
150
+ ## Queue gating (do not start without an explicit go-ahead)
151
+
152
+ - **#3** (registry checksum/version/status): gated on Anlage Slice 1 operational experience,
153
+ targeted around October.
154
+ - **#4** (ValueBuilders extraction): gated on Anlage Slice 4 stabilizing and Anlage's
155
+ duplicate-method inventory being produced first.
156
+ - **#2** (constraint -> HTML attribute mapping extraction): gated on a generality decision
157
+ after Anlage Slice 4; whether to even start is undecided.