maestro-flow 0.4.21 → 0.4.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/.agents/skills/team-adversarial-swarm/scripts/__pycache__/pheromone.cpython-313.pyc +0 -0
  2. package/.agents/skills/team-adversarial-swarm/scripts/__pycache__/scoring.cpython-313.pyc +0 -0
  3. package/.agents/skills/team-adversarial-swarm/scripts/aco.py +15 -15
  4. package/.agents/skills/team-adversarial-swarm/scripts/pheromone.py +2 -2
  5. package/.agents/skills/team-adversarial-swarm/scripts/scoring.py +1 -1
  6. package/.agents/skills/team-swarm/scripts/aco.py +15 -15
  7. package/.agents/skills/team-swarm/scripts/pheromone.py +2 -2
  8. package/.agents/skills/team-swarm/scripts/scoring.py +1 -1
  9. package/.agy/skills/team-adversarial-swarm/scripts/__pycache__/pheromone.cpython-313.pyc +0 -0
  10. package/.agy/skills/team-adversarial-swarm/scripts/__pycache__/scoring.cpython-313.pyc +0 -0
  11. package/.agy/skills/team-adversarial-swarm/scripts/aco.py +15 -15
  12. package/.agy/skills/team-adversarial-swarm/scripts/pheromone.py +2 -2
  13. package/.agy/skills/team-adversarial-swarm/scripts/scoring.py +1 -1
  14. package/.agy/skills/team-swarm/scripts/aco.py +15 -15
  15. package/.agy/skills/team-swarm/scripts/pheromone.py +2 -2
  16. package/.agy/skills/team-swarm/scripts/scoring.py +1 -1
  17. package/.claude/skills/team-adversarial-swarm/scripts/__pycache__/pheromone.cpython-313.pyc +0 -0
  18. package/.claude/skills/team-adversarial-swarm/scripts/__pycache__/scoring.cpython-313.pyc +0 -0
  19. package/.claude/skills/team-adversarial-swarm/scripts/aco.py +15 -15
  20. package/.claude/skills/team-adversarial-swarm/scripts/pheromone.py +2 -2
  21. package/.claude/skills/team-adversarial-swarm/scripts/scoring.py +1 -1
  22. package/.claude/skills/team-swarm/scripts/aco.py +15 -15
  23. package/.claude/skills/team-swarm/scripts/pheromone.py +2 -2
  24. package/.claude/skills/team-swarm/scripts/scoring.py +1 -1
  25. package/package.json +1 -1
@@ -89,7 +89,7 @@ def cmd_init(args: argparse.Namespace) -> None:
89
89
  paths = SessionPaths(Path(args.session))
90
90
  if not paths.config.exists():
91
91
  _fail(2, f"config not found: {paths.config}")
92
- config = json.loads(paths.config.read_text())
92
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
93
93
 
94
94
  paths.ensure_dirs()
95
95
 
@@ -107,7 +107,7 @@ def cmd_init(args: argparse.Namespace) -> None:
107
107
  "start_nodes": config.get("task_space", {}).get("start_nodes", "any"),
108
108
  "edges": config.get("task_space", {}).get("edges", "complete"),
109
109
  }
110
- paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False))
110
+ paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False), encoding="utf-8")
111
111
 
112
112
  # Initialize pheromone
113
113
  aco_cfg = config.get("aco", {})
@@ -148,9 +148,9 @@ def _pick_start_node(nodes: List[str], state: PheromoneState, mode: str) -> str:
148
148
 
149
149
  def cmd_select(args: argparse.Namespace) -> None:
150
150
  paths = SessionPaths(Path(args.session))
151
- config = json.loads(paths.config.read_text())
151
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
152
152
  state = PheromoneState.load(paths.pheromone_current)
153
- task_space = json.loads(paths.task_space.read_text())
153
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
154
154
 
155
155
  n_ants = config.get("swarm", {}).get("n_ants", 5)
156
156
  nodes = task_space["nodes"]
@@ -190,7 +190,7 @@ def _load_iteration_artifacts(paths: SessionPaths, iteration: int) -> List[dict]
190
190
  artifacts = []
191
191
  for f in files:
192
192
  try:
193
- artifacts.append(json.loads(Path(f).read_text()))
193
+ artifacts.append(json.loads(Path(f).read_text(encoding="utf-8")))
194
194
  except json.JSONDecodeError as e:
195
195
  print(f"warning: skipped malformed artifact {f}: {e}", file=sys.stderr)
196
196
  return artifacts
@@ -213,9 +213,9 @@ def _validate_artifact(art: dict, valid_nodes: set) -> Optional[str]:
213
213
 
214
214
  def cmd_update(args: argparse.Namespace) -> None:
215
215
  paths = SessionPaths(Path(args.session))
216
- config = json.loads(paths.config.read_text())
216
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
217
217
  state = PheromoneState.load(paths.pheromone_current)
218
- task_space = json.loads(paths.task_space.read_text())
218
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
219
219
  valid_nodes = set(task_space["nodes"])
220
220
 
221
221
  artifacts = _load_iteration_artifacts(paths, args.iter)
@@ -266,7 +266,7 @@ def cmd_update(args: argparse.Namespace) -> None:
266
266
  # Elitist: re-load best history, deposit extra on best path
267
267
  best_data = None
268
268
  if paths.best.exists():
269
- best_data = json.loads(paths.best.read_text())
269
+ best_data = json.loads(paths.best.read_text(encoding="utf-8"))
270
270
  current_best = max(scored, key=lambda x: x["score"]) if scored else None
271
271
  if current_best:
272
272
  best_art = next(a for a in artifacts if a["ant_id"] == current_best["ant_id"])
@@ -281,7 +281,7 @@ def cmd_update(args: argparse.Namespace) -> None:
281
281
  "evidence": best_art.get("evidence", []),
282
282
  "updated_at": time.time(),
283
283
  }
284
- paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False))
284
+ paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False), encoding="utf-8")
285
285
  # Elite deposit
