synth-optimizers 0.2.7.dev20260814__tar.gz → 0.2.9.dev20260814__tar.gz

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 (111) hide show
  1. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/Cargo.lock +5 -5
  2. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/Cargo.toml +1 -1
  3. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/PKG-INFO +1 -1
  4. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/pyproject.toml +1 -1
  5. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/codex_app_server.rs +59 -5
  6. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/lib.rs +50 -19
  7. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/runtime.rs +46 -6
  8. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/README.md +0 -0
  9. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/Cargo.toml +0 -0
  10. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/README.md +0 -0
  11. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/openapi/gepa-service-v1.yaml +0 -0
  12. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/jesterky_workflow.rs +0 -0
  13. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/machines.rs +0 -0
  14. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/pipeline.rs +0 -0
  15. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/planner.rs +0 -0
  16. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/prompting_best_practices.md +0 -0
  17. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_gepa/src/service.rs +0 -0
  18. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/Cargo.toml +0 -0
  19. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/README.md +0 -0
  20. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/README.md +0 -0
  21. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/app_server.rs +0 -0
  22. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/codex_home.rs +0 -0
  23. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/daytona.rs +0 -0
  24. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/docker.rs +0 -0
  25. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/jsonrpc_read_window.rs +0 -0
  26. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/limits.rs +0 -0
  27. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/local.rs +0 -0
  28. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/mod.rs +0 -0
  29. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/nano.rs +0 -0
  30. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/role_agent.rs +0 -0
  31. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/session.rs +0 -0
  32. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/substrate.rs +0 -0
  33. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/supervisor.rs +0 -0
  34. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/agent_runtime/usage.rs +0 -0
  35. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/artifact_store.rs +0 -0
  36. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/artifacts.rs +0 -0
  37. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/cache.rs +0 -0
  38. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/candidates.rs +0 -0
  39. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/checkpoints.rs +0 -0
  40. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/config.rs +0 -0
  41. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/configured_limits.rs +0 -0
  42. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/container_contract.rs +0 -0
  43. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/data_models.rs +0 -0
  44. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/disk_budget.rs +0 -0
  45. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/error.rs +0 -0
  46. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/event_visualization.rs +0 -0
  47. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/events.rs +0 -0
  48. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/evidence.rs +0 -0
  49. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/failures.rs +0 -0
  50. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/fsm.rs +0 -0
  51. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/http.rs +0 -0
  52. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/invariants.rs +0 -0
  53. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/jesterky.rs +0 -0
  54. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/jobs.rs +0 -0
  55. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/levers.rs +0 -0
  56. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/lib.rs +0 -0
  57. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/limit_engine.rs +0 -0
  58. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/limits.rs +0 -0
  59. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/observability.rs +0 -0
  60. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/operations.rs +0 -0
  61. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/process.rs +0 -0
  62. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/projections.rs +0 -0
  63. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/prompt_program.rs +0 -0
  64. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/registry.rs +0 -0
  65. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/resources.rs +0 -0
  66. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/rollouts.rs +0 -0
  67. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/runtime_records.rs +0 -0
  68. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/scores.rs +0 -0
  69. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/sensors.rs +0 -0
  70. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/state_machine.rs +0 -0
  71. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/stopper.rs +0 -0
  72. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/storage_maintenance.rs +0 -0
  73. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/usage.rs +0 -0
  74. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizer_platform/src/workspace.rs +0 -0
  75. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizers_py/Cargo.toml +0 -0
  76. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizers_py/README.md +0 -0
  77. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/rust/crates/synth_optimizers_py/src/lib.rs +0 -0
  78. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/README.md +0 -0
  79. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/__init__.py +0 -0
  80. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/_synth_optimizers.pyi +0 -0
  81. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/board_server.py +0 -0
  82. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/cli.py +0 -0
  83. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/discovery.py +0 -0
  84. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gelo/00-index.md +0 -0
  85. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gelo/10-cli.md +0 -0
  86. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gelo/20-sdk.md +0 -0
  87. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gelo/30-hosted.md +0 -0
  88. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gelo/containers/00-overview.md +0 -0
  89. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gelo/containers/20-contract.md +0 -0
  90. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/00-index.md +0 -0
  91. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/10-cli.md +0 -0
  92. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/20-sdk.md +0 -0
  93. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/algorithms/00-overview.md +0 -0
  94. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/algorithms/10-pipeline-modes.md +0 -0
  95. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/algorithms/20-proposer-and-policies.md +0 -0
  96. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/assets/synth-logo.png +0 -0
  97. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/containers/00-overview.md +0 -0
  98. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/containers/10-catalog.md +0 -0
  99. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs/gepa/containers/20-contract.md +0 -0
  100. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/docs_server.py +0 -0
  101. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/eval_stats.py +0 -0
  102. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/gelo.py +0 -0
  103. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/gepa.py +0 -0
  104. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/hosted.py +0 -0
  105. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/hosted_config.py +0 -0
  106. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/o11y.py +0 -0
  107. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/observability.py +0 -0
  108. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/py.typed +0 -0
  109. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/sdk.py +0 -0
  110. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/tunnels.py +0 -0
  111. {synth_optimizers-0.2.7.dev20260814 → synth_optimizers-0.2.9.dev20260814}/src/synth_optimizers/victorialogs.py +0 -0
