Frontend Job interface used snake_case (file_name, file_size, progress_pct,
step_label, provider_updates, result_csv_url, created_at) but backend
returns camelCase (fileName, fileSize, progressPct, stepLabel,
providerUpdates, resultCsvUrl, createdAt) — causing all fields to be
undefined and showing 'NaN MB', broken progress bar, empty labels.
Updated types/index.ts Job, ProviderUpdate, JobSummary interfaces and
JobProgressCard.tsx to use the correct camelCase field names.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two key mismatches caused silent failures in production:
1. core/config.py LLAMACLOUD_API_KEY: was reading LLAMACLOUD_API_KEY but
docker-compose passes LLAMA_CLOUD_API_KEY (official SDK name).
Now reads LLAMA_CLOUD_API_KEY with LLAMACLOUD_API_KEY as fallback.
2. core/config.py GOOGLE_API_KEY: was reading GOOGLE_API_KEY but .env /
docker-compose use GEMINI_API_KEY. Now reads GEMINI_API_KEY first.
3. docker-compose.yml: add MSAL_* aliases for AZURE_* vars so
server/config_runtime.py picks them up explicitly (not just via defaults).
4. docker-compose.yml: pass SESSION_SECRET from .env to container.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When LLAMACLOUD_API_KEY is empty the LlamaParse client constructed a Bearer
token with an empty secret, causing Python's HTTP stack to raise
"Illegal header value b'Bearer '" and fail every upload job.
Changes:
- _extract_document_content_local(): new method using PyMuPDF (PDF),
python-pptx (PPTX), python-docx (DOCX), openpyxl (XLSX) — all already
in requirements.txt
- _extract_document_content(): skip LlamaParser entirely if key is not set;
on LlamaParser exception, fall back to local extraction instead of raising
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three bugs fixed:
1. api/jobs.ts: remove manual Content-Type header on FormData upload.
Setting it without the multipart boundary caused Quart to reject the
request body — the root cause of brief upload failures.
2. progress.py: include full job.to_dict() in job.progress / job.completed
/ job.failed WebSocket messages. Frontend checks msg.job to call
updateJob() — without it, job cards never updated in real-time.
3. AppShell: move useWebSocket() here from BriefUploadPage so the WS
connection persists across all pages, not just the upload page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- index.css: bump text-secondary #888→#b0b0b0, text-muted #555→#787878,
border slightly lighter for better readability on dark backgrounds
- TopBar: add '← Dashboard' back button on all non-dashboard pages
- Sidebar: make AC logo clickable to navigate home
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
loading starts as true in the store, so the previous condition
`!user && !loading` prevented fetchMe from ever being called after
the Azure AD redirect — causing a permanent Loading spinner.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- msal_auth.py: replace verify_signature=False with real JWKS verification
using PyJWKClient; validates RS256 signature, aud=clientId, issuer v2.0
- App.tsx: split DEV bypass from empty-accounts case — in production,
accounts.length === 0 now correctly triggers loginRedirect instead of
calling fetchMe without a token
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>