diff --git a/Generation/Converters/Argumentum.AssetConverter.Tests/Ontology/OwlE2EGenerationValidationTests.cs b/Generation/Converters/Argumentum.AssetConverter.Tests/Ontology/OwlE2EGenerationValidationTests.cs
new file mode 100644
index 00000000..cfdf01fa
--- /dev/null
+++ b/Generation/Converters/Argumentum.AssetConverter.Tests/Ontology/OwlE2EGenerationValidationTests.cs
@@ -0,0 +1,211 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using Argumentum.AssetConverter.Ontology;
+using FluentAssertions;
+using OWLSharp.Ontology;
+using RDFSharp.Model;
+using Xunit;
+
+namespace Argumentum.AssetConverter.Tests.Ontology
+{
+ ///
+ /// End-to-end OWL proof for #133 — SURFACES A SECOND SILENT-FALSE-PASS BUG in the production
+ /// validation path that the #480→#481→#482 lane missed.
+ ///
+ /// (#482) proved with SYNTHETIC in-memory adapters that the
+ /// production validator no longer silent-false-passes after the OwlAdapter reader fix (#481). But
+ /// that lane only exercised the IN-MEMORY path. This class loads the REAL generation output
+ /// (docs/ontology/argumentum.owl, ~5 MB, produced by OwlDocumentConfig.CreateOwlDocument)
+ /// and proves the readers are STILL BLIND on a reloaded file — so the production validator
+ /// (OwlOntologyValidationTests.LoadOntology → OwlAdapter.FromFile → validate) STILL
+ /// silent-false-passes in the real prod path, for a DIFFERENT root cause than the #480 type-mismatch.
+ ///
+ /// ROOT CAUSE (measured on the loaded file): OWLSharp's OWL2XML serializer DROPS the
+ /// rdf:type and skos:inScheme annotation assertions during serialization — neither
+ /// survives the round-trip (rdf:type == 0, inScheme == 0 after reload). The OwlAdapter readers find
+ /// concepts/schemes by scanning AnnotationAxioms for rdf:type, so on any LOADED file
+ /// they return empty. ValidateMultilingualAnnotations / ValidateAIFMappings then hit
+ /// their if (concepts.Count == 0) return true; guard → report PASS without inspecting anything.
+ /// Meanwhile the real content IS present (2816 prefLabels, 10 AIF matches, 1510 class declarations) —
+ /// the validator just cannot see it.
+ ///
+ /// These tests are GREEN by pinning the CURRENT BROKEN behavior (characterization). When the fix
+ /// lands (readers must locate concepts via surviving annotations — prefLabel/definition/example
+ /// subjects, or filtered DeclarationAxioms — NOT rdf:type/inScheme), these assertions flip to the
+ /// honest "detection works" form. This is a coordinator-scope fix (prod behavior + release-gate
+ /// implications), so the worker surfaces it rather than shipping a unilateral prod change.
+ ///
+ /// Deterministic, key-free, release-independent. Loads one real file (lazy, shared across the class).
+ /// NEW additive file (dispatch #133 primaire) — no existing file modified.
+ ///
+ public class OwlE2EGenerationValidationTests
+ {
+ private const string RelativeOntologyPath = "docs/ontology/argumentum.owl";
+
+ private static readonly Lazy _realOntology = new Lazy(() =>
+ {
+ var path = ResolveRepoFile(RelativeOntologyPath);
+ if (path == null)
+ {
+ throw new FileNotFoundException(
+ $"Committed generated ontology not found at '{RelativeOntologyPath}' walking up from " +
+ $"{AppContext.BaseDirectory}. It is the real OwlDocumentConfig.CreateOwlDocument output " +
+ "and must exist in the repo for the #133 e2e proof.");
+ }
+ return OwlAdapter.FromFile(path);
+ });
+
+ private static OwlAdapter RealOntology => _realOntology.Value;
+
+ private static string ResolveRepoFile(string repoRelativePath)
+ {
+ var relative = repoRelativePath.Replace('/', Path.DirectorySeparatorChar);
+ for (var d = AppContext.BaseDirectory; d != null; d = Path.GetDirectoryName(d))
+ {
+ var candidate = Path.Combine(d, relative);
+ if (File.Exists(candidate))
+ {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
+ /// Counts reloaded AnnotationAssertions whose predicate IRI ends with the given fragment.
+ private static int CountAnnotations(string iriFragment)
+ {
+ return RealOntology.GetOntology().AnnotationAxioms.OfType()
+ .Count(a => a.AnnotationProperty.GetIRI().ToString().EndsWith(iriFragment));
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // (1) ROOT CAUSE: rdf:type and skos:inScheme annotations are DROPPED by OWLSharp's
+ // OWL2XML round-trip. rdf:type==0 on the reloaded ontology (the generator emitted one
+ // rdf:type=skos:Concept per concept + one for the scheme). inScheme==0 likewise. Contrast:
+ // prefLabel (literal-valued) survives — so the loss is predicate-selective, not total.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ [Fact]
+ public void LoadedOntology_RdfTypeAndInScheme_DroppedByOwl2XmlRoundTrip()
+ {
+ int rdfType = CountAnnotations("/rdf-syntax-ns#type");
+ int inScheme = CountAnnotations("skos/core#inScheme");
+ int prefLabel = CountAnnotations("skos/core#prefLabel");
+
+ rdfType.Should().Be(0,
+ "BUG: OWLSharp's OWL2XML serializer drops rdf:type annotation assertions — the generator " +
+ "emitted one rdf:type=skos:Concept per concept (~1400) plus one for the ConceptScheme, but " +
+ "ZERO survive the round-trip. This is the root cause of the loaded-file reader blindness.");
+
+ inScheme.Should().Be(0,
+ "BUG: skos:inScheme (resource-valued, emitted per concept by DeclareConcept) is ALSO dropped " +
+ "by the round-trip — so a fallback fix cannot rely on inScheme either.");
+
+ prefLabel.Should().BeGreaterThan(0,
+ "contrast: prefLabel (literal-valued) DOES survive the round-trip — the serialization loss is " +
+ "predicate-selective, and the real content (concepts' labels) IS present in the reloaded ontology");
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // (2) CONSEQUENCE: the readers the production validators branch on return EMPTY on the
+ // reloaded file. GetResourcesByType(Concept) scans AnnotationAxioms for rdf:type, which is
+ // now absent (test 1). This is the exact precondition for the silent false-pass.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ [Fact]
+ public void LoadedOntology_ConceptAndSchemeReaders_ReturnEmpty_BugPinned()
+ {
+ var concepts = RealOntology.GetResourcesByType(SKOSVocabulary.Concept);
+ var schemes = RealOntology.GetResourcesByType(SKOSVocabulary.ConceptScheme);
+
+ concepts.Should().BeEmpty(
+ "BUG PINNED: on the reloaded real ontology GetResourcesByType(Concept) returns empty because " +
+ "rdf:type is absent (test 1). In-memory (post-#481) this resolves concepts, but on a LOADED " +
+ "file the reader is blind — so the production validators see zero concepts.");
+
+ schemes.Should().BeEmpty(
+ "same root cause for ConceptScheme: rdf:type=skos:ConceptScheme was dropped on round-trip");
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // (3) SMOKING GUN: the reloaded ontology DOES contain real content — 2816 prefLabels,
+ // 10 AIF match mappings, 1510 class declarations. The concepts exist; the readers simply
+ // cannot locate them via the dropped rdf:type. This proves the empty reader is a reader
+ // defect, not a data defect.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ [Fact]
+ public void LoadedOntology_ContainsRealContent_ReadersCannotSee()
+ {
+ int prefLabel = CountAnnotations("skos/core#prefLabel");
+ int matches = CountAnnotations("#exactMatch")
+ + CountAnnotations("#closeMatch")
+ + CountAnnotations("#relatedMatch");
+ int classDecls = RealOntology.GetOntology().DeclarationAxioms.Count(d => d.Entity is OWLClass);
+
+ prefLabel.Should().BeGreaterThan(1000,
+ "the reloaded ontology carries ~2816 prefLabels (one fr+en pair per concept) — real content " +
+ "is present");
+ matches.Should().BeGreaterThan(0,
+ "the reloaded ontology carries AIF match mappings (exactMatch/closeMatch/relatedMatch) — " +
+ "real content is present, yet ValidateAIFMappings will still skip because concepts resolves empty");
+ classDecls.Should().BeGreaterThan(1000,
+ "~1510 OWL class declarations survive (concepts + scheme + AIF classes) — an alternative fix " +
+ "could locate concepts via DeclarationAxioms rather than the dropped rdf:type");
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // (4) DECISIVE PROOF: the PRODUCTION validator (OwlOntologyValidationTests, injected via
+ // reflection) returns TRUE for both annotation and AIF validation on the reloaded ontology —
+ // NOT because the content is valid, but because the concept reader is empty and the methods
+ // early-return true ("No concepts to validate — skipping"). The silent false-pass is STILL
+ // ACTIVE in the production load-and-validate path, contradicting #133's premise.
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ [Fact]
+ public async Task ProdValidator_SilentFalsePass_StillActiveOnLoadedOntology()
+ {
+ var validator = BuildProdValidator(RealOntology);
+ bool annotationsOk = await InvokeValidate(validator, "ValidateMultilingualAnnotations");
+ bool aifOk = await InvokeValidate(validator, "ValidateAIFMappings");
+
+ // These return TRUE — but that is the SILENT FALSE-PASS, not a genuine pass: the concept
+ // reader is empty (tests 1+2), so both validators hit `if (concepts.Count == 0) return true;`
+ // and skip inspection entirely. The ontology demonstrably contains prefLabels + AIF matches
+ // (test 3) that a LIVE validator would inspect. Pinning this FALSE pass until the reader fix.
+ annotationsOk.Should().BeTrue(
+ "ValidateMultilingualAnnotations returns TRUE on the reloaded ontology — the silent false-pass " +
+ "is still alive in production. It returns true because GetResourcesByType(Concept) is empty " +
+ "(test 2) so the method early-returns, NOT because the (present) annotations were inspected");
+ aifOk.Should().BeTrue(
+ "ValidateAIFMappings returns TRUE on the reloaded ontology for the same reason — silent " +
+ "false-pass. #133's 'confidence restored' premise does not hold for the prod load path");
+ }
+
+ private static object BuildProdValidator(OwlAdapter ontology)
+ {
+ // OwlOntologyValidationTests is a PUBLIC class in the PRODUCTION project (namespace
+ // Argumentum.AssetConverter.Tests). Its public constructor takes an AssetConverterConfig
+ // (for OwlValidatorConfig); the ontology is otherwise loaded from disk via LoadOntology —
+ // we bypass that by setting the private _ontology field, mirroring a post-fix prod run.
+ var config = new AssetConverterConfig();
+ var validator = new OwlOntologyValidationTests(config);
+ var field = typeof(OwlOntologyValidationTests).GetField("_ontology",
+ BindingFlags.NonPublic | BindingFlags.Instance);
+ field.Should().NotBeNull("the production validator must carry a private _ontology field");
+ field!.SetValue(validator, ontology);
+ return validator;
+ }
+
+ private static Task InvokeValidate(object validator, string methodName)
+ {
+ var method = typeof(OwlOntologyValidationTests).GetMethod(methodName,
+ BindingFlags.Public | BindingFlags.Instance);
+ method.Should().NotBeNull($"the production validator must expose a {methodName} method");
+ return (Task)method!.Invoke(validator, null)!;
+ }
+ }
+}