fix(ontology): #204 OwlAdapter readers — RDFResource.ToString() + SKOSHelper silent-empty fallback (fixes #480 bug) - #481
Conversation
…SHelper silent-empty fallback Fixes the bug surfaced by PR #480 (OwlAdapter readers returning empty). Root cause was deeper than the .URI type-mismatch: RDFResource.Equals uses runtime-type comparison (GetType()), so even RDFResource.Equals(fresh RDFResource) is false when the stored IRI is a subtype — only GetIRI().Equals works. Switched all 14 reader comparison sites to .ToString() equality (the URI-as-string basis the write-path tests already proved correct, agnostic to RDFResource subtype). Also completes the "incomplete SKOSHelper bypass" flagged on the dashboard: CheckIsNarrowerConcept / GetExactMatch/Close/RelatedMatch had try/catch fallbacks that only triggered on exception, but SKOSHelper returns false/empty SILENTLY — so the (now-correct) fallback scanner was never reached. Changed fallback to trigger on empty/false result too, not just exception. Tests: section-(A) [BUG] characterization suite from #480 flipped to proper round-trip assertions (now the regression suite for this fix). Full suite 218 pass / 0 fail / 5 skip (baseline preserved). Production impact: Tests/OwlOntologyValidationTests.cs (post-gen OWL validator, not xUnit) was silently false-passing "no concepts → skip → PASS" on annotation and AIF checks because GetResourcesByType(Concept) returned empty. Now resolves correctly → validation becomes reliable (unblocks trustworthy #133 OWL publication). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw]
Verdict: sound fix, well-characterized regression suite. One minor observation.
What changed
Two files -- OwlAdapter.cs (production fix) and OwlAdapterRegressionTests.cs (test suite flip + documentation update). 107 additions / 72 deletions.
Root cause analysis -- correct
The PR description accurately traces the failure chain:
- Original bug (#480): reader comparisons used
RDFResource.Equals(string)/RDFResource.Equals(Uri)-- always false byGetType()mismatch. - Naive "drop .URI" fix would still fail:
RDFResource.Equals(object)also uses runtime-type comparison, soa.SubjectIRI.Equals(X)is false when OWLSharp stores subtypes. The PR correctly identifies this deeper issue. - Fix: all 14 reader comparison sites switched to
.ToString()equality (string-IRI basis). This is the right approach -- agnostic to RDFResource subtype, and consistent with the write-path tests that already used.ToString()and passed.
SKOSHelper silent-empty fallback -- correct and important
The try { return SKOSHelper...(); } catch { fallback } pattern was incomplete: SKOSHelper returns false / empty list silently (no exception), so the fallback scanner was never reached. The fix checks for empty/false results before falling back:
if (SKOSHelper.CheckHasNarrowerConcept(_ontology, parentConcept, concept)) return true;
// ... (catch swallows, then annotation scanner runs)This is applied consistently to CheckIsNarrowerConcept, GetExactMatchConcepts, GetCloseMatchConcepts, and GetRelatedMatchConcepts. Correct.
Test changes -- thorough
Section-A [BUG]_* tests are properly flipped from asserting broken behavior (.BeEmpty(), .BeFalse()) to asserting correct round-trip behavior (.NotBeEmpty(), .HaveCount(3), .BeTrue()). Negative cases are added where appropriate (e.g., CheckIsNarrowerConcept(Res("Unrelated"), parent).Should().BeFalse(), CheckHasClass(Res("UndeclaredClass")).Should().BeFalse()).
Section-C write-path tests are unchanged -- good, they serve as a control proving the fix didn't regress graph construction.
Security
No secrets, credentials, or sensitive data in the diff.
Minor observation
The .ToString() comparisons use == (string equality) rather than .Equals() -- functionally identical for strings, and arguably cleaner in a LINQ .Where() chain. No action needed, just noting it's intentional and fine.
Production impact -- as described
The silent false-pass in OwlOntologyValidationTests.cs (ValidateMultilingualAnnotations / ValidateAIFMappings early-returning "no concepts -> skip -> PASS") is a real correctness gap. This fix makes OWL validation reliable, which matters for the v0.9.0 validation gate.
…ad (dispatch 4rkh1s secondaire) (#482) * fix(ontology): #204 OwlAdapter readers — RDFResource.ToString() + SKOSHelper silent-empty fallback Fixes the bug surfaced by PR #480 (OwlAdapter readers returning empty). Root cause was deeper than the .URI type-mismatch: RDFResource.Equals uses runtime-type comparison (GetType()), so even RDFResource.Equals(fresh RDFResource) is false when the stored IRI is a subtype — only GetIRI().Equals works. Switched all 14 reader comparison sites to .ToString() equality (the URI-as-string basis the write-path tests already proved correct, agnostic to RDFResource subtype). Also completes the "incomplete SKOSHelper bypass" flagged on the dashboard: CheckIsNarrowerConcept / GetExactMatch/Close/RelatedMatch had try/catch fallbacks that only triggered on exception, but SKOSHelper returns false/empty SILENTLY — so the (now-correct) fallback scanner was never reached. Changed fallback to trigger on empty/false result too, not just exception. Tests: section-(A) [BUG] characterization suite from #480 flipped to proper round-trip assertions (now the regression suite for this fix). Full suite 218 pass / 0 fail / 5 skip (baseline preserved). Production impact: Tests/OwlOntologyValidationTests.cs (post-gen OWL validator, not xUnit) was silently false-passing "no concepts → skip → PASS" on annotation and AIF checks because GetResourcesByType(Concept) returned empty. Now resolves correctly → validation becomes reliable (unblocks trustworthy #133 OWL publication). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(ontology): #204 OwlValidator live-path — silent false-pass is dead (dispatch 4rkh1s secondaire) NEW additive file: OwlValidatorLivePathTests.cs (+5 tests). Dispatch `4rkh1s` secondaire — prove the production OWL validation path is LIVE after the #481 reader fix. The production validator `Tests/OwlOntologyValidationTests.cs` (runtime validator invoked by `OwlValidatorConfig.Apply`, NOT an xUnit suite) had a silent false-pass: ValidateMultilingualAnnotations and ValidateAIFMappings early-return `true` ("No concepts to validate — skipping") when GetResourcesByType(Concept) was empty. Before #481 that reader ALWAYS returned empty, so the validators reported PASS regardless of whether annotations/AIF mappings existed. These tests drive the REAL production validator via reflection (its `_ontology` field is private; the validation methods are public). They prove: - (1) annotated concepts → validator inspects them and reports genuine PASS (not the skip-path); - (2) UNannotated concepts → validator now FAILS (the annotation check actually ran and found the missing labels/definitions) — the dead silent false-pass is gone; - (3) the GetResourcesByType(Concept) predicate the validators branch on resolves non-empty. Tests: 223 pass / 0 fail / 5 skip (218 + 5). Deterministic, key-free, release-independent. No existing file modified. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…ch 4rkh1s tertiaire) (#483) * fix(ontology): #204 OwlAdapter readers — RDFResource.ToString() + SKOSHelper silent-empty fallback Fixes the bug surfaced by PR #480 (OwlAdapter readers returning empty). Root cause was deeper than the .URI type-mismatch: RDFResource.Equals uses runtime-type comparison (GetType()), so even RDFResource.Equals(fresh RDFResource) is false when the stored IRI is a subtype — only GetIRI().Equals works. Switched all 14 reader comparison sites to .ToString() equality (the URI-as-string basis the write-path tests already proved correct, agnostic to RDFResource subtype). Also completes the "incomplete SKOSHelper bypass" flagged on the dashboard: CheckIsNarrowerConcept / GetExactMatch/Close/RelatedMatch had try/catch fallbacks that only triggered on exception, but SKOSHelper returns false/empty SILENTLY — so the (now-correct) fallback scanner was never reached. Changed fallback to trigger on empty/false result too, not just exception. Tests: section-(A) [BUG] characterization suite from #480 flipped to proper round-trip assertions (now the regression suite for this fix). Full suite 218 pass / 0 fail / 5 skip (baseline preserved). Production impact: Tests/OwlOntologyValidationTests.cs (post-gen OWL validator, not xUnit) was silently false-passing "no concepts → skip → PASS" on annotation and AIF checks because GetResourcesByType(Concept) returned empty. Now resolves correctly → validation becomes reliable (unblocks trustworthy #133 OWL publication). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(ontology): #204 OwlValidator live-path — silent false-pass is dead (dispatch 4rkh1s secondaire) NEW additive file: OwlValidatorLivePathTests.cs (+5 tests). Dispatch `4rkh1s` secondaire — prove the production OWL validation path is LIVE after the #481 reader fix. The production validator `Tests/OwlOntologyValidationTests.cs` (runtime validator invoked by `OwlValidatorConfig.Apply`, NOT an xUnit suite) had a silent false-pass: ValidateMultilingualAnnotations and ValidateAIFMappings early-return `true` ("No concepts to validate — skipping") when GetResourcesByType(Concept) was empty. Before #481 that reader ALWAYS returned empty, so the validators reported PASS regardless of whether annotations/AIF mappings existed. These tests drive the REAL production validator via reflection (its `_ontology` field is private; the validation methods are public). They prove: - (1) annotated concepts → validator inspects them and reports genuine PASS (not the skip-path); - (2) UNannotated concepts → validator now FAILS (the annotation check actually ran and found the missing labels/definitions) — the dead silent false-pass is gone; - (3) the GetResourcesByType(Concept) predicate the validators branch on resolves non-empty. Tests: 223 pass / 0 fail / 5 skip (218 + 5). Deterministic, key-free, release-independent. No existing file modified. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(utility): #204 UtilityExtensions layout math + path gate (dispatch 4rkh1s tertiaire) NEW additive file: UtilityExtensionsLayoutTests.cs (+24 tests). Dispatch `4rkh1s` tertiaire — cover a documented-fragile, deterministic, key-free area with ZERO existing coverage (grep confirms no test references ToJaggedArray/UtilityExtensions/PathIsUrl). (1) ToJaggedArray<T> — grid layout math. Pins rowLength = ceil(count/columns), the trailing short row (remainder only, no padding — a padding regression would inject phantom blank cards into final grid rows), row-major fill order (a transpose regression scrambles positions), and the ToJaggedArray→Flatten inverse round-trip. This is the same class of geometry the coordinator flagged ("count the geometry, not just the text"). (2) PathIsUrl — the asset-path gate. Pins that the relative paths CLAUDE.md documents as the root cause of "white/empty cards" (../../Cards/..., ../, bare, Windows C:\, Unix /var/) are NOT URLs (so the caller knows to rewrite them to absolute GitHub URLs), that http/https ARE URLs, that null/empty/whitespace return false, and that whitespace-padded URLs still resolve. Tests: 246 pass / 0 fail / 5 skip (223 + 24). Deterministic, key-free, release-independent. No existing file modified. AssetConverterConfig.cs / DatasetUpdaterRootConfig.cs / FallaciesLocalizationTests.cs untouched (reserved #444 β/γ po-2023). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…α resubmit) (#484) Re-submit the α deliverable from #444 (closed stale) as a clean doc-only PR. The original #444 bundled α (this assessment) + β (Memo Back loc fix = #446) + γ (gpt-5.5 PT task = #447). β and γ are already on master; γ additionally carried a parasite gpt-5.5→5.4-mini downgrade on 12 sites (avoided). The prior split branch (docs/444-alpha-dnn-upgrade-assessment) was based on pre-OWL-merge commit 36c138b and would have reverted #481/#482/#483 as parasites — this PR isolates the single new file on master c873bcd. Related: #131 (DNN security/upgrade), #132 (DNN deployment), #134 (release v0.9.0). Supersedes: #444 (closed stale; β=#446, γ=#447 already merged). Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…lent-false-pass bug (#486) Loads the committed generated ontology (docs/ontology/argumentum.owl, the real OwlDocumentConfig.CreateOwlDocument output) and runs the production validation path against it. This completes the #133 e2e proof that #482 (synthetic in-memory adapters) started — and surfaces a SECOND silent-false-pass bug the #480→#481→#482 lane missed because it only exercised the in-memory path. ROOT CAUSE (measured on the reloaded 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. OwlOntologyValidationTests.ValidateMultilingualAnnotations and .ValidateAIFMappings then hit their `if (concepts.Count == 0) return true;` guard and report PASS without inspecting anything. The real content IS present (2816 prefLabels, 10 AIF matches, 1510 class declarations) — the validator simply cannot see it. So #133's "confidence restored" premise does NOT hold for the production load-and-validate path: the silent false-pass is still alive there, for a different root cause than the #480 RDFResource type-mismatch. 4 characterization tests (all green, pinning current broken behavior): 1. rdf:type==0 and inScheme==0 after round-trip (prefLabel survives as contrast). 2. GetResourcesByType(Concept/ConceptScheme) returns empty on the loaded file. 3. The reloaded ontology DOES contain real content (prefLabel>1000, matches>0, classDecls>1000) — reader defect, not data defect. 4. Production validator returns TRUE for annotation + AIF checks on the loaded ontology — the silent false-pass, decisively pinned. When the fix lands (readers 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. The fix is a prod behavior change with release-gate implications → coordinator scope; this PR surfaces it rather than shipping a unilateral prod change. Deterministic, key-free, release-independent. NEW additive file — nothing modified. Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude <noreply@anthropic.com>
…nt-false-pass dead (#489) The #481 reader fix only covered the IN-MEMORY path. On a LOADED ontology (rdf:type + skos:inScheme dropped from the reloaded AnnotationAxioms by OWLSharp's OWL2XML round-trip), GetResourcesByType(Concept)/GetConcepts returned empty -> ValidateMultilingualAnnotations/ValidateAIFMappings hit their `concepts.Count == 0 -> return true` guard -> PASS without inspecting (the 2nd silent-false-pass, characterized by #486). Verified reloaded breakdown (probed on the real generated ontology): prefLabel=2816, definition=2816, example=2816, narrower/broader=1407, broadMatch=57, closeMatch=10, narrowMatch=3, hasTopConcept=1; rdf:type=0, inScheme=0 among AnnotationAxioms. Fix is READ-PATH ONLY (serializer untouched, per dispatch scope). When the rdf:type scan is empty, locate entities via the surviving SKOS annotations: - skos:Concept -> distinct subjects of skos:prefLabel (~1305 resolved) - skos:ConceptScheme -> subject of skos:hasTopConcept In-memory path preserved (rdf:type present -> early-return, no fallback), so the #482 in-memory live-path proofs still hold. Concepts deduped by URI string (not RDFResource.Equals) to avoid the equality bug class of #480. OwlE2EGenerationValidationTests (#486) flipped from bug-characterization to a genuine-pass regression suite: (1) rdf:type/inScheme drop still real (now benign), prefLabel survives (2) readers now resolve concepts (>1000) + scheme (NotBeEmpty) (3) real content now resolvable (>1000 distinct concept subjects) (4) prod validator genuinely inspects + passes (skip guard unreachable) Tests: Ontology namespace 25/25 green; full suite 259 passed / 0 failed / 5 skipped (no regression). Dispatch msg-...irkf5i primaire, base 4ac52e2. Read-path only: no write/serialize, no CSV/template/config touched. Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
/#133 (#495) Unit-isolation of the OWL2XML round-trip SURVIVOR fallback in OwlAdapter. The #489 read-path fix (e2e-pinned on the real 5MB ontology by OwlE2EGenerationValidationTests) made the production validators stop silent-false-passing when OWLSharp drops rdf:type on reload. But the survivor fallback branch (OwlAdapter.cs ~424-441) was unit-uncovered: OwlAdapterRegressionTests (#480/#481) builds ontologies WITH rdf:type (DeclareConcept), so GetResourcesByType always early-returns on the type scan and NEVER reaches the fallback. 7 additive tests on synthetic survivor-only ontologies (no DeclareConcept -> no rdf:type; only prefLabel/hasTopConcept that survive the round-trip), pinning what the e2e cannot: - Concept dedup: 3 concepts x fr+en prefLabel (6 assertions) -> 3 distinct subjects (the GetAnnotationSubjectsByProperty .Distinct() contract) - ConceptScheme via hasTopConcept SUBJECT (exact pin, not coarse NotBeEmpty) - Unknown type -> empty (fallback does not over-resolve) - rdf:type present -> type-scan governs, fallback SKIPPED. Decisive proof the fix did not change in-memory semantics; the e2e cannot test this because the real file has zero rdf:type - GetConcepts() survivor tail (distinct path from GetResourcesByType) - AIF exactMatch + closeMatch annotation-scan fallback Verified: 7/7 new tests green; full suite 282 passed / 0 failed / 5 skipped. Test-only, 0 prod change, 0 gate risk. Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(pdf): #632 Ghostscript CMYK+OutputIntent post-process module (code half of split) Adds a standalone post-process stage that converts the RGB-300-lossless (FlateDecode) PDFs from the QuestPDF stage into DeviceCMYK + OutputIntent (CGATS TR 001 / SWOP) print-ready PDFs via Ghostscript. The GS mechanism was POC-validated end-to-end by ai-01 (176/176 DeviceCMYK, GTS_PDFX OutputIntent, PPI preserved, ~23s/PDF). ai-01 runs + verdicts on the bundle v3. Additive — zero behavioral change to the default pipeline: - New ConverterMode flag PdfCmykPostProcess (1<<15 = 32768) - PdfCmykPostProcessConfig: OFF in Debug, ON in Release (EnabledDebug= false / EnabledRelease=true + GetEnabled via UseReleaseParams) - PdfCmykPostProcessor: discovers *.pdf under Target/, extracts ICC from ImageMagick.ColorProfiles.USWebCoatedSWOP (same profile as per-image ConvertToCmyk -> color-consistent, zero new licensing), generates PDFX_def.ps, invokes the ai-01-validated GS command, atomic replace. Skip-with-warning if GS absent (no crash). - Dispatch after Task.WhenAll -> runs as a post-pass on already-written PDFs. Can run standalone (Mode=PdfCmykPostProcess) on an existing bundle without re-harvest. - 7 tests (no GS required): gating, GS arg contract, PDFX_def.ps gen, ICC extraction, graceful-skip-when-absent. 7/7 pass. - CLAUDE.md: resolves the "CMYK conversion: Enabled" oxymore (per-image ConvertToCmyk is a no-op for the PNG path; GS post-process is the authoritative CMYK path). Scope honesty: CMYK + OutputIntent, NOT formal PDF/X-3 (no trim/bleed). Legacy ConvertToCmyk left in place (removing it = behavioral color shift, needs visual verdict on GS bundle first; documented superseded). Verification: build 0 errors; module tests 7/7; full suite 555/1/5 — the 1 failure (OwlE2EGenerationValidationTests round-trip) proven pre-existing on pristine master a86587c (OWL bug #481/#489 tracked #133, ai-01 lane). Relates #632 #133 #134. Co-Authored-By: Claude-Code <noreply@anthropic.com> * fix(pdf): #641 single-brace pdfmark object names in PDFX_def.ps In BuildPdfxDefPostscript only the first concatenated segment is interpolated ($"..."); the plain "..." segments kept {{icc_PDFX}} as a literal double brace, which Ghostscript parses as a nested procedure and aborts with a typecheck error on /_objdef (empirically reproduced with GS 10.07.1: exit 1, 1177-byte stub, zero OutputIntent). Fix per ai-01 review: single braces in non-interpolated segments + regression test pinning the single-brace form (NotContain "{{"). Applied by ai-01 (coup de collier, tag critical path) - module authored by po-2023. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude-Code <noreply@anthropic.com>
Fix: OwlAdapter readers returned empty (RDFResource type-mismatch + incomplete SKOSHelper bypass)
Fixes the real bug surfaced and characterized by PR #480 (the section-A
[BUG]_*tests).Root cause (deeper than #480's .URI probe)
PR #480 diagnosed
RDFResource.Equals(string)type-mismatch. The actual root cause is deeper:RDFResource.Equals(object)uses runtime-type comparison (GetType()equality), sordfResource.Equals(new RDFResource(sameUri))is also false when the stored IRI is a subtype of RDFResource (which is how OWLSharp stores them). OnlyGetIRI().Equals(...)(returns a bare RDFResource) worked. That's why 7 of 9 reader tests still failed after the naive "drop .URI" fix —a.SubjectIRI.Equals(X)/a.ValueIRI.Equals(X)are subtype-mismatched.Fix: switched all 14 reader comparison sites to
.ToString()equality (the URI-as-string basis). This is the same basis the section-C write-path tests already proved correct (they used.ToString()all along, which is why they passed). It's agnostic to RDFResource subtype and durable.Completes the "incomplete SKOSHelper bypass" (flagged on dashboard)
CheckIsNarrowerConcept/GetExactMatchConcepts/GetCloseMatchConcepts/GetRelatedMatchConceptshadtry { return SKOSHelper...(); } catch { fallback }patterns. ButSKOSHelperreturnsfalse/ empty list silently (no exception) — so thetryblock returned directly and the (now-correct) fallback scanner was never reached. Changed the fallback to trigger on empty/false result too, not just on exception. This is the "bypass the broken SKOSHelper" design the dashboard described — now it actually works.Tests
OwlAdapterRegressionTests.cswas the[BUG]characterization suite from test(ontology): #204 OwlAdapter regression — 🔴 BUG FOUND: all readers return empty (RDFResource.Equals(Uri) type-mismatch) #480 (asserted empty/false). Flipped to proper round-trip assertions — now the regression suite guarding this fix. All 9 assert the readers correctly retrieve what the write path declared.Production impact
Tests/OwlOntologyValidationTests.cs(the post-generation OWL validator, not an xUnit suite) called these readers in bulk:ValidateMultilingualAnnotations/ValidateAIFMappingsearly-returned "no concepts → skip → PASS" becauseGetResourcesByType(Concept)was empty — silent false-pass. After this fix the validator resolves concepts/annotations/AIF mappings correctly → OWL validation becomes reliable. This unblocks trustworthy#133(OWL publication) and closes a real gap in the release-v0.9.0 validation gate.Constraints honored
OwlAdapter.csis a targeted mechanical fix (read-path only).AssetConverterConfig.cs/DatasetUpdaterRootConfig.cstouched.mgskfx), ai-01's dashboard reco was "open a dedicated fix PR".🤖 Worker po-2024 — fix for the #480 bug.