format: an HTML file can be a fragment instead of a whole page - #89
Merged
Conversation
A new setting on html: structure, taking document or fragment. It defaults to the whole page these files have always been, so the ten pinned hashes are unchanged whether a recipe says nothing or says document out loud. A fragment is the same blocks without the skeleton - no doctype, no html element, no head and no body. It is what a content field, an email body or a partial render really holds. Half of this change is what was not built. The obvious next setting after XML was a third turn of the textenc work, since HTML carries a meta charset in band the way XML carries a declaration. The standard rules it out, checked at the source rather than recalled: the document encoding must be UTF-8, and the charset attribute must match "utf-8". So a UTF-16 page is not a document whose declaration is ignored, it is a non-conforming document. Measured beside that: html.parser takes text rather than bytes, so a UTF-16 file announcing utf-8 parses without complaint and reports charset utf-8. A mismatch nothing refuses. What the change cost. The bytes that close the body and the document sit in the last RECORD rather than in a footer, so the shape has to reach the block builder and not only the prologue - swapping the prologue alone gives a fragment ending in </body></html>, the right size and nonsense. That is its own mutation. Each shape answers for its own minimum, 118 B against 8 B, and the registry declares the default shape's the way it does for the JSON layouts. The label rides in the heading alone in a fragment, since there is no head to put a title in, and the format still declares its label visible because a heading is. The oracle had to be told which shape to expect. check_html hardcoded a doctype prefix and a closing html tag, so a fragment turned both over - and a checker that worked the shape out from the file would agree with a fragment produced where a page was ordered and the other way round. Co-Authored-By: Claude Opus 5 <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.
#88 is merged, so this sits on main.
A new setting on
html:structure, takingdocumentorfragment. Itdefaults to the whole page these files have always been.
A fragment is the same blocks without the skeleton - no doctype, no
htmlelement, no head and no body. It is what a content field, an email body or a
partial render really holds, and it is what a system under test is handed when
something else owns the page around it.
Half of this is what was NOT built
The obvious next setting after XML was a third turn of the
textencwork, sinceHTML carries
<meta charset="utf-8">in band exactly the way XML carries itsdeclaration. The standard rules it out, checked at the source rather than
recalled:
confidence certain" - the BOM ends the algorithm before
metais looked at.three places.
and the attribute value "must be an ASCII case-insensitive match for the
string
utf-8".So a UTF-16 page is not a document whose declaration is ignored - it is a
non-conforming document. And measured here:
html.parsertakes text, notbytes, so a UTF-16 file carrying
<meta charset="utf-8">parses withoutcomplaint, gives the same eight tags as the control and reports charset
utf-8. A mismatch nothing refuses.Fidelity for HTML is Full, so rule 4 applies and a deliberately non-conforming
fixture belongs to the chaos lab, which does not exist. Verdict:
O191. It costtwo questions to the standard rather than a day of code.
What the change cost
The bytes that close the body and the document sit in the last RECORD, not in
a footer:
So the shape has to reach the block builder, not only the prologue. Swapping the
prologue alone gives a fragment ending in
</body></html>- the right size,deterministic, and nonsense. That is its own mutation, and it is caught.
Each shape answers for its own minimum: 118 B for a page, 8 B for a
fragment, which is
<p></p>and a newline. The registry declares the defaultshape's, the way it does for the JSON layouts.
The label rides in the heading alone in a fragment, since there is no head
to put a title in. The format still declares its label visible, which stays true
because a heading is.
The oracle had to be TOLD the shape.
check_htmlhardcodedstartswith("<!doctype html>")and a closing</html>, so a fragment turnedboth over - the same trap the JSON layouts sprang. A checker that worked the
shape out from the file would agree with a fragment produced where a page was
ordered and the other way round, which are exactly the two defects this
setting can cause.
What holds
documentout loud - two different routes through the parser.NOT CAUGHTfirst time and the guard was at fault: itasked whether a fragment starts lower than a page, and a fragment that
inherited a page's prologue still does - so it passed on the very defect it
names. It now derives the gap from the two files: the smallest fragment has to
be the smallest page without its skeleton.
generator.Planreached the crowding threshold, so the label decision was cutinto its own function rather than the ceiling being raised.
Analysis written before the code, with what was not measured:
docs/HTML-STRUCTURE-2026-09-08.md. Measurement:MVP-FORMATS.md §5.2d.This closes the structural format group - JSON, XML, SVG and HTML all have
settings now.
What is verified, and what is not
Verified on this branch: build,
gofmt,go vet,staticcheckon the changedpackage, every new guard, every one of the six mutations, the document
consistency guards, and the ten pinned hashes.
go test ./...andpreflight --quick. Both were running when this was opened. They passed onthe same code before the rebase onto merged main, and the rebase moved no
content - but that is a reason to expect green, not evidence of it. Read CI.
TestARunStoppedPartWayNamesEveryFileThatFinishedis flaky and it is notthis branch's doing - it fails about one run in four on merged
maintoo,checked in a separate worktree, and this change does not touch it (it uses
explicit sizes, so
drawSizesnever runs). Written down asO192. If CI goesred on that one, it is the flake.
🤖 Generated with Claude Code