Daeseon Yoo

Active · May 23, 2026

Mimi

Turn any YouTube video into spaced-repetition English shadowing, plus daily drills for the grammar patterns, collocations, and prepositions that never come automatically. Java/Spring + Next.js, built solo.

8,892 LOC · 49 endpoints · 121 tests · multi-tenant · AWS ECS Fargate

Role
Solo (AI-paired with Claude Code)
Stack
Java 21 · Spring Boot 3.3 · PostgreSQL 16 · Next.js 16 · TypeScript · Gemini / Claude · AWS ECS
Mimi

A full-stack English-learning app for developers who read English fluently but freeze when they have to produce it. Java 21 / Spring Boot API + Next.js / TypeScript frontend, now shipping on the iOS App Store as Mimi — English Shadowing. The thesis is friction removal, not feature count: turn the input you already consume (YouTube) into the output practice you avoid.

Two halves that feed each other

Engineering highlights

Stack & infra

Java 21 · Spring Boot 3.3 · PostgreSQL 16 (Flyway, JSONB, raw-SQL CHECK) · Next.js 16 / React 19 / TypeScript strict · Tailwind 4 · shadcn/ui. Observability: Micrometer + Actuator → Prometheus, MDC request / user logging. Docker (multi-stage, ffmpeg + yt-dlp); GitHub Actions CI; keyless OIDC deploy to AWS ECS Fargate + RDS; frontend on Vercel. Tested with Testcontainers (real Postgres) + Vitest + Playwright (14 specs).

What it doesn't do

Project log

Chronological record of troubleshooting, retros, and updates while building this.

98 logged
Last update: Jul 17, 2026Architecture overview ↓
filter

Build log

Week of Jul 13, 202614 entries · 3 Update · 1 Troubleshoot · 6 Tech retro · 4 UX retro
  1. Consolidating a month of scattered branches into main — and hitting the EAS free-tier wall again

    Troubleshoot

    Jul 17, 2026 · 3 min

    main had drifted 10 days stale while a month of mobile work lived on ~20 unmerged branches. Verified via the EAS API that TestFlight was still build 18 (= old main), trial-merged the integration tip clean, consolidated it into main, pushed — then the new TestFlight cut died on the EAS free-plan iOS build limit, exactly as it had before.

  2. Google Play readiness now has explicit signing and policy gates

    Tech retro

    Jul 16, 2026 · 2 min

    Prepared the Android AAB/internal/draft profiles, removed release-only permission drift, and documented the Play Console work that still requires owner evidence.

  3. Visual review narrowed the iPhone screenshot candidate set

    UX retro

    Jul 16, 2026 · 1 min

    All four captures meet Apple's pixel dimensions, but only Home and Write remain visual candidates after integration review.

  4. iOS and Android store preparation now share one integrated candidate

    Tech retro

    Jul 16, 2026 · 2 min

    Merged both store-readiness tracks, rebuilt the integrated mobile candidate, and kept production signing and policy gates explicitly open.

  5. Mimi's tab bar now reflects its three equal learning pillars

    UX retro

    Jul 16, 2026 · 2 min

    Promoted YouTube shadowing and writing into the primary navigation, kept speaking as the raised center action, and moved review and the full practice library behind clear Home entry points without deleting their routes.

  6. Android release audit: native builds pass, authenticated flows still need evidence

    Tech retro

    Jul 15, 2026 · 4 min

    The first Android native pass fixed platform symbol weights, safe-area handling, portable monospace fonts, and microphone-denial recovery. APK and production AAB artifacts were produced, but Expo dependency checks and authenticated product flows remain open.

  7. Cards were truncating their labels; make them grow to fit instead

    UX retro

    Jul 15, 2026 · 1 min

    Practice pack cards and Home tiles capped text at two lines, clipping labels mid-word — worsened once U4 added a mastery block to the pack cards. Removed the line caps so cards grow to fit and row siblings match height.

  8. Close Expo SDK 56 dependency drift and verify the packaged API target

    Tech retro

    Jul 15, 2026 · 2 min

    Seven Expo dependencies were aligned to SDK 56, Doctor reached 21/21, and fresh Android artifacts were rebuilt with an independently verified production API URL.

  9. Keep Android symbol fonts out of the iOS bundle

    Tech retro

    Jul 15, 2026 · 2 min

    Android symbol weights rendered correctly, but their static imports silently added three Material Symbols fonts to iOS. A Metro platform-file split preserves Android fonts while removing them from iOS exports.

  10. Home now shows honest practice mastery without inventing weekly activity

    UX retro

    Jul 15, 2026 · 3 min

    U2 adds a compact, theme-aware Home mastery bar backed by real SRS and daily PracticeProgress data, plus a once-per-day evening streak-risk line that disappears after practice.

  11. Integrated the four UX-motivation tracks (U1–U4) and closed the first-sparring seam

    Tech retro

    Jul 15, 2026 · 2 min

    Merged three parallel Codex tracks (home mastery, session goal-gradient/peak-end, pack progress/milestones) on top of U1's data layer, resolving only two expected conflicts, then wired the one cross-track seam U4 deliberately left open. A pre-merge ground-truth check caught a branch ref that still carried a supposedly-reverted commit.

  12. F1 — A read-only sparring report that preserves client-owned SRS keys

    Update

    Jul 13, 2026 · 1 min

    The backend can now analyze a client-posted learner transcript into used targets, misses, corrections, and recurring mistakes without guessing or mutating client-owned SRS state.

  13. F2 — Threading a job description through the mock-interview backend

    Update

    Jul 13, 2026 · 1 min

    Mock-interview requests can now carry an optional, capped job description while preserving the existing no-JD prompt and strict question response contract.

  14. F6 — Enforcing the existing paid plan at every AI boundary

    Update

    Jul 13, 2026 · 1 min

    Every AI-backed practice route now uses the existing user-plan entitlement, while retaining a narrow email override for owners and testers.

