- SakeAnalysisResult.isFromCache flag added (not serialized to JSON)
- Both cache-hit paths return result.asCached() to signal caller
- camera_screen: EXP +10 only awarded on fresh API calls, not cache hits
- camera_screen: show '解析済みの結果を使用(経験値なし)' on cache hit
- camera_screen: clear _capturedImages after successful analysis
- camera_screen: catch(_) -> catch(e) with debugPrint logging
- SakeAnalysisResult.fromJson: auto-fill missing tasteStats keys with 3,
clamp all values to 1-5 range to prevent broken charts
- Bump version 1.0.37+44 -> 1.0.38+45
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Analysis cache redesign:
- Remove Hive persistence from AnalysisCacheService entirely
- Use Map<String, SakeAnalysisResult> in-memory instead of Hive boxes
- Cache now lives only for the duration of an app session; restart always
produces a fresh analysis — no stale misidentifications can persist
- Remove Hive init(), TTL/cleanupExpired(), getCachedByBrand() dead code
- API surface unchanged: callers (gemini_service, dev_menu) need no edits
- main.dart: delete legacy Hive boxes (analysis_cache_v1, brand_index_v1)
from disk on startup for existing users
- dev_menu_screen: update cache description text to reflect new behavior
Rationale:
- Camera captures always produce unique files -> cache hit rate was ~0%
- Each user supplies their own Gemini API key -> no shared cost benefit
- Persistent wrong results (e.g. misrecognized brand names) could survive
up to 30 days under the old design
- Different sake editions photographed separately have different hashes
and were never affected by the cache in the first place
Made-with: Cursor
- draft_service: GeminiService is now passed as a required parameter
to analyzeDraft/analyzeAllDrafts instead of being directly instantiated,
ensuring consistent Riverpod-managed injection
- gemini_provider: correct misleading comment (rate limiting is due to
static field, not Provider; Provider enables future safe refactoring)
- analysis_cache_service: cleanupExpired now also removes orphaned brand
index entries from _brandIndexBox after deleting expired _box entries
- analysis_cache_service: keysToDelete type corrected from List<dynamic>
to List<String>, removing unnecessary as String cast
- analysis_cache_service: _normalizeBrandName comment clarified to note
that .toLowerCase() only affects ASCII characters, not Japanese text
- camera_screen: add explicit ignore comment with rationale for
showDialog after async gap (mounted check immediately precedes it)
- camera_screen: remove leaked Cursor instruction comment from line 96
Made-with: Cursor
When API congestion persists after 3 retries + fallback:
- Mark exception with [CONGESTION] tag
- camera_screen catches it and calls DraftService.saveDraft()
- Shows orange snackbar (same UX as offline) instead of red error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- sake_detail_screen: hide SakeMbtiStampSection when isProVersion=false
(consumer APK no longer shows Pro版限定 placeholder at card bottom)
- sake_grid_item / sake_list_item: apply Pressable to actual tap targets
(grid/list cards now animate on press instead of non-interactive LevelTitleCard)
- soul_screen / sommelier_screen: ambient glow alpha 0.07→0.12 for visibility
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A-1 Micro-animation: Pressable wrapper widget (AnimatedScale 0.97 on tap)
applied to LevelTitleCard
A-2 Bold Typography: LevelTitleCard title font 28→36px, 現在の称号 label
demoted to bodySmall for stronger visual contrast
A-3 Ambient Glow: RadialGradient circle behind My Page (top-left, brandPrimary)
and Sommelier screen (top-right, brandAccent) via Stack + IgnorePointer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
My Page:
- LevelTitleCard: show nickname above title when set
- Add section header 成長記録 above gamification widgets
- Split profile card: basic info (nickname/gender) and sake character
(MBTI/sake persona) are now separate named sections
Sommelier:
- Add section headers テイストプロフィール / MBTI風診断 / さけのわ おすすめ
- Widen share button to full width with rounded rect shape
- Remove Dividers between sections, use SizedBox(32) spacing instead
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- soul_screen.dart: pass showBusinessMode: isBusinessApp to OtherSettingsSection
so the toggle is hidden when built with IS_BUSINESS_APP=false
- build_4_apks.sh: rewrite for current strategy (consumer/business x maita/eiji)
Pro variants removed; set_lite_app_id() restores gradle before each build
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove Phase X / Phase D labels from inline comments and imports
- Remove debugPrint calls from CustomPainter.paint() (called every frame)
- Remove commented-out locale entries (fr, de) from main.dart and language_selector
- Remove version header comments (v1.5 etc.) not needed in source
No logic changes. flutter analyze: No issues found.
- Deleted test/widget_test.dart (Flutter Counter template)
- File was unrelated to Ponshu Room Lite functionality
- Actual tests will be added in v1.0.16
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Bug Fix
- Badge "初めての一歩" was not unlocking when adding first sake item
- Root cause: GamificationService.checkAndUnlockBadges() was never called in camera_screen.dart
## Changes
- Import gamification_service.dart
- Call checkAndUnlockBadges(ref) after adding sake item to Hive
- Display newly unlocked badges in success SnackBar
- Extend SnackBar duration when badges are unlocked (4s -> 6s)
## User Impact
- Users will now see badge unlock notifications: "バッジ獲得: 初めての一歩 🍶"
- All badge unlock conditions (first_step, regional, collector, flavor) now work correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added debug logging to:
1. main.dart - Log IS_PRO_VERSION value at startup
2. main_screen.dart - Log IS_PRO_VERSION and isBusiness in screen build
3. pdf_preview_screen.dart - Log PDF Drive upload process with file ID verification
This will help diagnose why Google Drive uploads appear to succeed but files
are not visible in Drive.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed bug where AI analysis detail fields (type, alcoholContent, polishingRatio,
sakeMeterValue, riceVariety, yeast, manufacturingYearMonth) were not being saved
to HiddenSpecs, causing them to display as "-" in the UI.
Root cause: camera_screen.dart was only passing 3 fields (description, tasteStats,
flavorTags) to HiddenSpecs constructor, missing 7 detail fields that Gemini API
was successfully returning.
Verified: All detail fields now display correctly after AI analysis.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
✨ Improvements:
- Replace print() with debugPrint() (7 instances)
- Migrate withOpacity to withValues (9 instances)
- Remove unused imports (2 files)
- Fix BuildContext async gaps with mounted checks
- Remove unused local variables
📊 Analysis Results:
- Flutter analyzer: 57 → 46 issues (-11)
- Security audit: Passed ✅
- Code quality: Production ready ✅🎯 Purpose:
Complete snapshot before QR feature removal.
This is the last version with full QR functionality.
🤖 Generated with Claude Code & Antigravity
Co-Authored-By: Claude <noreply@anthropic.com>