indicium-adapters 0.6.0__tar.gz

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.
Files changed (59) hide show
  1. indicium_adapters-0.6.0/.github/workflows/ci.yml +32 -0
  2. indicium_adapters-0.6.0/.github/workflows/release.yml +97 -0
  3. indicium_adapters-0.6.0/.gitignore +12 -0
  4. indicium_adapters-0.6.0/CHANGELOG.md +292 -0
  5. indicium_adapters-0.6.0/CITATION.cff +25 -0
  6. indicium_adapters-0.6.0/CONTRIBUTING.md +39 -0
  7. indicium_adapters-0.6.0/LICENSE +21 -0
  8. indicium_adapters-0.6.0/PKG-INFO +311 -0
  9. indicium_adapters-0.6.0/README.md +256 -0
  10. indicium_adapters-0.6.0/RELEASING.md +190 -0
  11. indicium_adapters-0.6.0/SECURITY.md +14 -0
  12. indicium_adapters-0.6.0/docs/assets/brand/indicium-mark-dark.svg +12 -0
  13. indicium_adapters-0.6.0/docs/assets/brand/indicium-mark.svg +12 -0
  14. indicium_adapters-0.6.0/pyproject.toml +70 -0
  15. indicium_adapters-0.6.0/scripts/check_release.py +55 -0
  16. indicium_adapters-0.6.0/src/indicium_adapters/__init__.py +281 -0
  17. indicium_adapters-0.6.0/src/indicium_adapters/asb/__init__.py +57 -0
  18. indicium_adapters-0.6.0/src/indicium_adapters/asb/adapter.py +170 -0
  19. indicium_adapters-0.6.0/src/indicium_adapters/asb/biotools.py +101 -0
  20. indicium_adapters-0.6.0/src/indicium_adapters/asb/cli.py +166 -0
  21. indicium_adapters-0.6.0/src/indicium_adapters/asb/loader.py +273 -0
  22. indicium_adapters-0.6.0/src/indicium_adapters/asb/mapper.py +259 -0
  23. indicium_adapters-0.6.0/src/indicium_adapters/asb/models.py +169 -0
  24. indicium_adapters-0.6.0/src/indicium_adapters/asb/prov.py +66 -0
  25. indicium_adapters-0.6.0/src/indicium_adapters/asb/round_trip.py +159 -0
  26. indicium_adapters-0.6.0/src/indicium_adapters/composite.py +99 -0
  27. indicium_adapters-0.6.0/tests/__init__.py +0 -0
  28. indicium_adapters-0.6.0/tests/asb/__init__.py +0 -0
  29. indicium_adapters-0.6.0/tests/asb/fixtures/indicium_graph.ttl +15 -0
  30. indicium_adapters-0.6.0/tests/asb/fixtures/indicium_graph_broken.ttl +5 -0
  31. indicium_adapters-0.6.0/tests/asb/fixtures/sample_collection/claims.yaml +15 -0
  32. indicium_adapters-0.6.0/tests/asb/fixtures/sample_collection/collection.yaml +10 -0
  33. indicium_adapters-0.6.0/tests/asb/fixtures/sample_collection/skills/feature-detection-lcms/SKILL.md +24 -0
  34. indicium_adapters-0.6.0/tests/asb/fixtures/sample_collection/tools/mzmine.yaml +13 -0
  35. indicium_adapters-0.6.0/tests/asb/fixtures/skipped_skill_collection/collection.yaml +9 -0
  36. indicium_adapters-0.6.0/tests/asb/fixtures/skipped_skill_collection/leaves/skill-with-claim-and-empty-span/SKILL.md +12 -0
  37. indicium_adapters-0.6.0/tests/asb/fixtures/slug_list_collection/collection.yaml +19 -0
  38. indicium_adapters-0.6.0/tests/asb/fixtures/slug_list_collection/leaves/skill-with-claim/SKILL.md +28 -0
  39. indicium_adapters-0.6.0/tests/asb/fixtures/slug_list_collection/leaves/skill-with-provenance-papers/SKILL.md +24 -0
  40. indicium_adapters-0.6.0/tests/asb/fixtures/slug_list_collection/skills/router/SKILL.md +6 -0
  41. indicium_adapters-0.6.0/tests/asb/fixtures/slug_list_collection/tools/pathway-tool.yaml +6 -0
  42. indicium_adapters-0.6.0/tests/asb/fixtures/slug_list_collection/tools/plain-tool.yaml +9 -0
  43. indicium_adapters-0.6.0/tests/asb/fixtures/unlisted_claims_collection/collection.yaml +7 -0
  44. indicium_adapters-0.6.0/tests/asb/fixtures/unlisted_claims_collection/leaves/skill-declaring-claims/SKILL.md +8 -0
  45. indicium_adapters-0.6.0/tests/asb/test_adapter.py +147 -0
  46. indicium_adapters-0.6.0/tests/asb/test_biotools.py +69 -0
  47. indicium_adapters-0.6.0/tests/asb/test_cli.py +191 -0
  48. indicium_adapters-0.6.0/tests/asb/test_cli_optional_dependencies.py +34 -0
  49. indicium_adapters-0.6.0/tests/asb/test_loader.py +138 -0
  50. indicium_adapters-0.6.0/tests/asb/test_mapper.py +313 -0
  51. indicium_adapters-0.6.0/tests/asb/test_models.py +69 -0
  52. indicium_adapters-0.6.0/tests/asb/test_package.py +8 -0
  53. indicium_adapters-0.6.0/tests/asb/test_prov.py +35 -0
  54. indicium_adapters-0.6.0/tests/asb/test_round_trip.py +170 -0
  55. indicium_adapters-0.6.0/tests/test_classification_vocabulary.py +183 -0
  56. indicium_adapters-0.6.0/tests/test_composite.py +291 -0
  57. indicium_adapters-0.6.0/tests/test_protocol.py +263 -0
  58. indicium_adapters-0.6.0/tests/test_release_metadata.py +126 -0
  59. indicium_adapters-0.6.0/uv.lock +708 -0
