- Register thumbnails now load lazily (
frontend/src/components/VinylRegister.tsx) — addedloading="lazy"anddecoding="async"to every cover<img>in the grid/list view. The browser now only fetches thumbnails as cards enter (or approach) the viewport instead of firing all image requests at once on open. For a collection of 50 records, this reduces initial HTTP requests from ~50 to ~6–12, improving perceived open time and reducing backend load. No JavaScript involved — native browser feature, compatible with all modern browsers.
- Image disappears from register after add → reanalyze → delete → update flow — the previous implementation re-uploaded all in-memory images (including the already-persisted original) as new DB entries on every "Update in Register" action. If the re-upload of a fetched File failed (e.g. empty
blob.typereturned by Vite proxy),allImageUrlswas empty and the backend's keep-list purged every image for that record. Fixed by introducing aregisteredImageUrlsstate (parallel array touploadedImages) that tracks the DB URL for each already-persisted image.handleRegisterActionnow only uploads genuinely new images and sends[...existingDbUrls, ...newlyUploadedUrls]as the keep-list, leaving pre-existing images untouched. - Empty
blob.typecausing upload rejection (frontend/src/App.tsx) —Fileobjects reconstructed from register images viafetch()could haveblob.type = ""when the Vite proxy response lacked aContent-Typeheader. The backend upload endpoint rejected these. Fixed withblob.type || 'image/jpeg'fallback.
registeredImageUrlsstate added (frontend/src/App.tsx) — populated on register load, kept in sync on image deletion and after saves. Passed toVinylCardas a new prop.COMPLETEstatus badge removed (frontend/src/components/VinylCard.tsx) — the uppercase status text below the panel title was not meaningful to users and has been removed. The confidence bar remains.
- Backend
NameErrorcrash on reanalysis (backend/api/routes.py) —explanationandexisting_recordwere only assigned inside nestedtry/ifblocks inreanalyze_vinyl. If the Anthropic API call failed orcurrent_recordwas absent, STEP 4 referenced undefined variables. Both are now initialised to safe defaults (explanation = "",existing_record = None) before their conditional blocks. - Frontend:
registeredImageCountnot reset after reanalysis (frontend/src/App.tsx) — after a successful re-analysis response,registeredImageCountwas never advanced. The "📸 Analyze N new images" button therefore kept showing the same count, allowing the user to re-trigger reanalysis of already-analyzed images. Fixed by callingsetRegisteredImageCount(originalUploadedImages.length)on successful reanalysis completion.
- User switcher replaced with dropdown (
frontend/src/components/UserManager.tsx) — individual user buttons in the header are replaced by a compact<select>dropdown in all views (desktop and mobile). Active user is pre-selected; switching still triggers a register reload. - My Register button height aligned (
frontend/src/App.module.css) — on mobile breakpoints (≤768px,≤480px) the button now uses explicitheight: 32pxwithdisplay: flex; align-items: center, matching the user dropdown and+button. - Header right side made non-wrapping on mobile —
headerRightusesflex-wrap: nowrapso the My Register button never gets pushed off-screen regardless of the number of users.
- Per-record Notes field — initial analysis writes vision reasoning to
user_notes; re-analysis appends timestamped Claude valuation explanation. Shown in view mode (collapsible, click to expand) and editable in edit mode. Only persisted to DB on user-triggered Update. - Discogs metadata lookup re-enabled (
backend/agent/metadata.py) —lookup_metadata_from_both()now actually callslookup_discogs_metadata(). OptionalDISCOGS_TOKENenv var for authenticated requests (25→60 req/min). DISCOGS_TOKENadded to.env.examplewith setup instructions.- Expandable Notes section in VinylCard view mode — collapsed by default with ▲/▼ toggle.
- MetadataEnhancer changelog descriptions (
backend/agent/metadata_enhancer.py) — all change entries were always showing the first change description; addedchange_indexcounter to correctly map each field to its own description. - Re-analysis
NameError(backend/api/routes.py) —vinyl_recordwas not defined in the reanalysis scope; corrected to useexisting_record. - Multi-line EXPLANATION capture in reanalysis valuation — single-line regex now replaced with
split('EXPLANATION:', 1)to capture full Claude explanation.
- Value section compacted — replaced multi-block layout (header, large amount, condition badge, disclaimer) with a single compact row
💰 €XX [🔍 Web Search]+ slim 6 px scale bar. - Condition removed from value row — already displayed in the Details list above, no longer duplicated.
- VinylCard panel cut off when record loaded from Register (
frontend/src/components/VinylCard.module.css).cardwas usingmax-height: calc(100vh - 120px), making it 56px taller than its parent.cardContainer(calc(100vh - 176px))- The parent's
overflow: hiddenclipped the bottom of the card, hiding the Update button when only one image was present - Fixed by changing to
height: 100%; max-height: 100%so the card fills and scrolls within its container bounds
NodeJS.Timeouttype error inApp.tsx(frontend/src/App.tsx)metadataUpdateTimeoutRefwas typed asuseRef<NodeJS.Timeout | null>— a Node.js-specific type not available in browser environments- Changed to
useRef<ReturnType<typeof setTimeout> | null>, which resolves correctly in both browser and Node.js contexts and eliminates the TypeScript compile error
- Makefile — redundant with
start-docker.sh,status.sh, andphonox-cli status.sh— stale Phase 1-4 development dashboard, not referenced anywheredocker-status.sh— static info dump, not referenced anywhere; superseded byphonox-cliAPI_GUIDE.md— duplicated by the live Swagger UI at/docsand the structureddocs/api/MkDocs pagesTESTING_GUIDE.md— stale "ready to test" artifact from early development, no longer accurate
ARCHITECTURE.md— updated Register Flow (requireduser_tag), Chat Flow (collection analysis context injection), and Security section to reflect v2.0.0 changesREADME.md— replaced brokenTESTING_GUIDE.mdlink with live Swagger UI reference
- Collection Analysis Not Visible to Model in Chat (
frontend/src/components/ChatPanel.tsx,frontend/src/App.tsx)- Analysis report was stored as a
'system'-role message;handleSendMessageexplicitly filtered out all system messages before building the history sent to Claude, so follow-up questions had no context - Added
addAnalysis(content)method toChatPanelHandlethat inserts a properuser → assistantmessage pair (valid Anthropic alternating-role format) App.tsxnow callschatPanelRef.current.addAnalysis(reportContent)instead ofaddMessage(reportContent, 'system')- Analysis content is now included in the
chat_historyon every subsequent API call, giving Claude full context when the user asks follow-up questions
- Analysis report was stored as a
- Broken Access Control on Register Endpoint (
backend/api/register.py,frontend/src/services/registerApi.ts)GET /api/register/without auser_tagreturned all records from all users (OWASP A01)user_tagparameter is now required on the backend endpoint; missing or blank value returns HTTP 400- Frontend
getRegister(userTag: string)parameter is now required (wasOptional), eliminating the no-filter code path
- Chat Missing Metadata Context (
backend/api/routes.py)barcode,condition,user_noteswere never fetched from the DB for chat contextestimated_value_eurandspotify_urlwere fetched but not injected into the Claude system prompt- All five fields now included in
current_metadataand rendered in the system prompt so Claude has full record context when chatting against a loaded record
- Chat Response Ending With "0" (
frontend/src/components/ChatPanel.tsx){message.sourcesUsed && message.sourcesUsed > 0 && (...)}evaluated to0(rendered as text) when no web sources were used- Fixed by changing condition to
{(message.sourcesUsed ?? 0) > 0 && (...)}
- Blog Link in Header (
frontend/src/App.tsx)- Added
phonox-blog.web.applink below the Phonox headline in small, muted text - Opens in new tab; brightens on hover
- Tight spacing:
lineHeight: 1onh1+marginTop: 1pxon the link
- Added
- Collection Analysis (
backend/api/models.py,backend/api/routes.py,frontend/src/components/VinylRegister.tsx)- Increased
ChatRequest.messagemax_length from 2000 → 150000 to accommodate full collection JSON (110K+ chars for 500 records) - Added
collection_analysisflag to skip Tavily web search for analysis requests (faster response) - Increased
max_tokensfor collection analysis responses from 4000 → 16000 to prevent truncated reports
- Increased
- Restore Script (
scripts/restore.sh)- Fixed image restore extracting to
/uploadsinstead of/app/uploadsinside the container - Changed
tar -xf - -C /totar -xf - -C /appso images land at the correct Docker volume mount path
- Fixed image restore extracting to
- Upload Directory Path (
backend/api/register.py)- Fixed default
UPLOAD_DIRfallback path from hardcoded/app/uploadsto a relative path for local development
- Fixed default
- Move Records to Other Users (
frontend/src/components/VinylRegister.tsx,backend/api/register.py,frontend/src/services/registerApi.ts)- New 👤 button in Vinyl Register (alongside Spotify and Delete buttons) to transfer records between users
- Modal dialog for selecting target user or creating a new user
- Supports multi-user collections: if no other users exist, prompts to create one before moving
- Moved records instantly removed from current user's collection with updated record count
- Register auto-closes if last record is moved, allows parent to handle user selection
- Backend endpoint:
POST /api/register/movewith record_id, from_user, to_user parameters
- Button Consistency in List View
- All action buttons (Spotify 🎧, Move 👤, Delete 🗑️) now unified to 28×28px (desktop) and 22×22px (mobile)
- Delete button styling aligned with Spotify button (structured border + background)
- Consistent 0.8rem font size in list view, 0.7rem on mobile
- Eliminates visual inconsistency where delete button had different appearance
- Record image in list view now stretches to full row height (maintained 80×80px for balanced mobile UX)
- Move dialog includes record title/artist preview and smooth form flow (select user → create new user option)
- Error handling in move dialog with user-facing messages
- Loading state with "⏳ Moving..." confirmation button feedback
- Collection Analysis Large Payload Support (
backend/api/models.py,backend/api/routes.py,frontend/src/components/VinylRegister.tsx)- Increased ChatRequest.message max_length from 2000 to 150000 characters to support full collection JSON (14-15K chars for 500+ records)
- Added collection_analysis flag to ChatRequest to skip unnecessary web search for analysis requests (performance optimization)
- Backend now skips Tavily web search when collection_analysis=true, only using Claude's knowledge for faster analysis
- Increased max_tokens for collection analysis responses from 4000 → 16000 to prevent truncated reports
- Resolves issue where "Analyze Collection" button would fail silently or return incomplete analysis
- Vinyl Register Modal Height & Scrolling Improved (
frontend/src/components/VinylRegister.module.css)- Modal height increased to 98% (desktop), 100dvh (mobile)
- Vertical scrolling enabled for modal
- Fixes cut-off panel and ensures bottom button visibility
- Arrow-key interactive menu — replaced all numbered-input menus with a curses-based full-screen interface; navigate with ↑/↓, select with Enter, cancel with q/Esc
- Per-service status panel — header now shows Frontend, Backend, and DB each with their own ✔/✘ icon (colour-coded green/yellow/red) plus Network and latest Backup timestamp
- Interactive configure (
./phonox-cli configure) — curses menu lists all 6 configurable keys with masked current values; select a key, type new value, Save & apply writes.envand restarts containers - Two new model keys added to configure:
ANTHROPIC_AGGREGATION_MODELandANTHROPIC_ENHANCEMENT_MODEL - Interactive restore (
./phonox-cli restore) — curses menu lists all backups newest-first; requires typingyesto confirm before overwriting data - Collection management (
./phonox-cli manage-users) — curses menu lists collections with record counts; supports renaming all records in a collection via a single DB update
- Selection bar colour changed from black-on-cyan to white-on-blue for better readability on dark terminals
- CLI documentation (
docs/getting-started/cli.md) fully rewritten to reflect the new curses interface, all 6 configurable keys, interactive restore/configure/manage-users, and the new status panel
- Fixed False "Could Not Access Images" Warning in Backup Script (
scripts/backup.sh)docker cpused a hardcoded container name (phonox_backend) which does not match the actual container name assigned by Docker Compose v2 (e.g.phonox-backend-1), causing the copy command to return a non-zero exit code- Despite the warning the archive was sometimes still populated (if
docker cpmanaged to transfer files before failing), leading to a misleading "Empty image archive created" message while the backup was actually intact - Fix: replaced
docker cp "phonox_backend:/app/uploads/."withdocker compose cp backend:/app/uploads/.— uses the Compose service name, consistent with the directory-existence check already in the script, and works regardless of how Docker names the underlying container
- Fixed Page Reload When Adding Multiple Images on Samsung Internet Browser (
frontend/src/components/VinylCard.tsx)- On Samsung Internet Browser (Android), tapping "+ Add More Images" and selecting files caused the page to reload, losing all analysis state
- Root cause: the
onChangehandler was synchronously resettinge.target.value = ''inside the event callback; Samsung Internet Browser incorrectly interprets a synchronous file input value reset as a navigation event and triggers a page reload - Fix: attached a
ref(addImageInputRef) to the file input and deferred the value reset viasetTimeout(() => { addImageInputRef.current.value = '' }, 100)so it runs after the event fully completes — Chrome was unaffected by this browser-specific bug
-
Fixed CLI Restart Crash (
scripts/phonox_cli.py)KeyboardInterrupt(Ctrl+C) in the main menu no longer produces a raw traceback — caught gracefully and exits cleanlycheck_database_health()was checking the wrong container name (--filter name=phonox_db); fixed to use the service name (ps db)cmd_restart()ignored the return value ofcheck_database_health()— it now reads the result and runs a real recovery loop (docker compose restart db) with a 10-second countdown if the database is unhealthy after restart
-
Fixed Reanalysis Dead-Code Database Path (
backend/api/routes.py)- When the frontend called the reanalyze endpoint with
do_load_from_database=Truebut sent zero files (image pre-fetch failed during backend restart instability), the code path existed but never actually loaded images - Added a proper fallback: queries the
VinylImagetable, reads files from disk viaget_image_data(), and base64-encodes them so analysis proceeds without requiring the client to re-send images
- When the frontend called the reanalyze endpoint with
-
Fixed Duplicate
_parse_tavily_responseFunction (backend/agent/websearch.py)- The function was defined twice (at two separate locations); Python silently used only the second definition. Removed the duplicate.
-
Fixed Anthropic Credit Error Masked as Image Format Error (
backend/agent/vision.py)- When the Anthropic API returned a 400
invalid_request_errordue to an exhausted credit balance, the generic branch overwrote the real message with "Invalid image format or data." - Added a specific
"credit balance is too low"check before the generic branch so the real message (including the billing console URL) is surfaced correctly
- When the Anthropic API returned a 400
-
Fixed
reanalyze()Return Type (frontend/src/api/client.ts)- Was typed as
{ record_id: string; id?: string }— the actual response includesstatus,error,metadata, etc. Changed toRecord<string, unknown>
- Was typed as
-
DuckDuckGo Supplements Sparse Tavily Results (
backend/agent/websearch.py,backend/tools/web_tools.py)- Previously DuckDuckGo was only used when Tavily returned zero results
- Both search functions now check a configurable threshold: if Tavily returns fewer than
WEBSEARCH_MIN_RESULTS_THRESHOLDresults, DuckDuckGo runs and its results are merged and deduplicated - Added
_deduplicate_results()helper to prevent duplicate URL entries
-
Configurable Search Variables via
.envWEBSEARCH_MAX_RESULTS(default7) — max results returned per searchWEBSEARCH_MIN_RESULTS_THRESHOLD(default4) — minimum Tavily results before DDG supplementsWEBSEARCH_BARCODE_MAX_RESULTS(default5) — max results for barcode-specific searches- All three configurable in
.env/.env.exampleunder WEB Search & Scraping Configuration
-
Memory-First Image Architecture (
frontend/src/App.tsx,frontend/src/components/VinylCard.tsx)uploadedImages: File[](React state) is now the single source of truth for images;image_urlsmetadata is only used for backend DB round-trips, never for UI decisions- Added
registeredImageCountstate to track how many images came from the register at load time - Added
imagesLoadingstate with a UI hint while images are fetched from the backend after selecting a register record - "Reanalyze All" button visibility now checks
uploadedImages.length > 0(wasimage_urls.length > 0) originalImageCountin VinylCard now usesregisteredImageCountprop (wasimage_urls.length)- Fixed
Promise.alltoPromise.allSettledfor image pre-fetch so one failed image no longer loses all images
- Actionable Error Messages in the Frontend (
frontend/src/App.tsx,frontend/src/App.module.css)- Error display now surfaces the exact backend message (e.g. "Anthropic API credit balance exhausted. Please top up at https://console.anthropic.com/settings/billing") instead of a generic "Re-analysis failed. Please try again."
- URLs inside error messages are rendered as clickable links (opens in new tab)
- Error panel widened to 560 px (was 400 px) and gets flexible width on smaller screens
- Added warning icon; close button renamed "Dismiss"
-
Fixed Spotify URL Lost on Re-Analysis
reanalyzeendpoint now parses thecurrent_recordpayload sent by the frontend- If the agent graph's
search_spotify_albumcall returns nothing (non-deterministic web search), the URL already stored in the current record is preserved as a fallback - Previously the URL was silently dropped every time re-analysis ran without a Tavily hit
-
Fixed Spotify Search Parity Between Tavily and DuckDuckGo
- DuckDuckGo fallback in
search_spotify_albumpreviously used the plain query{artist} {title} spotify albumwith no site restriction, so results were dominated by review sites rather than direct Spotify album pages - DuckDuckGo now first tries a site-restricted query
{artist} {title} album site:open.spotify.com(mirrors what Tavily does withsite:spotify.com) - If that returns nothing it falls back to the broader query with
max_results=10 - Corrected a misleading log message ("trying broader Tavily search") that actually jumped straight to DuckDuckGo
- DuckDuckGo fallback in
- Full-Screen Expand/Collapse Panels
- Chat and vinyl card panels can expand to full screen on mobile and intermediate views
- Non-expanded panel is hidden; expanded panel covers entire viewport including header
- Works across all three breakpoints: desktop (>1200px), intermediate (768-1200px), mobile (<768px)
-
Mobile Responsive Layout
- Viewport locked to
100vh— no page scrolling, all content fits within screen - Grid rows use
minmax(0, 1fr)for strict 50/50 panel split on mobile - Container headers stay pinned with flex-shrink: 0
- Chat input section stays at bottom, textarea scrollbar removed
- Viewport locked to
-
Compact VinylCard Metadata
- Inline label-value layout with fixed 80px label width
- Reduced gaps and font sizes for professional, dense display
- Edit mode fields properly separated (Artist, Est. Value as individual fields)
-
VinylRegister Mobile Fix
- Record cards now match dropdown width on mobile phones
- Added overflow protection and word-break on long titles/artists
- Matching horizontal padding between controls and records container
-
Global Scrollbar Styling
- Consistent scrollbar appearance across entire app (10px width, rounded thumb)
- Removed scrollbar arrow buttons for cleaner look
- Firefox support via
scrollbar-width: thinandscrollbar-color - Removed redundant per-component scrollbar styles
-
ChatPanel Header on Mobile
- Upload button now visible on mobile (header no longer hidden)
- Compact padding on smaller screens
- Raw Data Section removed from VinylCard (toggle and JSON display)
- 8 files changed across frontend
- Global scrollbar in
index.cssreplaces component-specific scrollbar rules - CSS Modules:
panelHidden+mobileExpandedclasses for expand/collapse - No overlay div — solid background approach eliminates darkening artifacts
-
Enhanced Backup & Restore Scripts
- Added colored output (cyan) for better readability
- Progress bars for database restoration and container startup
- Real-time progress indication during image file copying
- Shows backup/restore file sizes and timestamps
- Improved error handling with better feedback messages
-
Cleaned Up Script Output
- Suppressed Docker warnings about deprecated
versionattribute - Hidden PostgreSQL debug messages (SET, CREATE TABLE, etc.)
- Only relevant status messages displayed
- Professional, user-friendly output
- Suppressed Docker warnings about deprecated
-
Optimized Image Restoration
- Uses efficient tar piping for faster image transfers
- Verifies file count after copying to ensure completeness
- Displays total files and size before transfer begins
- Progress bar completion indication
-
Fixed Image Restore on Raspberry Pi
- Images were not being restored due to container timing issues
- Increased PostgreSQL startup timeout to 60 seconds
- Containers now fully started before image restoration begins
- Added proper health checks for backend container
-
Fixed Docker Compose Warnings
- Suppressed WARN messages about
versionattribute (no action needed) - Cleaner terminal output during backup/restore operations
- Suppressed WARN messages about
- Backup script now extracts images from container volume directly
- Restore script uses tar-to-docker pipeline for efficient 1.5GB+ transfers
- Proper error handling for missing files and failed operations
- Better logging with progress indicators for long operations
-
Optimized Web Search Query Strategy
- Tavily now attempts restricted domain search first (discogs.com, musicbrainz.org, allmusic.com)
- If restricted search returns 0 results, Tavily retries without domain restrictions
- DuckDuckGo fallback uses cleaned query (removes special characters, catalog numbers)
- Eliminates inappropriate DuckDuckGo results by filtering catalog numbers and years
- Smarter fallback prevents irrelevant content when specific queries fail
-
Upgraded DuckDuckGo Library
- Migrated from deprecated
duckduckgo-searchtoddgs>=4.0.0 - Eliminates deprecation warnings in logs
- ddgs package is newer and more reliable
- Updated imports in websearch.py and web_tools.py
- Migrated from deprecated
-
Fixed Inappropriate DuckDuckGo Query Results (#websearch-fallback-quality)
- DuckDuckGo was returning irrelevant results (dictionary entries, unrelated sites)
- Root cause: Query contained special characters and non-essential terms
- Solution: Added
_clean_query_for_fallback()that sanitizes queries - Removes forward slashes, catalog numbers, years from fallback queries
- DuckDuckGo now receives focused queries: "artist title vinyl record"
-
Fixed Tavily Domain Restriction Returning Zero Results (#tavily-domain-filter)
- Implemented fallback strategy: restricted search → unrestricted search
- Prevents search failures when domain filter is too narrow
-
Fixed DuckDuckGo Fallback Robustness
- Replaced fragile HTML scraping with
duckduckgo-searchlibrary - DuckDuckGo now properly bypasses anti-bot measures and rate limiting
- Library handles HTTP headers and timeouts gracefully
- Fallback now works reliably when Tavily fails
- Replaced fragile HTML scraping with
-
Improved Websearch Error Handling
- Better logging for debugging search failures
- Timeouts and connection errors handled gracefully
- Functions return empty results instead of crashing
- Proper fallback chain: Tavily → DuckDuckGo → return empty results
- Added
duckduckgo-search>=3.9.0to requirements.txt - Library provides robust web search without API keys
-
Fixed Tavily API Key Not Being Passed to Client
- websearch.py was creating TavilyClient() without api_key parameter
- Environment variable TAVILY_API_KEY is now properly passed to all TavilyClient instances
- Fixes "websearch failed" errors that occurred during vinyl identification
-
Added DuckDuckGo Fallback to Agent Websearch
- All search functions now fallback to DuckDuckGo if Tavily fails or is unavailable
- search_vinyl_metadata: Tavily → DuckDuckGo fallback
- search_vinyl_by_barcode: Tavily barcode search → DuckDuckGo fallback
- search_spotify_album: Tries both Tavily and DuckDuckGo for album links
- Deduplicates results from both sources by URL to avoid duplicates
- DuckDuckGo fallback works even without Tavily API key configured
- Robust websearch with dual-source approach (Tavily primary, DuckDuckGo secondary)
- Better error handling and logging for troubleshooting search failures
- Added imports for BeautifulSoup and requests to support DuckDuckGo HTML parsing
- Search functions now continue gracefully if one method fails, trying alternatives
-
Full-Screen User Selection Modal
- Complete redesign of first-time user experience
- Full-screen responsive layout on mobile devices
- Significantly improved readability on all screen sizes
-
Enhanced Mobile Responsiveness
- Larger touch targets: 70px button height for better usability
- Improved typography: 2.5rem title, 1.3rem sections, 1.15rem text
- Better visual hierarchy and spacing between elements
- Optimized padding and margins for mobile (1.5rem-2rem)
-
Proper Modal Layering
- Use React Portals to render modals to document.body
- Guaranteed z-index positioning above all application overlays
- Fixes issues with chat panel blocking user selection on first visit
- modals now: 99999 z-index for guaranteed top layer
-
Improved First-Time User Flow
- Clear "Your Profiles" section with existing users
- Intuitive "or" divider between existing and new profiles
- Better visual feedback with smooth transitions
- Touch-friendly interface with larger buttons and spacing
- Better contrast and readability on mobile devices
- Smooth transitions and hover effects optimized for touch (no hover on mobile)
- Focused input states with glow effect
- Proper button disabled states and styling
- Cleaner mobile viewport usage with full-screen approach
-
Fixed Image Duplication on Record Load
- Images no longer duplicate when loading registered records into VinylCard
- Simplified image display logic: only shows
uploadedImagesin UI image_urlskept as metadata reference but not displayed to prevent duplication- Root cause: was displaying both database URLs and File objects simultaneously
-
Fixed Re-Analysis Status Polling
- Changed from ineffective polling mechanism to immediate response
- Re-analysis endpoint now returns complete analyzed metadata immediately
- Removed get_identify status endpoint polls for re-analysis (not needed)
- Fixes "Failed to get re-analysis status" errors
-
Simplified Image Management
- Removed unused
deletedImageUrlsstate tracking - Deletion now handled directly by removing from
uploadedImages - Single source of truth for image display
- Cleaner state management in VinylCard.tsx
- Removed unused
-
Fixed Spotify URL Deletion on Re-Analysis
- Backend: Added missing
spotify_urlandestimated_value_usdfields to reanalyze response - Spotify URL now properly preserved when re-analyzing records
- Previously was being dropped because response didn't include the field
- URL correctly detected by agent graph but wasn't passed through to client
- Backend: Added missing
-
Fixed Re-Analyze Button Visibility
- Button now only appears when NEW images are added to a register record
- No longer shows when just loading a record from register
- Correctly counts only newly added images (excludes database images)
- Prevents accidental re-analysis when no new images have been uploaded
-
Re-Analysis Now Mirrors Upload Flow
- Re-analysis includes full web search and market value estimation
- Results sent to chat panel via
intermediate_resultsfield - User sees search queries, sources, and Claude analysis in chat
- Same valuation logic as initial identify endpoint (Goldmine weighting)
-
Memory-First Architecture Refinement
- Images stay in browser memory until explicit "Update in Register" click
- Database only touched on explicit user action
- Loading from register: images fetched as File objects into memory
- Updated images: only newly added images are uploaded (no re-upload of existing)
- Metadata updates (condition, value) persist alongside correctly managed images
- Cleaner Update Logic
- VinylCard.handleRegisterAction now uploads all current
uploadedImages - Automatically replaces deleted images in database
- Combines with metadata updates in single transaction
- Fixed the "only upload new images" logic that was causing tracking issues
- VinylCard.handleRegisterAction now uploads all current
frontend/src/components/VinylCard.tsx- Image display and state managementfrontend/src/App.tsx- Image loading from register, keptimage_urlsas referencebackend/api/routes.py- Re-analysis endpoint returns immediate response with intermediate resultsbackend/agent/graph.py- Removed LangGraph MemorySaver checkpointer (prevents recursion)
-
LLM-Based Metadata Aggregation 🤖
- Replaced complex rule-based aggregation with Claude-powered intelligent merging
- Automatically resolves conflicts between multiple image analyses
- Normalizes capitalization ("PINK FLOYD" → "Pink Floyd")
- Merges barcodes, catalog numbers, and genres intelligently
- Provides reasoning for aggregation decisions
- ~75% reduction in code complexity (170 lines → 40 lines)
- New environment variable:
ANTHROPIC_AGGREGATION_MODEL(default: claude-sonnet-4-5-20250929)
-
Condition Assessment 🔍
- Automatic vinyl condition detection from images
- Uses Goldmine grading scale (M, NM, VG+, VG, G+, G, F, P)
- Conservative multi-image approach (uses worst condition seen)
- Condition notes with visible wear descriptions
- Condition badge in UI with color coding
- Condition multiplier affects value calculations
- Integrated into aggregation and enhancement workflows
-
Advanced Retry Logic 🔄
- Exponential backoff for transient API failures (1s → 2s → 4s)
- Up to 3 retry attempts per image
- Handles timeouts, rate limits, and temporary unavailability
- Detailed retry logging with attempt tracking
- Image-specific error reporting
- Graceful fallback to simple merge when LLM fails
-
Image-Context Aware Prompts 🎯
- Image 1 optimized for artist/title extraction
- Images 2+ optimized for barcode/catalog/genres
- Context-aware prompts reference previous results
- Better multi-image coordination
- Reduced duplicate analysis
- Higher accuracy with focused instructions
- Centralized Model Configuration
- All Claude models configurable via environment variables
ANTHROPIC_VISION_MODEL: Image analysis (default: claude-sonnet-4-5-20250929)ANTHROPIC_CHAT_MODEL: Chat responses (default: claude-haiku-4-5-20251001)ANTHROPIC_AGGREGATION_MODEL: Multi-image merging (default: claude-sonnet-4-5-20250929)ANTHROPIC_ENHANCEMENT_MODEL: Metadata enrichment (default: claude-opus-4-1-20250805)- All models documented in
.env.examplewith alternatives - Cost optimization strategies documented
- Easy switching between model tiers
- Fixed f-string Format Specifier Errors
- Properly escaped JSON examples in prompts with
{{}}and{{{{}}}} - Fixed "Invalid format specifier" errors during vision extraction
- Affected files:
graph.py,vision.py,metadata_enhancer.py - All multi-image analysis now works correctly
- Properly escaped JSON examples in prompts with
-
Metadata Quality Validation
- Validates confidence thresholds
- Checks for placeholder values (Unknown, N/A, ERROR)
- Validates year range (1900-2026)
- Warns about missing genres
- Validates barcode format (12-13 digits)
- Quality issues logged and tracked
-
Enhanced Confidence Scoring
- Updated weights: Discogs 40%, MusicBrainz 20%, Vision 18%, WebSearch 12%
- Added Image (5%) and User Input (5%) sources
- Better reflects reliability of each source
- More accurate confidence calculations
- New Documentation Files
LLM_AGGREGATION_APPROACH.md: Why and how LLM aggregation worksMODEL_CONFIGURATION.md: Complete model configuration guideMETADATA_DETECTION_ANALYSIS.md: Technical analysis of multi-image issuesMETADATA_DETECTION_IMPLEMENTATION.md: Implementation detailsMETADATA_DETECTION_QUICKSTART.md: Quick reference guideIMPROVEMENTS_IMPLEMENTED.md: Summary of all improvementsTESTING_GUIDE_IMPROVEMENTS.md: Testing procedures
- Per 2-Image Upload (with new aggregation):
- Vision analysis: $0.008 (2 × $0.004)
- LLM aggregation: $0.001
- Total: $0.009 (12.5% increase, worth it for intelligence gain)
- Condition extraction: included in vision analysis (no extra cost)
- Enhancement: $0.003 when adding images to existing records
- ✅ Multi-image uploads work without f-string errors
- ✅ LLM aggregation merges metadata intelligently
- ✅ Retry logic handles transient failures
- ✅ Image-specific prompts improve accuracy
- ✅ Condition detected and displayed in UI
- ✅ All model configurations via environment variables
- ✅ Quality validation catches suspicious data
- ✅ Backend starts without errors
-
Fixed Image Upload UnicodeDecodeError
- Added custom
RequestValidationErrorhandler to prevent encoding binary file data as UTF-8 - Prevents
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89...when uploading PNG/JPEG files - Error responses now only include sanitized error metadata (location, type, message)
- Added custom
-
Enhanced Image Upload Endpoint
POST /api/register/images/{record_id}now has comprehensive error handling- File size validation (max 10MB per file)
- Empty file detection
- Content-type validation for image files
- Individual error reporting per file (one file error doesn't block others)
- Proper database transaction management with rollback on failure
- Better error messages returned to frontend
- ✅ Image uploads succeed without UnicodeDecodeError
- ✅ Images saved to database and disk
- ✅ Images visible in register list view
- ✅ Images displayed on vinyl card detail view
- ✅ Proper error handling for invalid files
- ✅ Multi-file uploads partially succeed if some files fail
-
Fixed CORS Headers Blocking Frontend-Backend Communication
- Removed interfering debug middleware that prevented CORS header propagation
- Simplified and verified CORS configuration for all origins
- Ensures
Access-Control-Allow-Origin: *is properly returned - All cross-origin requests from frontend now work correctly
-
Fixed 500 Error on Chat Endpoint
- Removed duplicate
get_db()function in routes.py - Fixed
TypeError: 'NoneType' object is not callable - Now properly imports initialized get_db from database module
- Chat endpoint (record-specific and general) fully operational
- Removed duplicate
-
Web Search Performance Optimization
- Reduced URL scraping timeout from 10s to 5s
- Reduced scraped URLs per search from 2 to 1
- Improved error handling with automatic DuckDuckGo fallback
/webcommand now responds within acceptable timeframe (25-40s)
-
Web Scraping Configuration (NEW)
WEB_SCRAPING_TIMEOUT: Configurable timeout per URL (default: 10s)WEB_SCRAPING_MAX_URLS: Configurable URLs per search (default: 3)- Full environment variable support for deployment flexibility
- Tuning recommendations in
.env.examplefor different scenarios - Configuration logged on startup for verification
- ✅ All health checks passing
- ✅ CORS headers properly returned
- ✅ Database queries working
- ✅ Chat endpoint fully functional
- ✅ Update/Web Search buttons operational
- ✅ Web search with
/webcommand working
-
Automatic Database Retry Logic: Added robust connection retry mechanism with exponential backoff
- Configurable retry attempts (default: 5)
- Exponential backoff delays: 2s → 4s → 8s → 16s → 30s
- Comprehensive error alerting with troubleshooting steps
- Critical alerts log when max retries exceeded
- Connection pooling with pre-ping and recycling
- Masked URLs in logs for security
- Graceful HTTP 503 responses when DB unavailable
-
Enhanced CLI Installation: Improved
installcommand with detailed progress feedback- Step-by-step progress indicators
- Database health verification during setup
- Clear access instructions after installation
- Better error messages and guidance
-
CLI Documentation Command: New
docscommand to start MkDocs server locally- Automatic virtual environment detection and creation
- Uses existing .venv if available
- Automatically installs mkdocs from requirements
- Serves documentation at http://localhost:8001
-
Unified .env Configuration: All environment variables centralized in root
.env.example- Consolidated backend, frontend, and database settings
- Database retry configuration fully documented
- Frontend polling and UI settings included
- Single source of truth for all configuration
- Removed duplicate frontend/.env.example
-
Documentation Updates:
- Added comprehensive environment variables table in README
- Installation guide includes all configuration options
- Database retry settings documented with examples
- Frontend configuration options explained
-
New User Guides (3 comprehensive guides):
- Uploading Records: Image upload best practices, identification workflow
- Managing Collection: Collection organization, bulk operations, backups
- Chat Features: AI capabilities, query examples, web search integration
-
Enhanced Installation Guide: Complete environment setup with retry configuration
-
Database Troubleshooting Guide: Connection issues, retry examples, debugging tips
-
Updated MkDocs Navigation: Cleaned up structure, added troubleshooting section
-
CLI Documentation: All commands, usage examples, venv detection explained
- Improved CLI Design: Better user feedback with styled progress messages
- Virtual Environment Detection: Intelligent .venv detection before global installs
- Database Connection Manager: New
backend/db_connection.pymodule with:- DatabaseConnectionManager class for robust connection handling
- Retry logic with configurable parameters
- Health check integration
- Logging and alerting system
- Frontend Environment Cleanup: Removed redundant frontend/.env.example
- Professional Collection Analysis: Added "Estimate Complete Collection" feature with AI-powered analysis
- Compiles collection statistics (genres, decades, conditions, top artists)
- Sends to Claude for professional appraisal-style insights
- Displays formatted analysis with markdown support and tables
- Download analysis reports as markdown files
- ChatPanel Integration: New button to send collection analysis to chat as context
- Seamlessly switch from register to chat with analysis pre-loaded
- Analysis appears as styled context message in ChatPanel
- Continue discussion about collection insights with AI
- CSV Export Refinements:
- Changed delimiter from comma to semicolon for better European compatibility
- All fields exported as text format (prevents formula interpretation)
- European decimal formatting (comma-based)
- Barcode field prefixed with apostrophe to force text in spreadsheets
- Removed notes field from exports (chat entries no longer stored)
- Chat Storage Fix: Fixed database pollution where chat messages were being stored in user_notes
- Chat is now purely ephemeral
- CSV exports clean and data-focused
- VinylSpinner Integration: Unified loading spinner using phonox.png across collection analysis
- Analysis Modal: Professional styled modal with download and chat buttons
- System Messages: New styled context messages for rich markdown content in chat
- Docker Network Recovery: Fixed network connectivity issues after Docker updates with automatic recovery on restart
- CLI Health Checks: Added network and database health monitoring to CLI
- CLI Network Status: Display Docker network status in main menu alongside containers and backups
- CLI Restart Command: New
restartcommand with automatic network recovery (Option 6 in menu)
- Error Modal: Replaced browser alert with stylized, centered error modal matching app design
- Auto-dismiss after 5 seconds or manual close
- Gradient background and blur effect
- Better visibility and UX
- Image Management: Simplified image deletion flow
- Deleted images removed from memory, not sent to backend
- Backend overwrites image list on update (cleaner approach)
- Deleted images properly persisted to database
- Update Indicator: Added pulsing button with loading spinner during register update
- Shows "Updating..." text with rotating ⚙️ icon
- Button disabled during update (prevents double-clicks)
- Visual feedback for long-running operations
- In-Memory Analysis: Refactored
/reanalyzeendpoint to work entirely in-memory without database dependencies- Frontend sends current record data with new images
- Backend processes in-memory and returns merged metadata
- Database only involved when user explicitly saves to register
- Eliminates 500 errors on re-analysis of unsaved records
- Significantly faster analysis without DB queries
- Agent-Only Condition: Removed all
getCondition()fallbacks - condition now exclusively from agent's image analysis- No longer calculated from confidence score
- Shows "Not analyzed" if agent didn't provide condition
- Only persists to database when user saves to register
- Analysis Message Enhancement: Added condition suggestion to "Analysis Complete" message in chat
- Edit Display Fix: Condition field now correctly shows saved value when editing (not just "Good" default)
- Added professional MkDocs documentation site with Material theme
- Comprehensive documentation structure: Getting Started, User Guide, API Reference, Architecture, Development, Deployment
- Interactive navigation with dark mode toggle and built-in search
- Complete API documentation with endpoints, authentication, and code examples
- Contributing guidelines and project information
- Phonox logo integration in documentation header
- Ready for deployment to GitHub Pages, Cloud Run, or Netlify
- Added
.env.exampletemplate with all environment variables documented - Includes placeholder values and helpful comments for configuration
- Links to API key sources (Anthropic, Tavily)
- Examples for different deployment environments (local, Docker, production)
- Removed 14 temporary development documentation files
- Project structure cleaned up for open source release
- Optimized ChatPanel layout on mobile: increased message width from 85% to 95%, reduced padding for better readability
- Improved mobile header layout: kept all items on single row (Logo + Title + User Info + Register Button)
- Fixed ChatPanel container height on mobile: increased from 50vh to 65vh for better conversation visibility
- Changed quick action buttons to 2-column grid layout on mobile for better organization
- Unified button styling across header: aligned UserManager and Register Button heights, border-radius, and font sizes
- Adjusted padding consistency: ChatPanel, header, and main content all use 12px horizontal padding on mobile
- Fixed button overflow: Register Button now fits properly on mobile screens with responsive sizing
- Optimized spacing for touch targets while maintaining visual consistency
- Unified spinner behavior across all analyses (upload, re-analysis, value estimation)
- Consistent glass-morphism overlays with semi-transparent backgrounds and blur effects
- Updated VinylSpinner text colors to work with dark theme overlays
- All loading states now use same visual design system with centered VinylSpinner
- Customizable spinner messages for different analysis types
- Reduce Tavily search results from 12/6 to 7 for all search functions
- Fix batch image processing in re-analysis: now processes all images (existing + newly uploaded) together
- Fix image display after re-analysis: newly uploaded images now persist to database and appear in UI
- Updated
to_dict()to includeimage_urlsfrom VinylImage relationships - Improved image persistence across re-analysis operations
- Fix white screen issue and integrate ChatPanel messaging
- Enhanced frontend stability and chat integration
- Add Condition field UI
- Integrate value estimation features
- Optimize CLI performance
- Added Spotify URL support end-to-end:
- Backend DB:
vinyl_records.spotify_urlcolumn - API models:
spotify_urlinVinylMetadataModel,ReviewRequest, and register endpoints - Register add/update now persist
spotify_url - Frontend types and UI: edit/display in Vinyl card; 🎧 icon in register and card header
- Backend DB:
- Enhanced websearch with DuckDuckGo fallback alongside Tavily
- Combined results with deduplication; logging markers confirm DDG usage
- Version bump to
1.0.0in backend app and health/root endpoints - Minor UI polish for list/grid register views
- 0.3.x: Initial identification, register, and chat features