Summary
After a successful compile with doc != nil and validation enabled (the default), engine.Run executes diags = doc.Diagnostics — replacing the compiler's returned slice instead of merging. The compilers.Compiler contract only requires spec problems to be "returned as ir.Diagnostic values"; it never requires mirroring them into Document.Diagnostics. The one existing compiler happens to mirror (compilers/openapi/openapi.go:43-44), which masks the bug.
Any future conforming compiler that returns diagnostics without also storing them in the document has all of them — including error severity — vanish from Result.Diagnostics: the CLI prints nothing and exits 0 on a broken spec. Perversely, --skip-validate preserves the same diagnostics, so turning validation on loses findings.
Reproduction
A stub compiler registered via engine.NewWithRegistry returning one SeverityError diagnostic and an empty doc.Diagnostics: engine.Run → Result.Diagnostics is empty. With SkipValidate: true the diagnostic survives. No existing test covers the "doc non-nil, returned diags ⊄ doc.Diagnostics" case.
Root cause
engine/engine.go:85-91 — the post-validate re-alias assumes returned-slice ≡ doc.Diagnostics, an invariant the contract never states.
Expected
Two acceptable fixes; pick one and write it into the compilers.Compiler contract doc:
- merge (dedupe) returned diagnostics with
doc.Diagnostics in the engine, or
- make
Document.Diagnostics the single channel — drop the returned slice except for nil-doc refusals — so the invariant is structural instead of conventional.
Either way, add the missing engine test with a non-mirroring stub compiler.
Summary
After a successful compile with
doc != niland validation enabled (the default),engine.Runexecutesdiags = doc.Diagnostics— replacing the compiler's returned slice instead of merging. Thecompilers.Compilercontract only requires spec problems to be "returned as ir.Diagnostic values"; it never requires mirroring them intoDocument.Diagnostics. The one existing compiler happens to mirror (compilers/openapi/openapi.go:43-44), which masks the bug.Any future conforming compiler that returns diagnostics without also storing them in the document has all of them — including error severity — vanish from
Result.Diagnostics: the CLI prints nothing and exits 0 on a broken spec. Perversely,--skip-validatepreserves the same diagnostics, so turning validation on loses findings.Reproduction
A stub compiler registered via
engine.NewWithRegistryreturning oneSeverityErrordiagnostic and an emptydoc.Diagnostics:engine.Run→Result.Diagnosticsis empty. WithSkipValidate: truethe diagnostic survives. No existing test covers the "doc non-nil, returned diags ⊄ doc.Diagnostics" case.Root cause
engine/engine.go:85-91— the post-validate re-alias assumes returned-slice ≡doc.Diagnostics, an invariant the contract never states.Expected
Two acceptable fixes; pick one and write it into the
compilers.Compilercontract doc:doc.Diagnosticsin the engine, orDocument.Diagnosticsthe single channel — drop the returned slice except for nil-doc refusals — so the invariant is structural instead of conventional.Either way, add the missing engine test with a non-mirroring stub compiler.