diff --git a/ontology/uco/core/core.ttl b/ontology/uco/core/core.ttl
index f0ecbace..9d59127b 100644
--- a/ontology/uco/core/core.ttl
+++ b/ontology/uco/core/core.ttl
@@ -484,6 +484,29 @@ core:hasFacet
rdfs:range core:Facet ;
.
+core:hasFacet-shape
+ a sh:PropertyShape ;
+ sh:path core:hasFacet ;
+ sh:sparql [
+ a sh:SPARQLConstraint ;
+ sh:message "hasFacet must not be used to link two objects to one Facet."@en ;
+ sh:select """
+ PREFIX core:
+ PREFIX owl:
+ SELECT $this ?value
+ WHERE {
+ ?value core:hasFacet $this .
+ ?nOtherValue core:hasFacet $this .
+ FILTER ( ?value != ?nOtherValue )
+ FILTER NOT EXISTS {
+ ?value owl:sameAs|^owl:sameAs ?nOtherValue .
+ }
+ }
+ """ ;
+ ] ;
+ sh:targetObjectsOf core:hasFacet ;
+ .
+
core:id
a owl:DatatypeProperty ;
rdfs:label "id"@en ;
diff --git a/tests/examples/Makefile b/tests/examples/Makefile
index 2af81c99..87d80235 100644
--- a/tests/examples/Makefile
+++ b/tests/examples/Makefile
@@ -23,6 +23,8 @@ all: \
action_result_PASS_validation.ttl \
co_PASS_validation.ttl \
co_XFAIL_validation.ttl \
+ has_facet_inverse_functional_PASS_validation.ttl \
+ has_facet_inverse_functional_XFAIL_validation.ttl \
hash_PASS_validation.ttl \
hash_XFAIL_validation.ttl \
location_PASS_validation.ttl \
@@ -84,6 +86,8 @@ check: \
action_result_PASS_validation.ttl \
co_PASS_validation.ttl \
co_XFAIL_validation.ttl \
+ has_facet_inverse_functional_PASS_validation.ttl \
+ has_facet_inverse_functional_XFAIL_validation.ttl \
hash_PASS_validation.ttl \
hash_XFAIL_validation.ttl \
location_PASS_validation.ttl \
diff --git a/tests/examples/has_facet_inverse_functional_PASS.json b/tests/examples/has_facet_inverse_functional_PASS.json
new file mode 100644
index 00000000..573740fc
--- /dev/null
+++ b/tests/examples/has_facet_inverse_functional_PASS.json
@@ -0,0 +1,30 @@
+{
+ "@context": {
+ "kb": "http://example.org/kb/",
+ "core": "https://ontology.unifiedcyberontology.org/uco/core/",
+ "owl": "http://www.w3.org/2002/07/owl#"
+ },
+ "@graph": [
+ {
+ "@id": "kb:facet-1",
+ "@type": "core:Facet"
+ },
+ {
+ "@id": "kb:object-1",
+ "@type": "core:UcoObject",
+ "core:hasFacet": {
+ "@id": "kb:facet-1"
+ },
+ "owl:sameAs": {
+ "@id": "kb:object-2"
+ }
+ },
+ {
+ "@id": "kb:object-2",
+ "@type": "core:UcoObject",
+ "core:hasFacet": {
+ "@id": "kb:facet-1"
+ }
+ }
+ ]
+}
diff --git a/tests/examples/has_facet_inverse_functional_XFAIL.json b/tests/examples/has_facet_inverse_functional_XFAIL.json
new file mode 100644
index 00000000..46af64da
--- /dev/null
+++ b/tests/examples/has_facet_inverse_functional_XFAIL.json
@@ -0,0 +1,26 @@
+{
+ "@context": {
+ "kb": "http://example.org/kb/",
+ "core": "https://ontology.unifiedcyberontology.org/uco/core/"
+ },
+ "@graph": [
+ {
+ "@id": "kb:facet-1",
+ "@type": "core:Facet"
+ },
+ {
+ "@id": "kb:object-1",
+ "@type": "core:UcoObject",
+ "core:hasFacet": {
+ "@id": "kb:facet-1"
+ }
+ },
+ {
+ "@id": "kb:object-2",
+ "@type": "core:UcoObject",
+ "core:hasFacet": {
+ "@id": "kb:facet-1"
+ }
+ }
+ ]
+}
diff --git a/tests/examples/test_validation.py b/tests/examples/test_validation.py
index 30addef8..8a6a0222 100644
--- a/tests/examples/test_validation.py
+++ b/tests/examples/test_validation.py
@@ -177,6 +177,21 @@ def test_action_result_PASS_validation():
g = load_validation_graph("action_result_PASS_validation.ttl", True)
assert isinstance(g, rdflib.Graph)
+def test_has_facet_inverse_functional_PASS() -> None:
+ confirm_validation_results(
+ "has_facet_inverse_functional_PASS_validation.ttl",
+ True
+ )
+
+def test_has_facet_inverse_functional_XFAIL() -> None:
+ confirm_validation_results(
+ "has_facet_inverse_functional_XFAIL_validation.ttl",
+ False,
+ expected_focus_node_severities={
+ ("http://example.org/kb/facet-1", str(NS_SH.Violation))
+ }
+ )
+
def test_hash_PASS() -> None:
g = load_validation_graph("hash_PASS_validation.ttl", True)
assert isinstance(g, rdflib.Graph)