286
286
  state.deposit(best_data["path"], best_data["score"])
287
287
 
@@ -292,14 +292,14 @@ def cmd_update(args: argparse.Namespace) -> None:
292
292
 
293
293
  # Persist trails
294
294
  trails_file = paths.trails / f"{args.iter}.jsonl"
295
- trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log))
295
+ trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log), encoding="utf-8")
296
296
 
297
297
  mean_score = sum(s["score"] for s in scored) / len(scored) if scored else 0.0
298
298
  best_score = best_data["score"] if best_data else 0.0
299
299
  prev_best = 0.0
300
300
  history_files = sorted(paths.pheromone_history.glob("*.json"))
301
301
  if len(history_files) >= 2:
302
- prev = json.loads(history_files[-2].read_text())
302
+ prev = json.loads(history_files[-2].read_text(encoding="utf-8"))
303
303
  prev_best = prev.get("stats", {}).get("best_known", best_score)
304
304
  delta = best_score - prev_best
305
305
 
@@ -323,7 +323,7 @@ def cmd_update(args: argparse.Namespace) -> None:
323
323
 
324
324
  def cmd_converged(args: argparse.Namespace) -> None:
325
325
  paths = SessionPaths(Path(args.session))
326
- config = json.loads(paths.config.read_text())
326
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
327
327
  cv = config.get("convergence", {})
328
328
 
329
329
  state = PheromoneState.load(paths.pheromone_current)
@@ -339,7 +339,7 @@ def cmd_converged(args: argparse.Namespace) -> None:
339
339
  }
340
340
 
341
341
  if paths.best.exists():
342
- metrics["best_score"] = json.loads(paths.best.read_text()).get("score", 0.0)
342
+ metrics["best_score"] = json.loads(paths.best.read_text(encoding="utf-8")).get("score", 0.0)
343
343
 
344
344
  # max_iterations
345
345
  max_iter = cv.get("max_iterations", 5)
@@ -397,7 +397,7 @@ def cmd_report(args: argparse.Namespace) -> None:
397
397
 
398
398
  best = None
399
399
  if paths.best.exists():
400
- best = json.loads(paths.best.read_text())
400
+ best = json.loads(paths.best.read_text(encoding="utf-8"))
401
401
 
402
402
  # Top-K trails across all iterations
403
403
  all_trails = []
@@ -410,7 +410,7 @@ def cmd_report(args: argparse.Namespace) -> None:
410
410
  # Convergence curve
411
411
  curve = []
412
412
  for hf in sorted(paths.pheromone_history.glob("*.json"), key=lambda p: int(p.stem)):
413
- snap = json.loads(hf.read_text())
413
+ snap = json.loads(hf.read_text(encoding="utf-8"))
414
414
  curve.append({
415
415
  "iteration": snap["iteration"],
416
416
  "entropy": snap["stats"]["entropy"],
@@ -114,11 +114,11 @@ class PheromoneState:
114
114
 
115
115
  def save(self, path: Path) -> None:
116
116
  path.parent.mkdir(parents=True, exist_ok=True)
117
- path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False))
117
+ path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False), encoding="utf-8")
118
118
 
119
119
  @classmethod
120
120
  def load(cls, path: Path) -> "PheromoneState":
121
- return cls.from_dict(json.loads(path.read_text()))
121
+ return cls.from_dict(json.loads(path.read_text(encoding="utf-8")))
122
122
 
123
123
  def select_neighbors(
124
124
  self,
@@ -62,7 +62,7 @@ def load_verified_scores(scores_file: Path) -> Dict[str, float]:
62
62
  """Load pre-computed verified_scores from scorer role output (if exists)."""
63
63
  if not scores_file.exists():
64
64
  return {}
65
- data = json.loads(scores_file.read_text())
65
+ data = json.loads(scores_file.read_text(encoding="utf-8"))
66
66
  return {
67
67
  ant_id: entry["verified_score"]
68
68
  for ant_id, entry in data.get("scores", {}).items()
@@ -89,7 +89,7 @@ def cmd_init(args: argparse.Namespace) -> None:
89
89
  paths = SessionPaths(Path(args.session))
90
90
  if not paths.config.exists():
91
91
  _fail(2, f"config not found: {paths.config}")
92
- config = json.loads(paths.config.read_text())
92
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
93
93
 
94
94
  paths.ensure_dirs()
95
95
 
@@ -107,7 +107,7 @@ def cmd_init(args: argparse.Namespace) -> None:
107
107
  "start_nodes": config.get("task_space", {}).get("start_nodes", "any"),
108
108
  "edges": config.get("task_space", {}).get("edges", "complete"),
109
109
  }
110
- paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False))
110
+ paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False), encoding="utf-8")
111
111
 
112
112
  # Initialize pheromone
113
113
  aco_cfg = config.get("aco", {})
@@ -148,9 +148,9 @@ def _pick_start_node(nodes: List[str], state: PheromoneState, mode: str) -> str:
148
148
 
149
149
  def cmd_select(args: argparse.Namespace) -> None:
150
150
  paths = SessionPaths(Path(args.session))
151
- config = json.loads(paths.config.read_text())
151
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
152
152
  state = PheromoneState.load(paths.pheromone_current)
153
- task_space = json.loads(paths.task_space.read_text())
153
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
154
154
 
155
155
  n_ants = config.get("swarm", {}).get("n_ants", 5)
156
156
  nodes = task_space["nodes"]
@@ -190,7 +190,7 @@ def _load_iteration_artifacts(paths: SessionPaths, iteration: int) -> List[dict]
190
190
  artifacts = []
191
191
  for f in files:
192
192
  try:
193
- artifacts.append(json.loads(Path(f).read_text()))
193
+ artifacts.append(json.loads(Path(f).read_text(encoding="utf-8")))
194
194
  except json.JSONDecodeError as e:
195
195
  print(f"warning: skipped malformed artifact {f}: {e}", file=sys.stderr)
196
196
  return artifacts
