feat: Grammar Substrate Foundation (Epic #204)#226
Conversation
…oncise length - Reduced summaries to exactly 2 sentences for better readability - Added ReactMarkdown rendering for proper **bold** and *italic* formatting - Updated AI prompt to eliminate hashtags and enforce strict length limits - Shortened all fallback summaries to 2 sentences with markdown emphasis - Enhanced visual hierarchy with darker blue for bold, medium blue for italics - Improved user experience with digestible, punchy content 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Previously all enrollment buttons would show "Enrolling..." when any course enrollment started due to shared isEnrolling state. Changes: - Replace single isEnrolling boolean with enrollingCourseId string - Only disable and show loading state for the specific course being enrolled - Other course buttons remain clickable during enrollment - Improved user experience with accurate button states 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Integrate AudioButton component for AI message playback - Add first-time tooltip with localStorage tracking - Polish UI with Card components to match tutor interface - Enhance onboarding experience with professional audio features 🎯 Marketing Impact: Showcases app polish and audio capabilities ⚡ Minimal Engineering: Reuses existing tutor components 🔊 User Experience: Clear audio guidance with helpful tooltip 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…arding completion - Replace update/insert pattern with proper upsert - Handles existing user profiles gracefully - Eliminates ugly 500 error during demo flow 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix lesson_course_ordering count syntax - Update transform logic to handle correct data structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Change from lesson_course_ordering(count) to lessons(count) in CourseService.getCourses() - Update transform logic to reference course.lessons instead of course.lesson_course_ordering - Resolves courses showing 0 lesson counts on onboarding completion page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…ationship is ready
## PWA Features Implemented ### 📱 App Manifest - Added manifest.ts with native app-like configuration - Configured standalone display mode and splash screen colors - Set orientation, categories, and branding metadata ### 🔄 Service Worker & Caching - Integrated next-pwa with intelligent caching strategies - Font caching: CacheFirst (1 year expiration) - Image caching: CacheFirst (30 days expiration) - API caching: StaleWhileRevalidate (5 min expiration) - Page caching: NetworkFirst for optimal UX ### 🌐 Offline Experience - Created beautiful offline.html fallback page - Auto-connectivity detection and reload - Branded offline experience with feature highlights - Progress preservation messaging ### ⚡ Performance Optimizations - Background sync with skipWaiting enabled - Smart cache management with size limits - Network-first strategy for dynamic content - Cache-first strategy for static assets ### 🎨 Native App Feel - Standalone display mode removes browser chrome - Coordinated theme and background colors (#8b7355) - Educational app categorization for app stores - Portrait orientation for mobile-first experience Ready for installation testing and Lighthouse audit! 🚀 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- When users install the PWA, they want the app functionality, not the marketing landing page - /dashboard will redirect to /login via auth middleware if user not authenticated - Provides optimal UX: existing users go straight to app, new users go to login flow 🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Added icon-512x512.png and apple-touch-icon.png for complete PWA icon set - Added disable: false to force service worker registration in development - All PWA icons now exist for proper installability testing - Production PWA will be fully functional with complete icon support Ready for deployment! 🚀 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix cramped Training Ground buttons on mobile by stacking vertically - Redesign Challenge modal with compact chip-based layout - Convert large stat cards to "Your Bounty" chips (Exercises, Max XP, Est. Time) - Transform "What's Inside" to "Your Challenge" with single-row chip layout - Ensure modal height never blocks Start button on mobile - Add proper flex layout with scrollable content area Resolves mobile usability issues where buttons were cramped and modal required scrolling to reach primary action button. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Change course recommendation logic from A1 difficulty to vibras puras title - Support partial matches for vibras or puras in course titles - Quick fix for ASAP deployment while proper course mapping is developed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix exercise content overflow by adding proper scrolling support - Change flex layout from centering to scrollable with overflow-y-auto - Add proper padding to Check Answer buttons in all exercise types - Ensure buttons are visible and not cut off at bottom of screen - Update container sizing for better mobile/responsive behavior Fixes: - Multiple Choice Check Answer button not visible - Unable to scroll in game section when content overflows - Button container padding improvements across all exercise types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
✨ New Features: - Generate button in lesson metadata with AI overview creation - Modal interface for overview type selection and editing - Dynamic overview generation based on lesson content 🔧 Implementation: - API endpoint: POST /api/lessons/[id]/generate-overview - Overview types: general, readings, exercises, dialogs, grammar - Markdown-formatted output with engaging content - Replace/append modes for existing content 📁 Files Added: - app/api/lessons/[lessonId]/generate-overview/route.ts - components/author/GenerateOverviewModal.tsx - lib/content-generation/overview-generator.ts 📝 Files Modified: - components/author/MetadataPanel.tsx - Added Generate button - components/author/LessonEditor.tsx - Pass lessonId prop 🎯 Benefits: - Saves authors time creating engaging lesson descriptions - Consistent, professional overview formatting - Content-aware generation based on actual lesson data 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
Transform README from standard tech documentation to sophisticated literary prose befitting the platform's academic aspirations. Changes: - Replace promotional language with analytical descriptions - Remove emoji clutter and em-dash overuse - Adopt New Yorker-style intellectual tone - Focus on actual capabilities rather than aspirational features - Maintain technical accuracy while improving readability - Present architecture decisions as deliberate choices - Emphasize pedagogical philosophy over marketing claims The documentation now reflects the platform's serious academic intent rather than typical startup marketing conventions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Allows skipping expensive builds for documentation-only changes. Simply add [skip ci] anywhere in commit message to skip: - Container builds (build.yml) - Staging deployments (staging-deploy.yml) - CI checks for PRs (ci.yml) Saves compute time and resources for README updates and other non-functional changes. [skip ci]
…rols Add comprehensive push notification system with admin testing endpoints, scheduled daily/welcome notifications, and word-of-day generation API. Enhanced landing page with client-side auth integration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add responsive mobile-first dashboard layout - Implement constellation chart for learning progress - Add book Lottie animation for vocabulary visualization - Create typewriter message component for engagement - Add mobile stats chart with responsive design - Implement training ground CTA with improved UX - Add new dashboard wrapper and loader components - Update API routes for lesson completion tracking - Add user API endpoints for dashboard data - Configure Lottie animations for mandala, panda, book assets 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Downgrade zod from 4.1.12 to 3.23.8 for compatibility - Update workbox service worker bundle hash - Remove old workbox bundle and add new version 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…tifications - Add missing manifest.webmanifest file with proper PWA config - Create PWAInstallBanner component with beforeinstallprompt handling - Build NotificationSettings component for push notification management - Fix profile back button to use proper browser history navigation - Wire PWA components into root layout - Add missing notification API endpoints (vapid, subscribe, unsubscribe) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Replace ugly star emoji loader with beautiful mandala animation - Dramatically reduce spacing between chart selector and constellation - Match spacing symmetry between selector→chart and chart→streak (both mt-1) - Reduce chart height from h-80 to h-72 for better mobile compactness - Add "More charts coming soon!" tooltip to chart selector - Tighten streak indicator spacing from space-y-6 to space-y-3 Mobile dashboard now displays all components in tight, cohesive layout without excessive whitespace between elements. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…settings - Add missing VAPID keys to .env.local for push notifications - Enhance NotificationSettings with language preferences (Spanish/Latin) - Add notification time selection (7 AM - 9 PM options) - Add link to word-of-day page with language selection - Show notification status (enabled/disabled) with clear indicators - Reduce install banner timing to 3 seconds for testing - Fix proper email address for VAPID contact 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Spanish: 'Ver Palabra del Día' - Latin: 'Vide Verbum Diei' - More authentic language experience in PWA
- Replace title attribute with animated tooltip component - Tooltip shows on tap/click with smooth animation - Auto-hides after 3 seconds for better UX - Toggle functionality - tap to show/hide instantly - Proper mobile interaction instead of hover-only 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Created lib/mastra/workflows/wordOfDayGeneration.ts - Implements generateFreshSentences with OpenAI integration - Supports both Spanish and Latin sentence generation - Fixes 500 errors on /api/word-of-day/fresh-sentences - PWA should now work without backend errors
- Change user_preferences to user_wod_preferences table - Change language column to preferred_language - Fix language preference update calls - Should resolve 'Failed to update preference' errors
- Fix OpenAI import from 'openai' instead of '@mastra/core' - Update OpenAI API calls to use proper chat.completions.create - Fix PWA install banner to show without deferredPrompt requirement - Add fallback manual install instructions - Should resolve OpenAI 'not a constructor' errors
- Add icons config to metadata pointing to /logo.png - Update title and description to be language-agnostic 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add script to generate favicon sizes using sharp - Generate 16x16, 32x32, 180x180, 192x192, 512x512 icons from logo - Update layout metadata to use properly sized favicons - Update PWA manifest with correct icon references 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add logo as favicon and navigation branding
Multi-language onboarding support (Spanish, Latin, Icelandic)
## Changes - fix(tts): correct ElevenLabs API key env var name (ELEVEN_LABS_API_KEY → ELEVENLABS_API_KEY) - fix(exercises): make translation direction detection language-agnostic - fix(exercises): add language-aware feedback to Training Ground - fix(ui): add dark mode support to Profile and Flashcards components - fix(ui): eliminate extra scroll space on mobile Closes #129, #130, #131 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…135) ## Changes - feat(flashcards): Cross-deck review mode for practicing cards from all decks - feat(flashcards): XP system with gamification (streaks, levels) - feat(flashcards): Improved review UI with card flipping animation - fix(flashcards): Add missing cloze_count column to flashcards table - fix(flashcards): Make XP animation subtle top-right toast (non-intrusive) ## Migrations - 20251223_cross_deck_practice.sql - 20251223_flashcard_ux_revamp.sql 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Changes - feat(pwa): Service worker for offline support and push notifications - feat(pwa): Install banner for iOS/Android - feat(wod): Add Icelandic language toggle to Word of Day page - fix(wod): Supabase scope bug preventing unauthenticated fetches - fix(wod): Dark mode styling for Word of Day page ## Migrations - 20260107_add_icelandic_to_daily_words.sql ## Notes - Push notifications require VAPID keys in production environment - Supabase email templates for password reset need dashboard configuration 🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Add lesson_stage_type enum (learn, read, practice, review, assess) - Create lesson_stages table for configuring stage progression - Create user_lesson_stage_progress table for tracking user progress - Add RLS policies for stage access control - Add helper functions: calculate_lesson_progress, get_next_stage - Add TypeScript types for stages and progress data - Add lib/stages utilities for stage management Part of Epic #199 - Lesson Stage System 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
#202, #203) Components: - LessonStageViewer: Main container with stage navigation and progress tracking - StageProgressBar: Visual progress indicator with stage dots - StageProgressIndicator: Minimal progress bar for headers - Stage content components: Learn, Read, Practice, Review, Assess Features: - Sequential stage progression with completion tracking - "Pick N of M" exercise selection for Practice stage - Time-based completion criteria for Learn stage - Word click tracking for Read stage - Score-based assessment with retry option Tests: - 20 unit tests for checkStageCompletion function - All stage completion logic tested for each stage type Config: - Added coverage thresholds to vitest.config.ts (ratchet to prevent regression) - Fixed duplicate test script in package.json Part of Epic #199 - Lesson Stage System 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…#223) - Add ViewModeToggle component with classic/hub/stages modes - Integrate LessonStageViewer into LessonViewer for stages mode - Create backfill migration for existing lessons - Fetch and pass lesson stages to LessonViewer - Update hub index to export ViewModeToggle Part of Epic #220 - Stage System Integration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Renamed migrations with numeric prefixes to ensure correct ordering: - 20260107_01_lesson_stage_system.sql (schema first) - 20260107_02_backfill_lesson_stages.sql (backfill second) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests for the 5-stage lesson progression system: - View Mode Toggle: 3-way toggle display and switching - Stages View: Stage progression UI rendering - Learn Stage: Time-based progress tracking - Stage Navigation: Previous/Next button functionality - Progress Persistence: Stage progress indicator display All 7 tests passing: - should display 3-way view toggle in lesson header - should switch between Classic and Hub views - should display stage progression UI when Stages view is selected - should show time-based progress in Learn stage - should navigate between stages using Previous/Next buttons - should show stage progress indicator in header 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ProgressRing SVG component with smooth fill animation
- Position ring in top-right corner, out of content flow
- Show percentage in center, checkmark when complete
- Display elapsed time and target below ring
- Update button text to show countdown ("27s remaining...")
- Update E2E test selectors to match new UI
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…viewer - Add breadcrumb navigation: Course > Lesson > Current Stage - Integrate shimmer/gradient effects matching existing design system - Add gradient accent line at header top - Enhanced visual styling for lesson context and stage header - Update E2E tests to match new header format (numbered circle + label) - Add full stage completion flow E2E test with tutorial overlay handling - Fix test selectors for new stage UI patterns 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## Schema (graph-ready architecture) - Enhanced `grammar_concepts` with language, slug, hierarchy, source_refs - New `lexicon_entries` for language-agnostic vocabulary - New `text_sources` for manuscripts/corpora (sagas, codexes, grammars) - New `text_occurrences` for specific text passages with glossing - Edge tables: `grammar_relationships`, `grammar_lexicon_links`, `lexicon_occurrences` - Helper functions: `get_grammar_tree()`, `get_related_concepts()` ## UI - `/grammar` index page with language tabs (Latin 🏛️, Old Norse ⚔️, Gothic 📜) - `/grammar/[slug]` detail page with breadcrumbs, examples, related concepts - Category filters (morphology, syntax, phonology) - Search functionality - CEFR difficulty badges ## Seed Content ### Latin (from Allen & Greenough, EIEOL) - 6 cases with full paradigms and examples - 3 declensions (1st, 2nd, 3rd) - 2 conjugations (1st, 2nd) ### Gothic (from Wright's Grammar, EIEOL) - Alphabet with Wulfila's script - Noun declension classes (strong/weak) - Verb classes (strong ablaut, weak dental) ### Old Norse (from EIEOL) - Umlaut (i-umlaut, u-umlaut) with sound change tables - Noun declensions with paradigms - Verb classes - Vǫluspá stanza 1 with full grammatical analysis ## Architecture Notes - Designed for future graph DB migration (Memgraph) - Source references tracked for all content - Leipzig glossing format for morphological analysis - Cross-language relationships for comparative linguistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Outlines the strategy for connecting Latin processor output to Grammar Substrate schema: Phase 1: Concept Matching - Map processor GrammarConcept → grammar_concepts.slug - Case/declension/conjugation detection rules Phase 2: Vocabulary Linking - Connect analyzed words to lexicon_entries - Store morphological analysis Phase 3: Text Occurrence Storage - Store passages in text_occurrences - Link tokens to lexicon_occurrences Includes: - UI integration flow (word click → grammar page) - Database queries for cross-referencing - Future Memgraph migration notes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review: Grammar Substrate Foundation (Epic #204)This is a substantial PR implementing a graph-ready grammar architecture. I've reviewed the schema design, API routes, UI components, and implementation patterns. Overall, this is solid foundational work with some areas for improvement. 🟢 StrengthsArchitecture & Design
Database Schema (
|
PR Review - Critical Issues Found🚨 Major Concerns1. Scope Mismatch - PR Title vs. Actual ChangesThis PR is titled "Grammar Substrate Foundation" and claims to implement Epic #204, but contains 100 commits with features completely unrelated to grammar infrastructure:
Impact: This violates fundamental version control best practices. Each feature should be in its own PR for:
Recommendation: Split this into separate PRs:
2. Massive Size - 828,665 Additions Across 100+ Files
Impact:
Recommendation: Industry best practice is 200-400 lines per PR for thorough review. This PR is literally 2,000x that size. 3. Inadequate Test CoverageFor 828K lines and 54 new API endpoints:
Security Risk: Untested API routes handling user data and payments are a major security vulnerability. Recommendation: Each API route should have:
4. Commit Message Quality IssuesSeveral commits have concerning messages: Impact:
Recommendation: Follow conventional commits format:
|
Unit tests (156 tests): - GrammarIndexClient component logic (31 tests) - Grammar concepts schema and validation (50 tests) - Grammar database query patterns (40 tests) - Schema validation and graph-ready patterns (35 tests) E2E tests (Playwright): - Grammar index page navigation and filtering - Grammar detail page content and navigation - Multi-language switching (Latin, Old Norse, Gothic) - Category filtering and search functionality - Unicode/special character handling - Breadcrumb navigation - Related concepts and linked vocabulary - Source references display - Responsive design verification All unit tests passing: 156/156 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pull Request Review: Grammar Substrate Foundation (Epic #204)This is an impressive implementation of a multi-language grammar system with a well-designed graph-ready architecture. Overall, the code quality is high with excellent documentation, comprehensive tests, and thoughtful design patterns. Strengths1. Excellent Database Design
2. Security - RLS Policies
3. UI/UX Quality
4. Test Coverage
5. Code Quality
Issues and RecommendationsCritical Issues1. Missing User Authentication Check in POST EndpointsLocation: app/api/grammar-concepts/route.ts:8-50, app/api/lessons/[lessonId]/grammar/route.ts:53-109 The POST endpoints for creating grammar concepts don't verify user authentication or authorization. Anyone can create grammar content without being logged in. Recommendation: Add authentication checks and role verification before allowing INSERT operations. Also need RLS policies for INSERT/UPDATE/DELETE that check for admin roles. 2. Potential XSS in Markdown ContentLocation: app/grammar/[slug]/page.tsx:218 The grammar concept content is rendered as Markdown without HTML sanitization. If an attacker gains access to create content, they could inject malicious HTML/JavaScript. Recommendation: Use rehype-sanitize plugin with ReactMarkdown to sanitize HTML output. Medium Priority3. Missing Error Handling for Database FunctionsLocation: supabase/migrations/20260107_grammar_substrate.sql:267-335 The get_grammar_tree and get_related_concepts functions don't handle circular references or infinite recursion. Recommendation: Add max depth guards to prevent infinite loops. 4. N+1 Query Problem in Detail PageLocation: app/grammar/[slug]/page.tsx:64-113 The detail page makes 6 separate database queries (main concept, parent, children, outgoing relationships, incoming relationships, linked vocab). Recommendation: Combine into fewer queries using Supabase nested selects for better performance. 5. Missing Slug Generation for Old ConceptsLocation: supabase/migrations/20260107_grammar_substrate.sql:18-28 The migration adds slug as nullable but doesn't backfill slugs for existing concepts. Recommendation: Add a migration step to generate slugs for existing records and make the column NOT NULL. 6. Search Query Injection RiskLocation: app/grammar/page.tsx:48 The search uses .ilike with user input interpolation. Special characters like % and _ are wildcard characters in ILIKE and could cause unexpected behavior. Recommendation: Escape special characters or use PostgreSQL full-text search instead. Low Priority7. Performance: Missing Limit on Grammar Tree FunctionThe get_grammar_tree function could return unbounded results. Consider adding pagination. 8. Type Safety: Missing TypeScript Types for JSONB FieldsThe source_refs, examples, definitions, and cognates JSONB fields should have TypeScript interfaces or Zod schemas for better type safety. 9. Accessibility: Missing aria-label on Search Input10. Code Duplication: Language MetadataThe LANGUAGES constant is duplicated in app/grammar/page.tsx and app/grammar/[slug]/page.tsx. Extract to shared constant file. Performance ConsiderationsGood:
Needs Attention:
Test Coverage AssessmentExcellent coverage with 7 E2E test files and multiple unit tests for grammar queries and schema. Missing tests:
Architecture NotesPositive Patterns:
Potential Concerns:
SummaryThis is a solid, well-architected PR with excellent documentation and test coverage. The main concerns are:
After addressing the critical security issues, this is ready to merge. The foundation is excellent and will scale well as you add more languages and content. Estimated effort to address critical issues: 2-4 hours Great work on this epic! The graph-based approach and scholarly attention to detail (Leipzig glossing, proper citations) sets this apart from typical language learning platforms. 🤖 Generated with Claude Code |
The ORDER BY clause referenced sort_order but it wasn't included in the CTE's SELECT or RETURNS TABLE. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Ensures schema runs before seed: - 20260107_03_grammar_substrate.sql - 20260107_04_seed_grammar_content.sql 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PR Review: Grammar Substrate Foundation (Epic #204)Executive SummaryThis is an exceptionally well-architected PR that establishes a professional-grade linguistic reference system. The implementation demonstrates strong technical decisions, comprehensive testing, and academic rigor appropriate for a language learning platform. Recommendation: ✅ Approve with minor suggestions 🎯 Strengths1. Graph-Ready Architecture ⭐⭐⭐⭐⭐The node/edge separation is exemplary:
File: 2. Comprehensive Indexing Strategy ⭐⭐⭐⭐⭐Every access pattern is covered:
Lines: 3. Academic Source Tracking ⭐⭐⭐⭐⭐The source_refs JSONB DEFAULT '[]'::jsonb -- [{source, section, url}]Citations to Allen & Greenough, Wright, EIEOL demonstrate scholarly rigor. 4. Excellent Test Coverage ⭐⭐⭐⭐⭐
Files:
5. UI/UX Implementation ⭐⭐⭐⭐
Files: 🔍 Issues & SuggestionsCritical 🔴1. API Security Gap: Missing Authentication/AuthorizationFile: The POST endpoint creates grammar concepts without authentication: export async function POST(request: Request) {
// No auth check!
const supabase = await createClient();
// ...
}Issue: Anyone can create grammar concepts, despite RLS being enabled. Fix: Add authentication: export async function POST(request: Request) {
const supabase = await createClient();
// Check authentication
const { data: { user }, error: authError } = await supabase.auth.getUser();
if (authError || !user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
// Optional: Check for admin role
// const { data: profile } = await supabase
// .from('profiles')
// .select('role')
// .eq('id', user.id)
// .single();
// if (profile?.role !== 'admin') {
// return NextResponse.json({ error: "Forbidden" }, { status: 403 });
// }
// ... rest of the code
}Impact: Security vulnerability allowing unauthorized content creation. High Priority 🟠2. SQL Injection Risk in Search QueryFile: if (searchQuery) {
query.or(`display_name.ilike.%${searchQuery}%,summary.ilike.%${searchQuery}%`)
}Issue: While Supabase client libraries typically handle parameterization, this pattern could be risky if Fix: Use Supabase's safer query methods: if (searchQuery) {
query.or([
`display_name.ilike.%${searchQuery}%`,
`summary.ilike.%${searchQuery}%`
].join(','))
}Or better yet, use multiple if (searchQuery) {
query.or(`display_name.ilike.%${searchQuery}%,summary.ilike.%${searchQuery}%`)
}Impact: Potential SQL injection if user input is not properly sanitized. 3. Missing Write Policies in RLSFile: All new tables have only SELECT policies: CREATE POLICY "Anyone can view lexicon entries"
ON public.lexicon_entries FOR SELECT
USING (true);Issue: No INSERT/UPDATE/DELETE policies defined. Without explicit write policies, writes will fail even for admins (when RLS is enabled). Fix: Add admin write policies: -- Example for lexicon_entries
CREATE POLICY "Admins can insert lexicon entries"
ON public.lexicon_entries FOR INSERT
TO authenticated
WITH CHECK (
EXISTS (
SELECT 1 FROM public.profiles
WHERE id = auth.uid() AND role = 'admin'
)
);
CREATE POLICY "Admins can update lexicon entries"
ON public.lexicon_entries FOR UPDATE
TO authenticated
USING (
EXISTS (
SELECT 1 FROM public.profiles
WHERE id = auth.uid() AND role = 'admin'
)
);Impact: Content management features may not work even for authorized users. Medium Priority 🟡4. Inconsistent Slug ValidationFile: name: name.toLowerCase().replace(/\s+/g, "_"),Issue: The API generates slug from Fix: Either:
Impact: Inconsistent data structure could break URL routing. 5. Missing Error Handling in Grammar Detail PageFile: Multiple database queries without error handling: const { data: relatedOutgoing } = await supabase
.from('grammar_relationships')
.select(...)
.eq('source_concept_id', concept.id)Fix: Add error handling and graceful degradation: const { data: relatedOutgoing, error: relError } = await supabase
.from('grammar_relationships')
.select(...)
.eq('source_concept_id', concept.id)
if (relError) {
console.error('Failed to fetch relationships:', relError)
// Continue rendering with empty relationships
}Impact: Page could crash on database errors instead of degrading gracefully. 6. Performance: N+1 Query PotentialFile: const { data: langCounts } = await supabase
.from('grammar_concepts')
.select('language')Issue: Fetches ALL grammar concepts just to count languages. Could be expensive with thousands of concepts. Fix: Use aggregation: const { data: langCounts } = await supabase
.rpc('get_language_counts') // Create a DB function
// Or use a more efficient count query
const { count: langCount } = await supabase
.from('grammar_concepts')
.select('language', { count: 'exact', head: true })Impact: Slow page loads as content grows. Low Priority 🟢7. Accessibility: Missing ARIA LabelsFile: Filter buttons lack ARIA labels for screen readers. Fix: <button
key={key}
onClick={() => handleCategoryClick(key)}
disabled={count === 0}
aria-label={`Filter by ${cat.name} (${count} concepts)`}
aria-pressed={isActive}
className={...}
>8. Dark Mode ContrastFile: CEFR badge colors may have insufficient contrast in dark mode: ${concept.difficulty_level === 'A1' ? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400' : ''}Suggestion: Test with WCAG contrast checker and adjust opacity if needed. 9. TypeScript StrictnessFile: Using {relatedOutgoing?.map((rel: any, i) => (Fix: Define proper types: interface RelatedConcept {
relationship_type: string;
target_concept: {
id: string;
slug: string;
display_name: string;
language: string;
};
}
{relatedOutgoing?.map((rel: RelatedConcept, i) => (📊 Performance ConsiderationsDatabase Query Optimization✅ Good: Indexes cover all access patterns CREATE MATERIALIZED VIEW grammar_concept_stats AS
SELECT
language,
category,
COUNT(*) as concept_count
FROM grammar_concepts
WHERE is_published = true
GROUP BY language, category;
CREATE UNIQUE INDEX ON grammar_concept_stats(language, category);Refresh on content updates. 🔒 Security Assessment
🧪 Test Coverage AssessmentExcellent Coverage:
Missing Tests:
Recommendation: Add API integration tests before merging: // __tests__/api/grammar-concepts.test.ts
describe('POST /api/grammar-concepts', () => {
it('rejects unauthenticated requests', async () => {
const res = await fetch('/api/grammar-concepts', {
method: 'POST',
body: JSON.stringify({ name: 'test', display_name: 'Test' })
})
expect(res.status).toBe(401)
})
})📝 Documentation & Code QualityStrengths:
Suggestions:
🚀 Migration & DeploymentPre-Merge Checklist:
Post-Merge:
💯 Overall AssessmentCode Quality: A Final Verdict: This is outstanding architectural work. The graph-ready design, comprehensive testing, and attention to linguistic detail are exemplary. Address the security issues (#1, #3) before merging, and consider the other suggestions for future improvements. Action Items Before Merge:
Great work! This establishes a solid foundation for the grammar reference system. 🎉 Reviewed using Claude Code - Following CLAUDE.md conventions |
PR Review: Grammar Substrate Foundation (Epic #204)This is a substantial PR implementing a graph-ready grammar substrate for multi-language support. I've reviewed the schema design, UI components, and overall architecture. Here's my comprehensive feedback: ✅ Strengths1. Excellent Schema Design
2. Good Security Practices
3. Clean UI Implementation
4. Comprehensive Testing
|
Summary
Implements the Grammar Substrate Foundation (Epic #204) with a graph-ready architecture supporting Latin, Gothic, and Old Norse.
Architecture Highlights
Database Schema (Nodes & Edges)
UI Components
/grammar- Index page with language tabs (🏛️ Latin, ⚔️ Norse, 📜 Gothic)/grammar/[slug]- Detail page with breadcrumbs, examples, related conceptsSeeded Content
Key Schema Features
grammar_concepts.source_refs- JSONB array of{source, section, url}grammar_concepts.examples- JSONB array of{source, target, gloss}lexicon_occurrences.morphological_gloss- Leipzig format (NOM.SG.F)get_grammar_tree(),get_related_concepts()Test Plan
/grammar- verify language tabs work/grammar/la/cases/nominativedetail pageRelated Issues
Next Steps (not in this PR)
🤖 Generated with Claude Code