fix: live-activity persistence on refresh + reports page rebuild

Refresh-during-run no longer wipes the dashboard
- pipeline now keeps in-memory rolling logs of ai_thinking, param_changes,
  validation events, and the current early-termination state. Logs reset at
  the start of each new run (capped: 200 thinking / 50 param-change records /
  40 validation events)
- new GET /api/live_activity returns those logs + current phase + running
  state in one shot — the dashboard hits it on page load
- restoreHistory() in dashboard.js now replays each event into addThinking /
  addParamChanges / validationRunStart-Complete-Done / showEarlyTermination,
  and re-applies the active phase via setPhaseActive. Reload F5 mid-run no
  longer shows a fresh empty dashboard
- addThinking() preserves the original ts on replay (was using nowStr() so
  every replayed entry got the refresh time)

Reports page (/reports) rebuilt
- previous template crashed with 500 on legacy summary.json files that
  pre-date the win_rate / drawdown_pct fields. Server now backfills sane
  defaults for every metric the template touches
- runs now sorted by ts (was filesystem-iterdir order)
- new template: search bar + filter chips (All / Exploration / AI Iteration
  / Validation / AI insight / Has .set), phase tags, AI/.set badges, three
  action buttons per card (View Params / Download .set / Full Report)
- per-card detail modal shows metrics grid + full parameters table +
  AI reasoning + Download .set button — the missing "click to see params
  + download" path the user reported
- has_set detected per-run by globbing run_dir/*.set; AI insight tag shown
  when ai_insight.json exists on disk
This commit is contained in:
LEGSTECH Optimizer
2026-04-25 12:20:30 +00:00
parent 746ab8fb11
commit c42345ea1e
4 changed files with 522 additions and 61 deletions
+30
View File
@@ -96,6 +96,13 @@ class OptimizationPipeline:
# All completed runs (for history restoration)
self._completed_runs: list[dict] = []
# Live activity logs — persisted in-memory so a dashboard refresh during
# a run can replay them via /api/thinking, /api/param_changes, /api/validation.
self._thinking_log: list[dict] = []
self._param_changes: list[dict] = []
self._validation_log: list[dict] = []
self._early_term: Optional[dict] = None
# ── Public API ────────────────────────────────────────────────────────────
def configure(self, session: SessionConfig) -> None:
@@ -187,6 +194,11 @@ class OptimizationPipeline:
self._ai_insights.clear()
self._run_findings.clear()
self._run_insights.clear()
# Reset live-activity logs for the new run
self._thinking_log = []
self._param_changes = []
self._validation_log = []
self._early_term = None
budget.start()
@@ -1013,6 +1025,10 @@ class OptimizationPipeline:
}
if meta:
payload["meta"] = meta
# Persist for dashboard refresh — keep last 200 entries
self._thinking_log.append(payload)
if len(self._thinking_log) > 200:
self._thinking_log = self._thinking_log[-200:]
self._emit("ai_thinking", payload)
def _emit_early_termination(self, reason_code: str, message: str, details: dict = None) -> None:
@@ -1033,9 +1049,23 @@ class OptimizationPipeline:
}
if details:
payload["details"] = details
self._early_term = payload # remember for refresh
self._emit("early_termination", payload)
def _emit(self, event: str, data: dict = {}) -> None:
# Tee select live-activity events into in-memory logs so a dashboard
# refresh during the run can replay them via REST.
try:
if event == "param_changes":
self._param_changes.append(data)
if len(self._param_changes) > 50:
self._param_changes = self._param_changes[-50:]
elif event in ("validation_start", "validation_run_start", "validation_run_complete", "validation_done"):
self._validation_log.append({"event": event, **data})
if len(self._validation_log) > 40:
self._validation_log = self._validation_log[-40:]
except Exception:
pass
try:
self.socketio.emit(event, data)
except Exception as e: