Skip to main content

max / quasi

Add the second admission test: a member is sizeable before it is filled makeover-layout's header states the rule; this is the side that can break it, because a handler decides what the description knows before a renderer sees it. Node's admission paragraph gains the test, and the crate header states the two obligations it puts on a handler: return the screen filled, and pay for the count or say you never will.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-16 19:45 UTC
Signed with PGP, not checked
Commit: 741f90e08d8afcd08e03f9594343bf2a4bff5e21
Parent: d565c8d
2 files changed, +37 insertions, -0 deletions
@@ -94,6 +94,29 @@
94 94 //! See [`Response`] and [`Outcome`].
95 95 //! - **`Router<S>`, generic over app state** (8). See [`Router`].
96 96 //! - **Failure is classified** (9). See [`RouteError`].
97 + //!
98 + //! # What a handler owes the first paint
99 + //!
100 + //! `makeover-layout`'s header states the rule ("First paint is final paint"):
101 + //! nothing resizes after it is drawn, and nothing stands in for content that has
102 + //! not arrived. This crate is the side that can break it, because a handler
103 + //! decides what the description knows before a renderer ever sees it.
104 + //!
105 + //! Two obligations follow, and both are the handler's rather than the renderer's.
106 + //!
107 + //! **Return the screen filled.** A handler is sync and returns a whole
108 + //! [`Screen`], so the data is in hand before any markup exists and there is
109 + //! nothing to wait for. `Readiness::Pending` describes a region that changes
110 + //! later, and a handler reaching for it on the way out is describing a moment
111 + //! that did not happen.
112 + //!
113 + //! **Pay for the count, or say you never will.** Anywhere the description
114 + //! carries an optional measurement, the `Option` is a fact about the query and
115 + //! not about the clock. A handler that wants the reader to see a total runs the
116 + //! count before it returns; one that will not pay for the count leaves it empty
117 + //! permanently and gets a shape that reads honestly without it. Filling it in on
118 + //! a later pass is the one thing forbidden, because the number arrives wider
119 + //! than the space left for it.
97 120
98 121 pub mod chrome;
99 122 pub mod containment;
@@ -2368,6 +2368,20 @@
2368 2368 /// vocabulary is missing a word, and the fix is to add the word rather than to
2369 2369 /// add a widget here.
2370 2370 ///
2371 + /// There is a second test, and a member passes both or neither: **a renderer
2372 + /// must be able to size it before its content arrives.** A member that can only
2373 + /// be laid out once it is filled makes the screen move under the reader, which
2374 + /// is what "First paint is final paint" in `makeover-layout`'s header forbids.
2375 + /// If a member cannot be sized in advance as written, it is missing the fact
2376 + /// that would make it sizeable, and adding that fact is the fix — the same
2377 + /// shape as the first test, one layer along.
2378 + ///
2379 + /// The failure this catches is an optional measurement standing in for a
2380 + /// measurement that has not been taken. An `Option` on a count here means the
2381 + /// host cannot count, for the life of the screen; it never means the count is
2382 + /// still coming. A number that shows up after the first paint widens whatever
2383 + /// prints it.
2384 + ///
2371 2385 /// `#[non_exhaustive]`, the pairing [`Region`](layout::Region) made when it grew
2372 2386 /// [`Region::Widget`](layout::Region::Widget), and for the same reason: the
2373 2387 /// member after this one should not be a lockstep event across three renderers.