A ninth assertion that compares bytes, and the two levels it takes to land it - #4
Merged
Merged
Conversation
…carry it `preserved-text` asserts that a library's preserving write of a text equals that text, byte for byte. It is the corpus's first byte-level assertion and its last: rule 1 of runners/compare.md forbids textual comparison, and the exception added here is bounded by a DIRECTION rather than by an assertion number, so a second one cannot be added by precedent. This assertion compares a library's output to that same library's own input and never to the other library's output, which is the direction the rule's reason has no purchase on: it is about two libraries rendering one value as two texts, and this assertion's whole content is that nothing was re-rendered. It is not `round-trip` under another name. Round-trip re-reads a library's own output and compares documents, saying nothing about the bytes in between, and it writes with preservation explicitly OFF for that reason. This is that echo, asserted deliberately. Nine cases carry byte hazards the curated set does not hold, because that set was swept from what one engine emits: two line-ending conventions and both in one file, a file with no trailing newline, a comment carrying both delimiters, a value across two lines, an unterminated final statement, a duplicate name, a file of comments and an empty one. All nine are donated from the second language's syntax fixtures. Four more declare `operations`, a typed list of the four changes the touched record has to get right, and narrow the assertion to the property FR-023 states: every byte outside the extents of the objects those operations touched equals the input. `preserve-edit-no-op` is the case that caught the first language writing a field the value it already held and reformatting the object for it. The corpus goes from 56 cases and 148 assertions to 69 and 211. The 37 oracle cases gain the assertion over their input and over their committed `expected.epJSON` read as an object-notation input: those files carry a third tool's formatting rather than either library's, so the object-notation half needs no new fixture. The second language has no preserving write yet, which is why 46 entries land in known-divergence.toml naming idfkit-js#12. The assertion is cut while it fails, which is what makes the corpus the specification of the behaviour rather than a check applied afterwards, and the change that lands the writer removes them. Two first-language entries are added and neither is a finding this assertion exposed: idfkit#185 and idfkit#187 were open already and already carried entries on other assertions of the same cases. The four findings this assertion did expose were fixed in idfkit rather than accepted here.
`check-naming-register.mjs` reads the surface each package's exports map points at, so the change that adds `rawText` to a built .d.ts is the change the gate fires on. The register has to be published at a tag first, and this is that tag. `the original source text` becomes aligned on `IdfDocument.rawText`. It was divergent only because the second language had nothing to put in the column, and it is the minimum a consumer needs to answer whether a write will preserve without reaching into the layer. `the concrete syntax tree` becomes `the retained source structure` and stays divergent, with a reason that is true after this feature rather than before it. The old reason said TypeScript had no counterpart "because it has no lossless round-trip yet", which stops being true in the change that lands the writer; a divergence reason describing an absence that has ended reads as a gap nobody closed. The two languages really do retain different material for the same purpose, a flat list of text runs against the syntax layer plus a statement index, and the old concept name named Python's implementation of the idea rather than the idea, so the TypeScript column could only ever be empty. The anchoring and the touched record get no entry. Neither is public in either language, and the register governs public names.
46 typescript entries against idfkit-js#12 went stale the moment the writer landed, and a case passing while its entry remains is a failure rather than a pass. They are removed here, which is what conformance-2026.10 is: conformance-2026.9 with those entries gone. The parity ledger moves `lossless-round-trip` to complete on both sides, and `absence_kind` and `issue` go with the absence they recorded. `names` gains the two register concepts this feature made true. Complete rather than partial, on both sides. Both formats are in scope, so neither language carries a gap. The object notation's all-or-nothing terms differ from the text format's per-object terms in both languages equally, which is a property of the format rather than a difference between the libraries, and the entry's comment block states it so a reader who knows one format does not assume the other's terms.
This was referenced Sep 6, 2026
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.
Adds
preserved-text, the corpus's first byte-level assertion, the thirteen cases that carry it, and the governance entries for the preserving writer in both languages.The corpus goes from 56 cases and 148 assertions to 69 and 211.
It must merge as three commits, because each one is a level and the levels are cut from them:
mainafter mergee6d543fA ninth assertion, which compares bytesconformance-2026.9d1615ceRegister the names the preserving writer addsgovernance-2026.13086d563The preserving writer exists in both languagesconformance-2026.10andgovernance-2026.14A squash leaves
conformance-2026.9andgovernance-2026.13with no commit to point at. Two conformance levels are not indecision: FR-025 requires the assertion to land while the second language still fails it, so the entries exist, and the change that makes it pass must remove them, which changes the level. The corpus has done this once before, for the introspection capability.No tags are pushed with this branch.
governance/README.mdis explicit that "a tag is cut frommainafter the change has merged, never from a branch", and "publish before you pin" — so the four tags are cut here first, and only then mayidfkitand@idfkit/corepoint at them.What the assertion is, and is not
It is not
round-tripunder another name. Round-trip re-reads a library's own output and compares documents, saying nothing about the bytes in between, and it writes with preservation explicitly off for that reason. This is that echo, asserted deliberately.runners/compare.mdforbids textual comparison, and the exception added to rule 1 is bounded by a direction rather than by an assertion number: a library's output against that library's own input, never against the other library's output. An exception keyed to "assertion 9" would invite the next byte comparison by precedent.The cases
Nine carry byte hazards the curated set cannot hold, because it was swept from what one engine emits: both line-ending conventions and both in one file, no trailing newline, a comment carrying both delimiters, a value across two lines, an unterminated final statement, a duplicate name, comments-only and empty. All donated from the second language's syntax fixtures.
Four declare
operations, a typed list of the four changes the touched record has to get right, narrowing the assertion to the property FR-023 states.preserve-edit-no-opis the case that caught the first language reformatting an object after writing a field the value it already held.Divergence entries
46
typescriptentries land atconformance-2026.9naming idfkit-js#12 and are removed atconformance-2026.10, by the change that ends the absence. Both runners are green at the level they are run against.Two
pythonentries are added and neither is a finding this assertion exposed: idfkit#185 and idfkit#187 were open already and already carried entries on other assertions of the same cases. The five findings this feature did expose in the first language were fixed there rather than accepted here.Coverage recorded, not implied
Three cases declare
preserved-textand are skipped in both languages, because both readers refuse a versionless file. That gap is written into the README's coverage-gaps section rather than left to be inferred from a skip, along with the fact that this assertion proves reproduction of the text a read was given and not of a file's bytes.