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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions .github/workflows/code-quality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@ on:
branches: [develop, main]
paths:
- "src/**"
- "resources/schemas/**"
- "pyproject.toml"
- "poetry.lock"
- "Makefile"
- ".github/workflows/code-quality.yaml"
pull_request:
paths:
- "src/**"
- "resources/schemas/**"
- "pyproject.toml"
- "poetry.lock"
- "Makefile"
- ".github/workflows/code-quality.yaml"

Expand All @@ -34,7 +28,7 @@ jobs:

- name: Read Python version from pyproject.toml
id: python-version
run: echo "version=$(grep -m1 'python = ' pyproject.toml | grep -oP '\d+\.\d+' | head -1)" >> $GITHUB_OUTPUT
run: echo "version=$(grep -m1 'python = ' src/pyproject.toml | grep -oP '\d+\.\d+' | head -1)" >> $GITHUB_OUTPUT

- name: Set up Python
uses: actions/setup-python@v6
Expand All @@ -48,16 +42,16 @@ jobs:
uses: actions/cache@v4
with:
path: ~/.cache/pypoetry
key: poetry-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
key: poetry-${{ runner.os }}-${{ hashFiles('src/poetry.lock') }}

- name: Install dependencies
run: make install

- name: Lint LinkML schemas
run: poetry run linkml lint --ignore-warnings resources/schemas/
run: make lint-schema

- name: Run ruff linter
run: poetry run ruff check src/
run: make lint

- name: Generate models and docs
run: make -B all
Expand All @@ -70,7 +64,7 @@ jobs:
OUT_OF_SYNC=""

