Skip to content

P0: round-trip parity for Python + JS (Stage 7) - #10

Merged
phenomenon0 merged 2 commits into
mainfrom
feat/p0-parity-py-js
Jun 20, 2026
Merged

P0: round-trip parity for Python + JS (Stage 7)#10
phenomenon0 merged 2 commits into
mainfrom
feat/p0-parity-py-js

Conversation

@phenomenon0

Copy link
Copy Markdown
Contributor

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, 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 decode, 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:

  • Bytes round-trip: parseValueInner + parseScalarValue now decode b64"..." into a bytes value (invalid base64 = hard error). The emitter already wrote b64"...", so this closes a Py/JS divergence (Python's loose lexer already decoded it).
  • \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): typed packed round-trip with bytes + control chars, the loose bytes scalar inverse, \u decode, dup-key last-wins, 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 (surfaced, not hidden)

Typed time round-trip is a Go-only Parse/Emit-path feature. Python's loose codec collapses time to 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 -q433 passed, 4 xfailed
  • cd js && npm test514 passed
  • Go TestCrossImpl_* parity green against the rebuilt dist
  • python3 tests/all_impl_parity_test.py21/21 full Go/Python/JS parity

🤖 Generated with Claude Code

phenomenon0 and others added 2 commits June 19, 2026 20:52
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
phenomenon0 force-pushed the feat/p0-parity-py-js branch from 97cf543 to 58ff0ad Compare June 20, 2026 01:54
@phenomenon0
phenomenon0 merged commit c335622 into main Jun 20, 2026
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.

1 participant