Compare commits

...

1 Commits

Author SHA1 Message Date
felix
ea97464519 extended profiles 2026-09-27 17:10:20 +02:00
4 changed files with 80 additions and 336 deletions

View File

@@ -15,16 +15,32 @@
# mandaat:bindingStart/-Einde) -> mandaat (org:hasPost) -> mandataris
# (org:holds) -> persoon (mandaat:isBestuurlijkeAliasVan).
#
# Bestuursorgaan en bestuursorgaanInTijd delen besluit:Bestuursorgaan als
# sh:targetClass; rep:discriminator houdt ze in de seed uit elkaar (een
# orgaan in tijd heeft generiek:isTijdspecialisatieVan, een vast orgaan
# heeft die niet). Een spec kiest er één met rep:entity.
# Keten leidinggevenden: functionaris -> bestuursfunctie (org:holds) <-
# leidinggevend orgaan in tijd (lblodlg:heeftBestuursfunctie) ->
# leidinggevend bestuursorgaan (generiek:isTijdspecialisatieVan) ->
# bestuurseenheid (besluit:bestuurt). Elke leidinggevende functie heeft een
# eigen orgaan per eenheid (bv. "Algemeen directeur Gemeente Lille"), zonder
# mandaten. Naar de mandatarissen van dezelfde eenheid gaat het dus via de
# eenheid terug naar haar politieke organen.
#
# Vier entiteiten delen besluit:Bestuursorgaan als sh:targetClass;
# rep:discriminator houdt ze in de seed uit elkaar (alle voorwaarden gelden
# samen): een orgaan in tijd heeft generiek:isTijdspecialisatieVan, een vast
# orgaan niet; een leidinggevend orgaan heeft een classificatie uit
# ent:LeidinggevendeOrgaanClassificaties, een orgaan in tijd via zijn vaste
# orgaan. De classificatie beslist, niet of er een functie aan hangt: zo
# blijft een leidinggevend orgaan zonder functie zichtbaar als fout in de
# data. Een spec kiest er één met rep:entity. Let op: in zuivere SHACL moet
# elk besluit:Bestuursorgaan aan alle vier de shapes voldoen;
# rep:discriminator (een eigen uitbreiding) beperkt sh:targetClass. Zie de
# README van natural-language-report-service.
#
# Niet opgenomen (geen rapportvelden): harvest-velden en vendor-relaties.
# Een generiek org:Post (posts) deelt org:role met het mandaat; hier wint
# de lezing van het mandaat (bestuursfunctie). De eredienst-varianten
# (EredienstMandataris) staan in erediensten.ttl.
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@@ -71,11 +87,26 @@
sh:declare [ sh:prefix "xsd" ; sh:namespace "http://www.w3.org/2001/XMLSchema#"^^xsd:anyURI ] ;
] .
# De classificaties van leidinggevende organen, zoals de migraties ze
# gebruiken (bv. 2024/20241028125621). Eén lijst voor de rep:discriminator
# van de vier organen hieronder.
ent:LeidinggevendeOrgaanClassificaties
rdf:first <http://data.vlaanderen.be/id/concept/BestuursorgaanClassificatieCode/39854196-f214-4688-87a1-d6ad12baa2fa> ; # Algemeen directeur
rdf:rest (
<http://data.vlaanderen.be/id/concept/BestuursorgaanClassificatieCode/11f0af9e-016c-4e0b-983a-d8bc73804abc> # Adjunct-algemeen directeur
<http://data.vlaanderen.be/id/concept/BestuursorgaanClassificatieCode/62644b9c-4514-41dd-a660-4c35257f2b35> # Financieel directeur
<http://data.vlaanderen.be/id/concept/BestuursorgaanClassificatieCode/ed40469e-3b6f-4f38-99ba-18912ee352b0> # Adjunct-financieel directeur
<http://data.vlaanderen.be/id/concept/BestuursorgaanClassificatieCode/3e9f22c1-0d35-445b-8a37-494addedf2d8> # Financieel beheerder
<http://data.vlaanderen.be/id/concept/BestuursorgaanClassificatieCode/41caf7e6-b040-4720-9cc2-a96cfffed5b4> # Leidend ambtenaar
<http://data.vlaanderen.be/id/concept/BestuursorgaanClassificatieCode/5ab19107-82d2-4273-a986-3da86fda050d> # Griffier
) .
ent:Bestuursorgaan
a sh:NodeShape ;
rdfs:label "bestuursorgaan" ;
sh:targetClass besluit:Bestuursorgaan ;
rep:discriminator [ sh:path generiek:isTijdspecialisatieVan ; sh:maxCount 0 ] ;
rep:discriminator [ sh:path org:classification ; sh:in ent:LeidinggevendeOrgaanClassificaties ; sh:maxCount 0 ] ;
sh:property [ sh:path besluit:bestuurt ; sh:name "bestuurt" ;
sh:description "De bestuurseenheid die door dit orgaan bestuurd wordt" ;
sh:node ent:Bestuurseenheid ] ;
@@ -86,11 +117,28 @@ ent:Bestuursorgaan
sh:description "De organen in tijd die bij dit vaste orgaan horen" ;
sh:node ent:BestuursorgaanInTijd ] .
ent:LeidinggevendBestuursorgaan
a sh:NodeShape ;
rdfs:label "leidinggevend bestuursorgaan" ;
sh:targetClass besluit:Bestuursorgaan ;
rep:discriminator [ sh:path generiek:isTijdspecialisatieVan ; sh:maxCount 0 ] ;
rep:discriminator [ sh:path org:classification ; sh:in ent:LeidinggevendeOrgaanClassificaties ] ;
sh:property [ sh:path besluit:bestuurt ; sh:name "bestuurt" ;
sh:description "De bestuurseenheid waar deze leidinggevende functie bij hoort" ;
sh:node ent:Bestuurseenheid ] ;
sh:property [ sh:path org:classification ; sh:name "soort bestuursorgaan" ;
sh:description "Bv. algemeen directeur, financieel directeur" ;
sh:node ent:BestuursorgaanClassificatie ] ;
sh:property [ sh:path [ sh:inversePath generiek:isTijdspecialisatieVan ] ; sh:name "heeft tijdsspecialisaties" ;
sh:description "De leidinggevende organen in tijd die bij dit vaste orgaan horen" ;
sh:node ent:LeidinggevendBestuursorgaanInTijd ] .
ent:BestuursorgaanInTijd
a sh:NodeShape ;
rdfs:label "bestuursorgaanInTijd" ;
sh:targetClass besluit:Bestuursorgaan ;
rep:discriminator [ sh:path generiek:isTijdspecialisatieVan ; sh:minCount 1 ] ;
rep:discriminator [ sh:path ( generiek:isTijdspecialisatieVan org:classification ) ; sh:in ent:LeidinggevendeOrgaanClassificaties ; sh:maxCount 0 ] ;
sh:property [ sh:path mandaat:bindingStart ; sh:name "binding start" ;
sh:description "Start van de bestuursperiode; orgaan in tijd" ;
sh:datatype xsd:dateTime ] ;
@@ -102,7 +150,22 @@ ent:BestuursorgaanInTijd
sh:node ent:Bestuursorgaan ] ;
sh:property [ sh:path org:hasPost ; sh:name "bevat mandaat" ;
sh:description "De mandaten die dit orgaan bevat" ;
sh:node ent:Mandaat ] ;
sh:node ent:Mandaat ] .
ent:LeidinggevendBestuursorgaanInTijd
a sh:NodeShape ;
rdfs:label "leidinggevend bestuursorgaanInTijd" ;
sh:targetClass besluit:Bestuursorgaan ;
rep:discriminator [ sh:path ( generiek:isTijdspecialisatieVan org:classification ) ; sh:in ent:LeidinggevendeOrgaanClassificaties ] ;
sh:property [ sh:path mandaat:bindingStart ; sh:name "binding start" ;
sh:description "Start van de periode; leidinggevend orgaan in tijd" ;
sh:datatype xsd:dateTime ] ;
sh:property [ sh:path mandaat:bindingEinde ; sh:name "binding einde" ;
sh:description "Einde van de periode; leidinggevend orgaan in tijd" ;
sh:datatype xsd:dateTime ] ;
sh:property [ sh:path <https://data.vlaanderen.be/ns/generiek#isTijdspecialisatieVan> ; sh:name "is tijdspecialisatie van" ;
sh:description "Het leidinggevend vaste orgaan waarvan dit orgaan in tijd een periode is" ;
sh:node ent:LeidinggevendBestuursorgaan ] ;
sh:property [ sh:path lblodlg:heeftBestuursfunctie ; sh:name "bevat bestuursfunctie" ;
sh:description "De leidinggevende functies van dit orgaan, bv. secretaris" ;
sh:node ent:LeidinggevendeBestuursfunctie ] .
@@ -136,8 +199,8 @@ ent:Bestuurseenheid
sh:description "De hoofdvestiging van de bestuurseenheid" ;
sh:node ent:Vestiging ] ;
sh:property [ sh:path [ sh:inversePath besluit:bestuurt ] ; sh:name "bestuursorganen" ;
sh:description "De bestuursorganen van deze bestuurseenheid" ;
sh:node ent:Bestuursorgaan ].
sh:description "De bestuursorganen van deze bestuurseenheid: politiek of leidinggevend" ;
sh:or ( [ sh:node ent:Bestuursorgaan ] [ sh:node ent:LeidinggevendBestuursorgaan ] ) ].
ent:Mandaat
a sh:NodeShape ;
@@ -205,8 +268,11 @@ ent:LeidinggevendeBestuursfunctie
sh:property [ sh:path schema:contactPoint ; sh:name "contact" ;
sh:node ent:ContactPunt ] ;
sh:property [ sh:path [ sh:inversePath lblodlg:heeftBestuursfunctie ] ; sh:name "in bestuursorgaan" ;
sh:description "Het bestuursorgaan waar deze functie bij hoort" ;
sh:node ent:BestuursorgaanInTijd ] .
sh:description "Het leidinggevend orgaan in tijd waar deze functie bij hoort" ;
sh:node ent:LeidinggevendBestuursorgaanInTijd ] ;
sh:property [ sh:path [ sh:inversePath org:holds ] ; sh:name "bekleders" ;
sh:description "De functionarissen die deze functie bekleden" ;
sh:node ent:Functionaris ] .
ent:Functionaris
a sh:NodeShape ;
@@ -244,12 +310,9 @@ ent:Persoon
sh:datatype xsd:string ] ;
sh:property [ sh:path persoon:gebruikteVoornaam ; sh:name "gebruikte voornaam" ;
sh:datatype xsd:string ] ;
sh:property [ sh:path [ sh:inversePath mandaat:isBestuurlijkeAliasVan ] ; sh:name "mandatarissen" ;
sh:description "De mandatarissen van deze persoon" ;
sh:node ent:Mandataris ] ;
sh:property [ sh:path [ sh:inversePath mandaat:isBestuurlijkeAliasVan ] ; sh:name "functionarissen" ;
sh:description "De leidinggevende functies van deze persoon" ;
sh:node ent:Functionaris ] ;
sh:property [ sh:path [ sh:inversePath mandaat:isBestuurlijkeAliasVan ] ; sh:name "mandatarissen en functionarissen" ;
sh:description "De mandaten en de leidinggevende functies van deze persoon" ;
sh:or ( [ sh:node ent:Mandataris ] [ sh:node ent:Functionaris ] ) ] ;
sh:property [ sh:path [ sh:inversePath org:heldBy ] ; sh:name "posities" ;
sh:description "De posities (agent-in-positie) van deze persoon" ;
sh:node ent:AgentInPositie ] .