@@ -213,9 +213,9 @@ def _validate_artifact(art: dict, valid_nodes: set) -> Optional[str]:
213
213
 
214
214
  def cmd_update(args: argparse.Namespace) -> None:
215
215
  paths = SessionPaths(Path(args.session))
216
- config = json.loads(paths.config.read_text())
216
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
217
217
  state = PheromoneState.load(paths.pheromone_current)
218
- task_space = json.loads(paths.task_space.read_text())
218
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
219
219
  valid_nodes = set(task_space["nodes"])
220
220
 
221
221
  artifacts = _load_iteration_artifacts(paths, args.iter)
@@ -266,7 +266,7 @@ def cmd_update(args: argparse.Namespace) -> None:
266
266
  # Elitist: re-load best history, deposit extra on best path
267
267
  best_data = None
268
268
  if paths.best.exists():
269
- best_data = json.loads(paths.best.read_text())
269
+ best_data = json.loads(paths.best.read_text(encoding="utf-8"))
270
270
  current_best = max(scored, key=lambda x: x["score"]) if scored else None
271
271
  if current_best:
272
272
  best_art = next(a for a in artifacts if a["ant_id"] == current_best["ant_id"])
@@ -281,7 +281,7 @@ def cmd_update(args: argparse.Namespace) -> None:
281
281
  "evidence": best_art.get("evidence", []),
282
282
  "updated_at": time.time(),
283
283
  }
284
- paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False))
284
+ paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False), encoding="utf-8")
285
285
  # Elite deposit
286
286
  state.deposit(best_data["path"], best_data["score"])
287
287
 
@@ -292,14 +292,14 @@ def cmd_update(args: argparse.Namespace) -> None:
292
292
 
293
293
  # Persist trails
294
294
  trails_file = paths.trails / f"{args.iter}.jsonl"
295
- trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log))
295
+ trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log), encoding="utf-8")
296
296
 
297
297
  mean_score = sum(s["score"] for s in scored) / len(scored) if scored else 0.0
298
298
  best_score = best_data["score"] if best_data else 0.0
299
299
  prev_best = 0.0
300
300
  history_files = sorted(paths.pheromone_history.glob("*.json"))
301
301
  if len(history_files) >= 2:
302
- prev = json.loads(history_files[-2].read_text())
302
+ prev = json.loads(history_files[-2].read_text(encoding="utf-8"))
303
303
  prev_best = prev.get("stats", {}).get("best_known", best_score)
304
304
  delta = best_score - prev_best
305
305
 
@@ -323,7 +323,7 @@ def cmd_update(args: argparse.Namespace) -> None:
323
323
 
324
324
  def cmd_converged(args: argparse.Namespace) -> None:
325
325
  paths = SessionPaths(Path(args.session))
326
- config = json.loads(paths.config.read_text())
326
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
327
327
  cv = config.get("convergence", {})
328
328
 
329
329
  state = PheromoneState.load(paths.pheromone_current)
@@ -339,7 +339,7 @@ def cmd_converged(args: argparse.Namespace) -> None:
339
339
  }
340
340
 
341
341
  if paths.best.exists():
342
- metrics["best_score"] = json.loads(paths.best.read_text()).get("score", 0.0)
342
+ metrics["best_score"] = json.loads(paths.best.read_text(encoding="utf-8")).get("score", 0.0)
343
343
 
344
344
  # max_iterations
345
345
  max_iter = cv.get("max_iterations", 5)
@@ -397,7 +397,7 @@ def cmd_report(args: argparse.Namespace) -> None:
397
397
 
398
398
  best = None
399
399
  if paths.best.exists():
400
- best = json.loads(paths.best.read_text())
400
+ best = json.loads(paths.best.read_text(encoding="utf-8"))
401
401
 
402
402
  # Top-K trails across all iterations
403
403
  all_trails = []
@@ -410,7 +410,7 @@ def cmd_report(args: argparse.Namespace) -> None:
410
410
  # Convergence curve
411
411
  curve = []
412
412
  for hf in sorted(paths.pheromone_history.glob("*.json"), key=lambda p: int(p.stem)):
413
- snap = json.loads(hf.read_text())
413
+ snap = json.loads(hf.read_text(encoding="utf-8"))
414
414
  curve.append({
415
415
  "iteration": snap["iteration"],
416
416
  "entropy": snap["stats"]["entropy"],
@@ -114,11 +114,11 @@ class PheromoneState:
114
114
 
115
115
  def save(self, path: Path) -> None:
116
116
  path.parent.mkdir(parents=True, exist_ok=True)
117
- path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False))
117
+ path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False), encoding="utf-8")
118
118
 
119
119
  @classmethod
120
120
  def load(cls, path: Path) -> "PheromoneState":
121
- return cls.from_dict(json.loads(path.read_text()))
121
+ return cls.from_dict(json.loads(path.read_text(encoding="utf-8")))
122
122
 
123
123
  def select_neighbors(
124
124
  self,
@@ -62,7 +62,7 @@ def load_verified_scores(scores_file: Path) -> Dict[str, float]:
62
62
  """Load pre-computed verified_scores from scorer role output (if exists)."""
63
63
  if not scores_file.exists():
64
64
  return {}
65
- data = json.loads(scores_file.read_text())
65
+ data = json.loads(scores_file.read_text(encoding="utf-8"))
66
66
  return {
67
67
  ant_id: entry["verified_score"]
68
68
  for ant_id, entry in data.get("scores", {}).items()
@@ -89,7 +89,7 @@ def cmd_init(args: argparse.Namespace) -> None:
89
89
  paths = SessionPaths(Path(args.session))
90
90
  if not paths.config.exists():
91
91
  _fail(2, f"config not found: {paths.config}")
92
- config = json.loads(paths.config.read_text())
92
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
93
93
 
94
94
  paths.ensure_dirs()
95
95
 
@@ -107,7 +107,7 @@ def cmd_init(args: argparse.Namespace) -> None:
107
107
  "start_nodes": config.get("task_space", {}).get("start_nodes", "any"),
108
108
  "edges": config.get("task_space", {}).get("edges", "complete"),
109
109
  }
110
- paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False))
110
+ paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False), encoding="utf-8")
111
111
 