Week of Jul 6, 20261 entry · 1 Update
  1. Realtime voice sparring v1 — your due SRS cards ride into a live conversation

    Update

    Jul 7, 2026 · 2 min

    First in-app realtime voice feature: OpenAI Realtime sparring where today's due SRS cards are secretly planted in the conversation, detected when you actually say them, and graded on the spot. Shipped with zero new native dependencies via a hidden WebView WebRTC bridge.

Week of Jun 29, 20265 entries · 4 Update · 1 UX retro
  1. 1,956 example sentences the verb pack never had — generated in parallel, merged at build

    Update

    Jul 2, 2026 · 2 min

    The base-verb pack showed a Korean cue and an English pattern but no sentence using it. Generated one natural example + Korean translation for all 1,956 entries with a 103-agent workflow, and merged them as committed side-data so the hand-editable source stays clean.

  2. The verb pack was there all along — three discoverability bugs, one furious founder

    UX retro

    Jul 2, 2026 · 2 min

    The founder installed the new build and could not find the verb pack no matter what. It wasn't missing — it was mislabeled, buried behind a hidden tab, and served as a flat 1956-card firehose. Fixed all three and shipped an axis-browsable pack that still feeds the same SRS.

  3. A pack for telling an AI to write code — added end-to-end on mobile and web

    Update

    Jun 30, 2026 · 2 min

    Added a 108-entry study pack of the natural English a developer uses to instruct an AI coding assistant, authored by hand and wired through the existing pack pipeline on both clients — the only non-mechanical step was regenerating expo-router's typed routes.

  4. From shadowing app to study platform: a cross-pack drill engine on mobile and web

    Update

    Jun 30, 2026 · 2 min

    Added a 1,956-entry base-verb pack plus four study lists, gave every pack recall/reverse/compose modes, and built AI mix + story generators — shared across mobile (Expo) and web (Next.js), with the data/SRS/backend living in one core package.

  5. Shipping Mimi 1.1.0 to TestFlight — and the EAS build-credit wall

    Update

    Jun 30, 2026 · 2 min

    Cut Mimi 1.1.0 (the cross-pack study platform) to TestFlight through the existing eas build --auto-submit pipeline, verified the build VALID straight from the App Store Connect API, and hit the EAS included-build-credits wall on the way out.

Week of Jun 22, 20261 entry · 1 Troubleshoot
  1. Clearing an App Store 1.5 rejection: a Support URL that redirects reads as broken

    Troubleshoot

    Jun 24, 2026 · 2 min

    Apple rejected v1.0 because the Support URL redirected instead of serving a 200 page. Built a real /support page, hit a Vercel CLI file-count limit on the way, and shipped it.

