Skip to content

RAID-831: DataCite 4.7 LinkML schema + RAiD→DataCite crosswalk (spike) - #620

Merged
robleney-ardc merged 7 commits into
mainfrom
feature/RAID-831
Sep 1, 2026
Merged

RAID-831: DataCite 4.7 LinkML schema + RAiD→DataCite crosswalk (spike)#620
robleney-ardc merged 7 commits into
mainfrom
feature/RAID-831

Conversation

@robleney-ardc

Copy link
Copy Markdown
Contributor

What and why

RAID-831 is a spike: investigate a LinkML-driven RAiD→DataCite metadata crosswalk. This PR delivers the spike's two artifacts and a feasibility verdict. It does not change any runtime behaviour.

Ticket: RAID-831. Related: RAID-796 (DataCite schema alignment epic), RAID-832 (re-sync mechanism, which defers the crosswalk here), RAID-377 (mapping update to DataCite 4.7), RAID-776 (origin spike that proposed this).

Deliverables

  • api-svc/datamodel/src/v2/datacite.yaml — a hand-authored LinkML rendering of the DataCite 4.7 metadata schema (24 classes, 10 controlled-vocabulary enums with values taken verbatim from the XSD). It is an inert spike artifact: not referenced by the datamodel gradle build, so it does not affect codegen or CI. Validates with gen-json-schema via linkml/linkml:1.9.2 (exit 0, no warnings).
  • doc/reference/raid-datacite-crosswalk.md — a field-by-field RAiD→DataCite crosswalk authored from the current Java factories under api-svc/raid-api/.../factory/datacite/, including the controlled-vocabulary lookup tables and a declarable-vs-imperative verdict.

Feasibility verdict (the spike's answer)

  • Automated XSD→LinkML does not work off-the-shelf. schemauto import-xsd (linkml/schema-automator, the only maintained importer) fails on DataCite's xs:all root, and after that workaround fails again in its simpleContent/extension handling. The schema was therefore hand-authored from the XSD.
  • The mapping is a hybrid, not fully declarative. The vocabulary/structural layer (title/description/role/relation/resource-type lookups, constants, identifier renames) maps well to LinkML exact_mappings + linkml-map, with the code→enum maps expressible as SSSOM tables. But two things form an imperative floor that cannot be declarative: every person/org name is resolved live from ORCID/ISNI/ROR at emission time, and the single RAiD organisation[] list is split into contributors vs. fundingReferences (with a "latest role" reduce) by predicate logic. Recommendation: declarative vocab tables feeding a thin imperative adapter.

Targeted at DataCite 4.7

Built against 4.7 (not 4.6) to match RAID-377. The 4.6→4.7 delta captured here: relatedIdentifierType adds RAiD and SWHID; resourceTypeGeneral adds Poster/Presentation; relationType adds Other; and a new optional relationTypeInformation on relatedIdentifier/relatedItem. Notably, 4.7 adding RAiD natively is what makes RAID-797's native-RAiD emission schema-valid (RAID-797 emitted it ahead of the published XSD, relying on the REST API accepting it early).

Bugs surfaced (not fixed here — flagged for their own tickets)

  • publicationYear is hardcoded to Year.now() in all three DataciteAttributesDtoFactory overloads (there is a // TODO), so an update in a later year silently rewrites the DOI's publication year.
  • rightsList is never emittedDataciteRightFactory is injected but never called, so RAiD's licence never reaches DataCite.

🤖 Generated with Claude Code

robleney-ardc and others added 7 commits August 21, 2026 06:16
Spike deliverable: a hand-authored LinkML rendering of the DataCite 4.7
metadata schema (the importer schemauto import-xsd does not handle this XSD),
and a field-by-field RAiD->DataCite crosswalk authored from the current Java
factories, with a declarable-vs-imperative feasibility verdict.

The LinkML schema is an inert spike artifact (not wired into the datamodel
gradle build). Validated with gen-json-schema via linkml/linkml:1.9.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…bset

Configures the declarable part of the RAiD->DataCite crosswalk as a linkml-map
TransformationSpecification (constant types, titles/descriptions with vocab
enum_derivations, dates concatenation) and transforms a sample RaidDto into a
DataCite 4.7-shaped instance against the real raid-core.yaml (verified, exit 0).
Confirms the imperative floor (live PID name resolution, org split) cannot be
expressed. Cross-referenced from the crosswalk doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… not achievable in-app)

Adds an execution-model section to the crosswalk: linkml-map is Python with no
JVM runtime, so the transform can inform Java code but cannot drive it; the
factories stay (mapping stays in code); porting linkml-map is a large, ongoing
build dominated by re-creating ~20k LOC of linkml_runtime plus expr-semantics
parity, and still cannot cross the imperative floor. Realistic win = a governed,
single-sourced vocabulary crosswalk consumed by the existing factories.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The crosswalk note answered questions 1-3 and part of 5. Adds the rest, plus a
new assessment the earlier execution-model section did not cover.

- Hand-rolled build-time generator: feasible, and the three objections to
  porting linkml-map do not apply (own dialect, no expression language, no
  upstream to track). Precedent is already in buildSrc (AddStaticEnums,
  GenerateReferenceDataTask, Utils) and openApiGenerate; needs no Docker, so it
  can run in CodeBuild. Buys a build-time exhaustiveness check that fixes the
  silent-null unmapped-vocab issue. Recommend the governed vocab table first,
  generation as its destination.
- Q5: resolution as an enrichment step before the crosswalk, and Appendix 3
  unknown values (:unav) at that boundary; richness gaps listed.
- Q4: version per (RAiD, DataCite) pair; re-sync via RAID-832's flag + worker.
- Q6: same toolset, different mechanism; narrows RAID-776's "mappings as
  config" claim to schema.org only.
- Q7: RAID-797 as the worked example - two production lines; the cost was
  noticing and re-pushing, not the edit.
- Testing NFR: four layers, incl. the gated DATACITE_LIVE_TEST pattern.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Link the two surfaced bugs to their tickets: RAID-858 (publicationYear
  hardcoded to Year.now()) and RAID-859 (rightsList never emitted). Both
  re-verified against post-merge code and still present on main.
- RAID-797 has merged, so relatedRaid now emits the native "RAiD"
  relatedIdentifierType, not "DOI". Update the field-mapping row and mark
  Known issue 3 resolved, retained for provenance.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@robleney-ardc
robleney-ardc merged commit f4f376b into main Sep 1, 2026
8 checks passed
@robleney-ardc
robleney-ardc deleted the feature/RAID-831 branch September 1, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants