Keep the hand-rolled Psych parser; pin the yaml.v3-incompatible invariants - #28
Open
tannevaled wants to merge 1 commit into
Open
tannevaled wants to merge 1 commit into
tannevaled wants to merge 1 commit into
Conversation
… invariants A control run (gopkg.in/yaml.v3 v3.0.1) fed the reference parser's Node tree into this package's own scalar resolution and !ruby/* tag mapping, then replayed the emitter corpus and the full yaml/yaml-test-suite (402 tests). The verdict, matching the sibling go-ruby-hcl2 finding, is KEEP: the reference is spec-compliant YAML 1.2, Psych is bug-for-bug YAML 1.1, and the two cannot be reconciled without breaking the load-bearing behaviour. - Emitter: yaml.Marshal(2.0) == yaml.Marshal(2) == "2" — yaml.v3 cannot keep a Ruby Float distinct from an Integer, which rbgo's `require "yaml"` state/run-summary persistence depends on. - Value model: yaml.v3 decodes `:name` to the String ":name" (no Symbol) and has no !ruby/object / !ruby/range reconstruction. - Parser: swapping the block parser for yaml.v3 changed 115 accept/reject verdicts (61 regressions vs the required Psych verdict, 54 fixes) — a net conformance loss that also breaks the shrink-only ratchet, in both directions (yaml.v3 rejects a %YAML 1.2 directive and a nil-keyed mapping Psych accepts; accepts a stray flow close and post-flow junk Psych rejects). The reference round-trips 100% of this emitter's output, but is not a drop-in replacement for the Psych-faithful parser or emitter. No refactor and no new dependency: only a self-contained guard test (TestPsychModelInvariants) that pins the incompatible invariants, and a README "Relationship to gopkg.in/yaml.v3" section. Production code, the emitter, and the 335/402 conformance are unchanged; coverage stays 100%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Verdict: KEEP (control-run proven)
This branch set out to base the Ruby object model + byte-exact MRI-Psych emit on
the reference
gopkg.in/yaml.v3parser (Node tree), shedding the hand-rolledparser — the same idea as go-ruby-hcl2. As instructed, it was gated control-first.
The control run says do not swap, and this PR ships the KEEP outcome instead of
a refactor.
What was measured (yaml.v3 v3.0.1)
A prototype fed the reference parser's
Nodetree into this package's own scalarresolution and
!ruby/*tag mapping (never yaml.v3's Go-typedDecode, whichcollapses Float/Integer and uses YAML-1.2 core-schema resolution), then replayed:
Dumpsupports, parsed back through bothloaders. Result: 100% parse-equivalent (30/30). yaml.v3 correctly reads
everything this emitter writes.
yaml/yaml-test-suite(402 accept/reject tests). Result: 115verdict changes — 61 regressions against the required Psych verdict vs 54 fixes.
Net conformance loss (335 → 328) that also breaks the shrink-only ratchet.
Why it cannot move (bidirectional, first-hand)
Float 2.0vsInteger 2emit2.0/2Marshal(2.0)==Marshal(2)=="2"(collapses):namescalarSymbol("name")String(":name")(no Symbol)!ruby/object:/!ruby/range*Object/*Range%YAML 1.2directive (27NA)"text"[ a, b, c ] ](4H7K)The 61 regressions split 50 accept-axis (Psych YAML-1.1 leniencies yaml.v3 rejects)
accept-regressions are entangled in the same "trust yaml.v3's verdict" rule, so no
fallback combinator (AND / OR / fallback-on-reject) is ratchet-safe. The reference
is spec-compliant YAML 1.2; this package must be bug-for-bug Psych. This mirrors the
sibling KEEP verdict for go-ruby-hcl2 (the Ruby value model does not fit
go-cty).What this PR contains
dump.go,byte-identical to
main), and the 335/402 conformance are unchanged.TestPsychModelInvariants(psychmodel_invariants_test.go) — a self-containedguard test pinning the five incompatible invariants above, so a future "wrap
yaml.v3" attempt fails loudly. Verified to gate (fails when an invariant is
violated).
Proof
dump.go/value.go/yaml.godiff vsmainis empty;all
TestDump*/TestOracle*golden tests green againstruby 4.0.5.go test -race: 100.0% coverage;gofmt/go vetclean.Not auto-merged (rbgo convention).