File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -1078,4 +1078,28 @@ function runDataMigrations(db: DatabaseConnection): void {
10781078 db . prepare ( "ALTER TABLE pool_tiers ADD COLUMN pool_size_basis TEXT NOT NULL DEFAULT 'absolute'" ) . run ( ) ;
10791079 db . prepare ( "DELETE FROM scenario_results WHERE scenario_id IN (SELECT id FROM scenarios WHERE pool_tier_id IS NOT NULL)" ) . run ( ) ;
10801080 }
1081+
1082+ // Migration 24: scenario_results never had a uniqueness constraint on scenario_id, and
1083+ // saveResults() always generates a fresh row id, so every recalculation left the previous
1084+ // cached row orphaned instead of replacing it. Duplicate rows made a single scenario appear
1085+ // multiple times anywhere that LEFT JOINs scenario_results (e.g. scenariosRepository.getAll(),
1086+ // used by the scenarios list and Compare pages). Dedup down to the most recent row per
1087+ // scenario, then add a UNIQUE index so INSERT OR REPLACE correctly upserts on scenario_id.
1088+ const scenarioResultsIndex24 = db . prepare (
1089+ `SELECT name FROM sqlite_master WHERE type = 'index' AND name = 'idx_scenario_results_scenario_id'`
1090+ ) . get ( ) ;
1091+ if ( ! scenarioResultsIndex24 ) {
1092+ db . prepare ( `
1093+ DELETE FROM scenario_results
1094+ WHERE rowid NOT IN (
1095+ SELECT MAX(r.rowid)
1096+ FROM scenario_results r
1097+ WHERE r.calculated_at = (
1098+ SELECT MAX(r2.calculated_at) FROM scenario_results r2 WHERE r2.scenario_id = r.scenario_id
1099+ )
1100+ GROUP BY r.scenario_id
1101+ )
1102+ ` ) . run ( ) ;
1103+ db . prepare ( `CREATE UNIQUE INDEX idx_scenario_results_scenario_id ON scenario_results(scenario_id)` ) . run ( ) ;
1104+ }
10811105}
Original file line number Diff line number Diff line change 2525 import Edit2 from ' @lucide/svelte/icons/edit-2' ;
2626 import DollarSign from ' @lucide/svelte/icons/dollar-sign' ;
2727 import AlertTriangle from ' @lucide/svelte/icons/alert-triangle' ;
28+ import Route from ' @lucide/svelte/icons/route' ;
2829 import ExportButton from ' $lib/components/dashboard/ExportButton.svelte' ;
2930 import DiagnosticsBanner from ' $lib/components/dashboard/DiagnosticsBanner.svelte' ;
3031
3839 const scopeSummary = $derived (data .scopeSummary );
3940 const resolvedConfigs = $derived (data .resolvedConfigs || []);
4041
42+ const modelingTypeLabel = $derived .by (() => {
43+ switch (scenario .modeling_type ) {
44+ case ' incremental' : return ' Incremental' ;
45+ case ' gtm' : return ' GTM' ;
46+ case ' appraisal' : return ' Appraisal' ;
47+ default : return ' Appraisal' ;
48+ }
49+ });
50+
4151 const diagnostics = $derived (data .diagnostics ?? []);
4252
4353 // ADR 0009 — per-archetype stream economics (mix signal)
13701380
13711381 {#if scopeSummary }
13721382 <div class =" glass border border-border px-4 py-2.5 rounded-lg flex items-center space-x-2 text-xs select-none" >
1383+ <Route class =" h-4 w-4 text-primary shrink-0" />
1384+ <span class =" text-muted-foreground font-semibold" >Type:</span >
1385+ <Badge variant =" outline" class =" glass-inset py-0.5 px-2 font-semibold" >
1386+ {modelingTypeLabel }
1387+ </Badge >
1388+ <span class =" text-muted-foreground/60" >•</span >
13731389 <Users2 class =" h-4 w-4 text-primary shrink-0" />
13741390 <span class =" text-muted-foreground font-semibold" >Scope Targeting:</span >
13751391 <Badge variant =" outline" class =" glass-inset py-0.5 px-2 capitalize font-semibold" >
You can’t perform that action at this time.
0 commit comments