cmpnd 0.74.0__tar.gz → 0.76.0__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 (404) hide show
  1. {cmpnd-0.74.0 → cmpnd-0.76.0}/PKG-INFO +1 -1
  2. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_gepa_patch.py +1 -2
  3. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/callback.py +2 -14
  4. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/evals.py +1 -1
  5. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/programs.py +3 -3
  6. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/top.py +8 -2
  7. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/verbs.py +1 -1
  8. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/eval_handler.py +5 -19
  9. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/identity.py +0 -17
  10. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/models.py +1 -15
  11. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/optimizers/gepa.py +1 -8
  12. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/optimizers/resume.py +12 -11
  13. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/optimizers/tracker.py +3 -14
  14. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/program_naming.py +4 -7
  15. {cmpnd-0.74.0 → cmpnd-0.76.0}/docs/cli.md +20 -7
  16. {cmpnd-0.74.0 → cmpnd-0.76.0}/docs/sdk-instrumentation.md +14 -11
  17. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/program_names.py +3 -2
  18. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/trace_facts.py +0 -6
  19. {cmpnd-0.74.0 → cmpnd-0.76.0}/pyproject.toml +1 -1
  20. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/seed_review_run.py +2 -4
  21. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/conftest.py +14 -0
  22. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_composite_module.py +4 -4
  23. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_module_tracing.py +2 -3
  24. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimization_lineage.py +0 -2
  25. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimize_review_committee.py +0 -1
  26. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_save_load.py +28 -33
  27. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_trace_structure.py +21 -20
  28. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/rlm_shape.py +0 -8
  29. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_trace_streaming.py +0 -1
  30. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_multi_client_stress.py +0 -4
  31. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_callback.py +20 -9
  32. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_client_program_names.py +12 -7
  33. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_eval.py +4 -6
  34. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_eval_program_id.py +2 -9
  35. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_exporter.py +1 -2
  36. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_exporter_program_names.py +0 -1
  37. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_exporter_queue.py +0 -1
  38. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_exporter_real.py +2 -3
  39. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_gepa_callback.py +16 -12
  40. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_gepa_resume.py +15 -12
  41. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_identity.py +0 -21
  42. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_ingest_conformance.py +8 -18
  43. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_models.py +17 -7
  44. cmpnd-0.76.0/tests/test_no_signature_hash_on_the_wire.py +188 -0
  45. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_program_identity.py +3 -4
  46. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_program_naming.py +8 -8
  47. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_repro_reactv2_bugs.py +0 -1
  48. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_reversible_failures.py +1 -2
  49. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_schemas.py +0 -2
  50. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_trace_fact_wire_contract.py +0 -1
  51. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_trace_facts.py +0 -1
  52. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_tracker_enrich.py +3 -2
  53. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_xxh64.py +0 -2
  54. {cmpnd-0.74.0 → cmpnd-0.76.0}/uv.lock +1 -1
  55. {cmpnd-0.74.0 → cmpnd-0.76.0}/.gitignore +0 -0
  56. {cmpnd-0.74.0 → cmpnd-0.76.0}/AGENTS.md +0 -0
  57. {cmpnd-0.74.0 → cmpnd-0.76.0}/CLAUDE.md +0 -0
  58. {cmpnd-0.74.0 → cmpnd-0.76.0}/README.md +0 -0
  59. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/__init__.py +0 -0
  60. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_assessments.py +0 -0
  61. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_authored_import.py +0 -0
  62. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_deployments.py +0 -0
  63. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_exporter_pool.py +0 -0
  64. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_http.py +0 -0
  65. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_program_patch.py +0 -0
  66. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_rlm_patch.py +0 -0
  67. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_runtime_tools.py +0 -0
  68. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_timespec.py +0 -0
  69. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_traces.py +0 -0
  70. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/_valsets.py +0 -0
  71. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/authored.py +0 -0
  72. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/__init__.py +0 -0
  73. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/api_client.py +0 -0
  74. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/__init__.py +0 -0
  75. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/admin.py +0 -0
  76. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/assessments.py +0 -0
  77. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/auth.py +0 -0
  78. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/datasets.py +0 -0
  79. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/deployments.py +0 -0
  80. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/login.py +0 -0
  81. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/optimizations.py +0 -0
  82. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/org.py +0 -0
  83. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  84. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  85. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/traces.py +0 -0
  86. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/commands/valsets.py +0 -0
  87. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/credentials.py +0 -0
  88. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/deprecation.py +0 -0
  89. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/pagination.py +0 -0
  90. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/shell.py +0 -0
  91. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/trace_render.py +0 -0
  92. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/tree_help.py +0 -0
  93. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/cli/views.py +0 -0
  94. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/client.py +0 -0
  95. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/configuration.py +0 -0
  96. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/context.py +0 -0
  97. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/credentials.py +0 -0
  98. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/dataset_sync.py +0 -0
  99. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/datasets.py +0 -0
  100. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/decorators.py +0 -0
  101. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/deployment.py +0 -0
  102. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/deployment_metadata.py +0 -0
  103. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/deployment_runs.py +0 -0
  104. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/execution.py +0 -0
  105. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/exporter.py +0 -0
  106. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/exporter_http.py +0 -0
  107. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/helpers.py +0 -0
  108. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/imaging.py +0 -0
  109. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/ir/__init__.py +0 -0
  110. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/ir/decode.py +0 -0
  111. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/ir/encode.py +0 -0
  112. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/ir/hash.py +0 -0
  113. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/ir/program.py +0 -0
  114. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/ir/reflection.py +0 -0
  115. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/ir/types.py +0 -0
  116. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/ir/xxh64.py +0 -0
  117. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/lm_configuration.py +0 -0
  118. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/mcp_servers.py +0 -0
  119. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/named_programs.py +0 -0
  120. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/optimization.py +0 -0
  121. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/optimizers/__init__.py +0 -0
  122. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  123. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/packaging.py +0 -0
  124. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/programs.py +0 -0
  125. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/references.py +0 -0
  126. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/retry.py +0 -0
  127. {cmpnd-0.74.0 → cmpnd-0.76.0}/cmpnd/traces.py +0 -0
  128. {cmpnd-0.74.0 → cmpnd-0.76.0}/docs/2026-04-02-code-review.md +0 -0
  129. {cmpnd-0.74.0 → cmpnd-0.76.0}/docs/client.md +0 -0
  130. {cmpnd-0.74.0 → cmpnd-0.76.0}/docs/custom-proposer.md +0 -0
  131. {cmpnd-0.74.0 → cmpnd-0.76.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  132. {cmpnd-0.74.0 → cmpnd-0.76.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  133. {cmpnd-0.74.0 → cmpnd-0.76.0}/docs/reading-traces.md +0 -0
  134. {cmpnd-0.74.0 → cmpnd-0.76.0}/examples/generalizing_proposer.py +0 -0
  135. {cmpnd-0.74.0 → cmpnd-0.76.0}/examples/local_ollama.py +0 -0
  136. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/LINEAGE.md +0 -0
  137. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/README.md +0 -0
  138. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  139. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  140. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  141. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  142. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  143. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/py.typed +0 -0
  144. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  145. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/cmpnd_ingest/retry.py +0 -0
  146. {cmpnd-0.74.0 → cmpnd-0.76.0}/ingest/pyproject.toml +0 -0
  147. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/audit-sdist.sh +0 -0
  148. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/capture_review_run.py +0 -0
  149. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/check-pip-install.sh +0 -0
  150. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/check_release_artifacts.py +0 -0
  151. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/fixtures/review_run.json.gz +0 -0
  152. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/gepa_resume_bug.py +0 -0
  153. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/install-test-env.sh +0 -0
  154. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/release-gate.sh +0 -0
  155. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/release-real-models.sh +0 -0
  156. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/require_version_bump.py +0 -0
  157. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/selfhost_compat.py +0 -0
  158. {cmpnd-0.74.0 → cmpnd-0.76.0}/scripts/test-in-linux.sh +0 -0
  159. {cmpnd-0.74.0 → cmpnd-0.76.0}/stubs/dspy/__init__.pyi +0 -0
  160. {cmpnd-0.74.0 → cmpnd-0.76.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  161. {cmpnd-0.74.0 → cmpnd-0.76.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  162. {cmpnd-0.74.0 → cmpnd-0.76.0}/stubs/dspy/utils/__init__.pyi +0 -0
  163. {cmpnd-0.74.0 → cmpnd-0.76.0}/stubs/dspy/utils/callback.pyi +0 -0
  164. {cmpnd-0.74.0 → cmpnd-0.76.0}/stubs/dspy/utils/dummies.pyi +0 -0
  165. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/__init__.py +0 -0
  166. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/_credential_route.py +0 -0
  167. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/_deployment_signature_helpers.py +0 -0
  168. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/_exporter_helpers.py +0 -0
  169. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/_fake_clock.py +0 -0
  170. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/_identity_helpers.py +0 -0
  171. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/_resolve_refs.py +0 -0
  172. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/_traced_run.py +0 -0
  173. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/conftest.py +0 -0
  174. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/__init__.py +0 -0
  175. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/committee_task.py +0 -0
  176. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/continuation_task.py +0 -0
  177. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  178. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  179. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/fake_lm.py +0 -0
  180. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/helpers.py +0 -0
  181. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/target_capabilities.py +0 -0
  182. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_assessments_cli.py +0 -0
  183. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_capture_prompts.py +0 -0
  184. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  185. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_deploy_execute.py +0 -0
  186. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_deploy_pandas.py +0 -0
  187. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_deploy_programs.py +0 -0
  188. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_edge_cases.py +0 -0
  189. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_example_hash.py +0 -0
  190. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_gepa_optimization.py +0 -0
  191. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_interpreter_spans.py +0 -0
  192. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_lm_accuracy.py +0 -0
  193. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  194. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
  195. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimize_continuation.py +0 -0
  196. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
  197. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimize_failures.py +0 -0
  198. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimize_loop.py +0 -0
  199. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  200. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_parallel_export.py +0 -0
  201. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_project_attribution.py +0 -0
  202. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_react_tool_spans.py +0 -0
  203. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_traces_read.py +0 -0
  204. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_user_attribution.py +0 -0
  205. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/e2e/test_valsets_cli.py +0 -0
  206. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  207. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  208. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  209. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  210. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  211. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  212. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  213. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  214. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  215. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/10_react.json +0 -0
  216. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/11_refine.json +0 -0
  217. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/12_retrieve.json +0 -0
  218. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/13_knn.json +0 -0
  219. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/14_parallel.json +0 -0
  220. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/identity_vectors/README.md +0 -0
  221. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/ingest_conformance.py +0 -0
  222. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/__init__.py +0 -0
  223. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/authored_optimize/judge.py +0 -0
  224. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  225. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/conftest.py +0 -0
  226. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  227. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/hosted_optimize_probe.py +0 -0
  228. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/optimization_run_probe.py +0 -0
  229. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  230. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/test_logs_renders_traces.py +0 -0
  231. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/test_optimization_run.py +0 -0
  232. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/test_optimizing_progress.py +0 -0
  233. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/integration/test_unhappy_path.py +0 -0
  234. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/__init__.py +0 -0
  235. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/__main__.py +0 -0
  236. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/drain.py +0 -0
  237. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/ingest.py +0 -0
  238. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/latency_transport.py +0 -0
  239. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/rlm_size_report.py +0 -0
  240. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/sweep.py +0 -0
  241. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/test_rlm_shape.py +0 -0
  242. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/load/test_rlm_size_report.py +0 -0
  243. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/__init__.py +0 -0
  244. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/conftest.py +0 -0
  245. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/seed_parity.py +0 -0
  246. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_auth.py +0 -0
  247. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_datasets.py +0 -0
  248. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_deployments.py +0 -0
  249. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_evals.py +0 -0
  250. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_health.py +0 -0
  251. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_modules.py +0 -0
  252. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_optimizations.py +0 -0
  253. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_programs.py +0 -0
  254. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_signatures.py +0 -0
  255. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/parity/test_traces.py +0 -0
  256. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/__init__.py +0 -0
  257. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/_provider.py +0 -0
  258. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/_readiness.py +0 -0
  259. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/_reliability.py +0 -0
  260. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/_result.py +0 -0
  261. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/mint_bedrock_token.py +0 -0
  262. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_bedrock_converse.py +0 -0
  263. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_deploy_programs.py +0 -0
  264. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_optimize_client_committee.py +0 -0
  265. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_optimize_continuation.py +0 -0
  266. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_optimize_loop.py +0 -0
  267. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
  268. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_optimize_stress.py +0 -0
  269. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_provider_roles.py +0 -0
  270. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_readiness.py +0 -0
  271. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_real_provider_twins.py +0 -0
  272. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_reliability.py +0 -0
  273. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_reliability_classify.py +0 -0
  274. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_rlm_wasm.py +0 -0
  275. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/real_models/test_trace_search.py +0 -0
  276. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/span_reference_fixture.py +0 -0
  277. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_adapter_reentrancy.py +0 -0
  278. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_assessments_api.py +0 -0
  279. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_authored.py +0 -0
  280. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_authored_binding_credentials.py +0 -0
  281. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_authored_bundle_collisions.py +0 -0
  282. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_authored_metric_contract.py +0 -0
  283. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_authored_named_models.py +0 -0
  284. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_authored_optimize.py +0 -0
  285. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_candidate_lineage.py +0 -0
  286. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/__init__.py +0 -0
  287. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/conftest.py +0 -0
  288. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_admin.py +0 -0
  289. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_api_client.py +0 -0
  290. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_assessments.py +0 -0
  291. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_auth.py +0 -0
  292. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_credentials.py +0 -0
  293. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_datasets.py +0 -0
  294. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_deployment_runs.py +0 -0
  295. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_deployments.py +0 -0
  296. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_evals.py +0 -0
  297. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_login.py +0 -0
  298. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_main.py +0 -0
  299. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_optimizations.py +0 -0
  300. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_org.py +0 -0
  301. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_pagination.py +0 -0
  302. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_parity.py +0 -0
  303. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_programs.py +0 -0
  304. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_provider_credentials.py +0 -0
  305. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_schema_sample.py +0 -0
  306. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_shell.py +0 -0
  307. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_tool_servers.py +0 -0
  308. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_top.py +0 -0
  309. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_traces.py +0 -0
  310. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
  311. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  312. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_tree_help.py +0 -0
  313. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cli/test_valsets.py +0 -0
  314. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_client.py +0 -0
  315. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_client_floor.py +0 -0
  316. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_clustering/__init__.py +0 -0
  317. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_clustering/conftest.py +0 -0
  318. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_clustering/test_edge_cases.py +0 -0
  319. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  320. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_clustering/test_type_conversion.py +0 -0
  321. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_cmp551_grouping_identity.py +0 -0
  322. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_configuration.py +0 -0
  323. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_context.py +0 -0
  324. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_context_propagation.py +0 -0
  325. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_credential_kwargs.py +0 -0
  326. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_credential_selection.py +0 -0
  327. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_credential_values.py +0 -0
  328. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_dataset_polling.py +0 -0
  329. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_dataset_sync.py +0 -0
  330. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_datasets.py +0 -0
  331. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_decision_client_spans.py +0 -0
  332. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_decorators.py +0 -0
  333. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_deploy.py +0 -0
  334. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  335. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_deploy_program_id.py +0 -0
  336. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_deployment_metadata.py +0 -0
  337. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_deployment_runs.py +0 -0
  338. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_deployments_read.py +0 -0
  339. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_eval_example_attribution.py +0 -0
  340. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_eval_start.py +0 -0
  341. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_eval_trace_grouping.py +0 -0
  342. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_exception_paths.py +0 -0
  343. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_execute.py +0 -0
  344. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_execute_run_identity.py +0 -0
  345. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_fake_lm.py +0 -0
  346. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_gepa_integration.py +0 -0
  347. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_gepa_output_normalization.py +0 -0
  348. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_imaging.py +0 -0
  349. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_import_weight.py +0 -0
  350. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_ingest_second_producer.py +0 -0
  351. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_integration.py +0 -0
  352. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_interpreter_callback.py +0 -0
  353. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_ir_encoder_properties.py +0 -0
  354. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_ir_golden_vectors.py +0 -0
  355. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_ir_roundtrip.py +0 -0
  356. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_lm15_native_calls.py +0 -0
  357. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_lm_configuration.py +0 -0
  358. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_lm_usage_shapes.py +0 -0
  359. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_mcp_tools.py +0 -0
  360. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_multi_client_stress_readiness.py +0 -0
  361. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimization_program_names.py +0 -0
  362. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimization_progress.py +0 -0
  363. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimization_start.py +0 -0
  364. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimize.py +0 -0
  365. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimize_budget.py +0 -0
  366. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimize_lift.py +0 -0
  367. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimize_lms.py +0 -0
  368. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimize_proposer.py +0 -0
  369. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimize_reflection_lm.py +0 -0
  370. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_optimize_terminal_failure.py +0 -0
  371. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_packaging.py +0 -0
  372. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_packaging_callable_source.py +0 -0
  373. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_packaging_mcp_tools.py +0 -0
  374. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_packaging_program_source.py +0 -0
  375. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_poll_until.py +0 -0
  376. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_program_id_deprecation.py +0 -0
  377. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_program_lineage_persist.py +0 -0
  378. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_program_names_in_deploy.py +0 -0
  379. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_programs_create.py +0 -0
  380. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_project_payload.py +0 -0
  381. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_real_provider_twins_readiness.py +0 -0
  382. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_reflector_properties.py +0 -0
  383. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_reflector_smoke.py +0 -0
  384. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_release_gate_script.py +0 -0
  385. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_release_real_models_script.py +0 -0
  386. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_replaced_names.py +0 -0
  387. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_require_version_bump.py +0 -0
  388. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_retry.py +0 -0
  389. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_rlm_patch.py +0 -0
  390. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_run_attribution_paths.py +0 -0
  391. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_runtime_tools.py +0 -0
  392. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_seal_lm_credential.py +0 -0
  393. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_seed_review_run.py +0 -0
  394. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_signature_inputs.py +0 -0
  395. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_span_references.py +0 -0
  396. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_span_sent_once.py +0 -0
  397. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_system_one.py +0 -0
  398. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_trace_context_concurrency.py +0 -0
  399. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_trace_render.py +0 -0
  400. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_traces_read.py +0 -0
  401. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_tracked_gepa.py +0 -0
  402. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_truncation.py +0 -0
  403. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_upload_auth_headers.py +0 -0
  404. {cmpnd-0.74.0 → cmpnd-0.76.0}/tests/test_valsets_api.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.74.0
3
+ Version: 0.76.0
4
4
  Summary: DSPy observability and deployment SDK for cmpnd
5
5
  Project-URL: Homepage, https://cmpnd.ai
6
6
  Author-email: cmpnd <hello@cmpnd.ai>
@@ -133,9 +133,8 @@ def _run_tracked_compile(
133
133
  max_metric_calls=max_metric_calls,
134
134
  )
135
135
 
136
- # `enrich_from_program` hashed the student, so its generated name reuses that hash.
137
136
  if client_config is None and trk.program_id is None and dspy_callback._declared_program_name() is None:
138
- trk.generated_program_name = program_naming.generated_program_name(student, trk.program_signature_hash)
137
+ trk.generated_program_name = program_naming.generated_program_name(student)
139
138
 
140
139
  restore_attribution = callback.start_trace_attribution()
141
140
 
@@ -1188,16 +1188,11 @@ class _ModuleMixin(_CallbackBase):
1188
1188
  identity.get_field_schema(field_info.annotation) for field_info in output_fields.values()
1189
1189
  ]
1190
1190
 
1191
- span.program_signature_hash = identity.compute_program_hash(instance) or 0
1192
-
1193
1191
  # For ChainOfThought, also capture extended signature
1194
1192
  if hasattr(instance, "extended_signature"):
1195
1193
  ext_sig = instance.extended_signature
1196
1194
  span.attributes["extended_signature"] = getattr(ext_sig, "signature", str(ext_sig))
1197
1195
 
1198
- else:
1199
- span.program_signature_hash = identity.compute_program_hash(instance) or 0
1200
-
1201
1196
  # Capture demo count and contents if available
1202
1197
  demos = getattr(instance, "demos", None)
1203
1198
  if demos:
@@ -1257,9 +1252,6 @@ class _ModuleMixin(_CallbackBase):
1257
1252
  elif hasattr(instance, "predict") and hasattr(instance.predict, "signature"):
1258
1253
  signature = instance.predict.signature
1259
1254
 
1260
- # The root module's signature hash, once a branch below computes it. An unnamed
1261
- # program's generated name reuses it rather than reflecting the module again.
1262
- program_hash: int | None = None
1263
1255
  if signature:
1264
1256
  # Single-signature module (Predict, ChainOfThought, etc.)
1265
1257
  sig_name = getattr(signature, "__name__", None)
@@ -1287,12 +1279,10 @@ class _ModuleMixin(_CallbackBase):
1287
1279
  identity.get_field_schema(field_info.annotation) for field_info in output_fields.values()
1288
1280
  ]
1289
1281
 
1290
- program_hash = identity.compute_program_hash(instance)
1291
- trace.program_signature_hash = program_hash or 0
1292
1282
  trace.signature_summary = identity.build_signature_summary(signature)
1293
1283
  else:
1294
1284
  # Multi-child module: collect Predict children via DSPy's own walker
1295
- # for metadata; the hash is the structural one from the IR reflector.
1285
+ # for metadata.
1296
1286
  dspy_mod, _ = _get_dspy()
1297
1287
  children: list[tuple[str, Any]] = []
1298
1288
  if dspy_mod and hasattr(instance, "named_predictors"):
@@ -1303,8 +1293,6 @@ class _ModuleMixin(_CallbackBase):
1303
1293
 
1304
1294
  if children:
1305
1295
  trace.signature_name = instance.__class__.__name__
1306
- program_hash = identity.compute_program_hash(instance)
1307
- trace.program_signature_hash = program_hash or 0
1308
1296
 
1309
1297
  # Build component_signatures for grouped field display
1310
1298
  components = []
@@ -1340,7 +1328,7 @@ class _ModuleMixin(_CallbackBase):
1340
1328
  if trace.program_id is None and trace.declared_name is None:
1341
1329
  trace.program_id = context.optimization_program_id(trace.optimization_run_id)
1342
1330
  if trace.program_id is None and trace.declared_name is None:
1343
- trace.generated_name = program_naming.generated_program_name(instance, program_hash)
1331
+ trace.generated_name = program_naming.generated_program_name(instance)
1344
1332
  _warn_once_about_generated_name(trace)
1345
1333
 
1346
1334
  # Detect if this is a compiled program (from optimization)
@@ -13,7 +13,7 @@ def _add_list_args(p: ArgumentParser) -> None:
13
13
  "--program",
14
14
  dest="program_name",
15
15
  metavar="NAME",
16
- help="Only the runs of the program with this name, current or retired",
16
+ help="Only the runs of the program whose current name is NAME",
17
17
  )
18
18
  p.add_argument(
19
19
  "--module",
@@ -211,9 +211,9 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
211
211
  "rename",
212
212
  help="Name or rename a program",
213
213
  description=(
214
- "Make NAME the current name of PROGRAM. The name it had keeps resolving to it, so code that still "
215
- "declares the old name reaches the same program, with its id and history. A name another program "
216
- "answers to is refused."
214
+ "Make NAME the current name of PROGRAM, which keeps its id and history. The name it had is free at "
215
+ "once: it no longer resolves, and any program can take it, so code that still declares the old name "
216
+ "reaches a new program. Another program's current name is refused."
217
217
  ),
218
218
  )
219
219
  rename.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
@@ -1440,7 +1440,10 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
1440
1440
  help="Deploy authored Python source (file or stdin)",
1441
1441
  description=(
1442
1442
  "Stage authored Python without executing it in this process, check its "
1443
- "entrypoint in a fresh child, and upload the exact staged bundle."
1443
+ "entrypoint in a fresh child, and upload the exact staged bundle.\n\n"
1444
+ "The platform deploys only a program a user named. PROGRAM_NAME is such a name. "
1445
+ "A program with no name, or with a name a client generated, is refused until "
1446
+ "`cmpnd programs rename <p_id> <name>` names it."
1444
1447
  ),
1445
1448
  epilog=(
1446
1449
  "Examples:\n"
@@ -1501,7 +1504,10 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
1501
1504
  "dspy.Prediction(score=, feedback=): gepa writes each new instruction by "
1502
1505
  "reading that feedback, so a score-only metric is refused before the run "
1503
1506
  "starts. `--reflection-model` is required for the same reason — it names "
1504
- "the model that does the writing."
1507
+ "the model that does the writing.\n\n"
1508
+ "The platform optimizes only a program a user named. A --deployment-id whose "
1509
+ "program has no name, or a name a client generated, such as an optimization's "
1510
+ "result, is refused until `cmpnd programs rename <p_id> <name>` names it."
1505
1511
  ),
1506
1512
  epilog=(
1507
1513
  "Examples:\n"
@@ -167,7 +167,7 @@ DOMAIN_VERBS: dict[str, str] = {
167
167
  "trash": "moves a program to the Trash, where it stays restorable, and delete would read as the permanent purge",
168
168
  "restore": "returns a program from the Trash with its history, which create would not",
169
169
  "purge": "deletes a program in the Trash for good, the irreversible step after trash",
170
- "rename": "gives a program a new current name and keeps the old one resolving to it, which update would hide",
170
+ "rename": "gives a program a new current name and frees the old one for any program, which update would hide",
171
171
  }
172
172
 
173
173
  #: Leaves naming a related collection or sub-record rather than an action.
@@ -87,16 +87,10 @@ class EvalHandler:
87
87
  if program:
88
88
  eval_run.module_class = program.__class__.__name__
89
89
 
90
- # Hash via the IR reflector on the live module so the class tag
91
- # discriminates correctly; signature_summary is still derived from
92
- # the root signature for human display.
93
- program_hash = identity.compute_program_hash(program)
94
- if program_hash is not None:
95
- eval_run.program_signature_hash = program_hash
90
+ # The summary comes from the root signature, for human display.
96
91
  signature = identity.get_program_signature(program)
97
92
  if signature:
98
93
  eval_run.signature_summary = identity.build_signature_summary(signature)
99
- logger.debug(f"Eval run program_signature_hash: {eval_run.program_signature_hash}")
100
94
 
101
95
  # Extract input field names for filtering example inputs in on_evaluate_end
102
96
  if hasattr(signature, "input_fields"):
@@ -177,9 +171,7 @@ class EvalHandler:
177
171
  eval_run._dataset_exists = None # Will include examples as fallback
178
172
 
179
173
  exp = self._callback._current_exporter()
180
- eval_run.program_id = self._program_id(
181
- exp, program, eval_run.program_signature_hash, eval_run.optimization_run_id
182
- )
174
+ eval_run.program_id = self._program_id(exp, program, eval_run.optimization_run_id)
183
175
 
184
176
  logger.info(
185
177
  f"EvalRun created: program_id={eval_run.program_id}, "
@@ -216,9 +208,7 @@ class EvalHandler:
216
208
  self._callback._call_id_to_span[call_id] = span
217
209
  context.push_span(span)
218
210
 
219
- def _program_id(
220
- self, exp: Any, program: Any, program_hash: int | None, optimization_run_id: str | None
221
- ) -> str | None:
211
+ def _program_id(self, exp: Any, program: Any, optimization_run_id: str | None) -> str | None:
222
212
  """The program id the eval run carries, resolved the way a trace's is.
223
213
 
224
214
  A program name the call declares wins over the callback's program id. A run
@@ -227,9 +217,7 @@ class EvalHandler:
227
217
  The exporter resolves either name now, on the caller's thread, because the run's
228
218
  start is sent synchronously and must carry the id. The exporter keeps the answer
229
219
  for the process, so a name its traces already resolved costs no request. A name
230
- the exporter cannot resolve leaves the run without an id. ``program_hash`` is the
231
- evaluated program's signature hash, already computed for the run, so naming the
232
- program does not reflect it again.
220
+ the exporter cannot resolve leaves the run without an id.
233
221
  """
234
222
  from . import callback
235
223
 
@@ -238,9 +226,7 @@ class EvalHandler:
238
226
  program_id = self._callback._resolve_program_id() or context.optimization_program_id(optimization_run_id)
239
227
  if program_id is not None or program is None:
240
228
  return program_id
241
- return program_naming.resolve_generated_name(
242
- exp, program_naming.generated_program_name(program, program_hash)
243
- )
229
+ return program_naming.resolve_generated_name(exp, program_naming.generated_program_name(program))
244
230
  # Read with getattr because the member is optional: an in-memory collector
245
231
  # that resolves no names has none.
246
232
  resolve = getattr(exp, "resolve_program_name", None)
@@ -97,23 +97,6 @@ def _unsigned_to_signed_int64(value: int) -> int: # pyright: ignore[reportUnuse
97
97
  return masked - 0x10000000000000000 if masked >= 0x8000000000000000 else masked
98
98
 
99
99
 
100
- def format_signature_hash(value: int | None) -> str | None:
101
- """Render a ``program_signature_hash`` for the JSON wire as a quoted decimal
102
- string (e.g. ``"-935109221252960526"``), or ``None`` when absent.
103
-
104
- The hash is a signed int64 (xxhash64). Emitting it as a JSON *number* is
105
- unsafe: any consumer that decodes through an untyped path floats it and
106
- loses precision above 2^53 (Go ``any``/``map[string]any``, JavaScript
107
- natively). Carrying it as a string makes the safe representation automatic
108
- end to end. Storage stays signed int64; only the wire value is a
109
- string. ``None`` stays ``None`` (nullable optimization hash); the trace/span
110
- ``0`` sentinel renders as ``"0"``.
111
- """
112
- if value is None:
113
- return None
114
- return str(value)
115
-
116
-
117
100
  # A program id is server-issued: ``p_`` and twelve base62 characters, the shape
118
101
  # every platform id shares. The SDK never mints one; it carries the id serve gives
119
102
  # a program name, or the id a deployment's worker passes to the kernel.
@@ -13,7 +13,7 @@ from uuid import UUID, uuid4
13
13
 
14
14
  from cmpnd_ingest.trace_facts import CompletionFact, MetadataFact, StartedFact, StringMapChange
15
15
 
16
- from . import identity, imaging
16
+ from . import imaging
17
17
 
18
18
  # U+0000 (NUL) and lone UTF-16 surrogates are valid JSON, and valid in a Python
19
19
  # str, but the server cannot store either: a JSON column rejects both, and a NUL
@@ -758,8 +758,6 @@ class SpanBuilder:
758
758
  # Each element is None for primitives, or a schema dict for complex types
759
759
  signature_input_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
760
760
  signature_output_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
761
- # Hash of input/output fields+types for program identity (xxhash64, signed)
762
- program_signature_hash: int = 0
763
761
 
764
762
  # DSPy-specific: Module info
765
763
  module_type: str | None = None
@@ -905,7 +903,6 @@ class SpanBuilder:
905
903
  "signature_output_field_descs": self.signature_output_field_descs,
906
904
  "signature_input_field_schemas": self.signature_input_field_schemas,
907
905
  "signature_output_field_schemas": self.signature_output_field_schemas,
908
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
909
906
  "module_type": self.module_type,
910
907
  "module_name": self.module_name,
911
908
  "model_name": self.model_name,
@@ -945,7 +942,6 @@ class TraceBuilder:
945
942
  # The root DSPy module's class. The program's own name is `declared_name`,
946
943
  # which the transport resolves to `program_id`.
947
944
  module_class: str | None = None
948
- program_version: str | None = None
949
945
 
950
946
  # Signature info - identifies the program by its inputs/outputs
951
947
  signature_name: str | None = None
@@ -956,7 +952,6 @@ class TraceBuilder:
956
952
  # Schemas for complex types (Pydantic models, dataclasses)
957
953
  signature_input_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
958
954
  signature_output_field_schemas: list[dict[str, Any] | None] = field(default_factory=list)
959
- program_signature_hash: int = 0 # Hash of input/output fields+types (legacy program key)
960
955
  signature_summary: str | None = None # Human-readable: "input1, input2 -> output1, output2"
961
956
  component_signatures: list[dict[str, Any]] = field(default_factory=list)
962
957
  # The server-issued program id the trace declares (`p_…`), snapshotted from
@@ -1080,7 +1075,6 @@ class TraceBuilder:
1080
1075
  "module_class": self.module_class,
1081
1076
  # Also under the key a serve from before module_class reads (StartedFact.creation_dict).
1082
1077
  "program_name": self.module_class,
1083
- "program_version": self.program_version,
1084
1078
  "signature_name": self.signature_name,
1085
1079
  "signature_input_fields": self.signature_input_fields,
1086
1080
  "signature_output_fields": self.signature_output_fields,
@@ -1088,7 +1082,6 @@ class TraceBuilder:
1088
1082
  "signature_output_field_types": self.signature_output_field_types,
1089
1083
  "signature_input_field_schemas": self.signature_input_field_schemas,
1090
1084
  "signature_output_field_schemas": self.signature_output_field_schemas,
1091
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1092
1085
  "signature_summary": self.signature_summary,
1093
1086
  # The server forwards this blob verbatim into a jsonb column, and its
1094
1087
  # program registration is best-effort (a rejection is only logged), so
@@ -1123,13 +1116,11 @@ class TraceBuilder:
1123
1116
  "module_class": self.module_class,
1124
1117
  # Also under the key a serve from before module_class reads (StartedFact.creation_dict).
1125
1118
  "program_name": self.module_class,
1126
- "program_version": self.program_version,
1127
1119
  "signature_name": self.signature_name,
1128
1120
  "signature_input_fields": self.signature_input_fields,
1129
1121
  "signature_output_fields": self.signature_output_fields,
1130
1122
  "signature_input_field_types": self.signature_input_field_types,
1131
1123
  "signature_output_field_types": self.signature_output_field_types,
1132
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1133
1124
  "signature_summary": self.signature_summary,
1134
1125
  "component_signatures": _safe_serialize(self.component_signatures),
1135
1126
  "root_span_type": self.root_span_type,
@@ -1287,9 +1278,6 @@ class EvalRunBuilder:
1287
1278
  metric_name: str | None = None
1288
1279
  dataset_hash: str | None = None
1289
1280
  dataset_size: int = 0
1290
- # The evaluated module's signature hash (legacy program key). None when the run has
1291
- # no module, and then sent as null.
1292
- program_signature_hash: int | None = None
1293
1281
  signature_summary: str | None = None # Human-readable: "input1, input2 -> output1"
1294
1282
 
1295
1283
  # Model Configuration
@@ -1411,7 +1399,6 @@ class EvalRunBuilder:
1411
1399
  "metric_name": self.metric_name or "",
1412
1400
  "dataset_hash": self.dataset_hash or "",
1413
1401
  "dataset_size": self.dataset_size,
1414
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1415
1402
  "signature_summary": self.signature_summary,
1416
1403
  "model_name": self.model_name,
1417
1404
  "model_provider": self.model_provider,
@@ -1432,7 +1419,6 @@ class EvalRunBuilder:
1432
1419
  "metric_name": self.metric_name or "",
1433
1420
  "dataset_hash": self.dataset_hash or "",
1434
1421
  "dataset_size": self.dataset_size,
1435
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1436
1422
  "signature_summary": self.signature_summary,
1437
1423
  "model_name": self.model_name,
1438
1424
  "model_provider": self.model_provider,
@@ -146,10 +146,6 @@ class TrackedGEPA:
146
146
 
147
147
  component_signature_hash = identity.compute_signature_hash(config.get("components", []))
148
148
 
149
- # Compute program_signature_hash via the IR reflector — same encoder
150
- # the trace path uses, so optimization_runs and traces join on identity.
151
- program_signature_hash = identity.compute_program_hash(student) or 0
152
-
153
149
  # Initialize optimization tracking
154
150
  self._tracker = tracker.OptimizationTracker(
155
151
  optimizer_type="gepa",
@@ -159,7 +155,6 @@ class TrackedGEPA:
159
155
  reflection_model_name=reflection_model_name,
160
156
  valset_hash=valset_hash,
161
157
  component_signature_hash=component_signature_hash,
162
- program_signature_hash=program_signature_hash,
163
158
  )
164
159
 
165
160
  # This path builds its tracker without `enrich_from_program`, so it types the
@@ -170,9 +165,7 @@ class TrackedGEPA:
170
165
  # SDK generates for the student, the name its rollouts' traces carry, and
171
166
  # resolves it when the run starts.
172
167
  if self._tracker.program_id is None and callback._declared_program_name() is None:
173
- self._tracker.generated_program_name = program_naming.generated_program_name(
174
- student, program_signature_hash
175
- )
168
+ self._tracker.generated_program_name = program_naming.generated_program_name(student)
176
169
 
177
170
  # Store both sets for dataset payload building
178
171
  self._tracker._trainset = list(trainset)
@@ -30,7 +30,7 @@ def sidecar_path(log_dir: str) -> str:
30
30
  return os.path.join(log_dir, _SIDECAR)
31
31
 
32
32
 
33
- def write_run_id(log_dir: str, run_id: str, program_signature_hash: int | None, start_time: datetime | None) -> None:
33
+ def write_run_id(log_dir: str, run_id: str, program_id: str | None, start_time: datetime | None) -> None:
34
34
  """Persist the server-assigned ``run_id`` into ``log_dir`` so a later resume
35
35
  can re-adopt it. Called at ``start()``, which runs before GEPA creates its
36
36
  ``run_dir`` — so create the directory if it isn't there yet. Best-effort: a
@@ -38,7 +38,7 @@ def write_run_id(log_dir: str, run_id: str, program_signature_hash: int | None,
38
38
  """
39
39
  payload = {
40
40
  "run_id": run_id,
41
- "program_signature_hash": program_signature_hash,
41
+ "program_id": program_id,
42
42
  "start_time": start_time.isoformat() if start_time is not None else None,
43
43
  }
44
44
  try:
@@ -49,15 +49,16 @@ def write_run_id(log_dir: str, run_id: str, program_signature_hash: int | None,
49
49
  logger.warning("could not write resume sidecar in %s: %s", log_dir, e)
50
50
 
51
51
 
52
- def read_resumable(log_dir: str, program_signature_hash: int | None) -> tuple[str, datetime | None] | None:
52
+ def read_resumable(log_dir: str, program_id: str | None) -> tuple[str, datetime | None] | None:
53
53
  """Return ``(run_id, start_time)`` to re-adopt, or ``None`` if this is not a
54
54
  resumable job.
55
55
 
56
56
  Resumable requires: GEPA's ``gepa_state.bin`` checkpoint present (else GEPA
57
57
  itself starts fresh) AND a readable sidecar carrying a ``run_id``. A definite
58
- ``program_signature_hash`` mismatch (a different program reusing the same
59
- ``log_dir``) is rejected so it can't hijack the id; a missing hash on either
60
- side errs toward stitching, since ``log_dir`` is the strong identity signal.
58
+ ``program_id`` mismatch (a different program reusing the same ``log_dir``) is
59
+ rejected so it can't hijack the id. A missing program id on either side errs
60
+ toward stitching, since ``log_dir`` is the strong identity signal. A sidecar an
61
+ older SDK wrote names no program id, so it stitches.
61
62
  """
62
63
  if not os.path.exists(os.path.join(log_dir, _CHECKPOINT)):
63
64
  return None
@@ -71,13 +72,13 @@ def read_resumable(log_dir: str, program_signature_hash: int | None) -> tuple[st
71
72
  if not run_id:
72
73
  return None
73
74
 
74
- prior_hash = data.get("program_signature_hash")
75
- if prior_hash is not None and program_signature_hash is not None and prior_hash != program_signature_hash:
75
+ prior_program = data.get("program_id")
76
+ if prior_program is not None and program_id is not None and prior_program != program_id:
76
77
  logger.warning(
77
- "resume sidecar in %s is for a different program (hash %s != %s); not re-adopting",
78
+ "resume sidecar in %s is for a different program (%s != %s); not re-adopting",
78
79
  log_dir,
79
- prior_hash,
80
- program_signature_hash,
80
+ prior_program,
81
+ program_id,
81
82
  )
82
83
  return None
83
84
 
@@ -274,7 +274,6 @@ class OptimizationTracker:
274
274
  reflection_model_name: str = "",
275
275
  valset_hash: str = "",
276
276
  component_signature_hash: str = "",
277
- program_signature_hash: int = 0,
278
277
  ):
279
278
  # The SDK no longer mints this id. It stays None until start()
280
279
  # runs the synchronous start POST and adopts the id assigned by the
@@ -303,8 +302,6 @@ class OptimizationTracker:
303
302
  self.trainset_hash = "" # individual trainset hash
304
303
  self.valset_individual_hash = "" # individual valset hash
305
304
  self.component_signature_hash = component_signature_hash # SHA256[:16] for model sweep grouping
306
- # Program signature hash - matches trace format (64-bit int from input/output fields+types)
307
- self.program_signature_hash = program_signature_hash
308
305
  self.start_time = datetime.now(timezone.utc)
309
306
 
310
307
  # Collected data
@@ -751,7 +748,7 @@ class OptimizationTracker:
751
748
  log_dir = self.config.get("log_dir") if self.config else None
752
749
  if not log_dir or not self.optimization_run_id:
753
750
  return False
754
- readopted = resume.read_resumable(log_dir, self.program_signature_hash)
751
+ readopted = resume.read_resumable(log_dir, self.program_id)
755
752
  return readopted is not None and readopted[0] == self.optimization_run_id
756
753
 
757
754
  def finalize_error(self, exception: BaseException) -> None:
@@ -853,7 +850,7 @@ class OptimizationTracker:
853
850
  # that id (and its original start_time, so duration stays cumulative)
854
851
  # instead of minting a fresh row. Jobs without a log_dir stay per-run.
855
852
  log_dir = self.config.get("log_dir") if self.config else None
856
- readopted = resume.read_resumable(log_dir, self.program_signature_hash) if log_dir else None
853
+ readopted = resume.read_resumable(log_dir, self.program_id) if log_dir else None
857
854
  if readopted is not None:
858
855
  self.optimization_run_id, prior_start = readopted
859
856
  if prior_start is not None:
@@ -870,7 +867,7 @@ class OptimizationTracker:
870
867
  self._publish_program()
871
868
  # Persist the id so a later resume of the same log_dir re-adopts it.
872
869
  if log_dir and self.optimization_run_id:
873
- resume.write_run_id(log_dir, self.optimization_run_id, self.program_signature_hash, self.start_time)
870
+ resume.write_run_id(log_dir, self.optimization_run_id, self.program_id, self.start_time)
874
871
  logger.info(
875
872
  f"Started optimization tracking: run_id={self.optimization_run_id}, "
876
873
  f"max_metric_calls={self.max_metric_calls}"
@@ -1169,7 +1166,6 @@ class OptimizationTracker:
1169
1166
  "reflection_model_name": self.reflection_model_name,
1170
1167
  "valset_hash": self.valset_hash,
1171
1168
  "component_signature_hash": self.component_signature_hash,
1172
- "program_signature_hash": identity.format_signature_hash(self.program_signature_hash),
1173
1169
  "start_time": self.start_time.isoformat() if self.start_time else None,
1174
1170
  "end_time": self.end_time.isoformat() if self.end_time else None,
1175
1171
  "duration_ms": duration_ms,
@@ -1435,13 +1431,6 @@ def enrich_from_program(
1435
1431
  trk.component_signature_hash = identity.compute_signature_hash(components)
1436
1432
  trk.component_types = code_component_types(student)
1437
1433
 
1438
- # Program signature hash from the live module (routes through the IR
1439
- # reflector so Predict vs ChainOfThought discriminates — don't pass a bare
1440
- # signature).
1441
- program_hash = identity.compute_program_hash(student)
1442
- if program_hash is not None:
1443
- trk.program_signature_hash = program_hash
1444
-
1445
1434
  # Model names.
1446
1435
  if lm is not None and getattr(lm, "model", None):
1447
1436
  trk.model_name = lm.model or ""
@@ -34,7 +34,7 @@ _NAME_PUNCTUATION = "._-"
34
34
  _RESERVED_PREFIX = "p_"
35
35
 
36
36
 
37
- def generated_program_name(module: Any, signature_hash: int | None = None) -> str:
37
+ def generated_program_name(module: Any) -> str:
38
38
  """The name the SDK gives the program ``module`` runs as, when its user named none.
39
39
 
40
40
  The name is the module's class and twelve hex digits of its signature hash, such as
@@ -43,13 +43,10 @@ def generated_program_name(module: Any, signature_hash: int | None = None) -> st
43
43
  names. An optimizer's new instructions and demos keep the shape, so they keep the
44
44
  name. The name always fits the program-name syntax, whatever the class is called.
45
45
 
46
- A caller that already holds ``identity.compute_program_hash(module)`` passes it as
47
- ``signature_hash``, so the module is reflected once. Without it, this function
48
- computes the hash.
46
+ The SDK sends the signature hash nowhere else, so naming the program is the only
47
+ place that reflects the module for it.
49
48
  """
50
- if signature_hash is None:
51
- signature_hash = identity.compute_program_hash(module)
52
- signature_hash = signature_hash or 0
49
+ signature_hash = identity.compute_program_hash(module) or 0
53
50
  suffix = f"{signature_hash & 0xFFFF_FFFF_FFFF_FFFF:016x}"[:_SUFFIX_DIGITS]
54
51
  return f"{_class_part(module.__class__.__name__)}-{suffix}"
55
52
 
@@ -28,6 +28,18 @@ already has its program, and a program name with it is refused when the file
28
28
  binds no program. In the shell, `deploy()` and `optimize()` take the
29
29
  program name as `program=`.
30
30
 
31
+ The platform deploys and optimizes only a program a user named. A program name
32
+ given here, to `cmpnd.Program(name, ...)` or to `cmpnd programs rename` is one.
33
+ A program with no name, or with a name a client generated, such as an
34
+ optimization's result, is refused with `409` and error code `program_unnamed`,
35
+ and the error names the command that fixes it:
36
+ `cmpnd programs rename <p_id> <name>`. It also tells you to declare that name in
37
+ your code with `@cmpnd.program("<name>")` or `cmpnd.Program("<name>", ...)`. The
38
+ rename frees the generated name, so code that declares no name sends its later
39
+ traces to a new program under the generated name. Deployments of such a program that
40
+ already exist keep serving. A rename never clears the name of a program with
41
+ a deployment that can serve.
42
+
31
43
  `cmpnd programs create PROGRAM_NAME` makes the program ahead of time and prints
32
44
  its id on stdout. It refuses a program name that already has a program. The
33
45
  command reports what it created on stderr, and `--json` prints serve's whole
@@ -108,11 +120,12 @@ PROGRAM name up as the other `cmpnd programs` commands do, and sends
108
120
  `POST /api/v1/programs/{id}/rename`. The command needs `traces:write` or
109
121
  `optimizations:write`.
110
122
 
111
- The old name keeps resolving to the program, so code that still declares it
112
- reaches the same program. A program with no name takes its first name this
113
- way, and code that declares the new name with `cmpnd.Program(name, ...)`
114
- traces to it. Serve refuses a name another live
115
- program answers to, as its current or a retired name. A program in the Trash
123
+ The old name is free at once. It no longer resolves to the program, and any
124
+ program of the organization can take it, so code that still declares the old
125
+ name with `cmpnd.Program(name, ...)` reaches a new program, not this one.
126
+ Update that code to the new name. A program with no name takes its first name
127
+ this way, and code that declares the new name traces to it. Serve refuses
128
+ another live program's current name. A program in the Trash
116
129
  is refused until `cmpnd programs restore` returns it. A server from before the
117
130
  rename route says so, and the error points at the program's page in the web
118
131
  app, which renames it too.
@@ -460,8 +473,8 @@ optimization result, not a program. Each group's `--help` names the column its
460
473
  IDs come from.
461
474
 
462
475
  `cmpnd evals list [--program NAME] [--module CLASS] [--metric] [--status]`
463
- lists eval runs, newest first. `--program` takes the program's name, current
464
- or retired, and a name no program answers to is an error that names it.
476
+ lists eval runs, newest first. `--program` takes the program's current
477
+ name, and a name no program holds is an error that names it.
465
478
  `--module` takes the evaluated module's class, such as `ReAct`, and matches it
466
479
  exactly. The CLI sends them as `program_name` and `module_class`. Each eval
467
480
  run in `evals list`, `evals show` and `programs evals` names what it evaluated
@@ -139,14 +139,16 @@ and every other name still raises. `TestDspyHookCoverage` in
139
139
  `tests/test_callback.py` asserts the coverage against whatever DSPy is installed,
140
140
  so the next release that adds a hook is caught by CI rather than by a user's logs.
141
141
 
142
- ### Signature extraction and signature_hash
142
+ ### Signature extraction and the program signature hash
143
143
 
144
- This doc does not re-derive hash semantics — see [`./clustering.md`](../../docs/CLUSTERING.md) for why `signature_hash` exists and how xxhash maps to a signed Int64. Here's only _where_ it gets attached:
144
+ No record the SDK sends carries the program signature hash. A trace, an eval run and an
145
+ optimization run name their program by `program_id`.
146
+ [`CLUSTERING.md`](../../docs/CLUSTERING.md) explains how the hash is computed.
145
147
 
146
- - **On a span** — `_capture_module_attributes` in `callback.py`. After walking `instance.signature.input_fields` / `output_fields` and recording types and schemas, it calls `compute_signature_hash(signature)` and stores the result on `span.signature_hash`. Composite modules (multi-child `dspy.Module` subclasses) take the `compute_composite_signature_hash` branch.
147
- - **On a trace** — `_create_trace` in `callback.py`. Same split: single-signature modules hash directly; composite modules walk `dspy.Predict` children and call the composite helper.
148
+ - **On a span** — `_capture_module_attributes` in `callback.py` walks `instance.signature.input_fields` / `output_fields` and records names, types, descriptions and schemas. It reflects nothing, because a span never names a program.
149
+ - **On a trace** — `_create_trace` in `callback.py` records the root signature, or for a composite module the `dspy.Predict` children's signatures as `component_signatures`. A root module call that names no program reflects the module once, for the name the SDK generates (`program_naming.generated_program_name`). The hash goes into that name and nowhere else.
148
150
 
149
- Both code paths import from `python-sdk/cmpnd/identity.py`, which is the authoritative home of every hash the SDK computes.
151
+ `python-sdk/cmpnd/identity.py` is the home of every hash the SDK computes.
150
152
 
151
153
  ---
152
154
 
@@ -690,7 +692,10 @@ the top level and in `started_fact`, and again under `program_name`, the key a
690
692
  serve from before `module_class` reads and digests a start from. A standalone
691
693
  optimizer LM trace sends `GEPA` there. No trace body carries the program's own name: a declared name
692
694
  travels as the program id it resolves to, as "A declared program name resolves
693
- to an id in the exporter" describes.
695
+ to an id in the exporter" describes. No trace body carries a version either. A
696
+ trace serve executed names its version by the `deployment_id` serve stamps on
697
+ it, and `TraceBuilder` has no `program_version` field. A program that wants to
698
+ record a version of its own, such as a git SHA, adds it as a project tag.
694
699
 
695
700
  The callback seals `started_fact` after it sets the root type, before the
696
701
  module executes. It keeps that snapshot even when start emission is disabled.
@@ -735,7 +740,7 @@ What it captures beyond the normal span flow:
735
740
  - An `EvalRunBuilder` (constructed at `eval_handler.py:79`) that groups all per-example traces under one eval ID.
736
741
  - The metric name (via `get_metric_name` from `python-sdk/cmpnd/helpers.py`) and inferred provider.
737
742
  - Links to the enclosing optimization run when one exists (`eval_handler.py:85`), so eval runs triggered inside `GEPA.compile` roll up under the optimization in the UI.
738
- - The program the run evaluates, as `program_id`. A program name the call declares wins: the exporter resolves it with `resolve_program_name`, the same once-per-process answer its traces use, on the caller's thread because the eval start is sent synchronously. Without a declared name, the run takes the callback's program id, else the program of the optimization run it belongs to. A run with none of these resolves the name the SDK generates for the evaluated module, the name that module's traces carry, with `resolve_program_name(name, generated=True)`. A name serve refuses leaves the run without an id. The evaluated module's class goes as `module_class`, and again under `program_name`, the key a serve from before `module_class` reads. A run with no module sends `program_signature_hash` as null.
743
+ - The program the run evaluates, as `program_id`. A program name the call declares wins: the exporter resolves it with `resolve_program_name`, the same once-per-process answer its traces use, on the caller's thread because the eval start is sent synchronously. Without a declared name, the run takes the callback's program id, else the program of the optimization run it belongs to. A run with none of these resolves the name the SDK generates for the evaluated module, the name that module's traces carry, with `resolve_program_name(name, generated=True)`. A name serve refuses leaves the run without an id. The evaluated module's class goes as `module_class`, and again under `program_name`, the key a serve from before `module_class` reads. The run sends no `program_signature_hash`.
739
744
  - The exporter it publishes through: the callback's own (`_current_exporter`), so a client's eval runs go to the client's account.
740
745
  - The dataset — on `on_evaluate_start` it triggers the `dataset_sync` pre-flight described above.
741
746
 
@@ -761,7 +766,6 @@ The trace payload is built in `python-sdk/cmpnd/models.py` by `TraceBuilder.to_a
761
766
  "trace_id": "...",
762
767
  "parent_span_id": "...",
763
768
  "span_type": "predict",
764
- "signature_hash": -3847510293847, # signed Int64
765
769
  "signature_input_fields": ["question"],
766
770
  "signature_output_fields": ["answer"],
767
771
  "model_name": "gpt-4o-mini",
@@ -813,9 +817,8 @@ name form. A program name wins over the config's id. `configure()`
813
817
  checks the id's shape and raises `ValueError` on a bad one, and a `Config` built directly
814
818
  is checked the same way.
815
819
 
816
- The id rides every body that can declare a program. A deployment create names its program
817
- by the id alone and sends no `program_signature_hash`. The other bodies still carry the
818
- hash beside the id:
820
+ The id rides every body that can declare a program. No body carries a
821
+ `program_signature_hash`:
819
822
 
820
823
  | Body | Where | Built by |
821
824
  |---|---|---|
@@ -11,8 +11,9 @@ This module keeps the answers. The transport makes the requests, because they ri
11
11
  client and its retry budget.
12
12
 
13
13
  An answer is kept for the life of the process. A name that serve later points at
14
- another program reaches a process only when the process starts again, and a renamed
15
- program's old name keeps resolving to it.
14
+ another program reaches a process only when the process starts again. A rename frees
15
+ the old name, so a process that resolved it before the rename keeps sending to the
16
+ renamed program, and a process started after it gets a new program under that name.
16
17
 
17
18
  A name settles in one of two ways. Serve gives an id, or serve refuses the name, as a
18
19
  server without program names does with ``405``. A refused name sends its traces without