What happens
Running crates/byard-cli/examples/weather under byard dev (a debug build)
trips the text pipeline's debug-only consistency assert, roughly one run in
three, after ten to twenty idle frames with no interaction at all:
thread 'main' panicked at crates/byard-core/src/encoder/text_glyph.rs:225:5:
a text primitive changed while its upstream dirty flag stayed unset. The glyphs
themselves are still correct, shaping is content-addressed - but `dirty` is also
what builds the incremental redraw region, so this line may not be repainted
where it changed.
Reproduce:
cd crates/byard-cli/examples/weather
cargo run -p byard-cli -- dev # leave it idle ~15s; repeat if it survives
What the offending line looked like
Instrumenting the assert to dump the pool caught it once:
i=6 text="Nairobi" size=14 color=[0.60, 0.63, 0.67, 1.0] wrap=Some(608.0)
"Nairobi" is a Button label, written #[color: 0xFFFFFF] with no size.
The line carrying it had size 14 and colour 0x9AA0AC — the attributes of
the Text lines inside the (unmounted) when state == "ok" branch. So this is
not only a missing dirty flag: on that frame a line's content and its
attributes came from two different elements.
The statusline shows the box count oscillating 6 ↔ 7 on the frames around it,
with nothing in the view that should mount or unmount while idle.
What is not the cause
Ruled out with tests that now live in crates/byard-compiler/tests/http_capability.rs
or were written and discarded for proving nothing:
- Hovering a button. A headless sweep across all four city buttons changes
no text line's resolved bytes, and does not change the box count either — so
hover is not what oscillates.
- A wrap-width change from a resize. Rendering the same tree at 900 px and
then 500 px marks every line whose position moved; a_narrower_viewport_marks_the_lines_whose_wrap_width_it_changed
covers it and passes.
- The controller call itself. The same view with
inject Http and the
http.get(…) call removed is stable across runs, on this branch and on
main. So is examples/todo (0 trips in 3 runs, versus 1 in 3 for weather).
Every headless reproduction attempt passes. It has only ever been seen in a
live window.
Why it matters
The assert's own documentation is precise about the consequence: the glyphs are
correct, because shaping is content-addressed, but dirty is what
dirty_text_bounds unions into the incremental redraw scissor. A line that
changed with the flag unset is shaped correctly and then clipped out of the
region that gets redrawn — which looks identical to a stale-glyph bug on
screen and has a completely different cause. In a release build the assert is
absent and the symptom is a text line that does not repaint where it changed.
The content/attribute mismatch is the more alarming half, and it is the
instance-identity-across-passes
failure mode: lowering, layout and paint each have to carry the same row's
identity, and any disagreement between them fails silently.
Not caused by RFC-0029
Filed from the RFC-0029 HTTP/JSON work because that is what surfaced it, but
the code paths that change there are inert for this view until a button is
pressed, and the trip happens while idle. This is in the encoder/paint identity
area, not the controller boundary.
What happens
Running
crates/byard-cli/examples/weatherunderbyard dev(a debug build)trips the text pipeline's debug-only consistency assert, roughly one run in
three, after ten to twenty idle frames with no interaction at all:
Reproduce:
What the offending line looked like
Instrumenting the assert to dump the pool caught it once:
"Nairobi"is aButtonlabel, written#[color: 0xFFFFFF]with nosize.The line carrying it had size 14 and colour
0x9AA0AC— the attributes ofthe
Textlines inside the (unmounted)when state == "ok"branch. So this isnot only a missing
dirtyflag: on that frame a line's content and itsattributes came from two different elements.
The statusline shows the box count oscillating 6 ↔ 7 on the frames around it,
with nothing in the view that should mount or unmount while idle.
What is not the cause
Ruled out with tests that now live in
crates/byard-compiler/tests/http_capability.rsor were written and discarded for proving nothing:
no text line's resolved bytes, and does not change the box count either — so
hover is not what oscillates.
then 500 px marks every line whose position moved;
a_narrower_viewport_marks_the_lines_whose_wrap_width_it_changedcovers it and passes.
inject Httpand thehttp.get(…)call removed is stable across runs, on this branch and onmain. So isexamples/todo(0 trips in 3 runs, versus 1 in 3 for weather).Every headless reproduction attempt passes. It has only ever been seen in a
live window.
Why it matters
The assert's own documentation is precise about the consequence: the glyphs are
correct, because shaping is content-addressed, but
dirtyis whatdirty_text_boundsunions into the incremental redraw scissor. A line thatchanged with the flag unset is shaped correctly and then clipped out of the
region that gets redrawn — which looks identical to a stale-glyph bug on
screen and has a completely different cause. In a release build the assert is
absent and the symptom is a text line that does not repaint where it changed.
The content/attribute mismatch is the more alarming half, and it is the
instance-identity-across-passes
failure mode: lowering, layout and paint each have to carry the same row's
identity, and any disagreement between them fails silently.
Not caused by RFC-0029
Filed from the RFC-0029 HTTP/JSON work because that is what surfaced it, but
the code paths that change there are inert for this view until a button is
pressed, and the trip happens while idle. This is in the encoder/paint identity
area, not the controller boundary.