forgia · dev log
Engineering log for the forgia LMS platform.
- 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 - 04 Aug 26Retake a Quiz
A completed Quiz Lesson was a dead end. Now it offers a Retake — one QuizAttempt row rewound in place, a Completion that is never revoked, and a Course-complete redirect that finally learned to ask whether anything new actually happened.
liveviewquizectoddddesign-system - 04 Aug 26Authoring a Grouping Lesson
A sixth Lesson type, and the first whose draft is allowed to be wrong. The rules gate Publish instead of Save, colours stay distinct by swapping rather than erroring, and three bugs that no green test suite could have found.
liveviewgroupingectoddddesign-system - 03 Aug 26References on a Lesson, and a Bibliography That Compiles Itself
Typed References attached to a Lesson, rendered as an endnote and compiled into a Course-wide Bibliography grouped by Chapter — plus the LiveView bug that stayed invisible to sixteen green tests and took a real browser to find.
liveviewectojsonbdesign-systemdddtesting - 23 Jul 26Find the Sentence: Manual Markers, Instructor Feedback, and a Bug the Browser Caught
Three small upgrades to Sentence Selection — Prompt images, instructor-authored completion messages, and manual || markers replacing the punctuation auto-split — plus a cross-field data-loss bug that only a real browser run surfaced.
liveviewsentence-selectionectodesign-systemddd - 17 Jul 26Sentence Selection, and the Refactor It Forced
A fifth Lesson type — the student selects every sentence in a passage that matches a Prompt, checked server-side with no reveal — and the LiveComponent-per-type refactor it took to add it safely.
liveviewsentence-selectionectodesign-systemdddarchitecture - 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 - 15 Jul 26The Feature Was Named After the One Thing It Must Never Do
The Card asked for a banner warning users about downtime. The design system called the section 'Maintenance mode'. Building it well meant refusing that name — because the whole point of this banner is that the site stays up.
liveviewectopostgresddddesign-systemsecurity - 14 Jul 26The Data Wasn't Hidden. It Was Never Written.
An Instructor wanted to know which quiz questions his students fail most. The obvious job was building the screen. The actual job was discovering that in a try-until-correct quiz, every student ends at 100% — and nothing about their struggle was being recorded at all.
ectopostgresanalyticsliveviewevent-sourcingddd - 13 Jul 26The Price That Lied: Dropping the Local Integer
A Course stored its price as an integer. That integer was wrong for most of the people who saw it — and it was also secretly deciding whether the Course was free. This slice deletes the column, replaces it with an explicit Pricing model, and lets Paddle be the only thing that ever prints a number.
ectoliveviewpaddledddmigrations - 13 Jul 26The Flag You Don't Have Can't Be Wrong
An inbox needs to know what you've already seen. The obvious way to build that is a boolean — and the boolean is a bug waiting for someone to forget to reset it. Two timestamps and a comparison do the same job, and one of the hardest cases falls out for free.
ectoliveviewdddpostgresgdpr - 13 Jul 26A Course Worth Clicking: Thumbnail, Intro Video, Highlights
A Course gets a catalog preview — a Thumbnail, an ungated Intro Video, and a What-you'll-learn list — authored from a new Landing page tab. Plus two bugs that only a real browser could find: a form that ate its own inputs, and a phantom row conjured by LiveView on every keystroke.
liveviewectodesign-systemuploadsddd - 13 Jul 26A Certificate Remembers. A Review Forgets.
Two records hang off the same Enrollment. One freezes your name forever; the other refuses to store it at all. That contradiction is not an oversight — it is the whole design, and it decides what happens to your words when you ask to be erased.
ectoliveviewdddadraccessibility - 11 Jul 26Word Match Lessons
A fourth Lesson type: the instructor marks Blanks in a passage with four delimiter pairs, the student refills them from a shuffled Word Bank salted with Distractors, and the server checks — returning nothing but a count of errors.
liveviewword-matchectodesign-systemdddsecurity - 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 - 22 Jun 26A profile page, two navbars, and a Profile that doesn't exist yet
Users can now set their own name and avatar on a /profile page. The decision worth writing down isn't the form — it's that a Profile is created lazily, on the first Save, because self-registered magic-link users never had one. Plus the Ecto trap that silently skips the write, why the dropdown got built twice, and the value of reading the design system before inventing CSS.
sliceprofileliveviewectodesign-systemddd - 17 Jun 26Student Takes a Quiz
The student-facing side of Quiz Lessons: one question at a time, immediate per-question feedback, a resumable in-progress attempt, a stale-reset when the instructor edits mid-attempt, and a Completion recorded on the final correct answer.
liveviewquizectodesign-systemddd - 17 Jun 26Slice 7: Quiz Lessons
Instructors can now add Quiz Lessons with single-choice Questions — a three-panel editor for writing questions, typing answer options, marking the correct one, and reordering. Students taking the quiz is a separate slice.
slicequizquestionsliveviewectodesign-system - 31 May 26Code Review: 10 Security and Quality Fixes
A code review pass found 10 issues across the codebase — from a cross-course lesson access hole to a TOCTOU race in enrollment. Here's what was fixed and why.
securitycode-reviewhardeningliveviewecto