Week of Jun 15, 20268 entries · 1 Troubleshoot · 2 Tech retro · 4 UX retro · 1 Business
  1. A recruiter-facing case-study page: promoting a shipped app from a web portfolio

    Business

    Jun 21, 2026 · 2 min

    Built a public /case-study page for Mimi so the app can be promoted from a web portfolio — live status pills, a browser demo, the system architecture, and the engineering decisions, framed for a backend/working-holiday job search.

  2. Making Mimi feel physical: skeletons, haptics, and a mic dead-end fix

    UX retro

    Jun 20, 2026 · 4 min

    Replaced bare loading spinners with skeleton placeholders, added a thin expo-haptics layer to every drill outcome, and turned a silent denied-mic failure into a Settings deep-link. The honest catch: none of it is verifiable until the next native build.

  3. Five doomed builds to a locked phone: a floundering post-mortem

    Tech retro

    Jun 17, 2026 · 3 min

    I rebuilt to a locked physical iPhone five-plus times before switching to the simulator — and only because the user asked. An honest accounting of what was tunnel-vision and what was a real environment wall.

  4. Subtracting your way to a 'real app': Mimi's 6→4 tab restructure

    UX retro

    Jun 16, 2026 · 4 min

    A code-grounded audit said Mimi read like a feature toolkit, not a daily app. The fix was mostly deletion — 6 tabs to 4, a feature-grid home to one action, a mixing-desk video screen to a script — plus finally giving the output drill real AI feedback.

  5. A first-run that doesn't dump you on an empty screen

    UX retro

    Jun 16, 2026 · 1 min

    New signups were landing straight on an empty home with nothing to tap. Added a 3-step onboarding — gated on the signup path so it never bounces existing users.

  6. Progress you can feel, a resume that's actually where you left off

    UX retro

    Jun 16, 2026 · 2 min

    Three retention refinements: a 'what I've built' stats card (no new backend), a Continue button that resumes the clip you actually last opened, and a Practice tab that stops dumping everything on first view.

  7. Moving the backend AWS (Toronto) → Naver Cloud (Seoul): one box, ~10x cheaper

    Tech retro

    Jun 15, 2026 · 3 min

    The Mimi backend was on AWS ECS in ca-central-1 (Toronto) — wrong on cost (~$50–65/mo) and latency (~180ms) for a Korea-targeted service. Migrated it to a single Naver Cloud (Seoul) box running everything in docker-compose behind Caddy, cut over DNS, and tore AWS down. Cost dropped to roughly a credit-covered ~₩50k/mo.

  8. Five things that bit during the Naver Cloud (Seoul) migration

    Troubleshoot

    Jun 15, 2026 · 3 min

    The AWS→NCP move worked, but five NCP/Caddy specifics each cost a debugging loop: KVM block-storage args, a 10GB boot disk, NCP's root-password SSH model, ACME failing on a stale DNS then a CAA lock, and a pot-provider port mismatch. Symptoms + fixes.

Week of Jun 8, 202610 entries · 6 Update · 1 Tech retro · 3 UX retro
  1. Mimi mobile gets a face — Home redesign, a named color system, and native tab icons

    UX retro

    Jun 13, 2026 · 2 min

    The Mimi mobile app moved from placeholder Expo art and an emoji tab bar to a branded Home screen, a role-named light/dark color system, and SF Symbols tab icons — a visual rebrand with no behavior change, committed mobile-only.

  2. Mobile shadowing: split a clip into sentence A–B loops

    Update

    Jun 13, 2026 · 2 min

    Shadowing wants to drill one short line on repeat, but the player could only loop the whole clip. Reused the video's already-stored sentence timestamps to split a clip into tappable sentence chips, each an A–B loop. Zero new backend.

  3. Mobile shadowing: three active drills + STT pronunciation feedback

    Update

    Jun 13, 2026 · 2 min

    The mobile player mined rich per-clip data (chunked translation, practice scenario, transcript) and rendered it as read-only boxes. Turned it into active drills on the three pillars of fluency — listen, reorder, produce — plus opt-in STT pronunciation feedback. Zero new backend/AI/migration.

  4. Mobile clip player: pin the video, tab the drills

    UX retro

    Jun 13, 2026 · 2 min

    The clip player had grown into a wall of ~8 stacked cards — to do a drill you scrolled the video off-screen, in a shadowing app where the video is the point. Repinned the video as a fixed header and put the drills behind Listen/Order/Speak/Shadow/Notes tabs.

  5. Every Korean string needs a declared role: rebuilding 502 cues in English word order

    Update

    Jun 10, 2026 · 2 min

    The drill app's production cues had drifted into natural Korean word order — the exact thing the method forbids. A dedicated cue field in English word order (직독직해) was generated and order-verified for all 502 items, and an argumentation-moves layer (67 moves in 8 functions) was added for actually defending one's thinking.

  6. Phrasal verbs are a system, not a list: the particle workshop

    Update

    Jun 10, 2026 · 2 min

    Instead of drilling 'kick off' and 'roll back' as isolated flashcards, the new workshop groups phrasal verbs by particle and teaches each particle's core spatial image first — so unseen phrasal verbs become guessable. Plus 72 real dev collocations, and the Practice tab finally shows all its tools.

  7. Authoring a 150-item dev-English drill bank with a verify-after-author workflow

    Update

    Jun 9, 2026 · 2 min

    The interview-prep app needed a big bank of real spoken dev English — phrasal verbs, expressions, code-narration, connectors. A workflow authored 4 banks in parallel and a second adversarial agent cut 205 drafts down to 150, enforcing 'what an engineer actually says, not textbook'.

  8. From flashcards to a live AI interviewer — four training modes in one pass

    Update

    Jun 9, 2026 · 2 min

    The drill app got its missing layer: an AI mock interviewer that digs into your spoken answers, a connector-cloze chaining drill, a speed round, weak-card repair, and a 60-second speech mode — plus a fix for category drills that silently hid most of their cards behind SRS pacing.

  9. Brevity budgets are per-layer: enriching 309 terse flashcards with real explanations

    UX retro

    Jun 9, 2026 · 2 min

    The drill cards were optimized for brevity everywhere — including the reveal, where learning actually happens. A fan-out enrichment pass added a friendly Korean nuance/when-to-use/pitfall explanation and an example translation to every one of the 309 phrase cards, with a one-shard failure recovered standalone.

  10. Migrating prod to Toronto — and recovering a terraform state I killed mid-write

    Tech retro

    Jun 8, 2026 · 3 min

    Moving the backend from Seoul to ca-central-1 to cut mic latency turned into an incident: I SIGKILLed a stuck terraform apply, truncated the state file to zero bytes, and had to re-import 49 orphaned AWS resources back into state instead of deleting and rebuilding.