112
112
  # Initialize pheromone
113
113
  aco_cfg = config.get("aco", {})
@@ -148,9 +148,9 @@ def _pick_start_node(nodes: List[str], state: PheromoneState, mode: str) -> str:
148
148
 
149
149
  def cmd_select(args: argparse.Namespace) -> None:
150
150
  paths = SessionPaths(Path(args.session))
151
- config = json.loads(paths.config.read_text())
151
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
152
152
  state = PheromoneState.load(paths.pheromone_current)
153
- task_space = json.loads(paths.task_space.read_text())
153
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
154
154
 
155
155
  n_ants = config.get("swarm", {}).get("n_ants", 5)
156
156
  nodes = task_space["nodes"]
@@ -190,7 +190,7 @@ def _load_iteration_artifacts(paths: SessionPaths, iteration: int) -> List[dict]
190
190
  artifacts = []
191
191
  for f in files:
192
192
  try:
193
- artifacts.append(json.loads(Path(f).read_text()))
193
+ artifacts.append(json.loads(Path(f).read_text(encoding="utf-8")))
194
194
  except json.JSONDecodeError as e:
195
195
  print(f"warning: skipped malformed artifact {f}: {e}", file=sys.stderr)
196
196
  return artifacts
@@ -213,9 +213,9 @@ def _validate_artifact(art: dict, valid_nodes: set) -> Optional[str]:
213
213
 
214
214
  def cmd_update(args: argparse.Namespace) -> None:
215
215
  paths = SessionPaths(Path(args.session))
216
- config = json.loads(paths.config.read_text())
216
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
217
217
  state = PheromoneState.load(paths.pheromone_current)
218
- task_space = json.loads(paths.task_space.read_text())
218
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
219
219
  valid_nodes = set(task_space["nodes"])
220
220
 
221
221
  artifacts = _load_iteration_artifacts(paths, args.iter)
@@ -266,7 +266,7 @@ def cmd_update(args: argparse.Namespace) -> None:
266
266
  # Elitist: re-load best history, deposit extra on best path
267
267
  best_data = None
268
268
  if paths.best.exists():
269
- best_data = json.loads(paths.best.read_text())
269
+ best_data = json.loads(paths.best.read_text(encoding="utf-8"))
270
270
  current_best = max(scored, key=lambda x: x["score"]) if scored else None
271
271
  if current_best:
272
272
  best_art = next(a for a in artifacts if a["ant_id"] == current_best["ant_id"])
@@ -281,7 +281,7 @@ def cmd_update(args: argparse.Namespace) -> None:
281
281
  "evidence": best_art.get("evidence", []),
282
282
  "updated_at": time.time(),
283
283
  }
284
- paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False))
284
+ paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False), encoding="utf-8")
285
285
  # Elite deposit
286
286
  state.deposit(best_data["path"], best_data["score"])
287
287
 
@@ -292,14 +292,14 @@ def cmd_update(args: argparse.Namespace) -> None:
292
292
 
293
293
  # Persist trails
294
294
  trails_file = paths.trails / f"{args.iter}.jsonl"
295
- trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log))
295
+ trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log), encoding="utf-8")
296
296
 
297
297
  mean_score = sum(s["score"] for s in scored) / len(scored) if scored else 0.0
298
298
  best_score = best_data["score"] if best_data else 0.0
299
299
  prev_best = 0.0
300
300
  history_files = sorted(paths.pheromone_history.glob("*.json"))
301
301
  if len(history_files) >= 2:
302
- prev = json.loads(history_files[-2].read_text())
302
+ prev = json.loads(history_files[-2].read_text(encoding="utf-8"))
303
303
  prev_best = prev.get("stats", {}).get("best_known", best_score)
304
304
  delta = best_score - prev_best
305
305
 
@@ -323,7 +323,7 @@ def cmd_update(args: argparse.Namespace) -> None:
323
323
 
324
324
  def cmd_converged(args: argparse.Namespace) -> None:
325
325
  paths = SessionPaths(Path(args.session))
326
- config = json.loads(paths.config.read_text())
326
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
327
327
  cv = config.get("convergence", {})
328
328
 
329
329
  state = PheromoneState.load(paths.pheromone_current)
@@ -339,7 +339,7 @@ def cmd_converged(args: argparse.Namespace) -> None:
339
339
  }
340
340
 
341
341
  if paths.best.exists():
342
- metrics["best_score"] = json.loads(paths.best.read_text()).get("score", 0.0)
342
+ metrics["best_score"] = json.loads(paths.best.read_text(encoding="utf-8")).get("score", 0.0)
343
343
 
344
344
  # max_iterations
345
345
  max_iter = cv.get("max_iterations", 5)
@@ -397,7 +397,7 @@ def cmd_report(args: argparse.Namespace) -> None:
397
397
 
398
398
  best = None
399
399
  if paths.best.exists():
400
- best = json.loads(paths.best.read_text())
400
+ best = json.loads(paths.best.read_text(encoding="utf-8"))
401
401
 
402
402
  # Top-K trails across all iterations
403
403
  all_trails = []
@@ -410,7 +410,7 @@ def cmd_report(args: argparse.Namespace) -> None:
410
410
  # Convergence curve
411
411
  curve = []
412
412
  for hf in sorted(paths.pheromone_history.glob("*.json"), key=lambda p: int(p.stem)):
413
- snap = json.loads(hf.read_text())
413
+ snap = json.loads(hf.read_text(encoding="utf-8"))
414
414
  curve.append({
415
415
  "iteration": snap["iteration"],
416
416
  "entropy": snap["stats"]["entropy"],
@@ -114,11 +114,11 @@ class PheromoneState:
114
114
 
115
115
  def save(self, path: Path) -> None:
116
116
  path.parent.mkdir(parents=True, exist_ok=True)
117
- path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False))
117
+ path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False), encoding="utf-8")
118
118
 
