diff --git a/rocrate_validator/profiles/five-safes-crate/may/4_sign_off.ttl b/rocrate_validator/profiles/five-safes-crate/may/4_sign_off.ttl
new file mode 100644
index 00000000..b30dedc2
--- /dev/null
+++ b/rocrate_validator/profiles/five-safes-crate/may/4_sign_off.ttl
@@ -0,0 +1,58 @@
+# Copyright (c) 2025 eScience Lab, The University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+@prefix ro: <./> .
+@prefix ro-crate: .
+@prefix five-safes-crate: .
+@prefix rdf: .
+@prefix schema: .
+@prefix sh: .
+@prefix validator: .
+@prefix xsd: .
+@prefix shp: .
+
+
+five-safes-crate:SignOffPhaseStartTime
+ a sh:NodeShape ;
+ sh:name "SignOffPhaseStartTime" ;
+
+ sh:target [
+ a sh:SPARQLTarget ;
+ sh:select """
+ PREFIX schema:
+ PREFIX shp:
+ SELECT ?this
+ WHERE {
+ ?this schema:additionalType shp:SignOff ;
+ schema:actionStatus ?status .
+ FILTER(?status IN (
+ "http://schema.org/ActiveActionStatus",
+ "http://schema.org/CompletedActionStatus",
+ "http://schema.org/FailedActionStatus"
+ ))
+ }
+ """ ;
+ ] ;
+
+ sh:property [
+ a sh:PropertyShape ;
+ sh:name "StartTime" ;
+ sh:path schema:startTime ;
+ sh:minCount 1 ;
+ sh:maxCount 1 ;
+ sh:pattern "^[0-9]{4}-[0-9]{2}-[0-9]{2}[Tt][0-9]{2}:[0-9]{2}:[0-9]{2}([.|,][0-9]+)?(Z|z|[+-][0-9]{2}:[0-9]{2})$" ;
+ sh:severity sh:Info ;
+ sh:description "Sign Off object MAY have a startTime property if action is active, completed or failed. This must follow ISO-8601 syntax" ;
+ sh:message "Sign Off object MAY have a startTime property if action is active, completed or failed. This must follow ISO-8601 syntax" ;
+ ] .
diff --git a/rocrate_validator/profiles/five-safes-crate/must/4_sign_off.ttl b/rocrate_validator/profiles/five-safes-crate/must/4_sign_off.ttl
new file mode 100644
index 00000000..2b61a259
--- /dev/null
+++ b/rocrate_validator/profiles/five-safes-crate/must/4_sign_off.ttl
@@ -0,0 +1,89 @@
+# Copyright (c) 2025 eScience Lab, The University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+@prefix ro: <./> .
+@prefix ro-crate: .
+@prefix five-safes-crate: .
+@prefix rdf: .
+@prefix schema: .
+@prefix purl: .
+@prefix sh: .
+@prefix validator: .
+@prefix xsd: .
+
+five-safes-crate:SignOffObjectActionAndName
+ a sh:NodeShape ;
+ sh:name "SignOff" ;
+ sh:description "Sign Off phase" ;
+
+ sh:target [
+ a sh:SPARQLTarget ;
+ sh:select """
+ PREFIX schema:
+ PREFIX shp:
+ SELECT ?this
+ WHERE {
+ ?this schema:additionalType shp:SignOff .
+ }
+ """ ;
+ ] ;
+
+ sh:property [
+ sh:path schema:name ;
+ sh:datatype xsd:string ;
+ sh:minCount 1 ;
+ sh:severity sh:Violation ;
+ sh:message "Sign Off phase MUST have a human-readable name string." ;
+ ] ;
+
+ sh:property [
+ sh:path rdf:type ;
+ sh:minCount 1 ;
+ sh:hasValue schema:AssessAction;
+ sh:severity sh:Violation ;
+ sh:message "Sign Off phase MUST be a `schema:AssessAction`." ;
+ ] .
+
+five-safes-crate:SignOffObjectHasActionStatus
+ a sh:NodeShape ;
+ sh:name "SignOffStatus" ;
+ sh:description "Sign Off Phase Action Status" ;
+
+ sh:target [
+ a sh:SPARQLTarget ;
+ sh:select """
+ PREFIX schema:
+ PREFIX shp:
+ SELECT ?this
+ WHERE {
+ ?this schema:additionalType shp:SignOff ;
+ schema:actionStatus ?status .
+ }
+ """ ;
+ ] ;
+
+ sh:property [
+ a sh:PropertyShape ;
+ sh:name "actionStatus" ;
+ sh:description "The value of actionStatus MUST be one of the allowed values." ;
+ sh:path schema:actionStatus ;
+ sh:in (
+ "http://schema.org/PotentialActionStatus"
+ "http://schema.org/ActiveActionStatus"
+ "http://schema.org/CompletedActionStatus"
+ "http://schema.org/FailedActionStatus"
+ ) ;
+ sh:severity sh:Violation ;
+ sh:message "The value of actionStatus MUST be one of the allowed values: PotentialActionStatus; ActiveActionStatus; CompletedActionStatus; FailedActionStatus." ;
+ ] .
diff --git a/rocrate_validator/profiles/five-safes-crate/should/4_sign_off.ttl b/rocrate_validator/profiles/five-safes-crate/should/4_sign_off.ttl
new file mode 100644
index 00000000..d671a0d9
--- /dev/null
+++ b/rocrate_validator/profiles/five-safes-crate/should/4_sign_off.ttl
@@ -0,0 +1,178 @@
+# Copyright (c) 2025 eScience Lab, The University of Manchester
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+@prefix ro: <./> .
+@prefix ro-crate: .
+@prefix five-safes-crate: .
+@prefix rdf: .
+@prefix schema: .
+@prefix sh: .
+@prefix validator: .
+@prefix xsd: .
+@prefix shp: .
+
+
+# There SHOULD be a Sign-Off Phase
+five-safes-crate:SignOffPhase
+ a sh:NodeShape ;
+ sh:targetNode <./> ;
+ sh:description "Check the Sign-Off Phase" ;
+ sh:sparql [
+ sh:select """
+ PREFIX schema:
+ PREFIX shp:
+ SELECT $this
+ WHERE {
+ FILTER NOT EXISTS {
+ ?action schema:additionalType shp:SignOff .
+ }
+ }
+ """ ;
+ sh:severity sh:Warning ;
+ sh:message "There SHOULD be a Sign-Off Phase in the Final RO-Crate" ;
+ ] ;
+ sh:sparql [
+ sh:select """
+ PREFIX schema:
+ PREFIX shp:
+ SELECT $this
+ WHERE {
+ ?action schema:additionalType shp:SignOff .
+ FILTER NOT EXISTS {
+ $this schema:mentions ?action .
+ }
+ }
+ """ ;
+ sh:severity sh:Warning ;
+ sh:message "The Root Data Entity SHOULD mention a Sign-Off Phase Object" ;
+ ] .
+
+
+five-safes-crate:SignOffPhaseProperties
+ a sh:NodeShape ;
+ sh:description "Check Sign-Off Phase Properties" ;
+ sh:target [
+ a sh:SPARQLTarget ;
+ sh:select """
+ PREFIX schema:
+ PREFIX shp:
+ SELECT ?this
+ WHERE {
+ ?this schema:additionalType shp:SignOff .
+ }
+ """
+ ] ;
+ sh:property [
+ sh:description "Check if the Sign Off phase has an actionStatus" ;
+ sh:path schema:actionStatus ;
+ sh:minCount 1 ;
+ sh:severity sh:Warning ;
+ sh:message "The Sign-Off Phase SHOULD have an actionStatus" ;
+ ] ;
+ sh:property [
+ sh:description "Check if the Sign Off phase has an agent" ;
+ sh:path schema:agent ;
+ sh:minCount 1 ;
+ sh:severity sh:Warning ;
+ sh:message "The Sign-Off Phase SHOULD have an agent" ;
+ ] ;
+ sh:property [
+ sh:description "Check if the Sign Off phase has an instrument (TRE Policy)" ;
+ sh:path schema:instrument ;
+ sh:class schema:CreativeWork ;
+ sh:nodeKind sh:IRI;
+ sh:minCount 1 ;
+ sh:severity sh:Warning ;
+ sh:message "The Sign-Off Phase SHOULD have an TRE policy (instrument) with type CreativeWork" ;
+ ] ;
+ sh:property [
+ sh:description "Check if the Sign Off phase has an instrument (TRE Policy)" ;
+ sh:path ( schema:instrument schema:name ) ;
+ sh:datatype xsd:string ;
+ sh:minCount 1 ;
+ sh:severity sh:Warning ;
+ sh:message "The Sign-Off Phase SHOULD have an TRE policy (instrument) with a human-readable name" ;
+ ] ;
+ sh:sparql [
+ a sh:SPARQLConstraint ;
+ sh:description "Check if the Sign Off phase lists the workflow as an object" ;
+ sh:select """
+ PREFIX schema:
+ PREFIX rocrate:
+ SELECT $this
+ WHERE {
+ ?root a schema:Dataset ;
+ schema:mainEntity ?mainEntity ;
+ rdf:type rocrate:RootDataEntity .
+ FILTER NOT EXISTS {
+ $this schema:object ?mainEntity .
+ }
+ }
+ """ ;
+ sh:severity sh:Warning ;
+ sh:message "The Sign-Off Phase SHOULD list the workflow (mainEntity) as an object" ;
+ ];
+ sh:sparql [
+ a sh:SPARQLConstraint ;
+ sh:description "Check if the Sign Off phase lists the Responsible Project as an object" ;
+ sh:select """
+ PREFIX schema:
+ PREFIX rocrate:
+ SELECT $this
+ WHERE {
+ ?root a schema:Dataset ;
+ rdf:type rocrate:RootDataEntity ;
+ schema:sourceOrganization ?sourceOrg .
+ FILTER NOT EXISTS {
+ $this schema:object ?sourceOrg .
+ }
+ }
+ """ ;
+ sh:severity sh:Warning ;
+ sh:message "The Sign-Off Phase SHOULD list the Responsible Project (sourceOrganization) as an object" ;
+ ].
+
+
+five-safes-crate:SignOffPhaseEndTime
+ a sh:NodeShape ;
+ sh:description "Sign Off end time check" ;
+
+ sh:target [
+ a sh:SPARQLTarget ;
+ sh:select """
+ PREFIX schema:
+ PREFIX shp:
+ SELECT ?this
+ WHERE {
+ ?this schema:additionalType shp:SignOff ;
+ schema:actionStatus ?status .
+ FILTER(?status IN (
+ "http://schema.org/CompletedActionStatus",
+ "http://schema.org/FailedActionStatus"
+ ))
+ }
+ """ ;
+ ] ;
+
+ sh:property [
+ a sh:PropertyShape ;
+ sh:name "EndTime" ;
+ sh:path schema:endTime ;
+ sh:minCount 1 ;
+ sh:maxCount 1 ;
+ sh:pattern "^[0-9]{4}-[0-9]{2}-[0-9]{2}[Tt][0-9]{2}:[0-9]{2}:[0-9]{2}([.|,][0-9]+)?(Z|z|[+-][0-9]{2}:[0-9]{2})$" ;
+ sh:severity sh:Warning ;
+ sh:description "Sign Off object SHOULD have endTime property if action completed or failed. This must follow ISO-8601 syntax" ;
+ sh:message "Sign Off object SHOULD have endTime property if action completed or failed. This must follow ISO-8601 syntax" ;
+ ] .
diff --git a/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json b/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json
index f1f48cd9..3401e529 100644
--- a/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json
+++ b/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json
@@ -277,6 +277,7 @@
},
"name": "Sign-off of execution according to Agreement policy: approved",
"endTime": "2023-04-19T17:15:12+01:00",
+ "startTime": "2023-04-19T10:15:12+01:00",
"object": [
{
"@id": "./"
diff --git a/tests/integration/profiles/five-safes-crate/test_5src_4_signoff_phase.py b/tests/integration/profiles/five-safes-crate/test_5src_4_signoff_phase.py
new file mode 100644
index 00000000..7f9cd8f3
--- /dev/null
+++ b/tests/integration/profiles/five-safes-crate/test_5src_4_signoff_phase.py
@@ -0,0 +1,579 @@
+# Copyright (c) 2024-2025 CRS4
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+
+from rocrate_validator.models import Severity
+from tests.ro_crates import ValidROC
+from tests.shared import do_entity_test, SPARQL_PREFIXES
+
+# set up logging
+logger = logging.getLogger(__name__)
+
+
+# ---- SHOULD fails tests
+
+
+def test_5src_no_signoff_phase():
+ """
+ Test a Five Safes Crate where no Sign-Off phase is listed.
+ """
+
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ <#signoff-3b741265-cfef-49ea-8138-a2fa149bf2f0> ?p ?o .
+ }
+ WHERE {
+ <#signoff-3b741265-cfef-49ea-8138-a2fa149bf2f0> ?p ?o .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhase"],
+ expected_triggered_issues=[
+ "There SHOULD be a Sign-Off Phase in the Final RO-Crate"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_no_name():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has no name.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:name ?name .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:name ?name .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.REQUIRED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOff"],
+ expected_triggered_issues=[
+ "Sign Off phase MUST have a human-readable name string."
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_wrong_type():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has no name.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff rdf:type ?type .
+ }
+ INSERT {
+ ?signoff rdf:type .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ rdf:type ?type .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.REQUIRED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOff"],
+ expected_triggered_issues=[
+ "Sign Off phase MUST be a `schema:AssessAction`."
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_wrong_action_status():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has the wrong action status.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:actionStatus ?status .
+ }
+ INSERT {
+ ?signoff schema:actionStatus .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:actionStatus ?status .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.REQUIRED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffStatus"],
+ expected_triggered_issues=[
+ "The value of actionStatus MUST be one of the allowed values:"
+ + " PotentialActionStatus; ActiveActionStatus; CompletedActionStatus; FailedActionStatus."
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_not_mentioned():
+ """
+ Test a Five Safes Crate where the Sign-Off phase is not mentioned by the MainRootEntity.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ <./> schema:mentions <#signoff-3b741265-cfef-49ea-8138-a2fa149bf2f0> .
+ }
+ WHERE {
+ <./> schema:mentions <#signoff-3b741265-cfef-49ea-8138-a2fa149bf2f0> .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhase"],
+ expected_triggered_issues=[
+ "The Root Data Entity SHOULD mention a Sign-Off Phase Object"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_no_endtime():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has no endTime.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:endTime ?endTime .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:endTime ?endTime .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseEndTime"],
+ expected_triggered_issues=[
+ "Sign Off object SHOULD have endTime property if action completed or failed."
+ + " This must follow ISO-8601 syntax"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_malformed_endtime():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has an endTime
+ in the wrong format.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:endTime ?endTime .
+ }
+ INSERT {
+ ?signoff schema:endTime <2025-10-20> .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:endTime ?endTime .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseEndTime"],
+ expected_triggered_issues=[
+ "Sign Off object SHOULD have endTime property if action completed or failed."
+ + " This must follow ISO-8601 syntax"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_no_starttime():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has no startTime.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:startTime ?startTime .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:startTime ?startTime .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.OPTIONAL,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseStartTime"],
+ expected_triggered_issues=[
+ "Sign Off object MAY have a startTime property if action is active, completed or failed."
+ + " This must follow ISO-8601 syntax"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_malformed_starttime():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has a startTime
+ in the wrong format.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:startTime ?startTime .
+ }
+ INSERT {
+ ?signoff schema:startTime <2025-10-20> .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:startTime ?startTime .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.OPTIONAL,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseStartTime"],
+ expected_triggered_issues=[
+ "Sign Off object MAY have a startTime property if action is active, completed or failed."
+ + " This must follow ISO-8601 syntax"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_no_actionstatus():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has no actionStatus.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:actionStatus ?actionStatus .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:actionStatus ?actionStatus .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseProperties"],
+ expected_triggered_issues=[
+ "The Sign-Off Phase SHOULD have an actionStatus"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_no_agent():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has no agent.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:agent ?agent .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:agent ?agent .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseProperties"],
+ expected_triggered_issues=[
+ "The Sign-Off Phase SHOULD have an agent"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_no_instrument():
+ """
+ Test a Five Safes Crate where the Sign-Off phase has no TRE policy (instrument).
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:instrument ?instrument .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:instrument ?instrument .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseProperties"],
+ expected_triggered_issues=[
+ "The Sign-Off Phase SHOULD have an TRE policy (instrument) with type CreativeWork"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_instrument_not_iri():
+ """
+ Test a Five Safes Crate where the Sign-Off phase TRE policy (instrument) is not an IRI.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:instrument ?instrument .
+ }
+ INSERT {
+ ?signoff schema:instrument "Not a cross-reference" .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:instrument ?instrument .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseProperties"],
+ expected_triggered_issues=[
+ "The Sign-Off Phase SHOULD have an TRE policy (instrument) with type CreativeWork"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_instrument_no_type():
+ """
+ Test a Five Safes Crate where the Sign-Off phase instrument has no type.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?instrument rdf:type ?type .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:instrument ?instrument .
+ ?instrument rdf:type ?type .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseProperties"],
+ expected_triggered_issues=[
+ "The Sign-Off Phase SHOULD have an TRE policy (instrument) with type CreativeWork"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_instrument_no_name():
+ """
+ Test a Five Safes Crate where the Sign-Off phase instrument has no type.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?instrument schema:name ?name .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:instrument ?instrument .
+ ?instrument schema:name ?name .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseProperties"],
+ expected_triggered_issues=[
+ "The Sign-Off Phase SHOULD have an TRE policy (instrument) with a human-readable name"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_object_notworkflow():
+ """
+ Test a Five Safes Crate where there is no workflow in the Sign-Off objects.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:object .
+ }
+ INSERT {
+ ?signoff schema:object .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:object .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseProperties"],
+ expected_triggered_issues=[
+ "The Sign-Off Phase SHOULD list the workflow (mainEntity) as an object"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )
+
+
+def test_5src_signoff_phase_object_not_responsible_project():
+ """
+ Test a Five Safes Crate where there is no Responsible Project in the Sign-Off objects.
+ """
+ sparql = (
+ SPARQL_PREFIXES
+ + """
+ DELETE {
+ ?signoff schema:object <#project-be6ffb55-4f5a-4c14-b60e-47e0951090c70> .
+ }
+ INSERT {
+ ?signoff schema:object .
+ }
+ WHERE {
+ ?signoff a schema:AssessAction ;
+ schema:additionalType ;
+ schema:object <#project-be6ffb55-4f5a-4c14-b60e-47e0951090c70> .
+ }
+ """
+ )
+
+ do_entity_test(
+ rocrate_path=ValidROC().five_safes_crate_result,
+ requirement_severity=Severity.RECOMMENDED,
+ expected_validation_result=False,
+ expected_triggered_requirements=["SignOffPhaseProperties"],
+ expected_triggered_issues=[
+ "The Sign-Off Phase SHOULD list the Responsible Project (sourceOrganization) as an object"
+ ],
+ profile_identifier="five-safes-crate",
+ rocrate_entity_mod_sparql=sparql,
+ )