Skip to main content

max / makenotwork

8.4 KB · 194 lines History Blame Raw
1 //! Forward fence, the hand-written stylesheet ratchet.
2 //!
3 //! Every page ships both frontends. Measured 2026-08-14: 322KB of hand-written
4 //! `style.css` plus 23KB of other hand-written sheets, against 10.7KB of
5 //! `layout.css` and 3.3KB of `geometry.css` generated by makeover-build. The
6 //! generated sheets are 4% of the CSS and the hand-written one has not shrunk,
7 //! and that is currently the largest standing cost of the conversion, paid on
8 //! every page load by every visitor including signed-out ones.
9 //!
10 //! # Parked, 2026-08-18
11 //!
12 //! The ratchet below is `#[ignore]`d by Max's ruling until the conversion is
13 //! finished and the duplicated CSS is gone. Everything this doc argues still
14 //! holds and none of it is retracted; see the test's own doc comment for why the
15 //! enforcement was suspended anyway, and for what turns it back on.
16 //! `the_two_lists_agree` is unaffected and still runs.
17 //!
18 //! # Why this seal exists rather than a note saying the same thing
19 //!
20 //! The conversion's payoff is the hand-written sheet shrinking. Without a
21 //! number, "we converted a screen" and "we removed its cost" are the same
22 //! claim, and only the first one has ever actually happened: four conversion
23 //! batches landed and this file's subject did not move. A seal makes the second
24 //! claim checkable, the same way `frontend_globals` makes the script half
25 //! checkable and `DEAD_VOCABULARY_HIGH_WATER` (in `build.rs`) makes the
26 //! generated half checkable. This is the third of the three.
27 //!
28 //! # Kibibytes rather than bytes
29 //!
30 //! A byte-exact seal on a hand-edited file fails on whitespace, and a guard
31 //! that fires on a reflow is a guard people learn to edit rather than read. A
32 //! KiB is coarse enough that ordinary editing is free and fine enough that a
33 //! deleted rule block shows up.
34 //!
35 //! # One-sided, and which side
36 //!
37 //! Growing fails. Under the feature freeze, adding to a hand-written sheet
38 //! wants an argument, and the argument belongs in the commit that raises the
39 //! number rather than nowhere.
40 //!
41 //! Shrinking fails too, asking for the seal to be lowered. That is the half
42 //! that makes it a ratchet rather than a ceiling: a conversion batch that
43 //! deletes a screen's CSS and leaves the seal where it was has left the next
44 //! batch room to grow back into, which is exactly how 322KB happened.
45 //!
46 //! `check_vocabulary_use` deliberately only warns on the way down, and the
47 //! reasoning does not carry here: dead vocabulary falls as a side effect of
48 //! work aimed elsewhere, and this number falls only when somebody deleted CSS
49 //! on purpose. Somebody who did that can lower a constant.
50
51 use std::fs;
52
53 /// Total hand-written CSS, in whole kibibytes, that this repo may ship.
54 ///
55 /// 337 on 2026-08-15, the first measurement: `style.css` 322,102 bytes,
56 /// `wizard.css` 13,459, `media-player.css` 8,884, `no-js.css` 1,212, for
57 /// 345,657 total. Nothing about that number is a target. It is where four
58 /// conversion batches left it, recorded so the fifth has to move it.
59 ///
60 /// Lower it whenever it falls. Raising it is a decision, not a fix.
61 const CSS_KIB_HIGH_WATER: usize = 337;
62
63 /// The sheets this repo writes by hand.
64 ///
65 /// Kept in step with `HAND_WRITTEN_CSS` in `build.rs`, which is the list the
66 /// breakpoint and vocabulary guards read. A sheet in one list and not the other
67 /// is a sheet that can take bytes from a sealed neighbour and read as a
68 /// deletion, so `the_two_lists_agree` below holds them together.
69 ///
70 /// `layout.css`, `geometry.css` and `embed-geometry.css` are excluded because
71 /// they are generated: their size is makeover's answer, and shrinking them is
72 /// not this repo's work to do.
73 const SHEETS: [&str; 4] = [
74 "static/style.css",
75 "static/wizard.css",
76 "static/media-player.css",
77 "static/no-js.css",
78 ];
79
80 /// Total bytes across [`SHEETS`], and the per-sheet breakdown for the message.
81 fn measure() -> (usize, Vec<(&'static str, usize)>) {
82 let each: Vec<(&str, usize)> = SHEETS
83 .iter()
84 .map(|path| {
85 let bytes = fs::read(path)
86 .unwrap_or_else(|e| panic!("read {path}: {e}"))
87 .len();
88 (*path, bytes)
89 })
90 .collect();
91 (each.iter().map(|(_, bytes)| bytes).sum(), each)
92 }
93
94 /// Parked by Max on 2026-08-18, and the module doc above argues against parking
95 /// it, so the argument gets answered rather than ignored.
96 ///
97 /// What the doc says: a guard people edit rather than read is not a guard, and
98 /// without a number "we converted a screen" and "we removed its cost" are the
99 /// same claim. Both points stand. What they do not survive is the assertion
100 /// pair below: this test fails on any decrease as well as any increase, so mid
101 /// conversion it fires on work going in the direction it exists to reward, and
102 /// the number gets re-baselined every batch. A seal that is re-baselined on
103 /// schedule measures nothing, and it teaches exactly the editing habit the doc
104 /// warns about.
105 ///
106 /// So the number is kept and the enforcement is suspended, rather than the seal
107 /// being quietly lowered batch by batch. `CSS_KIB_HIGH_WATER` stays at 337
108 /// because it is the record of where the conversion started, and it is what the
109 /// finished conversion gets measured against.
110 ///
111 /// WHAT BRINGS IT BACK: the conversion complete and the duplicated CSS removed.
112 /// At that point the number stops moving in both directions, and the decision
113 /// deferred with it becomes worth taking: seal the minified size rather than raw
114 /// bytes, which is what Max ruled earlier the same day. `lightningcss` is
115 /// already a dependency of this repo, so that costs no new dependency.
116 ///
117 /// STATE AT PARKING, measured 2026-08-18:
118 ///
119 /// static/style.css 324,648 (322,102 at the 2026-08-15 baseline)
120 /// static/wizard.css 13,459
121 /// static/media-player.css 8,953
122 /// static/no-js.css 1,212
123 /// total 348,272 = 340 KiB against the sealed 337
124 ///
125 /// Red on the grow assertion by 3 KiB, which is what prompted the ruling.
126 ///
127 /// GoingsOn mnw-server `2d01900f`.
128 #[test]
129 #[ignore = "parked by Max 2026-08-18 until the conversion is finished and the duplicated CSS is gone: see this test's doc comment"]
130 fn hand_written_css_does_not_grow() {
131 let (total, each) = measure();
132 let kib = total / 1024;
133
134 let breakdown = each
135 .iter()
136 .map(|(path, bytes)| format!(" {path}: {bytes}"))
137 .collect::<Vec<_>>()
138 .join("\n");
139
140 assert!(
141 kib <= CSS_KIB_HIGH_WATER,
142 "hand-written CSS grew to {kib} KiB ({total} bytes), above the sealed \
143 {CSS_KIB_HIGH_WATER}:\n{breakdown}\n\n\
144 The conversion is supposed to move this number down. If a rule really \
145 belongs in a hand-written sheet rather than in makeover-webview, raise \
146 CSS_KIB_HIGH_WATER to {kib} and say why in the same commit."
147 );
148
149 assert_eq!(
150 kib, CSS_KIB_HIGH_WATER,
151 "hand-written CSS fell to {kib} KiB ({total} bytes). Lower \
152 CSS_KIB_HIGH_WATER to {kib} so it cannot grow back:\n{breakdown}"
153 );
154 }
155
156 #[test]
157 fn the_two_lists_agree() {
158 // `build.rs` is not a module this test can import, so the list is compared
159 // as text. Crude, and it is the only thing standing between the two lists
160 // drifting apart, which would let bytes move from a weighed sheet to an
161 // unweighed one and read as a deletion.
162 let build = fs::read_to_string("build.rs").expect("read build.rs");
163 let (start, _) = build
164 .split_once("const HAND_WRITTEN_CSS")
165 .expect("build.rs declares HAND_WRITTEN_CSS");
166 // On `= [` rather than `[`, because the declaration's first bracket is the
167 // type annotation (`: [&str; 4]`) and matching that reads the length back
168 // as the list.
169 let declared = &build[start.len()..];
170 let list = declared
171 .split_once("= [")
172 .and_then(|(_, rest)| rest.split_once(']'))
173 .map(|(inner, _)| inner)
174 .expect("HAND_WRITTEN_CSS is an array literal");
175
176 for sheet in SHEETS {
177 assert!(
178 list.contains(sheet),
179 "{sheet} is weighed by this seal but is not in build.rs's \
180 HAND_WRITTEN_CSS, so it is not breakpoint- or vocabulary-checked."
181 );
182 }
183
184 let in_build = list.matches("static/").count();
185 assert_eq!(
186 in_build,
187 SHEETS.len(),
188 "build.rs's HAND_WRITTEN_CSS lists {in_build} sheets and this seal \
189 weighs {}. A sheet in one list and not the other can take bytes from \
190 a sealed neighbour and read as a deletion.",
191 SHEETS.len()
192 );
193 }
194