FastAPI's Jinja2Templates doesn't include Flask's tojson filter.
Register json.dumps as the tojson filter so replica_detail.html
can safely embed replica.name in a JS string literal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allows promoting any replica to master with zero document re-downloads.
The sync_map rebuild uses existing DB data only — pure in-memory join.
Changes:
- app/sync/promote.py: preflight() checks (doc count, sync lock, ack
warnings) and promote() transaction (pause scheduler, rebuild all
sync_maps, create old-master replica, swap settings, resume scheduler)
- app/api/master.py: GET /api/master/promote/{id}/preflight (dry run)
and POST /api/master/promote/{id} (execute)
- app/models.py: add promoted_from_master bool field to Replica
- app/database.py: idempotent ALTER TABLE migration for new column
- app/main.py: register master router
- app/templates/replica_detail.html: "Promote to Master" button +
dialog with pre-flight summary, 3-card stats, ack checkboxes, spinner
- app/ui/routes.py: flash query param on dashboard route
- app/templates/dashboard.html: blue info banner for post-promotion flash
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Two-column layout: add form (left) + prerequisites guide (right)
- Help sidebar with numbered checklist: reachability, API token, interval
- Callout for existing-instance reconcile workflow
- "What gets synced" info card with primary-color accent border
- Form fields with helper text under each input
- Spinner/busy state on submit buttons during API calls
- Clean status badges replacing emoji in replica table
- Better modal footer layout for edit dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Needed to safely recover missing docs: run reconcile first (populates
sync_map for existing docs), then reset-ts, then sync (only missing
docs get uploaded, existing ones get metadata patch only).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>