# src/ and JSON schemas are deterministic — use git diff directly
for f in $(git diff --name-only src/ resources/schemas/*json); do
for f in $(git diff --name-only src/erspec/ src/resources/schemas/*json); do
OUT_OF_SYNC="$OUT_OF_SYNC $f"
done

Expand Down
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [unreleased]

## [1.0.0-rc.1] - 2026-04-21
### Added
* data model: `context` field on `EntityMention` — carries the surrounding textual context for an entity mention

### Changed
* Repository layout restructured: schemas, scripts, templates, and build configuration consolidated under `src/`; root-level `resources/`, `Makefile`, `pyproject.toml` and `poetry.lock` moved into `src/`
* Schema files renamed to drop the version suffix (e.g. `core-schema-v0.1.0.yaml` → `core-schema.yaml`); internal schema version bumped to 1.0.0
* Makefile: build logic consolidated into a single root-level `Makefile`; CI workflows updated to match revised `make` targets and paths
* Gherkin test suite overhauled: new feature files added for outcome integration, request publishing, and detailed resolution cases; outdated `ere-ers-common-cases` and `ere-ers-full-rebuilds` feature files removed; unhappy-path scenarios significantly extended
* Schema docs and worked examples updated to reflect the current schema structure and revised repository paths

### Fixed
* CI: PR comment step is now skipped on cross-fork pull requests to prevent permission failures

## [0.2.0-rc.2] - 2026-02-20
### Added
* CI: GitHub Actions quality-check workflow (`.github/workflows/code-quality.yaml`) — LinkML schema linting, `ruff` Python linting, model/docs generation with sync verification, and PR comment posting ([ERS1-103])
Expand Down Expand Up @@ -49,6 +63,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [0.1.0-rc.1] - 2025-12-22

* Initial release, fulfilling Project's Delivery 1 (ERE Technical Contract and related code).
* [LinkML schema](resources/schema/ers-core_v0.1.0.yaml) to specify the interaction with the ERE service.
* [LinkML schema](src/resources/schemas/core-schema.yaml) to specify the interaction with the ERE service.
* Includes auto-generated [navigable documents](docs/schema/README.md), a [class diagram](docs/schema/README.md) and a [sequence diagram](docs/ere-interface-seq-diag.png).
* [Gherkin Tests](test/features/), based on [collected test data](test/test_data/), possible [test cases](test/test_data/analysis/README.md)
70 changes: 32 additions & 38 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,28 @@ define log_done
endef

# ─── Paths & Naming ─────────────────────────────────────────────────────────────
# Root-relative paths — used by Make for dependency tracking only.
# Recipes use src/-relative paths via `cd src &&`.

SCHEMAS_DIR = resources/schemas
SCRIPTS_DIR = resources/scripts
TEMPLATES_DIR = resources/templates
SCHEMAS_DIR = src/resources/schemas
SCRIPTS_DIR = src/resources/scripts
TEMPLATES_DIR = src/resources/templates
MODELS_DIR = src/erspec/models

# Schema identifiers
ERE_SCHEMA_NAME = ere-service-schema
CORE_SCHEMA_NAME = core-schema
JSON_SCHEMA_NAME = er-schema
SCHEMA_VERSION = 0.1.0
ERE_SCHEMA_NAME = ere-service-schema
CORE_SCHEMA_NAME = core-schema
JSON_SCHEMA_NAME = er-schema

# Source schemas (core is imported by ere, so it is a dependency)
ERE_SCHEMA_PATH = $(SCHEMAS_DIR)/$(ERE_SCHEMA_NAME)-v$(SCHEMA_VERSION).yaml
CORE_SCHEMA_PATH = $(SCHEMAS_DIR)/$(CORE_SCHEMA_NAME)-v$(SCHEMA_VERSION).yaml
ERE_SCHEMA_PATH = $(SCHEMAS_DIR)/$(ERE_SCHEMA_NAME).yaml
CORE_SCHEMA_PATH = $(SCHEMAS_DIR)/$(CORE_SCHEMA_NAME).yaml
ALL_SCHEMA_SOURCES = $(ERE_SCHEMA_PATH) $(CORE_SCHEMA_PATH)

# Generated artefacts
PYTHON_ERE_MODEL = $(MODELS_DIR)/ere.py
PYTHON_CORE_MODEL = $(MODELS_DIR)/core.py
JSON_SCHEMA_PATH = $(SCHEMAS_DIR)/$(JSON_SCHEMA_NAME)-v$(SCHEMA_VERSION).json
PYTHON_ERE_MODEL = $(MODELS_DIR)/ere.py
PYTHON_CORE_MODEL = $(MODELS_DIR)/core.py
JSON_SCHEMA_PATH = $(SCHEMAS_DIR)/$(JSON_SCHEMA_NAME).json

MODEL_DOCS_DIR = docs/schema
MODEL_DOCS_README = $(MODEL_DOCS_DIR)/README.md
MODEL_DOCS_DIR = docs/schema
MODEL_DOCS_README = $(MODEL_DOCS_DIR)/README.md

# ─── Help ────────────────────────────────────────────────────────────────────────

Expand All @@ -58,26 +56,26 @@ help:
}' $(MAKEFILE_LIST)

# ─── Setup ───────────────────────────────────────────────────────────────────────
# Note: Python, Poetry and Make are pre-requisites and are not handled here.

.PHONY: install
install: ## Install dependencies using Poetry
$(call log_progress,Installing dependencies using Poetry...)
@poetry sync
@cd src && poetry sync
$(call log_done,Dependencies installed.)

# ─── Quality ─────────────────────────────────────────────────────────────────────

.PHONY: lint
lint: ## Run ruff linter on source code
$(call log_progress,Running ruff checks...)
@poetry run ruff check src/
@cd src && poetry run ruff check erspec/
$(call log_done,Ruff checks completed.)

.PHONY: lint-schema
lint-schema: ## Run LinkML linter on YAML schemas
$(call log_progress,Linting LinkML schemas...)
@poetry run linkml lint --ignore-warnings $(SCHEMAS_DIR)/
@cd src && poetry run linkml lint --ignore-warnings resources/schemas/$(ERE_SCHEMA_NAME).yaml
@cd src && poetry run linkml lint --ignore-warnings resources/schemas/$(CORE_SCHEMA_NAME).yaml
$(call log_done,LinkML schema lint completed.)

# ─── Aggregate targets ──────────────────────────────────────────────────────────
Expand All @@ -94,41 +92,37 @@ generate-models: $(PYTHON_ERE_MODEL) $(JSON_SCHEMA_PATH) ## Generate Python mode
generate-doc: $(MODEL_DOCS_README) ## Generate schema documentation and diagrams
$(call log_done,Documentation generated.)

# ─── Python Pydantic models (split generation: ere + core) ──────────────────────
# ─── Python Pydantic models ──────────────────────────────────────────────────────

$(PYTHON_ERE_MODEL) $(PYTHON_CORE_MODEL) &: $(ALL_SCHEMA_SOURCES)
$(call log_progress,Generating Python models...)
@mkdir -p $(MODELS_DIR)
@poetry run python $(SCRIPTS_DIR)/generate_models.py \
--schema $(ERE_SCHEMA_PATH) \
--output $(PYTHON_ERE_MODEL) \
--template-dir $(TEMPLATES_DIR) \
--schemas-dir $(SCHEMAS_DIR)
@poetry run ruff check --fix $(MODELS_DIR)
@cd src && poetry run python resources/scripts/generate_models.py \
--schema resources/schemas/$(ERE_SCHEMA_NAME).yaml \
--output erspec/models/ere.py \
--template-dir resources/templates \
--schemas-dir resources/schemas
@cd src && poetry run ruff check --fix erspec/models/
$(call log_done,Python models generated.)

# ─── JSON Schema ─────────────────────────────────────────────────────────────────
# The ERE schema imports core, so `linkml generate json-schema` will include both.

$(JSON_SCHEMA_PATH): $(ALL_SCHEMA_SOURCES)
$(call log_progress,Generating JSON Schema...)
@mkdir -p $(dir $(JSON_SCHEMA_PATH))
@poetry run linkml generate json-schema --indent 2 $(ERE_SCHEMA_PATH) > $(JSON_SCHEMA_PATH)
@cd src && poetry run linkml generate json-schema --indent 2 \
resources/schemas/$(ERE_SCHEMA_NAME).yaml > resources/schemas/$(JSON_SCHEMA_NAME).json
$(call log_done,JSON Schema generated -> $(JSON_SCHEMA_PATH))

# ─── Documentation & PlantUML diagrams ──────────────────────────────────────────

$(MODEL_DOCS_README): $(ALL_SCHEMA_SOURCES)
$(call log_progress,Generating schema documentation...)
@mkdir -p $(MODEL_DOCS_DIR)
# Index is named README.md so GitHub renders it when browsing the directory.
@poetry run linkml generate doc $(ERE_SCHEMA_PATH) \
-d $(MODEL_DOCS_DIR) --index-name README
# TODO: Prefer PNG once upstream is fixed (https://github.com/linkml/linkml/issues/3009)
# TODO: --no-mergeimports doesn't work (https://github.com/linkml/linkml/issues/1296), so, for
# the moment, we include core imported classes in the diagram.
@poetry run linkml generate plantuml \
-d $(MODEL_DOCS_DIR) --format svg $(ERE_SCHEMA_PATH)
@cd src && poetry run linkml generate doc resources/schemas/$(ERE_SCHEMA_NAME).yaml \
-d ../docs/schema --index-name README
@cd src && poetry run linkml generate plantuml \
-d ../docs/schema --format svg resources/schemas/$(ERE_SCHEMA_NAME).yaml
$(call log_done,Documentation generated -> $(MODEL_DOCS_DIR))

# ─── Clean ───────────────────────────────────────────────────────────────────────
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Entity Resolution Specifications

Formal software contract, shared data models, sample messages, and compliance tests required for integrating new Entity Resolution Engines (EREs) into the system.
Formal software contract, shared data models, sample messages, and compliance tests required for integrating new Entity Resolution Engines (EREs) into Entity Resolution System.

> Note: Active development continues in the OP-TED repository: https://github.com/OP-TED/entity-resolution-spec

## Requirements

Expand Down Expand Up @@ -39,9 +38,15 @@ make install
This will install the necessary user dependencies in a Poetry-managed virtual environment.


## Repository Layout

This repository follows the repository owner's requirements for project structure, which place the self-contained Python project (source code, dependencies, and build scripts) under `src/`. This layout is required for the repository owner's deployment tooling to locate and operate the project correctly.

The canonical `Makefile` lives at the repo root and runs all targets from there. All `poetry` commands are directed to the project in `src/` via `poetry --directory src`.

## Development

This project uses principles of model-driven development (MDD) and domain-driven design (DDD). The core models are defined in the `resources/schemas` directory using [LinkML](https://linkml.io/), and the Python (Pydantic) models are generated from these specifications.
This project uses principles of model-driven development (MDD) and domain-driven design (DDD). The core models are defined in the `src/resources/schemas` directory using [LinkML](https://linkml.io/), and the Python (Pydantic) models are generated from these specifications.

Generated Python models are in `src/erspec/models`. Regenerate them with:

Expand All @@ -52,10 +57,9 @@ make all
This regenerates both the LinkML-based models (Python, JSONSchema) and the navigable documentation. See the Makefile for more granular targets.


## Running and Testing
## Gherkin Specification

TODO: this will be added in future. Right now, this repository contains
specifications only and does not have runnable unit tests.
This repository contains Gherkin feature files under `test/features/` that serve as a formal specification of the expected behaviour of the ERE. They describe the observable contract between ERS and ERE at specification level — independent of any particular ERE implementation — and may serve as the basis for implementing acceptance tests for a conformant ERE.


## Test data
Expand Down
2 changes: 1 addition & 1 deletion docs/schema/about_entity_mention.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ Alias: about_entity_mention

| Name | Description | Modifies Slot |
| --- | --- | --- |
| [UserAction](UserAction.md) | Immutable record of a curator action on an entity mention resolution | no |
| [Decision](Decision.md) | Canonical placement of an entity mention to a cluster | no |
| [UserAction](UserAction.md) | Immutable record of a curator action on an entity mention resolution | no |



Expand Down
2 changes: 1 addition & 1 deletion docs/schema/candidates.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ Alias: candidates

| Name | Description | Modifies Slot |
| --- | --- | --- |
| [UserAction](UserAction.md) | Immutable record of a curator action on an entity mention resolution | no |
| [EntityMentionResolutionResponse](EntityMentionResolutionResponse.md) | An entity resolution response returned by the ERE | no |
| [Decision](Decision.md) | Canonical placement of an entity mention to a cluster | no |
| [UserAction](UserAction.md) | Immutable record of a curator action on an entity mention resolution | no |



Expand Down
2 changes: 1 addition & 1 deletion docs/schema/created_at.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ Alias: created_at

| Name | Description | Modifies Slot |
| --- | --- | --- |
| [UserAction](UserAction.md) | Immutable record of a curator action on an entity mention resolution | no |
| [Decision](Decision.md) | Canonical placement of an entity mention to a cluster | no |
| [UserAction](UserAction.md) | Immutable record of a curator action on an entity mention resolution | no |



Expand Down
4 changes: 2 additions & 2 deletions docs/schema/ere_request_id.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ Alias: ere_request_id

| Name | Description | Modifies Slot |
| --- | --- | --- |
| [EREMessage](EREMessage.md) | Root abstraction to represent attributes common to both requests and results | no |
| [EREErrorResponse](EREErrorResponse.md) | Response sent by the ERE when some error/exception occurs while processing a ... | no |
| [EREResponse](EREResponse.md) | Root class to represent all the responses sent by the ERE | no |
| [EntityMentionResolutionRequest](EntityMentionResolutionRequest.md) | An entity resolution request sent to the ERE, containing the entity to be res... | no |
| [EREMessage](EREMessage.md) | Root abstraction to represent attributes common to both requests and results | no |
| [EntityMentionResolutionResponse](EntityMentionResolutionResponse.md) | An entity resolution response returned by the ERE | no |
| [ERERequest](ERERequest.md) | Root class to represent all the requests sent to the ERE | no |
| [EntityMentionResolutionResponse](EntityMentionResolutionResponse.md) | An entity resolution response returned by the ERE | no |



Expand Down
2 changes: 1 addition & 1 deletion docs/schema/id.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ Alias: id

| Name | Description | Modifies Slot |
| --- | --- | --- |
| [UserAction](UserAction.md) | Immutable record of a curator action on an entity mention resolution | no |
| [Decision](Decision.md) | Canonical placement of an entity mention to a cluster | no |
| [UserAction](UserAction.md) | Immutable record of a curator action on an entity mention resolution | no |



Expand Down
2 changes: 1 addition & 1 deletion docs/schema/source_id.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ Alias: source_id

| Name | Description | Modifies Slot |
| --- | --- | --- |
| [LookupState](LookupState.md) | Tracks the resolution state for entity mentions from a particular source | no |
| [EntityMentionIdentifier](EntityMentionIdentifier.md) | A container that groups the attributes needed to identify an entity mention i... | no |
| [LookupState](LookupState.md) | Tracks the resolution state for entity mentions from a particular source | no |



Expand Down
4 changes: 2 additions & 2 deletions docs/schema/timestamp.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ Alias: timestamp

| Name | Description | Modifies Slot |
| --- | --- | --- |
| [EREMessage](EREMessage.md) | Root abstraction to represent attributes common to both requests and results | no |
| [EREErrorResponse](EREErrorResponse.md) | Response sent by the ERE when some error/exception occurs while processing a ... | no |
| [EREResponse](EREResponse.md) | Root class to represent all the responses sent by the ERE | no |
| [EntityMentionResolutionRequest](EntityMentionResolutionRequest.md) | An entity resolution request sent to the ERE, containing the entity to be res... | no |
| [EREMessage](EREMessage.md) | Root abstraction to represent attributes common to both requests and results | no |
| [EntityMentionResolutionResponse](EntityMentionResolutionResponse.md) | An entity resolution response returned by the ERE | no |
| [ERERequest](ERERequest.md) | Root class to represent all the requests sent to the ERE | no |
| [EntityMentionResolutionResponse](EntityMentionResolutionResponse.md) | An entity resolution response returned by the ERE | no |



Expand Down
4 changes: 2 additions & 2 deletions docs/schema/type.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ Alias: type

| Name | Description | Modifies Slot |
| --- | --- | --- |
| [EREMessage](EREMessage.md) | Root abstraction to represent attributes common to both requests and results | no |
| [EREErrorResponse](EREErrorResponse.md) | Response sent by the ERE when some error/exception occurs while processing a ... | no |
| [EREResponse](EREResponse.md) | Root class to represent all the responses sent by the ERE | no |
| [EntityMentionResolutionRequest](EntityMentionResolutionRequest.md) | An entity resolution request sent to the ERE, containing the entity to be res... | no |
| [EREMessage](EREMessage.md) | Root abstraction to represent attributes common to both requests and results | no |
| [EntityMentionResolutionResponse](EntityMentionResolutionResponse.md) | An entity resolution response returned by the ERE | no |
| [ERERequest](ERERequest.md) | Root class to represent all the requests sent to the ERE | no |
| [EntityMentionResolutionResponse](EntityMentionResolutionResponse.md) | An entity resolution response returned by the ERE | no |



Expand Down
1 change: 1 addition & 0 deletions src/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.0.0
2 changes: 1 addition & 1 deletion src/erspec/models/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


metamodel_version = "None"
version = "0.1.0"
version = "1.0.0"


class UserActionType(str, Enum):
Expand Down
2 changes: 1 addition & 1 deletion src/erspec/models/ere.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@


metamodel_version = "None"
version = "0.1.0"
version = "1.0.0"


class UserActionType(str, Enum):
Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions pyproject.toml → src/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "ers-spec"
version = "0.3.0"
version = "1.0.0"
description = """
The core components for the Entity Resolution System (ERS) components.

Expand All @@ -11,7 +11,7 @@ authors = [
{name = "Meaningfy", email = "hi@meaningfy.ws"}
]

readme = "README.md"
readme = "../README.md"
requires-python = ">=3.12"

[tool.poetry.dependencies]
Expand All @@ -30,7 +30,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
packages = [
{ include = "erspec", from = "src" }
{ include = "erspec" }
]

[tool.ruff]
Expand Down
Loading
Loading