P0: round-trip parity for Python + JS (Stage 7) - #10
Merged
Conversation
Mirrors the P0 round-trip behaviors from feat/p0-roundtrip-core into the Python and JS codecs and adds golden round-trip tests, so Parse(Emit(v))==v holds cross-language. Architecture note: Python exposes only the loose codec (parse == parse_loose, emit == canonicalize_loose) and was ALREADY hardened for all six behaviors — conservative quoting, bytes round-trip (b64"..."), \u control-char decode, numeric error checks, dup-key last-wins, and NaN/Inf reject. So Python needs no source change here; the deliverable is the golden round-trip table proving it (py/tests/test_roundtrip_golden.py, 12 tests). JS: the typed GLYPH-T parser (parse.ts) had three real round-trip gaps vs the emitter; fixed: - Bytes round-trip: parseValueInner + parseScalarValue now decode b64"..." into a bytes value (invalid base64 is a hard error). The emitter already wrote b64"...", so this closes a Py/JS divergence (Python's loose lexer already did). - \u decode: parseQuotedString + parseQuotedScalar now decode \uXXXX, matching what the emitter writes for sub-0x20 control chars. - Duplicate-key last-wins: both map parsers (PackedParser.parseMap and the scalar parseMapScalar) now replace a repeated key in place, matching Go/Python (GValue.get is first-wins, so dedup must happen at build time). Added js/src/roundtrip_golden.test.ts (9 tests) covering the typed packed round-trip with bytes + control chars, the loose bytes scalar inverse, \u decode, dup-key last-wins, and the JSON-bridge NaN/Inf reject. Rebuilt the tracked js/dist (only parse.js changed). NaN/Inf policy (maintainer decision): rejected on emit in the JSON/loose bridge (toJsonLoose/fromJsonLoose in JS, to_json_loose/from_json_loose in Python) — both already enforced; pinned by tests. Scope note: typed `time` round-trip is a Go-only Parse/Emit-path feature. Python loose collapses time to text on emit and does not reconstruct a typed time on parse (consistent with the JSON-like loose collapse); it is excluded from the Python golden table and noted there. Not one of the six P0 behaviors. Verification: cd py && python3 -m pytest -q -> 433 passed; cd js && npm test -> 514 passed; Go TestCrossImpl_* parity green against the rebuilt dist; and tests/all_impl_parity_test.py -> 21/21 full Go/Python/JS parity. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
phenomenon0
force-pushed
the
feat/p0-parity-py-js
branch
from
June 20, 2026 01:54
97cf543 to
58ff0ad
Compare
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.
Stage 7 — cross-language P0 round-trip parity
Branches off
main. Mirrors the P0 round-trip behaviors from #4 (feat/p0-roundtrip-core) into the Python and JS codecs, with golden round-trip tests, soParse(Emit(v))==vholds cross-language.Architecture note
Python exposes only the loose codec (
parse==parse_loose,emit==canonicalize_loose) and was already hardened for all six behaviors — conservative quoting, bytes round-trip (b64"..."),\udecode, numeric error checks, dup-key last-wins, NaN/Inf reject. So Python needs no source change; the deliverable is the golden table that proves it (py/tests/test_roundtrip_golden.py, 12 tests).JS source fixes (
js/src/parse.ts)The typed GLYPH-T parser had three real round-trip gaps vs the emitter:
parseValueInner+parseScalarValuenow decodeb64"..."into a bytes value (invalid base64 = hard error). The emitter already wroteb64"...", so this closes a Py/JS divergence (Python's loose lexer already decoded it).\udecode:parseQuotedString+parseQuotedScalarnow decode\uXXXX, matching what the emitter writes for sub-0x20 control chars.PackedParser.parseMapand the scalarparseMapScalar) now replace a repeated key in place, matching Go/Python. (GValue.getis first-wins, so dedup must happen at build time.)Added
js/src/roundtrip_golden.test.ts(9 tests): typed packed round-trip with bytes + control chars, the loose bytes scalar inverse,\udecode, dup-key last-wins, JSON-bridge NaN/Inf reject. Rebuilt the trackedjs/dist(onlyparse.jschanged).NaN/Inf policy (maintainer decision)
Rejected on emit in the JSON/loose bridge (
toJsonLoose/fromJsonLoosein JS,to_json_loose/from_json_loosein Python) — both already enforced; pinned by tests.Scope note (surfaced, not hidden)
Typed
timeround-trip is a Go-only Parse/Emit-path feature. Python's loose codec collapsestimeto text on emit and does not reconstruct a typed time on parse (consistent with the JSON-like loose-collapse decision). It is excluded from the Python golden table (with a note in the test) and is not one of the six P0 behaviors.Verification
cd py && python3 -m pytest -q→ 433 passed, 4 xfailedcd js && npm test→ 514 passedTestCrossImpl_*parity green against the rebuilt distpython3 tests/all_impl_parity_test.py→ 21/21 full Go/Python/JS parity🤖 Generated with Claude Code