Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
80ee29a
feat(validation): add native OO-LD schema and instance validator
LukasOro Jul 31, 2026
8e96921
feat(validation): track meta-schema v0.8.0
LukasOro Jul 31, 2026
f39b280
feat(validation): cite the specification's rule ids in findings
LukasOro Jul 31, 2026
3030aa8
feat(validation): enforce ten more normative rules
LukasOro Jul 31, 2026
71c734c
feat(validation): drive rule checks from the specification catalogue
LukasOro Aug 4, 2026
8c965d6
style(tests): store the hand-written fixtures with LF line endings
LukasOro Aug 4, 2026
988bc38
docs: explain how to turn a specification rule into a check
LukasOro Aug 4, 2026
4fdda5e
docs: add CLAUDE.md with the conventions agents keep getting wrong
LukasOro Aug 4, 2026
26ceba9
docs(spec): design a check registry and an `oold checks` command
LukasOro Aug 4, 2026
264d71e
docs(spec): collapse the check mappings into one registry structure
LukasOro Aug 5, 2026
3ba00a6
docs(spec): pin where compatibility for a new rule's check lives
LukasOro Aug 5, 2026
08b9945
docs(spec): correct the version gate, and cost out a changed rule
LukasOro Aug 5, 2026
3b497b5
docs(spec): state which ids the registry covers, and fix the grep guard
LukasOro Aug 5, 2026
57c1945
refactor(validation): fold the check mappings into a single registry
LukasOro Aug 5, 2026
f6fcbbb
feat(validation): gate checks on the catalogue, and add `oold checks`
LukasOro Aug 5, 2026
bd34c1b
test(validation): arm remote-context fixture against literal @context
LukasOro Aug 5, 2026
ca11109
style: apply ruff-format to the check-registry drift test
LukasOro Aug 6, 2026
a7a7f3c
feat(validation): check catalogue and fixture slice against facts
LukasOro Aug 6, 2026
92524af
docs: fix the vendoring procedure and say what a new check owes
LukasOro Aug 6, 2026
6589527
feat(validation): vendor reshaped catalogue, and enforce its new rules
LukasOro Aug 12, 2026
793aaa1
test(validation): give three checks a fixture that actually reaches them
LukasOro Aug 12, 2026
ebc3907
fix(docs): restate and guard Zensical's default Markdown extensions
LukasOro Aug 12, 2026
fb99f5c
feat(validation): enforce four more catalogued rules
LukasOro Aug 12, 2026
cbcf95a
chore(validation): vendor the 43-rule catalogue
LukasOro Aug 13, 2026
5bcd043
docs(validation): record catalogue's source so a rebase cannot orphan it
LukasOro Aug 13, 2026
09ede4a
feat(validation): track upstream's two-tier meta-schema split
LukasOro Aug 13, 2026
bee7204
feat(validation): enforce two more rules, and leave the third alone
LukasOro Aug 14, 2026
13ade19
feat(validation): enforce narrow-only composition
LukasOro Aug 14, 2026
5a6b6d7
feat(validation): track the v1.0.0-rc.2 release
LukasOro Aug 15, 2026
fc4de8c
docs: move documentation out of source dirs and drop meta-talk
LukasOro Aug 17, 2026
5feef83
feat(validation): model the rule catalogue and type the MCP results
LukasOro Aug 17, 2026
7875b51
fix(validation): count x-oold-context synonyms as mapped terms
LukasOro Aug 18, 2026
18a952c
feat(validation): accept raw JSON in every MCP document tool
LukasOro Aug 18, 2026
c5d9324
fix(validation): report the keyword coverage.vocab leaves out
LukasOro Aug 18, 2026
6897095
feat(validation): classify a single file from its $schema
LukasOro Aug 19, 2026
ef375d3
chore: ignore the local graphify-out directory
LukasOro Aug 19, 2026
b4e23ac
fix(cli): keep the missing-extra guard on oold-validate
LukasOro Aug 20, 2026
561d0b8
fix(validation): warn on an unmapped term instead of failing
simontaurus Aug 23, 2026
65d2ce3
fix(validation): share rule filtering, report a corrupt rules schema
simontaurus Aug 23, 2026
7050e8b
fix(validation): import select_rules where the MCP server uses it
simontaurus Aug 23, 2026
2a03e2f
fix(validation): keep a processor failure distinct from a missing term
simontaurus Aug 23, 2026
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
7 changes: 7 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Files copied verbatim from the oold-schema repository at a release tag must keep their exact
# bytes: `src/oold/validation/meta/index.json` records a sha256 for each vendored meta-schema, and
# the fixtures are refreshed by copying bytes straight out of a tag. With `core.autocrlf=true` -
# the Windows default - git would rewrite their line endings on checkout, silently breaking those
# checksums and making every refresh from upstream show a whole-file diff.
src/oold/validation/meta/*/** -text
tests/data/oold/** -text
30 changes: 30 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@ jobs:
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

validate-schemas:
# Runs the OO-LD validator over the committed fixtures, offline, so a regression in the
# validation pipeline fails CI even when the unit tests still pass.
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v4

- name: Set up the environment
uses: ./.github/actions/setup-python-env

- name: Validate the committed OO-LD fixtures
run: make validate

- name: Check parity against the reference harness
run: |
# oold-schema owns the reference implementation. Comparing against its current main
# catches drift that the committed fixture snapshot cannot see.
git clone --depth 1 https://github.com/OO-LD/oold-schema.git /tmp/oold-schema
npm --prefix /tmp/oold-schema install --no-audit --no-fund
OOLD_SCHEMA_DIR=/tmp/oold-schema uv run python -m pytest \
tests/test_validation/test_parity_live.py -v --no-cov
continue-on-error: true

check-docs:
runs-on: ubuntu-latest
steps:
Expand All @@ -66,6 +90,12 @@ jobs:
- name: Set up the environment
uses: ./.github/actions/setup-python-env

# A project that declares any markdown_extensions replaces Zensical's defaults
# rather than extending them, so a missing one silently switches an extension
# off without failing the build below. Catch that before it does.
- name: Check the restated Markdown extensions match Zensical's defaults
run: uv run python scripts/check_markdown_extensions.py

- name: Check if documentation can be built
run: uv run zensical build -s

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,6 @@ benchmark_comparison.txt
*/osw_files/*

# Local
CLAUDE.md
AGENTS.md
.ign
graphify-out/
15 changes: 15 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"mcpServers": {
"oold-validation": {
"command": "uv",
"args": [
"run",
"--directory",
".",
"python",
"-m",
"oold.validation.mcp_server"
]
}
}
}
26 changes: 25 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
# `pre-commit install` (no --hook-type flags needed).
default_install_hook_types: [pre-commit, commit-msg]

# Files copied verbatim from the oold-schema repository at a release tag: the vendored
# meta-schemas (whose sha256 is recorded in meta/index.json) and the OO-LD test fixtures.
# Reformatting them would break those checksums and make every future refresh from upstream
# produce a spurious diff, so the whitespace/JSON-formatting hooks must leave them alone.
# `check-json` still runs on them - it validates without rewriting.
x-vendored: &vendored >-
^(src/oold/validation/meta/[0-9]|tests/data/oold/)

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: "v6.0.0"
Expand All @@ -13,10 +21,12 @@ repos:
- id: check-json
exclude: ^.devcontainer/devcontainer.json
- id: pretty-format-json
exclude: ^.devcontainer/devcontainer.json
exclude: ^(\.devcontainer/devcontainer\.json|src/oold/validation/meta/[0-9]|tests/data/oold/)
args: [--autofix, --no-sort-keys]
- id: end-of-file-fixer
exclude: *vendored
- id: trailing-whitespace
exclude: *vendored

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.15.7"
Expand All @@ -33,3 +43,17 @@ repos:
hooks:
- id: conventional-pre-commit
stages: [commit-msg]

# Declaring any markdown_extensions in zensical.toml replaces Zensical's defaults
# instead of extending them, so dropping one switches an extension off with no
# warning and no build failure. See scripts/check_markdown_extensions.py.
- repo: local
hooks:
- id: markdown-extensions
name: check zensical.toml restates Zensical's default Markdown extensions
# Goes through make so the invocation matches `make check` and CI exactly.
entry: make check-extensions
language: system
pass_filenames: false
files: ^(zensical\.toml|scripts/check_markdown_extensions\.py|Makefile)$
require_serial: true
33 changes: 33 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Working in this repository

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as mentioned: no AI only documentation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reduced to a pointer. The validation invariants moved to docs/architecture.md ("Validation subsystem design"), the vendoring rules to docs/maintaining-meta-schemas.md, the working conventions to docs/contributing.md. What is left is the command list plus links. No file under src/ or tests/ cites it any more.


Guidance for AI agents. Human contributors want the published contributing guide
([docs/contributing.md](docs/contributing.md)), which this file does not repeat.

## Commands

```bash
make check # lint, type-check, dependency audit
make test # pytest with coverage
make validate # run the validator over the committed fixtures
make docs-test # strict docs build, fails on any warning

OOLD_SCHEMA_DIR=../oold-schema uv run pytest -m parity # compare against oold-schema's own validator
```

The parity tests skip silently without `OOLD_SCHEMA_DIR`, so a green `make test` does not mean
parity holds. Run them explicitly when touching `src/oold/validation/`.

## Where the details live

This file used to carry the validation subsystem's invariants, the vendoring rules for the
meta-schemas, and this repository's working conventions directly. They now live in the published
docs, redistributed by topic so each is maintained in one place:

- **Validation subsystem design** - the two identifier systems, why severity is read from the
rule catalogue rather than hardcoded, why a check skips rather than guesses, why checks judge
the resolved context, why check ids are a public interface, and why this repository and
oold-schema stay decoupled: [docs/architecture.md, "Validation subsystem design"](docs/architecture.md#validation-subsystem-design).
- **Vendored meta-schemas and fixtures** - byte-exactness, line endings, and how to add a
version: [docs/maintaining-meta-schemas.md](docs/maintaining-meta-schemas.md).
- **Working conventions** - commit style and releases, no AI attribution, regular dashes, and not
creating scratch files: [docs/contributing.md, "Conventions"](docs/contributing.md#conventions).
143 changes: 9 additions & 134 deletions CONTRIBUTING.md

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should avoid spreading between root level markdown and published docs => move all content in docs, keep shallow references in files like CONTRIBUTING.md + concise (overview/simple getting started only) root-level README that points to the docs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs/contributing.md is canonical now and the root file is a short pointer into the docs. They had already drifted: the root carried the current automated-release process while the docs copy still described manual tagging.

Original file line number Diff line number Diff line change
@@ -1,136 +1,11 @@
# Contributing

Contributions are welcome! You can help by reporting bugs, implementing features, or improving documentation. File issues and PRs at [github.com/OO-LD/oold-python](https://github.com/OO-LD/oold-python).

## Development Setup

Requires `uv` and `git`.

```bash
git clone git@github.com:YOUR_NAME/oold-python.git
cd oold-python
```

With `make`:

```bash
make install
```

Without `make`:

```bash
uv sync --all-extras
uv run pre-commit install
```

`pre-commit install` installs both the `pre-commit` and `commit-msg` stage hooks
(via `default_install_hook_types`); the latter enforces Conventional Commits (see
below).

## Making Changes

1. Create a branch: `git checkout -b name-of-your-fix`
2. Make your changes and add tests in `tests/`
3. Run checks and tests (see below)
4. Commit and push, then open a pull request

### Checks and tests

With `make`:

```bash
make check # lint, type-check, dependency audit
make test # pytest with coverage
```

Without `make`:

```bash
uv lock --locked
uv run pre-commit run -a
uv run ty check
uv run deptry src
uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml
```

### Docs

With `make`:

```bash
make docs # serve with live reload at http://localhost:8000
make docs-test # strict build, fails on any warning
```

Without `make`:

```bash
uv run zensical serve
uv run zensical build -s
```

## Commit messages (Conventional Commits)

This project uses [Conventional Commits](https://www.conventionalcommits.org/).
Commit messages drive versioning and the changelog automatically, so the format
matters. The local `commit-msg` hook rejects malformed messages.

Format: `type(scope): subject`, for example `fix: correct sidebar collapse on
small screens`. The scope is optional.

| Type | Release effect | Use for |
| ---- | -------------- | ------- |
| `feat` | minor bump | a new feature |
| `fix` | patch bump | a bug fix |
| `perf` | patch bump | a performance improvement |
| `docs`, `chore`, `test`, `refactor`, `ci`, `style`, `build` | no release | changes that do not ship user-facing behavior |
| `BREAKING CHANGE:` footer, or `!` after the type | major bump | an incompatible change |

A breaking change is marked either with a `!` (`feat!: drop Python 3.9`) or a
`BREAKING CHANGE:` footer in the commit body.

## Releasing

Releases are fully automated by python-semantic-release. You do not tag or bump
the version by hand.

1. Open a PR. CI comments the version that a merge would release, based on your
commits.
2. Merge to `main`. On merge, CI reads the new conventional commits, bumps the
version in `pyproject.toml` and `CITATION.cff`, updates `CHANGELOG.md`,
commits with `[skip ci]`, and pushes the `vX.Y.Z` tag.
3. CI then builds the package, publishes it to PyPI via OIDC trusted publishing,
and deploys the docs to GitHub Pages.

If a merge contains only non-releasing commit types (for example `docs` or
`chore`), no release is cut. The version lives in `pyproject.toml`; never edit it
manually.

## Citation and authorship

Authors of the project are listed explicitly in [`CITATION.cff`](CITATION.cff). This list is the set of creators shown on each [Zenodo](https://zenodo.org/doi/10.5281/zenodo.8374237) release. We keep it opt-in and curated rather than auto-generated from GitHub, so nobody is listed without consent, and the entries in `CITATION.cff` take precedence over GitHub's automatic contributor detection.

To be officially listed as an author for future Zenodo releases, add yourself to the `authors:` list in `CITATION.cff`. Two ways, in order of preference:

1. **Preferred - within your feature PR:** include the `CITATION.cff` edit directly in the same PR that contributes your feature or fix, so authorship is recorded together with the work.
2. **Standalone PR:** if you are already a GitHub contributor and simply want to be listed as an author on Zenodo, open a single PR that only adds your entry.

In either case, add an entry like:

```yaml
- given-names: Your
family-names: Name
affiliation: "Your institution" # optional
orcid: "https://orcid.org/0000-0000-0000-0000" # optional, use your real ORCID
```

Notes:

- Append yourself to the end of the list (order is the citation order); mention it in the PR if a different position is intended.
- `affiliation` and `orcid` are optional but recommended for durable, unambiguous attribution.
- Only entries present in `CITATION.cff` at the tagged commit appear on that release's Zenodo record, so add yourself before a release to be included.

## AI Guidelines

We believe that AI, and in particular LLMs, can be helpful conventional tools to accelerate development and improve quality when used responsibly. AI or any other tool is never the author of code; a human developer always is. Therefore, it is mandatory to carefully review all generated content for correctness, quality, and the absence of legal and ethical issues. For consistency, please avoid patterns that are hard to maintain manually, such as duplicated content or special characters like em dashes or UTF icons.
Contributions are welcome! You can help by reporting bugs, implementing features, or improving
documentation. File issues and PRs at
[github.com/OO-LD/oold-python](https://github.com/OO-LD/oold-python).

The full contributing guide, including development setup, checks and tests, translating a
specification rule into a check, commit message conventions, the release process, and citation
and authorship, is published at
[oo-ld.github.io/oold-python/contributing](https://oo-ld.github.io/oold-python/contributing/)
(source: [`docs/contributing.md`](docs/contributing.md)).
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,24 @@ check: ## Run code quality tools.
@uv run ty check
@echo "🚀 Checking for obsolete dependencies: Running deptry"
@uv run deptry src
@echo "🚀 Checking docs build config: Markdown extensions still match Zensical's defaults"
@uv run python scripts/check_markdown_extensions.py

.PHONY: test
test: ## Test the code with pytest
@echo "🚀 Testing code: Running pytest"
@uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml

.PHONY: validate
validate: ## Validate the committed OO-LD fixtures with the built-in validator
@echo "🚀 Validating OO-LD schemas: oold validate"
@uv run oold validate tests/data/oold --offline
@uv run oold compliance tests/data/oold/compliance --offline

.PHONY: check-extensions
check-extensions: ## Check zensical.toml still restates Zensical's default Markdown extensions
@uv run python scripts/check_markdown_extensions.py

.PHONY: benchmark
benchmark: ## Run performance benchmarks with pytest-benchmark
@echo "🚀 Running benchmarks: pytest-benchmark"
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,29 @@ loaded = MyModel["ex:foo"] # resolves via registered backend

Custom backends implement the `Backend` interface (`resolve_iris`, `store_json_dicts`).

### Validation

Check that an OO-LD schema is well formed, and that its `@context` actually carries every
declared property into RDF. A property declared in `properties` but missing from `@context` is
neither a JSON Schema error nor a JSON-LD error - it just quietly disappears, and the data loses
meaning.

```bash
pip install "oold[validation]"

oold validate Person.schema.json # one schema
oold validate ./schemas/ # a whole directory
oold validate-instance doc.instance.json # a document against the schema it names
```

Exit code is 0 only when every check passes, so it drops straight into CI. The same pipeline is
available as a Python API and as an MCP server (`oold[mcp]`).

It is a native port of the reference harness in
[oold-schema](https://github.com/OO-LD/oold-schema), verified to agree with it on verdicts, and
it validates against versioned meta-schemas - a released version, the unreleased upstream state,
or several at once. See [docs/how-to/validation.md](docs/how-to/validation.md).

## Development

This project uses [uv](https://docs.astral.sh/uv/) and `make`. Clone and set up:
Expand Down
Loading
Loading