Skip to main content

max / makenotwork

State both mail bounds in the public copy, and move currency out of money labels tiers.md promised up to 10,000 recipients per send and called it a floor, with no mention of the 25,000-a-month cap, so a weekly send to a 10,000-person list read as permitted and was refused on the third. Both guide pages now state both bounds, give the division that turns them into sends a month, and say that automatic publish notifications count against the monthly total -- they reserve through mail_caps::reserve the same as a broadcast, and the page previously implied they were free. The monthly figure substitutes from assumptions.toml rather than being typed, and is linted like recipients_per_send_display. The postmark expense line is commented as deliberately zero for the alpha. Separately, the bulk Set Price field said 'New price ($)'. makeover-layout 0.33.0 ruled a unit is a fact about the value, not part of the question's name, so it answers .unit("USD") instead, which gives .form-unit a writer and lowers the dead-vocabulary seal to 48. Ten money labels in templates/ carried the same shape and now match, with the unit as an adjacent span pointed at by aria-describedby, exactly what makeover-webview emits.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01DwpiantpUgohzML4xr6KeQ
Author: Max Johnson <me@maxj.phd> · 2026-08-31 00:18 UTC
Signed with PGP, not checked
Commit: 2ae3c9ce62c8dd7c0b34bf9146d8317619df2936
Parent: 09c9d0b
13 files changed, +90 insertions, -43 deletions
M server/Cargo.lock +12 -12
@@ -10767,6 +10767,18 @@
10767 10767 name = "synckit-config"
10768 10768 version = "0.2.0"
10769 10769
10770 + [[patch.unused]]
10771 + name = "kberg"
10772 + version = "0.1.0"
10773 +
10774 + [[patch.unused]]
10775 + name = "ops-status"
10776 + version = "0.1.0"
10777 +
10778 + [[patch.unused]]
10779 + name = "painhours"
10780 + version = "0.1.0"
10781 +
10770 10782 [[patch.unused]]
10771 10783 name = "quasi-immediate"
10772 10784 version = "0.91.1"
@@ -10782,15 +10794,3 @@
10782 10794 [[patch.unused]]
10783 10795 name = "quasi-tauri"
10784 10796 version = "0.91.1"
10785 -
10786 - [[patch.unused]]
10787 - name = "kberg"
10788 - version = "0.1.0"
10789 -
10790 - [[patch.unused]]
10791 - name = "ops-status"
10792 - version = "0.1.0"
10793 -
10794 - [[patch.unused]]
10795 - name = "painhours"
10796 - version = "0.1.0"
M server/build.rs +8 -1
@@ -526,12 +526,19 @@
526 526 /// `.form-note`'s reason one entry up: it is markup in a dependency, and no
527 527 /// described screen on this server spells a choice detail yet.
528 528 ///
529 + /// 49 to 48, 2026-08-30: `.form-unit` gained a writer. The bulk "Set Price"
530 + /// field carried its currency in the label (`New price ($)`), which the
531 + /// makeover-layout 0.33.0 ruling forbids; it now says `New price` and answers
532 + /// `.unit("USD")`, so makeover-webview draws the unit span. This is the shape a
533 + /// falling number should have -- a class stopped being dead because a described
534 + /// screen started meaning it, not because the vocabulary shrank.
535 + ///
529 536 /// So a rising number here is not automatically a regression. Read it against
530 537 /// what moved: markup deleted in favour of a description will raise it, and a
531 538 /// class that genuinely lost its writer will too. The list the failure prints
532 539 /// is what tells them apart -- if a name on it is one this server still means
533 540 /// to render, that is the bug.
534 - const DEAD_VOCABULARY_HIGH_WATER: usize = 49;
541 + const DEAD_VOCABULARY_HIGH_WATER: usize = 48;
535 542
536 543 /// The scripts this build script generates into `static/`.
537 544 ///
@@ -118,6 +118,7 @@
118 118 ("derived.R_cap", Form::Dollars),
119 119 ("cohort.cap_display", Form::Verbatim),
120 120 ("broadcasts.recipients_per_send_display", Form::Verbatim),
121 + ("mail_cap.monthly_display", Form::Verbatim),
121 122 ("synckit.storage_usd_per_gb_month", Form::Money),
122 123 ("synckit.invoice_floor_usd", Form::Money),
123 124 ("derived.annual_founding_basic", Form::Dollars),
@@ -33,6 +33,10 @@
33 33 fastmail = 5.00
34 34 domains = 2.00
35 35 cloudflare = 0.00
36 + # Zero on purpose for the alpha: the free tier covers 100 mails a month and
37 + # nothing here is sending yet. Postmark's real shape is $15/mo at 10k plus
38 + # roughly $0.70-0.82 per 1,000 above that, so price this line the month
39 + # broadcast volume becomes real rather than leaving the 0 to be believed.
36 40 postmark = 0.00
37 41 apple_developer_amortized = 8.25 # $99/yr ÷ 12
38 42 co_llc_periodic_amortized = 0.83 # $10/yr ÷ 12
@@ -123,6 +127,11 @@
123 127 # project whether or not the tier subscription lapsed. Lower, because there is no
124 128 # revenue behind it, and above any honest use of a free account.
125 129 no_subscription = 5000
130 + # Display string with thousands separator, for the public copy that has to state
131 + # the monthly bound beside the per-send one. Same pattern as
132 + # `broadcasts.recipients_per_send_display`; keep in sync with the tier values
133 + # above, which are flat.
134 + monthly_display = "25,000"
126 135 # Where the warning band starts, as a fraction of the effective cap. A ratio
127 136 # rather than a fixed remainder so a raised cap warns proportionally.
128 137 warn_at_fraction = 0.8
@@ -636,7 +636,8 @@
636 636 Act::new("Set Price", view.write(slug, "bulk/price"))
637 637 .over(SELECTION)
638 638 .asking(
639 - Field::new(layout::FieldKind::Number, "price_dollars", "New price ($)")
639 + Field::new(layout::FieldKind::Number, "price_dollars", "New price")
640 + .unit("USD")
640 641 .hint("Enter 0 to make items free."),
641 642 ),
642 643 ))
@@ -212,9 +212,10 @@
212 212 </div>
213 213
214 214 <div class="filter-section">
215 - <div class="filter-title" id="price-label">Price ($)</div>
216 - <div class="price-inputs" role="group" aria-labelledby="price-label">
217 - <label for="min-price" class="sr-only">Minimum price in dollars</label>
215 + <div class="filter-title" id="price-label">Price</div>
216 + <div class="price-inputs" role="group" aria-labelledby="price-label" aria-describedby="price-unit">
217 + <span class="form-unit" id="price-unit">USD</span>
218 + <label for="min-price" class="sr-only">Minimum price</label>
218 219 {# The applied bound is rendered back in: hx-include resends
219 220 these on every request, so a blank input would silently
220 221 clear the filter on the next interaction.
@@ -225,18 +226,20 @@
225 226 <input type="number" id="min-price" name="min_price" placeholder="0" min="0" step="0.01"
226 227 value="{{ sidebar.current_min_price }}"
227 228 class="discover-filter"
228 - aria-label="Minimum price in dollars"
229 + aria-label="Minimum price"
230 + aria-describedby="price-unit"
229 231 hx-get="/discover/results"
230 232 hx-trigger="change delay:500ms"
231 233 hx-target="#results-container" hx-swap="outerHTML"
232 234 hx-indicator="#search-spinner"
233 235 hx-include=".discover-filter, #q">
234 236 <span aria-hidden="true">to</span>
235 - <label for="max-price" class="sr-only">Maximum price in dollars</label>
237 + <label for="max-price" class="sr-only">Maximum price</label>
236 238 <input type="number" id="max-price" name="max_price" placeholder="Any" min="0" step="0.01"
237 239 value="{{ sidebar.current_max_price }}"
238 240 class="discover-filter"
239 - aria-label="Maximum price in dollars"
241 + aria-label="Maximum price"
242 + aria-describedby="price-unit"
240 243 hx-get="/discover/results"
241 244 hx-trigger="change delay:500ms"
242 245 hx-target="#results-container" hx-swap="outerHTML"