119
119
  @classmethod
120
120
  def load(cls, path: Path) -> "PheromoneState":
121
- return cls.from_dict(json.loads(path.read_text()))
121
+ return cls.from_dict(json.loads(path.read_text(encoding="utf-8")))
122
122
 
123
123
  def select_neighbors(
124
124
  self,
@@ -62,7 +62,7 @@ def load_verified_scores(scores_file: Path) -> Dict[str, float]:
62
62
  """Load pre-computed verified_scores from scorer role output (if exists)."""
63
63
  if not scores_file.exists():
64
64
  return {}
65
- data = json.loads(scores_file.read_text())
65
+ data = json.loads(scores_file.read_text(encoding="utf-8"))
66
66
  return {
67
67
  ant_id: entry["verified_score"]
68
68
  for ant_id, entry in data.get("scores", {}).items()
@@ -89,7 +89,7 @@ def cmd_init(args: argparse.Namespace) -> None:
89
89
  paths = SessionPaths(Path(args.session))
90
90
  if not paths.config.exists():
91
91
  _fail(2, f"config not found: {paths.config}")
92
- config = json.loads(paths.config.read_text())
92
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
93
93
 
94
94
  paths.ensure_dirs()
95
95
 
@@ -107,7 +107,7 @@ def cmd_init(args: argparse.Namespace) -> None:
107
107
  "start_nodes": config.get("task_space", {}).get("start_nodes", "any"),
108
108
  "edges": config.get("task_space", {}).get("edges", "complete"),
109
109
  }
110
- paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False))
110
+ paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False), encoding="utf-8")
111
111
 
112
112
  # Initialize pheromone
113
113
  aco_cfg = config.get("aco", {})
@@ -148,9 +148,9 @@ def _pick_start_node(nodes: List[str], state: PheromoneState, mode: str) -> str:
148
148
 
149
149
  def cmd_select(args: argparse.Namespace) -> None:
150
150
  paths = SessionPaths(Path(args.session))
151
- config = json.loads(paths.config.read_text())
151
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
152
152
  state = PheromoneState.load(paths.pheromone_current)
153
- task_space = json.loads(paths.task_space.read_text())
153
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
154
154
 
155
155
  n_ants = config.get("swarm", {}).get("n_ants", 5)
156
156
  nodes = task_space["nodes"]
@@ -190,7 +190,7 @@ def _load_iteration_artifacts(paths: SessionPaths, iteration: int) -> List[dict]
190
190
  artifacts = []
191
191
  for f in files:
192
192
  try:
193
- artifacts.append(json.loads(Path(f).read_text()))
193
+ artifacts.append(json.loads(Path(f).read_text(encoding="utf-8")))
194
194
  except json.JSONDecodeError as e:
195
195
  print(f"warning: skipped malformed artifact {f}: {e}", file=sys.stderr)
196
196
  return artifacts
@@ -213,9 +213,9 @@ def _validate_artifact(art: dict, valid_nodes: set) -> Optional[str]:
213
213
 
214
214
  def cmd_update(args: argparse.Namespace) -> None:
215
215
  paths = SessionPaths(Path(args.session))
216
- config = json.loads(paths.config.read_text())
216
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
217
217
  state = PheromoneState.load(paths.pheromone_current)
218
- task_space = json.loads(paths.task_space.read_text())
218
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
219
219
  valid_nodes = set(task_space["nodes"])
220
220
 
221
221
  artifacts = _load_iteration_artifacts(paths, args.iter)
@@ -266,7 +266,7 @@ def cmd_update(args: argparse.Namespace) -> None:
266
266
  # Elitist: re-load best history, deposit extra on best path
267
267
  best_data = None
268
268
  if paths.best.exists():
269
- best_data = json.loads(paths.best.read_text())
269
+ best_data = json.loads(paths.best.read_text(encoding="utf-8"))
270
270
  current_best = max(scored, key=lambda x: x["score"]) if scored else None
271
271
  if current_best:
272
272
  best_art = next(a for a in artifacts if a["ant_id"] == current_best["ant_id"])
@@ -281,7 +281,7 @@ def cmd_update(args: argparse.Namespace) -> None:
281
281
  "evidence": best_art.get("evidence", []),
282
282
  "updated_at": time.time(),
283
283
  }
284
- paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False))
284
+ paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False), encoding="utf-8")
285
285
  # Elite deposit
286
286
  state.deposit(best_data["path"], best_data["score"])
287
287
 
@@ -292,14 +292,14 @@ def cmd_update(args: argparse.Namespace) -> None:
292
292
 
293
293
  # Persist trails
294
294
  trails_file = paths.trails / f"{args.iter}.jsonl"
295
- trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log))
295
+ trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log), encoding="utf-8")
296
296
 
297
297
  mean_score = sum(s["score"] for s in scored) / len(scored) if scored else 0.0
298
298
  best_score = best_data["score"] if best_data else 0.0
299
299
  prev_best = 0.0
300
300
  history_files = sorted(paths.pheromone_history.glob("*.json"))
301
301
  if len(history_files) >= 2:
302
- prev = json.loads(history_files[-2].read_text())
302
+ prev = json.loads(history_files[-2].read_text(encoding="utf-8"))
303
303
  prev_best = prev.get("stats", {}).get("best_known", best_score)
304
304
  delta = best_score - prev_best
305
305
 
@@ -323,7 +323,7 @@ def cmd_update(args: argparse.Namespace) -> None:
323
323
 
324
324
  def cmd_converged(args: argparse.Namespace) -> None:
325
325
  paths = SessionPaths(Path(args.session))
326
- config = json.loads(paths.config.read_text())
326
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
327
327
  cv = config.get("convergence", {})
328
328
 
329
329
  state = PheromoneState.load(paths.pheromone_current)
@@ -339,7 +339,7 @@ def cmd_converged(args: argparse.Namespace) -> None:
339
339
  }
340
340
 
341
341
  if paths.best.exists():
