diff --git a/app.py b/app.py index 068de05..04d168b 100644 --- a/app.py +++ b/app.py @@ -94,13 +94,23 @@ def history(): @app.route("/reports/") def reports_index(): """Reports browser page — fixes the 404 on the Reports button.""" + import json, re runs = [] for run_dir in sorted(REPORTS_DIR.iterdir(), reverse=True) if REPORTS_DIR.exists() else []: + if not run_dir.is_dir(): + continue summary = run_dir / "summary.json" if summary.exists(): - import json try: - runs.append(json.loads(summary.read_text())) + txt = summary.read_text(encoding="utf-8") + # Fix legacy NaN values (invalid JSON) written before the fix + txt = re.sub(r'\bNaN\b', 'null', txt) + txt = re.sub(r'\bInfinity\b', 'null', txt) + data = json.loads(txt) + # Replace None scores with 0 for display + data["score"] = data.get("score") or 0 + data["score_delta"] = data.get("score_delta") or 0 + runs.append(data) except Exception: pass return render_template("reports_index.html", runs=runs[:100]) @@ -114,14 +124,22 @@ def reports_file(filename): @app.route("/api/runs") def runs_list(): + import json, re runs = [] if REPORTS_DIR.exists(): for run_dir in sorted(REPORTS_DIR.iterdir(), reverse=True): + if not run_dir.is_dir(): + continue summary = run_dir / "summary.json" if summary.exists(): - import json try: - runs.append(json.loads(summary.read_text())) + txt = summary.read_text(encoding="utf-8") + txt = re.sub(r'\bNaN\b', 'null', txt) + txt = re.sub(r'\bInfinity\b', 'null', txt) + data = json.loads(txt) + data["score"] = data.get("score") or 0 + data["score_delta"] = data.get("score_delta") or 0 + runs.append(data) except Exception: pass return jsonify(runs[:50]) diff --git a/optimizer.db-shm b/optimizer.db-shm new file mode 100644 index 0000000..37127b6 Binary files /dev/null and b/optimizer.db-shm differ diff --git a/optimizer.db-wal b/optimizer.db-wal new file mode 100644 index 0000000..a1017bd Binary files /dev/null and b/optimizer.db-wal differ diff --git a/optimizer_loop.py b/optimizer_loop.py index 6d629fb..d98717f 100644 --- a/optimizer_loop.py +++ b/optimizer_loop.py @@ -66,6 +66,7 @@ class OptimizerLoop: self.current_run_id: Optional[str] = None self.score_history: list[dict] = [] self.run_start_ts: Optional[float] = None + self.session_tested_deltas: list[dict] = [] # dedup within this session only with open(config_path) as f: self.cfg = yaml.safe_load(f) @@ -177,12 +178,11 @@ class OptimizerLoop: self._emit("log", {"level": "warn", "msg": "No actionable findings. Stopping."}) break - # Mutation proposals - recent_deltas = store.get_recent_param_deltas(cfg["mutation"]["dedup_lookback_runs"]) + # Mutation proposals — only dedup within this session hypotheses = mutator.propose( findings=findings, current_params=current_params, - recent_deltas=recent_deltas, + recent_deltas=self.session_tested_deltas, max_proposals=cfg["mutation"]["max_hypotheses_per_cycle"], ) @@ -249,6 +249,7 @@ class OptimizerLoop: hypothesis=hyp, baseline_score=current_metrics.composite_score) store.update_hypothesis_status(hyp.hypothesis_id, "tested", run_id) + self.session_tested_deltas.append(hyp.param_delta) # track for session dedup if iteration_best is None or test_metrics.composite_score > iteration_best.composite_score: iteration_best = test_metrics