Conversation
- Add ability to dismiss AI candidates to exclude from future discoveries - Store dismissed candidates in SQLite with reasoning and tags - Batch dismiss, undo dismiss, and clear all dismissed endpoints - Debug endpoints for troubleshooting AI run failures - Fix stdin piping for large prompts to CLI providers - Fix JSON parsing to handle noisy CLI output with banners - Capture error details on failed runs for debugging
New 6th tree view mode plotting ancestors on an interactive Leaflet.js world map with lineage-colored markers, migration polylines, time filtering, and paternal/maternal layer toggles. Includes Nominatim geocoding with progressive broadening for historical place names, permanent SQLite cache, and SSE-streamed batch geocoding.
There was a problem hiding this comment.
Pull request overview
This PR introduces a comprehensive migration map visualization feature alongside AI discovery improvements and debugging capabilities. The migration map is a new tree view mode that plots ancestors geographically on an interactive Leaflet.js map with lineage-colored markers, migration polylines, time-based filtering, and geocoding via Nominatim. Additionally, the PR adds functionality to dismiss AI discovery candidates, debug endpoints for troubleshooting AI runs, and integrates the AI toolkit service for better provider management.
Changes:
- Migration Map visualization with Leaflet.js, including geocoding service with Nominatim integration and permanent SQLite cache
- AI Discovery dismiss/restore functionality with new
discovery_dismissedtable - AI Discovery debug endpoints exposing run metadata, prompts, and outputs
- Progressive geocoding with broadening strategy for historical place names
- SSE-based batch geocoding with EventSource for real-time progress
Reviewed changes
Copilot reviewed 28 out of 29 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| server/src/db/migrations/005_discovery_dismissed.ts | New table for tracking dismissed AI candidates |
| server/src/db/migrations/006_place_geocode.ts | New table for geocoding cache |
| server/src/services/geocode.service.ts | Nominatim integration with rate limiting and caching |
| server/src/services/map.service.ts | Map data assembly joining tree data with geocoded coordinates |
| server/src/routes/map.routes.ts | REST and SSE endpoints for map data and geocoding |
| server/src/services/ai-discovery.service.ts | AI toolkit integration and dismiss functionality |
| server/src/routes/ai-discovery.routes.ts | Debug endpoints and dismiss operations |
| client/src/components/ancestry-tree/views/MigrationMapView.tsx | Leaflet map component with filtering and controls |
| client/src/components/map/GeocodeProgressBar.tsx | SSE progress UI for batch geocoding |
| client/src/components/map/mapUtils.ts | Marker and popup HTML generation utilities |
| client/src/components/favorites/SparseTreeMapPage.tsx | Standalone map page for favorites |
| client/src/components/ai/AiDiscoveryModal.tsx | Dismiss UI and generation filtering |
| shared/src/index.ts | Type definitions for map data structures |
| client/src/services/api.ts | API methods for map and dismiss operations |
| package.json files | Version bumps and new dependencies (leaflet, react-leaflet) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add HTML escaping in map popup to prevent XSS - Add network error handling in Nominatim fetch - Enforce global rate limit timestamp across broadening attempts - Use dynamic app version in User-Agent header - Validate depth param with max bound of 15 - Remove auto-reset of global geocode cache from stream endpoint - Remove unused databaseService import - Fix lineage fallback to 'self' instead of arbitrary 'paternal' - Add safe JSON parse for AI discovery response - Update changelog title to reflect full scope
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 13 comments.
Comments suppressed due to low confidence (1)
server/src/services/ai-discovery.service.ts:460
maxGenerationsis applied only in the SQLite query branch. In the fallback branch (!databaseService.isSqliteEnabled()), the filter is ignored, so requests withmaxGenerationswon’t behave consistently depending on backend mode. Consider applying a generation filter in the fallback path too (if generation info is available), or rejecting/ignoring the option consistently when SQLite is disabled.
} else {
// Fallback to loading all
const db = await databaseService.getDatabase(dbId);
personsToAnalyze = Object.entries(db)
.filter(([id, person]) => {
if (excludeIds.has(id)) return false;
if (minBirthYear !== undefined) {
const birthYear = getBirthYear(person);
if (birthYear !== null && birthYear < minBirthYear) return false;
}
return true;
})
.slice(0, sampleSize)
.map(([id, person]) => ({ id, person }));
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Distinguish not_found vs error in geocoding (errors are retryable) - Serialize Nominatim requests with promise queue for strict rate limiting - Add contact URL to User-Agent for Nominatim compliance - Remove duplicate complete event (batchGeocode + route) - Use shared types in map.service.ts instead of duplicating - Add error count to MapData.geocodeStats in shared types - Fix empty state to check markersWithCoords instead of persons count - Add EventSource cleanup on component unmount + NaN guard - Use safeJsonParse for dismissed candidates JSON - Remove hardcoded ULID prefix in parseAiResponse - Update log message from "Claude CLI" to "AI provider" - Clarify parentId/childId semantics in flattenAncestryTree
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Exclude not_found places from ungeocoded count (consistent with getUngeocodedPlaces) - Fix migration line direction: normalize using generation numbers (ancestor → descendant) - Skip persons without geocoded coordinates from map data - Gate AI debug endpoints behind ENABLE_AI_DEBUG env var / non-production mode - Validate dbId exists before starting geocode stream - Skip redundant ancestry tree fetch when map view is active
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (2)
server/src/routes/ai-discovery.routes.ts:35
- The
sampleSizeparameter in the quick discovery endpoint is not validated on the server side. A malicious client could send an extremely large value (e.g., millions) which could cause performance issues or resource exhaustion when querying the database and sending large prompts to the AI provider. Consider adding validation to ensure sampleSize is within reasonable bounds (e.g., max 10000) and return a 400 error if exceeded.
router.post('/:dbId/quick', async (req: Request, res: Response) => {
const { dbId } = req.params;
const { sampleSize, model, excludeBiblical, minBirthYear, maxGenerations, customPrompt } = req.body;
logger.start('ai-discovery', `Quick discovery request dbId=${dbId} sample=${sampleSize || 100} model=${model || 'default'} excludeBiblical=${excludeBiblical || false} maxGenerations=${maxGenerations || 'all'} prompt=${customPrompt ? `"${customPrompt.slice(0, 50)}..."` : 'none'}`);
const result = await aiDiscoveryService.quickDiscovery(dbId, {
sampleSize: sampleSize || 100,
model,
excludeBiblical: excludeBiblical || false,
minBirthYear,
maxGenerations,
customPrompt,
}).catch(err => {
logger.error('ai-discovery', `Quick discovery failed: ${err.message}`);
res.status(500).json({ success: false, error: err.message });
return null;
});
if (result !== null) {
logger.done('ai-discovery', `Quick discovery complete: analyzed=${result.totalAnalyzed} candidates=${result.candidates.length}`);
res.json({ success: true, data: result });
}
});
server/src/routes/ai-discovery.routes.ts:35
- The
maxGenerationsparameter is not validated on the server side. While the client limits it to 100, a malicious client could send an arbitrarily large value or negative number. Consider adding server-side validation to ensure maxGenerations is within reasonable bounds (e.g., 1-100) if provided.
router.post('/:dbId/quick', async (req: Request, res: Response) => {
const { dbId } = req.params;
const { sampleSize, model, excludeBiblical, minBirthYear, maxGenerations, customPrompt } = req.body;
logger.start('ai-discovery', `Quick discovery request dbId=${dbId} sample=${sampleSize || 100} model=${model || 'default'} excludeBiblical=${excludeBiblical || false} maxGenerations=${maxGenerations || 'all'} prompt=${customPrompt ? `"${customPrompt.slice(0, 50)}..."` : 'none'}`);
const result = await aiDiscoveryService.quickDiscovery(dbId, {
sampleSize: sampleSize || 100,
model,
excludeBiblical: excludeBiblical || false,
minBirthYear,
maxGenerations,
customPrompt,
}).catch(err => {
logger.error('ai-discovery', `Quick discovery failed: ${err.message}`);
res.status(500).json({ success: false, error: err.message });
return null;
});
if (result !== null) {
logger.done('ai-discovery', `Quick discovery complete: analyzed=${result.totalAnalyzed} candidates=${result.candidates.length}`);
res.json({ success: true, data: result });
}
});
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add max batch size (1000) validation for apply-batch and dismiss-batch - Convert correlated subqueries to JOINs in getPersonsWithPlaces - Add error handling for SSE geocode stream with error event to client
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 29 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
New Features
Migration Map (Phase 15.23)
/favorites/sparse-tree/:dbId/mapAI Discovery Improvements
discovery_dismissedSQLite tableGET /api/ai-discovery/debug/runs,GET /api/ai-discovery/debug/runs/:runIdAPI Endpoints
/api/map/:dbId/:personId/api/map/:dbId/sparse/api/map/geocode/stream?dbId=/api/map/geocode/stats/api/map/geocode/reset-not-found/api/ai-discovery/:dbId/dismiss/api/ai-discovery/:dbId/dismiss-batch/api/ai-discovery/:dbId/dismissed/api/ai-discovery/:dbId/dismissed/:personId/api/ai-discovery/:dbId/dismissed/api/ai-discovery/debug/runs/api/ai-discovery/debug/runs/:runIdFiles Changed
New files (10): migrations (005, 006), geocode service, map service, map routes, MigrationMapView, SparseTreeMapPage, GeocodeProgressBar, mapUtils
Modified files (19): schema.sql, migrations index, server index, shared types, client api, AncestryTreeView, SparseTreePage, App.tsx, ai-discovery routes/service/modal, package files, changelog, PLAN.md, README
Test Plan