fix(html): turn the dialect's silent losses into named errors - #149
Merged
Conversation
The HTML dialect degraded four classes of input without a word, and `rustmotion validate` answered "Valid scenario" for all of them. An author who writes a construct the transpiler cannot honour has to be told. `<style>` content was transpiled into a `text` component and painted into the video. It is now refused: `<style>` has a real, expected visual effect that the dialect cannot deliver (there is no cascade engine), so dropping it silently defeats a genuine intent. `<script>`, `<title>`, `<noscript>`, `<template>` and `<head>` are skipped instead — no browser paints them, so ignoring them defeats nothing and merely stops their text leaking onto the canvas. A `<scene>` nested inside any container disappeared from the scenario. It is now refused, naming the offending parent, and the search is recursive: that also catches the case an unclosed `<p>` creates, where HTML5 error recovery hoists the `<h1>` out of the scene and leaves an empty one behind. Recursing silently would have hidden exactly that corruption. `<img>`, `<video>` and `<svg>` became empty `div`s. They are now refused with the equivalent `rm-*` element named in the message. No `bool` schema field was reachable: `coerce_value` left `"false"` a string, so `auto_scroll`, `diff`, `loop`, `show_grid` and friends could not be expressed at all. It now matches `coerce_dsl_value`, and a bare HTML boolean attribute resolves to `true`.
11 tasks
LeadcodeDev
added a commit
that referenced
this pull request
Aug 10, 2026
The HTML dialect degraded four classes of input without a word, and `rustmotion validate` answered "Valid scenario" for all of them. An author who writes a construct the transpiler cannot honour has to be told. `<style>` content was transpiled into a `text` component and painted into the video. It is now refused: `<style>` has a real, expected visual effect that the dialect cannot deliver (there is no cascade engine), so dropping it silently defeats a genuine intent. `<script>`, `<title>`, `<noscript>`, `<template>` and `<head>` are skipped instead — no browser paints them, so ignoring them defeats nothing and merely stops their text leaking onto the canvas. A `<scene>` nested inside any container disappeared from the scenario. It is now refused, naming the offending parent, and the search is recursive: that also catches the case an unclosed `<p>` creates, where HTML5 error recovery hoists the `<h1>` out of the scene and leaves an empty one behind. Recursing silently would have hidden exactly that corruption. `<img>`, `<video>` and `<svg>` became empty `div`s. They are now refused with the equivalent `rm-*` element named in the message. No `bool` schema field was reachable: `coerce_value` left `"false"` a string, so `auto_scroll`, `diff`, `loop`, `show_grid` and friends could not be expressed at all. It now matches `coerce_dsl_value`, and a bare HTML boolean attribute resolves to `true`.
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.
Replaces #147, which GitHub closed when its base branch was deleted on merging #146 — stacked PRs do not survive
--delete-branchon the branch below them. Same commit, retargeted ontochantier/audit-remediationdirectly.Every one of these degraded input without a word, and
rustmotion validateanswered "Valid scenario" for all of them. In a dialect an author writes by hand, a construct the transpiler cannot honour has to be named, not dropped.<style>refused,<script>skipped — and why they differ<style>and<script>content was transpiled intotextcomponents and painted into the video.The audit proposed excluding both, plus
head/title/noscript/template, as one block. The implementation splits them, on a criterion worth stating: does an author believe this construct does something visible?<script>to appear on screen. Skipping it defeats no intent — it only stops the text leaking onto the canvas. Same for<title>,<noscript>,<template>,<head>.<style>block to have an effect. The dialect has no cascade engine and cannot deliver one, so ignoring it silently defeats a real intent. Refused, with a named error.Nested
<scene>refused, recursivelyA
<scene>inside any container vanished from the scenario — one scene silently became zero, and validate reported the shorter duration as correct.Now refused, naming the offending parent. The search is recursive, which turned out to matter beyond the audit's reproduction: with an unclosed
<p>, HTML5 error recovery hoists the<h1>out of the scene and leaves the<scene>node nested under<p>with no children. A non-recursive check would miss it, and auto-recursing would have silently repaired a structure the author had actually broken — hiding the corruption instead of reporting it.<img>,<video>,<svg>refused, not mappedThese became empty
divs. They are now refused with the equivalentrm-image/rm-video/rm-svgnamed in the message.The audit proposed mapping them automatically; that was declined.
<svg>would require re-serialising the DOM subtree into thedatafield, and<img>/<video>need attribute translation intostyle.width/style.height— that is a new feature, not a bug fix, and it would duplicate logic therm-*path already implements and tests. Redirecting to the existing, schema-checked path is the smaller and safer change.code/prewere deliberately left alone: they lose no content today, they are merely styled as plain containers.Boolean attributes
No
boolschema field was reachable from HTML:coerce_valueleft"false"as a string, soauto_scroll,diff,loop,show_grid,show_borders,pulsecould not be expressed at all. Now aligned withcoerce_dsl_value.Behaviour change worth flagging: an empty attribute value (
attr="") now yieldstruerather than being dropped. html5ever cannot distinguish<rm-codeblock diff>fromdiff=""at DOM level, so the two cannot be told apart — a bare boolean is idiomatic HTML, an intentionally-empty string attribute in a video dialect is not, so the trade favours the former. A field expecting a string and givenattr=""now produces a named type error instead of silently falling back to its default.Verification
cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,cargo test --workspace— 752 passed, 0 failed, re-run by the orchestrator on the integrated tree. 24 new tests (10 unit, 14 integration intests/silent_loss_regressions.rs), each written red first.Known gap, reported not fixed
Text written directly inside
<head>still leaks as atextcomponent: html5ever removes the<head>node itself under the "in body" rule and re-parents its text as a sibling, so there is no element left to match by the time transpilation runs. Outside the audit's reproduction; recorded rather than papered over.