Skip to content

truth-pass: Annex III process genus, regulatory-basis provenance, pin… #145

truth-pass: Annex III process genus, regulatory-basis provenance, pin…

truth-pass: Annex III process genus, regulatory-basis provenance, pin… #145

name: ROBOT Validation
# Independent DL validation using ROBOT (v1.9.10) and HermiT.
# Runs alongside the production owlrl pipeline as a cross-check, not a replacement.
#
# Two-job structure:
#
# Job 1: ontology-validate (single, ~5-10 min)
# Phase 0: confirm ROBOT installs and runs in CI.
# Phase 1: merge ARCO ontology files and run robot validate-profile --profile DL.
# Confirms the ontology is OWL 2 DL conformant.
# Gates CI: profile violations fail the build.
# Phase 2: run robot reason --reasoner HermiT for consistency check.
# Gates CI: HermiT crash or DL inconsistency fails the build.
#
# Job 2: hermit-cross-check (matrix, runs in parallel; needs: ontology-validate)
# Phase 3: per-fixture HermiT vs OWL-RL classification agreement.
# Each certificate-grade fixture runs as its own parallel matrix job.
# Gates CI: any classification mismatch on any fixture fails the build.
#
# Why two jobs with `needs:` instead of one big serial job:
# - Phase 3 only makes sense if the ontology is already DL-conformant + consistent
# (Phases 1+2). Sequential dependency preserves that ordering.
# - Within Phase 3, the six fixtures are independent. Running them in parallel
# (matrix) drops Phase 3 wall time from ~35-45 min sequential to ~7-10 min.
# - Total wall time: ~10 min (Phases 1+2) + ~10 min (matrix) = ~20 min,
# instead of the prior ~50 min serial-with-timeout-bump design.
#
# GhostSystem (ARCO_instances_adversarial_blanknode.ttl) is excluded from the
# cross-check matrix. Its disposition is an anonymous individual; HermiT does
# not emit ClassAssertion axioms for blank-node individuals (DL profile
# behavior, not a defect). The audit-side detect_latent_risk traversal returns
# False under HermiT and True under OWL-RL on GhostSystem only. GhostSystem is
# a reasoner-property probe, not production modeling guidance.
# Documented in LIMITATIONS.md §7.4.
on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
ROBOT_VERSION: v1.9.10
ROBOT_JAR_URL: https://github.com/ontodev/robot/releases/download/v1.9.10/robot.jar
jobs:
ontology-validate:
name: DL profile + HermiT consistency
runs-on: ubuntu-latest
# 30 min: realistic per-job is ~10-15 min (ROBOT setup + Java + merge +
# validate-profile + HermiT consistency on the merged ontology). 30 gives
# comfortable headroom for slow runners without masking a hung job.
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Cache ROBOT JAR
id: cache-robot
uses: actions/cache@v4
with:
path: ~/.local/share/robot/robot.jar
key: robot-${{ env.ROBOT_VERSION }}
- name: Download ROBOT
if: steps.cache-robot.outputs.cache-hit != 'true'
shell: bash
run: |
set -euo pipefail
mkdir -p ~/.local/share/robot
curl -L -o ~/.local/share/robot/robot.jar "$ROBOT_JAR_URL"
- name: Verify ROBOT version
shell: bash
run: |
set -euo pipefail
java -jar ~/.local/share/robot/robot.jar --version
echo "ROBOT smoke test PASSED."
# Phase 1: OWL 2 DL profile validation.
# Merges the same files the Python pipeline loads, then runs validate-profile.
# Uses Sentinel as the instance fixture for this validation; per-fixture
# cross-checking moves to Job 2 (matrix).
- name: Merge ARCO ontology files
shell: bash
run: |
set -euo pipefail
ROBOT="java -jar $HOME/.local/share/robot/robot.jar"
$ROBOT merge \
--input "03_TECHNICAL_CORE/ontology/imports/bfo-2020.owl" \
--input "03_TECHNICAL_CORE/ontology/imports/iao_bot.owl" \
--input "03_TECHNICAL_CORE/ontology/imports/ro_bot.owl" \
--input "03_TECHNICAL_CORE/ontology/imports/cco_bot.owl" \
--input "03_TECHNICAL_CORE/ontology/ARCO_core.ttl" \
--input "03_TECHNICAL_CORE/ontology/ARCO_governance_extension.ttl" \
--input "03_TECHNICAL_CORE/ontology/ARCO_instances_sentinel.ttl" \
--output /tmp/arco_merged.owl
echo "Merge complete: /tmp/arco_merged.owl"
- name: Validate OWL 2 DL profile
shell: bash
run: |
ROBOT="java -jar $HOME/.local/share/robot/robot.jar"
mkdir -p /tmp/robot-reports
set +e
$ROBOT validate-profile \
--profile DL \
--input /tmp/arco_merged.owl \
--output /tmp/robot-reports/dl_profile_report.txt \
2>&1
PROFILE_EXIT=$?
set -e
echo "=== OWL 2 DL Profile Report ==="
cat /tmp/robot-reports/dl_profile_report.txt || echo "(no report file generated)"
echo "=== Profile validation exit code: ${PROFILE_EXIT} ==="
if [ "${PROFILE_EXIT}" -eq 0 ]; then
echo "RESULT: ARCO is OWL 2 DL conformant."
else
echo "FAIL: ARCO has OWL 2 DL profile violations. See report above."
exit 1
fi
- name: Upload profile report
if: always()
uses: actions/upload-artifact@v4
with:
name: owl2-dl-profile-report
path: /tmp/robot-reports/
if-no-files-found: warn
# Phase 2: HermiT consistency check.
# Confirms ontology is DL-consistent and HermiT terminates without error.
# Reasoned graph uploaded as artifact.
- name: Reason with HermiT (consistency)
shell: bash
run: |
ROBOT="java -jar $HOME/.local/share/robot/robot.jar"
mkdir -p /tmp/robot-reports
set +e
$ROBOT reason \
--reasoner hermit \
--axiom-generators "ClassAssertion SubClass" \
--include-indirect true \
--input /tmp/arco_merged.owl \
--output /tmp/robot-reports/arco_reasoned.owl \
2>&1 | tee /tmp/robot-reports/hermit_log.txt
REASON_EXIT=${PIPESTATUS[0]}
set -e
echo "=== HermiT reasoning exit code: ${REASON_EXIT} ==="
if [ "${REASON_EXIT}" -eq 0 ]; then
echo "RESULT: ARCO is consistent under HermiT. Reasoned graph produced."
ls -la /tmp/robot-reports/arco_reasoned.owl
else
echo "FAIL: HermiT reported a problem. See log above."
exit 1
fi
- name: Upload HermiT reasoned graph
if: always()
uses: actions/upload-artifact@v4
with:
name: hermit-reasoned-graph
path: |
/tmp/robot-reports/arco_reasoned.owl
/tmp/robot-reports/hermit_log.txt
if-no-files-found: warn
hermit-cross-check:
name: HermiT vs OWL-RL (${{ matrix.fixture }})
runs-on: ubuntu-latest
# 45 min: realistic per-job is ~10-15 min (one fixture's HermiT pass plus
# setup). 45 gives 3x headroom on a slow runner so we are not chasing
# timeouts on what is supposed to be the fast parallel design. Real fix
# if a single matrix job ever hits 45 min is profiling that fixture, not
# bumping further.
timeout-minutes: 45
needs: ontology-validate
strategy:
# All fixtures still report even if one fails: better diagnostic signal
# than fail-fast, since a real DL/RL disagreement on any fixture is its
# own information.
fail-fast: false
matrix:
fixture:
- ARCO_instances_sentinel.ttl
- ARCO_instances_creditscoring.ttl
- ARCO_instances_verification.ttl
- ARCO_instances_adversarial_decoy.ttl
- ARCO_instances_adversarial_decoy_5b.ttl
- ARCO_instances_flag_tests.ttl
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Cache ROBOT JAR
id: cache-robot
uses: actions/cache@v4
with:
path: ~/.local/share/robot/robot.jar
key: robot-${{ env.ROBOT_VERSION }}
- name: Download ROBOT
if: steps.cache-robot.outputs.cache-hit != 'true'
shell: bash
run: |
set -euo pipefail
mkdir -p ~/.local/share/robot
curl -L -o ~/.local/share/robot/robot.jar "$ROBOT_JAR_URL"
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Python deps
shell: bash
run: |
python -m pip install --quiet rdflib owlrl==7.1.4
# Phase 3: HermiT vs OWL-RL agreement on ONE fixture (this matrix slot).
# Uses --fixture argument to select; sequential six-fixture sweep stays
# available (no --fixture argument) for local CLI use.
- name: HermiT vs OWL-RL agreement (${{ matrix.fixture }})
shell: bash
run: |
set -euo pipefail
export ROBOT_JAR="$HOME/.local/share/robot/robot.jar"
python 03_TECHNICAL_CORE/scripts/hermit_cross_check.py \
--fixture "${{ matrix.fixture }}"