342
- metrics["best_score"] = json.loads(paths.best.read_text()).get("score", 0.0)
342
+ metrics["best_score"] = json.loads(paths.best.read_text(encoding="utf-8")).get("score", 0.0)
343
343
 
344
344
  # max_iterations
345
345
  max_iter = cv.get("max_iterations", 5)
@@ -397,7 +397,7 @@ def cmd_report(args: argparse.Namespace) -> None:
397
397
 
398
398
  best = None
399
399
  if paths.best.exists():
400
- best = json.loads(paths.best.read_text())
400
+ best = json.loads(paths.best.read_text(encoding="utf-8"))
401
401
 
402
402
  # Top-K trails across all iterations
403
403
  all_trails = []
@@ -410,7 +410,7 @@ def cmd_report(args: argparse.Namespace) -> None:
410
410
  # Convergence curve
411
411
  curve = []
412
412
  for hf in sorted(paths.pheromone_history.glob("*.json"), key=lambda p: int(p.stem)):
413
- snap = json.loads(hf.read_text())
413
+ snap = json.loads(hf.read_text(encoding="utf-8"))
414
414
  curve.append({
415
415
  "iteration": snap["iteration"],
416
416
  "entropy": snap["stats"]["entropy"],
@@ -114,11 +114,11 @@ class PheromoneState:
114
114
 
115
115
  def save(self, path: Path) -> None:
116
116
  path.parent.mkdir(parents=True, exist_ok=True)
117
- path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False))
117
+ path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False), encoding="utf-8")
118
118
 
119
119
  @classmethod
120
120
  def load(cls, path: Path) -> "PheromoneState":
121
- return cls.from_dict(json.loads(path.read_text()))
121
+ return cls.from_dict(json.loads(path.read_text(encoding="utf-8")))
122
122
 
123
123
  def select_neighbors(
124
124
  self,
@@ -62,7 +62,7 @@ def load_verified_scores(scores_file: Path) -> Dict[str, float]:
62
62
  """Load pre-computed verified_scores from scorer role output (if exists)."""
63
63
  if not scores_file.exists():
64
64
  return {}
65
- data = json.loads(scores_file.read_text())
65
+ data = json.loads(scores_file.read_text(encoding="utf-8"))
66
66
  return {
67
67
  ant_id: entry["verified_score"]
68
68
  for ant_id, entry in data.get("scores", {}).items()
@@ -89,7 +89,7 @@ def cmd_init(args: argparse.Namespace) -> None:
89
89
  paths = SessionPaths(Path(args.session))
90
90
  if not paths.config.exists():
91
91
  _fail(2, f"config not found: {paths.config}")
92
- config = json.loads(paths.config.read_text())
92
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
93
93
 
94
94
  paths.ensure_dirs()
95
95
 
@@ -107,7 +107,7 @@ def cmd_init(args: argparse.Namespace) -> None:
107
107
  "start_nodes": config.get("task_space", {}).get("start_nodes", "any"),
108
108
  "edges": config.get("task_space", {}).get("edges", "complete"),
109
109
  }
110
- paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False))
110
+ paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False), encoding="utf-8")
111
111
 
112
112
  # Initialize pheromone
113
113
  aco_cfg = config.get("aco", {})
@@ -148,9 +148,9 @@ def _pick_start_node(nodes: List[str], state: PheromoneState, mode: str) -> str:
148
148
 
149
149
  def cmd_select(args: argparse.Namespace) -> None:
150
150
  paths = SessionPaths(Path(args.session))
151
- config = json.loads(paths.config.read_text())
151
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
152
152
  state = PheromoneState.load(paths.pheromone_current)
153
- task_space = json.loads(paths.task_space.read_text())
153
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
154
154
 
155
155
  n_ants = config.get("swarm", {}).get("n_ants", 5)
156
156
  nodes = task_space["nodes"]
@@ -190,7 +190,7 @@ def _load_iteration_artifacts(paths: SessionPaths, iteration: int) -> List[dict]
190
190
  artifacts = []
191
191
  for f in files:
192
192
  try:
193
- artifacts.append(json.loads(Path(f).read_text()))
193
+ artifacts.append(json.loads(Path(f).read_text(encoding="utf-8")))
194
194
  except json.JSONDecodeError as e:
195
195
  print(f"warning: skipped malformed artifact {f}: {e}", file=sys.stderr)
196
196
  return artifacts
@@ -213,9 +213,9 @@ def _validate_artifact(art: dict, valid_nodes: set) -> Optional[str]:
213
213
 
214
214
  def cmd_update(args: argparse.Namespace) -> None:
215
215
  paths = SessionPaths(Path(args.session))
216
- config = json.loads(paths.config.read_text())
216
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
217
217
  state = PheromoneState.load(paths.pheromone_current)
218
- task_space = json.loads(paths.task_space.read_text())
218
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
219
219
  valid_nodes = set(task_space["nodes"])
220
220
 
221
221
  artifacts = _load_iteration_artifacts(paths, args.iter)
@@ -266,7 +266,7 @@ def cmd_update(args: argparse.Namespace) -> None:
266
266
  # Elitist: re-load best history, deposit extra on best path
267
267
  best_data = None
268
268
  if paths.best.exists():
269
- best_data = json.loads(paths.best.read_text())
269
+ best_data = json.loads(paths.best.read_text(encoding="utf-8"))
270
270
  current_best = max(scored, key=lambda x: x["score"]) if scored else None
271
271
  if current_best:
272
272
  best_art = next(a for a in artifacts if a["ant_id"] == current_best["ant_id"])
@@ -281,7 +281,7 @@ def cmd_update(args: argparse.Namespace) -> None:
281
281
  "evidence": best_art.get("evidence", []),
282
282
  "updated_at": time.time(),
283
283
  }
284
- paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False))
284
+ paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False), encoding="utf-8")
285
285
  # Elite deposit
286
286
  state.deposit(best_data["path"], best_data["score"])
287
287
 