Week of Jun 1, 202627 entries · 10 Update · 7 Troubleshoot · 10 Tech retro
  1. On-device speech recognition can't do dev jargon — moving the interview mic to Whisper

    Tech retro

    Jun 7, 2026 · 3 min

    The interview-prep mic went through a crash, an AirPods audio-session interruption, and a dictionary-biasing dead-end before the real fix landed: Apple's on-device recognizer simply cannot transcribe terms like 'idempotency', so we record and send to Whisper large-v3 (Groq free tier) instead.

  2. A provider fallback chain so a free Gemini key can spill over to OpenAI

    Tech retro

    Jun 4, 2026 · 2 min

    The app called a single AI provider chosen by config. Turned it into a priority chain — Gemini → OpenAI → Claude — that falls back on quota/error, so free-tier Gemini serves everyday traffic and spills over only when it's full.

  3. First AWS deploy, three walls: a CAA record, an un-issued cert, and a free-tier backup

    Troubleshoot

    Jun 4, 2026 · 2 min

    The first terraform apply of the backend to ap-northeast-2 mostly came up — then RDS rejected the backup window, the HTTPS listener refused a cert that wasn't issued yet, and ACM validation FAILED on a CAA record Vercel had quietly set. Three classic first-deploy traps, in order.

  4. The whole backend deploy, as Terraform you can read line by line

    Tech retro

    Jun 4, 2026 · 4 min

    Turned the manual AWS console runbook into complete, validated Infrastructure as Code: VPC, RDS, S3, Secrets, ECR, IAM with keyless GitHub OIDC, ALB+ACM, and ECS Fargate — one file per concern, commented to teach. The design decisions (no NAT, who-owns-deploys, secrets generated not typed) are the part worth keeping.

  5. `expo lint` isn't read-only — it rewrites package.json on first run

    Troubleshoot

    Jun 4, 2026 · 1 min

    Running `npx expo lint` once to check a new screen quietly added eslint + eslint-config-expo to package.json, regenerated the lockfile, and scaffolded an eslint.config.js — none of it part of the change being made. A reminder that 'lint' commands can mutate the repo.

  6. The CI job that failed every push after the monorepo move

    Troubleshoot

    Jun 4, 2026 · 2 min

    A failure email on every push to main, all from one GitHub Actions job dying at 10s: 'Some specified paths were not resolved, unable to cache dependencies.' The frontend CI job still assumed the frontend was the repo root — its lockfile cache path and npm ci both pointed at the wrong place after the monorepo migration.

  7. Localizing the mobile app to Korean — one agent per screen, and the entity that doesn't decode outside JSX

    Tech retro

    Jun 4, 2026 · 2 min

    The Expo app was English-only despite a Korean audience. Added a lightweight i18n layer and translated all 18 screens by fanning out one agent per file — each edits its screen and returns its dictionary slice — then merged the slices. The bug worth remembering: an HTML entity that JSX silently decodes becomes a literal once the string lives in a data layer.

  8. Don't co-install a React Native app and a web app in one npm workspace

    Troubleshoot

    Jun 4, 2026 · 1 min

    Turning the repo into a monorepo broke the Vercel web build — installing the workspace pulled in the Expo/Metro dependency tree, including a native module with no Vercel/linux prebuild. The fix: keep the cross-platform app out of the workspace and share code through a file: link.

  9. Sentence gym v2: one sentence, ~60 graded sub-variations

    Update

    Jun 4, 2026 · 2 min

    v1 bent a base sentence through 15 grammatical categories. v2 splits each category into its essential sub-variations — ~52 core + ~15 extra — and adds a 0-100 AI score on the optional self-check, turning one sentence into a ~60-rep mastery drill.

  10. Daily sentence gym: bending one mined sentence 15 grammatical ways

    Tech retro

    Jun 4, 2026 · 3 min

    A new daily drill that takes one base English sentence and rewrites it through 15 grammatical operations. The interesting part wasn't the feature — it was the shared LLM client's hardcoded 600-token cap that would have truncated the output JSON on the default provider.

  11. The Vercel deploy that failed four native binaries deep

    Troubleshoot

    Jun 4, 2026 · 3 min

    Merging the monorepo to main and pushing to Vercel surfaced a chain of missing linux native prebuilts — @parcel/watcher, then @next/swc, then Tailwind's oxide and lightningcss. The lesson: when lockfile pins turn into whack-a-mole, change the install itself.

  12. Web and app are two surfaces, not one — porting the gym to the Next.js frontend

    Tech retro

    Jun 4, 2026 · 2 min

    The sentence gym was built on the Expo mobile app first. Trying to 'see it on web' surfaced a basic truth that's easy to forget in a monorepo: the Next.js frontend and the Expo app are separate surfaces that share only the core package — a feature in one isn't in the other until it's ported.

  13. Account deletion done right: audit the cascade, don't trust the memo

    Troubleshoot

    Jun 3, 2026 · 2 min

    The App Store requires in-app account deletion. A leftover note claimed most user tables wouldn't cascade-delete — so I audited the real schema before writing a line. It was wrong: everything already cascades. The only real gap was audio files on disk, plus two bugs the test flushed out.

  14. When the dev server became a fork bomb: a local incident that's a production failure in miniature

    Tech retro

    Jun 3, 2026 · 4 min

    Bringing up the web + backend locally to test, the Next.js dev server spawned hundreds of worker processes, exhausted memory, and made fork() fail machine-wide — ps, curl, and pkill all stopped working. It wasn't a production bug, but it's the exact shape of one: a crash-loop with no backoff and no memory limit. Notes on the triage and the defenses that contain it.

  15. Build-passes ≠ works: a 26-finding adversarial audit of the mobile app, and the bugs a green build hid

    Tech retro

    Jun 3, 2026 · 4 min

    The Expo app's eleven screens all compiled and bundled but had never run on a device. A six-dimension, adversarially-verified audit found 26 real issues — including a hard launch blocker (no signup screen) and a core feature that was silently dead (the shadowing loop never looped). 24 were fixed.

  16. Full parity: the last screen ports, and the whole web app now runs native on one shared core

    Update

    Jun 3, 2026 · 2 min

    The preposition primer was the last web-only screen. With it ported, every screen in Mimi has a native counterpart built on @shadow-ai/core. A note on porting visual features: ship the content, defer the decoration.

  17. Mobile hits active-learning parity: SM-2 review is ported — what 'done' actually means

    Update

    Jun 3, 2026 · 2 min

    The spaced-repetition clip review — the last core learning screen — is now native. Mimi's whole active learning loop runs on mobile from one shared core. A note on why I call the learning surface 'done' rather than counting screens.

  18. Mobile YouTube half, part 2: the in-app segment player — and why the loop is the feature

    Update

    Jun 3, 2026 · 2 min

    The clip player is now in-app: a YouTube IFrame embedded via react-native-youtube-iframe, playing just the clip's segment and looping it for shadowing. The embed was easy; the re-seek-on-ended loop is the part that actually makes it a shadowing tool.

  19. Mobile shadowing, complete: recording yourself — and the one place the shared client couldn't follow

    Update

    Jun 3, 2026 · 2 min

    The mobile app can now record your shadowing take with expo-audio and upload it to your account. The whole shared API layer carried over to native unchanged — except the one multipart boundary where a web Blob and a React Native file URI genuinely differ.

  20. Turning Mimi into a product it can charge for — an entitlement skeleton that doesn't touch a card

    Update

    Jun 2, 2026 · 3 min

    Laid the groundwork for monetization toward an App Store launch: a payment-source-agnostic entitlement model where Mimi stores only 'is this user pro, until when' and a secret-gated webhook flips it — plus the Terms and Privacy pages a real service needs.

  21. Going native: turning the Mimi web app into an Expo iOS/Android app — share the brain, rebuild the shell

    Tech retro

    Jun 2, 2026 · 3 min

    Rather than wrapping the Next.js web app in a WebView (App Store rejection risk) or forking it (drift), I extracted the platform-agnostic core into a shared monorepo package and built a fresh Expo client on top of it. The web app migrated to the shared package with a near-empty diff via re-export shims.

  22. Mimi: turning YouTube into daily English practice for developers — the whole build in one read

    Update

    Jun 2, 2026 · 3 min

    A single-read overview of Mimi — a full-stack app that turns YouTube videos into spaced-repetition shadowing practice and drills the grammar, collocations, and prepositions that never come automatically. The product, the two halves, and the engineering decisions worth defending.

  23. The monorepo pays off: the native drill is UI-only — the brain came from core

    Update

    Jun 2, 2026 · 1 min

    The first real feature on the Expo app — a native Pattern Drill — reuses the web app's exact content, card-key format, session policy, and grading from @shadow-ai/core. Only the UI is new.

  24. Mobile reaches Practice-half parity — and the port keeps getting cheaper

    Update

    Jun 2, 2026 · 2 min

    The Expo app now has the whole Practice hub — pattern + collocation drills, the AI compose check, and the weak-spots dashboard — every screen a thin native shell over @shadow-ai/core. The drill half is at web parity; the YouTube shadowing half is the harder batch left.

  25. Porting the YouTube half to mobile, part 1: split the hard feature by dependency

    Update

    Jun 2, 2026 · 2 min

    The shadowing half of Mimi needs native video and audio, which is the genuinely hard part of the mobile port. So I split it by dependency: Library, Import, and clip detail — everything that's just a core API call — shipped first, with an honest open-in-YouTube placeholder where the native player will go.

  26. Bringing the README to recruiter caliber — and deleting the claims the code didn't back

    Tech retro

    Jun 2, 2026 · 2 min

    Rewrote Mimi's README to a deep, portfolio-grade reference modeled on a sibling project. The part worth recording is the verification: several security/infra claims copied from the template (presigned URLs, HTTP Range) weren't true for Mimi, and got caught by grepping each one against the actual code before committing.

  27. The prod Dockerfile was missing yt-dlp — caught by building it before the deploy

    Troubleshoot

    Jun 1, 2026 · 2 min

    Prepping the AWS backend deploy, I built the production Docker image locally first. yt-dlp — which the YouTube import shells out to — wasn't in it. A months-old commit claimed it added yt-dlp to the prod Dockerfile, but the diff showed the install actually went into Dockerfile.dev. The image build caught a deploy that would have shipped a broken import feature.

