Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ Notable changes to the plugins in this repository. The format follows [Keep a Ch

## Unreleased

### Changed

- `class-generation` and `regen-classes` generate from the component repo's own BMM file and its sibling dependencies, mounted under `/in/` and passed by path. A bare schema id loads the copy bundled in the `bmm-publisher` image, which lags the repos and rendered an older model without any error; mounting a directory over `/app/resources` hid the bundled dependencies. `regen-classes` stops when a file is missing instead of falling back to an id.
- `scaffold` template set revision 2: the `AGENTS.md` build region no longer says the repo has no build tooling, and its class-table command loads the sibling BASE BMM with `-d` (new variable `base_bmm_schema_id`, inferred from the `specifications-BASE` clone), so links to BASE types resolve. A new `AGENTS.md` gets a `TODO(scaffold)` marker for the repo's own tooling, and `.claude/CLAUDE.md` says repo guidance belongs in `AGENTS.md` outside the regions. Existing repos upgrade on the next `/openehr-specs:scaffold` run.
- `scaffold` asks to confirm `first_release` only when it creates `manifest.json`, the one file that uses it, and its rules say where `/init`-style guidance goes.
- `amendment-record` covers changes without a Jira ticket: no invented or placeholder key, an offer to raise one, an entry without a reference otherwise, and none when the author says none is needed.
- `authoring` reference `manifest-spec-entry.md`: the expressions example shows the current `ITS-BMM-<COMPONENT>` url expression and `ITS-BMM-BASE` dependency (as in RM) instead of the retired MagicDraw `uml` expressions.

## openehr-specs 0.4.0 (2026-10-07)

### Added
Expand Down
14 changes: 13 additions & 1 deletion plugins/openehr-specs/skills/amendment-record/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@ Reference tickets as `{spec_tickets}/SPECXX-NNN[SPECXX-NNN^]` (the `{spec_ticket
resolves to the site ticket URL). The full project-key table and CR↔PR linking phrasing are in
`references/conventions.md`.

### No Ticket

Entries without a Jira reference are established practice: 266 of the 974 entries across the
`specifications-*` repos carry none (surveyed 2026-10-08). When a change has no ticket:

- Never invent a key or write a placeholder (`SPECXX-NNN`, `TBD`).
- Ask whether to raise one first: a problem report (`SPECPR`) for a defect, a change request in the
component's project for a change. If one is raised, reference it.
- Otherwise write the Details cell without a reference, in the same style as the other entries.
- Whether a change needs an entry at all is the author's call. When they say none is needed, for example
for a fix that restores published text lost by tooling, add none.

## Version Numbering

The issue number tracks the **document version**, independent of the component release version
Expand Down Expand Up @@ -121,7 +133,7 @@ SPECRM-142 — add tags to LOCATABLE; addresses SPECPR-401
2. Choose the Issue number: bump patch, minor, or major per the table above, or reuse the top entry's number when the change ships in the same version increment (see "Grouping Multiple Changes" in `references/conventions.md`). If the right bump is unclear, state the assumed bump and proceed; the diff in step 8 lets the author correct it. Run `git -C <repo> status --short` and `git -C <repo> diff --stat` when the change is already in the working tree, so the entry describes what actually changed.
3. Add the new entry at the **top** of the table (after the header row)
4. Move `[[latest_issue]]` and `[[latest_issue_date]]` anchors to the new entry. When the version is reused, leave the Issue cell of the previous top entry blank.
5. Reference all relevant Jira CRs and PRs (see `references/conventions.md` for keys, phrasing, and detail-text style)
5. Reference all relevant Jira CRs and PRs (see `references/conventions.md` for keys, phrasing, and detail-text style); without a ticket, follow **No Ticket** above
6. If this is the first entry after a release, add a release boundary row directly below the new entry, labelled with the release just completed (the newest release with a date in the `releases` array of `manifest.json`; ask the author if unsure)
7. Use today's date as the completion date unless the author gives one (ask if uncertain; do not guess), in the format `dd Mon yyyy` (e.g., `17 Nov 2022`)
8. When invoked directly, show the diff of the record (`git -C <repo> diff -- <record>`) and stop; do not publish or commit unless asked. Inside a larger flow (a release, an authoring task), return to that flow instead.
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,17 @@ Each component's `manifest.json` contains a `specifications` array. Here is the
"specifications": [ ... ],
"expressions": [
{
"id": "openEHR_UML-RM.mdzip",
"type": "uml",
"title": "RM UML",
"description": "UML file for MagicDraw 19. Contains UML 2.5 standard XMI file."
"id": "ITS-BMM-RM",
"type": "url",
"title": "RM BMM",
"description": "BMM files for openEHR Reference Model classes",
"link": "https://github.com/openEHR/specifications-ITS-BMM/tree/master/components/RM"
},
{
"id": "openEHR_UML-BASE.mdzip",
"id": "ITS-BMM-BASE",
"dependency": {
"component": "BASE",
"release": "latest"
"release": "development"
}
}
],
Expand Down Expand Up @@ -93,5 +94,6 @@ When creating a new release:
## Expressions Array

Expressions are computable artifacts associated with the component:
- **`type: "uml"`** — UML model file. Historically a MagicDraw `.mdzip`; class tables and diagrams are now generated from the component's BMM schema by `bmm-publisher` (see the `class-generation` skill).
- **`dependency`** — references an expression from another component (inherited)
- **`type: "url"`** — a link to a computable artifact. The class model is published as BMM files in `specifications-ITS-BMM`: give it the id `ITS-BMM-<COMPONENT>`, the title `<COMPONENT> BMM` and a link to `.../specifications-ITS-BMM/tree/master/components/<COMPONENT>`, as RM and BASE do.
- **`dependency`** — references another component's expression by id (for example `ITS-BMM-BASE` in a component whose classes use BASE types), with that component and its release.
- **`type: "uml"`** — legacy MagicDraw `.mdzip` model. Do not add one: class tables and diagrams are generated from the component's BMM by `bmm-publisher` (see the `class-generation` skill).
39 changes: 25 additions & 14 deletions plugins/openehr-specs/skills/class-generation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ To run from a cloned `bmm-publisher` checkout instead, see "Local development" i
| `legacy-adoc` | Flat per-class definition tables for the legacy `docs/UML/classes` layout; `-o <container-dir>` sets the target, e.g. `/app/output/UML/classes` |
| `asciidoc` (`adoc`) | Class, effective (flattened), and definition tables plus rendered SVG class/package diagrams (current layout) |

Pass schema id(s) **without** the `.bmm.json` suffix (e.g. `openehr_base_1.3.0`), or `all`. Add a
repeatable `-d <schema>` for each dependency schema, loaded for cross-references only (not exported),
and `-v`/`-vv` for progress or detailed logging. The other commands (`plantuml`, `embed-svg`, `yaml`,
Pass the **component repo's own BMM file** as a path, mounted into the container under `/in/`, and a
repeatable `-d` with the path of each dependency schema, loaded for cross-references only (not
exported). A bare schema id (`openehr_base_1.3.0`) or `all` loads the copy **bundled in the image**,
which lags the repos: a run from an id exits 0 but renders the older model, so use ids only to
reproduce published output. Add `-v` to log each file read (confirm it is `/in/…`) or `-vv` for detail. The other commands (`plantuml`, `embed-svg`, `yaml`,
`split-json`, `odin`) and all options are in `references/bmm-publisher.md`.

## Output Layouts and How Specs Consume Them
Expand Down Expand Up @@ -93,20 +95,27 @@ image::ROOT:uml/classes/COMPOSITION.svg[]

## Typical Workflow

1. **Identify the schema** as `openehr_<component>_<version>`, e.g. `openehr_base_1.3.0`,
`openehr_rm_1.2.0`. Ask the user for the component and release when they are not stated;
`references/bmm-publisher.md` lists the bundled ids.
1. **Locate the schema files.** Work from the component repo's root. The schema is
`computable/BMM/openehr_<component>_<version>.bmm.json`; each dependency comes from its sibling clone
(RM, AM, LANG and TERM classes refer to BASE types:
`../specifications-BASE/computable/BMM/openehr_base_<version>.bmm.json`). When a file is missing, say
which and stop rather than falling back to a bundled id.
2. **Choose the command by layout** (see above): `legacy-adoc` for the legacy layout, `asciidoc` for
the current one.
3. **Generate** into a working directory, mapping ownership to the host user:
3. **Generate** into a temporary directory, mounting the files read-only and mapping ownership to the
host user (RM shown, with its BASE dependency):
```bash
OUT=$(mktemp -d)
docker run --rm --user $(id -u):$(id -g) \
-v ./out:/app/output \
ghcr.io/openehr/bmm-publisher asciidoc -v openehr_rm_1.2.0 -d openehr_base_1.3.0
-v "$PWD/computable/BMM/openehr_rm_1.2.0.bmm.json":/in/openehr_rm_1.2.0.bmm.json:ro \
-v "$PWD/../specifications-BASE/computable/BMM/openehr_base_1.3.0.bmm.json":/in/openehr_base_1.3.0.bmm.json:ro \
-v "$OUT":/app/output \
ghcr.io/openehr/bmm-publisher asciidoc -v /in/openehr_rm_1.2.0.bmm.json -d /in/openehr_base_1.3.0.bmm.json
```
For the legacy layout, run `legacy-adoc -o /app/output/UML/classes <schema> -d <dependency>`
instead; the tables land in `./out/UML/classes`.
4. **Place** the output. Legacy layout: copy `./out/UML/classes/*.adoc` into the component's
For the legacy layout, run `legacy-adoc -v -o /app/output/UML/classes /in/<schema>.bmm.json -d
/in/<dependency>.bmm.json` instead; the tables land in `$OUT/UML/classes`. Without the dependency,
links to its types come out as `link:/classes/<Type>`.
4. **Place** the output. Legacy layout: copy `$OUT/UML/classes/*.adoc` into the component's
`docs/UML/classes/`. Current layout: ask the user how the component wires the
`output/Adoc/<schema>/` content. Ask before overwriting files in a `specifications-XX` repo, and do
not commit.
Expand All @@ -120,7 +129,9 @@ the user can run `/openehr-specs:publish <component>`.
## Guardrails

- **Generated output is never hand-edited.** Fix the BMM schema and regenerate.
- **Use the repo's BMM, not the bundled copy.** Mount the files under `/in/` and pass their paths. Do not
mount a directory over `/app/resources`: it hides the bundled schemas, so a dependency named by id is
then missing and the run exits 1.
- **`resources/*.bmm.json` is upstream input** in a cloned `bmm-publisher` checkout: do not modify it
unless the task is specifically to change the model. With the Docker image, mount your own schemas
with `-v ./my-schemas:/app/resources` instead.
unless the task is specifically to change the model.
- Provide cross-referenced dependency schemas with `-d` so type links resolve (e.g. RM depends on BASE).
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,24 @@ From each BMM schema the tool can produce:

## Arguments and options

- **Positional**: one or more schema ids **without** the `.bmm.json` extension (e.g.
`openehr_rm_1.2.0`), or literal `.bmm.json` paths, or `all` to process every schema in the input
directory.
- **`-d <schema>`** (repeatable): a dependency schema id or path, loaded for cross-reference
- **Positional**: one or more literal `.bmm.json` paths (the normal case: the component repo's own
schema, mounted into the container), or schema ids **without** the `.bmm.json` extension (e.g.
`openehr_rm_1.2.0`), which load the copy bundled in the image, or `all` for every bundled schema.
The bundled copies lag the repos, so a run from an id can render an older model without any error.
- **`-d <schema>`** (repeatable): a dependency schema path or id, loaded for cross-reference
resolution only — **not** exported. Accepted by `asciidoc`, `legacy-adoc`, and `plantuml`. Example:
RM references BASE types, so generate RM with `-d openehr_base_1.3.0`.
RM references BASE types, so generate RM with `-d /in/openehr_base_1.3.0.bmm.json` (the sibling BASE
clone's file, mounted). Without it, links to BASE types come out as `link:/classes/<Type>`.
- **`-o <dir>`** (`legacy-adoc` only): output directory for the class `.adoc` files. Default:
`<output>/legacy-adoc/<schema_id>` per schema.
- **`-v`** / **`-vv`**: progress output / detailed file-write logging.

## Input / output

- **Input**: BMM schemas in `resources/` (`.bmm.json`, shipped with the image). Mount your own with
`-v ./my-schemas:/app/resources`.
- **Input**: `.bmm.json` files mounted into the container and passed by path (recommended:
`-v "$PWD/computable/BMM/<schema>.bmm.json":/in/<schema>.bmm.json:ro`), or the schemas bundled in
`resources/`, named by id. Mounting a directory over `/app/resources` replaces the bundled set, so a
dependency named by id must then be in that directory too, or the run exits 1.
- **Output**: artefacts in `output/` — mount a volume to retrieve them: `-v ./out:/app/output`.
- **`BMM_OUTPUT_DIR`** env var overrides the output path: `-e BMM_OUTPUT_DIR=/data/out`.

Expand All @@ -67,20 +71,29 @@ shipped by a given image tag.
## Invocation examples (Docker)

```bash
# From a component repo's root: its own BMM plus the sibling BASE dependency, by path (the normal case)
docker run --rm --user $(id -u):$(id -g) \
-v "$PWD/computable/BMM/openehr_rm_1.2.0.bmm.json":/in/openehr_rm_1.2.0.bmm.json:ro \
-v "$PWD/../specifications-BASE/computable/BMM/openehr_base_1.3.0.bmm.json":/in/openehr_base_1.3.0.bmm.json:ro \
-v ./out:/app/output \
ghcr.io/openehr/bmm-publisher legacy-adoc -v -o /app/output/UML/classes \
/in/openehr_rm_1.2.0.bmm.json -d /in/openehr_base_1.3.0.bmm.json

# All bundled schemas → AsciiDoc tables + SVG diagrams
docker run --rm -v ./out:/app/output ghcr.io/openehr/bmm-publisher asciidoc all

# Single schema with a dependency, host-owned output, verbose
# Bundled schemas by id: reproduces published output, may lag the repos
docker run --rm --user $(id -u):$(id -g) \
-v ./out:/app/output \
ghcr.io/openehr/bmm-publisher asciidoc -v openehr_rm_1.2.0 -d openehr_base_1.3.0

# Legacy per-class tables -> ./out/UML/classes (copy into the spec repo's docs/UML/classes)
# Legacy per-class tables of the bundled BASE by id -> ./out/UML/classes (to compare with published output;
# for a spec repo, use the by-path example above)
docker run --rm --user $(id -u):$(id -g) \
-v ./out:/app/output \
ghcr.io/openehr/bmm-publisher legacy-adoc -o /app/output/UML/classes openehr_base_1.3.0

# Your own BMM schemas
# A directory of your own schemas replacing the bundled set (it must hold every dependency too)
docker run --rm \
-v ./my-schemas:/app/resources \
-v ./out:/app/output \
Expand Down
30 changes: 20 additions & 10 deletions plugins/openehr-specs/skills/regen-classes/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
name: regen-classes
description: Regenerate BMM-derived class tables and UML diagrams for a component via bmm-publisher
argument-hint: "<schema-id, e.g. openehr_rm_1.2.0> [-d <dependency-schema> ...]"
argument-hint: "<schema-id of this repo's BMM, e.g. openehr_rm_1.2.0> [-d <dependency-schema-id> ...]"
allowed-tools:
- "Bash(docker --version)"
- "Bash(docker run * ghcr.io/openehr/bmm-publisher *)"
Expand All @@ -10,26 +10,36 @@ allowed-tools:
disable-model-invocation: true
---

Regenerate the class tables and UML diagrams for `$ARGUMENTS` with `bmm-publisher`. The arguments are a
schema id without `.bmm.json` (for example `openehr_rm_1.2.0`) plus any `-d <dependency-schema>` flags
(RM depends on BASE: `openehr_rm_1.2.0 -d openehr_base_1.3.0`). The layouts and the placement into
Regenerate the class tables and UML diagrams for `$ARGUMENTS` with `bmm-publisher`, from the BMM files in
the component repo at the current directory, not from the copies bundled in the image (they lag the
repos, and a run from a bundled id exits 0 while rendering the older model). The arguments are a schema
id without `.bmm.json` (for example `openehr_rm_1.2.0`) plus any `-d <dependency-schema-id>` flags (RM
depends on BASE: `openehr_rm_1.2.0 -d openehr_base_1.3.0`). The layouts and the placement into
`docs/UML/` are in the `openehr-specs:class-generation` skill; this wrapper covers generation only.

If `$ARGUMENTS` is empty, ask for the schema id and stop.

1. Run `docker --version`. If Docker is missing, say it is required (the image and its dev container
both run on it) and stop.
2. Write to `./out` unless the user names another target. Ask before writing into a
2. Map each id to its file and Glob that it exists: the schema to `computable/BMM/<id>.bmm.json`, and a
dependency `openehr_<component>_<version>` to the sibling clone's
`../specifications-<COMPONENT>/computable/BMM/<id>.bmm.json` (component upper-cased). If one is
missing, name it and stop; never fall back to the bundled id.
3. Write to `./out` unless the user names another target. Ask before writing into a
`specifications-XX` repo's `docs/UML/`.
3. Pick the command by layout (step 2 of the `class-generation` workflow): `legacy-adoc -o
4. Pick the command by layout (step 2 of the `class-generation` workflow): `legacy-adoc -o
/app/output/UML/classes` when the spec's chapters contain `include::{uml_export_dir}/classes/`
(the tables land in `./out/UML/classes`), otherwise `asciidoc`. Generate, mapping output ownership
to the host user:
(the tables land in `./out/UML/classes`), otherwise `asciidoc`. Mount each file read-only under
`/in/`, pass the paths, and map output ownership to the host user (RM with BASE shown):
```bash
docker run --rm --user $(id -u):$(id -g) \
-v "$PWD/computable/BMM/openehr_rm_1.2.0.bmm.json":/in/openehr_rm_1.2.0.bmm.json:ro \
-v "$PWD/../specifications-BASE/computable/BMM/openehr_base_1.3.0.bmm.json":/in/openehr_base_1.3.0.bmm.json:ro \
-v ./out:/app/output \
ghcr.io/openehr/bmm-publisher asciidoc -v $ARGUMENTS
ghcr.io/openehr/bmm-publisher asciidoc -v /in/openehr_rm_1.2.0.bmm.json -d /in/openehr_base_1.3.0.bmm.json
```
4. List what was produced (`classes/`, `effective/`, diagram SVGs). Do not commit. Generated files are
Add one mount and one `-d /in/<id>.bmm.json` per dependency. The `-v` log lists each file read:
check that every one is under `/in/`.
5. List what was produced (`classes/`, `effective/`, diagram SVGs). Do not commit. Generated files are
never hand-edited: change the BMM and regenerate. To place the output, follow the
`class-generation` skill.
6 changes: 5 additions & 1 deletion plugins/openehr-specs/skills/scaffold/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ AsciiDoc-specific.
2. **Variables.** When `status` is `needs_input`, ask for each missing or invalid variable, then plan
again with `--var name=value`. A variable whose source is `default` or starts with `inferred` is a
guess. Ask the user to confirm the ones that matter (`license`, `jira_project`, `bmm_schema_id`,
`default_branch`, `first_release`, `published_url`) and list the rest in one line. Never invent a
`base_bmm_schema_id`, `default_branch`, `published_url`, and `first_release` only when the plan creates
`manifest.json`, the one file that uses it) and list the rest in one line. Never invent a
title, a Jira key or a filter id. Values the user confirmed earlier are recorded in
`.claude/scaffold.json` and kept; a recorded default is not, so a BMM schema added later shows up
as a new guess to confirm. `--overwrite` and `--pin` accept only the file ids and `agents:<region>`
Expand Down Expand Up @@ -92,6 +93,9 @@ AsciiDoc-specific.
`pinned` in `.claude/scaffold.json`). Changing what a region says for every repo is a change to the
plugin's template set.
- Run the skill again later to upgrade: the recorded revision is compared with the latest.
- Repository-specific guidance, such as what `/init` would write into a root `CLAUDE.md`, goes into
`AGENTS.md` outside the regions. `.claude/CLAUDE.md` only imports `AGENTS.md`; a second `CLAUDE.md`
would duplicate it.

To change the template set, add a revision, or understand a file strategy, read
`${CLAUDE_SKILL_DIR}/references/maintaining.md`.
Loading
Loading