@@ -292,14 +292,14 @@ def cmd_update(args: argparse.Namespace) -> None:
292
292
 
293
293
  # Persist trails
294
294
  trails_file = paths.trails / f"{args.iter}.jsonl"
295
- trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log))
295
+ trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log), encoding="utf-8")
296
296
 
297
297
  mean_score = sum(s["score"] for s in scored) / len(scored) if scored else 0.0
298
298
  best_score = best_data["score"] if best_data else 0.0
299
299
  prev_best = 0.0
300
300
  history_files = sorted(paths.pheromone_history.glob("*.json"))
301
301
  if len(history_files) >= 2:
302
- prev = json.loads(history_files[-2].read_text())
302
+ prev = json.loads(history_files[-2].read_text(encoding="utf-8"))
303
303
  prev_best = prev.get("stats", {}).get("best_known", best_score)
304
304
  delta = best_score - prev_best
305
305
 
@@ -323,7 +323,7 @@ def cmd_update(args: argparse.Namespace) -> None:
323
323
 
324
324
  def cmd_converged(args: argparse.Namespace) -> None:
325
325
  paths = SessionPaths(Path(args.session))
326
- config = json.loads(paths.config.read_text())
326
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
327
327
  cv = config.get("convergence", {})
328
328
 
329
329
  state = PheromoneState.load(paths.pheromone_current)
@@ -339,7 +339,7 @@ def cmd_converged(args: argparse.Namespace) -> None:
339
339
  }
340
340
 
341
341
  if paths.best.exists():
342
- metrics["best_score"] = json.loads(paths.best.read_text()).get("score", 0.0)
342
+ metrics["best_score"] = json.loads(paths.best.read_text(encoding="utf-8")).get("score", 0.0)
343
343
 
344
344
  # max_iterations
345
345
  max_iter = cv.get("max_iterations", 5)
@@ -397,7 +397,7 @@ def cmd_report(args: argparse.Namespace) -> None:
397
397
 
398
398
  best = None
399
399
  if paths.best.exists():
400
- best = json.loads(paths.best.read_text())
400
+ best = json.loads(paths.best.read_text(encoding="utf-8"))
401
401
 
402
402
  # Top-K trails across all iterations
403
403
  all_trails = []
@@ -410,7 +410,7 @@ def cmd_report(args: argparse.Namespace) -> None:
410
410
  # Convergence curve
411
411
  curve = []
412
412
  for hf in sorted(paths.pheromone_history.glob("*.json"), key=lambda p: int(p.stem)):
413
- snap = json.loads(hf.read_text())
413
+ snap = json.loads(hf.read_text(encoding="utf-8"))
414
414
  curve.append({
415
415
  "iteration": snap["iteration"],
416
416
  "entropy": snap["stats"]["entropy"],
@@ -114,11 +114,11 @@ class PheromoneState:
114
114
 
115
115
  def save(self, path: Path) -> None:
116
116
  path.parent.mkdir(parents=True, exist_ok=True)
117
- path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False))
117
+ path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False), encoding="utf-8")
118
118
 
119
119
  @classmethod
120
120
  def load(cls, path: Path) -> "PheromoneState":
121
- return cls.from_dict(json.loads(path.read_text()))
121
+ return cls.from_dict(json.loads(path.read_text(encoding="utf-8")))
122
122
 
123
123
  def select_neighbors(
124
124
  self,
@@ -62,7 +62,7 @@ def load_verified_scores(scores_file: Path) -> Dict[str, float]:
62
62
  """Load pre-computed verified_scores from scorer role output (if exists)."""
63
63
  if not scores_file.exists():
64
64
  return {}
65
- data = json.loads(scores_file.read_text())
65
+ data = json.loads(scores_file.read_text(encoding="utf-8"))
66
66
  return {
67
67
  ant_id: entry["verified_score"]
68
68
  for ant_id, entry in data.get("scores", {}).items()
@@ -89,7 +89,7 @@ def cmd_init(args: argparse.Namespace) -> None:
89
89
  paths = SessionPaths(Path(args.session))
90
90
  if not paths.config.exists():
91
91
  _fail(2, f"config not found: {paths.config}")
92
- config = json.loads(paths.config.read_text())
92
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
93
93
 
94
94
  paths.ensure_dirs()
95
95
 
@@ -107,7 +107,7 @@ def cmd_init(args: argparse.Namespace) -> None:
107
107
  "start_nodes": config.get("task_space", {}).get("start_nodes", "any"),
108
108
  "edges": config.get("task_space", {}).get("edges", "complete"),
109
109
  }
110
- paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False))
110
+ paths.task_space.write_text(json.dumps(task_space, indent=2, ensure_ascii=False), encoding="utf-8")
111
111
 
112
112
  # Initialize pheromone
113
113
  aco_cfg = config.get("aco", {})
@@ -148,9 +148,9 @@ def _pick_start_node(nodes: List[str], state: PheromoneState, mode: str) -> str:
148
148
 
149
149
  def cmd_select(args: argparse.Namespace) -> None:
150
150
  paths = SessionPaths(Path(args.session))
151
- config = json.loads(paths.config.read_text())
151
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
152
152
  state = PheromoneState.load(paths.pheromone_current)
153
- task_space = json.loads(paths.task_space.read_text())
153
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
154
154
 
155
155
  n_ants = config.get("swarm", {}).get("n_ants", 5)
156
156
  nodes = task_space["nodes"]
@@ -190,7 +190,7 @@ def _load_iteration_artifacts(paths: SessionPaths, iteration: int) -> List[dict]
190
190
  artifacts = []
191
191
  for f in files:
192
192
  try:
193
- artifacts.append(json.loads(Path(f).read_text()))
193
+ artifacts.append(json.loads(Path(f).read_text(encoding="utf-8")))
194
194
  except json.JSONDecodeError as e:
195
195
  print(f"warning: skipped malformed artifact {f}: {e}", file=sys.stderr)
196
196
  return artifacts
@@ -213,9 +213,9 @@ def _validate_artifact(art: dict, valid_nodes: set) -> Optional[str]:
213
213
 