Week of May 25, 202627 entries · 8 Update · 3 Troubleshoot · 13 Tech retro · 3 UX retro
  1. 영작 mode: from recall to production, by adding one method to the AI abstraction

    Tech retro

    May 31, 2026 · 2 min

    The drills so far test recall — you reproduce a known model. The composition mode is the other half: you write your OWN sentence using a target, and the AI judges it and offers a better version. Built by adding a single low-level complete() to the existing provider abstraction rather than a parallel client.

  2. Moving the drill streak off localStorage and onto the account

    Tech retro

    May 31, 2026 · 2 min

    The pattern and collocation drills tracked the daily streak in browser localStorage — fine for a personal tool, but it's the exact line between 'my practice' and a product. Added a backend practice slice (one row per user, timezone-correct streak rules as domain behavior) and swapped the frontend to a TanStack Query hook with optimistic reps.

  3. Three drill upgrades: in-session requeue, spoken models, and a weak-spots view

    Update

    May 31, 2026 · 2 min

    On top of the spaced-repetition drills: cards you miss now reappear within the same session, the model sentence is spoken aloud so you can shadow it, and a weak-spots dashboard surfaces what you keep getting wrong — all from data the daily grade already collects.

  4. Two honest gaps after a feature sprint: a cost-guard on the AI endpoint and frontend tests

    Tech retro

    May 31, 2026 · 2 min

    Asked 'is there anything left to harden?', the honest answer was two real things, not busywork: the one AI-cost endpoint had no rate limit, and the frontend logic shipped this session had no tests. Both closed.

  5. Spaced repetition for the drills — and why Leitner, not SM-2

    Tech retro

    May 31, 2026 · 2 min

    The pattern and collocation drills reshuffled the whole deck every time, wasting reps on chunks you'd already nailed. Added per-card spaced repetition on the account: the daily drill now surfaces what's due plus a trickle of new cards. Picked a Leitner box system over SM-2 because the drill's two outcomes map to it exactly.

  6. Hardening the async AI pipeline: timeouts, a bounded pool, graceful shutdown, and honest 4xx

    Tech retro

    May 30, 2026 · 3 min

    A multi-agent audit of the codebase found a classic compound prod-hang: AI HTTP clients with no read timeout running on an unbounded @Async executor, plus a catch-all that turned client errors into 500s. Here's the fix and why each part matters.

  7. Revert hardcoded AWS account ID in ECS task definition to ACCOUNT_ID placeholder

    Troubleshoot

    May 30, 2026 · 2 min

    A prior session had substituted the real 12-digit AWS account ID into 9 ARN/image/bucket references in ecs-task-definition.json; reverted to the ACCOUNT_ID placeholder before it reached git history and gitignored *.local.json for real values.

  8. Chunk-by-chunk shadowing: learning to speak in breaths, not words

    Update

    May 30, 2026 · 1 min

    A Chunks tab in the player reveals a sentence one sense-group at a time. You say each chunk out loud before the next appears — training the habit of speaking in chunks instead of translating Korean word by word.

  9. Prepositions are two problems: a collocation chunk drill, and a Practice hub to hold it

    UX retro

    May 30, 2026 · 2 min

    Trying to add a picture to 'made a living off lying' surfaced the real insight: prepositions split into spatial ones (a diagram works) and idiomatic collocations (no logic — learn the whole word+preposition as one chunk). Built a 101-chunk collocation drill, and folded the three standalone drills under one Practice hub to stop the nav from sprawling.

  10. Daily Pattern Drill: forcing the frames you don't use into your mouth

    Update

    May 30, 2026 · 2 min

    Not phrasal verbs — the sentence frames natives use without thinking ('as long as ___', 'I should have + p.p.', 'Have you ever…?'). A daily production drill: see the frame + a meaning, say the English out loud, check a model, mark it, repeat — with a day streak so it actually happens every day.

  11. When the abstraction leaks: AiAnalysisClient returned ClaudeClient.AnalysisResult

    Tech retro

    May 30, 2026 · 2 min

    The pluggable AI-provider interface returned a type nested inside one of its implementations — so the 'neutral' abstraction depended on Claude, and Gemini imported Claude to satisfy it. The fix, plus extracting the one service the codebase forgot.

  12. Adding JWT revocation without a session store: a token_version claim

    Tech retro

    May 30, 2026 · 2 min

    Stateless JWTs can't be revoked — so changing your password didn't actually log out a stolen token. Here's the token_version approach: one claim, one column, one indexed lookup per request, and real revocation.

  13. Making the AI path observable: request-id correlation + Micrometer timers

    Tech retro

    May 30, 2026 · 2 min

    The prod JSON logger declared requestId/userId fields that nothing ever filled, and the slowest path in the app — the AI call — had no metrics. Fixed both: a correlation-id filter and a Micrometer timer on the analysis call.

  14. The full grammar curriculum: 82 patterns, authored in parallel, reviewed for accuracy

    Update

    May 30, 2026 · 2 min

    The pattern drill got its real content: the foundational-but-always-confusing English patterns a Korean developer needs — the 'as' family, relative clauses, indirect questions, tense, conditionals, verb patterns, articles, and more. 82 patterns / 246 items, authored one-agent-per-category in parallel, then reviewed before merge.

  15. Preposition fill-in drill: turning reading into recall

    Update

    May 30, 2026 · 1 min

    A Drill tab on the prepositions page: blank out the preposition in a phrase, type it, get checked. Items come from both the curated primer and the learner's own mined clip phrases — so reading the diagrams turns into active recall.

  16. Preposition spotlight: teaching the why, not the gloss

    Update

    May 30, 2026 · 2 min

    A new learning feature: for every meaningful preposition in a clip, the AI explains the relationship it encodes (off of = source you lean on; into = transformation) in Korean — built to fix the habit of guessing prepositions by mapping Korean particles word-by-word.

  17. A preposition study page: curated images + the ones you mined yourself

    Update

    May 30, 2026 · 2 min

    Beyond per-clip preposition notes, a dedicated /prepositions page: a curated primer of 16 core prepositions taught by their spatial image, with the real examples you've mined from your own clips mapped onto each one.

  18. Six quick wins from the audit: a forgeable prod secret, an open actuator, and LIKE wildcards

    Tech retro

    May 30, 2026 · 3 min

    Second hardening batch: fail-fast on the dev JWT secret in prod, lock down actuator/Swagger, escape search wildcards, kill a Deck N+1, stream S3 recordings, and de-Claude a misleading field name. Small diffs, real risk removed.

  19. Testing the parts that mattered: the AI pipeline, the default parser, and the isolation gates

    Tech retro

    May 30, 2026 · 2 min

    16 source files had tests; 103 didn't. Rather than chase a coverage number, I tested the four things whose failure would hurt most: the async analysis pipeline, the default provider's parser, the SRS streak logic, and the per-user isolation boundaries.

  20. Show the relationship, don't translate it: animated preposition diagrams

    UX retro

    May 30, 2026 · 2 min

    The first cut of the preposition page was text cards full of Korean — wrong for an English-learning tool. Reworked it to animated SVG diagrams + English only: a ball moving into / over / through a box, so you feel the relationship instead of reading a gloss.

  21. Rewriting the README for recruiters — and trusting code over my own planning docs

    Tech retro

    May 28, 2026 · 2 min

    Rewrote the README for a North American recruiter/interviewer audience. Before writing a word I verified every technical claim against the code — and found my own planning docs (ROADMAP §0.4, PROGRESS) still list translation + 직독직해 as out-of-scope even though they shipped, have migrations, and have E2E specs. Treated code as the source of truth.

  22. AI provider abstraction — Claude credit ran out, swapped to Gemini in one env var

    Tech retro

    May 27, 2026 · 1 min

    Anthropic credit hit zero mid-session. Introduced `AiAnalysisClient` interface; `ClaudeClient` and a new `GeminiClient` both implement it, each gated by `@ConditionalOnProperty(name = "tubeshadow.ai.provider")`. Switching providers is now one env var. Gemini's free tier (1500 req/day) covers personal use indefinitely; operating cost dropped to $0/mo.

  23. Decks (Anki-style clip grouping) + Review next-due toast

    Update

    May 27, 2026 · 2 min

    Two user-perception fixes shipped together. (1) Anki-style decks: a new `decks` table, nullable `deck_id` on `clips` (`ON DELETE SET NULL` so clips fall back to Inbox), library sidebar, per-card move dropdown, deck filter on review. (2) After Easy/Good/Hard/Again, a toast surfaces the actual next-due date so the clip doesn't feel like it 'vanished' from the queue.

  24. Gemini 2.5 Flash: thinking tokens silently truncated JSON output

    Troubleshoot

    May 27, 2026 · 1 min

    After switching to Gemini, every analysis came back as `GEMINI_PARSE_FAILED: Unexpected end-of-input` at the same column. Cause: Gemini 2.5 Flash burns 'thinking' tokens before emitting visible output, eating most of `maxOutputTokens: 800` and truncating the JSON mid-string. Fix: `thinkingConfig: { thinkingBudget: 0 }` and bumped output cap to 4096.

  25. Prompt-engineering 직독직해: forcing English word order in Korean output

    UX retro

    May 27, 2026 · 3 min

    Asking Gemini for 'Korean chunks paired with English chunks' produced grammatical Korean in natural Korean order — useless for shadowing practice. Three prompt revisions: (1) hard rules + bad examples to force source order, (2) tune chunk size from 1-word to 2–5-word sense groups, (3) explicit BAD examples that show the failure mode.

  26. Recording upload 415: MIME `Content-Type` whitelist must strip codec parameter

    Troubleshoot

    May 26, 2026 · 1 min

    Chrome's MediaRecorder tags audio with `audio/webm;codecs=opus`. The recording upload service compared the full string against a whitelist of base types, so Chrome uploads were rejected with 415. Stripped MIME parameters before the check.

  27. i18n: next-intl with path-based routing across 5 locales

    Tech retro

    May 25, 2026 · 2 min

    Migrated the entire frontend to `next-intl` with path-based routing (`/en`, `/ko`, `/ja`, `/zh`, `/es`), default English. All 17 user-facing files were keyed; `<Link>` and `useRouter` imports across the app moved to `@/i18n/navigation` wrappers. ja/zh/es fall back to English copy for now (translation is a non-engineering task).