@@ -1306,7 +1306,7 @@ dependencies = [
1306
1306
 
1307
1307
  [[package]]
1308
1308
  name = "synth_gepa"
1309
- version = "0.2.7"
1309
+ version = "0.2.9"
1310
1310
  dependencies = [
1311
1311
  "base64",
1312
1312
  "fs2",
@@ -1321,7 +1321,7 @@ dependencies = [
1321
1321
 
1322
1322
  [[package]]
1323
1323
  name = "synth_mapo"
1324
- version = "0.2.7"
1324
+ version = "0.2.9"
1325
1325
  dependencies = [
1326
1326
  "serde",
1327
1327
  "serde_json",
@@ -1333,7 +1333,7 @@ dependencies = [
1333
1333
 
1334
1334
  [[package]]
1335
1335
  name = "synth_marl_promptopt"
1336
- version = "0.2.7"
1336
+ version = "0.2.9"
1337
1337
  dependencies = [
1338
1338
  "clap",
1339
1339
  "serde",
@@ -1347,7 +1347,7 @@ dependencies = [
1347
1347
 
1348
1348
  [[package]]
1349
1349
  name = "synth_optimizer_platform"
1350
- version = "0.2.7"
1350
+ version = "0.2.9"
1351
1351
  dependencies = [
1352
1352
  "fs2",
1353
1353
  "libc",
@@ -1364,7 +1364,7 @@ dependencies = [
1364
1364
 
1365
1365
  [[package]]
1366
1366
  name = "synth_optimizers_py"
1367
- version = "0.2.7"
1367
+ version = "0.2.9"
1368
1368
  dependencies = [
1369
1369
  "pyo3",
1370
1370
  "serde_json",
@@ -3,7 +3,7 @@ members = ["rust/crates/synth_optimizer_platform", "rust/crates/synth_gepa", "ru
3
3
  resolver = "2"
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.7"
6
+ version = "0.2.9"
7
7
  edition = "2021"
8
8
  license = "Apache-2.0"
9
9
  authors = ["Synth Laboratories <josh@usesynth.ai>"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: synth-optimizers
3
- Version: 0.2.7.dev20260814
3
+ Version: 0.2.9.dev20260814
4
4
  Requires-Dist: pydantic>=2.0.0
5
5
  Requires-Dist: synth-containers==0.4.1.dev20260814
6
6
  Requires-Dist: websocket-client>=1.8.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "synth-optimizers"
3
- version = "0.2.7.dev20260814"
3
+ version = "0.2.9.dev20260814"
4
4
  description = "Public Synth optimizer tooling with a Rust GEPA core."
5
5
  authors = [{name = "Synth Laboratories", email = "josh@usesynth.ai"}]
6
6
  readme = "README.md"
@@ -10,10 +10,10 @@ use reqwest::blocking::Client;
10
10
  use serde_json::{json, Map, Value};
11
11
  use synth_optimizer_platform::{
12
12
  jesterky_workspace_read_model, looks_like_jesterky_manifest,
13
- proposer_delta_chunks_from_protocol, read_jesterky_manifest, record_manifest_validation,
14
- run_turn, AgentTurnOutcome, CodexTurnRequest, NanoAgentTurnIdentity, NanoCodexExecution,
15
- NanoCodexSessionPool, NanoCodexTurnRequest, OptimizerError, PromptProgram, Result,
16
- SynthOptimizerConfig,
13
+ proposer_delta_chunks_from_protocol, proposer_uses_chatgpt_auth, read_jesterky_manifest,
14
+ record_manifest_validation, run_turn, AgentTurnOutcome, CodexTurnRequest,
15
+ NanoAgentTurnIdentity, NanoCodexExecution, NanoCodexSessionPool, NanoCodexTurnRequest,
16
+ OptimizerError, PromptProgram, Result, SynthOptimizerConfig,
17
17
  };
18
18
 
19
19
  const GEPA_REFLECTIVE_FRAME_SCHEMA_VERSION: &str = "gepa_reflective_frame.v1";
@@ -556,7 +556,26 @@ fn normalize_proposer_usage(config: &SynthOptimizerConfig, model: &str, usage: V
556
556
  let provider = config.proposer.provider.trim().to_ascii_lowercase();
557
557
  let model_lower = model.trim().to_ascii_lowercase();
558
558
  let reported_cost = usage_f64_from_map(&usage_map, "cost_usd")
559
- .filter(|value| value.is_finite() && *value > 0.0);
559
+ .filter(|value| value.is_finite() && *value >= 0.0);
560
+ if proposer_uses_chatgpt_auth(&config.proposer.auth_mode) {
561
+ usage_map.insert(
562
+ "provider".to_string(),
563
+ Value::String("chatgpt_subscription".to_string()),
564
+ );
565
+ usage_map.insert("model".to_string(), Value::String(model.to_string()));
566
+ if reported_cost.is_none() {
567
+ // ChatGPT-authenticated Codex turns are subscription-backed rather
568
+ // than metered API calls. Record an explicit zero incremental API
569
+ // charge so the hard cost ledger does not mistake the absence of
570
+ // an API receipt for unknown spend.
571
+ usage_map.insert("cost_usd".to_string(), json!(0.0));
572
+ usage_map.insert(
573
+ "cost_source".to_string(),
574
+ Value::String("chatgpt_subscription_no_incremental_api_charge".to_string()),
575
+ );
576
+ }
577
+ return Value::Object(usage_map);
578
+ }
560
579
  if provider.eq_ignore_ascii_case("openrouter") && model_lower == OPENROUTER_GROK43_MODEL {
561
580
  return normalize_openrouter_grok43_usage(model, usage_map);
562
581
  }
@@ -3378,3 +3397,38 @@ fn non_empty(value: Option<&str>) -> Option<&str> {
3378
3397
  Some(value)
3379
3398
  }
3380
3399
  }
3400
+
3401
+ #[cfg(test)]
3402
+ mod cost_tests {
3403
+ use super::*;
3404
+
3405
+ #[test]
3406
+ fn chatgpt_proposer_emits_explicit_zero_incremental_api_cost() {
3407
+ let mut config = SynthOptimizerConfig::default();
3408
+ config.proposer.auth_mode = "chatgpt".to_string();
3409
+ let usage = normalize_proposer_usage(
3410
+ &config,
3411
+ "gpt-5.6-luna",
3412
+ json!({"prompt_tokens": 100, "completion_tokens": 20, "total_tokens": 120}),
3413
+ );
3414
+ assert_eq!(usage.get("cost_usd"), Some(&json!(0.0)));
3415
+ assert_eq!(
3416
+ usage.get("cost_source"),
3417
+ Some(&json!("chatgpt_subscription_no_incremental_api_charge"))
3418
+ );
3419
+ assert_eq!(usage.get("provider"), Some(&json!("chatgpt_subscription")));
3420
+ }
3421
+
3422
+ #[test]
3423
+ fn chatgpt_proposer_preserves_an_explicit_cost_receipt() {
3424
+ let mut config = SynthOptimizerConfig::default();
3425
+ config.proposer.auth_mode = "chatgpt".to_string();
3426
+ let usage = normalize_proposer_usage(
3427
+ &config,
3428
+ "gpt-5.6-luna",
3429
+ json!({"cost_usd": 0.25, "cost_source": "provider"}),
3430
+ );
3431
+ assert_eq!(usage.get("cost_usd"), Some(&json!(0.25)));
3432
+ assert_eq!(usage.get("cost_source"), Some(&json!("provider")));
3433
+ }
3434
+ }
@@ -3799,7 +3799,7 @@ fn consume_async_lane_work(
3799
3799
  | OptimizerJobStatus::Cancelled
3800
3800
  | OptimizerJobStatus::Expired => {
3801
3801
  if let Some(outcome) =
3802
- schedule_failed_rollout_retry_if_allowed(context, state, resources, &job)?
3802
+ schedule_failed_runtime_retry_if_allowed(context, state, resources, &job)?
3803
3803
  {
3804
3804
  if let Some(updated) =
3805
3805
  state.cursor.pipeline_state.lane_leases.get_mut(&lease_key)
@@ -6956,7 +6956,7 @@ fn advance_pending_runtime_job(
6956
6956
  });
6957
6957
  }
6958
6958
  if updated_job.status.is_terminal() {
6959
- if let Some(outcome) = schedule_failed_rollout_retry_if_allowed(
6959
+ if let Some(outcome) = schedule_failed_runtime_retry_if_allowed(
6960
6960
  context,
6961
6961
  state,
6962
6962
  resources,
@@ -7078,7 +7078,7 @@ fn advance_pending_runtime_job(
7078
7078
  | OptimizerJobStatus::Cancelled
7079
7079
  | OptimizerJobStatus::Expired => {
7080
7080
  if let Some(outcome) =
7081
- schedule_failed_rollout_retry_if_allowed(context, state, resources, &job)?
7081
+ schedule_failed_runtime_retry_if_allowed(context, state, resources, &job)?
7082
7082
  {
7083
7083
  return Ok(outcome);
7084
7084
  }
@@ -9354,14 +9354,16 @@ fn consume_failed_rollout_attempt(
9354
9354
  Ok(())
9355
9355
  }
9356
9356
 
9357
- fn schedule_failed_rollout_retry_if_allowed(
9357
+ fn schedule_failed_runtime_retry_if_allowed(
9358
9358
  context: &mut GepaRunContext,
9359
9359
  state: &mut GepaRunState,
9360
9360
  resources: &GepaStepResources,
9361
9361
  job: &OptimizerJob,
9362
9362
  ) -> Result<Option<GepaAdvanceOutcome>> {
9363
- if !matches!(job.kind, OptimizerJobKind::Rollout)
9364
- || matches!(job.status, OptimizerJobStatus::Cancelled)
9363
+ if !matches!(
9364
+ job.kind,
9365
+ OptimizerJobKind::Rollout | OptimizerJobKind::Proposer
9366
+ ) || matches!(job.status, OptimizerJobStatus::Cancelled)
9365
9367
  {
9366
9368
  return Ok(None);
9367
9369
  }
@@ -9391,7 +9393,7 @@ fn schedule_failed_rollout_retry_if_allowed(
9391
9393
  resources,
9392
9394
  state.cursor.phase.clone(),
9393
9395
  "retry_scheduled",
9394
- "scheduled GEPA failed rollout job retry",
9396
+ "scheduled GEPA failed runtime job retry",
9395
9397
  Map::new(),
9396
9398
  )?;
9397
9399
  Ok(Some(GepaAdvanceOutcome {
@@ -9399,8 +9401,9 @@ fn schedule_failed_rollout_retry_if_allowed(
9399
9401
  terminal: false,
9400
9402
  result: None,
9401
9403
  message: format!(
9402
- "scheduled GEPA failed rollout job retry: {} attempt={}/{}",
9404
+ "scheduled GEPA failed runtime job retry: {} kind={} attempt={}/{}",
9403
9405
  updated.job_id,
9406
+ updated.kind.as_str(),
9404
9407
  updated.attempt.saturating_add(1),
9405
9408
  updated.retry_policy.max_attempts
9406
9409
  ),
@@ -20225,17 +20228,7 @@ fn record_runtime_effect_job(
20225
20228
  let mut job = OptimizerJob::new(input.job_id, input.run_id, input.kind);
20226
20229
  job.status = input.status;
20227
20230
  job.candidate_id = input.candidate_id.map(str::to_string);
20228
- if matches!(job.kind, OptimizerJobKind::Rollout) {
20229
- job.retry_policy = RetryPolicy {
20230
- max_attempts: 3,
20231
- backoff_seconds: 2,
20232
- retryable_failure_types: vec![
20233
- "synth_optimizer_http_error".to_string(),
20234
- "synth_optimizer_container_error".to_string(),
20235
- "synth_optimizer_failed".to_string(),
20236
- ],
20237
- };
20238
- }
20231
+ job.retry_policy = runtime_effect_retry_policy(&job.kind);
20239
20232
  job.attempt = if matches!(job.status, OptimizerJobStatus::Pending) {
20240
20233
  input.effect.attempt.saturating_sub(1)
20241
20234
  } else {
@@ -20301,6 +20294,26 @@ fn record_runtime_effect_job(
20301
20294
  workspace.record_optimizer_job(&job)
20302
20295
  }
20303
20296
 
20297
+ fn runtime_effect_retry_policy(kind: &OptimizerJobKind) -> RetryPolicy {
20298
+ match kind {
20299
+ OptimizerJobKind::Rollout => RetryPolicy {
20300
+ max_attempts: 3,
20301
+ backoff_seconds: 2,
20302
+ retryable_failure_types: vec![
20303
+ "synth_optimizer_http_error".to_string(),
20304
+ "synth_optimizer_container_error".to_string(),
20305
+ "synth_optimizer_failed".to_string(),
20306
+ ],
20307
+ },
20308
+ OptimizerJobKind::Proposer => RetryPolicy {
20309
+ max_attempts: 2,
20310
+ backoff_seconds: 2,
20311
+ retryable_failure_types: vec!["synth_optimizer_proposer_error".to_string()],
20312
+ },
20313
+ _ => RetryPolicy::default(),
20314
+ }
20315
+ }
20316
+
20304
20317
  fn budget_exceeded_error(run_id: &str, breach: &BudgetLimitBreach) -> OptimizerError {
20305
20318
  OptimizerError::BudgetExceeded {
20306
20319
  run_id: run_id.to_string(),
@@ -21714,4 +21727,22 @@ mod run_loop_terminalization_tests {
21714
21727
  None
21715
21728
  );
21716
21729
  }
21730
+
21731
+ #[test]
21732
+ fn proposer_runtime_jobs_get_one_bounded_retry() {
21733
+ let policy = runtime_effect_retry_policy(&OptimizerJobKind::Proposer);
21734
+ assert_eq!(policy.max_attempts, 2);
21735
+ assert_eq!(policy.backoff_seconds, 2);
21736
+ assert_eq!(
21737
+ policy.retryable_failure_types,
21738
+ vec!["synth_optimizer_proposer_error".to_string()]
21739
+ );
21740
+ }
21741
+
21742
+ #[test]
21743
+ fn unrelated_runtime_jobs_keep_the_fail_closed_default() {
21744
+ let policy = runtime_effect_retry_policy(&OptimizerJobKind::Annotation);
21745
+ assert_eq!(policy.max_attempts, 1);
21746
+ assert!(policy.retryable_failure_types.is_empty());
21747
+ }
21717
21748
  }
@@ -468,7 +468,7 @@ impl<'a> GepaRuntimeExecutor<'a> {
468
468
  Ok(outcome) => outcome,
469
469
  Err(error) => {
470
470
  if let Some(retry) =
471
- self.schedule_rollout_retry_if_allowed(&running_job, &lease_id, &error)?
471
+ self.schedule_runtime_retry_if_allowed(&running_job, &lease_id, &error)?
472
472
  {
473
473
  return Err(retry);
474
474
  }
@@ -509,6 +509,7 @@ impl<'a> GepaRuntimeExecutor<'a> {
509
509
  json!(self.config.gepa.rollout_submission_mode),
510
510
  );
511
511
  }
512
+ persist_runtime_outcome_before_completion(self.workspace, run_id, job_id, &outcome)?;
512
513
  record_runtime_effect_completed(
513
514
  self.workspace,
514
515
  RuntimeEffectCompletionInput {
@@ -527,14 +528,13 @@ impl<'a> GepaRuntimeExecutor<'a> {
527
528
  Ok(outcome)
528
529
  }
529
530
 
530
- fn schedule_rollout_retry_if_allowed(
531
+ fn schedule_runtime_retry_if_allowed(
531
532
  &self,
532
533
  job: &OptimizerJob,
533
534
  lease_id: &str,
534
535
  error: &OptimizerError,
535
536
  ) -> Result<Option<OptimizerError>> {
536
- if !matches!(job.kind, OptimizerJobKind::Rollout)
537
- || !is_retryable_rollout_runtime_error(error)
537
+ if !is_retryable_runtime_job_error(job, error)
538
538
  || job.attempt >= job.retry_policy.max_attempts
539
539
  {
540
540
  return Ok(None);
@@ -555,8 +555,9 @@ impl<'a> GepaRuntimeExecutor<'a> {
555
555
  return Ok(None);
556
556
  };
557
557
  Ok(Some(OptimizerError::Failed(format!(
558
- "retryable rollout runtime failure scheduled for retry job_id={} attempt={}/{} next_retry_at={}",
558
+ "retryable runtime failure scheduled for retry job_id={} kind={} attempt={}/{} next_retry_at={}",
559
559
  updated.job_id,
560
+ updated.kind.as_str(),
560
561
  updated.attempt.saturating_add(1),
561
562
  updated.retry_policy.max_attempts,
562
563
  updated.next_retry_at.unwrap_or_else(|| "now".to_string())
@@ -666,7 +667,7 @@ impl<'a> GepaRuntimeExecutor<'a> {
666
667
  .and_then(|usage| usage.get("cost_usd"))
667
668
  .or_else(|| response.get("cost_usd"))
668
669
  .and_then(Value::as_f64)
669
- .filter(|value| value.is_finite() && *value > 0.0)
670
+ .filter(|value| value.is_finite() && *value >= 0.0)
670
671
  .or_else(|| proposer_static_cost_usd(self.config, &usage, &response));
671
672
  let cost_usd = reported_cost_usd.unwrap_or(0.0);
672
673
  let backend = response
@@ -1096,6 +1097,32 @@ fn is_retryable_rollout_runtime_error(error: &OptimizerError) -> bool {
1096
1097
  )
1097
1098
  }
1098
1099
 
1100
+ fn is_retryable_runtime_job_error(job: &OptimizerJob, error: &OptimizerError) -> bool {
1101
+ match job.kind {
1102
+ OptimizerJobKind::Rollout => is_retryable_rollout_runtime_error(error),
1103
+ OptimizerJobKind::Proposer => matches!(error, OptimizerError::Proposer(_)),
1104
+ _ => false,
1105
+ }
1106
+ }
1107
+
1108
+ #[cfg(test)]
1109
+ mod runtime_job_retry_tests {
1110
+ use super::*;
1111
+
1112
+ #[test]
1113
+ fn proposer_errors_are_retryable_runtime_job_errors() {
1114
+ let job = OptimizerJob::new("proposer-job", "run", OptimizerJobKind::Proposer);
1115
+ assert!(is_retryable_runtime_job_error(
1116
+ &job,
1117
+ &OptimizerError::Proposer("app-server stalled".to_string())
1118
+ ));
1119
+ assert!(!is_retryable_runtime_job_error(
1120
+ &job,
1121
+ &OptimizerError::Config("invalid proposer config".to_string())
1122
+ ));
1123
+ }
1124
+ }
1125
+
1099
1126
  fn dispatch_async_rollout(
1100
1127
  client: &ContainerClient,
1101
1128
  request: &Value,
@@ -1566,6 +1593,19 @@ fn ensure_job_lease(
1566
1593
  Ok(())
1567
1594
  }
1568
1595
 
1596
+ fn persist_runtime_outcome_before_completion(
1597
+ workspace: &WorkspaceStore,
1598
+ run_id: &str,
1599
+ job_id: &str,
1600
+ outcome: &RuntimeEffectOutcome,
1601
+ ) -> Result<()> {
1602
+ let stored = crate::stored_runtime_outcome(outcome)?;
1603
+ let mut job = workspace.optimizer_job(run_id, job_id)?;
1604
+ job.payload
1605
+ .insert("runtime_outcome".to_string(), serde_json::to_value(stored)?);
1606
+ workspace.record_optimizer_job(&job)
1607
+ }
1608
+
1569
1609
  fn runtime_lease_id(worker_id: &str, job_id: &str) -> String {
1570
1610
  format!("lease_{worker_id}_{job_id}_{}", now_millis())
1571
1611
  }