Changelog
All notable changes to this project. Format: Keep a Changelog, trimmed.
Append new entries to [Unreleased]. Never edit released sections.
[Unreleased]
[0.40.0] — 2026-07-16 — Account hub consolidation
Added
/account— a single hub replacing the separate Profile and Settings pages, with four tabs: Overview (profile card, stats, score trends, practice history — everything that used to live on/profile), Preferences (theme, coding language, email announcements), Billing (subscription status and a link to the billing portal), and Security (an embedded Clerk profile for credentials and sessions, plus account deletion).- Header — the separate "Profile" and "Settings" links are gone; clicking the avatar in
the top-right corner now opens
/accountdirectly instead of Clerk's default popover. Sign out moved into the Security tab.
Changed
/profileand/settingsnow redirect to/account(Overview and Preferences respectively) instead of rendering their own pages.
[0.39.1] — 2026-07-15 — Database query performance pass
Fixed
- Running code — each "Run" no longer fetches the same problem row from the database twice; the judge now reuses a single lookup.
- Pattern Contract pages (
/patterns/[patternId]) — removed a redundant lookup that queried the same pattern up to four times per page load. - Profile page — the practice-history query now pulls only the fields the page renders, instead of full attempt payloads (code, run output, AI coaching notes) for every completed attempt.
- Dashboard "Topics" grid — solved-problem counts per topic are now computed by the database directly instead of loading every solved problem into the app to count them.
[0.39.0] — 2026-07-15 — Dashboard topics sorted by pattern complexity
Changed
- Dashboard "Topics" grid — topic playlists now sort by curated pattern complexity (Two Pointers → Sliding Window → ... → Dynamic Programming → Matrices) instead of alphabetically. Previously "Backtracking" appeared first and "Two Pointers" last purely because of name sort order.
Added
Pattern.complexityScore— a curated complexity rank (1–16) for each of the 16 registered patterns.Playlist.order— an explicit sort key for playlist listings, letting curated sets (e.g. "Top Picks") and topic playlists be ordered deliberately instead of by name or creation time.
[0.38.0] — 2026-07-15 — Playlist progress home
Added
- Playlist page rebuilt as a progress home (
/playlist/[slug]) — a hero with the playlist's pattern Elo (or a list-scoped rating for mixed lists), a rating trend graph replayed from your attempts on that list, and a five-axis skill radar (Coverage, Accuracy, Recognition, Edge cases, Speed). Single-pattern topic playlists link straight to their interactive pattern explainer. - Per-problem Elo signal + soft status — each problem row shows the rating change
from your last rated attempt on it and a gentle status (In good shape / Keep going /
Not started);
?list=deep links are preserved. - AI progress insight (Pro) — 1–3 short bullets on how you're doing and what to
improve next, grounded only in your playlist metrics and the pattern's notes. Gated
behind the
playlist-ai-summaryfeature flag and Pro entitlement; free users see an honest locked card, and no AI call is made when locked or when the flag is off.
[0.37.2] — 2026-07-15 — Pattern Explainer chip merging + scroll containment
Fixed
- Binary Search explainer — the
midmarker sat on a permanently separate pointer row, floating detached belowlo/hieven when nothing overlapped;lo/mid/hinow share one row and merge into a single labeled chip when they coincide (lo·mid,lo·mid·hi), matching the Greedy explainer's marker behavior. - All explainers (code panels) — long skeleton lines bled past the panel border (Heap's swap line, Linked List's sublist walk); code bodies now scroll horizontally with the active-line highlight spanning the full line.
- All explainers (playground / mental model / mistakes) — panning a wide visualization horizontally dragged the step label, timeline controls, phase text and meters out of view; those now pin to the visible edge while only the visualization scrolls.
[0.37.1] — 2026-07-14 — Pattern Explainer visualization consistency pass
Fixed
- Binary Search explainer — the
midmarker rendered underneath the array cells (a dead.bsvizheight override never matched), so the probe pointer was invisible whenever it sat nearlo/hi.midnow has its own pointer row,lo/himerge into a singlelo·hichip when the range collapses to one element, and the inspector shows—instead of a misleading0when no probe is active. - Graphs (topological sort) explainer — the state inspector rendered as unstyled inline text instead of the shared inspector cells; the node currently being processed was faded green like a finished node instead of highlighted blue; and the live-highlighted code skeleton panel was missing from the playground (every other explainer has it).
- Greedy Algorithms explainer — the jump visualization's
i/end/farmarkers stacked on top of each other whenever they landed on the same index (always true at the start); markers that coincide now merge into a single labeled chip (i·end,end·far) on the shared pointer row. Its CSS classes also collided with the shared graph-visualization namespace (gviz) and could bleed styles across pages after client-side navigation; renamed to a localjviznamespace. - Sliding Window & Two Pointers explainers —
left/rightpointer chips stacked when both pointers sat on the same index (single-element window, converged pointers); they now merge into one labeled chip. - DFS / BFS / Graphs shared graph language — resting edges, arrowheads, and node outlines were nearly invisible in both themes; raised their contrast and un-faded processed nodes so graph structure stays legible while state is signalled by color.
- Intervals explainer — un-swept interval bars were rendered at 35% opacity and read as an empty panel; they now stay clearly legible with a dashed "not yet swept" border.
- Heap explainer — node index labels collided with the root/current node's highlight ring in the tree view; labels now clear the ring.
- Prefix Sum explainer — the checkpoint pointer chip was labeled
l, indistinguishable from the digit1beside numeric checkpoints; renamed toleft. - All explainers (Variations section) — cards with long one-line code snippets forced their
grid column wider than the page, overflowing the viewport (grid items' implicit
min-width); snippets now scroll inside their card. - Console noise: React "Invalid DOM property" warnings from kebab-case SVG attributes in the feedback dialog icon.
[0.37.0] — 2026-07-13 — Dynamic Programming Pattern Explainer
Added
- Dynamic Programming interactive explainer (
/patterns/dynamic-programming) — hand-built pattern page with House Robber as the reference instance: an animated street-and-ledger visualization of tabulation, a recognition quiz, a hover-annotated code skeleton (TypeScript / Python / Java), an editable playground with a scrubbable execution timeline, a naive-recursion vs. tabulation complexity race with real call counts, a mistake simulator that executes three genuinely wrong implementations (forgotten skip branch, adjacent lookback, botched base case), variations with LeetCode anchors, and a pattern-matching quiz. Follows Sliding Window's ten-section format; linked from the pattern notes wherever Dynamic Programming appears.
[0.36.1] — 2026-07-13 — Pattern Explainer viz UX (Heap, DFS, BFS, Graphs, Trie)
Fixed
- Heap, DFS, BFS, Graphs, Trie Pattern Explainers — graph/tree visualizations used dark-only
chrome (invisible edges and panel borders on light theme), stacked stack/queue/order badges on
top of nodes, and Graphs was missing base graph styles entirely so the topo viz had no canvas
frame. Rebuilt with a shared theme-aware node-link language (
_shared/graph-viz.css), HUD chips below the canvas, dual labeled tree/array panels for Heap, and path/word-end highlighting for Trie.
[0.36.0] — 2026-07-13 — Greedy Algorithms Pattern Explainer
Added
- Interactive Greedy Algorithms Pattern Explainer at
/patterns/greedy-algorithms— a recognition quiz, a range-scout visualization for Jump Game II, an editable playground, a brute-force-vs-greedy complexity race, and a mistake simulator for three real bugs (local-only jumps, counting every farthest growth, looping past the last index) — built with thedsa-pattern-explainerskill.
[0.35.0] — 2026-07-13 — Matrices Pattern Explainer
Added
- Interactive Matrices Pattern Explainer at
/patterns/matrices— a recognition quiz, a grid visualization that walks spiral-order layer bounds (top/right/bottom/left), an editable playground for multiple grid shapes, a brute-rescan-vs-bound-spiral complexity race, and a mistake simulator for three real bugs (skipping reverse-wall guards, forgetting to shrink a bound, exclusive loop ends) — built with thedsa-pattern-explainerskill.
[0.34.1] — 2026-07-13 — Trie Pattern Explainer fixes
Fixed
- Trie Pattern Explainer (
/patterns/trie) — the prefix-tree visualization rendered as solid black circles with invisible connecting lines and invisible letters (broken CSS theming), the code skeleton highlighted the wrong line while stepping through the playground, the Variations cards had no styling and no LeetCode references, the complexity race was unreadable in light mode, and the playground's tree visualization changed height and reflowed on every step. All fixed — the tree now renders correctly in both themes, node positions stay fixed as the tree is built, and the code highlight tracks the active line correctly. - Pattern Explainer pages, all patterns (
/patterns/*) — the blue and violet accent colors used for emphasized text, kickers, and labels across every pattern explainer page were tuned for dark mode only, making them nearly unreadable in light mode. Now theme-aware, like every other color in the design system. - Pattern Explainer pages, all patterns (
/patterns/*) — panel borders, dividers, and subtle background tints (the glass card border, the variable inspector, code-panel dividers, quiz score card, summary tags, and more) were hardcoded to a white tint meant only for dark backgrounds, so they were invisible on the light theme. Now theme-aware. - Trie Pattern Explainer (
/patterns/trie) — the Summary section referenced a CSS class that didn't exist, so it rendered as a plain, borderless box instead of the styled card every other pattern explainer uses, and was missing its closing tag chips entirely. Fixed to match the standard structure.
[0.34.0] — 2026-07-13 — Backtracking Pattern Explainer
Added
- Interactive Backtracking Pattern Explainer at
/patterns/backtracking— a recognition quiz, a decision-tree visualization with choose / prune / backtrack states for Generate Parentheses, an editable playground for n = 1..3, a brute-force-vs-pruned complexity race, and a mistake simulator for three real bugs (forgetting to undo, missing the close guard, recording partials) — built with thedsa-pattern-explainerskill.
[0.33.0] — 2026-07-12 — Prefix Sum Pattern Explainer
Added
- A new interactive "Pattern Explainer" page for Prefix Sum (
/patterns/prefix-sum), reachable from the pattern notes wherever Prefix Sum appears. Follows Sliding Window's ten-section format: an odometer-style decision-rule contract, an animated build-then-query mental model, a three-question recognition quiz, a hover-to-explain code skeleton (TypeScript, Python, or Java), an editable playground with multi-query support, a brute-force-vs-prefix-sum race, a "break it on purpose" bug simulator covering three real Prefix Sum mistakes, the pattern's common variations (range sum, prefix + hashmap, 2D, XOR), a final recognition quiz, and a one-line summary.
[0.32.0] — 2026-07-12 — Graphs Pattern Explainer
Added
- Interactive Graphs Pattern Explainer at
/patterns/graphs— a recognition quiz, a node-link graph visualization with live indegree badges and the ready queue for Kahn's algorithm, an editable playground, a complexity race (naive rescans vs single O(V+E) pass), a mistake simulator for three real bugs (forgetting to decrement, ignoring incomplete results), and a three-question quiz — built with thedsa-pattern-explainerskill.
[0.31.0] — 2026-07-12 — Trie Pattern Explainer
Added
- Interactive Trie Pattern Explainer at
/patterns/trie— a recognition quiz, an auto-animating prefix tree visualization, an editable playground inserting and searching for prefixes, a complexity race (naïve array prefix search vs optimal trie), and a mistake simulator covering common bugs (forgettingisWord = true, confusing search and startsWith) — built with thedsa-pattern-explainerskill.
[0.30.0] — 2026-07-12 — Breadth-First Search Pattern Explainer
Added
- Interactive Breadth-First Search Pattern Explainer at
/patterns/breadth-first-search— a recognition quiz, a node-and-link graph visualization showing the current node, FIFO queue frontier, visited set, level, and discovery order, an editable playground with multiple starting nodes, a complexity race (DFS-for-shortest-path vs BFS level-by-level), and a mistake simulator exercising the classic queue bugs (no visited guard, mark on dequeue, pop instead of shift) — built with thedsa-pattern-explainerskill.
[0.29.0] — 2026-07-12 — Depth-First Search Pattern Explainer
Added
- Interactive Depth-First Search Pattern Explainer at
/patterns/depth-first-search— a recognition quiz, a node-and-link graph visualization showing the current node, recursion stack path, visited set and discovery order, an editable playground with multiple starting nodes, a complexity race (naïve restarts vs single traversal with visited), and a mistake simulator exercising the classic guard bugs (no visited check, mark too late, reversed child consideration) — built with thedsa-pattern-explainerskill. Introduces the reusableGraphVizcomponent for future DFS/BFS/Graph explainers.
[0.28.0] — 2026-07-12 — Intervals Pattern Explainer
Added
- An interactive Intervals Pattern Explainer at
/patterns/intervals— a recognition quiz, a number-line mechanics visualization (sort, then sweep, extending or closing a merged reach), an editable playground, a complexity race, and a mistake simulator covering three real bugs (forgetting to sort, an off-by-one on touching endpoints, and overwriting instead of extending the merged reach) — built with thedsa-pattern-explainerskill.
[0.27.0] — 2026-07-12 — Linked-List Pattern Explainer
Added
- Interactive Linked List Pattern Explainer (
/patterns/linked-list): a recognition quiz, a node-and-arrow mechanics visualization built around iterative in-place reversal, a variable playground, an animated re-scan-vs-single-walk complexity race, and a mistake simulator covering the three classic reversal bugs (flipping before saving, forgetting to advance the walker, returning the wrong pointer).
[0.26.0] — 2026-07-12 — Stack Pattern Explainer
Added
- A new interactive "Pattern Explainer" page for Stack, reachable from the pattern notes you already see (both the standalone pattern page and the in-workspace Notes tab link to it). Ten focused sections built around a single running example — Next Greater Element — including a "waiting room" mental model, a three-question recognition quiz, a hover-to-explain code skeleton (TypeScript, Python, or Java), an editable step-through playground, a brute-force-vs- stack race, a "break it on purpose" bug simulator with three real monotonic-stack mistakes, the pattern's common variations (next-greater, distance-based, bracket matching, augmented stacks), a final recognition quiz, and a one-line summary. Introduces a new vertical LIFO visualization for the stack itself, alongside the existing array-cell visualization reused by the array/window family of patterns.
[0.25.0] — 2026-07-12 — Heap Pattern Explainer
Added
- A new interactive "Pattern Explainer" page for Heap, the second in the series after Sliding Window. Ten sections walk through a size-k min-heap keeping the largest values seen in a stream: a bubble-buoyancy mental model, a three-question recognition quiz, a hover-to-explain code skeleton (TypeScript, Python, or Java), an editable step-through playground, a heap-vs- brute-force race, a "break it on purpose" bug simulator with four real broken implementations, the pattern's common variations, a final recognition quiz, and a one-line summary. Introduces a new array-as-tree visualization, reused across every section on the page.
[0.24.0] — 2026-07-12 — Binary Search pattern explainer
Added
- An interactive Binary Search Pattern Explainer at
/patterns/binary-search: a recognition quiz, an auto-animating "higher, lower, gone" mechanics walkthrough, a hoverable reference skeleton in TypeScript/Python/Java, a scrubbable playground over your own sorted array and target, a linear-vs-binary complexity race, a mistake simulator covering three real bugs (wrong loop bound, eliminating the wrong half, forgetting to advance the pointer), variations with real LeetCode anchors, and a closing quiz.
[0.23.0] — 2026-07-12 — Two Pointers Pattern Explainer
Added
- Interactive Two Pointers pattern explainer at
/patterns/two-pointers: a recognition quiz, a mechanics visualization of pointers converging from both ends of a sorted sequence, an editable playground with a step-by-step trace, a complexity race against brute-force all-pairs, three simulated wrong implementations (wrong direction, blind advance, an off-by-one boundary bug), real LeetCode variations, and a closing quiz.
[0.22.0] — 2026-07-12 — Pattern Explainer authoring skill
Added
- A repo skill (
dsa-pattern-explainer) codifying how to build the remaining 15 interactive Pattern Explainer pages consistently with Sliding Window's: the section structure, shared components, and copy voice, so each new pattern is a scaffolding pass instead of a from-scratch design.
Changed
- Internal only, no user-facing change. Extracted the pattern-agnostic pieces of the
Sliding Window explainer (reveal/number/section-header components, the page nav, motion and
timeline hooks, and the shared stylesheet) into
app/patterns/_shared/, so future pattern pages import them instead of duplicating them.
[0.21.1] — 2026-07-08 — Governance audit reconciliation
Fixed
- Internal only, no user-facing change. Reconciled findings from a structural audit of the
project's governance documents: a stale cross-reference in
prisma/patterns/CLAUDE.mdto a retired data structure, an undeclared vendored-docs path in the governance map, and two architecture decision records whose status hadn't been updated after their changes shipped. - Bumped the
constitution-clidev dependency (governance tooling, not part of the app).
[0.21.0] — 2026-07-06 — Sliding Window Pattern Explainer
Added
- A new interactive "Pattern Explainer" page for Sliding Window, reachable from the pattern notes you already see (both the standalone pattern page and the in-workspace Notes tab now link to it when available). It replaces a plain scrolled block of text with ten focused sections: the decision-rule contract, an animated mental model, a three-question recognition quiz, a hover-to-explain code skeleton (TypeScript, Python, or Java), an editable step-through playground, a brute-force-vs-sliding-window race, a "break it on purpose" bug simulator, the pattern's common variations, a final recognition quiz, and a one-line summary.
- This is a pilot for one pattern; the rest of the pattern notes are unaffected and continue to work as before.
Changed
app/globals.cssgained a handful of new design tokens (--text-body,--text-tiny,--code-bg/--code-dark-bg,--hit-target-mobile/--hit-target-desktop,--ease-out) used by the new page — no visual change to existing pages.
[0.20.0] — 2026-07-05 — Problem source attribution
Added
- Problems can now credit the question bank they were adapted from. When a catalog entry has both a source platform and problem number set, the problem page shows a small "Inspired by LeetCode #42"-style line linking back to the original. Existing problems are unaffected — nothing is shown until a problem is explicitly attributed.
[0.19.0] — 2026-07-05 — Upgrade to Prisma ORM v7
Changed
- Prisma upgraded from v6 to v7. The client now connects through an explicit
@prisma/adapter-pgdriver adapter instead of Prisma's engine reading the schema's connection URL directly. No change in database behavior or query semantics for end users.
[0.18.1] — 2026-07-05 — Catalog patternName reconciled against the pattern registry
Fixed
npm run problems:lintno longer fails on every catalog problem. EverypatternNameinprisma/problems/*.tsreferenced the old 25-slug taxonomy (e.g.two_pointer,dp_linear,graph_dfs) instead of Article A7's 16 canonical pattern names, so every problem'spatternNamefailed to resolve toprisma/patterns/:PATTERNS(introduced when the registry was populated in 0.18.0). Updated all 103 catalog entries across all 16 topic files to reference the canonical names (e.g.stack_count/stack_monotonic→Stack;union_find/topological_sort/shortest_path→Graphs;tree_dfs_bottom_up/tree_dfs_top_down/graph_dfs→Depth-First Search).
[0.18.0] — 2026-07-06 — Pattern registry populated; obsolete category mapping retired (Article A7)
Added
- The pattern registry now has real content — all 16 patterns from Article A7 (Two Pointers, Sliding Window, Linked List, Prefix Sum, Intervals, Stack, Binary Search, Heap, Depth-First Search, Breadth-First Search, Graphs, Trie, Backtracking, Dynamic Programming, Greedy Algorithms, Matrices), each with a description written for the AI reviewer's alignment grading.
- Governance conventions for the pattern registry — documented the closed 16-pattern set, its
coverage floor, and note-completeness expectations across
lib/features/CLAUDE.md,prisma/problems/CLAUDE.md,prisma/CLAUDE.md, and a newprisma/patterns/CLAUDE.md.
Changed
- Topic playlists are now sourced directly from the pattern registry, not a separate hand-maintained category list — one source of truth instead of two lists kept in sync by convention.
- Playlist documentation now correctly reflects the already-live
TOPICkind, alongsideCURATEDandUSER.
Removed
- The obsolete
CATEGORIESlist (and its unused icon/color fields) is gone from the database seed script.
[0.17.0] — 2026-07-06 — Pattern is a first-class database entity (Article A6)
Changed
Patternis now a real database table, not a hardcoded 25-slug map.Problem.patternId,PatternNote.patternId, andPlaylist.patternId(nullable) are schema-level foreign keys toPattern.id— an unknown pattern now fails at the database, not silently. Extending the pattern set is a data change (prisma/patterns.ts), not a code change.- This is a hard cutover, not a preserving migration (ADR-0012): existing problems, attempts, ratings, and pattern notes are wiped and rebuilt from scratch. Every user's practice history and rating resets to zero. A brand-new pattern set and problem catalog are authored separately, after this lands.
- Dashboard topic cards' "Owned" badge now resolves via a direct
Playlist → Patternlink instead of a hardcoded slug map.
[0.16.2] — 2026-07-01 — Conformance re-audit: practice-loop ordering unguarded
Fixed
- Article C1's
enforcementwas overstated asGATED. A conformance re-audit found the Approach-before-Code ordering has no server-side guard — only the lock's one-way-ness is repo-guarded; a direct API call can persist code with no approach ever locked. Downgraded toUNGUARDEDand added to the mechanization backlog inCONSTITUTION.md. No behavior change; this corrects the governance record to match what the code actually guarantees.
[0.16.1] — 2026-07-01 — Governance document reorganization
Changed
- Relocated the project constitution from
decisions/CONSTITUTION.mdtoCONSTITUTION.mdat the repo root, and moved the governance-map entry point fromCLAUDE.mdtoAGENTS.md, aligning with the current governance framework's structural conventions. All cross-references acrossCLAUDE.md,AGENTS.md,lib/features/CLAUDE.md, anddecisions/INDEX.mdupdated to match. - Fixed an ADR (0008) whose
servesfield pointed at a non-existent id.
[0.16.0] — 2026-06-29 — Pattern taxonomy completion (A1/A2)
Fixed
- 5 catalog patterns were unregistered in the canonical taxonomy.
greedy,merge_intervals,matrix,prefix_sum, andshortest_pathwere assigned to problems in 0.15.0 but missing fromPATTERN_DISPLAY, the patternId→display map that also gates pattern-note generation. Effect: pattern notes returnedunknown_patternfor any problem in those patterns, and the AI review'sexpectedPatternfell back to the raw slug (e.g.shortest_path) instead of a display label, weakening alignment grading. Added all five — the taxonomy is now 25 patterns and every catalogpatternIdresolves.
Changed
- Pattern display labels now cover the full catalog: mastery views, the
/patterns/[patternId]page, and OpenGraph images render proper names (e.g. "Shortest Path") for the five previously-unregistered patterns.
[0.15.0] — 2026-06-28 — Constitution Conformance: A1, A5 & A3
Fixed
Problem.patternIdis now non-null (A1) — every catalog problem carries an explicit canonicalpatternIdfrom the 20-pattern taxonomy; the schema column is tightened toNOT NULL.- Pattern-ownership floor raised to 5 attempts (A5) —
OWNED_THRESHOLD.minAttemptscorrected from 3 to the constitution-ratified value of 5. RawAttemptRow.patternNormalizedrenamed topattern(A3) — aligns the type name with its actual content (the pattern display name derived fromProblem.patternId) following the removal ofAttempt.patternNormalized.
Removed
- Dead
patternIdForSeed()fallback — removed fromseed.ts; everySeedProblemnow carries an explicitpatternId, making the runtime fallback unreachable.
[0.14.0] — 2026-06-28 — Expected Pattern AI Review Reinforcement & Schema Cleanup
Changed
- AI coaching pattern payload & prompt — updated the coaching payload to retrieve the problem's static
patternIdand pass it asexpectedPatternto the LLM reviewer. Configured the system prompt to explicitly verify this pattern, marking detours or sub-optimal patterns as a failure mode (mismatch/stall), even if test cases pass. - Rating replay engine — updated the Elo calculator to replay and group attempts directly using the problem's static
patternIdinstead of the dynamic attempt field. - UI page pattern displays — updated the workspace review pane, attempts history page, and profile page to resolve pattern names from
problem.patternId.
Removed
Attempt.patternNormalizeddatabase column — dropped the redundant dynamic post-coaching classification column from theAttempttable and generated the database schema migration.
[0.13.0] — 2026-06-28 — Concrete Problem to Pattern Mapping
Added
- Problem patternId field — added a database-backed
patternIdfield to theProblemmodel to establish a concrete static link between a problem and its canonical algorithmic pattern (e.g.two_pointer,sliding_window). - Seed patternId resolver — updated the database seed script to compute and seed
patternIdfor all 103 catalog problems, utilizing default rules and custom overrides for complex tree/graph/stack/DP problems.
Changed
- Workspace pattern note loading — modified
app/problem/[slug]/page.tsxto read the canonicalpatternIddirectly from theProblemdatabase record, eliminating the previous weak/imprecise playlist-slug mapping fallback. - Curated lists unification — transitioned curated lists to use the unified
Playlistprimitive (specificallykind="CURATED"playlists). - Dashboard back navigation — redirected back links on the playlist view to
/dashboardinstead of the redundant/listspage.
Removed
- Obsolete
/listspage & routes — deletedapp/lists/directory and all/listsand/lists/[slug]frontend routes. Added 308 permanent redirects innext.config.tsto forward old paths. - Redundant List module — deleted the entire duplicate
lib/features/list/backend directory (repository, service, types, and tests).
[0.12.0] — 2026-06-28 — Destructive Migration & Category Cleanup
Changed
- Dashboard page & topic grid — transitioned the dashboard to render topic list cards using
Playlist(specificallykind="TOPIC"playlists) instead of the obsoleteCategorymodel. Solved counts are tracked by playlist ID. - Problem slug page — updated page metadata, breadcrumbs, and pattern note mappings to read from the problem's first topic playlist rather than the defunct
Categorymodel.
Removed
Categorydatabase model — dropped theCategorytable and all category relations/references fromProblemmodel.Modeenum — dropped the database levelModeenum,Attempt.mode, andUserPreference.defaultMode.- Category service & repository — deleted the entire category feature folder (
lib/features/category/).
[0.11.0] — 2026-06-28 — Single Approach->Code->Review loop
Changed
- Practice modes collapsed — simplified the dual practice modes (
LEARNING/INTERVIEW) to a single default flow. All attempts now useINTERVIEWmode (one-shot) by default, eliminating the toggles and branching logic. - Practice loop flow — now routes uniformly: Approach → Code → Review → Next/Retry.
Removed
- Mode preferences and toggles — removed the mode toggle UI from Settings, user preference persistence for
defaultMode, and mode column display in profile history. - Learning mode branching — removed branching logic from workspace and attempt creation, along with associated test assertions.
[0.10.0] — 2026-06-28 — Unified playlist routes + problems index
Added
/playlist/[slug]— a unified playlist view (topics, curated, any kind) showing problems in order with per-user solved/attempted status; opening a problem from here threads the playlist (?list=). Fires aplaylist_openedevent./problems— an index of every problem with all the playlists it belongs to (the many-to-many payoff), each tag linking to its playlist.- Playlist-aware practice loop — when a problem is opened with
?list=<slug>, the post-review CTA splits into Retry (same problem) and Next problem → (advances by playlist position); the last item routes to the playlist page instead of dead-ending. Without?list, the loop is unchanged.
Changed
/topic/[slug]→/playlist/[slug]— old topic URLs now 308-redirect to the unified playlist route, and the dashboard links straight to/playlist.
[0.9.0] — 2026-06-28 — Playlist data layer; Elo by difficulty score
Added
- Playlist read layer (
lib/features/playlist/*) —playlistService.getTopicPlaylists(),getPlaylistBySlug()(with per-user solved/attempted status), andgetAllProblemsWithTags()(every problem with all the playlists it belongs to). Membership reads fromPlaylistItem. Groundwork for the unified/playlistand/problemssurfaces; not yet wired into a page.
Changed
- Per-pattern rating scores against
Problem.difficultyScore— the Elo "opponent" each attempt is rated against is now the numericdifficultyScore, falling back to the difficulty bucket when absent. BecausedifficultyScoreis seeded from the same buckets, ratings and thepattern_ownedsignal are unchanged at cutover.
[0.8.1] — 2026-06-28 — Type-check fix for playlist repo tests
Fixed
lib/features/list/list.repo.test.tsmocks are now typed against the real PrismafindMany/findUniquesignatures, sonpx tsc --noEmitis clean again. No behavior change — the tests already passed at runtime.
[0.8.0] — 2026-06-28 — Playlist primitive groundwork (ADR 0010)
Added
Problem.difficultyScore— an additive numeric difficulty (migration20260628120000_add_difficulty_score), seeded from the EASY/MEDIUM/HARD bucket (1000/1500/2000). It becomes the Elo opponent rating and the within-playlist sort key as the playlist unification lands; seeding it from the existing buckets keeps every rating unchanged at cutover.- Topic playlists — every topic is now also seeded as a
kind="TOPIC"playlist whose membership mirrors that topic's problems, ordered by difficulty. This is the first step of makingPlaylistthe single grouping primitive (ADR 0010). Non-destructive —Problem.categoryIdis untouched in this release; the two-mode system and theCategorymodel are removed in later Phase 2 steps.
[0.7.0] — 2026-06-23 — Dashboard as the navigation hub
Changed
- Curated sets on the dashboard — curated lists now render in a "Curated sets" section on the dashboard (alongside Topics), instead of only at
/lists. - Compact progress hero — the "Your progress" hero no longer fills the screen: the longitudinal trend chart and the in-progress patterns now sit side by side, so Topics and Curated sets are reachable without scrolling past a full-height hero. The company-specific placeholder moved into Curated sets.
- Decluttered header — removed the Topics and Lists links from the global header; both are reached from the dashboard hub (the logo links home to the dashboard for signed-in users).
[0.6.0] — 2026-06-23 — Curated lists (playlists)
Added
- Playlists — additive
Playlist+PlaylistItemschema (migration20260622140000_add_playlists). A playlist is an ordered set of problems;kindis"CURATED"or"USER".Problem.categoryIdis untouched — membership is additive and many-to-many. (Phase 2 widens this into the canonical grouping primitive.) - "Top Picks" curated list — seeded (idempotent) with 16 foundational problems spanning the core patterns, ordered for a top-to-bottom warm-up.
/lists+/lists/[slug]— browse curated lists and work through a list's problems in order, with per-problem solved/attempted status. "Lists" added to the header nav.- List feature (
lib/features/list/*) —listService.getCuratedLists()andgetListBySlug(). lists_viewed+list_openedevents — fire on the lists browse page (curated_count) and a list detail view (list_slug,problem_count,solved_count), making list-open rate measurable.
[0.5.0] — 2026-06-22 — Dashboard analytics hero
Added
- Dashboard hero — the authenticated home now opens with a personalized hero: the global interview rating + tier, patterns owned (chips), problems solved, an in-progress patterns list (each with its rating and a progress bar toward the ownership bar), the longitudinal score-trend chart, and a company-specific placeholder (Phase 2).
PatternService.getRatingSummary— one pass over a user's attempts → global rating + per-pattern ratings, pre-split into owned vs in-progress.ratingTier(lib/features/pattern/rating.ts) — maps a rating to a tier label (Novice → Mastery); the "Owned" band starts exactly at the ownership bar.AttemptService.getScoreTrend— recent scored attempts shaped as chronological chart points.
Changed
- Dashboard is server-rendered — the per-user hero and topic grid render on the server, removing the client-side
/api/progressfetch waterfall. The page is now dynamic (category summaries stay cached). The topic-card "Owned" badge is mapped via the canonical pattern slug (patternIdForCategory) instead of a fragile category-name match. ScoreTrendsChartmoved toapp/_components/and shared by the dashboard and profile.
Removed
GET /api/progressand the clientDashboardClient— superseded by server-side rendering of the dashboard.
[0.4.0] — 2026-06-22 — Per-pattern Elo rating; "owned" redefined
Added
- Per-pattern Elo rating (
lib/features/pattern/rating.ts) —computePatternRatingsreplays a user's finalized attempts on-read (no migration, no stored rating). Each attempt is an Elo "match" vs the problem's difficulty bucket (EASY 1000 / MEDIUM 1500 / HARD 2000); the outcome blends correctness, passing, time efficiency, and confidence calibration. Emits a per-attempt rating series for trend charts.computeGlobalRatingis a capped attempts-weighted mean across patterns.
Changed
- "Owned" is now Elo-based —
OWNED_THRESHOLDchanged from the rigid average-correctness/time/confidence gates over the last 5 attempts to{ minAttempts: 3, minRating: 1600 }.computeOwnedPatternsis reimplemented on top of the rating, so ownership and the dashboard rating share one source of truth. This fixes the prior definition that left most users' north star reading 0 indefinitely. OwnedPatternshape — now{ pattern, rating, attempts }(was{ pattern, attempts, avgCorrectness, avgTimeRatio, avgConfidence }). Thepattern_ownedanalytics event now carriesratinginstead of the threeavg_*properties.
[0.3.9] — 2026-06-22 — Signed-in users land on the dashboard
Changed
- Authenticated home redirect — signed-in users hitting
/are now redirected to/dashboardinstead of seeing the marketing landing page. The landing copy and its sign-in CTAs only apply to logged-out visitors.
[0.3.8] — 2026-06-22 — DB and UX Performance Optimizations
Added
- HTML5 Speculation Rules — added prefetching for
/topic/*links and pre-rendering for/problem/*workspaces on hover. - Monaco Editor Preloading — preloads Monaco editor resources on approach lock to eliminate typing workspace transition latency.
- Client-side dynamic progress API — added
/api/progressdynamic endpoint to support client-side hydration of category progress.
Fixed
- User Authentication DB Cache — cached user queries using
react.cache()to prevent redundant Clerk API requests and DB writes on render cycles. - Page-level dynamic caching — wrapped problem slug lookups in request-scoped caching.
- Aggregated Attempt Counting — rewrote attempt count checks using a single consolidated raw SQL query.
- Static Topic Dashboard — refactored the topics dashboard to be statically pre-rendered instead of dynamically generated on every hit.
- Decoupled Quota Chip — moved expensive entitlement DB checks out of root layout SSR to post-hydration client fetches.
- Lazy Entitlement Grants — introduced eligible check guards to prevent unnecessary DB writes on campaign entitlement check cycles.
- New Relic Dev Guard — prevented console noise and loading crashes in developer environments when license keys are missing.
- Turbopack Scope Correction — pinned Turbopack watcher root to
__dirnameto restrict the watcher from reading the whole user home directory.
[0.3.7] — 2026-06-22 — Product analytics: funnel-granularity events (P1)
Added
dashboard_viewedevent — fires when the Topics dashboard loads (withpatterns_owned), the top of the activation funnel.topic_selectedevent — fires on a topic page view (topic_slug,topic_name).problem_openedevent — fires on a problem page view (problem_slug,problem_id,category_slug,difficulty,is_preview), the step just beforeapproach_committed.credit_consumedevent — fires server-side when a finite AI-review credit is actually spent (paywall on, not exhausted), withremaining. Closes the gap betweenai_review_receivedandquota_exhaustedfor credit-burn-rate analysis.TrackEventclient component (app/_components/TrackEvent.tsx) — fires a one-shot PostHog event on mount so server components can emit page-view-style funnel events.
Fixed
package-lock.jsonversion sync — bumped to matchpackage.json(the lockfile had drifted to 0.3.5 while package.json was 0.3.6).
[0.3.6] — 2026-06-22 — Preferred coding language persistence in UserPreference
Added
preferredCodingLanguagepreference column — Added database-backed storage of the user's preferred coding language in theUserPreferencetable to avoid ambiguity with future localized spoken language (locale) preferences.updatePreferredCodingLanguageserver action — A lightweight server action to update the user's preferred coding language from the workspace environment dynamically.- Coding workspace and settings page synchronization — The settings page form now includes a select dropdown to view and update the preferred programming language. The coding workspace initializes Monaco's language from this DB preference if set (falling back to localStorage) and automatically updates it in the database when the user changes it in the coding pane.
[0.3.5] — 2026-06-22 — Product analytics: value-moment & paywall instrumentation (P0)
Added
ai_review_receivedevent — the activation "aha" moment. Captured server-side on every run withsuccess(coaching actually generated),failure_mode,passed,attempt_num, pattern guess vs normalized, andquota_remaining. Previously onlyattempt_run_completedfired, which couldn't tell whether the user got real coaching.quota_exhaustedevent — fired when a free user hits the credit wall, the highest-intent monetization trigger (was previously unmeasured).upsell_shownevent — the in-workspace quota-wallUpsellCardimpression (the in-product paywall surface had zero tracking).pattern_ownedevent — the North-Star crossing. NewPatternService.detectNewlyOwnedPatterndetects, statelessly, when an attempt first tips a pattern into "owned" and emits the event once per crossing.checkout_initiatedsurfaceproperty — now distinguishesworkspace_quota_wallvspricing_page(added to both theUpsellCardand pricing-page paths), enabling per-surface conversion analysis.
[0.3.4] — 2026-06-22 — Onboarding credit eligibility & webhook idempotency fixes
Added
- Release-discipline CI gate — a
Version & Changelogworkflow (.github/workflows/version-changelog.yml) +scripts/ci/check-version-changelog.shfail any PR intomainunlesspackage.jsonis bumped (valid, greater SemVer) andCHANGELOG.mdhas a matching## [<version>]section. Make it a required status check in branch protection to enforce it. An emergencyskip-releasePR label bypasses the gate for hotfix rollouts.
Removed
- Stale
VERSIONfile — a vestige of the old gstack/ship4-part versioning, frozen at0.3.1and read by nothing since the project adopted standard SemVer inpackage.json(commit1ce3de2).package.jsonis now the single source of truth.
Fixed
- Free-tier credit eligibility —
findEligibleGrantsno longer excludes finite credit grants. A malformedNOT: { AND: [...] }clause collapsed toamount IS NULL, so the 5 signup credits (and any finite grant) were never returned when the paywall was enabled. Removed the clause; exhausted grants are still filtered by callers and byatomicReserve'sWHERE used < amountguard. - Duplicate signup side effects — the Clerk
user.createdwebhook now sends the welcome email and emits theuser_signed_upanalytics event only on first provisioning, guarding against duplicate webhook deliveries (svix retries). - API routes no longer throw across the boundary —
POST /api/attempts/[id]/runandPOST /api/attemptsnow map unexpected errors (includingAttemptConflict) to the standard JSON error envelope instead of surfacing a raw 500. - Attempt-creation rate limiting — added a
STANDARD_ROUTE_LIMITSpreset and applied it toPOST /api/attempts, which was previously unthrottled. - Entitlement observability —
ensureSignupGrantnow logs when thesignup_v1grant cannot be provisioned (inactive/expired campaign) or throws, instead of failing silently.
[0.3.3] — 2026-06-20 — Workspace state, language parity & historical attempt fixes
Added
- Monaco Editor model isolation — Bound the editor's key and path props to a unique string containing the problem slug and attempt identifier, preventing Monaco's global model cache from retaining and leaking past attempt code into the active workspace.
- Stopwatch static duration for past attempts — Passed the calculated elapsed duration (
completedAt-startedAt) to the stopwatch to render static time taken for past attempts instead of displaying00:00. - Selected language persistence — Synchronized selected languages to
localStorageunderdefault_language, ensuring that the preferred language defaults correctly for new attempts and page reloads. - Dynamic template loading on language change — Added dynamic starter code template loading for non-seeded languages. Implemented GET
/api/judgeAPI endpoint to translate and cache missing starter codes on demand, complete with a visual progress overlay while loading.
Fixed
- Missing AI reviews and test results in historical attempts — Fixed a bug where test results, console outputs, and AI reviews were not displayed when viewing past attempts in the workspace (fixed by reconstructing
RunResultfrom Prisma JSON cases, and setting a dynamickeyon the workspace based on the attempt identifier to force React to unmount and reset all states). - Results layout rearrangement — Swapped the order of the AI score graph (radar chart) and the before/after retry delta component in the results pane to show the score graph above the delta.
[0.3.2] — 2026-06-20 — Server-Side Execution Judge & GTM Launch Features
Added
- Managed Judge0 CE API Integration — Migrated the server-side code execution engine from self-hosted Piston to a managed Judge0 CE API (via RapidAPI) with base64-encoded request/response sandboxing, supporting isolated code execution for 9 programming languages: Python, JavaScript, TypeScript, C, C++, Java, Kotlin, Rust, and Swift.
- Judge Metadata & Harness Schemas — Added database migrations to record execution metrics (
memoryKb,judgeVersion) onAttemptand seed language-specific wrapper testing code (harnesses) onProblem. - Judge Route Rate Limiting — Configured
JUDGE_ROUTE_LIMITSsliding-window limits (10/min per user, 30/min per IP) to prevent denial-of-service abuse on the code execution endpoint. - Anonymous Pattern Cached Views & Preview Mode — Workspace pages at
/problem/[slug]support preview mode for unauthenticated visitors, disabling editing and code runs and displaying sign-up CTAs./patterns/[patternId]pages display cached-only views. - Dynamic sitemap & robots.txt — Dynamic
/sitemap.xmland/robots.txtroutes to allow public crawling of static and DB paths while disallowing private routes. - Dynamic Open Graph Images — Next.js metadata routes at
/patterns/[patternId]/opengraph-image.tsxand/problem/[slug]/opengraph-image.tsxrender custom dynamic OG cards. - Landing Page Refinement — Sleek landing page additions at
app/page.tsxincluding structured pricing cards, testimonials, and footer links. - Campaign & Promo Route — Seeding of
launch_week_2026promotional campaign, and a dynamic/promo/[slug]route enforcing authentication/claims. - Welcome Email Webhook — Integrates Resend in the Clerk
user.createdwebhook to automate welcome emails. - Product Hunt Launch Draft — Copwriting draft added at
marketing/product_hunt_launch.md. - Per-Attempt History View — Users can now view past completed attempts in read-only mode by visiting
/problem/[slug]?attempt=<attemptId>. This mode blocks Monaco editor inputs, disables language changes, stops the stopwatch, shows a custom past-attempt banner, and changes the ResultsPane CTA to "Resume active attempt". - In-Progress Workspace Resume — Auto-saves code drafts debounced (500ms) during the
CODINGphase toPATCH /api/attempts/[id]/code-draft. If a user reloads the workspace on an uncompleted attempt where the approach is locked, the workspace resumes straight to theCODINGphase with Monaco populated with the draft code.
Fixed
- Public Routes Access — Exposed
/sitemap.xmland/robots.txtas public routes in middlewareproxy.tsto prevent unauthenticated crawler redirects to Clerk login. - Typecheck Circular Reference — Resolved a pre-existing TypeScript circularity reference error in
lib/rate-limit/postgres.provider.test.tswhere typing mock variables withtypeof txinside callback parameters caused compiler type loops. - CSP Whitelisting & Prefetch CORS Redirects — Added permissions for
accounts.dsamind.comand*.vercel.liveinsidenext.config.ts, exempted static assets and/ingestroutes inproxy.ts, and implemented dynamic client-sideLinkrouting to/sign-inwhen logged out to avoid CORS prefetch redirects. - Custom Auth Routes — Added local
app/sign-inandapp/sign-upcatch-all pages to prevent 404s when prefetching or visiting these paths. - Typecheck Compilation — Declared
useroutside thetry/catchblock inapp/layout.tsxand passedisAuthenticatedto<FinalCTA>and<Footer>inapp/page.tsxto fix compiler type errors. - New Relic browser RUM injection moved from a hand-written
<script dangerouslySetInnerHTML>in<head>tonext/script's<Script strategy="beforeInteractive" dangerouslySetInnerHTML>, matching the existing theme-init script pattern. See ADR 0009 for the residualdangerouslySetInnerHTMLtrade-off (server-generated content, not user input). - Judge Service Tests Out of Sync with Judge0 Migration —
lib/features/judge/judge.service.test.ts'sexecutesuite mockedglobal.fetchwith stale Piston-shaped responses, left over from before the Judge0 migration. SincejudgeService.executecallsJudge0Client.submitdirectly, the mock was never intercepted and the suite failed in CI. Updated the suite to mockJudge0Client.submitwith Judge0-shaped (base64-encoded,status: {id, description}) responses.
[0.3.1] — 2026-06-13 — M3 Observability + Rate-limit refactor
Added
- PostHog analytics — 12 events instrumented across the user journey:
user_signed_up,attempt_created,attempt_run_completed,approach_committed,code_run,self_score_saved,coaching_retry_clicked,pro_plan_activated,checkout_initiated,checkout_session_completed,subscription_upserted,subscription_canceled. Client-side viainstrumentation-client.ts, server-side vialib/posthog-server.tssingleton. All server events use Clerk user ID asdistinctIdfor consistent identity across client and server. - New Relic APM —
instrumentation.tsloads the New Relic agent on server startup (Node.js runtime only).newrelic.jsconfigures EU datacenter (collector.eu01.nr-data.net). - New Relic Browser RUM —
lib/newrelic-browser.tsinjects browser timing header viadangerouslySetInnerHTMLin root layout. Guarded byNEXT_RUNTIME === 'nodejs'. - Pluggable rate-limit architecture —
lib/rate-limit/replaces the old single-file rate-limit stub.RateLimitProviderinterface withPostgresRateLimitProvider(default, always active) andUpstashRateLimitProvider(activated whenUPSTASH_REDIS_REST_URL+UPSTASH_REDIS_REST_TOKENare set).RateLimitEntryPrisma model for the Postgres sliding window. - Rate-limit advisory lock — Postgres provider uses
pg_advisory_xact_lockinside the transaction to prevent concurrent burst bypass under READ COMMITTED isolation.
Fixed
- Rate-limit 429 response body now matches API contract (
{ ok: false, error: { code: 'TOO_MANY_REQUESTS', message: '...' } }). Retry-Afterheader is always included in 429 responses (falls back towindowSeconds).- IP extraction prioritises
x-real-ip(platform-set, not user-spoofable) overx-forwarded-for. - PostHog
NEXT_PUBLIC_POSTHOG_HOSTdefault changed from EU to US to match/ingestrewrite target. subscription_createdPostHog event renamed tosubscription_upserted(fires on both create and update).- Upstash provider no longer adds a redundant
rl:key prefix (aligned with Postgres provider).
[0.2.2] — 2026-06-13 — v0.3 perception layer + workspace QA
Added
-
post-mergegit hook (scripts/git-hooks/post-merge). Runsnpx prisma generateautomatically when a merge brings in changes toprisma/schema.prisma, and prints a warning pointing tonpm run db:migrate/npx prisma migrate deploywhenprisma/migrations/changes. Self-installs via apostinstallnpm script (scripts/install-git-hooks.mjs) that setscore.hooksPath— zero manual setup per clone. Motivation: after PR #9 landed schema changes on main, developers who pulled were left with a stalelib/generated/prisma/(missingAttempt/PatternNotedelegates), causing a runtime "Cannot read properties of undefined (reading 'findMany')" on/dashboard. -
Repository governance system: layered
CLAUDE.md+AGENTS.md,decisions/ADRs,CHANGELOG.md, scopedCLAUDE.mdforprisma/,prisma/problems/,app/api/. See ADR-0001. -
.claude/skills/project skills:dsa-implement-feature,dsa-add-api-route,dsa-migrate-schema,dsa-audit-governance,dsa-verify-ship,dsa-capture-work. -
gstack skill suite — added the routing section + skills list to root
CLAUDE.md. -
Workspace QA sprint — PR2 (UX/state): Signals pill moved to
position: fixedat the viewport level inProblemWorkspace(gated onattemptId !== null), escaping all pane flex/scroll/overflow constraints permanently. "Start next attempt" now resets the editor tostarterCodeso each attempt begins from a clean slate. -
v0.3 perception layer — PR1 (data + AI infra):
Modeenum (LEARNING | INTERVIEW) onAttempt, defaultINTERVIEW(preserves v0.2 single-shot semantics for existing rows).AttemptScore.scoreConfidenceJSON column — AI-emitted per-metric confidence (1-5) for the subjective metrics (patternRecognition,skeletonFluency,edgeCases,confidenceAfter).PatternNotemodel — globally cached AI-generated pattern teaching notes, one canonical row per pattern slug, lazy-generated on first LEARNING-mode access.lib/features/pattern/pattern-note.{repo,service}.ts—getOrGenerate(cache-first, AI-fallback) +findCached. Canonical-slug ↔ display-name map covers all 20 patterns from v0.2 PRD §11 taxonomy.lib/ai/{agents,prompts}/pattern-note.{agent,prompt}.ts— Anthropic-backed agent; default modelclaude-sonnet-4-6, configurable viaDSA_PATTERN_NOTE_MODEL.GET /api/pattern-notes/[patternId]— auth-gated route, zod-validated snake_case patternId, returns{ content, generatedAt, model, cached }.prisma/scripts/pregen-pattern-notes.ts+npm run db:pregen-notes— one-off pre-generation script for the 5 priority patterns (two_pointer, sliding_window, binary_search, dp_linear, tree_dfs_bottom_up).- AI scoring fills all 8 metrics + scoreConfidence (T6 + T24). Coaching prompt rubric expanded to score
patternRecognition,skeletonFluency,edgeCases,confidenceAfterfrom signals + code + approach text + timing, alongside the existing observable metrics (correctness,timeRatio,complexityAchievement,planCodeAlignment). The agent also emits ascoreConfidencemap — its own 1-5 confidence in each of the four subjective scores. Persisted toAttemptScoreon finalize and onRetryCoachingButtonre-run.SelfScoreFormstays in the UI for now (deletion lands in PR2 with the page restructure).
-
v0.3 perception layer — PR2 (in progress):
- Signal toolbar simplification (T3). Deleted the
SignalContextInputsub-component. Signals are now pure one-tap — no "what's blocking you?" affordance. (Server discarded the value anyway, and a text input at the moment of cognitive breakdown contradicts the product principle of zero friction during breakdown.) - Structured 4-section coach output (T4 + T17). Coaching agent now emits three discrete text sections (
gotRight→divergence→nextStep) via the existing tool call, plus thefailureModeenum as the fourth visible section.CoachingNoterenders all four with the first ("What you got right") getting heavier visual weight via emerald-500 left-accent — coach, not critic, per design D4. Structured sections persist toattempt.metadata.coachingSections; the flattened concatenated string still writes toattempt.coachingNotefor backward compat with v0.2 rows. - Single-page progressive disclosure (T2 + T5 + T16 + Eng-D2). The problem page (
/problem/[slug]) is now the single home for the entire attempt lifecycle: Approach → Code → Tests → AI Review → Start Next Attempt. The dedicated review route (/attempt/[id]/review/page.tsx) is deleted; old bookmarks 404 (Eng-D2 = C). Each section reveals progressively as the previous CTA fires; locked sections (Approach after Write Code, Code after Run) render at full contrast with a small 🔒 + timestamp badge (T16 — settled, not disabled, per design D3). The Review section renders inline using the same reusable components from_components/(CoachingNote, SignalTimeline, EdgeCaseComparison, RetryDelta) plus a new submitted-code panel (T5) and an AI-score chip row showing all 8 metrics as 1-5 dots (timeRatio shown as0.92×float). "Start Next Attempt" resets the page in-place for a fresh attempt on the same problem. The/runAPI now returns the full review + previous-attempt summary inline so no follow-up fetch is needed. - Mode selector (T2 partial / A2 deferred). A segmented
[ Learning | Interview ]control sits at the top of the problem page; the chosen mode is persisted onAttempt.mode(defaultINTERVIEW). The mode-divergent iterative behavior from A2 (LEARNING allows multiple Runs before user-triggered finalize) is deferred to PR2.1 — for now both modes behave identically (one-shot lock on Run per D9). The selector locks in place once the attempt starts so the captured mode is stable. Mode visual distinction (D2): active section gets a subtle warm tint in LEARNING and stays cool zinc in INTERVIEW. A one-line descriptor under the selector explains what each mode means. - Approach panel — whiteboard restyle (D3 = A literal). All six measurement fields stay (pattern, steps, time, space, confidence, edge cases — load-bearing per CrackingInterviews/CLAUDE.md). Presentation reshaped: conversational question headings per block ("Which pattern is this?", "Sketch the algorithm in 2-3 lines."), generous block spacing, edge cases demoted to a collapsible
+ Edge cases (optional)link, redundant "Show problem description" toggle removed (the description stays visible in the left pane on desktop), CTA renamedWrite Code →to match the page state machine. - P2002 race fix. Multi-keystroke
onAttemptInteractwas firing parallelPOST /api/attemptscalls before React state could mark one in-flight. The unique constraint(userId, problemId, attemptNum)would then fail. Switched the in-flight guard from React state to a synchronoususeRef, with an await-loop fallback for parallel callers waiting on the first call's completion. Server-side:createAttemptRecordis now idempotent (returns the existing in-progress attempt instead of destroying it viadeleteMany).
- Signal toolbar simplification (T3). Deleted the
-
Pattern Notes — AI-Nudged Pattern System.
/patterns/[patternId]pages surface AI-generated contract-first teaching notes (Mental Model → Templates → Rules → When to Use → Edge Cases → Drill). Nudge card (PatternNudgeCard) fires post-attempt in both the desktop (ReviewPane) and mobile (MobileReviewSection) render paths whenfailureModeispattern_mismatchorapproach_voidand the pattern maps to a known slug. Reuses the existingpatternNoteService.getOrGenerate()andCoachingResult.normalizedPattern— no second LLM call.taxonomyNameToPatternIdadded topattern-note.service.tsto bridge the Title CaseAttempt.patternNormalizedvalues to snake_case slugs. Routes are public by default (no Clerk middleware change). Visualizer and DB-backed personalization deferred to Phase 2. -
v0.3 perception layer — PR3 (in progress, hygiene + polish):
- Light/dark theme toggle (T8). Class-based dark mode (Tailwind 4
@custom-variant), cookie-persisted (theme=light|dark, SameSite=Lax, 1yr). Layout reads the cookie server-side and sets.darkon<html>before render; head-script fallback honorsprefers-color-schemefor first-visit users without the cookie. ThemeToggle button in the header (sun/moon glyph, accessible label) flips both the class and the cookie.prefers-reduced-motionCSS hook added to globals.css. - Clickable problem rows (T9 part 1).
ProblemListrows now navigate as a whole — typinguseRouter().push()on row click, with a guard that skips when the user clicks the inner title<Link>(preserving keyboard nav + right-click + screen-reader path). - Back-link contrast (T9 part 2).
text-zinc-500→text-zinc-700 dark:text-zinc-400across all top-of-page back-links (topic, problem, attempts pages). WCAG AA contrast on resting state; the previous styling was failing on light mode. - Uniform dashboard cards (T10 part 1).
min-h-[170px]on topic cards; Link wrapper nowblockso the article fills the link's full height. Descriptions still useline-clamp-2so taller content doesn't expand a single card past its neighbors. - Regression fix.
app/problem/[slug]/attempts/page.tsxwas linking each historical attempt to/attempt/[id]/review, which 404s after PR2's Eng-D2 deletion. List rows are now static (no link); per-attempt deep-linking will return via a?attempt=<id>query param in a follow-up.
- Light/dark theme toggle (T8). Class-based dark mode (Tailwind 4
-
Workspace QA sprint — radar chart (issue 7):
ScoreRadarreplacesAIScoreChipsinResultsPane. Scores are now visualized as a radar chart (recharts@^2.15.4, dynamic import withssr: false). Eight axes: correctness, time ratio, complexity, plan↔code alignment, pattern recognition, skeleton fluency, edge cases, confidence after.timeRatio(a float) is normalized to 1–5 viaclamp(5 / v, 1, 5);v = 0maps to 5 (no target set). Nullscore(coaching failed) renders an "AI analysis not available" placeholder instead of the chart.edgeCasesadded toMETRIC_LABEL. The array had 7 entries;edgeCaseswas missing. Now all 8AttemptScoredimensions are covered.
-
Workspace QA sprint — AI behavior (issues 1, 2, 3, 6):
- AI gate on syntax errors (issue 1).
finalizeRunnow skips the coaching API call whenrun.status === "ERROR"— syntax/runtime crashes are not coaching opportunities.coachingErroris set to"skipped:error"instead.runStatusis persisted toattempt.metadata(JSON column, no migration needed) soretryCoachingcan also gate on it without re-reading run results. - AI loading indicator split (issue 2).
runninginProblemWorkspaceis now two phases:testRunning(browser test execution) andreviewLoading(AI POST).ResultsPaneshows Tests/Console tabs immediately after tests finish and renders an animated skeleton in the Review tab while the AI call is in flight.reviewLoadingis skipped for ERROR runs. approach_errorfailure mode (issue 3). New mode added toFAILURE_MODESinlib/dsa-taxonomy.tsfor algorithmically-wrong approaches (wrong search direction, incorrect loop invariant, etc.). Coaching prompt divergence section now checks both implementation drift and algorithmic correctness of the approach.CoachingNotelabel added; TypeScript exhaustiveness check onFAILURE_MODE_LABELenforces completeness.- Clean-pass brevity (issue 6). Coaching prompt updated with a 2-sentence budget exception for clean passes and a CLEAN PASS SHORT-CIRCUIT instruction — prevents verbose output when the attempt passed cleanly with no drift.
- AI gate on syntax errors (issue 1).
-
Fix: ERROR run no longer locks the code editor.
setCodeLocked(true)is now deferred until after the browser test completes and the status is confirmed non-ERROR. Forstatus === "ERROR"the editor stays unlocked, the Run button stays visible, and the server POST is skipped — the attempt remains in-progress so the next successful run finalises it normally. Fixes the regression introduced by PR #51's AI-gate (issue 1), where skipping coaching on ERROR still left the user unable to edit their code. -
Fix: AI coach now runs for case-level runtime errors. The "ERROR" status is overloaded in the browser runner: it covers both true syntax/compile errors (worker fails to parse,
results = []) and case-level runtime exceptions (function ran but threw inside a test case,results.length > 0). The coach gate — in bothuseAttemptLifecycle.ts(client) andattempt.service.ts(server) — now discriminates onresults.length === 0rather thanstatus === "ERROR". Result: all tests throwing a runtime error (e.g.null.length, wrong return type) now correctly lock the editor and trigger AI coaching.retryCoachinggate also updated: now checkscases.length === 0instead of the stalerunStatus === "ERROR"metadata field, which is more robust and backward-compatible.
Changed
- Migration history note: shadow-DB validation is broken because
20260511223558_add-attempt-signal-score-replace-submissions-userproblempredates the20260512000000_baselineit depends on. The v0.3 migration was authored viaprisma migrate diff+ applied withprisma migrate deployto bypass shadow validation. Future schema changes will need the same workaround until the migration history is repaired. - ProblemSolver lint fixes. Removed
useEffectthat calledsetMidTabsynchronously (violatedreact-hooks/set-state-in-effect);setMidTab("CODE")is now called at each phase-transition callsite (approach→coding, run→reviewing, review received). RemovedheadingRefprop fromTestsBodyandReviewPane(was declared but never passed a value — dead prop). Pane tab labels shortened to "Approach" / "Code" (numbers dropped). - Vitest type fix in
attempt.service.test.ts. Replacedjest.Mocked<AttemptRepository>(Jest global namespace) withMocked<AttemptRepository>from"vitest"— the project uses Vitest; the Jest shim was hiding a real type boundary. - Landing page copy. Removed "Phase 1 / 2 / 3 / The north star" kicker labels from feature sections; trimmed the "Twelve owned patterns" subtitle pending a content rewrite.
dsa-audit-governanceskill. Added explicit 5-phase execution order (gather → static → semantic → historical → synthesis), tool/fallback policy for shell vs. file-read vs.Unable-to-Verify, expanded local CLAUDE.md budget rules with explicit glob patterns, monorepo workspacepackage.jsonhandling notes, and clarified CHANGELOG entry format (multi-line bullets allowed; standalone prose paragraphs are not).- Governance dead-pointer sweep (round 1). Fixed 8 stale references found by
/dsa-audit-governance:lib/auth.ts→userServicefromlib/features/user/user.service.ts(CLAUDE.md, AGENTS.md, app/api/CLAUDE.md); removed deletedlib/submissions.tsandlib/problem-json.tsfrom lib description (→lib/features/problem/problem.types.ts); updatedapp/api/project map toattempts/+pattern-notes/; replacedsubmissions/route.tsguidance inapp/api/CLAUDE.mdwith the liveattempts/[id]/runpath; removed duplicated "No DB in client" rule fromprisma/CLAUDE.md; added missingdsa-capture-workto the CLAUDE.md skills list. Filed #49 to track legacyProblemSolver.tsxdeletion. - Governance dead-pointer sweep (round 2). Fixed 5 remaining gaps found by post-workspace-QA
/dsa-audit-governancere-run:prisma/CLAUDE.mdstill referencedlib/auth.ts:getCurrentUser(→userService.getCurrentUser()inlib/features/user/user.service.ts) and the deletedUserProblemcomposite key example (→Attempt @@uniquewith ADR-0002 note); CLAUDE.md project map missingapp/attempt/,app/patterns/,lib/ai/, andlib/dsa-taxonomy.ts;app/api/CLAUDE.mdroute list updated to enumerate v0.3 sub-routes ([id]/approach,[id]/score,[id]/signals);prisma/CLAUDE.mdquery-patterns rule rephrased to be an elaboration (not a restatement) of the root singleton rule.
[0.3.0] — 2026-06-11 — v0.4 Market Readiness (M0+M1+M2)
Added
- Stripe Checkout integration:
POST /api/checkoutcreates a Stripe Checkout Session (redirect-to-Stripe). Supports two plans: Pro ($9/mo, recurring) and Interview Pass ($29, one-time 90-day unlimited grant).client_reference_idandmetadata.userIdwire the session back to the local user record. - Stripe webhook handler (
POST /api/webhooks/stripe): raw-body signature verification viastripe.webhooks.constructEvent. Handlescheckout.session.completed(Interview Pass → 90-day CreditGrant; subscription → Subscription row upsert),customer.subscription.created/updated(re-fetches authoritative state from Stripe — order-agnostic), andcustomer.subscription.deleted(marks canceled). 200 returned even on handler errors to prevent Stripe retries causing double-grants. - Billing portal redirect (
GET /api/billing-portal): redirects authenticated Pro subscribers to the Stripe Customer Portal. Falls back to/pricingif no active subscription. /pricingpage: single Pro card (copy leads with loop value, not feature list), Interview Pass card, quiet free-tier text ledger, FAQ (6 questions). Anti-slop constraints: zinc surfaces, Geist Mono price, text ledger under 11px uppercase headers. Signed-in visitors see their remaining credits. Already-Pro visitors see billing management link./checkout/successpage: polls/api/quotaat 1.5s intervals untilremaining === null(Pro active) or 15s timeout elapses. Shows activation spinner → Pro confirmation → fallback message with support email if webhook is delayed.UpsellCardcomponent: shown when quota is exhausted. Achievement recap ("5 free reviews used") → honest review skeleton (section labels as empty outlines, no blur) → "Upgrade to Pro" checkout CTA → "Keep practicing" pattern-note link. Per design findings 9, 11, 14.- Pre-spend quota surfacing in the code pane: amber "Last free review" label under the Run button at 1 remaining; grey "Tests run free · no AI review" note when quota is already exhausted.
- Quiet "N reviews left" line after each rendered review for free users (amber at 1).
lib/stripe.ts— lazy Stripe client singleton. Throws a descriptive error ifSTRIPE_SECRET_KEYis missing at call time (not at module load, sonext buildis safe).STRIPE_PRICE_PRO_MONTHLYandSTRIPE_PRICE_INTERVIEW_PASSenv vars documented in.env.example.CONFIGURATION_ERRORandSTRIPE_ERRORerror codes added tolib/api-response.ts.- 7 new Prisma models (one migration:
20260611000000_v0.4_entitlement_billing_prefs):CreditGrant,UsageEvent(withUsageStatusenum:RESERVED | COMMITTED | REFUNDED),Campaign,Subscription,UserPreference,Feedback,AiCallLog. lib/features/entitlement/:entitlement.types.ts(typedEntitlementFeature,QuotaSnapshot,ConsumeResult);entitlement.repo.ts(atomic reserve via rawUPDATE WHERE used<amount,commitOrRefund,upsertCampaignGrant);entitlement.service.ts(check,reserveCredit,settleCredit,grantForCampaign, lazyensureSignupGrantfallback).- Three-transaction pattern in
attempt.service.finalizeRunandretryCoaching: txn1 = atomic credit reserve, LLM call outside any transaction (avoids pgbouncer pool exhaustion), txn2 = commit on success / refund on LLM failure. Credits are never lost to our errors. - Clerk webhook (
POST /api/webhooks/clerk): raw-body svix signature verification.user.created→prisma.user.upsert+entitlementService.grantForCampaign(userId, "signup_v1").user.deleted→ cascading deletion of all user data. GET /api/quota: returns current user's quota snapshot forMeterChip.MeterChipcomponent in the header: shows "N reviews left" (amber at 1), "0 reviews left", or "Pro". Refreshes on window focus.scripts/backfill-signup-grants.ts: idempotent--dry-runcapable script to provisionsignup_v1grants for all existing users.signup_v1Campaign seeded inprisma/seed.ts(feature:ai_review, grantAmount: 5,endsAt: null— evergreen).- 14 unit tests for
EntitlementService(check, reserveCredit, settleCredit, grantForCampaign). Vitest alias@/lib/generated/prisma → lib/__mocks__/prisma-stub.tsso tests run withoutprisma generate. DSA_PAYWALL_ENABLEDenv flag: instant rollback to v0.3 behavior (no quota checks) with zero redeploy.- CI workflow (
.github/workflows/ci.yml): build + lint + problems:lint + vitest on every PR and main push. Postgres service container for integration tests usingprisma migrate deployon a fresh DB. lib/env.ts— zod-validated environment contract. Server vars validated lazily (safe fornext buildin CI without runtime secrets); public vars validated at boot. Fail-fast error messages name the missing variable.lib/logger.ts— level-aware structured logger. JSON output in production (parseable by Sentry/CloudWatch), coloured human-readable output in development. Replaces bareconsole.*in app code.lib/rate-limit.ts— per-user + per-IP rate limiter on AI spend routes. Uses Upstash Ratelimit whenUPSTASH_REDIS_REST_URLis configured; skips gracefully in dev/test. Applied toPOST /api/attempts/[id]/runandGET /api/pattern-notes/[patternId].- Security headers (
next.config.ts):X-Frame-Options: SAMEORIGIN,X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policy(no camera/mic/geo), andContent-Security-Policy-Report-Only(Report-Only in M0; becomes enforced in M1 after tuning against reports). scripts/repair-migration-history.ts— one-time script to rename the baseline migration entry in_prisma_migrationson existing production databases after the timestamp reorder.- ADR-0003 (Sentry + PostHog observability stack), ADR-0004 (Stripe Checkout payments provider), ADR-0005 (campaign-based credit grant entitlement architecture).
Changed
proxy.ts: added/api/checkout/*,/api/billing-portal/*,/api/quota/*, and/checkout/*to the protected routes list.
Fixed
proxy.tsdead route matchers: removed references to/api/submissionsand/api/problems(routes removed in v0.2). Replaced with the live protected route list (/api/attempts/*,/api/pattern-notes/*,/attempt/*,/settings/*,/profile/*) and an explicit public route list (/,/pricing,/terms,/privacy,/changelog,/help,/patterns/*,/api/webhooks/*).- Migration history ordering: renamed
20260512000000_baseline→20260510000000_baselinesoprisma migrate deployon a fresh database applies the baseline schema before the add-attempt migration. Unblocks CI integration tests and green production deploys from scratch. .env.example: rewrote from stale SQLite/mock-auth Phase A config to the live Postgres + Clerk + Anthropic stack. Added env var slots for Stripe, PostHog, Sentry, Upstash, and Resend (M1–M4 surfaces).
[0.2.1] - 2026-06-09
Added
- Pattern and complexity dropdowns in the approach gate. Before opening the editor, you can now explicitly select the pattern you intend to use (e.g. Sliding Window, Two Pointers) and your target time and space complexity from fixed dropdowns. Leaving any blank is fine — blank = coached miss, surfaced in the post-run completeness card.
- Complexity shown while coding. The "Your approach" strip visible above the editor now shows the time and space complexity you committed to, with an italic "not set" placeholder if left blank.
Changed
- Approach gate is now the only entry point.
ProblemWorkspaceis promoted to the single renderer for the problem page — the?ws=1flag and the legacyProblemSolvercomponent are gone.
Fixed
- Autosave reset bug on "Start next attempt". The autosave snapshot now correctly includes all five approach fields, preventing a spurious re-save of an empty draft on the new attempt.
Removed
- Free-text extraction pipeline deleted. The regex + planned LLM approach-extraction infrastructure has been removed. Pattern and complexity are now captured deterministically via dropdowns, eliminating latency, cost, and non-determinism.
ProblemSolver.tsxdeleted (1,901 lines). The legacy single-pane component is no longer used.
[0.2.0] - 2026-05-25
Added
- DSA Mind Design System — triptych problem solver. Three-pane split layout (problem description / solver / AI review) with horizontal drag-to-resize handles. Replaces the single-column mobile-only layout with a proper desktop IDE-style workspace.
- Tests dock with vertical drag. After running code, a resizable tests panel docks at 50% of the middle pane. Drag the handle up/down to resize. Hidden while coding so the code editor fills the full pane — appears only once Run is clicked.
- Landing page redesign. Full new marketing page with Hero, three-phase feature sections (Approach / Code / Review), social proof strip, and a closing CTA. Removed Clerk auth component dependencies from the landing route.
RetryDeltametric cards. Four-card grid showing delta values (signals fired, plan↔code alignment, confidence after, time ratio) between the current and previous attempt. Replaced the earlier dumbbell chart.
Changed
bodyheight:min-h-full→h-full. Root fix for the triptych: the body is now viewport-bounded, soflex-1 min-h-0chains propagate correctly through all pane descendants. Previously the body could grow past the viewport, makingflex-1on<main>unbounded.- CoachingNote sections. Four structured sections (What you got right / Where it diverged / Next step) with 2px colored left rails (emerald/amber). Failure mode pill moves inline into the "Where it diverged" heading row.
- Section header tokens. All review page headings standardized to
text-[11px] font-semibold uppercase tracking-[0.08em] text-zinc-500. - SignalTimeline & EdgeCaseComparison. Section headings updated to design system tokens; track background and legend text refined.
- Deleted
CollapsibleDescription.tsx. Component was no longer imported anywhere; description is now passed as a React node prop intoProblemSolver. attempt.service.test.ts— updated assertion to matchfinalizeRun's current return shape ({ reviewUrl, review, previous }).