View File

@@ -389,10 +389,8 @@ ent:Werkingsgebied
sh:property [ sh:path geo:sfWithin ; sh:name "ligt in" ;
sh:node ent:Werkingsgebied ] ;
sh:property [ sh:path [ sh:inversePath geo:sfWithin ] ; sh:name "bevat" ;
sh:node ent:Werkingsgebied ] ;
sh:property [ sh:path [ sh:inversePath geo:sfWithin ] ; sh:name "bestuurseenheden" ;
sh:description "De bestuurseenheden die in dit gebied liggen" ;
sh:node ent:Bestuurseenheid ] ;
sh:description "De gebieden en de bestuurseenheden die in dit gebied liggen" ;
sh:or ( [ sh:node ent:Werkingsgebied ] [ sh:node ent:Bestuurseenheid ] ) ] ;
sh:property [ sh:path skos:exactMatch ; sh:name "exacte match" ;
sh:node ent:Concept ] .

View File

@@ -1,104 +0,0 @@
# The report spec, as SHACL
A spec is `rep:ReportSpec` + `sh:NodeShape`. The shape half selects
the subjects and is real SHACL; the `rep:` half says what the CSV holds,
which SHACL has no word for. This file is the spec format's own shapes graph:
a machine-readable definition that doubles as reference documentation.
The namespace:
```
rep: → http://mu.semte.ch/vocabularies/reporting/
```
File: `config/report-profiles/report-spec-shapes.ttl`.
## What it defines
One `sh:NodeShape` per term:
- **ReportSpec** — the document. `dct:title`, `rep:profile` (which profile it
was written against), `sh:targetClass`, `rep:entity` (optional; picks the
shape when two entities share one `sh:targetClass`), `sh:property` (the
filters), `rep:columns` (the CSV, in order, an rdf:List).
- **Filter** — a `sh:property` on the ReportSpec: `sh:path` plus exactly one
condition (`sh:hasValue`, `sh:in`, `sh:minCount`/`sh:maxCount`,
`sh:min/maxInclusive`/`sh:min/maxExclusive`, `sh:pattern` + `sh:flags`,
`rep:anyOf`). A path with no condition is refused by the validator.
Optional `rep:where` (see below).
- **Column** — a list item: `sh:path` (hops, or `rep:self` for the row's own
subject), `rdfs:label` (unique, required), optional `rep:collect`
(`sh:groupConcat` default, `rep:row`, `sh:min`, `sh:max`), optional
`sh:separator`, optional `sh:nodeKind sh:IRI` (the cell shows the URI of
the node the path ends on; only then may the path end on a link). No
constraints on a column — `sh:hasValue` and friends belong in filters, or
in the column's `rep:where`.
## Conditions on the way: rep:where
A filter tests only the end of its own path, and two filters never share a
node. `rep:where` tests a node in the middle of a path. It is written like a
filter, with its path from the row. The steps it has in common with its
filter or column are the same nodes; its condition tests where its own path
ends. On a filter it drops rows; on a column it drops values from the cell,
never rows. Several `rep:where` on one filter or column all apply.
```turtle
[ sh:path ( org:holds [ sh:inversePath lblodlg:heeftBestuursfunctie ]
generiek:isTijdspecialisatieVan besluit:bestuurt
[ sh:inversePath besluit:bestuurt ] [ sh:inversePath generiek:isTijdspecialisatieVan ]
org:hasPost [ sh:inversePath org:holds ]
mandaat:isBestuurlijkeAliasVan foaf:familyName ) ;
rdfs:label "Burgemeester" ;
rep:where [ sh:path ( org:holds [ sh:inversePath lblodlg:heeftBestuursfunctie ]
generiek:isTijdspecialisatieVan besluit:bestuurt
[ sh:inversePath besluit:bestuurt ] [ sh:inversePath generiek:isTijdspecialisatieVan ]
org:hasPost org:role ) ;
sh:hasValue <http://data.vlaanderen.be/id/concept/BestuursfunctieCode/5ab0e9b8a3b2ca7c5e000013> ] ]
```
Per leidinggevende: the names of the mandatarissen whose mandate, in an
organ of the same bestuurseenheid, has the role Burgemeester. The condition
shares seven steps with the column, so it tests the mandate. `validate_spec`
says so: `rep:where 1 on column "Burgemeester" applies at "mandaat", after
step 7`.
On a filter with `sh:maxCount 0`, the filter and its `rep:where` together
become "none such".
## What it cannot say (and the runner enforces anyway)
A shapes graph cannot express "a column must end on a value field, or on a
link with `sh:nodeKind sh:IRI`" (that needs the profile), "a path resolves hop by hop" (needs the profile walk),
"at most 24 anyOf terms" (cardinality over list contents), or "anyOf takes
words, not URIs" (term typing), or "a rep:where shares at least one step with
its filter or column", or "a date or number condition ends on a value, not on
a link". Those live in the service's `src/runner/check.js`.
## Not yet
`sh:count`, `sh:sum`, `sh:orderBy`, `sh:limit`, `sh:offset` (SHACL-AF names for when counting and ordering arrive), `rep:graph` (regex the subject's
graph must match).
## Example
```turtle
<http://data.lblod.info/id/report-specs/e3f1> a rep:ReportSpec , sh:NodeShape ;
dct:title "Meldingen met bijlagen" ;
rep:profile <http://data.lblod.info/id/report-profiles/toezicht> ;
sh:targetClass meb:Submission ;
sh:property [ sh:path ( pav:createdBy skos:prefLabel ) ;
sh:hasValue "West-Vlaanderen" ] ;
rep:columns (
[ sh:path rep:self ; rdfs:label "melding" ]
[ sh:path nmo:sentDate ; rdfs:label "datumVerstuurd" ]
[ sh:path ( prov:generated dct:hasPart nfo:fileName ) ;
rdfs:label "bestandsnamen" ; rep:collect rep:row ]
) .
```
## Validating a spec against these shapes
The shapes are self-describing documentation. `validate_spec` (the conversion tool)
does more: it walks every path through the mounted profile and refuses
what a shapes graph cannot see. Both together are the spec contract.

