Skip to content

fix(ontology): #133 OwlAdapter read-path survivor fallback — 2nd silent-false-pass dead - #489

Merged
jsboige merged 1 commit into
masterfrom
fix/133-owl-readpath-survivor
Jun 16, 2026
Merged

fix(ontology): #133 OwlAdapter read-path survivor fallback — 2nd silent-false-pass dead#489
jsboige merged 1 commit into
masterfrom
fix/133-owl-readpath-survivor

Conversation

@jsboige

@jsboige jsboige commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

What

Fixes the second silent-false-pass in the production OWL validation path — the one #486 characterized and the #480#481#482 lane missed because it only exercised the in-memory path.

Root cause (verified by probing the reloaded ontology): OWLSharp's OWL2XML round-trip drops rdf:type and skos:inScheme from the reloaded AnnotationAxioms. On a LOADED ontology, GetResourcesByType(Concept) / GetConcepts() scanned for rdf:typeemptyValidateMultilingualAnnotations / ValidateAIFMappings hit their if (concepts.Count == 0) return true; guard → PASS without inspecting, even though the real content was present.

Reloaded AnnotationAxioms breakdown (facts, not assumptions):

predicate count predicate count
skos:prefLabel 2816 skos:broadMatch 57
skos:definition 2816 skos:closeMatch 10
skos:example 2816 skos:narrowMatch 3
skos:narrower / broader 1407 skos:hasTopConcept 1
rdf:type 0 skos:inScheme 0

Fix — READ-PATH ONLY (serializer untouched, per dispatch scope)

When the rdf:type scan is empty, locate entities via the SKOS annotations that DO survive the round-trip:

  • skos:Concept → distinct subjects of skos:prefLabel (~1305 concepts resolved)
  • skos:ConceptScheme → subject of skos:hasTopConcept (the scheme)

In-memory path is preserved: rdf:type is present in memory → early-return before the fallback → the #482 in-memory live-path proofs still hold unchanged. Concept subjects are deduped by URI string (not RDFResource.Equals) to sidestep the equality bug class of #480.

Regression suite — #486 flipped red → genuine-green

# Test Before (bug) After (fix)
1 …RdfTypeAndInScheme_DroppedByOwl2XmlRoundTrip drop real drop real (now benign), prefLabel survives
2 …ConceptAndSchemeReaders_ResolveViaSurvivingAnnotations… BeEmpty() NotBeEmpty() (concepts >1000, scheme resolved)
3 …ContainsRealContent_NowResolvableByReaders readers blind readers resolve >1000 concepts
4 …ProdValidator_InspectsConceptsAndGenuinelyPasses… false-pass (skip→true) genuine pass (skip guard unreachable)

Verification

Dispatch

msg-…irkf5i primaire (base 4ac52e24). Verdict/merge = ai-01 — I signal, I don't declare PASS.

🤖 Worker po-2024

…nt-false-pass dead

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: Claude Opus 4.6 <noreply@anthropic.com>

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NanoClaw]

Clean fix for the silent-false-pass bug characterized in #486. The approach is well-chosen: read-path only, serializer deliberately untouched (out of scope, OWLSharp concern).

OwlAdapter.cs (prod fix):

  • GetResourcesByType gains a survivor fallback: rdf:type scan → empty → locate via surviving SKOS annotations (prefLabel for Concept, hasTopConcept for ConceptScheme). In-memory ontologies (rdf:type present) take the early-return and are unaffected — the #482 in-memory proofs still hold.
  • GetConcepts similarly falls back to prefLabel subjects.
  • New helper GetAnnotationSubjectsByProperty deduplicates by URI string (not RDFResource.Equals) — correct, since each concept carries fr+en = two assertions but one subject.
  • The fallback is gated: only activates when the type scan returns 0, so normal in-memory paths are untouched. No perf concern.

OwlE2EGenerationValidationTests.cs (regression suite):

  • Updated from characterization (pinning broken behavior → GREEN) to regression (asserting the fix → GREEN).
  • 4 tests: (1) serializer drop still real, (2) readers now resolve on loaded file, (3) real content resolvable, (4) prod validator genuinely inspects (not skip-false-pass).
  • Test 4 now asserts the concepts list is non-empty BEFORE calling the validator — proving the skip guard is unreachable. This is the right precondition check.

No security concerns. LGTM.

@jsboige
jsboige merged commit 0b37637 into master Jun 16, 2026
3 checks passed
@jsboige
jsboige deleted the fix/133-owl-readpath-survivor branch June 16, 2026 05:08
jsboige added a commit that referenced this pull request Jun 16, 2026
/#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>
jsboige added a commit that referenced this pull request Jul 2, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants