Skip to content

Run contracts SHACL validation of examples in CI - #262

Merged
matthiasautrata merged 1 commit into
developfrom
issue/200-ci-contracts-shacl-validation
Oct 13, 2026
Merged

matthiasautrata merged 1 commit into
developfrom
issue/200-ci-contracts-shacl-validation

Conversation

@tonyseale

Copy link
Copy Markdown
Contributor

Resolves #200.

What

Adds a step to build-spec.yml, after the contracts unit tests, that runs python dprod-contracts/validate.py. The script parses every dprod-contracts/**/*.ttl and SHACL-validates each file in dprod-contracts/examples/ against dprod-contracts-shapes.ttl (RDFS inference on), exiting non-zero on any parse error, violation or malformed shape.

Why

CI already runs tests/ and dprod-contracts/tests/, but the contracts tests validate inline snippets only. No step validates the example files themselves, so a regression in an example or in the shapes could land unnoticed.

This happened in practice: a property shape in ConstraintShape that lost its sh:path odrl:operator still parsed as Turtle and passed every unit test, but made pyshacl abort on every example.

Verified locally

  • python -m unittest discover -s tests: 26 tests OK
  • python -m unittest discover -s dprod-contracts/tests: 49 tests OK
  • python dprod-contracts/validate.py: all 7 TTLs parse, all 4 examples conform, exit 0
  • With sh:path odrl:operator removed: validate.py exits 1, so the step fails the build

🤖 Generated with Claude Code

The contracts unit tests validate inline snippets only, so a regression in
an example file or a malformed shape (e.g. a property shape that loses its
sh:path) could reach develop unnoticed. Add a build step that runs
dprod-contracts/validate.py, which parses every contracts TTL and validates
each example against the shapes, failing the build on any error.

Resolves: #200

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
dprod Ready Ready Preview Oct 8, 2026 4:01pm UTC

@matthiasautrata matthiasautrata left a comment

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.

Reviewed: up to date with develop, checks green; validate.py passes on develop and exits 1 on failure, so the step gates the build. Step 1 of #275.

@matthiasautrata
matthiasautrata merged commit 9006bc3 into develop Oct 13, 2026
6 of 7 checks passed
@matthiasautrata
matthiasautrata deleted the issue/200-ci-contracts-shacl-validation branch October 13, 2026 20:57
jgeluk added a commit to matthiasautrata/dprod that referenced this pull request Oct 21, 2026
[Carried forward onto develop for EKGF#275 by Matthias Autrata: conflicts
resolved against EKGF#244/EKGF#245/EKGF#262/EKGF#268/EKGF#270/EKGF#272, the namespace changed from
https://www.omg.org/spec/DPROD/dprod/ to https://ekgf.org/dprod/spec/develop/,
and EKGF#244's duty-only shape and test migrated from dprod:recurrence to
dprod:schedule, which is not duty-only.]
jgeluk added a commit to matthiasautrata/dprod that referenced this pull request Oct 21, 2026
Shapes: accept tab-separated POSIX crontab fields and reject smuggled
tab/CR/LF content; allow EXDATE/RDATE lines in RFC 5545 expressions while
rejecting non-standard or duplicate RRULE parts; reject a
dprod:scheduleTimeZone that conflicts with the embedded TZID; move the
IRI-identifier message onto its own shape so it no longer annotates
unrelated violations.

Ontology: every dprod:ScheduleFormat now declares dprod:carriesTimeZone,
and the timezone-required rule keys on it so extension formats get the
same enforcement as POSIX crontab.

Semantics: schedule errors freeze the affected duty (Rule D-FREEZE),
surface in the result's errors field, and the processor registry is part
of Σ, so the totality and determinism theorems hold for unprocessable
schedules too.

Tests and CI: every schedule case is validated both with the ontology
graph and in the bare documented CLI configuration, making the sh:class
constraints falsifiable; the obsolete-recurrence sweep covers
specification.md; validate.py runs in the build workflow.

Docs: define the previously missing example schedules, rewrite the RRULE
components table as complete expressions, and sync the guide checklist
and specification with the new rules.

[Carried forward onto develop for EKGF#275 by Matthias Autrata: the
build-spec.yml change is dropped (EKGF#262 already runs validate.py in CI),
and the namespace in the new shapes is the develop one. Schedule errors
are fitted to the error model of EKGF#267: instead of an `errors` field on a
Permit result, an unprocessable schedule of a collected duty is gathered
in E₅ and gives Failure on the Permit path, like E₃ and E₄. Deny and
NotApplicable are unaffected, since resolving the schedule can only add
duties or violations. EvaluationError admits ScheduleError, and §9.7's
completion covers schedule errors.]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jgeluk added a commit to matthiasautrata/dprod that referenced this pull request Oct 21, 2026
[Carried forward onto develop for EKGF#275 by Matthias Autrata: conflicts
resolved against EKGF#244/EKGF#245/EKGF#262/EKGF#268/EKGF#270/EKGF#272, the namespace changed from
https://www.omg.org/spec/DPROD/dprod/ to https://ekgf.org/dprod/spec/develop/,
and EKGF#244's duty-only shape and test migrated from dprod:recurrence to
dprod:schedule, which is not duty-only.]
jgeluk added a commit to matthiasautrata/dprod that referenced this pull request Oct 21, 2026
Shapes: accept tab-separated POSIX crontab fields and reject smuggled
tab/CR/LF content; allow EXDATE/RDATE lines in RFC 5545 expressions while
rejecting non-standard or duplicate RRULE parts; reject a
dprod:scheduleTimeZone that conflicts with the embedded TZID; move the
IRI-identifier message onto its own shape so it no longer annotates
unrelated violations.

Ontology: every dprod:ScheduleFormat now declares dprod:carriesTimeZone,
and the timezone-required rule keys on it so extension formats get the
same enforcement as POSIX crontab.

Semantics: schedule errors freeze the affected duty (Rule D-FREEZE),
surface in the result's errors field, and the processor registry is part
of Σ, so the totality and determinism theorems hold for unprocessable
schedules too.

Tests and CI: every schedule case is validated both with the ontology
graph and in the bare documented CLI configuration, making the sh:class
constraints falsifiable; the obsolete-recurrence sweep covers
specification.md; validate.py runs in the build workflow.

Docs: define the previously missing example schedules, rewrite the RRULE
components table as complete expressions, and sync the guide checklist
and specification with the new rules.

[Carried forward onto develop for EKGF#275 by Matthias Autrata: the
build-spec.yml change is dropped (EKGF#262 already runs validate.py in CI),
and the namespace in the new shapes is the develop one. Schedule errors
are fitted to the error model of EKGF#267: instead of an `errors` field on a
Permit result, an unprocessable schedule of a collected duty is gathered
in E₅ and gives Failure on the Permit path, like E₃ and E₄. Deny and
NotApplicable are unaffected, since resolving the schedule can only add
duties or violations. EvaluationError admits ScheduleError, and §9.7's
completion covers schedule errors.]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tonyseale pushed a commit to matthiasautrata/dprod that referenced this pull request Oct 29, 2026
[Carried forward onto develop for EKGF#275 by Matthias Autrata: conflicts
resolved against EKGF#244/EKGF#245/EKGF#262/EKGF#268/EKGF#270/EKGF#272, the namespace changed from
https://www.omg.org/spec/DPROD/dprod/ to https://ekgf.org/dprod/spec/develop/,
and EKGF#244's duty-only shape and test migrated from dprod:recurrence to
dprod:schedule, which is not duty-only.]
tonyseale pushed a commit to matthiasautrata/dprod that referenced this pull request Oct 29, 2026
Shapes: accept tab-separated POSIX crontab fields and reject smuggled
tab/CR/LF content; allow EXDATE/RDATE lines in RFC 5545 expressions while
rejecting non-standard or duplicate RRULE parts; reject a
dprod:scheduleTimeZone that conflicts with the embedded TZID; move the
IRI-identifier message onto its own shape so it no longer annotates
unrelated violations.

Ontology: every dprod:ScheduleFormat now declares dprod:carriesTimeZone,
and the timezone-required rule keys on it so extension formats get the
same enforcement as POSIX crontab.

Semantics: schedule errors freeze the affected duty (Rule D-FREEZE),
surface in the result's errors field, and the processor registry is part
of Σ, so the totality and determinism theorems hold for unprocessable
schedules too.

Tests and CI: every schedule case is validated both with the ontology
graph and in the bare documented CLI configuration, making the sh:class
constraints falsifiable; the obsolete-recurrence sweep covers
specification.md; validate.py runs in the build workflow.

Docs: define the previously missing example schedules, rewrite the RRULE
components table as complete expressions, and sync the guide checklist
and specification with the new rules.

[Carried forward onto develop for EKGF#275 by Matthias Autrata: the
build-spec.yml change is dropped (EKGF#262 already runs validate.py in CI),
and the namespace in the new shapes is the develop one. Schedule errors
are fitted to the error model of EKGF#267: instead of an `errors` field on a
Permit result, an unprocessable schedule of a collected duty is gathered
in E₅ and gives Failure on the Permit path, like E₃ and E₄. Deny and
NotApplicable are unaffected, since resolving the schedule can only add
duties or violations. EvaluationError admits ScheduleError, and §9.7's
completion covers schedule errors.]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tonyseale pushed a commit to matthiasautrata/dprod that referenced this pull request Oct 29, 2026
…KGF#275)

The spec says every Data Contract worked example validates against
dprod-contracts-shapes.ttl, but nothing checked it: validate.py and the
CI job from EKGF#262 cover only the Turtle examples in
dprod-contracts/examples/. tests/test_examples.py now validates the
contracts examples, their README blocks and the spec's eg-contract with
the same settings as validate.py (RDFS inference, the vocabulary as
ont_graph, advanced SHACL).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tonyseale pushed a commit to matthiasautrata/dprod that referenced this pull request Oct 29, 2026
[Carried forward onto develop for EKGF#275 by Matthias Autrata: conflicts
resolved against EKGF#244/EKGF#245/EKGF#262/EKGF#268/EKGF#270/EKGF#272, the namespace changed from
https://www.omg.org/spec/DPROD/dprod/ to https://ekgf.org/dprod/spec/develop/,
and EKGF#244's duty-only shape and test migrated from dprod:recurrence to
dprod:schedule, which is not duty-only.]
tonyseale pushed a commit to matthiasautrata/dprod that referenced this pull request Oct 29, 2026
Shapes: accept tab-separated POSIX crontab fields and reject smuggled
tab/CR/LF content; allow EXDATE/RDATE lines in RFC 5545 expressions while
rejecting non-standard or duplicate RRULE parts; reject a
dprod:scheduleTimeZone that conflicts with the embedded TZID; move the
IRI-identifier message onto its own shape so it no longer annotates
unrelated violations.

Ontology: every dprod:ScheduleFormat now declares dprod:carriesTimeZone,
and the timezone-required rule keys on it so extension formats get the
same enforcement as POSIX crontab.

Semantics: schedule errors freeze the affected duty (Rule D-FREEZE),
surface in the result's errors field, and the processor registry is part
of Σ, so the totality and determinism theorems hold for unprocessable
schedules too.

Tests and CI: every schedule case is validated both with the ontology
graph and in the bare documented CLI configuration, making the sh:class
constraints falsifiable; the obsolete-recurrence sweep covers
specification.md; validate.py runs in the build workflow.

Docs: define the previously missing example schedules, rewrite the RRULE
components table as complete expressions, and sync the guide checklist
and specification with the new rules.

[Carried forward onto develop for EKGF#275 by Matthias Autrata: the
build-spec.yml change is dropped (EKGF#262 already runs validate.py in CI),
and the namespace in the new shapes is the develop one. Schedule errors
are fitted to the error model of EKGF#267: instead of an `errors` field on a
Permit result, an unprocessable schedule of a collected duty is gathered
in E₅ and gives Failure on the Permit path, like E₃ and E₄. Deny and
NotApplicable are unaffected, since resolving the schedule can only add
duties or violations. EvaluationError admits ScheduleError, and §9.7's
completion covers schedule errors.]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tonyseale pushed a commit that referenced this pull request Oct 29, 2026
Resolves #184. Supersedes #234. Step 7 of #275.

Carries Jacobus Geluk's reusable schedule model (#234) forward onto
`develop`. His two commits are kept with their authorship (`047dcb4`,
`81777dd`); each has a note recording what changed while carrying it
forward. Following Tony's and Jacobus's decision on #184, the anchor
rule is: **the schedule's DTSTART/TZ sets the occurrence times, and
activation only limits the range.**

## Stacking

This branch is built on #268 → #272, plus a merge of #270 (`687ea77`),
because the schedule errors use #268's error model and §3.2 combines
with #270's `asset`. Until those three merge, this PR also shows their
commits. The schedule work is the last four commits:

- `047dcb4` Implement reusable schedule model (Jacobus)
- `81777dd` Address review findings on the schedule model (Jacobus)
- `7c67d39` Migrate the spec and examples from `dprod:recurrence` to
`dprod:schedule`
- `257e65c` Bump the contracts modification dates

**Merge after #270, #268 and #272.** Then squash-merge directly (the
shared changes are identical) or first rebase the four commits onto
`develop`.

## Changes compared with #234

- **No frozen-archive changes, and no duplicate of #244.** #244's
duty-only shape and test now cover `dprod:deadline`,
`dprod:subjectOfDuty` and `dprod:objectOfDuty`. `dprod:schedule` is not
duty-only. The obsolete `dprod:recurrence` is rejected by
`RejectObsoleteRecurrenceShape` on every rule type, and there are tests
for both.
- **Develop namespace.** `https://www.omg.org/spec/DPROD/dprod/` is
replaced throughout, including the TZID-conflict SPARQL. The namespace
move stays at step 10 of #275.
- **Schedule errors fit #267's fail-closed model.** There is no `errors`
field on the Result. A schedule that can't be processed adds its error
to a new set E₅ (`occurrences(d, Σ) = Error(e)`, computed in Step 3),
and Step 7 returns `Failure(E₃ ∪ E₄ ∪ E₅)`. D-FREEZE: a duty whose
schedule is in error makes no transition, so only the Permit path is
affected; Deny and NotApplicable are not. `EvaluationError =
Set<ResolutionError | ScheduleError>`, and the §9.7 completion argument
also replaces each schedule error with some set of occurrence times.
- **The workflow change is dropped.** #262 already runs `validate.py` in
CI.
- **`ScheduleIdentifierShape` is renamed
`RejectAnonymousScheduleShape`.** The spec generator renders every
NodeShape that has a `sh:targetClass` as a class except `Reject*`
shapes, so the old name appeared as a spurious "ScheduleIdentifier"
class.
- **The core spec and examples move to `dprod:schedule`.** The
data-offer-and-contract, lifecycle-and-versioning, provider-duties and
odcs-mapping examples, plus the `respec/template.html` contract example,
now use identified `Schedule` nodes with an explicit `DTSTART;TZID=…`
and an RRULE. The overview diagram is regenerated, and
`tests/test_spec_contract_sections.py` expects `Schedule` and
`ScheduleFormat` classes.
- **The modification dates are bumped** in the contracts docs and in
`dct:modified` on the profile, shapes and vocabulary.

All 9 review findings on #234 were checked and are addressed. They are:
crontab whitespace, the RFC 5545 regex (EXDATE/RDATE order, duplicate
FREQ), the TZ conflict and `carriesTimeZone`, how ScheduleError is
consumed and totality, the `expand` signature, bare-config tests,
undefined guide examples, the identifier message on its own shape, the
RRULE table, and the recurrence sweep of specification.md.

## Tests

- This branch: core 26 OK, contracts 98 OK, `validate.py` clean.
- Integrated with #264, #266, #270, #268 and #272: core 26 OK, contracts
102 OK, `validate.py` clean.

Every JSON-LD example that uses a schedule conforms to the contracts
shapes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Jacobus Geluk <jacobus.geluk@ekgf.org>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 57772625 Deployed Oct 8, 2026 by vercel[bot]
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.

CI: run dprod-contracts SHACL validation (validate.py) on push/PR

2 participants