View File

@@ -1,213 +0,0 @@
# Shapes for the report spec itself (rep:ReportSpec).
# Reference definition of the spec format: a machine-readable
# contract that doubles as documentation. validate_spec does more than this
# graph can express: it walks every path through the mounted profile and
# refuses what shapes alone cannot see (columns ending on a link without
# sh:nodeKind sh:IRI, unresolved
# hops, anyOf term limits, filters without a condition).
#
# Not yet: sh:count, sh:sum, sh:orderBy, sh:limit, sh:offset, rep:graph.
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix dct: <http://purl.org/dc/terms/> .
@prefix rep: <http://mu.semte.ch/vocabularies/reporting/> .
<http://data.lblod.info/id/report-profiles/report-spec>
a owl:Ontology ;
dct:title "Report spec shapes" ;
sh:prefixes [
sh:declare [ sh:prefix "rep" ; sh:namespace "http://mu.semte.ch/vocabularies/reporting/"^^xsd:anyURI ] ;
sh:declare [ sh:prefix "sh" ; sh:namespace "http://www.w3.org/ns/shacl#"^^xsd:anyURI ] ;
sh:declare [ sh:prefix "rdf" ; sh:namespace "http://www.w3.org/1999/02/22-rdf-syntax-ns#"^^xsd:anyURI ] ;
sh:declare [ sh:prefix "rdfs"; sh:namespace "http://www.w3.org/2000/01/rdf-schema#"^^xsd:anyURI ] ;
sh:declare [ sh:prefix "dct" ; sh:namespace "http://purl.org/dc/terms/"^^xsd:anyURI ] ;
] .
rep:ReportSpecShape
a sh:NodeShape ;
rdfs:label "rapportspec" ;
sh:targetClass rep:ReportSpec ;
sh:property [
sh:path dct:title ;
sh:name "titel" ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path rep:profile ;
sh:name "profiel" ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:targetClass ;
sh:name "klasse" ;
sh:nodeKind sh:IRI ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
# rep:entity picks the shape when two entities share one sh:targetClass
# (the profile lists the entity URI per entity).
sh:property [
sh:path rep:entity ;
sh:name "entiteit" ;
sh:nodeKind sh:IRI ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:property ;
sh:name "filter" ;
sh:node rep:FilterShape ;
] ;
sh:property [
sh:path rep:columns ;
sh:name "kolommen" ;
sh:minCount 1 ;
sh:maxCount 1 ;
] .
# A filter: a path plus exactly one condition. A path without a condition
# selects everything and is refused by check.js (filterConstraint).
rep:FilterShape
a sh:NodeShape ;
sh:property [
sh:path sh:path ;
sh:name "pad" ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:hasValue ;
sh:name "gelijk aan" ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:in ;
sh:name "in lijst" ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:minInclusive ;
sh:name "vanaf" ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:maxInclusive ;
sh:name "tot en met" ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:minExclusive ;
sh:name "na" ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:maxExclusive ;
sh:name "voor" ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:pattern ;
sh:name "regex" ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:flags ;
sh:name "regex-vlaggen" ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:minCount ;
sh:name "minimaal aantal" ;
sh:datatype xsd:integer ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:maxCount ;
sh:name "maximaal aantal" ;
sh:datatype xsd:integer ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path rep:anyOf ;
sh:name "een van deze woorden" ;
sh:maxCount 1 ;
] ;
# rep:where: a condition on a node this filter walks through, written as a
# filter from the row. The steps it shares with this filter are the same
# nodes. check.js refuses one that shares no step, and one nested in
# another (whereConditions).
sh:property [
sh:path rep:where ;
sh:name "voorwaarde onderweg" ;
sh:node rep:FilterShape ;
] ;
sh:property [
sh:path rdfs:label ;
sh:name "label" ;
sh:maxCount 0 ;
] .
# A column: path, label, collect mode, separator. No constraints here;
# check.js refuses a constraint inside rep:columns (columnPure). The one
# exception is sh:nodeKind sh:IRI: the cell shows the URI of the node the
# path ends on, and only then may the path end on a link. A condition
# on a node the column walks through goes in rep:where; it drops values from
# the cell, never rows.
rep:ColumnShape
a sh:NodeShape ;
sh:property [
sh:path sh:path ;
sh:name "pad" ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path rdfs:label ;
sh:name "kolomnaam" ;
sh:datatype xsd:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path rep:collect ;
sh:name "verzamelwijze" ;
sh:nodeKind sh:IRI ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:separator ;
sh:name "scheidingsteken" ;
sh:datatype xsd:string ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path sh:nodeKind ;
sh:name "toon de URI" ;
sh:in ( sh:IRI ) ;
sh:maxCount 1 ;
] ;
sh:property [
sh:path rep:where ;
sh:name "voorwaarde onderweg" ;
sh:node rep:FilterShape ;
] .
# rep:collect takes exactly these values (SHACL Core groupConcat is the
# default when absent; min/max are SHACL-AF names). A shapes graph cannot
# enumerate an IRI value set without a class; check.js enforces it (columnPure).
rep:RowShape
a sh:NodeShape ; rdfs:comment "rep:collect value: one CSV line per value" .
# rep:self as sh:path marks the row's own subject URI: nothing to walk,
# the runner takes it from the seed.
rep:self
a rdf:Property ;
rdfs:comment "in a column's sh:path slot: the row's own subject URI" .