Skip to main content

max / makenotwork

Usability audit remediations and audit run 20 cleanup Usability improvements from full UX audit across complexity, learnability, discoverability, and feature completeness: Terminology and docs: - Rename "content insertions" to "dynamic clips" everywhere - Replace content-insertions.md with dynamic-clips.md (broader examples) - Update all cross-references in items, media-library, export, roadmap - Rename toast messages: "Insertion renamed" -> "Clip renamed", etc. Dashboard grouping and progressive disclosure: - Add .section-group-label CSS class for visual category dividers - Account tab: group into Security, Preferences, Data, Account Management - Payments tab: group into Payouts, Settings, History - Creator Plan tab: group into Usage, Outreach - Project Settings: replace "Danger Zone" with "Project Management" - Notification preferences: group by Security, Sales, Content, Platform - SyncKit/Cloud Sync: add as opt-in project feature (new CloudSync enum variant), gate tab visibility, add intro explaining the feature Navigation and discoverability: - Add keyboard shortcuts help button (?) in site header - Rename footer "What's New" to "Changelog" - Add mobile filter toggle on Discover page with active filter count - Link tier names to /docs/guide/tiers from Creator Plan tab - Rename "Plan" tab to "Creator Plan" Content management: - Add inline Publish button for draft items in content table - Show item titles in bulk delete confirmation dialog - Add success toast after bulk publish/unpublish/delete Onboarding and labeling: - Define Project/Item hierarchy in dashboard empty state - Change onboarding "Skip" to "Hide for now" with recovery toast - Rename "Export Portal" to "Export Your Data" - Rename "Import Data" to "Import from Another Platform" - Expand license keys help text with activation explanation Form and interaction improvements: - Restructure promo code form from cramped flex row to 2-column grid - Add copy-to-clipboard buttons for custom domain DNS records - Add DNS propagation note and setup guide link Pre-existing (audit run 20): - Extract media player to static JS/CSS, add video player template - Remove stale database_schema.md (schema.md is canonical) - Update docs, patterns, CONTRIBUTING, todo - Add earn-back credit program to roadmap and guarantees - Add chargeback protection fund doc - Bump docengine and synckit-client deps - Fix SyncKit crypto import
Co-Authored-By
Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-06 18:10 UTC
Commit: aa0751123d353902d2abf3bd98253da475e6f563
Parent: ad832b8
69 files changed, +2142 insertions, -1770 deletions
M CLAUDE.md +1 -9
@@ -6,15 +6,7 @@
6 6
7 7 **No Emoji.** The diamond mark (Young Serif period glyph) is the only graphic element. No emoji anywhere.
8 8
9 - **Typography (Three-Tier System):**
10 - - **H1**: Young Serif (wordmark, page/section headings)
11 - - **H2/H3/meta**: IBM Plex Mono (subheadings, taglines, footer)
12 - - **Body**: Lato (paragraphs, lists, table content)
13 -
14 - **Colors:**
15 - - Background: warm beige `#ede8e1` — never pure white
16 - - Text: dark charcoal-brown `#3d3530` — never pure black
17 - - Accent: violet `#6c5ce7` — diamond mark only, used sparingly
9 + **Typography and colors:** See `_meta/docs/brand.md` for the three-tier type system (Young Serif / IBM Plex Mono / Lato), color palette (warm beige / charcoal-brown / violet), and the diamond mark rule.
18 10
19 11 **Platform Principles:**
20 12 - **0% platform fee** — Stripe's ~3% processing fee is the only cost
@@ -6,7 +6,6 @@
6 6
7 7 - Rust (stable)
8 8 - PostgreSQL
9 - - Redis / Valkey
10 9
11 10 ## Build & Run
12 11
@@ -450,8 +450,7 @@
450 450 - **Rust 2024 edition** (Rust 1.85+). Uses `gen` keyword restrictions and other 2024 features.
451 451 - No `.unwrap()` in production code. Use `?`, `.ok_or()`, or `unwrap_or_default()`.
452 452 - Prefer `Option::and_then`/`map` over `if let Some`/`match` for simple transforms.
453 - - Keep route files under 500 lines. Split into directory modules when they grow beyond that.
454 - - Files with 500+ lines of branching logic should be split. Flat lists (SQL queries, type conversions, static data) are exempt.
453 + - File size guideline per root `CONTRIBUTING.md`: 500-line limit on branching logic, flat lists exempt. Route files follow the same rule — split into directory modules when they grow beyond 500 lines.
455 454
456 455 ## Dependencies
457 456
@@ -4,21 +4,15 @@
4 4
5 5 ## Design System
6 6
7 - ### Typography (Three Tiers)
7 + Typography and brand colors are defined in `_meta/docs/brand.md` (three-tier type system, base palette, diamond mark rule). The server extends the base palette with CSS custom properties:
8 8
9 - | Tier | Font | Usage |
10 - |------|------|-------|
11 - | H1 | Young Serif | Wordmark, page/section headings |
12 - | H2/H3/meta | IBM Plex Mono | Subheadings, taglines, footer, code |
13 - | Body | Lato | Paragraphs, lists, table content, buttons |
14 -
15 - ### Colors
9 + ### Extended Colors (CSS Variables)
16 10
17 11 | Variable | Value | Usage |
18 12 |----------|-------|-------|
19 - | `--background` | `#ede8e1` (warm beige) | Page background. Never pure white. |
20 - | `--detail` | `#3d3530` (charcoal-brown) | Body text. Never pure black. |
21 - | `--highlight` | `#6c5ce7` (violet) | Diamond mark only. Used sparingly. |
13 + | `--background` | `#ede8e1` | Page background (brand beige) |
14 + | `--detail` | `#3d3530` | Body text (brand charcoal-brown) |
15 + | `--highlight` | `#6c5ce7` | Diamond mark only (brand violet) |
22 16 | `--light-background` | `#f4f0eb` | Cards, elevated surfaces |
23 17 | `--surface-muted` | `#ddd7c5` | Secondary buttons, status boxes |
24 18 | `--border` | `#d0cbb8` | Borders, dividers |
@@ -30,7 +24,6 @@
30 24
31 25 ### Rules
32 26
33 - - **No emoji.** The diamond mark (`.dot` class, Young Serif period glyph) is the only graphic element.
34 27 - **No pure white/black.** Use `--background` and `--detail` instead.
35 28 - **Accent color is for the dot only.** Do not use `--highlight` for buttons, links, or borders.
36 29
@@ -79,48 +79,7 @@
79 79
80 80 ## HTMX Dual-Response Pattern
81 81
82 - Routes detect HTMX requests and return different formats. Page routes return HTML fragments; API clients get JSON.
83 -
84 - ```rust
85 - if is_htmx_request(&headers) {
86 - // Return HX-Redirect header
87 - let mut response = Response::new(Body::empty());
88 - response.headers_mut().insert(
89 - "HX-Redirect",
90 - format!("/dashboard/item/{}", item.id).parse().expect("valid path"),
91 - );
92 - return Ok(response);
93 - }
94 -
95 - Ok(Json(ItemResponse { /* ... */ }).into_response())
96 - ```
97 -
98 - ### Response patterns by action
99 -
100 - | Action | HTMX response | Non-HTMX response |
101 - |--------|---------------|-------------------|
102 - | Create + redirect | `HX-Redirect` header | `Json(ItemResponse)` |
103 - | Delete with feedback | `htmx_toast_response("Deleted", "success")` | `StatusCode::NO_CONTENT` |
104 - | Save with status | `Html(SaveStatusTemplate { success, message })` | `Json(UpdateTextResponse)` |
105 - | Inline edit | HTML fragment (re-rendered partial) | `Json(...)` |
106 -
107 - ### Helper functions
108 -
109 - ```rust
110 - // src/helpers.rs
111 - pub fn is_htmx_request(headers: &HeaderMap) -> bool {
112 - headers.get("HX-Request").is_some()
113 - }
114 -
115 - pub fn htmx_toast_response(message: &str, toast_type: &str) -> impl IntoResponse {
116 - // Returns HX-Trigger header with showToast JSON event
117 - // Frontend JS listens for this and renders a toast notification
118 - }
119 -
120 - pub fn hx_toast(message: &str, toast_type: &str) -> HeaderValue {
121 - // Serializes: { "showToast": { "message": "...", "type": "..." } }
122 - }
123 - ```
82 + See `CONTRIBUTING.md` § HTMX Responses for the full pattern (dual-format handlers, response table, helper functions).
124 83
125 84 ## `ListResponse<T>` Envelope
126 85
@@ -143,55 +102,7 @@
143 102
144 103 ## Error Handling
145 104
146 - **Location:** `src/error.rs`
147 -
148 - ### AppError enum
149 -
150 - ```rust
151 - pub enum AppError {
152 - NotFound,
153 - Unauthorized,
154 - Forbidden,
155 - BadRequest(String),
156 - Validation(String),
157 - Database(sqlx::Error),
158 - Internal(anyhow::Error),
159 - Storage(String),
160 - InvalidFileType(String),
161 - FileTooLarge(String),
162 - MalwareDetected(String),
163 - ServiceUnavailable(String),
164 - }
165 - ```
166 -
167 - ### HTTP status mapping
168 -
169 - | Variant | Status code |
170 - |---------|------------|
171 - | `NotFound` | 404 |
172 - | `Unauthorized` | 401 |
173 - | `Forbidden` | 403 |
174 - | `BadRequest` | 400 |
175 - | `Validation` | 422 |
176 - | `Database`, `Internal`, `Storage` | 500 |
177 - | `InvalidFileType` | 400 |
178 - | `FileTooLarge` | 413 |
179 - | `MalwareDetected` | 422 |
180 - | `ServiceUnavailable` | 503 |
181 -
182 - ### User-safe messages
183 -
184 - Internal errors (`Database`, `Internal`, `Storage`) return generic "Something went wrong" to the client. `Validation` and `BadRequest` messages pass through since they describe user input problems.
185 -
186 - ### JSON error middleware
187 -
188 - API routes use a middleware layer (`json_error_layer` in `routes/api/mod.rs`) that converts `AppError` HTML responses to JSON `{"error": "..."}` responses. Page routes render an error template.
189 -
190 - ### Result alias
191 -
192 - ```rust
193 - pub type Result<T> = std::result::Result<T, AppError>;
194 - ```
105 + See `CONTRIBUTING.md` § Error Handling for the `AppError` enum, HTTP status mapping, user-safe message rules, and JSON error middleware. Source: `src/error.rs`.
195 106
196 107 ## Rate Limiting
197 108
@@ -3,7 +3,7 @@
3 3 ## Status
4 4 Done: All pre-beta phases, UX audit remediation, creator trust audit remediation. Active: Creator setup (Stripe), manual testing. Next: Soft launch.
5 5
6 - v0.4.10 deployed 2026-05-04. Audit grade A (Run 20, 2026-05-04). ~83K LOC, 1,214+ test annotations, 0 cargo warnings, 2 cold spots. CI on astra operational. Mutation kill rate 99.4%. Property-based testing active (proptest). `cargo test --features fast-tests` for fast runs.
6 + v0.4.10 deployed 2026-05-04. Audit grade A (Run 20, 2026-05-04). ~83K LOC, 1,214+ test annotations, 0 cargo warnings, 2 cold spots. CI on astra operational. Mutation kill rate 99.4%. Property-based testing active (proptest). `cargo test --features fast-tests` for fast runs. Doc fuzz (2026-05-06): deleted stale database_schema.md, fixed MT README, updated SyncKit version in docs.
7 7
8 8 Human tasks (manual testing, outreach, legal, infrastructure) moved to `human_todo.md`.
9 9 Completed items moved to `todo_done.md`.
@@ -20,6 +20,8 @@
20 20 ### Deferred Code Quality
21 21 - [ ] Remove `async-trait` in favor of Rust 2024 native async traits (chronic)
22 22 - [ ] Add README.md to server/
23 + - [x] Delete stale `database_schema.md` (50 migrations) — superseded by `schema.md` (57 migrations)
24 + - [x] Fix MT README prerequisites: removed Redis/Valkey (sessions are PostgreSQL-backed)
23 25 - [ ] Split oversized route files: exports.rs (737), license_keys.rs (741), health.rs (844), tabs/user.rs (707)
24 26 - [ ] Monitor scheduler.rs (1249), git/mod.rs (624) for growth
25 27 - [x] [rust-fuzz] Replace `.unwrap()` with `.expect("tier passed is_none guard")` in db/creator_tiers.rs:607
@@ -27,7 +29,7 @@
27 29
28 30 ### Dashboard Usability — Remaining (2026-05-05)
29 31
30 - Dashboard restructure complete (Phases 1-6 in todo_done.md). Tab layout: Projects, Payments, Analytics, Profile, Account, Plan + overflow. Fan/creator progressive disclosure implemented. Labels removed. Jargon renamed.
32 + Dashboard restructure complete (Phases 1-6 in todo_done.md). Tab layout: Projects, Payments, Analytics, Profile, Account, Plan + overflow. Fan/creator progressive disclosure implemented. Labels removed. Jargon renamed. Discoverability items complete.
31 33
32 34 #### Performance
33 35 - [x] Add performance philosophy doc (`docs/performance_philosophy.md`) — Tufte/McMaster-Carr principles applied to MNW
@@ -45,18 +47,23 @@
45 47
46 48 Unify audio and video playback into one shared component. Video gets custom controls, insertions, chapters, speed, volume, progress persistence — matching the audio experience. Plan at `~/.claude/plans/eager-ancient-salmon.md`.
47 49
48 - - [ ] **Phase 1:** Extract media-player.js + media-player.css from audio_player.html (no behavior change, just extraction)
49 - - [ ] **Phase 2:** Make state machine media-type aware (audio: dual-element gapless; video: single-element with segment advancement)
50 - - [ ] **Phase 3:** Create unified media_player.html template + /watch/{slug} route for video items
51 - - [ ] **Phase 4:** Update item.html video section — poster + "Watch" button linking to /watch/ (matches /listen/ pattern)
52 - - [ ] **Phase 5:** Add keyboard shortcuts (Space, arrows, M, F, S) to unified player
50 + - [x] **Phase 1:** Extract media-player.js + media-player.css from audio_player.html — CSS renamed to media-* classes, JS reads config from `<script type="application/json">` bridge, keyboard shortcuts (Space/arrows/M/F/S) included. Template: 1035 → 206 lines
51 + - [x] **Phase 2:** Make state machine media-type aware — video uses single element (no dual gapless), all mediaB references guarded with null checks
52 + - [x] **Phase 3:** VideoPlayerTemplate + video_player.html template + video item handler in item.rs. Video items get dedicated player page (like audio). build_segments_json handles Video content type
53 + - [x] **Phase 4:** Video items now route to dedicated player page (early return in item handler, matching audio pattern) — item.html video section is dead code
54 + - [x] **Phase 5:** Keyboard shortcuts included in Phase 1 (Space, arrows, M, F, S)
55 + - [x] **Code fuzz fixes:** null standbyEl crashes in chapter nav + position restore, play-before-seek race in single-element advance, integer cast overflow guards in build_segments_json
53 56
54 - #### Discoverability
55 - - [ ] Add Media Library access from content editors — "Insert Image" button in blog/item editors
56 - - [ ] Add content search/filter to project Content tab — search by title, filter by status/type
57 - - [ ] Add "Embed & Share" quick action on Item Overview
58 - - [ ] Add bulk operations hint on Content tab
59 - - [ ] Add contextual next-step suggestions after key actions
57 + #### Remaining (from code fuzz 2026-05-05)
58 + - [ ] **Video URL fallback on S3 presign failure** — audio handler falls back to `audio_url` (CDN URL stored in DB), but video handler falls back to `None` because `ContentData::Video` has no `video_url` field. Add `video_url` column to items table (migration), populate from CDN base + S3 key on upload, fall back to it in video handler when presign fails. Matches audio pattern.
59 + - [ ] **Arrow key seek should use virtual timeline** — arrow keys currently set `el.currentTime` directly, bypassing segment boundaries. In segment mode this can seek into content from adjacent segments or past boundaries. Fix: route arrow seek through the progress bar seek logic (find target segment from virtual time, load correct segment, seek to offset). Affects both audio and video.
60 +
61 + #### Discoverability — DONE
62 + - [x] Add Media Library access from content editors — "Insert Image" button in blog/item editors, section editors. Shared `media-picker.js` modal fetches `/api/media`, inserts markdown ref at cursor
63 + - [x] Add content search/filter to project Content tab — client-side search by title, filter by status + type dropdowns
64 + - [x] Add "Embed & Share" quick action on Item Overview — button navigates to Embed tab
65 + - [x] Add bulk operations hint on Content tab — form-hint explaining checkbox selection
66 + - [x] Add contextual next-step suggestions after key actions — empty state flow hint, draft/published guidance on item overview
60 67
61 68 #### Feature Completeness
62 69 - [ ] Add download count analytics per item
@@ -179,6 +186,12 @@
179 186 - [ ] Full spec in git history. MVP: 22A + 22B + 22E + 22C + 22H
180 187 - [ ] Trigger: first Everything tier creator subscribes
181 188
189 + ### Chargeback Protection Fund
190 + - [ ] Mutual pool: creators contribute ~0.75% of sales, pool reimburses chargeback losses (all types, not just fraud)
191 + - [ ] Requires: dispute webhooks on connected accounts, pool ledger, per-creator caps, experience rating
192 + - [ ] Full plan: `docs/internal/business/chargeback_protection_fund.md`
193 + - [ ] Prerequisite: 50+ participating creators, 3-month reserve buildup before payouts activate
194 +
182 195 ### Phase 24: Payment Independence
183 196 - [ ] Stablecoin checkout, reduce creator-side fees, Stripe dependency mitigation, international expansion
184 197
@@ -4,6 +4,30 @@
4 4
5 5 ---
6 6
7 + ## Unified Media Player — Phase 1 (2026-05-05)
8 +
9 + - [x] Extract `static/media-player.js` (~450 lines) — full state machine: simple mode, segment mode (dual-element gapless), insertions, chapters, seek, speed, volume, progress persistence. Keyboard shortcuts: Space (play/pause), arrows (seek/volume), M (mute), F (fullscreen/video), S (skip insertion)
10 + - [x] Extract `static/media-player.css` (~370 lines) — all player styles with `media-*` class names (renamed from `audio-*`)
11 + - [x] Slim `audio_player.html` from 1,035 to 206 lines — inline CSS/JS removed, uses `<link>` + `<script src>` + JSON config bridge (`<script type="application/json">`)
12 + - [x] Data bridge: template passes segments, mediaType, itemId via JSON script tag; JS reads on init
13 +
14 + Phase 2-4 (2026-05-05):
15 + - [x] **Phase 2:** media-player.js handles video — single `<video>` element (no dual-element gapless), all `mediaB` references null-guarded
16 + - [x] **Phase 3:** `VideoPlayerTemplate` + `video_player.html` — custom controls, insertions, chapters, speed, volume, progress persistence for video. Video item handler in `item.rs` with presigned S3 URL, segment building. `build_segments_json` updated for `ContentData::Video`
17 + - [x] **Phase 4:** Video items now route to dedicated player page via early return (matching audio pattern). `item.html` inline video player is dead code
18 +
19 + ---
20 +
21 + ## Discoverability Improvements (2026-05-05)
22 +
23 + - [x] Add Media Library access from content editors — shared `media-picker.js` modal with search/folder filter, inserts markdown ref at cursor. Added to blog editor, item text editor, section editors (new + edit)
24 + - [x] Add content search/filter to project Content tab — client-side search by title + status/type dropdown filters above items table
25 + - [x] Add "Embed & Share" quick action on Item Overview — button navigates to existing Embed tab
26 + - [x] Add bulk operations hint on Content tab — form-hint text explaining checkbox bulk actions
27 + - [x] Add contextual next-step suggestions — empty state create/pricing/publish flow hint, draft vs published guidance on item overview
28 +
29 + ---
30 +
7 31 ## Dashboard Restructure + Usability (2026-05-05)
8 32
9 33 Dashboard: