forgia · dev log
Engineering log for the forgia LMS platform.
- 28 Aug 26The Congratulation That Was in the Way
A Student got every Blank right, and the modal that told her so covered the passage she wanted to re-read. She could not check what she had written. The fix is a labelled way out — and a completed state to land on, because the modal was carrying the only way forward.
elixirphoenixliveviewcssgettextstudentusability - 24 Aug 26The Number Is Always There, and It Is Wrong at First
Bunny reports a video's length at every stage of transcoding, including the stages where it doesn't know yet. That one fact — a required field that is present and false — decided the shape of the whole integration, and is why the client answers with three outcomes when the caller only needs two.
elixirphoenixintegrationmoxtestingdomain-model - 24 Aug 26The Field Asked for the One Thing Bunny Makes Hard to Copy
A Video Lesson wanted a full Bunny embed URL. Bunny's own UI hands you a GUID and buries the URL. Accepting the id was a twenty-line change; the interesting part is that moving that twenty lines into the domain layer uncovered a bug in a completely different field that had been storing unplayable URLs the whole time.
elixirphoenixliveviewectodomain-modelvideo - 24 Aug 26Six Copies of a Nav, Four Kinds of Drift
A client reported one missing link. The link was missing because the Student topnav had been hand-copied into six templates, and by the time anyone looked, the six copies disagreed about four separate things — including one that hid the Catalog from exactly the visitor who had not bought anything yet.
phoenixliveviewcomponentsrefactoringdesign-systemelixir - 24 Aug 26A Repair Tool That Is Actually Part of the Workflow
Durations are fetched when a Lesson is saved, which sounds complete until you notice that uploading a video and immediately pasting its URL is the normal thing to do — and guarantees no duration. The button that fixes it is not a maintenance script; it is step two.
elixirphoenixliveviewcssgettextinstructor - 09 Aug 26Two Fields Are Not a Template Language
A bulk invite should read like something a person wrote. That takes one textarea, two merge fields, and a surprising number of decisions about what not to build — including a debounce that would have quietly eaten the last word of every message.
emailphoenixliveviewdesign-systemproduct - 09 Aug 26The Invitation That Knows Who You Are
An Invitation is bound to one exact email, so a typo at sign-up throws it away. The fix is a query param the registration screen trusts only as far as the database backs it — plus the enumeration oracle that opened underneath it, and the rate limit that caught my own verification script.
securityonboardingphoenixemaildesign-system - 23 Jul 26A Content-Security-Policy, and Three Things It Caught Before Shipping
A CSP header allowlisting our own scripts plus Paddle and Bunny, shipped report-only first — which is exactly what caught a frame-src fallback misunderstanding, a Paddle CDN subdomain, and Course thumbnails actually living on Backblaze B2, not local disk.
securityphoenixplugpaddlebunny - 20 Jul 26A Real Front Door
The root route stops being a dev-only nav page and becomes a public Landing Page — nav, hero, methodology, instructor bio, and a live course catalog — while reusing the exact catalog card and Paddle pricing already built for /catalog.
liveviewphoenixgettextpaddledesign-system - 16 Jul 26A Profile With No Name Yet
Greeting a Student in Portuguese before they've told us their name meant loosening an invariant that had held since the Profile aggregate was born — plus a single plug that resolves EN/PT-BR the same way for every request, forever.
i18ngettextliveviewectophoenixadrddd - 14 Jul 26The Rate Limiter That Throttles Everyone Except the Attacker
A magic-link login has no password to brute-force, so the threat isn't guessing — it's spending. Someone else's inbox, and your finite mail quota. The obvious per-IP limit protects neither, and behind a reverse proxy it does something much worse than nothing.
securityphoenixplughammerrate-limiting - 14 Jul 26A Green Test Suite and an Unclickable Button
Giving away one Lesson to sell a Course is a small feature with a sharp edge: the route that serves it was never allowed to run without a session. And the CSS class I reused made the Save button stop working — while all 684 tests stayed green.
liveviewphoenixcssdaisyuitestingauthorization - 22 Jun 26Readable course URLs: a slug that knows when to stop changing
Courses now live at /catalog/scrum-master-fundamentos instead of /catalog/42. The slug is auto-derived from the title — no field for the Instructor to fill — and the one decision worth writing down is about time: it regenerates on every title edit while the Course is a Draft, then freezes the moment it's Published, so a shared or indexed URL never breaks. Plus the small Phoenix trick that made ~25 links change by themselves, and why the migration is the only place a -2 suffix is allowed.
sliceseoroutingslugectophoenixddd - 29 May 26Slice 1: Create and Edit a Course
First working slice: the Instructor can create a Course with title, description, price, and language — and edit it after creation.
slicecoursesphoenixliveview