Working with ODK¶
What the Ontology Development Kit workflow looks like, and exactly which steps OntoBoard does for you. Every verdict on this page was checked against the code and against a real ODK repository; where something is partial or missing it says so, because a tool that overstates itself is worse than one that admits a gap.
Legend — ✅ done inside Protégé · 🔶 partly · ❌ you leave Protégé for this · ➖ not applicable
Before anything else: what can I do right now?¶
OntoBoard → Check requirements…
It probes Docker (with docker info, not --version — the CLI answers while the daemon is
down), Podman, make, sh, a native odk environment and Java, then lists what you can do today
and what to install for the rest. It does not probe git, although three menu items wrap the
git executable — if Git… misbehaves on a machine with no git, that is why.
1. Creating a project¶
ODK's own route is seed-via-docker.sh. OntoBoard has its own, and the two produce different
repositories — this is the single most important thing on this page.
ODK's seven steps, and what each one is inside Protégé:
| # | ODK step | OntoBoard | |
|---|---|---|---|
| 1 | Go to the parent directory | ✅ | Project → New ODK project…, Create in folder → Choose… |
| 2 | Start Docker | ➖ | Not needed for the wizard. Needed later, to build a real ODK repo |
| 3 | curl -O seed-via-docker.sh, chmod +x |
❌ | OntoBoard never downloads or runs ODK's seeder |
| 4 | docker pull obolibrary/odkfull |
➖ | Not needed for the wizard. OntoBoard never pulls — but docker run fetches a missing image itself, so the first build of a real ODK repo is slow rather than broken |
| 5 | ./seed-via-docker.sh <ontology> |
🔶 | The wizard writes 17 files and opens the edit file — a different repository, see below |
| 6 | Configure <id>-odk.yaml |
✅ | Project → Project configuration… since 1.80.0. Every key visible, and since 1.89.0 every scalar editable at any depth — measured table below |
| 7 | git init, create the remote, push |
🔶 | Commit, pull, push, branch, checkout and clone work. Turn an ontology into an ODK repository… runs git init and records the history; creating the remote is left to you |
What the wizard produces¶
Ontology ID, title, description, base IRI and licence, validated, then 17 files written with no Docker and no network:
src/ontology/ <id>-edit.owl <id>-odk.yaml <id>.Makefile <id>-idranges.owl
catalog-v001.xml profile.txt Makefile
src/sparql/ check_labels.rq
.github/workflows/ qc.yml docs.yml
.gitattributes .gitignore mkdocs.yaml docs/… README.md
The repository is created at <chosen folder>/<ontology id>, and creation is refused if that
directory already exists — there is no "seed into the folder I already made".
From an ontology you already have — Turn an ontology into an ODK repository…, since 1.133.0¶
Project → Turn an ontology into an ODK repository… makes the same project as the wizard from an ontology you already have, and turns its earlier versions into the project's history. You give:
- the ontology — a file in any format the OWL API reads (RDF/XML, Turtle, OWL/XML, functional syntax, Manchester, JSON-LD…), or a web address; the open ontology is offered;
- its earlier versions, if you have them — files or web addresses, in any order and any mix
of formats. Each is placed by the version it says it is: its
owl:versionInfo, else its version IRI, else a version in its file name or address; - the folder to make the project in — a new folder named after the project id;
- the id and title, filled in from the ontology: its preferred prefix
(
vann:preferredNamespacePrefix), else the end of its IRI, else its file name, skipping words such asontologyorcorethat say nothing about which ontology it is; - whether to turn imports into modules and whether to record the history in git.
A window then shows nine steps, each marked when it starts (▶), and when it ends (✓, or ! when something needs your attention), with every line it writes below them:
| # | Step | What it does |
|---|---|---|
| 1 | Read the ontology | Reads it with its imports, and says its format, IRI, version, size and any import that could not be read |
| 2 | Read the earlier versions | Each on its own — what it imported is history, not something to fetch. Oldest first; a version given twice, or one not older than the ontology's own, is left out with a note |
| 3 | Make the project | The files New ODK project… writes |
| 4 | Record the earlier versions | Oldest first, each as it was published: the edit file, releases/<version>/<id>.owl, and with git a commit dated as the version says and a tag v<version> |
| 5 | Turn imports into modules | For each import, the terms your ontology uses are extracted into imports/<name>_import.owl and listed with their labels in imports/<name>_terms.txt, and the import names the module. A title, description and licence are added if the ontology has none, because the quality report asks for them |
| 6 | Build and test | As Build… → test. A failure is shown, and the project is made all the same |
| 7 | Release the current version | Merged with its modules, reasoned with ELK, stamped with its version IRI and the version before it: releases/<version>/, <id>.owl and <id>.ttl |
| 8 | Record it in git | A History table in the README, then a commit and a tag |
| 9 | Ready to publish | The commands that push it to GitHub with every tag |
Stop ends it after the step it is in; what is written so far stays. Open the project opens the edit file in Protégé. The commits are signed with the name and e-mail git knows you by, or OntoBoard if git has none.
Converting again gives the same repository. The same files, apart from dates the files
record; the same tree at every tag; and, for every earlier version that says when it was made
(dcterms:date, issued, created or modified, or a dated version), the same commit. A
version that says no date is committed at the time of the conversion, so its commit differs
while its files do not.
What it leaves to you.
- Earlier versions are recorded as they were published. They are not rebuilt and their imports are not turned into modules.
- An import that cannot be read stays an import of the whole ontology. Once it can be read, Imported terms… turns it into a module.
- Creating the repository on GitHub and pushing are the two commands step 9 prints.
- Without git, the versions are still written under
releases/, and step 9 says how to start a history from them.
The thing to decide before you start¶
An OntoBoard-scaffolded project and a real ODK repository cannot adopt each other.
- Project → Update project files… refuses any repository carrying
src/ontology/run.shor anODK_VERSION_MAKEFILEmarker — i.e. every genuine ODK repo. It will not rewrite a Makefile it did not write: OntoBoard's generated one is eight targets, ODK's is several hundred lines. - ODK's own
sh run.sh make update_repocannot run on an OntoBoard-scaffolded project, because there is norun.shand the YAML is missing most of what ODK's generator needs.
So choose deliberately. Want the full ODK pipeline, imports machinery and OBO release artefacts? Seed with ODK, then open the result in OntoBoard — everything in sections 3 and 4 below still applies, and the build runs in ODK's own container. Want a small project that builds with nothing installed, on any platform including Windows? Use the wizard.
Step 6: the ODK YAML, key by key¶
The one step with a hard requirement attached: the YAML should be completely visible and editable inside Protégé. Visible: yes, every key. Editable: the scalars, not the structures.
Measured against mwo-odk.yaml — 13 top-level keys, of which 9 are
editable in place and 4 are shown read-only:
| Key | What it controls | |
|---|---|---|
id |
✅ | How files are named and which default term IDs are assumed. Lowercase |
title |
✅ | Seeds the README and other generated defaults |
github_org |
✅ | Basic repository config |
repo |
✅ | Basic repository config |
git_main_branch |
✅ | |
uribase |
✅ | Base IRI. Read but not used — OntoBoard recovers the base from the edit file, because uribase is lossy |
primary_release |
✅ | Which artefact most users interact with, e.g. full → <id>.owl |
remove_owl_nothing |
✅ | |
robot_java_args |
✅ | RAM ROBOT may consume, e.g. -Xmx8G |
release_artefacts |
✅ | Each item, e.g. release_artefacts[0] |
export_formats |
✅ | Each item — owl, ttl |
import_group |
✅ | Every scalar inside it: import_group.products[2].mirror_from, import_group.products[0].module_type, each annotation_properties entry |
robot_report |
✅ | robot_report.use_labels, .fail_on, .custom_profile, .report_on[0] |
Since 1.89.0 every scalar in the file is editable, at any depth. Measured on that same file: 47 values, 36 of them editable, against 13 values and 9 editable before. So all twelve settings the ODK tutorial walks you through can be changed in Protégé.
The dialog shows the nesting indented and addresses each value by path, which matters because
module_type appears under all four of MWO's imported products and id under each as well —
keying an edit by its leaf name would write one product's value into another's.
Why this was ever shallow, and what is still refused. It looked like a limit of the technique and was not. Editing splices a value's own characters out of the original text, and a scalar's span is exactly its own characters wherever it sits — so a scalar two blocks down is no more dangerous to replace than a top-level one. The walk simply never went past the top level.
What is still refused is a structure, whose span covers everything inside it: replacing
import_group in place would delete every product under it. So import_group and
robot_report are shown read-only while their scalars are editable, which is the honest split.
Adding or removing a list entry is not here. Changing export_formats[1] from ttl to
something else is a splice; adding a third format inserts a line, which is a different
operation. A key containing a . or a [ is shown and not offered, because a.b would then
mean two things and an edit could land on a key other than the one clicked — no ODK
configuration has one.
Why it splices rather than rewrites. Loading that file with a YAML library and writing it
back rewrites 28 of its 41 lines — normalising a flow sequence, a nested indent, CRLF to LF and a
missing final newline — so changing one word would arrive in a pull request as a whole-file diff.
Measured: editing title through this editor changes one line, and every comment survives.
Two guards on the disk. The file is re-read and compared immediately before saving, so if it
changed underneath — a text editor, a git pull, a make update_repo — the save is refused
rather than discarding that change. The write goes through a temporary file in the same directory
and an atomic move, so a crash cannot leave a truncated config that breaks every ODK target at
once.
A file with a duplicate top-level key is refused outright. YAML accepts one silently and keeps both, so an edit would patch whichever copy the parser reached while ODK reads the other.
The full schema is ODK's: incatools.github.io/ontology-development-kit/project-schema.
Reading and writing is not the same as honouring. Only id, title, description,
license and robot_version drive anything in OntoBoard, and only Update project files…,
which refuses a real ODK repository. Everything else is text OntoBoard preserves faithfully and
does not act on — what ODK does with it next is ODK's business, by way of update_repo.
2. The workspace¶
| Directory | What OntoBoard does with it | |
|---|---|---|
src/ontology/ |
✅ | The edit file, the catalog, both Makefiles, imports/ and the -odk.yaml are all read and acted on — see section 3. Reuse → Refresh imports audits every import the project declares and rebuilds the ones it can, since 1.99.0 |
src/sparql/ |
✅ | Check and test → SPARQL… runs your committed *.rq with the same pass/fail convention make sparql_test uses, since 1.33.0. The scaffold writes one starter query rather than ODK's full set |
src/scripts/ |
✅ | Project → Run a project script… lists what is there — honouring a SCRIPTSDIR the Makefile moves — and runs the one you pick, with arguments, inside the container. See below |
src/metadata/ |
🔶 | Read to check it, never written. The OBO Foundry registry entry: <id>.yml is the PURL configuration for purl.obolibrary.org/config, <id>.md the entry for OBOFoundry.github.io. Check and test → Check project metadata compares both against the ontology; nothing edits them, because for a project not in the Foundry the file is scaffold the build never reads — and three of four measured projects are not |
src/templates/ |
✅ | ROBOT templates. Opened automatically in the OntoBoard Sheets tab — see below, which also covers a knowledge graph's kg/templates/ — and built by Spreadsheets → Template… |
Three of the five are covered, and the one that is not is only needed for an OBO Foundry
submission. Everything in the first three runs inside Protégé — no terminal, no separate
sh run.sh: ROBOT operations run in-process, make targets and your own scripts run through the
same container the build uses, and SPARQL checks run against the ontology you have open.
The sheet editor — its own tab since 1.117.0¶
Window → Tabs → OntoBoard Sheets, or Spreadsheets → Sheet editor, which opens it for you. A tab you have never opened is not in the tab strip until something adds it, which is what that menu item is for.
It opens the templates of whatever you have open, with one grid per file. Where it looks, in
this order, taking the first folder that holds a .tsv or .csv (since 1.134.0):
templates/beside the open file - or, for an ODK edit file insrc/ontology/, the project'ssrc/templates/instead;- the project's
src/templates/; kg/templates/, for a knowledge graph kept beside the ontology;templates/at the project root.
The project is the nearest folder above the file - at most four levels up - that holds .git or
src/ontology/, so a project nested in another repository never picks up its host's templates,
and nothing searches subfolders. The toolbar says which folder is open and how many sheets it
has (Folder: kg/templates (9 sheets)); hover it to see where it looked first and which other
folders hold sheets. Project templates ▾ lists every place it looks (three when two of them are
the same folder) with what each holds, so a different one can be chosen -
including one that does not exist yet, which then becomes the folder New sheet... writes into.
Opening the view creates nothing. Switching the active ontology to another file of the same
project changes nothing; switching to another project opens its templates, unless that would
replace unsaved edits or a folder you chose yourself, in which case the status line says where
the new one's templates are and waits.
What is there that a spreadsheet program is not:
- The names come from the ontology. A column that names another term completes from every open sheet and from the ontology, so a reference to something that exists is one keystroke and a reference to something that does not is visible immediately.
- The mistakes are marked where the typing happens. Ten kinds — ambiguous names, duplicate identifiers, near-duplicate labels, dead references, pasted whitespace, curly quotes, missing identifiers, placeholders, quoted references, columns the template ignores. On the MatWerk sheets that is 1,227 marked cells on a graph that builds with zero problems, which is the argument for the feature: ROBOT is happy and the data is not.
- The ones with an obvious correction can be fixed in one undoable step. 307 were applied to the MatWerk sheets at once, and the built graph was byte-identical afterwards.
- A reference to something that does not exist can be made to exist. Right-click it: the sheet, the type and the identifier are all worked out from the sheets’ own evidence, and it refuses rather than guessing when they do not say.
- The header rows are editable. Row 1 is your column name and row 2 is ROBOT's template
string - the line that decides what the column does. They are two editable rows where the
column header used to be, with the spreadsheet column letter above them. Every edit is checked
against robot-core's own
validateTemplateString, because a template string ROBOT does not recognise is not a bad column: it is aColumnExceptionand nothing at all is built from the sheet. A cell ROBOT would refuse is marked red; one it accepts and then quietly does nothing with -II, a>on a class column, an axiom annotation that follows nothing - is marked amber. Add column adds one with its template cell empty, which is ROBOT's own way of saying "ignore this column". - Identifiers are minted, not typed. Generate IRIs... fills every row that has content but
no identifier, and offers six ways to form one, each previewed with a real example before
anything is written: a block from the project's
-idranges.owl; continuing the sheet's own numbering; a timestamp; a timestamp and who is editing; a random UUID; a UUID derived from the label; or the label itself as a readable identifier. The one offered first is the one that matches what the sheet already holds. - An existing Excel file can be brought in. Import Excel… reads an
.xlsxand writes each tab into the folder as a.tsv. It does not write.xlsxback: reading that format is four of its parts and writing it is all of them, and.tsvis what ROBOT reads and what git can merge. - Nothing is written until you press Save. These files are in git and somebody else is editing them.
Running your own scripts¶
Since 1.81.0 - Project → Run a project script… lists what is in src/scripts/ and runs
the one you pick.
The directory is src/scripts unless the Makefile declares SCRIPTSDIR, which is read rather
than assumed - a project that moved the directory would otherwise be told it has no scripts.
Where it runs. The container first, then a native ODK environment, then this machine. That
order is not a fallback chain with the best option last: measured on a real ODK project, all
three of its scripts need something that exists only inside obolibrary/odkfull -
run-command.sh calls /usr/bin/time, update_repo.sh calls /tools/odk.py, and
validate_id_ranges.sc is an Ammonite script needing amm. Offering to run those on your
laptop would be offering a failure. The mount, working directory and heap settings are the same
ones a build gets, because they come from the same code.
What runs it comes from the script's #! line, or from its extension when it has none
(.sh, .bash, .py, .pl, .rb, .sc). A script with neither is listed but not offered -
guessing at an interpreter would produce a confident wrong answer.
You see the command before it runs. This is the one place in OntoBoard that runs code somebody else wrote and OntoBoard has not inspected, so the exact argv, the route and any warning are on screen, and nothing starts until you agree to that particular command.
Windows line endings are called out, because the failure they cause names the wrong thing: a
shell in a Linux container reading a script whose first line ends \r reports "cannot execute:
required file not found", which reads as a missing interpreter. Every script in the project this
was built against has CRLF endings, usually from git's core.autocrlf.
Arguments, since 1.85.0. The chooser has an argument line, split the way a shell splits
words - "my file.owl" is one argument, not two. An unclosed quote is refused rather than
guessed at, because the command you approve has to be the command that runs. The confirmation
lists each argument in brackets, which is what tells you your quoting did what you meant.
Nothing is handed to a shell: the arguments become separate elements of the process command, so a semicolon or a backtick in one is passed to the script as that character and cannot start a second command. The single exception is the native-ODK route, which really does compose a shell line, and every argument is quoted there.
Stopping one, since 1.85.0. The progress dialog's Stop button kills a running script outright - and a running build, and a running git command. Before this it dismissed the dialog and left the work going, so stopping a forty-minute ODK build freed the window and nothing else. An in-process ROBOT operation still cannot be interrupted; the dialog distinguishes the two.
Limits. A script gets 15 minutes rather than a build's 45 - a backstop for a runaway nobody is watching, since the button needs somebody in front of it. Arguments are not remembered between runs.
Also runnable, and worth knowing about:
- Make targets - Build and release → Build… lists the targets from
src/ontology/Makefile, following itsincludelines into<id>.Makefile, so your custom targets appear in the chooser. Note that a real ODK Makefile setsSHELL = $(SCRIPTSDIR)/run-command.sh, so every recipe line of the build already runs through a script in this directory. - ROBOT operations - everything OntoBoard does with ROBOT, under Reuse, Terms, Spreadsheets and Check and test, in-process, on the ontology you have open.
- SPARQL - your committed
src/sparql/*.rq, with the same pass/fail conventionmake sparql_testuses.
3. src/ontology, file by file¶
<ontology>-odk.yaml 🔶¶
Shown and editable since 1.80.0 — Project → Project configuration… lists every top-level key with its value and its line number, and lets you change the scalar ones.
Lists and nested blocks (release_artefacts, import_group, robot_report) are shown but not
editable there, deliberately: the editor replaces exactly the characters a value occupies, and a
structure's span covers everything inside it, so an in-place edit would delete the contents. The
same goes for a | block of text.
Edits are spliced, not re-serialised. Loading a real config and writing it back rewrites 28
of its 41 lines — normalising a flow sequence, a nested indent, CRLF to LF and a missing final
newline — so a one-word change would land in a pull request as a whole-file diff. Measured on a
real project, changing title through this editor changes one line, and every comment
survives.
The file is re-read and compared immediately before saving: if it changed underneath — a text
editor, a git pull, a make update_repo — the save is refused rather than discarding that
change. The write goes through a temporary file in the same directory and an atomic move, so a
crash cannot leave a truncated config that breaks every ODK target at once.
Reading and writing are not the same as honouring. Five scalars drive the generated-project
regenerator, export_formats drives Release… since 1.98.0 and import_group.products drives
Refresh imports since 1.99.0; the rest — release_artefacts, primary_release, uribase,
documentation — are shown and saved but not yet acted on by anything in the plugin.
Five scalars are ever read — id, title, description, license, robot_version — and only
by Update project files…, which refuses to run on a real ODK repository at all. Open existing
ODK project… reads title and nothing else.
Three keys do drive something, since 1.91.0:
| Key | What it changes |
|---|---|
robot_java_args |
The heap a containerised build gives ROBOT. Previously hardcoded to -Xmx8G and passed with docker run -e, which overrides the environment — so a project asking for -Xmx16G got 8G in silence, and the plugin's own out-of-memory advice told you to set ROBOT_JAVA_ARGS, the very variable it was overriding. Both fixed |
robot_report.fail_on |
Which severity Check and test → Quality report… treats as a failure |
robot_report.use_labels |
Whether that report shows labels or IRIs |
robot_report.use_base_iris, namespaces, uribase |
What the report is about. As in ODK, only the project's own terms - the namespaces listed, or else <uribase>/<ID>_ and <uribase>/<id> - unless use_base_iris is false. Build... → test says how many findings were about other ontologies' terms, imported or merged in, and left out. Since 1.132.0 |
Still ignored, including keys you are likely to want to change:
github_org·repo·git_main_branch·release_artefacts·primary_release·documentation·robot_report.custom_profileandreport_onNo longer on this list:
export_formats, which Release… honours since 1.98.0, andimport_group.products, which Refresh imports reads since 1.99.0.
Two consequences worth stating plainly. On a real ODK repository, exactly one key
(title) is ever read — the ontology id comes from the edit file's name instead. And five of the
keys OntoBoard's own scaffold writes are read by nothing, including uribase, which is
decorative: the base IRI is recovered from the edit file, because uribase is lossy.
To add or remove a list entry, still open the file in a text editor — and for anything
OntoBoard does not act on, remember that changing it here only changes the file. What ODK does
with it next is ODK's business, by way of update_repo.
make update_repo 🔶 (each tool regenerates its own projects)¶
ODK's sh run.sh make update_repo re-reads <ontology>-odk.yaml and regenerates what it
derives from it: the Makefile, the GitHub Actions workflows, profile.txt when
robot_report.custom_profile is not set, and the scaffolding for imports/.
OntoBoard's equivalent is Project → Update project files…, which re-renders the generated files from the project's own YAML and shows you the diff before writing anything. It exists because a generator fix otherwise never reaches a project made last month — that project could only get a corrected Makefile by being created again.
Neither tool regenerates the other's project, and that is deliberate on both sides.
ODK update_repo |
OntoBoard Update project files… | |
|---|---|---|
| On an ODK repository | regenerates it | refuses, and names sh run.sh make update_repo |
| On an OntoBoard-scaffolded project | cannot run — there is no run.sh |
regenerates it |
The refusal is the safety property. Regeneration rewrites the Makefile, the CI workflow, the
README and the ignore files from OntoBoard's templates; on a real ODK repository that is
destruction, not an update — a real ODK src/ontology/Makefile is commonly over 750 lines of ODK's own
build, and replacing it with the eight-target one this plugin generates would remove every import
rule, every release artefact and every quality target the project has. Two signals decide it,
either one conclusive: src/ontology/run.sh, which only ODK writes, and
ODK_VERSION_MAKEFILE, which ODK's generated Makefile declares and OntoBoard's does not.
An ODK-seeded project's Makefile declares ODK_VERSION_MAKEFILE and has a run.sh beside it; a
project OntoBoard scaffolded has neither.
One difference worth knowing: imports/ is not part of this. ODK's update_repo regenerates
the import scaffolding; in OntoBoard that is Reuse → Refresh imports, which audits every
product the project declares and rebuilds the ones it can — see section 4.
Makefile and <ontology>.Makefile 🔶¶
OntoBoard knows the difference when listing targets — it reads the generated Makefile and
follows its include into your custom one, so your targets appear in Build…. It writes the
generated Makefile only for projects it scaffolded, and never touches either file on a real ODK
repo.
It does not help you edit the custom Makefile, and will not warn you if you edit the generated
one — which ODK will overwrite the next time you run update_repo.
<ontology>-idranges.owl ✅¶
Project → ID ranges… tabulates who holds which block, and allocates a new block to an editor.
One thing to know. Allocation does not insert a range — it rewrites the file from a
template, so indentation is normalised. Up to 1.76.0 that also lost anything the template did
not model: measured on a real repository, allocating a range would silently drop the
Datatype: rdf:PlainLiteral declared after the template's closing Datatype: xsd:integer.
Since 1.77.0 such declarations are carried through, and anything that still would not survive stops the write: OntoBoard names the lines that would go and leaves the file untouched, rather than subtracting from it. Formatting is still normalised, so the diff is a little larger than one range.
profile.txt ✅ (by location, not by configuration)¶
Check and test → Quality report… pre-fills its Report profile field with the profile.txt
sitting beside the open ontology, and falls back to ROBOT's bundled profile when you clear it.
It finds that file by filesystem convention. Since 1.91.0 it also reads
robot_report.fail_on and robot_report.use_labels from the project's YAML, so the report
agrees with the project's own build about which findings count as a failure — which is what
taking the profile from the project was always meant to achieve, and did not.
robot_report.custom_profile: TRUE — the key that declares your project ships a profile — is
still not read, because the file is found by looking rather than by being told. A project whose
Makefile points ROBOT_PROFILE somewhere else will not be honoured.
Not the same thing: Check and test → Profile… is OWL 2 EL/QL/RL/DL validation — ODK's
validate_profile, notrobot report --profile. The names collide; the operations do not.
<ontology>-edit.owl ✅¶
Project → Open existing ODK project… takes a folder and opens the single *-edit.owl under
src/ontology, refusing rather than guessing when there is none or more than one.
OntoBoard never writes the edit file. Every save goes through Protégé's own pipeline, so the
serialisation and layout stay Protégé's business. It does write one sidecar beside it,
<id>-edit.owl.ontoboard.json, holding your board: which terms are on it and where. That file is
yours to commit or ignore.
catalog-v001.xml 🔶¶
Read by Reuse → Check imports, which shows what each import maps to and why an unresolved one
failed. Written by Reuse → Import terms from a list…, which adds a <uri> entry for each module
it saves.
Since 1.83.0, Reuse → Imports graph… draws the same structure as a picture — the whole
closure rather than the direct imports, with unresolved ones dashed and red, and a Copy as DOT
button for your own layout engine. It is the only place that will tell you a module is imported
by more than one other, which is the structure module_type: mirror produces and which a list
of direct imports cannot show. Worth saying plainly: on a project whose imports are flat it adds
nothing the table does not already say — measured on a real ODK repository, the graph is a star
of five nodes.
Resolution itself is Protégé's, not OntoBoard's. The plugin installs no catalog-backed IRI
mapper; imports resolve because Protégé's own loader handles the catalog. In practice this works —
but if an import resolves differently from how robot resolves it, that is the reason.
4. Adding a custom import¶
ODK's own walkthrough is six steps. Four of them OntoBoard does for you, in one operation.
| ODK's step | In OntoBoard | |
|---|---|---|
1. Declare it under import_group in the YAML |
✅ | Reuse → Import terms from a list… writes the product itself, since 1.100.0. Project configuration… also edits every scalar in the block and adds or removes list entries |
| 2. Check the Makefile picked it up | 🔶 | Build and release → Build… lists the targets, following include into <ontology>.Makefile. Nothing shows you the generated import rules |
3. Add term IRIs to <import>_terms.txt, then make refresh-imports |
✅ | Find terms… or Import terms from a list… writes the list from the terms you picked, and adds to it when the import already exists; Imported terms… adds and takes out terms by label and rebuilds the module at once; Reuse → Refresh imports is the refresh |
4. Copy the import URI into <ontology>-edit.owl and catalog-v001.xml |
✅ | Import terms… writes both, so the import resolves offline. Pattern library… feeds the same operation |
5. Copy the generated import schema into <ontology>.Makefile |
❌ | By hand. Only needed for a custom module, which is the case ODK's own generated rule refuses to build too |
6. Set module_type, then update_repo, clean, make |
🔶 | Import terms… writes the module_type it actually used. Changing it later is a scalar edit in Project configuration…; regenerating is Update project files… on a scaffolded project or sh run.sh make update_repo on an ODK one; clean and make are targets in Build… |
An OBO id is enough; anything else needs its source — and OntoBoard writes it the same way.
ODK downloads http://purl.obolibrary.org/obo/<id>.owl when a product gives no mirror_from, so
a product for an OBO ontology is one line. When Import terms… declares what it built it omits
mirror_from for exactly that case and writes it for every other, which is why a declaration it
produces looks like one a person would have written:
products:
- id: iao # an OBO ontology: the PURL is the default
module_type: slme
module_type_slme: BOT
- id: edam # anything else has to say where it came from
mirror_from: https://edamontology.org/EDAM_1.25.owl
module_type: slme
module_type_slme: STAR
BOT, TOP, STAR — and not SUBSET. The extraction methods
ODK documents for module_type_slme are BOT, TOP and STAR, and OntoBoard runs all three plus
MIREOT. SUBSET is the one real projects use that it cannot: ROBOT reaches that through a
different operation, so Refresh imports names it rather than falling back to BOT, which would
quietly produce a module that is not the one in the repository. NFDIcore and PMDCO declare three
SUBSET products between them.
The pattern library — new in 1.82.0, four collections since 1.87.0, yours since 1.92.0¶
Reuse → Pattern library… browses 163 ontology design patterns shipped inside the plugin, any you have added yourself, and imports terms from one.
The BFO four, new in 1.96.0. Extracted from the BFO that NFDIcore and MWO actually use —
which took some care, because the obvious URL is the wrong one. purl.obolibrary.org/obo/bfo.owl
resolves to bfo_classes_only.owl, which declares zero object properties: patterns extracted
from it would have had no relations in them at all, which is exactly what makes the 123 ODP
patterns useless to a BFO project. The artifact used is the one NFDIcore's own
nfdicore-odk.yaml names — mirror_from: http://purl.obolibrary.org/obo/bfo/2020/notime/bfo.owl
— pinned by its immutable raw tag URL and checked by sha256 at extraction time. MWO 3.0.2 uses
the identical set of 66 BFO terms and declares no BFO product of its own, so its BFO is
NFDIcore's by construction.
Measured: NFDIcore uses 66 distinct BFO terms. The previous 159 patterns between them touched 37 of those; the four new ones touch 53, of which 20 were covered by nothing before.
| Collection | Patterns | Documented at |
|---|---|---|
bfo |
4 | extracted in 1.96.0 from BFO 2020 ‘notime’, the exact artifact NFDIcore pins and MWO agrees with |
odp |
123 | the ODP portal, harvested earlier |
nfdi |
15 | NFDI MatWerk |
pmdco |
13 | PMDco |
mwo |
8 | MWO |
The 36 BFO-family patterns were added in 1.87.0 because the ODP collection and the OBO world share no vocabulary at all — measured, zero of the 123 ODP patterns share a single IRI with a real BFO-based project. For somebody working in MWO or PMDco the original library was 123 patterns about other people's upper ontology.
Every pattern is an OWL file, and a Turtle file beside it. Those three pages document their
patterns as prose and diagrams, so each one here was built by extracting its terms from the
ontology it belongs to — which makes it importable like any other, rather than something to read
and copy by hand. pattern.owl is what the plugin loads; pattern.ttl is the same content for
a human and for a diff.
Not a logical module. ROBOT's STAR and BOT extraction are right for an import and wrong for a pattern: measured on these sources, a PMDco pattern documented with eight terms came out with 119 classes and 1,508 axioms, because a BFO-based ontology connects everything to the upper-ontology spine and a logical module must follow it. All 36 came to 13 MB. So each pattern is the induced sub-ontology instead — its own terms, every axiom that mentions only those terms, their labels and definitions, and one level of parent above each so you can see where it sits without importing BFO. 6 to 26 classes each, 1.5 MB in total.
Separated by publisher as well as collection. Within the ODP collection the portal is a
catalogue of submissions published by thirteen different places — ontologydesignpatterns.org
has 81, the eCareAtHome smart-home set has 9, Poznań has 6, and three have no publisher at all
because their only recorded IRI is an absolute path from the machine the harvest ran on. Both
groupings are offered, with category and a search box as the other ways in.
Corrected in 1.92.0. Two of those ways in were not actually offered. By collection and Suggested for this ontology were both written in 1.87.0, both tested, both described here — and neither was in the dialog's dropdown, which still listed the three orderings that existed before them. The recommender below could not be selected by anybody for five releases. A test now reads the orderings off
PatternOrderand fails if one of them is not offered.
Suggestions for the ontology you have open — new in 1.87.0¶
162 patterns is too many to browse when you want one, so the browser's first ordering is Suggested for this ontology: the patterns whose vocabulary your ontology already speaks, best first, each with the reason.
The reason is the point. A ranked list with no evidence is a magic box, and somebody deciding
whether to adopt a stranger's pattern needs something checkable — "3 of its 4 terms are already
in your ontology: Agent, Role, hasRole" is, and 0.75 is not. Where your ontology has a label
for a matched term, its own label is used, because BFO_0000004 tells nobody anything and
"independent continuant" does.
Two signals, both chosen by measuring rather than reasoning. A pattern counts as relevant when your ontology uses the same IRIs, or has terms going by the same names; an exact IRI counts for double, since sharing a name is good evidence and not the same thing. Scored by how much of the pattern you already have, not by how many terms matched — ranking by match count put a 195-term pattern first on the strength of twenty-five generic words like "object" and "entity".
Two things the measurements overturned:
- The builtin vocabulary is not evidence. The first version looked like it worked: 43 of 123
patterns "shared an IRI" with a real project. Every one of those matches was
owl:Thing, which 42 patterns declare. A signal that fires on everything ranks nothing. - It cannot read the pattern files. Parsing all of them to answer one question took 24 seconds, measured — a dialog that looks like it has hung. Each pattern's terms are stored in the index instead, which is generated from the files and checked against them by a test, so the stored list cannot quietly disagree. 24 seconds became 0.3.
Importing a pattern is importing terms. Choosing one hands it to
Reuse → Import terms from a list…, which is the ODK path: it extracts a module, writes
<import>_terms.txt, saves the module, adds the import statement and adds the catalog entry.
Nothing here re-implements those five steps.
That is also why what arrives is smaller than the pattern file. 101 of the 123 patterns
declare owl:imports — 76 of them to the ODP annotation schema, four to DUL, and many to
sibling patterns. Copying a pattern file into a project would drag an upper ontology in behind
it. Extracting a module takes the terms you chose and the axioms that give them meaning, and
the details pane says so for any pattern that imports anything.
The pattern file is copied to src/ontology/mirror/<id>.owl first, because ROBOT needs a file
to read and a resource inside the plugin jar has no file path. mirror/ is where ODK keeps the
upstream copies a build extracts from, so the next refresh-imports finds it.
The counts shown are read from the file, never from the metadata beside it. That metadata
states a class count for every pattern and is wrong for 41 of the 123 — affordance claims two
classes and has eight, eventprocessing claims eight and has seventeen, vesselspecies claims
zero and has five — and a property count wrong for 17 more. A chooser that states a pattern's
size is making a claim, so the claim is computed when the pattern is opened.
Ten of the 123 are another one under a second name and say which: agentrole is
agent-role, partof is part-of, collection, collectionentity and collection-entity
are one pattern with three directories. Importing two of them would import the same terms twice.
So the library lists 123 entries and holds 113 patterns, which is worth knowing before counting
anything.
An imported pattern arrives as a group — new in 1.95.0¶
Choosing a pattern hands its terms to Import terms…, which writes the term list, the module, the import and the catalog entry. Since 1.95.0 the canvas also shows them together: the terms are laid out as one block inside a frame labelled with the pattern's name, placed clear of whatever is already on the board.
Before this they went wherever an unpositioned node goes — a row along the top, mixed in with everything else, with nothing to say that those eight classes arrived together or what they are.
Three things it deliberately does not do:
- It does not move terms you have already placed. A class you have arranged stays where you put it, and the status line says how many were left — so a frame holding less than the whole pattern is explained rather than puzzling.
- It does not lay the pattern out cleverly. A roughly square block, capped at five columns so a 26-term pattern does not become a frame four thousand pixels wide. The canvas already has six layout algorithms on the toolbar; Arrange on the frame's contents does a better job than a one-off ever would.
- It is one undo step. Ctrl+Z puts the board back.
A frame carries its contents when you drag it, so the pattern stays a unit afterwards.
Patterns of your own — new in 1.92.0¶
A pattern of your own is a file in a folder. No index, no metadata file, no registration, no
rebuild. Drop .owl, .ttl, .rdf, .owx, .omn or .ofn files into
and they are in the library the next time you open it, searchable, groupable, suggestible and
importable exactly like the 159 that ship. A sub-folder is a collection, so your group's
patterns sit together under its name beside odp, mwo, pmdco and nfdi.
Your patterns… in the dialog does both halves of this: it copies a file in for you, and it
shows and changes the folder. Point the folder inside an ODK project — src/patterns, say —
and the patterns travel with the repository, so everybody who clones it has them.
Nothing is asked for that the file already says. Measured across the 159 shipped patterns,
77 carry coversRequirements, 73 hasIntent, 62 rdfs:comment and 36 rdfs:label on the
ontology itself, so the name, the description, the competency questions and the publisher are
read out of the file rather than typed into a dialog. A file that documents nothing falls back to
its own filename and the host of its IRI — which is all but two of them for the publisher, since
only 2 declare one.
Those four numbers were wrong when first published, in 1.92.0, as 112/66/65/38. They came from a regular expression matching tag names inside the
<Ontology>element — and in OWL/XML, which every shipped pattern is, that element is the whole document, so an annotation on any class was counted as an annotation on the ontology. Re-measured in 1.93.0 with an XML parser reading only the ontology node's direct children. Two of the 159 files are not well-formed XML and parse in neither count, so the denominator is 157.
Why this needs no index when the shipped library does. A jar cannot be listed — under Felix a
classpath directory answers a bundle: URL that enumerates nothing, which is why
patterns/index.tsv exists at all. A directory on disk can be listed. The asymmetry is in the
container, not in the patterns.
Nothing is skipped in silence. A file that will not parse is named, with the parser's reason, behind a problems button in the dialog — and so is the count of files left unread when a folder holds more than 200, which is as many as this reads because each one has to be parsed. A library quietly holding fewer patterns than the folder does cannot be trusted about the ones it holds.
Your own ids never shadow a shipped pattern's. A file called componency.ttl becomes
componency-2, because three things key on the id — lookup, the parse cache, and the filename the
import copies the pattern out as.
Limits. The library knows nothing about what you have already imported, so it will not warn you that you imported this pattern last week; and note that a suggestion scoring 1.00 means your ontology already contains all of that pattern, which is a statement that you have applied it rather than advice to adopt it. A contributed pattern gets no category or domain, because nothing can infer one — it is grouped by its folder and its publisher instead.
Editing an import's terms — Imported terms…, since 1.132.0¶
OntoBoard → Reuse → Imported terms… (it replaced Term lists…) lists each import with every term it brings, by label: the ones on its term list, the ones that came along because a listed term needs them, and how often your own axioms use each. Add terms from … opens Find terms on the import's own source; Take out of the import removes listed terms; Rebuild from the source cuts it again when upstream has released. Each writes the term list, re-extracts the module by the project's declared method, keeps the IRI the module already has, and changes the copy Protégé has open — then saves it, so the board and the class tree show the change at once.
Two things this fixed on the way. Re-importing terms from an ontology that was already imported replaced its module and term list with the new terms alone; they are now added to it. And Refresh imports gave a rebuilt module the address of its own file as its IRI, so the edit file's import no longer named it; a module now keeps the IRI it declares.
Comments survive, because the file is spliced rather than rewritten. This is not a detail.
OntoBoard has been able to write a term list from scratch since 1.38.0, which is right for a
module it just extracted and wrong for one somebody maintains. Measured on a real
iao_terms.txt — 18 lines, 13 terms — reading the terms and writing them back leaves 15 of
those lines gone: every trailing # label comment, both of the file's own headers, and the
order re-sorted. Across that project all 50 terms in its five populated lists carry a trailing
comment and there are 11 standalone comment lines, so there was nothing unusual about the file
that got mangled.
Adding a term keeps all 20 lines of that file and adds exactly one; removing one takes only the line asked for. Both measured.
What this gives you that a text editor does not:
- each OBO number shown with the label your ontology gives it, resolved over the imports closure — a term list names terms from other ontologies, so the only place they are declared is inside an import;
- a line that was meant to be a term and is not, named rather than skipped — skipping means the module is quietly missing it;
- a term added from the Protégé selection, with its label written as the trailing comment if the file already annotates every term. A file whose convention is a label on every line should not acquire a bare entry, and a file with none should not acquire the project's first;
- a new entry placed after the last term rather than at the end of the file, since a list commonly closes with a comment and an entry under one would read as though that comment introduced it;
- the same two disk guards the configuration editor has: the file is re-read and compared before writing, so a change made underneath is not discarded, and the write goes through a temporary file and an atomic move.
Not here: reordering, editing a term in place (remove and add), and editing the comments themselves.
Term lists with comments — fixed in 1.77.0¶
Up to 1.76.0, Refresh imports… reported which imports had a term list and then could not rebuild any of them if those lists carried trailing comments — which is the format ODK and ROBOT both write:
Measured on a real repository at the time: five lists, 49 terms, none readable. They all read now, and a line that genuinely is not a term is still reported as malformed rather than skipped in silence.
Where an import comes from — fixed in 1.99.0¶
The same shape of defect, one layer up. Refresh imports found its imports by listing
imports/ and read each one's upstream source from a # Source: comment at the top of the term
list — a comment only OntoBoard ever writes. Measured across NFDIcore, MWO, PMDCO and ECTO:
39 term lists, none carrying it. So the rebuild worked on projects OntoBoard had created and
on no real one, and every import of every real repository reported "does not record where its
terms came from".
The project said so all along, in the place ODK itself looks:
import_group:
products:
- id: bfo
mirror_from: http://purl.obolibrary.org/obo/bfo/2020/notime/bfo.owl
module_type: mirror
- id: iao # no mirror_from: ODK downloads $(OBOBASE)/iao.owl
module_type: custom
- id: obi
module_type: slme
module_type_slme: BOT
slme_individuals: exclude
All 35 products those four projects declare now have a source. 15 are a kind OntoBoard rebuilds —
slme with BOT, TOP, STAR or MIREOT, run with the project's own method and individuals setting
rather than this plugin's default. The other 20 are named with the reason instead:
module_type |
why not | count |
|---|---|---|
custom |
ODK's own generated rule for it prints "This rule needs to be overwritten in <project>.Makefile" and fails — the real command is hand-written there, and a generic rebuild would overwrite it |
14 |
slme with SUBSET |
ROBOT reaches that extraction another way; rebuilding with BOT would quietly produce a different module | 3 |
mirror |
the module is the mirrored ontology — ODK's rule has no term list at all, so there is nothing to extract | 2 |
filter |
ODK extracts BOT and then removes the axioms outside the base IRIs; without that removal the module comes out larger than the repository's | 1 |
Reading the declaration also makes two states visible that a directory scan cannot see:
- Declared and absent — a product in the YAML with nothing built for it. Exactly where adding one to the list leaves you.
- On disk and undeclared — a module in
imports/that no product names. ODK builds itsIMPORTSlist fromimport_group.products, somake refresh-importsnever touches that file and nothing regenerates it. Three of the four projects had one, and so did every project OntoBoard made, because Import terms… did not add to the scaffold'sproducts: []. It now declares the product it builds — except for a pattern from the library, below.
Such a module is still rebuilt from its # Source: comment when it has one, because that is what
a refresh did before — reading the declaration must not take a working rebuild away.
A pattern from OntoBoard's library is the exception. Pattern library… → Import terms…
records the pattern's own IRI as the term list's # Source:, adds a # Pattern: <id> line, and
declares no product. The module is cut from the copy the library ships, which the pattern's IRI
need not serve, and that portal is often down — so nothing downloads it. Refresh imports lists it
as pattern library and leaves it alone; to change it, import the pattern again. Until 1.130.0
the source recorded, and the mirror_from declared, was the library's working copy: a file:
path on the importing machine, committed with the project and resolvable on no other.
5. Proposing the change¶
Share → Pull request…, since 1.101.0. The last step that used to send you to a browser.
Show what is open lists the repository's pull requests — number, state, title, branch and author — with each URL beside it.
Open a pull request for this branch proposes the branch you are on. It asks for a title, a description, the branch to merge into (empty means the repository's default, which an ODK project's is not always) and whether to open it as a draft.
It runs the GitHub CLI rather than storing a token. A pull request needs an authenticated
GitHub session; gh already holds one in your system keyring, put there by gh auth login and
scoped by you. OntoBoard keeps no credentials of its own, which is the same reason it runs real
git, real make and real ROBOT rather than reimplementing them.
Nothing is proposed without being shown first. This is the one operation in OntoBoard that other people see the moment it happens — it sends mail, it appears on a board, it asks for somebody's time. So three things happen in order, cheapest first:
- the branch is checked for being proposable at all — a detached HEAD, the base branch itself,
or a branch nobody has pushed each get their own message rather than a
gherror about refs; gh pr create --dry-runvalidates the branch, the base and the remote without creating anything;- the exact command is shown, and nothing runs until you approve that text.
Without gh there is still an answer. If it is not installed or not logged in, the result
says which of the two and how to fix it — and gives you GitHub's own compare URL for your
branch, which opens the "open a pull request" form with no install and no login. It is the one
place in this plugin where leaving Protégé is the honest recommendation.
Reviewing one is still the browser's job. Reading a diff, commenting and merging are a larger piece of work, and for an ontology a line diff of RDF/XML is not the diff anybody wants — Build and release → Compare releases… is, and it already exists.
6. Releasing, in either convention¶
Build and release → Release… turns the edit file into the published one: it runs the reasoner, writes the inferences down, stamps a version, keeps a copy that will never change again, and refuses if the project’s own quality profile reports an error or if a term the last release published has vanished.
The version is written in one of two conventions, and from 1.117.0 you choose which. They are not a formatting preference — they publish different IRIs, and a version IRI cannot be taken back once anybody has imported it:
| Dated (OBO) | Semantic (semver) | |
|---|---|---|
| version | 2026-10-12 |
1.2.0 |
owl:versionIRI |
<base>/releases/2026-10-12/<id>.owl |
<base>/1.2.0 |
| kept at | releases/2026-10-12/<id>.owl |
releases/1.2.0/<id>.owl |
| next one | whatever day it is | a decision: patch, minor or major |
The dated form is what OBO publishes and what the generated Makefile defaults to
(VERSION = $(TODAY)). The semantic form numbers releases MAJOR.MINOR.PATCH as
semver.org defines it: a project at 1.2.0 publishes
owl:versionIRI <base>/1.2.0 and owl:versionInfo "1.2.0".
The default is read from what the ontology already declares, so releasing a project you did
not create continues its history instead of starting a second one beside it. A semantic release
also links owl:priorVersion to the one before it, so a consumer can walk the chain backwards.
The build does both too. The generated Makefile takes VERSION and VERSION_IRI as
overrides, which is how release.sh drives it:
And if the project keeps a release.sh whose VERSION disagrees with what you are about to
cut, the result says so before anything is written — two release paths with two versions is how
a project publishes 3.0.2 twice.
7. Publishing the documentation¶
Build and release → Documentation…, since 1.103.0. Widoco turns an ontology's own annotations into the HTML page an ODK project publishes; this runs it on the release product and opens the result, so you can see the page without pushing and waiting for a deploy.
Widoco is installed, not bundled, and the reason is measured. It is not on Maven Central at all, and its only published form is a 40,858,575-byte shaded jar containing 1,533 OWL API entries and 2,044 Guava entries — packages this plugin imports from Protégé. A second copy of those inside the bundle is how an OSGi plugin stops resolving. So OntoBoard finds the jar the way it finds Konclude, and since 1.135.0 offers to get it when it cannot. Download… saves the release below into a folder you choose and keeps it only if its SHA-256 is the one OntoBoard was built against; Copy link and Open release page are for fetching it with your browser, after which Choose file… takes the jar:
widoco-1.4.25-jar-with-dependencies_JDK-11.jar (about 39 MB)
https://github.com/dgarijo/Widoco/releases/download/v1.4.25/
It needs Java 11. The jar is compiled to class file version 55. Measured on both hosts:
| bundled Java | can run Widoco | |
|---|---|---|
| Protégé 5.6.9 | Temurin 11.0.25 | yes, with its own JVM |
| Protégé 5.5.0 | 1.8.0_121 | no — point the Run it with field at a JDK 11 |
The dialog shows which Java it found and what version it reported, so a failure says your Protégé's Java is too old rather than printing a class-file error - and where to get a newer one.
Downloading from Protégé 5.5.0. Its Java, 1.8.0_121, predates the certificate authority GitHub's downloads use and cannot check them. On Windows Download… then checks them against Windows' own certificate store instead, which works (measured); on Linux it tries the system's Java certificate store where there is one; on macOS it says so, and the browser is the way round.
The release product, not the edit file. An edit file imports its modules rather than containing them, so a page made from it covers the terms you wrote and none you reuse — and the version IRI Release… stamps is not in it yet. Documenting the open ontology is offered as a preview of unreleased work, with a warning saying exactly that.
Success is the page existing, not the exit code. A real run exited 0 while printing
ERROR … Could not generate changelog: Widoco could not fetch the previous release to diff
against and quietly left that section out. So the page is checked for directly, and the log
lines worth reading are reported beside it — the two Xerces warnings every run prints are
dropped, because nobody can act on them.
It writes to a scratch folder, build/documentation, not to docs/. That directory is
published, and a generated page landing there would be committed by accident.
What this adds up to¶
OntoBoard is strongest where ODK needs ROBOT and a reasoner: building, reporting, explaining,
extracting modules, comparing releases, minting identifiers. It is weakest wherever the ODK
workflow routes through <ontology>-odk.yaml, because most of that file is still text it keeps
rather than configuration it acts on.
Since 1.80.0 the YAML is shown and its scalars are editable, and since 1.81.0 the project's own scripts are listed and runnable — the two gaps that used to head this list. What is left, in the order it is worth doing:
- ~~Adding or removing a list entry in the YAML~~ — done in 1.97.0. Project → Project configuration… has Add to a list… and Remove entry: pick a list, type the value, and it is appended; select an entry and it goes, taking its continuation lines with it if it is a block. Indentation is copied from the list's own last entry rather than computed, because ODK files in the wild indent sequences both ways and guessing would reformat the file on the first edit. A value YAML would read as a number or a boolean is quoted for you; an ordinary word, or an IRI, is not.
-
Acting on more of what the YAML says —
robot_java_argsand tworobot_reportkeys drive something since 1.91.0,export_formatssince 1.98.0, andimport_group.productssince 1.99.0: Reuse → Refresh imports now gets its list of imports and each one's upstream source from the project, rather than by listingimports/and reading a# Source:comment only OntoBoard ever wrote. Across NFDIcore, MWO, PMDCO and ECTO there are 39 term lists and not one carries that comment, so the rebuild used to work on projects OntoBoard had made and on no real one; all 35 products those four declare now have a source, 15 of them a kind this rebuilds, and the other 20 are named with the reason — most oftenmodule_type: custom, which ODK's own generated rule refuses too.Reading the declaration also turns up two things a directory scan cannot see. An import can be declared and absent, which is the state adding a product leaves you in. And an import can be on disk and undeclared, which means ODK's
IMPORTSlist has no entry for it andmake refresh-importsnever touches it — three of those four projects have one, and so does every project OntoBoard makes, because the scaffold writesproducts: []and Import terms… does not add to it. OntoBoard reports that about itself now; making Import terms… declare the product is the next piece of work, and needs a YAML operation that can seed an empty list.release_artefactsandprimary_releaseare still text OntoBoard preserves and does not obey;release_artefactsin particular means three content variants (base, full, simple), which is a larger job than a second writer. 3. Knowing what you already imported — the pattern library will happily suggest a pattern whose terms are already in an import you wrote last week, and a score of 1.00 means exactly that without saying so. Your own patterns can be contributed without a rebuild since 1.92.0, which was the item that used to sit here.