Skip to main content

max / makenotwork

State what Fan+ actually grants, on both pages The landing card and /fan-plus each listed four benefits and the lists disagreed. Neither matched the code. Audited 2026-08-05 against the credit issuance path here and the perk checks in multithreaded. What a Fan+ subscriber actually gets: $5 monthly credit db::promo_codes::issue_fan_plus_credit_code, sent by email each billing cycle + badge mt templates/pages/thread.html, gated on author_is_fan_plus (fan-only; creators excluded) Forum signatures mt routes/account.rs, gated on effective_plus Image embeds mt reject_embeds_for_free_user, which rejects markdown image syntax from non-plus posters Three claims named nothing: Platform polls no voting feature exists anywhere in the tree Dev community access the forum is open to everyone. Fan+ buys perks inside it, not entry to it Early access no code path grants Fan+ anything on testnot. AccessGate defaults to Open, and the one mode that would gate on Fan+ is a deploy setting on a staging box, not an entitlement Two of the real four were on neither page, which is the more interesting half: signatures and image embeds are the perks a Fan+ subscriber actually notices day to day, and nothing sold them. Both lists now name the same four. A test pins them to each other and to the retired claims, because two pages stating a benefit set by hand is exactly how they came apart. Not touched, needs a decision: site-docs/public/guide/fan-plus.md says the + mark appears "on your profile and across the site" (it renders on forum posts only) and that creators carry a `c` mark in the same position (no such mark exists in either codebase).
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-05 23:03 UTC
Signed with PGP, not checked
Commit: b4a5c320d4c8bb5e6773042ac6220eb645c92fe1
Parent: b63fcc2
3 files changed, +68 insertions, -8 deletions
@@ -30,11 +30,13 @@
30 30
31 31 <div class="fan-plus-section">
32 32 <h2 class="section-header">What you get</h2>
33 + {# Keep this list and the Fan+ card on the landing page identical,
34 + and keep both to what the code grants. Audited 2026-08-05. #}
33 35 <ul>
34 - <li><strong>$5 monthly credit</strong>: a promo code delivered by email, usable toward any purchase on the platform</li>
35 - <li><strong>+ badge</strong>: displayed next to your name in social contexts</li>
36 - <li><strong>Platform polls</strong>: vote on feature priorities and platform direction</li>
37 - <li><strong>Dev community access</strong>: join the conversation about how Makenotwork is built</li>
36 + <li><strong>$5 monthly credit</strong>: a promo code delivered by email each billing cycle, usable toward any purchase on the platform</li>
37 + <li><strong>+ badge</strong>: displayed next to your name on your forum posts</li>
38 + <li><strong>Forum signatures</strong>: a signature block rendered under everything you post</li>
39 + <li><strong>Image embeds</strong>: post images in forum threads</li>
38 40 </ul>
39 41 </div>
40 42
@@ -126,11 +126,15 @@
126 126 <div class="card--bordered fork-card">
127 127 <h2 class="fork-heading">Fan+</h2>
128 128 <p class="fork-lede">Support the platform directly and get something back every month.</p>
129 + {# Keep this list and the one on /fan-plus identical, and keep
130 + both to what the code grants. Audited 2026-08-05 against the
131 + credit issuance path here and the perk checks in
132 + multithreaded; see the Fan+ guide page for the detail. #}
129 133 <ul class="fork-list">
130 - <li><strong>$5 monthly credit</strong>: usable toward any purchase on the platform</li>
131 - <li><strong>+ badge</strong>: shown next to your name in social contexts</li>
132 - <li><strong>Platform polls and dev community</strong>: a say in what we build next</li>
133 - <li><strong>Early access</strong>: preview new features on <a href="https://testnot.work">testnot.work</a> before they ship</li>
134 + <li><strong>$5 monthly credit</strong>: a code each billing cycle, usable toward any purchase on the platform</li>
135 + <li><strong>+ badge</strong>: shown next to your name on forum posts</li>
136 + <li><strong>Forum signatures</strong>: a signature block under everything you post</li>
137 + <li><strong>Image embeds</strong>: post images in forum threads</li>
134 138 </ul>
135 139 <div class="fork-actions">
136 140 <a class="btn-primary btn--large" href="/fan-plus">Sign up for Fan+</a>
@@ -436,3 +436,57 @@
436 436 "list price missing from the DOM, the toggle would have to fetch it"
437 437 );
438 438 }
439 +
440 + // ── Fan+ benefit claims ──
441 +
442 + /// The landing card and /fan-plus must name the same benefits, and only ones
443 + /// the code grants.
444 + ///
445 + /// They had drifted: the landing page claimed "Early access" on testnot.work
446 + /// and both claimed "Platform polls" and "Dev community access". Audited
447 + /// 2026-08-05 against the credit issuance path here and the perk checks in
448 + /// multithreaded, none of those three was a thing Fan+ unlocks. Two pages
449 + /// stating a benefit set by hand is how that happened, so this pins them
450 + /// together rather than trusting the next edit to touch both.
451 + #[tokio::test]
452 + async fn fan_plus_benefits_match_across_both_pages() {
453 + let mut h = TestHarness::new().await;
454 +
455 + let landing = h.client.get("/").await;
456 + let fan_plus = h.client.get("/fan-plus").await;
457 + assert_eq!(landing.status, 200);
458 + assert_eq!(fan_plus.status, 200);
459 +
460 + // Each entitlement, with the code that grants it:
461 + // $5 monthly credit -> db::promo_codes::issue_fan_plus_credit_code
462 + // + badge -> mt thread.html, gated on author_is_fan_plus
463 + // Forum signatures -> mt account.rs, gated on UserPerks::effective_plus
464 + // Image embeds -> mt reject_embeds_for_free_user
465 + for claim in [
466 + "$5 monthly credit",
467 + "+ badge",
468 + "Forum signatures",
469 + "Image embeds",
470 + ] {
471 + assert!(
472 + landing.text.contains(claim),
473 + "landing page dropped the {claim} benefit"
474 + );
475 + assert!(
476 + fan_plus.text.contains(claim),
477 + "/fan-plus dropped the {claim} benefit"
478 + );
479 + }
480 +
481 + // Retired claims, none of which named a real entitlement.
482 + for gone in ["Platform polls", "Dev community access", "Early access"] {
483 + assert!(
484 + !landing.text.contains(gone),
485 + "landing page still claims {gone}"
486 + );
487 + assert!(
488 + !fan_plus.text.contains(gone),
489 + "/fan-plus still claims {gone}"
490 + );
491 + }
492 + }