TL;DR
- Re‑engineered Horizon’s auth flow to be both secure and user‑friendly, adding graceful error handling for mistyped passwords, dropped connections, and double submissions.
- Experimented with a Rails‑based LLM that auto‑generates code from plain‑English descriptions; early results show promise but still need refinement.
- Audited Eli’s UX to eliminate friction points, enabling seamless re‑entry after breaks and reducing silent churn.
- Added six customizable “rooms” (light/dark, warm/quiet) to Eli, giving users contextual environments that influence comfort and engagement.
- The core mantra, “Free AI can answer you. Eli is built to hold you.”, drives every design and implementation decision.
Re‑thinking Authentication in Horizon
The first half of the day was all about the login flow. I had been iterating on Horizon’s authentication for weeks, each pass tightening security while shaving latency. The last iteration finally felt right: a single‑page login that validates credentials via a GraphQL mutation, then issues a short‑lived JWT and a refresh token stored in an HTTP‑only cookie.
Graceful Failure
What really mattered was the experience when something went wrong. Instead of a generic “invalid credentials” page, I wrapped the mutation in a try/catch block that catches:
-
AuthenticationError– wrong password -
NetworkError– dropped connection -
ValidationError– double submission
Each error maps to a specific, user‑friendly message:
def handle_login_error(error)
case error
when AuthenticationError
flash.now[:alert] = "That username or password doesn't match our records."
when NetworkError
flash.now[:alert] = "Connection lost. Please check your internet and try again."
when ValidationError
flash.now[:alert] = "We detected a duplicate request. Please wait a moment."
else
flash.now[:alert] = "Something went wrong. Try again later."
end
end
The messages are intentionally vague enough to avoid giving attackers hints, yet specific enough to guide legitimate users. I also added a debounce on the submit button to prevent double submissions at the UI level.
Invisible but Critical
These changes are buried three layers deep: the GraphQL resolver, the Rails controller, and the front‑end component. No one sees them unless something breaks, but when they do, the system fails kindly. That’s the kind of invisible kindness I like to build.
Rails + LLM: Turning Sentences into Code
After Horizon settled, I dove into a side project: a Rails app that uses an LLM to generate Ruby code from plain‑English prompts. The goal is to prototype a “code‑as‑service” layer that could one day power Eli’s dynamic room generation.
Architecture
-
Prompt Engine – a thin wrapper around OpenAI’s
gpt-4o-miniAPI. -
Code Validator – runs the generated code through
rubocopand a unit‑test harness to catch syntax errors. - Feedback Loop – user corrections are fed back into the prompt to improve future generations.
# Gemfile
gem 'openai', '~> 3.0'
gem 'rubocop', '~> 1.50'
class CodeGenerator
def initialize(prompt)
@prompt = prompt
end
def generate
response = OpenAI::Client.new.chat(
model: "gpt-4o-mini",
messages: [
{ role: "system", content: "You are a helpful Ruby developer." },
{ role: "user", content: @prompt }
]
)
response.dig("choices", 0, "message", "content")
end
def validate(code)
result = RuboCop::Runner.new([], []).run(code)
result.empty?
end
end
Early Results
The model sometimes produces a working snippet on the first try, especially for straightforward tasks like “create a Rails migration for a users table.” More complex logic, like a service object that interacts with an external API, still requires a few rounds of correction. Still, the fact that it can generate a complete method from a single sentence is a glimpse of what a future AI‑assisted developer might look like.
Eli: From One Room to Six
Eli is a conversational companion built on top of the same authentication stack. The second half of the day was dedicated to polishing the user journey.
UX Audit
I walked through every point where a user might silently lose interest:
- Session Expiry – implemented silent refresh using the refresh token.
- Navigation Gaps – added “Continue where you left off” prompts.
- Onboarding – simplified the first‑time flow to a single screen.
These fixes reduce friction and keep users engaged.
The Six Rooms
The real fun came with the “rooms” feature. Each room is a pre‑configured set of UI themes and background audio that changes the user’s context:
| Room | Theme | Audio |
|---|---|---|
| 1 | Light, warm | Café chatter |
| 2 | Dark, quiet | Library hush |
| 3 | Light, cool | Beach waves |
| 4 | Dark, warm | Fireplace crackle |
| 5 | Light, neutral | City traffic |
| 6 | Dark, cool | Night sky |
The implementation is a simple Rails view component that swaps CSS variables and plays an audio stream via the Web Audio API.
class RoomComponent < ViewComponent::Base
def initialize(room:)
@room = room
end
def theme_css
@room.theme_css
end
def audio_url
@room.audio_url
end
end
<div class="room" style="<%= theme_css %>">
<audio autoplay loop src="<%= audio_url %>"></audio>
<%= render @content %>
</div>
Users can switch rooms on the fly, and the app remembers their last choice in a user_room column on the users table.
The Core Mantra
A line I’d written weeks ago kept resurfacing: “Free AI can answer you. Eli is built to hold you.” That sentence is the north star for every decision:
- Free AI – the LLM that powers code generation and conversational responses.
- Hold you – the gentle error handling, the frictionless re‑entry, the comforting rooms.
Everything else—login flow, six rooms, quiet error messages—serves that promise.
Evening Reflections
I wrapped up the day with a mix of media: Rick and Morty for a dose of chaos, Boardwalk Empire for sharp dialogue, a Joe Rogan clip for a lighter tone, and an unexpected America’s Got Talent episode that actually kept me engaged. It’s a reminder that even when the code is solid, life still needs a little entertainment.
Looking Ahead
- Horizon – still adding analytics to monitor authentication success rates and failure reasons.
- LLM Code Generator – need to improve the feedback loop and add a sandbox for safe execution.
- Eli – rooms are built, but I’ll start A/B testing to see which environments drive the most engagement.
The system that used to fail loudly now fails kindly, and a companion that had one room now has six. Tomorrow I’ll test whether these changes hold up under real traffic and user feedback.
Top comments (0)