Hand-written. ../harvested/serde.md is what the documents say; this file is what the
implementation found, and it wins. Each entry names the harvested entry it answers by that entry's
stable key.
- A decoded JSON
Stringcan be UTF-8-tagged and invalid, and neither layer that produces it raises. Besideserde/b5e5efc8, which puts the strictness burden on the witness: a witness assertingStringcannot catch this, because an invalid-UTF-8Stringis aString. Measured on Ruby 3.2.11, 3.3.12, 3.4.10 and 4.0.6 against json 2.19.9 (the gemspec floor) and 3.0.2 (the bundle's):JSON.parse(%Q({"a":"\xff"}).b)["a"]returns aStringwhose#encodingis UTF-8, whose#bytesis[255]and whose#valid_encoding?isfalse. Phase 3a's#read_utf8and#read_string(encoding)retag and apply no replacement policy, by their own stated contract, so nothing between the wire and the witness validates. Phase 7a'sDexpace::Serde::JSON::Codec#loadtherefore calls#valid_encoding?on the drained text and raisesDexpace::Serde::DeserializationErrorbefore parsing (7a P7-6). A transcode is the wrong repair:"\xc3\xa9".b.encode(::Encoding::UTF_8, ::Encoding::BINARY)raisesEncoding::UndefinedConversionErrorfor a valid two-byteé, because BINARY has no character semantics to convert from — so the retag-then-transcode recipe ofdocs/knowledge/notes/io-and-byte-streams.md(its key io-and-byte-streams/6eb5155f, a note and not a harvested rule) is retag, then validate, and the transcode step applies only when a declared charset is not UTF-8. review ·docs/work/mvp/phase7/phase7a/2026-09-10-phase7a-serialization-design.md· high · sha:manual-phase7a-utf8-validation JSON::Coder's constructor and its tolerance of an unknown option both changed between json 2.19.9 and 3.0, so an adapter that forwards caller options verbatim is configured differently on the two, silently on one and loudly on the other. Adds toserde/d3bef411andserde/b36d4403, which describe the private-engine and fresh-factory contract theCodermakes satisfiable literally (7a P7-4), and toserde/d15ade64, the reason the floor is a gemspec line: measured on 2026-09-20 on 3.4.10 with json 2.19.9 and on 3.2.11 and 4.0.6 with json 3.0.2,JSON::Coder.instance_method(:initialize).parametersis[[:opt, :options], [:block, :as_json]]on 2.19.9 and[[:key, :object_class], [:key, :array_class], [:key, :on_load], [:keyrest, :options], [:block, :as_json]]on 3.0.2;Coder.new(max_nestng: 4).dump([1])returns"[1]"on 2.19.9 and raisesArgumentError: unknown keyword: max_nestngon 3.0.2;Coder.new(encoders: {})is accepted on 2.19.9 and refused on 3.0.2;Coder.new(nil)works on 2.19.9 and raises on 3.0.2; a duplicate key is accepted last-wins on 2.9.1, a warning on 2.19.9 and aParserErroron 3.0.2, whileCoder.new(allow_duplicate_key: false)raisesParserErroron both floored versions; andCoder.new.dump(Object.new)and.dump(Time.at(0))raiseGeneratorErroron both withoutstrict: true— theCoderis strict on its own account, andstrict: trueis documentation of intent rather than a switch. Phase 7a's codec therefore validates options against its own allowlist, constructs theCoderwith keywords only, fixesallow_duplicate_key: falseunless the caller opts in, and never forwardsencoders:. Design open question 4's premise ("Coder.newaccepts unknown options silently") is true at the floor and false at 3.0. review ·docs/work/mvp/phase7/phase7a/2026-09-10-phase7a-serialization-checklist.md· high · sha:manual-phase7a-coder-option-drift