cmpnd 0.67.0__tar.gz → 0.69.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 (399) hide show
  1. {cmpnd-0.67.0 → cmpnd-0.69.0}/PKG-INFO +1 -1
  2. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_authored_import.py +0 -1
  3. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/authored.py +0 -5
  4. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/programs.py +45 -0
  5. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/verbs.py +1 -0
  6. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/deployment.py +1 -4
  7. {cmpnd-0.67.0 → cmpnd-0.69.0}/docs/cli.md +18 -0
  8. {cmpnd-0.67.0 → cmpnd-0.69.0}/docs/sdk-instrumentation.md +3 -1
  9. {cmpnd-0.67.0 → cmpnd-0.69.0}/pyproject.toml +1 -1
  10. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/seed_review_run.py +146 -66
  11. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_assessments_cli.py +4 -9
  12. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_deploy_programs.py +0 -1
  13. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimize_loop.py +7 -11
  14. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_valsets_cli.py +6 -10
  15. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_authored.py +2 -1
  16. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_programs.py +77 -0
  17. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_deploy.py +3 -4
  18. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_deploy_program_id.py +1 -1
  19. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_seed_review_run.py +106 -2
  20. {cmpnd-0.67.0 → cmpnd-0.69.0}/uv.lock +1 -1
  21. {cmpnd-0.67.0 → cmpnd-0.69.0}/.gitignore +0 -0
  22. {cmpnd-0.67.0 → cmpnd-0.69.0}/AGENTS.md +0 -0
  23. {cmpnd-0.67.0 → cmpnd-0.69.0}/CLAUDE.md +0 -0
  24. {cmpnd-0.67.0 → cmpnd-0.69.0}/README.md +0 -0
  25. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/__init__.py +0 -0
  26. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_assessments.py +0 -0
  27. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_deployments.py +0 -0
  28. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_exporter_pool.py +0 -0
  29. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_gepa_patch.py +0 -0
  30. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_http.py +0 -0
  31. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_program_patch.py +0 -0
  32. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_rlm_patch.py +0 -0
  33. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_runtime_tools.py +0 -0
  34. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_timespec.py +0 -0
  35. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_traces.py +0 -0
  36. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/_valsets.py +0 -0
  37. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/callback.py +0 -0
  38. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/__init__.py +0 -0
  39. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/api_client.py +0 -0
  40. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/__init__.py +0 -0
  41. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/admin.py +0 -0
  42. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/assessments.py +0 -0
  43. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/auth.py +0 -0
  44. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/datasets.py +0 -0
  45. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/deployments.py +0 -0
  46. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/evals.py +0 -0
  47. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/login.py +0 -0
  48. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/optimizations.py +0 -0
  49. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/org.py +0 -0
  50. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  51. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  52. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/top.py +0 -0
  53. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/traces.py +0 -0
  54. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/commands/valsets.py +0 -0
  55. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/credentials.py +0 -0
  56. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/deprecation.py +0 -0
  57. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/pagination.py +0 -0
  58. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/shell.py +0 -0
  59. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/trace_render.py +0 -0
  60. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/tree_help.py +0 -0
  61. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/cli/views.py +0 -0
  62. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/client.py +0 -0
  63. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/configuration.py +0 -0
  64. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/context.py +0 -0
  65. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/credentials.py +0 -0
  66. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/dataset_sync.py +0 -0
  67. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/datasets.py +0 -0
  68. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/decorators.py +0 -0
  69. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/deployment_metadata.py +0 -0
  70. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/deployment_runs.py +0 -0
  71. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/eval_handler.py +0 -0
  72. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/execution.py +0 -0
  73. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/exporter.py +0 -0
  74. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/exporter_http.py +0 -0
  75. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/helpers.py +0 -0
  76. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/identity.py +0 -0
  77. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/imaging.py +0 -0
  78. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/ir/__init__.py +0 -0
  79. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/ir/decode.py +0 -0
  80. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/ir/encode.py +0 -0
  81. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/ir/hash.py +0 -0
  82. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/ir/program.py +0 -0
  83. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/ir/reflection.py +0 -0
  84. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/ir/types.py +0 -0
  85. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/ir/xxh64.py +0 -0
  86. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/lm_configuration.py +0 -0
  87. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/mcp_servers.py +0 -0
  88. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/models.py +0 -0
  89. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/named_programs.py +0 -0
  90. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/optimization.py +0 -0
  91. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/optimizers/__init__.py +0 -0
  92. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/optimizers/gepa.py +0 -0
  93. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  94. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/optimizers/resume.py +0 -0
  95. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/optimizers/tracker.py +0 -0
  96. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/packaging.py +0 -0
  97. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/programs.py +0 -0
  98. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/references.py +0 -0
  99. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/retry.py +0 -0
  100. {cmpnd-0.67.0 → cmpnd-0.69.0}/cmpnd/traces.py +0 -0
  101. {cmpnd-0.67.0 → cmpnd-0.69.0}/docs/2026-04-02-code-review.md +0 -0
  102. {cmpnd-0.67.0 → cmpnd-0.69.0}/docs/client.md +0 -0
  103. {cmpnd-0.67.0 → cmpnd-0.69.0}/docs/custom-proposer.md +0 -0
  104. {cmpnd-0.67.0 → cmpnd-0.69.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  105. {cmpnd-0.67.0 → cmpnd-0.69.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  106. {cmpnd-0.67.0 → cmpnd-0.69.0}/docs/reading-traces.md +0 -0
  107. {cmpnd-0.67.0 → cmpnd-0.69.0}/examples/generalizing_proposer.py +0 -0
  108. {cmpnd-0.67.0 → cmpnd-0.69.0}/examples/local_ollama.py +0 -0
  109. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/LINEAGE.md +0 -0
  110. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/README.md +0 -0
  111. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  112. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  113. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  114. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  115. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  116. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  117. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/py.typed +0 -0
  118. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  119. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/retry.py +0 -0
  120. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
  121. {cmpnd-0.67.0 → cmpnd-0.69.0}/ingest/pyproject.toml +0 -0
  122. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/audit-sdist.sh +0 -0
  123. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/capture_review_run.py +0 -0
  124. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/check-pip-install.sh +0 -0
  125. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/check_release_artifacts.py +0 -0
  126. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/fixtures/review_run.json.gz +0 -0
  127. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/gepa_resume_bug.py +0 -0
  128. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/install-test-env.sh +0 -0
  129. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/release-gate.sh +0 -0
  130. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/release-real-models.sh +0 -0
  131. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/require_version_bump.py +0 -0
  132. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/selfhost_compat.py +0 -0
  133. {cmpnd-0.67.0 → cmpnd-0.69.0}/scripts/test-in-linux.sh +0 -0
  134. {cmpnd-0.67.0 → cmpnd-0.69.0}/stubs/dspy/__init__.pyi +0 -0
  135. {cmpnd-0.67.0 → cmpnd-0.69.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  136. {cmpnd-0.67.0 → cmpnd-0.69.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  137. {cmpnd-0.67.0 → cmpnd-0.69.0}/stubs/dspy/utils/__init__.pyi +0 -0
  138. {cmpnd-0.67.0 → cmpnd-0.69.0}/stubs/dspy/utils/callback.pyi +0 -0
  139. {cmpnd-0.67.0 → cmpnd-0.69.0}/stubs/dspy/utils/dummies.pyi +0 -0
  140. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/__init__.py +0 -0
  141. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/_credential_route.py +0 -0
  142. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/_deployment_signature_helpers.py +0 -0
  143. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/_exporter_helpers.py +0 -0
  144. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/_fake_clock.py +0 -0
  145. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/_identity_helpers.py +0 -0
  146. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/_resolve_refs.py +0 -0
  147. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/_traced_run.py +0 -0
  148. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/conftest.py +0 -0
  149. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/__init__.py +0 -0
  150. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/committee_task.py +0 -0
  151. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/conftest.py +0 -0
  152. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/continuation_task.py +0 -0
  153. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  154. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  155. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/fake_lm.py +0 -0
  156. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/helpers.py +0 -0
  157. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/target_capabilities.py +0 -0
  158. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_capture_prompts.py +0 -0
  159. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_composite_module.py +0 -0
  160. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  161. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_deploy_execute.py +0 -0
  162. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_deploy_pandas.py +0 -0
  163. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_edge_cases.py +0 -0
  164. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_example_hash.py +0 -0
  165. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_gepa_optimization.py +0 -0
  166. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_interpreter_spans.py +0 -0
  167. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_lm_accuracy.py +0 -0
  168. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_module_tracing.py +0 -0
  169. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimization_lineage.py +0 -0
  170. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  171. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
  172. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimize_continuation.py +0 -0
  173. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
  174. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimize_failures.py +0 -0
  175. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  176. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  177. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_parallel_export.py +0 -0
  178. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_project_attribution.py +0 -0
  179. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_react_tool_spans.py +0 -0
  180. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_save_load.py +0 -0
  181. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_trace_structure.py +0 -0
  182. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_traces_read.py +0 -0
  183. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/e2e/test_user_attribution.py +0 -0
  184. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  185. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  186. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  187. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  188. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  189. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  190. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  191. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  192. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  193. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/10_react.json +0 -0
  194. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/11_refine.json +0 -0
  195. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/12_retrieve.json +0 -0
  196. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/13_knn.json +0 -0
  197. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/14_parallel.json +0 -0
  198. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/identity_vectors/README.md +0 -0
  199. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/ingest_conformance.py +0 -0
  200. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/__init__.py +0 -0
  201. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/authored_optimize/judge.py +0 -0
  202. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  203. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/conftest.py +0 -0
  204. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  205. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/hosted_optimize_probe.py +0 -0
  206. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/optimization_run_probe.py +0 -0
  207. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  208. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/test_logs_renders_traces.py +0 -0
  209. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/test_optimization_run.py +0 -0
  210. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/test_optimizing_progress.py +0 -0
  211. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/integration/test_unhappy_path.py +0 -0
  212. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/__init__.py +0 -0
  213. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/__main__.py +0 -0
  214. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/drain.py +0 -0
  215. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/ingest.py +0 -0
  216. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/latency_transport.py +0 -0
  217. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/rlm_shape.py +0 -0
  218. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/rlm_size_report.py +0 -0
  219. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/sweep.py +0 -0
  220. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/test_rlm_shape.py +0 -0
  221. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/load/test_rlm_size_report.py +0 -0
  222. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/__init__.py +0 -0
  223. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/conftest.py +0 -0
  224. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/seed_parity.py +0 -0
  225. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_auth.py +0 -0
  226. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_datasets.py +0 -0
  227. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_deployments.py +0 -0
  228. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_evals.py +0 -0
  229. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_health.py +0 -0
  230. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_modules.py +0 -0
  231. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_optimizations.py +0 -0
  232. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_programs.py +0 -0
  233. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_signatures.py +0 -0
  234. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_trace_streaming.py +0 -0
  235. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/parity/test_traces.py +0 -0
  236. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/__init__.py +0 -0
  237. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/_provider.py +0 -0
  238. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/_readiness.py +0 -0
  239. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/_reliability.py +0 -0
  240. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/_result.py +0 -0
  241. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/mint_bedrock_token.py +0 -0
  242. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_bedrock_converse.py +0 -0
  243. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_deploy_programs.py +0 -0
  244. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_multi_client_stress.py +0 -0
  245. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_optimize_client_committee.py +0 -0
  246. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_optimize_continuation.py +0 -0
  247. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_optimize_loop.py +0 -0
  248. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
  249. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_optimize_stress.py +0 -0
  250. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_provider_roles.py +0 -0
  251. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_readiness.py +0 -0
  252. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_real_provider_twins.py +0 -0
  253. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_reliability.py +0 -0
  254. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_reliability_classify.py +0 -0
  255. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_rlm_wasm.py +0 -0
  256. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/real_models/test_trace_search.py +0 -0
  257. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/span_reference_fixture.py +0 -0
  258. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_adapter_reentrancy.py +0 -0
  259. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_assessments_api.py +0 -0
  260. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_authored_binding_credentials.py +0 -0
  261. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_authored_bundle_collisions.py +0 -0
  262. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_authored_metric_contract.py +0 -0
  263. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_authored_named_models.py +0 -0
  264. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_authored_optimize.py +0 -0
  265. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_callback.py +0 -0
  266. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_candidate_lineage.py +0 -0
  267. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/__init__.py +0 -0
  268. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/conftest.py +0 -0
  269. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_admin.py +0 -0
  270. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_api_client.py +0 -0
  271. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_assessments.py +0 -0
  272. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_auth.py +0 -0
  273. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_credentials.py +0 -0
  274. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_datasets.py +0 -0
  275. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_deployment_runs.py +0 -0
  276. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_deployments.py +0 -0
  277. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_evals.py +0 -0
  278. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_login.py +0 -0
  279. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_main.py +0 -0
  280. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_optimizations.py +0 -0
  281. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_org.py +0 -0
  282. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_pagination.py +0 -0
  283. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_parity.py +0 -0
  284. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_provider_credentials.py +0 -0
  285. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_schema_sample.py +0 -0
  286. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_shell.py +0 -0
  287. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_tool_servers.py +0 -0
  288. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_top.py +0 -0
  289. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_traces.py +0 -0
  290. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
  291. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  292. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_tree_help.py +0 -0
  293. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cli/test_valsets.py +0 -0
  294. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_client.py +0 -0
  295. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_client_floor.py +0 -0
  296. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_client_program_names.py +0 -0
  297. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_clustering/__init__.py +0 -0
  298. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_clustering/conftest.py +0 -0
  299. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_clustering/test_edge_cases.py +0 -0
  300. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  301. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_clustering/test_type_conversion.py +0 -0
  302. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_cmp551_grouping_identity.py +0 -0
  303. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_configuration.py +0 -0
  304. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_context.py +0 -0
  305. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_context_propagation.py +0 -0
  306. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_credential_kwargs.py +0 -0
  307. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_credential_selection.py +0 -0
  308. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_credential_values.py +0 -0
  309. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_dataset_polling.py +0 -0
  310. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_dataset_sync.py +0 -0
  311. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_datasets.py +0 -0
  312. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_decision_client_spans.py +0 -0
  313. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_decorators.py +0 -0
  314. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  315. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_deployment_metadata.py +0 -0
  316. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_deployment_runs.py +0 -0
  317. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_deployments_read.py +0 -0
  318. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_eval.py +0 -0
  319. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_eval_example_attribution.py +0 -0
  320. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_eval_start.py +0 -0
  321. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_eval_trace_grouping.py +0 -0
  322. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_exception_paths.py +0 -0
  323. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_execute.py +0 -0
  324. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_execute_run_identity.py +0 -0
  325. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_exporter.py +0 -0
  326. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_exporter_program_names.py +0 -0
  327. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_exporter_queue.py +0 -0
  328. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_exporter_real.py +0 -0
  329. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_fake_lm.py +0 -0
  330. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_gepa_callback.py +0 -0
  331. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_gepa_integration.py +0 -0
  332. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_gepa_output_normalization.py +0 -0
  333. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_gepa_resume.py +0 -0
  334. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_identity.py +0 -0
  335. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_imaging.py +0 -0
  336. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_import_weight.py +0 -0
  337. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_ingest_conformance.py +0 -0
  338. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_ingest_second_producer.py +0 -0
  339. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_integration.py +0 -0
  340. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_interpreter_callback.py +0 -0
  341. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_ir_encoder_properties.py +0 -0
  342. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_ir_golden_vectors.py +0 -0
  343. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_ir_roundtrip.py +0 -0
  344. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_lm15_native_calls.py +0 -0
  345. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_lm_configuration.py +0 -0
  346. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_lm_usage_shapes.py +0 -0
  347. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_mcp_tools.py +0 -0
  348. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_models.py +0 -0
  349. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_multi_client_stress_readiness.py +0 -0
  350. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimization_progress.py +0 -0
  351. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimization_start.py +0 -0
  352. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimize.py +0 -0
  353. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimize_budget.py +0 -0
  354. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimize_lift.py +0 -0
  355. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimize_lms.py +0 -0
  356. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimize_proposer.py +0 -0
  357. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimize_reflection_lm.py +0 -0
  358. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_optimize_terminal_failure.py +0 -0
  359. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_packaging.py +0 -0
  360. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_packaging_callable_source.py +0 -0
  361. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_packaging_mcp_tools.py +0 -0
  362. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_packaging_program_source.py +0 -0
  363. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_poll_until.py +0 -0
  364. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_program_id_deprecation.py +0 -0
  365. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_program_identity.py +0 -0
  366. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_program_lineage_persist.py +0 -0
  367. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_program_names_in_deploy.py +0 -0
  368. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_programs_create.py +0 -0
  369. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_project_payload.py +0 -0
  370. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_real_provider_twins_readiness.py +0 -0
  371. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_reflector_properties.py +0 -0
  372. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_reflector_smoke.py +0 -0
  373. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_release_gate_script.py +0 -0
  374. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_release_real_models_script.py +0 -0
  375. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_replaced_names.py +0 -0
  376. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_repro_reactv2_bugs.py +0 -0
  377. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_require_version_bump.py +0 -0
  378. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_retry.py +0 -0
  379. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_reversible_failures.py +0 -0
  380. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_rlm_patch.py +0 -0
  381. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_run_attribution_paths.py +0 -0
  382. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_runtime_tools.py +0 -0
  383. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_schemas.py +0 -0
  384. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_seal_lm_credential.py +0 -0
  385. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_signature_inputs.py +0 -0
  386. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_span_references.py +0 -0
  387. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_span_sent_once.py +0 -0
  388. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_system_one.py +0 -0
  389. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_trace_context_concurrency.py +0 -0
  390. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_trace_fact_wire_contract.py +0 -0
  391. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_trace_facts.py +0 -0
  392. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_trace_render.py +0 -0
  393. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_traces_read.py +0 -0
  394. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_tracked_gepa.py +0 -0
  395. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_tracker_enrich.py +0 -0
  396. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_truncation.py +0 -0
  397. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_upload_auth_headers.py +0 -0
  398. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_valsets_api.py +0 -0
  399. {cmpnd-0.67.0 → cmpnd-0.69.0}/tests/test_xxh64.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.67.0
3
+ Version: 0.69.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>
@@ -263,7 +263,6 @@ def _program_facts(
263
263
  lm = packaging._extract_lm_object(program)
264
264
  lm_kwargs = getattr(lm, "kwargs", {}) if lm is not None else {}
265
265
  facts = ProgramFacts(
266
- program_signature_hash=identity.compute_program_hash(program),
267
266
  entrypoint_kind=kind,
268
267
  module_class=type(program).__name__,
269
268
  litellm_model=getattr(lm, "model", None),
@@ -43,7 +43,6 @@ class SourceCheckError(ValueError):
43
43
  class ProgramFacts:
44
44
  """Facts derived by importing the staged program without calling forward."""
45
45
 
46
- program_signature_hash: int | None = None
47
46
  entrypoint_kind: str = "instance"
48
47
  module_class: str | None = None
49
48
  signature_name: str | None = None
@@ -72,11 +71,7 @@ class ProgramFacts:
72
71
 
73
72
  def deploy_body(self) -> dict[str, Any]:
74
73
  """Return fields the deployment create route accepts."""
75
- from . import identity
76
-
77
74
  body: dict[str, Any] = {}
78
- if self.program_signature_hash is not None:
79
- body["program_signature_hash"] = identity.format_signature_hash(self.program_signature_hash)
80
75
  for name in (
81
76
  "module_class",
82
77
  "signature_name",
@@ -207,6 +207,19 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
207
207
  purge.add_argument("--force", action="store_true", help="Skip the confirmation (required when not on a terminal)")
208
208
  purge.set_defaults(func=handle_purge)
209
209
 
210
+ rename = sub.add_parser(
211
+ "rename",
212
+ help="Name or rename a program",
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."
217
+ ),
218
+ )
219
+ rename.add_argument("program", metavar="PROGRAM", help="Program name or p_ id")
220
+ rename.add_argument("new_name", metavar="NAME", help="The program's new name, unique in your organization")
221
+ rename.set_defaults(func=handle_rename)
222
+
210
223
  select = sub.add_parser("select", help="Pin an optimization run as this program's chosen version")
211
224
  select.add_argument("id", help=_COMPILED_HASH_HELP)
212
225
  select.add_argument("--run", dest="run_id", required=True, help="Optimization run ID (op_...) to pin")
@@ -624,6 +637,38 @@ def handle_purge(client: api_client.ApiClient, args: Any) -> Any:
624
637
  return _trash_write(client, f"/api/v1/programs/{program_id}/purge")
625
638
 
626
639
 
640
+ def handle_rename(client: api_client.ApiClient, args: Any) -> Any:
641
+ """Make ``args.new_name`` the current name of the program ``args.program`` names, keeping its id."""
642
+ from ... import programs
643
+ from . import top
644
+
645
+ try:
646
+ programs.validate_program_name(args.new_name)
647
+ except ValueError as exc:
648
+ raise api_client.CliError(str(exc)) from exc
649
+ program_id, trashed = resolve(client, args.program, old_server_remedy=_rename_remedy())
650
+ if trashed:
651
+ raise api_client.CliError(
652
+ f"{args.program} is in the Trash. Restore it with `{top.hint_programs('restore', args.program)}` "
653
+ "before you rename it."
654
+ )
655
+ try:
656
+ return client.post(f"/api/v1/programs/{program_id}/rename", json={"name": args.new_name})
657
+ except api_client.CliError as exc:
658
+ # A server from before the rename route answers 405, because its GET dispatcher owns the path.
659
+ if exc.status_code == 405:
660
+ _refuse_if_old(client, exc, _rename_remedy(program_id))
661
+ raise api_client.CliError(
662
+ f"this cmpnd server predates renaming a program through the API. {_rename_remedy(program_id)}"
663
+ ) from exc
664
+ raise
665
+
666
+
667
+ def _rename_remedy(program_id: str = "") -> str:
668
+ where = f"on its page in the web app, at /ui/programs/{program_id}" if program_id else "in the web app"
669
+ return f"Rename the program {where}, or upgrade the server."
670
+
671
+
627
672
  def _trash_listing(client: api_client.ApiClient) -> Any:
628
673
  """GET the Trash. On a server from before the Trash API it answers 404, which is the old-server signal."""
629
674
  try:
@@ -167,6 +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
171
  }
171
172
 
172
173
  #: Leaves naming a related collection or sub-record rather than an action.
@@ -527,13 +527,10 @@ def deploy_module_under(
527
527
  # Package the module
528
528
  zip_bytes = packaging.generate_source_zip(module, metric, mcp_tools=pinned_mcp_tools)
529
529
 
530
- # Compute program identity via the IR reflector on the live module (class
531
- # tag included); the signature is kept around only for display metadata.
530
+ # The deployment names its program by id alone; the signature only describes it.
532
531
  sig = packaging._get_module_signature(module)
533
- program_signature_hash = identity.compute_program_hash(module) or 0
534
532
 
535
533
  deploy_body: dict[str, Any] = {
536
- "program_signature_hash": identity.format_signature_hash(program_signature_hash),
537
534
  # Declare the ZIP's exact length so the server can reject an oversized
538
535
  # deploy before the upload.
539
536
  "declared_content_length": len(zip_bytes),
@@ -99,6 +99,24 @@ also names the `cmpnd programs optimized` command for a compiled program hash.
99
99
  program hash, the value in the `program_hash` column of
100
100
  `cmpnd programs optimized list`, as `cmpnd programs select` does.
101
101
 
102
+ ## Renaming a program
103
+
104
+ `cmpnd programs rename PROGRAM NAME` makes NAME the current name of PROGRAM,
105
+ which is a program name or a `p_` id. The program keeps its id and its
106
+ history. The CLI checks NAME's syntax before it sends anything, looks a
107
+ PROGRAM name up as the other `cmpnd programs` commands do, and sends
108
+ `POST /api/v1/programs/{id}/rename`. The command needs `traces:write` or
109
+ `optimizations:write`.
110
+
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
116
+ is refused until `cmpnd programs restore` returns it. A server from before the
117
+ rename route says so, and the error points at the program's page in the web
118
+ app, which renames it too.
119
+
102
120
  ## The Trash
103
121
 
104
122
  A program in the Trash leaves every view, and its traces with it, until it is
@@ -803,7 +803,9 @@ name form. A program name wins over the config's id. `configure()`
803
803
  checks the id's shape and raises `ValueError` on a bad one, and a `Config` built directly
804
804
  is checked the same way.
805
805
 
806
- The id rides every body that can declare a program, beside `program_signature_hash`:
806
+ The id rides every body that can declare a program. A deployment create names its program
807
+ by the id alone and sends no `program_signature_hash`. The other bodies still carry the
808
+ hash beside the id:
807
809
 
808
810
  | Body | Where | Built by |
809
811
  |---|---|---|
@@ -5,7 +5,7 @@ name = "cmpnd"
5
5
  # it's new) — no git tags. Bump with `uv version --bump {minor,patch}` or by
6
6
  # hand. Runtime `cmpnd.__version__` reads installed metadata, so it tracks
7
7
  # this automatically. See docs/plans/2026-07-02-sdk-pypi-tag-free-publish.md.
8
- version = "0.67.0"
8
+ version = "0.69.0"
9
9
  description = "DSPy observability and deployment SDK for cmpnd"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.11"
@@ -25,6 +25,11 @@ Each run is made fresh: every trace/span UUID is remapped to a new one, a fresh
25
25
  reserve-then-PUT shape), the traces are linked to it, and all timestamps are
26
26
  shifted so the run lands at "now".
27
27
 
28
+ Every seeded program is named. The seeder resolves each name to its `p_` id
29
+ through `PUT /api/v1/programs/by-name/{name}` and declares that id on the
30
+ program's traces, runs and deployments, and pins by it. The replayed payloads
31
+ keep the `program_signature_hash` the SDK captured, as the SDK still sends it.
32
+
28
33
  Non-gating: any failure logs and exits 0 (the review pipeline must not break on it).
29
34
 
30
35
  Env: CMPND_E2E_ENDPOINT, CMPND_E2E_API_KEY (the review app URL + key; no LM keys).
@@ -148,9 +153,9 @@ def _transform(obj, uuid_map: dict[str, str], delta: datetime.timedelta, opt_run
148
153
  # A realistic GEPA `log_dir` (== gepa `run_dir`) the resume pair shares. The
149
154
  # completed run and its interrupted twin are the SAME job (CMP-442): a laptop
150
155
  # sleep / power cut split one optimization into a stuck `running` row plus a
151
- # separate `completed` row, keyed on this directory. `program_signature_hash`
152
- # already matches (both are the same committee `Predict`); the shared `log_dir`
153
- # is what makes them read as one interrupted-then-resumed job.
156
+ # separate `completed` row, keyed on this directory. Both declare the committee
157
+ # program's `p_` id; the shared `log_dir` is what makes them read as one
158
+ # interrupted-then-resumed job.
154
159
  _LOG_DIR = "/home/researcher/gepa_runs/committee-3b"
155
160
 
156
161
  # Freeze the captured run right after iteration 2 — the point where GEPA has
@@ -625,6 +630,50 @@ def _replay_flat_fixture(
625
630
  }
626
631
 
627
632
 
633
+ # The name of each program the seed plants. The seeder resolves each to its p_ id
634
+ # and declares that id; a review app makes each program on its first seed.
635
+ _PROGRAM_COMMITTEE = "committee-extractor"
636
+ _PROGRAM_DISTRICT_COUNTER = "district-counter"
637
+ _PROGRAM_SOURCE_CATEGORIZER = "source-categorizer"
638
+ _PROGRAM_TOOL_KIND_DEMO = "tool-kind-demo"
639
+ _PROGRAM_REACT_COMMITTEE = "react-committee"
640
+
641
+
642
+ def _declare_program(payload: dict, program_id: str | None) -> dict:
643
+ """A copy of a trace or optimization run payload that declares ``program_id``.
644
+ With no id, the copy declares no program."""
645
+ out = dict(payload)
646
+ if program_id is not None:
647
+ out["program_id"] = program_id
648
+ return out
649
+
650
+
651
+ def _declare_program_traces(traces: list, module_class: str, program_id: str | None) -> list:
652
+ """Declare ``program_id`` on the captured run's program traces: those whose root
653
+ module is the run's program class. The optimizer's own traces (GEPA) belong to
654
+ no program and stay undeclared."""
655
+ return [
656
+ _declare_program(t, program_id) if isinstance(t, dict) and t.get("module_class") == module_class else t
657
+ for t in traces
658
+ ]
659
+
660
+
661
+ def _ensure_program(endpoint: str, api_key: str, name: str) -> str | None:
662
+ """The p_ id of the program named ``name``, made on first use. Non-gating:
663
+ None on any failure, and the seed goes on without the program."""
664
+ try:
665
+ _status, resp = _post(
666
+ endpoint, "/api/v1/programs/by-name/" + urllib.parse.quote(name, safe=""), {}, api_key, method="PUT"
667
+ )
668
+ except Exception as exc: # noqa: BLE001 - non-gating, matches the seeder's philosophy
669
+ log.warning("resolve program %s failed (non-gating): %s", name, exc)
670
+ return None
671
+ program_id = resp.get("program_id") if isinstance(resp, dict) else None
672
+ if not program_id:
673
+ log.warning("resolve program %s returned no id", name)
674
+ return program_id
675
+
676
+
628
677
  def _post(endpoint: str, path: str, payload, api_key: str, method: str = "POST"):
629
678
  body = json.dumps(payload).encode("utf-8")
630
679
  req = urllib.request.Request(
@@ -650,20 +699,21 @@ def _reserve_optimization(endpoint: str, api_key: str) -> str | None:
650
699
  return run_id
651
700
 
652
701
 
653
- def _create_deployment(endpoint: str, api_key: str, program_signature_hash: int) -> str | None:
702
+ def _create_deployment(endpoint: str, api_key: str, program_id: str) -> str | None:
654
703
  """Create a real deployment row (no ZIP upload) and return its `d_…` id.
655
704
 
656
705
  The traces-index Deployment filter (CMP-277) reads distinct deployment_id
657
706
  off traces, and the ingest sanitizer strips any deployment_id that isn't a
658
707
  real deployment for the org — so seeded traces need real rows to point at.
659
708
  A create-only POST suffices; the row exists (pending upload) and the
660
- sanitizer accepts its id. The program hash lets the filter menu + Deployments
661
- page resolve a human program-name label. Non-gating: returns None on any
662
- failure so the seed proceeds without the deployment dimension.
709
+ sanitizer accepts its id. The program id, which the route requires, lets the
710
+ filter menu + Deployments page resolve a human program-name label.
711
+ Non-gating: returns None on any failure so the seed proceeds without the
712
+ deployment dimension.
663
713
  """
664
714
  payload = {
715
+ "program_id": program_id,
665
716
  "litellm_model": "openai/gpt-4o",
666
- "program_signature_hash": program_signature_hash,
667
717
  "metadata": {"seed": "true"},
668
718
  }
669
719
  try:
@@ -677,16 +727,16 @@ def _create_deployment(endpoint: str, api_key: str, program_signature_hash: int)
677
727
  def _stamp_deployments(traces: list[dict], dep_for) -> None:
678
728
  """Stamp ~2/3 of the traces with the deployment for their own program (every
679
729
  third trace left unstamped for a visible deployed/undeployed mix), keying on
680
- each trace's program_signature_hash so a deployment's traces coherently share
681
- its program. `dep_for(hash) -> id | None` lazily creates + caches one
682
- deployment per program hash. No-op for traces without a hash."""
730
+ each trace's declared program_id so a deployment's traces coherently share
731
+ its program. `dep_for(program_id) -> id | None` lazily creates + caches one
732
+ deployment per program. No-op for traces that declare no program."""
683
733
  for i, trace in enumerate(traces):
684
734
  if i % 3 == 0 or not isinstance(trace, dict):
685
735
  continue
686
- h = trace.get("program_signature_hash")
687
- if h is None:
736
+ program_id = trace.get("program_id")
737
+ if not program_id:
688
738
  continue
689
- dep = dep_for(h)
739
+ dep = dep_for(program_id)
690
740
  if dep:
691
741
  trace["deployment_id"] = dep
692
742
 
@@ -703,13 +753,6 @@ _CSRF_INPUT_RE = re.compile(r'name="csrf_token"\s+value="([^"]+)"')
703
753
  _CSRF_META_RE = re.compile(r'name="csrf-token"\s+content="([^"]+)"')
704
754
 
705
755
 
706
- def _display_program_hash(signed_hash: int) -> str:
707
- """The 16-hex display form of a signed-int64 program_signature_hash — the
708
- shape the /ui/programs/{hash}/pin route parses (ids.FormatProgramHashInt64
709
- in serve: fmt.Sprintf("%016x", uint64(n)))."""
710
- return format(signed_hash & 0xFFFFFFFFFFFFFFFF, "016x")
711
-
712
-
713
756
  def _scrape_csrf(html: str) -> str | None:
714
757
  """Pull a CSRF token from a serve HTML page — the hidden `csrf_token` form
715
758
  input, or the `<meta name="csrf-token">` fallback (both carry the per-session
@@ -718,12 +761,12 @@ def _scrape_csrf(html: str) -> str | None:
718
761
  return m.group(1) if m else None
719
762
 
720
763
 
721
- def _pin_committee_program(endpoint: str, display_hash: str) -> None:
764
+ def _pin_committee_program(endpoint: str, program_id: str) -> None:
722
765
  """Pin the completed committee program for the demo admin (CMP-634).
723
766
 
724
- Pins are per-user web state: `program_pins` FKs the ingested `programs` row
725
- and serve's Pin() is reachable ONLY through the session+CSRF web route
726
- POST /ui/programs/{hash}/pin — the API key (bearer, no cookies) can't hit it.
767
+ Pins are per-user web state, and serve's Pin() is reachable ONLY through the
768
+ session+CSRF web route POST /ui/programs/{id}/pin, which takes the program's
769
+ p_ id — the API key (bearer, no cookies) can't hit it.
727
770
  So we drive the browser flow with a cookie jar: log in as the bootstrap admin,
728
771
  harvest a fresh CSRF token from the program page, then POST the pin. Wholly
729
772
  non-gating — any failure logs and returns, like the rest of the seed.
@@ -761,13 +804,13 @@ def _pin_committee_program(endpoint: str, display_hash: str) -> None:
761
804
  # single-use for this POST only.
762
805
  post("/auth/login", {"email": _ADMIN_EMAIL, "password": password, "csrf_token": login_csrf})
763
806
  # 3) Harvest a FRESH CSRF token from the (now authenticated) program page.
764
- pin_csrf = _scrape_csrf(get(f"/ui/programs/{display_hash}"))
807
+ pin_csrf = _scrape_csrf(get(f"/ui/programs/{program_id}"))
765
808
  if not pin_csrf:
766
809
  log.warning("pin: no CSRF token on program page (login may have failed); skipping pin")
767
810
  return
768
811
  # 4) POST the pin.
769
- status = post(f"/ui/programs/{display_hash}/pin", {"csrf_token": pin_csrf})
770
- log.info("pinned committee program %s (status %s)", display_hash, status)
812
+ status = post(f"/ui/programs/{program_id}/pin", {"csrf_token": pin_csrf})
813
+ log.info("pinned committee program %s (status %s)", program_id, status)
771
814
 
772
815
 
773
816
  def main() -> int:
@@ -778,19 +821,19 @@ def main() -> int:
778
821
  return 0
779
822
 
780
823
  try:
781
- # Lazily create one real deployment row per program hash the first time a
824
+ # Lazily create one real deployment row per program the first time a
782
825
  # trace of that program is stamped, tied to the program so the filter
783
826
  # menu + Deployments page resolve a human program-name label. The ingest
784
827
  # sanitizer strips any id without a backing row, so real rows are
785
828
  # required for the traces-index Deployment filter (CMP-277). Review apps
786
829
  # run CMPND_DEPLOY_BACKEND=lambda, so the create forwards through the
787
830
  # per-PR deploy stack; non-gating (cached None) if that's down.
788
- dep_cache: dict[int, str | None] = {}
831
+ dep_cache: dict[str, str | None] = {}
789
832
 
790
- def dep_for(program_signature_hash: int) -> str | None:
791
- if program_signature_hash not in dep_cache:
792
- dep_cache[program_signature_hash] = _create_deployment(endpoint, api_key, program_signature_hash)
793
- return dep_cache[program_signature_hash]
833
+ def dep_for(program_id: str) -> str | None:
834
+ if program_id not in dep_cache:
835
+ dep_cache[program_id] = _create_deployment(endpoint, api_key, program_id)
836
+ return dep_cache[program_id]
794
837
 
795
838
  parts = json.loads(gzip.decompress(_FIXTURE.read_bytes()))
796
839
 
@@ -807,11 +850,21 @@ def main() -> int:
807
850
  # record to it. It carries the shared `log_dir` so its interrupted twin
808
851
  # (step 3) reads as the same job.
809
852
  opt = next(p for p in parts if p["kind"] == "optimization")
853
+ # The captured run optimizes one program, whose root module class the run
854
+ # records as its program_name. Its runs and program traces declare it.
855
+ committee_class = opt["payload"].get("program_name") or "Predict"
856
+ committee_id = _ensure_program(endpoint, api_key, _PROGRAM_COMMITTEE)
810
857
  new_op = _reserve_optimization(endpoint, api_key)
811
858
  if not new_op:
812
859
  log.warning("could not reserve an optimization run id; aborting seed")
813
860
  return 0
814
- opt_payload = _transform(_with_log_dir(opt["payload"]), uuid_map, delta, opt_run=new_op, tags=_TAGS_COMPLETE)
861
+ opt_payload = _transform(
862
+ _declare_program(_with_log_dir(opt["payload"]), committee_id),
863
+ uuid_map,
864
+ delta,
865
+ opt_run=new_op,
866
+ tags=_TAGS_COMPLETE,
867
+ )
815
868
  _post(endpoint, f"/api/v1/optimizations/{new_op}", opt_payload, api_key, method="PUT")
816
869
  log.info("seeded optimization run %s", new_op)
817
870
 
@@ -819,7 +872,10 @@ def main() -> int:
819
872
  n_traces = n_spans = 0
820
873
  for p in parts:
821
874
  if p["kind"] == "traces_batch":
822
- payload = [_transform(item, uuid_map, delta, new_op, _TAGS_COMPLETE) for item in p["payload"]]
875
+ payload = [
876
+ _transform(item, uuid_map, delta, new_op, _TAGS_COMPLETE)
877
+ for item in _declare_program_traces(p["payload"], committee_class, committee_id)
878
+ ]
823
879
  _stamp_deployments(payload, dep_for)
824
880
  _post(endpoint, "/api/v1/traces/batch", payload, api_key)
825
881
  n_traces += len(payload)
@@ -837,11 +893,10 @@ def main() -> int:
837
893
  # login+web-route flow (the only path to a per-user pin) can never break
838
894
  # the seed.
839
895
  try:
840
- committee_hash = opt["payload"].get("program_signature_hash")
841
- if committee_hash is not None:
842
- _pin_committee_program(endpoint, _display_program_hash(int(committee_hash)))
896
+ if committee_id is not None:
897
+ _pin_committee_program(endpoint, committee_id)
843
898
  else:
844
- log.warning("pin: optimization payload has no program_signature_hash; skipping pin")
899
+ log.warning("pin: the committee program has no id; skipping pin")
845
900
  except Exception as exc: # noqa: BLE001 - non-gating, like the rest of the seed
846
901
  log.warning("pin committee program failed (non-gating): %s", exc)
847
902
 
@@ -855,7 +910,11 @@ def main() -> int:
855
910
  running_op = _reserve_optimization(endpoint, api_key)
856
911
  if running_op:
857
912
  interrupted = _transform(
858
- _interrupted_variant(opt["payload"]), umap, delta, opt_run=running_op, tags=_TAGS_RUNNING
913
+ _declare_program(_interrupted_variant(opt["payload"]), committee_id),
914
+ umap,
915
+ delta,
916
+ opt_run=running_op,
917
+ tags=_TAGS_RUNNING,
859
918
  )
860
919
  code, _ = _post(endpoint, f"/api/v1/optimizations/{running_op}", interrupted, api_key, method="PUT")
861
920
  log.info("seeded running (interrupted) optimization run %s", running_op)
@@ -863,7 +922,11 @@ def main() -> int:
863
922
  n_it = 0
864
923
  for p in parts:
865
924
  if p["kind"] == "traces_batch":
866
- kept = [t for t in p["payload"] if t.get("trace_id") in keep]
925
+ kept = [
926
+ t
927
+ for t in _declare_program_traces(p["payload"], committee_class, committee_id)
928
+ if t.get("trace_id") in keep
929
+ ]
867
930
  if kept:
868
931
  payload = [_transform(item, umap, delta, running_op, _TAGS_RUNNING) for item in kept]
869
932
  _post(endpoint, "/api/v1/traces/batch", payload, api_key)
@@ -900,7 +963,9 @@ def main() -> int:
900
963
  umap = {u: str(uuid.uuid4()) for u in acc}
901
964
  rid = _reserve_optimization(endpoint, api_key)
902
965
  if rid:
903
- failed = _lifecycle_variant(_transform(opt["payload"], umap, delta, rid, _TAGS_FAILED), "failed")
966
+ failed = _lifecycle_variant(
967
+ _transform(_declare_program(opt["payload"], committee_id), umap, delta, rid, _TAGS_FAILED), "failed"
968
+ )
904
969
  code, _ = _post(endpoint, f"/api/v1/optimizations/{rid}", failed, api_key, method="PUT")
905
970
  log.info("seeded failed optimization run %s (status %s)", rid, code)
906
971
  else:
@@ -909,7 +974,8 @@ def main() -> int:
909
974
  # 4) Plant a synthetic RLM (predict-with-REPL) trace so the notebook
910
975
  # view (CMP-372) has trajectory data to render — the captured
911
976
  # optimization run has none. Unlinked to any optimization run.
912
- notebook_batch = [_notebook_trace(now)]
977
+ district_counter_id = _ensure_program(endpoint, api_key, _PROGRAM_DISTRICT_COUNTER)
978
+ notebook_batch = [_declare_program(_notebook_trace(now), district_counter_id)]
913
979
  _stamp_deployments(notebook_batch, dep_for)
914
980
  code, _ = _post(endpoint, "/api/v1/traces/batch", notebook_batch, api_key)
915
981
  log.info("seeded RLM notebook trace (status %s)", code)
@@ -919,7 +985,12 @@ def main() -> int:
919
985
  # autofill next to the completed run above. No end_time, partial trajectory,
920
986
  # status in_progress → serve renders the "running" notebook. Left unstamped
921
987
  # (a live run predates any deployment link).
922
- code, _ = _post(endpoint, "/api/v1/traces/batch", [_notebook_trace_in_progress(now)], api_key)
988
+ code, _ = _post(
989
+ endpoint,
990
+ "/api/v1/traces/batch",
991
+ [_declare_program(_notebook_trace_in_progress(now), district_counter_id)],
992
+ api_key,
993
+ )
923
994
  log.info("seeded in-progress RLM notebook trace (status %s)", code)
924
995
 
925
996
  # 4b) Plant the two CMP-187 tool-span demo traces so every review app
@@ -930,26 +1001,32 @@ def main() -> int:
930
1001
  tool_demos: list[dict] = []
931
1002
  if _SOURCE_CATEGORIZER_FIXTURE.exists():
932
1003
  tool_demos.append(
933
- _replay_flat_fixture(
934
- _SOURCE_CATEGORIZER_FIXTURE,
935
- now,
936
- "RLM",
937
- _SEED_TAGS + ["tool-demo"],
938
- "vector_pdf source_table/glenn-pdf-p15.pdf (page 1)",
939
- "candidate_columns, precinct",
1004
+ _declare_program(
1005
+ _replay_flat_fixture(
1006
+ _SOURCE_CATEGORIZER_FIXTURE,
1007
+ now,
1008
+ "RLM",
1009
+ _SEED_TAGS + ["tool-demo"],
1010
+ "vector_pdf source_table/glenn-pdf-p15.pdf (page 1)",
1011
+ "candidate_columns, precinct",
1012
+ ),
1013
+ _ensure_program(endpoint, api_key, _PROGRAM_SOURCE_CATEGORIZER),
940
1014
  )
941
1015
  )
942
1016
  else:
943
1017
  log.warning("skipping SourceCategorizer seed: fixture missing at %s", _SOURCE_CATEGORIZER_FIXTURE)
944
1018
  if _TOOL_KIND_DEMO_FIXTURE.exists():
945
1019
  tool_demos.append(
946
- _replay_flat_fixture(
947
- _TOOL_KIND_DEMO_FIXTURE,
948
- now,
949
- "ToolKindDemo",
950
- _SEED_TAGS + ["tool-demo"],
951
- "espresso place review (function-tool + module-tool)",
952
- "The speaker loved the new downtown espresso place and its incredible pastries.",
1020
+ _declare_program(
1021
+ _replay_flat_fixture(
1022
+ _TOOL_KIND_DEMO_FIXTURE,
1023
+ now,
1024
+ "ToolKindDemo",
1025
+ _SEED_TAGS + ["tool-demo"],
1026
+ "espresso place review (function-tool + module-tool)",
1027
+ "The speaker loved the new downtown espresso place and its incredible pastries.",
1028
+ ),
1029
+ _ensure_program(endpoint, api_key, _PROGRAM_TOOL_KIND_DEMO),
953
1030
  )
954
1031
  )
955
1032
  else:
@@ -959,13 +1036,16 @@ def main() -> int:
959
1036
  # the committee — the tool-span ReAct exemplar the golden-path demo lacked.
960
1037
  if _REACT_COMMITTEE_FIXTURE.exists():
961
1038
  tool_demos.append(
962
- _replay_flat_fixture(
963
- _REACT_COMMITTEE_FIXTURE,
964
- now,
965
- "ReAct",
966
- _SEED_TAGS + [_GOLDEN_PATH, "react-demo"],
967
- "committee fundraising email (ReAct + lookup_disclaimer tool)",
968
- "NATIONAL REPUBLICAN SENATORIAL COMMITTEE",
1039
+ _declare_program(
1040
+ _replay_flat_fixture(
1041
+ _REACT_COMMITTEE_FIXTURE,
1042
+ now,
1043
+ "ReAct",
1044
+ _SEED_TAGS + [_GOLDEN_PATH, "react-demo"],
1045
+ "committee fundraising email (ReAct + lookup_disclaimer tool)",
1046
+ "NATIONAL REPUBLICAN SENATORIAL COMMITTEE",
1047
+ ),
1048
+ _ensure_program(endpoint, api_key, _PROGRAM_REACT_COMMITTEE),
969
1049
  )
970
1050
  )
971
1051
  else:
@@ -29,18 +29,13 @@ def _ns(**kw: object) -> argparse.Namespace:
29
29
 
30
30
  class TestAssessmentsCliE2E:
31
31
  def test_create_list_show_against_live_api(self, server_client, trace_capture):
32
- # Produce a real trace to assess under a minted program, then read back its
33
- # program hash + a span.
32
+ # Produce a real trace to assess under a minted program, then read back a span.
34
33
  with declaring_program("test_assessments_cli") as program_id:
35
34
  dspy.Predict("question -> answer")(question="Who represents District 4?")
36
35
  flush_and_wait()
37
36
  trace_id = trace_capture.last_trace_id
38
37
  assert trace_id is not None, "no trace captured"
39
38
  data = server_client.get_trace(trace_id)
40
- # The trace API returns the hash as a signed int64 (decimal); the
41
- # assessments and modules APIs speak the 16-hex display form. Normalize
42
- # so the create/list calls and the assertion compare like for like.
43
- program_hash = format(int(data["trace"]["program_signature_hash"]) & 0xFFFFFFFFFFFFFFFF, "016x")
44
39
  assert data["spans"], "trace has no spans to annotate"
45
40
  span_id = data["spans"][0]["span_id"]
46
41
 
@@ -62,9 +57,9 @@ class TestAssessmentsCliE2E:
62
57
  )
63
58
  assert created["id"].startswith("as_")
64
59
  assert created["conclusion"] == "down"
65
- # The program hash the server copied off the trace round-trips as the same
66
- # 16-hex string the trace read returned — the CLI passes it through verbatim.
67
- assert created["program_signature_hash"] == program_hash
60
+ # The assessment names the trace's program by its id, never by a signature hash.
61
+ assert created["program_id"] == program_id
62
+ assert "program_signature_hash" not in created
68
63
  anns = {(a["kind"], a["ref"]): a for a in created["annotations"]}
69
64
  assert ("span", span_id) in anns
70
65
  assert ("output", "/answer") in anns
@@ -219,7 +219,6 @@ class TestDeployBadImportRejected:
219
219
  create_resp = client.post(
220
220
  f"{config.endpoint}/api/v1/deployments",
221
221
  json={
222
- "program_signature_hash": 0,
223
222
  "declared_content_length": len(zip_bytes),
224
223
  "program_id": program_id_for(f"test_deploy_programs-{uuid.uuid4().hex[:8]}"),
225
224
  # A worker that spends egress tokens needs the model a token names, so