214
214
  def cmd_update(args: argparse.Namespace) -> None:
215
215
  paths = SessionPaths(Path(args.session))
216
- config = json.loads(paths.config.read_text())
216
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
217
217
  state = PheromoneState.load(paths.pheromone_current)
218
- task_space = json.loads(paths.task_space.read_text())
218
+ task_space = json.loads(paths.task_space.read_text(encoding="utf-8"))
219
219
  valid_nodes = set(task_space["nodes"])
220
220
 
221
221
  artifacts = _load_iteration_artifacts(paths, args.iter)
@@ -266,7 +266,7 @@ def cmd_update(args: argparse.Namespace) -> None:
266
266
  # Elitist: re-load best history, deposit extra on best path
267
267
  best_data = None
268
268
  if paths.best.exists():
269
- best_data = json.loads(paths.best.read_text())
269
+ best_data = json.loads(paths.best.read_text(encoding="utf-8"))
270
270
  current_best = max(scored, key=lambda x: x["score"]) if scored else None
271
271
  if current_best:
272
272
  best_art = next(a for a in artifacts if a["ant_id"] == current_best["ant_id"])
@@ -281,7 +281,7 @@ def cmd_update(args: argparse.Namespace) -> None:
281
281
  "evidence": best_art.get("evidence", []),
282
282
  "updated_at": time.time(),
283
283
  }
284
- paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False))
284
+ paths.best.write_text(json.dumps(best_data, indent=2, ensure_ascii=False), encoding="utf-8")
285
285
  # Elite deposit
286
286
  state.deposit(best_data["path"], best_data["score"])
287
287
 
@@ -292,14 +292,14 @@ def cmd_update(args: argparse.Namespace) -> None:
292
292
 
293
293
  # Persist trails
294
294
  trails_file = paths.trails / f"{args.iter}.jsonl"
295
- trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log))
295
+ trails_file.write_text("\n".join(json.dumps(t, ensure_ascii=False) for t in trail_log), encoding="utf-8")
296
296
 
297
297
  mean_score = sum(s["score"] for s in scored) / len(scored) if scored else 0.0
298
298
  best_score = best_data["score"] if best_data else 0.0
299
299
  prev_best = 0.0
300
300
  history_files = sorted(paths.pheromone_history.glob("*.json"))
301
301
  if len(history_files) >= 2:
302
- prev = json.loads(history_files[-2].read_text())
302
+ prev = json.loads(history_files[-2].read_text(encoding="utf-8"))
303
303
  prev_best = prev.get("stats", {}).get("best_known", best_score)
304
304
  delta = best_score - prev_best
305
305
 
@@ -323,7 +323,7 @@ def cmd_update(args: argparse.Namespace) -> None:
323
323
 
324
324
  def cmd_converged(args: argparse.Namespace) -> None:
325
325
  paths = SessionPaths(Path(args.session))
326
- config = json.loads(paths.config.read_text())
326
+ config = json.loads(paths.config.read_text(encoding="utf-8"))
327
327
  cv = config.get("convergence", {})
328
328
 
329
329
  state = PheromoneState.load(paths.pheromone_current)
@@ -339,7 +339,7 @@ def cmd_converged(args: argparse.Namespace) -> None:
339
339
  }
340
340
 
341
341
  if paths.best.exists():
342
- metrics["best_score"] = json.loads(paths.best.read_text()).get("score", 0.0)
342
+ metrics["best_score"] = json.loads(paths.best.read_text(encoding="utf-8")).get("score", 0.0)
343
343
 
344
344
  # max_iterations
345
345
  max_iter = cv.get("max_iterations", 5)
@@ -397,7 +397,7 @@ def cmd_report(args: argparse.Namespace) -> None:
397
397
 
398
398
  best = None
399
399
  if paths.best.exists():
400
- best = json.loads(paths.best.read_text())
400
+ best = json.loads(paths.best.read_text(encoding="utf-8"))
401
401
 
402
402
  # Top-K trails across all iterations
403
403
  all_trails = []
@@ -410,7 +410,7 @@ def cmd_report(args: argparse.Namespace) -> None:
410
410
  # Convergence curve
411
411
  curve = []
412
412
  for hf in sorted(paths.pheromone_history.glob("*.json"), key=lambda p: int(p.stem)):
413
- snap = json.loads(hf.read_text())
413
+ snap = json.loads(hf.read_text(encoding="utf-8"))
414
414
  curve.append({
415
415
  "iteration": snap["iteration"],
416
416
  "entropy": snap["stats"]["entropy"],
@@ -114,11 +114,11 @@ class PheromoneState:
114
114
 
115
115
  def save(self, path: Path) -> None:
116
116
  path.parent.mkdir(parents=True, exist_ok=True)
117
- path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False))
117
+ path.write_text(json.dumps(self.to_dict(), indent=2, ensure_ascii=False), encoding="utf-8")
118
118
 
119
119
  @classmethod
120
120
  def load(cls, path: Path) -> "PheromoneState":
121
- return cls.from_dict(json.loads(path.read_text()))
121
+ return cls.from_dict(json.loads(path.read_text(encoding="utf-8")))
122
122
 
123
123
  def select_neighbors(
124
124
  self,
@@ -62,7 +62,7 @@ def load_verified_scores(scores_file: Path) -> Dict[str, float]:
62
62
  """Load pre-computed verified_scores from scorer role output (if exists)."""
63
63
  if not scores_file.exists():
64
64
  return {}
65
- data = json.loads(scores_file.read_text())
65
+ data = json.loads(scores_file.read_text(encoding="utf-8"))
66
66
  return {
67
67
  ant_id: entry["verified_score"]
68
68
  for ant_id, entry in data.get("scores", {}).items()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maestro-flow",
3
- "version": "0.4.21",
3
+ "version": "0.4.22",
4
4
  "description": "Workflow orchestration CLI with MCP endpoint support and extensible architecture",
5
5
  "type": "module",
6
6
  "imports": {