Week of May 18, 20264 entries · 2 Troubleshoot · 1 Tech retro · 1 Monetization
  1. BYOAI — send analysis prompts to the user's own ChatGPT/Claude/Gemini

    Monetization

    May 24, 2026 · 1 min

    Built a 'Send to my own AI' button that constructs an analysis prompt locally and opens ChatGPT/Claude/Gemini/Perplexity with `?q=...`, falling back to clipboard if auto-fill fails. Operating cost: zero; user keeps the conversation in their preferred model. Sets up a clean alternative to a paid tier.

  2. StaleObjectStateException on clip delete cascading to recordings

    Troubleshoot

    May 24, 2026 · 1 min

    Spring Data's derived `deleteByClipId` hydrates the entity into the persistence context before deleting it, racing with the DB-level `ON DELETE CASCADE`. Switched to `@Modifying @Query` so the delete bypasses the session cache.

  3. AuthRateLimitFilter: Filter auto-registration trap → HandlerInterceptor

    Tech retro

    May 23, 2026 · 1 min

    A `Filter` bean was auto-registered by Spring Boot independent of the `SecurityFilterChain` wiring and tried to instantiate it with a default constructor it didn't have. Switched to `HandlerInterceptor`; same behavior, no auto-registration trap.

  4. YouTube transcript fetch replaced with yt-dlp + self-healing import

    Troubleshoot

    May 23, 2026 · 1 min

    The `timedtext` URL embedded in YouTube watch HTML carries a short-lived token; subtitle fetch returned 200 with empty body within minutes. Migrated entirely to `yt-dlp` subprocess and added an idempotent recovery hook so re-importing the same URL retries the transcript and the dimension probe.