@@ -0,0 +1,32 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - name: Set up Python ${{ matrix.python-version }}
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - name: Install dependencies
22
+ run: |
23
+ python -m pip install --upgrade pip
24
+ # indicium-standard resolves from PyPI (2.0.0 and later).
25
+ # .[asb] brings httpx + pydantic + pyyaml for the src/indicium_adapters/asb
26
+ # submodule that tests/asb exercises; .[dev] brings pytest, pytest-cov,
27
+ # pyshacl and the pinned ruff.
28
+ pip install -e ".[dev,asb]"
29
+ - name: Lint
30
+ run: ruff check .
31
+ - name: Run tests
32
+ run: pytest --cov=indicium_adapters --cov-report=term-missing
@@ -0,0 +1,97 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v7
16
+
17
+ - uses: astral-sh/setup-uv@v7
18
+
19
+ - name: Check tag, version and release date
20
+ run: uv run --no-project --python 3.12 python scripts/check_release.py --tag "$GITHUB_REF_NAME" --notes-output "$RUNNER_TEMP/release-notes.md"
21
+
22
+ - name: Require indicium-standard on PyPI
23
+ run: |
24
+ curl -sSf -o /dev/null https://pypi.org/pypi/indicium-standard/2.0.0/json || {
25
+ echo "::error::indicium-standard 2.0.0 is not on PyPI yet; release after it"
26
+ exit 1
27
+ }
28
+
29
+ - name: Install, lint and test
30
+ run: |
31
+ uv venv --python 3.12 .venv
32
+ uv pip install --python .venv/bin/python --no-sources -e ".[dev,asb]"
33
+ .venv/bin/ruff check .
34
+ .venv/bin/pytest
35
+
36
+ - name: Build and check distributions
37
+ run: |
38
+ rm -rf dist
39
+ uv build --no-sources
40
+ uvx --from twine==7.0.0 twine check --strict dist/*
41
+
42
+ - name: Check installed wheel
43
+ run: |
44
+ uv venv --python 3.11 .wheel-env
45
+ uv pip install --python .wheel-env/bin/python --no-sources dist/*.whl
46
+ uv pip check --python .wheel-env/bin/python
47
+ .wheel-env/bin/python -c "import sys, indicium, indicium_adapters; from importlib.metadata import version; v = version('indicium-adapters'); assert 'v' + v == sys.argv[1], v; print('indicium-adapters', v, 'indicium-standard', version('indicium-standard'))" "$GITHUB_REF_NAME"
48
+
49
+ - uses: actions/upload-artifact@v7
50
+ with:
51
+ name: release-distributions
52
+ path: dist/*
53
+ if-no-files-found: error
54
+
55
+ - uses: actions/upload-artifact@v7
56
+ with:
57
+ name: release-notes
58
+ path: ${{ runner.temp }}/release-notes.md
59
+ if-no-files-found: error
60
+
61
+ publish:
62
+ needs: build
63
+ runs-on: ubuntu-latest
64
+ environment:
65
+ name: pypi
66
+ url: https://pypi.org/p/indicium-adapters
67
+ permissions:
68
+ id-token: write
69
+ steps:
70
+ - uses: actions/download-artifact@v8
71
+ with:
72
+ name: release-distributions
73
+ path: dist
74
+
75
+ - name: Publish to PyPI with Trusted Publishing
76
+ uses: pypa/gh-action-pypi-publish@release/v1
77
+
78
+ github-release:
79
+ needs: publish
80
+ runs-on: ubuntu-latest
81
+ permissions:
82
+ contents: write
83
+ steps:
84
+ - uses: actions/download-artifact@v8
85
+ with:
86
+ name: release-distributions
87
+ path: dist
88
+
89
+ - uses: actions/download-artifact@v8
90
+ with:
91
+ name: release-notes
92
+ path: release-notes
93
+
94
+ - uses: softprops/action-gh-release@v3
95
+ with:
96
+ body_path: release-notes/release-notes.md
97
+ files: dist/*
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ dist/
7
+ build/
8
+ .coverage
9
+ htmlcov/
10
+ *.so
11
+ .venv/
12
+ .wheel-env/
@@ -0,0 +1,292 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ---
9
+
10
+ ## [0.6.0] - 2026-11-02
11
+
12
+ First release published to PyPI. 0.5.0 was never tagged or published, and its
13
+ changes ship in this release; the tag before this one is `v0.4.0`.
14
+
15
+ ### Added
16
+ - `ClassificationVocabularyProvider`, an opt-in protocol for an adapter that
17
+ declares curated classification vocabulary: `primary_domain`, `subdomains` and
18
+ `techniques`. `classification_vocabulary(adapter)` reads the three members into a
19
+ `ClassificationVocabulary` and never substitutes `domain_id` for a missing
20
+ `primary_domain`; an adapter that declares nothing reports `primary_domain=None`.
21
+ (#5)
22
+ - Convention: `DomainAdapter.enrich_claim()` MAY set an
23
+ `ontology_term_justifications: dict[str, str]` companion map alongside
24
+ `ontology_terms`, mapping the same slot keys to SEMAPV justification CURIEs (e.g.
25
+ `semapv:CompositeMatching`, `semapv:LexicalMatching`). Downstream SSSOM emitters
26
+ (Perspicacité) use this as `mapping_justification`; absent keys default to
27
+ `semapv:CompositeMatching`. Documented in the `enrich_claim` docstring and the
28
+ README.
29
+ - `RoundTripResult.skills_checked`, also printed by `verify-claims` and returned
30
+ under `skills_checked` in its JSON: the number of skills read from disk, so a pass
31
+ that read no skill is visible.
32
+ - `RoundTripResult.skills_listed` and `skills_skipped`, also in the `verify-claims`
33
+ JSON and pretty output: the entries under `skills` in `collection.yaml`, and the
34
+ slug and reason of each listed skill that has no SKILL.md or fails validation.
35
+ The loader keeps such skills in `ASBCollection.skipped_skills` (new model
36
+ `ASBSkippedSkill`) instead of dropping them.
37
+ - `ASBClaimRecord.context`, the SuperPattern context slot. The mapper writes it as
38
+ `asb:context`, which the indicium `asb:Claim` shape requires, when the record has
39
+ one. An earlier entry and the body of commit 8ebaff0 said ASB claim records carry
40
+ no context; ASB's own claims do, and it was this package's record that lacked the
41
+ field.
42
+ - `RoundTripResult.skills_with_unlisted_claims`: skills whose frontmatter declares
43
+ `n_claims` above 0 but lists no claim IRI. `verify-claims` returns it in its JSON
44
+ and prints a warning on stderr; it does not fail the gate. `ASBSkillRecord` reads
45
+ `n_claims`.
46
+ - **The round-trip gate fails on a graph that indicium's shapes would not check.**
47
+ `run_round_trip_check()` and `verify-claims` now fail (exit 1) when no node of the
48
+ indicium graph is typed with a class an indicium SHACL shape targets: validating
49
+ such a graph has no focus node and conforms whatever it holds. The count is
50
+ `RoundTripResult.shape_targeted_nodes`, returned as `shape_targeted_nodes` in the
51
+ `verify-claims` JSON. `shape_targeted_classes()` reads the targets from the shape
52
+ files indicium ships, and `count_shape_targeted_nodes()` counts a graph's nodes.
53
+ - `tests/test_release_metadata.py`: `CITATION.cff` and `pyproject.toml` carry the
54
+ same version, `indicium_adapters.asb.__version__` is the installed distribution
55
+ version, this file has a section for that version and at most one Unreleased
56
+ section, and `SECURITY.md` supports the `pyproject.toml` major.minor line.
57
+ - `.github/workflows/release.yml`: on a `v*` tag, checks the tag, the versions and
58
+ the changelog date (`scripts/check_release.py`), runs lint and tests, builds the
59
+ sdist and the wheel with `uv build`, runs `twine check --strict`, installs the
60
+ wheel in a clean environment and imports it, publishes to PyPI with Trusted
61
+ Publishing from the `pypi` environment, and creates the GitHub release with the
62
+ two distributions. `RELEASING.md` holds the checklist.
63
+
64
+ ### Changed
65
+ - **The dependency is the renamed `indicium-standard>=2.0.0`** (was `indicium>=1.7`),
66
+ and the version moved to 0.6.0 in the same commit. The import name stays
67
+ `indicium`. The ASB mapper requires indicium main at or after 62c7f52, the first
68
+ commit exporting `ASB_BASE` and `INDICIUM_BASE`; earlier 2.0.0 builds satisfy the
69
+ floor but make `import indicium_adapters.asb` raise `ImportError`.
70
+ - **`requires-python` lowered from `>=3.12` to `>=3.11`.** Nothing in the package
71
+ needed 3.12: all 24 source and test modules parse under 3.11 (no PEP 695
72
+ type-parameter syntax, no `type` alias statements), there is no 3.12-only stdlib
73
+ API in use, and the only runtime dependency, `indicium-standard`, already declares
74
+ `>=3.11`. At that commit the suite passed unchanged on 3.11: 73 passed, 4 skipped,
75
+ 90.31% coverage.
76
+
77
+ The floor mattered because it made every `DomainAdapter` uninstallable in the
78
+ AgenticScienceBuilder environment, which runs Python 3.11 and is where the
79
+ classification vocabulary adapters feed is actually consumed. With the floor
80
+ lowered, installing `indicium-adapters` plus `indicium-adapters-astronomy` into
81
+ that environment takes ASB's controlled vocabulary from 17 primary domains to 18,
82
+ adding `astronomy` along with its 11 subdomains and its techniques; previously an
83
+ astronomy paper was classified as `other` no matter what.
84
+ - Dropped the `Programming Language :: Python :: 3.10` classifier. It advertised a
85
+ version that could never resolve, since `indicium-standard` requires `>=3.11`.
86
+ - **The ASB mapper takes its vocabulary bases from indicium** (`ASB_BASE`,
87
+ `INDICIUM_BASE`) instead of the hardcoded
88
+ `https://w3id.org/holobiomicslab/indicium#`. A claim is typed `asb:Claim`; its
89
+ subject, predicate and object fill `asb:subject`, `asb:relation` and `asb:object`,
90
+ the terms the indicium `asb:Claim` shape declares. A qualifier rides
91
+ `asb:qualifier` when it is a closed Bucur term and `asb:domainQualifier`
92
+ otherwise, as the `DomainAdapter.qualifiers` contract states. The other mapper
93
+ terms keep their local names under `INDICIUM_BASE`. No file in the current
94
+ collections uses the old namespace, so it is not read as an alias.
95
+ - **Evidence spans, tools and papers are typed and described with indicium's own
96
+ terms, so its shapes check them.** On the metabolomics v2 collection the graph
97
+ typed 0 of its 22,753 typed nodes with a class an indicium shape targets, and
98
+ `validate_graph` conformed without checking anything. An evidence span is now an
99
+ Evidence, `sepio:0000002` (was `indicium:Evidence`), with `indicium:eco_code`,
100
+ `indicium:quote_exact`, `indicium:source_doi` and `indicium:paragraph_id`. A tool
101
+ is a SoftwareEntity, `fabio:ComputerProgram` (was `indicium:SoftwareEntity`), with
102
+ its IRI in `dct:identifier`, `indicium:canonical_url` as an IRI (a value that is
103
+ not an absolute IRI stays a literal, which the shape reports) and
104
+ `indicium:edam_operation` and `indicium:edam_topics`. A paper is a Source,
105
+ `fabio:Expression` (was `indicium:InformationSource`), with its DOI in
106
+ `dct:identifier` (was `cito:hasIdentifier`) and its title in `dct:title`. The v2
107
+ graph now has 16,894 nodes the shapes target, and it conforms.
108
+ - `ASBEvidenceSpan.eco_code` defaults to `textual_quotation`, the EcoCodeAsbSubset
109
+ value indicium maps to `ECO:0000033`. The former default `eco:textual-quotation`
110
+ was written as `ECO_textual-quotation`, an ECO IRI that does not exist. Every
111
+ EcoCodeAsbSubset value maps to the ECO term indicium gives it, an `ECO:` CURIE to
112
+ its IRI, and any other value stays a literal that the indicium shape reports.
113
+ - `indicium:bio_tools_id` is a string, written only when a bio.tools id is known;
114
+ an unresolved id is no longer written as `rdf:nil`, which the shape rejects.
115
+ - `ASBAdapter` writes one paper node per DOI. When the records citing a paper give
116
+ it different titles it gets no `dct:title`, which the indicium shape allows once,
117
+ and the adapter warns with their count and the first ten DOIs: 50 of the 498
118
+ papers of metabolomics v2.
119
+ - Skill and tool instance IRIs keep the `https://w3id.org/holobiomicslab/asb-skill/`
120
+ and `.../asb-tool/` form that ASB's release code mints, now exported as
121
+ `ASB_SKILL_BASE` and `ASB_TOOL_BASE`. `ASBAdapter` binds the `asb:` prefix to the
122
+ vocabulary and `asbskill:` to the skill instances.
123
+ - `ASBSourcePaper.title` may be empty: current releases leave it empty when the
124
+ title was not resolved, and the DOI identifies the paper. The mapper writes no
125
+ empty title.
126
+ - A skill or tool record that fails validation is skipped with a warning; the
127
+ loader no longer catches every exception, and `tools.lock.yaml` entries that fail
128
+ are no longer dropped silently.
129
+ - `indicium_adapters.asb.__version__` reads the installed distribution version (it
130
+ was a hardcoded `0.5.0`). `CITATION.cff` moves from 0.4.0 to 0.6.0, gains the
131
+ author's ORCID and drops the `date-released` of 0.4.0, since 0.6.0 has no release.
132
+ - `SECURITY.md` writes its supported-versions table in plain text and lists 0.6.x
133
+ as the supported line (it still listed 0.4.x).
134
+ - CI tests Python 3.11, 3.12 and 3.13. `ruff check .` now fails the job on any
135
+ finding; ruff is pinned to 0.17.3 in the `dev` extra because each release widens
136
+ its default rule set. The extras install no longer falls back to a bare install.
137
+ CI installs `indicium-standard` from the public repository, pinned to commit
138
+ 500dd82, and no longer needs the `ORG_PAT` token; the pin moves to PyPI once
139
+ 2.0.0 is published.
140
+ - `uv.lock` is refreshed: it still declared `requires-python = ">=3.12"`.
141
+ - `pyproject.toml` declares `readme = "README.md"`. The built wheel's METADATA had
142
+ no long description, so its package index page would have been empty; it now
143
+ carries the README with `Description-Content-Type: text/markdown`, and
144
+ `twine check --strict` passes on the wheel and the sdist.
145
+
146
+ ### Fixed
147
+ - **`verify-claims` raised `AttributeError: 'str' object has no attribute 'get'` on
148
+ every current collection.** `load_collection()` read only the earlier layout,
149
+ where `collection.yaml` lists skills as mappings. Current releases list skills and
150
+ tools as slugs, keep leaf skills in `leaves/<slug>/SKILL.md`, put the collection
151
+ IRI under `iri`, write evidence spans as plain strings and papers under
152
+ `derived_from` or `provenance.source_papers`. The loader now reads both layouts.
153
+ - SKILL.md frontmatter was split on any `---`, so a quoted value containing it cut
154
+ the YAML short. Only a fence line closes the block now.
155
+ - A tool slug containing the word `path` was indexed as a mapping and raised
156
+ `TypeError`.
157
+ - The round-trip gate counted a claim as resolved only when typed with the
158
+ hardcoded holobiomicslab `Claim` class. It now resolves claims typed with
159
+ indicium's `asb:Claim`, the class ASB's indicium emitter writes.
160
+ - The pretty `verify-claims` FAIL report printed each dangling reference as a
161
+ dataclass repr; it now prints `<skill slug>: <claim IRI>`.
162
+ - **The round-trip gate failed open.** A listed skill with no SKILL.md was dropped
163
+ without a word, and one that failed validation was dropped with a warning, so
164
+ `verify-claims` exited 0 without checking the claim IRIs it listed. Both now fail
165
+ the gate (exit 1, `RoundTripResult.ok` false), and the claim IRIs a skipped
166
+ skill's frontmatter lists are still checked. `RoundTripResult.render()` now
167
+ returns the report `verify-claims` prints.
168
+ - The `verify-claims --help` text named only `metadata.claims` and the
169
+ `skills/<slug>/SKILL.md` layout; it now names the frontmatter `claims` key and
170
+ `leaves/<slug>/SKILL.md`.
171
+
172
+ ### Known gaps
173
+ - Claims, evidence spans, tools and papers are typed with the classes the indicium
174
+ shapes target. Skills, which the indicium model does not describe, a claim's
175
+ `super_pattern`, the `name` of skills and tools and the skill-to-evidence link
176
+ `hasEvidence` use this mapper's local names under `INDICIUM_BASE`.
177
+ - Evidence spans carry `indicium:source_doi` and `indicium:paragraph_id`, which
178
+ indicium declares on other classes (`paragraph_id` on Source, `source_doi` on
179
+ Figure, Script and DataAsset). The Evidence shape is open, so no shape checks
180
+ them. `indicium:of_source`, the Evidence-to-Source link indicium models, is not
181
+ written.
182
+ - The skill-to-claim assertion nodes use `SEPIO_Assertion`, `SEPIO_hasSubject` and
183
+ `SEPIO_hasObject`, which are not SEPIO terms; no shape targets them.
184
+ - A paper whose records give different titles is written without `dct:title`, with
185
+ a warning naming its DOI.
186
+ - The `verify-claims` shape-targeted count is graph-wide: one targeted node passes
187
+ it, and instances of subclasses of a targeted class are not counted.
188
+ - A claims-file record without `context` maps to a claim without `asb:context`,
189
+ which the `asb:Claim` shape requires, and the shape reports a violation. ASB's
190
+ own claims carry a context; the mapper writes it when the record has one.
191
+ - Current tool records carry `repo_url`, not `canonical_url`, so the bio.tools
192
+ resolver has nothing to look up for them.
193
+ - No SKILL.md in the current collections lists a claim IRI, so `verify-claims`
194
+ checks none. Metabolomics v2 has 5859 skills and none lists a claim IRI.
195
+ Proteomics v1 and genomics v1 declare `n_claims` totalling 5454 across 558 skills
196
+ and 34070 across 3246 skills, but list no IRI. Each passes with 0 claims
197
+ resolved.
198
+ - `uv lock` and `uv sync` need the indicium checkout beside this one:
199
+ `[tool.uv.sources]` points `indicium-standard` at `../indicium`.
200
+
201
+ ---
202
+
203
+ ## [0.5.0] - 2026-05-24 (never tagged)
204
+
205
+ ### Added
206
+
207
+ - New `indicium_adapters.asb` subpackage: maps ASB-Skill collection
208
+ on-disk artifacts (collection.yaml, SKILL.md frontmatter,
209
+ tools/<slug>.yaml) onto `indicium:Evidence`, `indicium:SoftwareEntity`,
210
+ `indicium:Claim`, `indicium:InformationSource`, and SEPIO skill→claim
211
+ assertions.
212
+ - PROV-O envelope (`prov:wasGeneratedBy`, `prov:wasDerivedFrom`,
213
+ `prov:atTime`) attached to every typed node.
214
+ - `BioToolsResolver` with SQLite cache and `ASB_ADAPTER_OFFLINE` env
215
+ hook for CI determinism.
216
+ - `run_round_trip_check()` CI gate that fails on any dangling
217
+ `metadata.claims` URI in any SKILL.md.
218
+ - `verify-claims` console script for `asb-skill-collections` CI integration.
219
+
220
+ ### Notes
221
+
222
+ - Subpackage requires the new optional extra: `pip install -e '.[asb]'`.
223
+ - The reverse (indicium → ASB-Skill hints) adapter is deferred to v1.1.
224
+ - The `SciSkill`→`ASBSkill` Python rename in ASB lands in Wave 2;
225
+ this subpackage works against on-disk artifacts only and is unaffected.
226
+
227
+ ---
228
+
229
+ ## [0.4.0] - 2026-05-24
230
+
231
+ ### Removed
232
+ - `MetabolomicsAdapter` and its `metabolomics.shapes.ttl` and associated tests.
233
+ These have moved to the new `indicium-adapters-metabolomics` package, which
234
+ ships richer enrichment (RefMet+BridgeDb compound index) and adds two sibling
235
+ adapters: `MetaboKGAdapter` and `EMIAdapter`.
236
+ Migration: `pip install indicium-adapters-metabolomics`; the
237
+ `discover_adapters()["metabolomics"]` entry remains available, now sourced
238
+ from the new package.
239
+
240
+ ---
241
+
242
+ ## [0.3.0] - 2026-05-23
243
+
244
+ ### Added
245
+ - `CompositeAdapter`: combines 2+ `DomainAdapter` instances into one that satisfies both
246
+ `DomainAdapter` and `SHACLProvider` protocols. Composition rules: `qualifiers` (union),
247
+ `ontology_prefixes` (merged dict, later adapter wins on key conflict), `extraction_context()`
248
+ (concatenated with double newline), `enrich_claim()` (sequential chain: each adapter
249
+ enriches the output of the previous), `shacl_shapes()` (merged `rdflib.Graph`; adapters
250
+ without `shacl_shapes()` are silently skipped; uses `isinstance(SHACLProvider)` check).
251
+ - `compose_adapters(adapters)`: factory function: returns the single adapter unchanged
252
+ when `len(adapters) == 1` (no wrapping overhead); returns `CompositeAdapter` for 2+;
253
+ raises `ValueError` for an empty list.
254
+ - Both `CompositeAdapter` and `compose_adapters` exported from the top-level
255
+ `indicium_adapters` package.
256
+
257
+ ---
258
+
259
+ ## [0.2.0] - 2026-05-23
260
+
261
+ ### Added
262
+ - `SHACLProvider` Protocol mixin: adapters that implement `shacl_shapes() -> rdflib.Graph` are
263
+ automatically detected by `indicium.validate_graph()` and their shapes are merged into validation.
264
+ - `api_version: int` field on `DomainAdapter` Protocol and `CURRENT_API_VERSION` constant.
265
+ `discover_adapters()` now skips adapters whose `api_version` does not match `CURRENT_API_VERSION`
266
+ and emits a `RuntimeWarning` with upgrade instructions.
267
+ - `MetabolomicsAdapter.shacl_shapes()`: returns the bundled `metabolomics.shapes.ttl` graph
268
+ containing CURIE-format constraints on `asb:subject_ontology_term` / `asb:object_ontology_term`.
269
+
270
+ ### Fixed
271
+ - `metabolomics.shapes.ttl` shape target corrected from the inert `sh:targetSubjectsOf CHEBI:is_a`
272
+ to `sh:targetClass asb:Claim` with property shapes on the `asb:{slot}_ontology_term` predicates
273
+ emitted by `claims_to_graph()`.
274
+ - CURIE regex pattern in `metabolomics.shapes.ttl` broadened from `[A-Za-z0-9_]+` to
275
+ `[A-Za-z0-9_.+-]+` in the local part, accommodating PubChem, InChIKey, and other identifiers
276
+ that contain dots, hyphens, or plus signs.
277
+ - `rdflib>=7.0` added as an explicit runtime dependency (was only transitive via `indicium`).
278
+ - `indicium` dependency floor raised from `>=1.0` to `>=1.4`, which is the minimum version that
279
+ provides the `extra_shapes` kwarg required by `SHACLProvider` injection.
280
+
281
+ ---
282
+
283
+ ## [0.1.0] - 2026-05-20
284
+
285
+ ### Added
286
+ - `DomainAdapter` Protocol with `domain_id`, `domain_label`, and `api_version` fields.
287
+ - `discover_adapters()`: entry-point discovery function resolving all `indicium.adapters`
288
+ entry points at import time; returns a `dict[str, DomainAdapter]` keyed by `domain_id`.
289
+ - `MetabolomicsAdapter` with:
290
+ - `enrich_claim()`: annotates `subject` / `object` / `qualifier` slots with CHEBI CURIEs from
291
+ a static compound lookup table (live network lookup planned for a future `[network]` extra).
292
+ - `domain_context` string injected into LLM extraction prompts for metabolomics-aware extraction.
@@ -0,0 +1,25 @@
1
+ cff-version: 1.2.0
2
+ title: indicium-adapters
3
+ message: "If you use this software, please cite it as below."
4
+ type: software
5
+ version: 0.6.0
6
+ authors:
7
+ - family-names: Nothias
8
+ given-names: Louis-Felix
9
+ email: louis-felix.nothias@cnrs.fr
10
+ orcid: "https://orcid.org/0000-0001-6711-6719"
11
+ repository-code: "https://github.com/HolobiomicsLab/indicium-adapters"
12
+ license: MIT
13
+ keywords:
14
+ - indicium
15
+ - knowledge-graph
16
+ - rdf
17
+ - shacl
18
+ - domain-adapter
19
+ - protocol
20
+ abstract: >-
21
+ Protocol-only core of the indicium domain-adapter ecosystem.
22
+ Defines DomainAdapter and SHACLProvider structural Protocols, the
23
+ CompositeAdapter helper, and the discover_adapters() entry-point
24
+ discovery function. Domain-specific bundles ship in sibling
25
+ indicium-adapters-<domain> packages.
@@ -0,0 +1,39 @@
1
+ # Contributing to indicium-adapters
2
+
3
+ Thanks for your interest!
4
+
5
+ ## Setup
6
+
7
+ The sibling path in `[tool.uv.sources]` supports contributors who clone
8
+ `indicium` and `indicium-adapters` side by side:
9
+
10
+ ```bash
11
+ cd indicium-adapters
12
+ uv sync --extra dev --extra asb
13
+ ```
14
+
15
+ In a lone public clone, use `uv sync --no-sources --extra dev --extra asb`
16
+ until the lock is regenerated after the `indicium-standard` 2.0.0 upload.
17
+ Keep `--no-sources` on later `uv run` commands too.
18
+
19
+ ## Development workflow
20
+
21
+ 1. Create a feature branch from `main`.
22
+ 2. Write tests first (TDD). The suite is `pytest`.
23
+ 3. Run `uv run ruff check .` and `uv run pytest` before pushing. In a lone
24
+ clone, pass `--no-sources` to both `uv run` commands. CI fails on any ruff
25
+ finding.
26
+ 4. Open a PR.
27
+
28
+ ## Scope
29
+
30
+ This package is the Protocol-only core. Domain-specific adapters
31
+ (metabolomics, lipidomics, proteomics, ...) live in sibling
32
+ `indicium-adapters-<domain>` repositories. Don't add domain enrichment
33
+ logic here.
34
+
35
+ ## Bumping CURRENT_API_VERSION
36
+
37
+ Bumping `CURRENT_API_VERSION` is a coordinated cross-repo release. Plan
38
+ it with the maintainers of every `indicium-adapters-*` package; the README
39
+ lists the ten known at 0.6.0.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Louis-Felix Nothias
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.