consequa-x 1.2.0__tar.gz → 2.0.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 (213) hide show
  1. {consequa_x-1.2.0 → consequa_x-2.0.0}/CHANGELOG.md +118 -0
  2. {consequa_x-1.2.0 → consequa_x-2.0.0}/MANIFEST.in +2 -0
  3. {consequa_x-1.2.0/src/consequa_x.egg-info → consequa_x-2.0.0}/PKG-INFO +82 -72
  4. {consequa_x-1.2.0 → consequa_x-2.0.0}/README.md +81 -71
  5. consequa_x-2.0.0/docs/application-integration.md +130 -0
  6. consequa_x-2.0.0/docs/design-2.0.md +109 -0
  7. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/design.md +2 -2
  8. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/framework-integration.md +45 -30
  9. consequa_x-2.0.0/docs/migrating-to-2.0.md +100 -0
  10. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/offline-reports.md +16 -5
  11. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/product-roadmap.md +55 -54
  12. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/quickstart.md +15 -13
  13. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/release-notes.md +28 -0
  14. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/research-roadmap.md +5 -4
  15. consequa_x-2.0.0/docs/stable-api.md +110 -0
  16. {consequa_x-1.2.0 → consequa_x-2.0.0}/examples/agent_loop_sqlite.py +8 -8
  17. {consequa_x-1.2.0 → consequa_x-2.0.0}/examples/artifact_delivery.py +33 -33
  18. {consequa_x-1.2.0 → consequa_x-2.0.0}/pyproject.toml +1 -1
  19. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/analyze_matched_local_study.py +7 -7
  20. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/audit_interaction_sampling.py +10 -10
  21. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/audit_refund_interactions.py +1 -1
  22. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/audit_release_artifacts.py +3 -2
  23. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/build_public_docs.py +6 -6
  24. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/build_site_demo.py +10 -9
  25. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/check_installed_release.py +173 -77
  26. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/export_render_site.py +1 -1
  27. consequa_x-2.0.0/scripts/refresh_sample_report.py +61 -0
  28. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/run_matched_trace_judge.py +1 -1
  29. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/run_tau_benchmark_validation.py +18 -18
  30. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/run_upstream_car_study.py +5 -3
  31. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/summarize_matched_execution.py +2 -2
  32. consequa_x-2.0.0/src/consequa_x/__init__.py +54 -0
  33. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/attribution.py +3 -1
  34. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/decision_replay.py +6 -5
  35. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/evaluation.py +8 -8
  36. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/interaction_sampling.py +1 -1
  37. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/payments.py +5 -4
  38. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/policy_experiment.py +5 -5
  39. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/policy_runner.py +17 -15
  40. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/runner.py +25 -20
  41. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/targets.py +1 -1
  42. consequa_x-2.0.0/src/consequa_x/capture.py +547 -0
  43. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/cli.py +209 -6
  44. consequa_x-2.0.0/src/consequa_x/compare.py +106 -0
  45. consequa_x-2.0.0/src/consequa_x/errors.py +35 -0
  46. consequa_x-2.0.0/src/consequa_x/explanation.py +222 -0
  47. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/graph.py +26 -15
  48. consequa_x-2.0.0/src/consequa_x/guard.py +79 -0
  49. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/mcp_capture.py +10 -6
  50. consequa_x-2.0.0/src/consequa_x/migrate.py +180 -0
  51. consequa_x-2.0.0/src/consequa_x/models.py +733 -0
  52. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/policies.py +6 -12
  53. consequa_x-2.0.0/src/consequa_x/readback.py +102 -0
  54. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/record_verifier.py +61 -61
  55. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/report.py +123 -50
  56. consequa_x-2.0.0/src/consequa_x/research.py +22 -0
  57. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/schema.py +8 -5
  58. consequa_x-1.2.0/src/consequa_x/schemas/trace-v0.1.json → consequa_x-2.0.0/src/consequa_x/schemas/trace-v1.0.json +617 -543
  59. consequa_x-2.0.0/src/consequa_x/store.py +57 -0
  60. consequa_x-2.0.0/src/consequa_x/summary.py +142 -0
  61. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/verification.py +95 -18
  62. {consequa_x-1.2.0 → consequa_x-2.0.0/src/consequa_x.egg-info}/PKG-INFO +82 -72
  63. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x.egg-info/SOURCES.txt +27 -2
  64. consequa_x-2.0.0/tests/fixtures/v0.1/README.md +13 -0
  65. consequa_x-2.0.0/tests/fixtures/v0.1/record-timeout-after-commit.json +234 -0
  66. consequa_x-2.0.0/tests/fixtures/v0.1/refund-another-actor.json +267 -0
  67. consequa_x-2.0.0/tests/fixtures/v0.1/refund-stale-readback.json +273 -0
  68. consequa_x-2.0.0/tests/fixtures/v0.1/refund-timeout-after-commit.explanation.json +455 -0
  69. consequa_x-2.0.0/tests/fixtures/v0.1/refund-timeout-after-commit.json +273 -0
  70. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_agent_loop_example.py +1 -1
  71. consequa_x-2.0.0/tests/test_async_capture.py +98 -0
  72. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_calibration.py +1 -1
  73. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_calibration_mechanisms.py +1 -1
  74. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_capture.py +49 -35
  75. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_cli.py +1 -1
  76. consequa_x-2.0.0/tests/test_compare_verify_redact.py +95 -0
  77. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_decision_coalition.py +6 -3
  78. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_decision_contrastive.py +3 -2
  79. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_decision_replay.py +7 -5
  80. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_decision_shapley.py +2 -1
  81. consequa_x-2.0.0/tests/test_docs_examples.py +20 -0
  82. consequa_x-2.0.0/tests/test_errors.py +87 -0
  83. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_graph.py +13 -9
  84. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_graph_identity.py +6 -6
  85. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_installed_release_check.py +7 -0
  86. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_integration.py +28 -28
  87. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_interval_replay.py +3 -3
  88. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_matched_baseline_audit.py +4 -4
  89. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_matched_local_study.py +6 -6
  90. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_matched_trace_judge.py +8 -7
  91. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_mcp_capture.py +1 -1
  92. consequa_x-2.0.0/tests/test_migrate.py +118 -0
  93. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_model_eval_integration.py +4 -4
  94. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_models.py +90 -50
  95. consequa_x-2.0.0/tests/test_multi_action.py +165 -0
  96. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_ollama.py +2 -2
  97. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_payments.py +5 -4
  98. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_policies.py +4 -4
  99. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_policy_experiment.py +3 -3
  100. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_policy_experiment_integration.py +5 -5
  101. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_policy_runner.py +11 -10
  102. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_public_integration.py +76 -88
  103. consequa_x-2.0.0/tests/test_readback_guard.py +96 -0
  104. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_record_verifier.py +46 -29
  105. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_refund_interactions.py +1 -1
  106. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_release_inventory.py +1 -1
  107. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_replay_targets.py +4 -3
  108. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_report.py +45 -41
  109. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_report_cli.py +3 -3
  110. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_research_integration.py +1 -1
  111. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_schema.py +14 -13
  112. consequa_x-2.0.0/tests/test_summary_store.py +105 -0
  113. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_task_templates.py +1 -1
  114. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_tau_benchmark_validation.py +5 -5
  115. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_verification.py +7 -4
  116. consequa_x-1.2.0/docs/application-integration.md +0 -145
  117. consequa_x-1.2.0/docs/stable-api.md +0 -100
  118. consequa_x-1.2.0/src/consequa_x/__init__.py +0 -28
  119. consequa_x-1.2.0/src/consequa_x/capture.py +0 -376
  120. consequa_x-1.2.0/src/consequa_x/explanation.py +0 -216
  121. consequa_x-1.2.0/src/consequa_x/models.py +0 -532
  122. {consequa_x-1.2.0 → consequa_x-2.0.0}/CONTRIBUTING.md +0 -0
  123. {consequa_x-1.2.0 → consequa_x-2.0.0}/LICENSE +0 -0
  124. {consequa_x-1.2.0 → consequa_x-2.0.0}/SECURITY.md +0 -0
  125. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/attribution-calibration.md +0 -0
  126. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/build-contract.md +0 -0
  127. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/car-reproduction-plan.md +0 -0
  128. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/completion-plan.md +0 -0
  129. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/decision-contrastive.md +0 -0
  130. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/decision-replay.md +0 -0
  131. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/decision-shapley.md +0 -0
  132. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/graph-witnesses.md +0 -0
  133. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/interaction-sampling.md +0 -0
  134. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/interval-followup-plan.md +0 -0
  135. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/launch-readiness.md +0 -0
  136. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/local-pypi-setup.md +0 -0
  137. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/matched-trace-judge.md +0 -0
  138. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/policy-evaluation.md +0 -0
  139. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/policy-replay.md +0 -0
  140. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/refund-interactions.md +0 -0
  141. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/release-readiness.md +0 -0
  142. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/studies/matched-local-2026-10-18.json +0 -0
  143. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/studies/refund-family-candidates-v1.json +0 -0
  144. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/study-manifests.md +0 -0
  145. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/study-plan.md +0 -0
  146. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/tau-benchmark-validation.md +0 -0
  147. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/upstream-car-comparison.md +0 -0
  148. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/v1.1-release.md +0 -0
  149. {consequa_x-1.2.0 → consequa_x-2.0.0}/docs/variable-length-game-plan.md +0 -0
  150. {consequa_x-1.2.0 → consequa_x-2.0.0}/examples/decision_shapley_fixture.py +0 -0
  151. {consequa_x-1.2.0 → consequa_x-2.0.0}/examples/report_quickstart.py +0 -0
  152. {consequa_x-1.2.0 → consequa_x-2.0.0}/requirements-dev.lock +0 -0
  153. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/audit_interval_comparison.py +0 -0
  154. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/audit_review_revision.py +0 -0
  155. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/build_matched_study_figures.py +0 -0
  156. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/configure_pypi_auth.py +0 -0
  157. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/draw_paper_diagrams.py +0 -0
  158. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/finalize_interval_artifacts.py +0 -0
  159. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/finalize_matched_local_study.py +0 -0
  160. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/finalize_refund_artifacts.py +0 -0
  161. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/plot_interval_comparison.py +0 -0
  162. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/plot_paper_results.py +0 -0
  163. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/publish_alpha.py +0 -0
  164. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/publish_release.py +0 -0
  165. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/run_interaction_sampling.py +0 -0
  166. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/run_matched_local_study.py +0 -0
  167. {consequa_x-1.2.0 → consequa_x-2.0.0}/scripts/summarize_interaction_sampling.py +0 -0
  168. {consequa_x-1.2.0 → consequa_x-2.0.0}/setup.cfg +0 -0
  169. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/__main__.py +0 -0
  170. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/adapters/__init__.py +0 -0
  171. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/adapters/ollama.py +0 -0
  172. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/__init__.py +0 -0
  173. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/assessment_metrics.py +0 -0
  174. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/calibration.py +0 -0
  175. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/calibration_export.py +0 -0
  176. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/calibration_mechanisms.py +0 -0
  177. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/decision_coalition.py +0 -0
  178. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/decision_contrastive.py +0 -0
  179. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/decision_shapley.py +0 -0
  180. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/interaction_export.py +0 -0
  181. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/interaction_mechanisms.py +0 -0
  182. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/interval_comparison.py +0 -0
  183. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/intervals.py +0 -0
  184. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/study.py +0 -0
  185. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/benchmark/templates.py +0 -0
  186. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x/py.typed +0 -0
  187. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x.egg-info/dependency_links.txt +0 -0
  188. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x.egg-info/entry_points.txt +0 -0
  189. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x.egg-info/requires.txt +0 -0
  190. {consequa_x-1.2.0 → consequa_x-2.0.0}/src/consequa_x.egg-info/top_level.txt +0 -0
  191. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_assessment_metrics.py +0 -0
  192. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_attribution.py +0 -0
  193. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_calibration_cli.py +0 -0
  194. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_decision_contrastive_cli.py +0 -0
  195. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_decision_shapley_cli.py +0 -0
  196. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_interaction_sampling.py +0 -0
  197. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_interaction_sampling_cli.py +0 -0
  198. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_interaction_workers.py +0 -0
  199. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_interval_comparison.py +0 -0
  200. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_intervals.py +0 -0
  201. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_matched_execution_summary.py +0 -0
  202. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_matched_figure_builder.py +0 -0
  203. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_matched_study_finalizer.py +0 -0
  204. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_policy_evaluation.py +0 -0
  205. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_prompt_profile_cli.py +0 -0
  206. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_refund_interactions_cli.py +0 -0
  207. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_release_helpers.py +0 -0
  208. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_render_site_export.py +0 -0
  209. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_site_demo.py +0 -0
  210. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_snapshot_integrity.py +0 -0
  211. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_stable_release.py +0 -0
  212. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_study.py +0 -0
  213. {consequa_x-1.2.0 → consequa_x-2.0.0}/tests/test_upstream_car_study.py +0 -0
@@ -1,5 +1,118 @@
1
1
  # Changes
2
2
 
3
+ ## 2.0.0 - 19 October 2026 (breaking)
4
+
5
+ See [the 2.0 design](docs/design-2.0.md) and the
6
+ [migration guide](docs/migrating-to-2.0.md). Deferred to 2.1: verifier
7
+ composition, optional framework integrations and OpenTelemetry export.
8
+
9
+ - Typed exceptions: every intentional error derives from `ConsequaError` and
10
+ still from `ValueError` or `TypeError`. `ValidationError` (invalid records,
11
+ traces and settings; malformed JSON is now one too), `SchemaVersionError`,
12
+ `ConsequaTypeError`, `ContractError` (a verifier cannot evaluate the
13
+ contract), `ReceiptError` (a verifier or matcher result crosses its identity
14
+ boundary, or recorded evidence cannot support the interpretation) and
15
+ `RecorderError`. `InsufficientEvidenceError` and `PolicyError` join the
16
+ hierarchy.
17
+ - Captured payloads are deeply read-only. Event data, arguments, contract
18
+ expectations, results, readback records, scopes and evidence details are
19
+ `FrozenDict` and `FrozenList` values: they compare equal to and serialize
20
+ like ordinary dicts and lists, but any change raises `ConsequaTypeError`.
21
+ `copy.deepcopy` returns the same read-only value; `thaw()` returns an
22
+ editable copy, and `to_dict()` still exports ordinary dicts and lists.
23
+ - Trace schema `1.0`. A trace holds one or more `actions`, each with its own
24
+ contract; `responses`, `assessments` (zero or more per action) and
25
+ `verifications` each name their `action_id`. A required `time_base`
26
+ (`unix_ms` or `relative_ms`) says what the integer millisecond times count
27
+ from. `trace_id` is no longer repeated on actions and events. All records
28
+ are keyword-only. Schema `0.1` input is refused with a `SchemaVersionError`
29
+ that names the migration route; the packaged schema is `trace-v1.0.json`.
30
+ New helpers: `Trace.get_action`, `single_action`, `responses_for`,
31
+ `assessments_for`, `verifications_for`, `latest_assessment`,
32
+ `latest_verification`.
33
+ - `TraceRecorder.register` can be called for several actions and returns an
34
+ `ActionCapture` whose `attempt`, `call`, `dispatch`, `response`,
35
+ `assessment` and `verify` record for that action. The recorder-level methods
36
+ still work while exactly one action is registered. Assessments are optional
37
+ and may be repeated. The default clock is wall-clock `unix_ms`; a custom
38
+ clock records `relative_ms` unless told otherwise. `capture_tool` and
39
+ `call_mcp_tool` accept an `ActionCapture`.
40
+ - `explain()` returns a `TraceExplanation` with one `ActionExplanation` per
41
+ action (`for_action`, `single_action`). An action without verification is
42
+ reported as `NO_VERIFICATION` instead of raising. The evidence graph has a
43
+ contract node per action, and offline reports show every action, with an
44
+ Action column for responses and verifications and the time base in the
45
+ footer.
46
+ - Verifiers own domain interpretation. `VerificationResult` gains
47
+ `target_effect_ids` (`None` when the verifier did no target matching,
48
+ otherwise this action's effects that match the full target) and `findings`
49
+ (`Finding(code, effect_ids, detail)`). `RefundVerifier` records refund
50
+ matches and the `EXCESS_EFFECTS`, `DUPLICATE_EFFECT`, `PARAMETER_MISMATCH`,
51
+ `PARTIAL_EFFECT` and `REVERSED_EFFECT` findings; `RecordVerifier` records
52
+ record matches and `EXCESS_EFFECTS`, `DUPLICATE_EFFECT`,
53
+ `PARAMETER_MISMATCH` and `REMOVED_EFFECT`. `TargetMatcher`,
54
+ `RecordTargetMatcher`, `RecordVerifier.target_matcher()` and
55
+ `explain(..., target_matcher=...)` are removed; a custom verifier sets
56
+ `target_effect_ids` instead.
57
+ - `explain()` is domain-independent: it reads receipts, responses and
58
+ assessments and never reinterprets evidence details. Diagnoses are
59
+ structured `Diagnosis(code, evidence, message)` records; `codes` gives the
60
+ codes and `diagnosis` a display label. `UNASSESSABLE_ASSESSMENT` now applies
61
+ to every predicate whenever the outcome is known but alignment cannot be
62
+ judged (1.x omitted it for refunds). `CORE_DIAGNOSES` lists the codes
63
+ `explain()` produces.
64
+ - Offline reports show each action's assessment alignment and diagnosis
65
+ codes, computed from the recorded receipts. Codes from custom verifiers are
66
+ counted rather than printed.
67
+ - Migration: `migrate_trace()` and `consequa-x migrate OLD --output NEW`
68
+ convert schema `0.1` traces. Target matches and findings are added only when
69
+ the 2.0 verifier (the refund verifier by default; pass others with
70
+ `verifiers=`) reproduces the recorded receipt exactly; no recorded conclusion
71
+ is changed. `consequa-x validate` checks trace files. Migrated traces of the
72
+ 1.x demo reproduce their 1.x explanations; fixtures written by the released
73
+ 1.2.0 package are in `tests/fixtures/v0.1`.
74
+ - `summarize(traces)` and `consequa-x summarize PATH... [--format text|markdown|json]`
75
+ count verified state, origin, alignment, tool responses and diagnoses over
76
+ every action, with stated denominators; `unknown` and `unverified` stay
77
+ separate from failures, and actions with divergences or violations are
78
+ listed. Folders (non-trace JSON is skipped and counted) and `.jsonl` stores
79
+ are accepted; invalid inputs are listed by path and error type only and make
80
+ the command exit 2.
81
+ - `poll_readback(capture, read, until_ms=, interval_ms=)` waits until the
82
+ contract horizon, then reads back and verifies repeatedly (every observation
83
+ and receipt is recorded) until the outcome is `satisfied` or `violated` or the
84
+ time limit passes; an unresolved `unknown` is returned as is.
85
+ - Async agents: `capture_tool` wraps `async def` tools, `ActionCapture.acall`
86
+ (and `TraceRecorder.acall`) awaits a tool as one recorded attempt,
87
+ `averify`/`ActionCapture.averify` accept verifiers whose `verify` is a
88
+ coroutine (receipts are checked exactly as `verify` checks them), and
89
+ `apoll_readback` polls with awaited reads and `asyncio.sleep`.
90
+ - `retry_advice(receipt or trace)` turns verified evidence into
91
+ `DO_NOT_RETRY` (the outcome already holds, whoever produced it), `WAIT`
92
+ (missing or unknown readback), `ESCALATE` (relevant effects that do not
93
+ satisfy the contract) or `RETRY_PERMITTED` (a complete readback shows no
94
+ relevant effect; retry with an idempotency key, since an in-flight attempt can
95
+ still commit), each with a reason.
96
+ - `compare(left, right)` and `consequa-x compare A B [--fail-on-difference]`
97
+ compare two traces action by action: contract, tool responses, latest
98
+ assessment, verified state and origin, alignment and diagnosis codes, plus
99
+ actions present in only one trace.
100
+ - `consequa-x verify TRACE --readback OBS.json [--scope-field F] --output NEW`
101
+ verifies a captured readback file with the built-in refund or record
102
+ verifier and saves the trace with the new receipt, without Python code.
103
+ - `render_report(..., include_details=True, redact=("card_number", ...))` and
104
+ `consequa-x report --include-details --redact KEY` replace the values of the
105
+ named JSON keys everywhere in the included trace. Values under other keys are
106
+ kept; this is omission by name, not anonymization.
107
+ - `TraceStore(path)`: an append-only JSON Lines file of validated traces with
108
+ unique trace IDs; reading reports the line number of an invalid entry.
109
+ - Research interfaces (`EffectEstimate`, `Intervention`, `leave_one_out`,
110
+ `recency_attribution`, the policy types) are imported from
111
+ `consequa_x.research` instead of the top-level namespace.
112
+ - Research code (benchmark, replay, policies, studies and their audit
113
+ scripts) produces and reads schema `1.0` traces. Retained 1.x study outputs
114
+ under `output/` are unchanged.
115
+
3
116
  ## 1.2.0 - 19 October 2026
4
117
 
5
118
  Additive only: the 1.x API and trace schema `0.1` are unchanged.
@@ -33,6 +146,11 @@ Additive only: the 1.x API and trace schema `0.1` are unchanged.
33
146
  - The website's workflow cards, documentation and quickstart show the capture
34
147
  API. `scripts/check_installed_release.py` now also exercises the recorder,
35
148
  tool capture and record verifier from the installed wheel.
149
+ - Published to PyPI with registry hashes verified; see the
150
+ [release record](output/release-1.2.0-2026-10-19/README.md). Installed checks
151
+ passed on Windows Python 3.11.9 and 3.12.10, on one Alpine Linux 3.24.2 QEMU
152
+ guest with Python 3.14.8 and from a fresh PyPI install. macOS has never been
153
+ tested.
36
154
 
37
155
  ## 1.1.1 - 18 October 2026
38
156
 
@@ -5,3 +5,5 @@ recursive-include docs/studies *.json
5
5
  recursive-include tests *.py
6
6
  recursive-include examples *.py
7
7
  recursive-include scripts *.py
8
+ recursive-include tests/fixtures *.json
9
+ recursive-include tests/fixtures *.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: consequa-x
3
- Version: 1.2.0
3
+ Version: 2.0.0
4
4
  Summary: Evidence-aware explanation of consequential agent actions
5
5
  Author: Devrajsinh Jhala
6
6
  License-Expression: MIT
@@ -76,98 +76,103 @@ evaluation only; never pass it to an explainer.
76
76
 
77
77
  ## Use it in Python
78
78
 
79
- Register the intended outcome before the action, capture what happened, read the
80
- outcome back from a trusted read-only source, then verify and explain:
79
+ Register each consequential action with its intended outcome before running it,
80
+ let the recorder capture what happens, read the outcome back from a trusted
81
+ read-only source, then verify and explain:
81
82
 
82
83
  ```python
83
84
  from consequa_x import (
84
- Action, AgentAssessment, AssessmentStatus, Observation, OutcomeContract,
85
- Trace, explain, render_report, verify,
85
+ Observation, OutcomeContract, RecordVerifier, TraceRecorder, explain, render_report,
86
86
  )
87
87
 
88
- expected = {"payment_id": "p1", "amount_minor": 1000, "currency": "INR"}
89
- contract = OutcomeContract("contract-1", "refund_processed", expected,
90
- deadline_ms=50, stability_ms=20)
91
- action = Action("refund-1", "trace-1", "agent", "refund", expected, 0, contract)
92
-
93
- # In an application, capture this independently from a trusted read-only source.
94
- observation = Observation(
95
- "readback-1", "independent-ledger", 100, 100, {"payment_id": "p1"},
96
- ({**expected, "effect_id": "effect-1", "committed_at_ms": 25,
97
- "status": "processed", "action_id": "refund-1", "actor": "agent"},),
98
- )
99
- receipt = verify(action, observation)
100
- trace = Trace("trace-1", action, (), (),
101
- AgentAssessment(AssessmentStatus.FAILURE, 10),
102
- (observation,), (receipt,))
103
-
104
- assert receipt.status.value == "satisfied" # the refund really happened
105
- assert receipt.origin.value == "confirmed" # and this action produced it
106
- assert explain(trace).assessment_alignment == "contradicted" # the agent said failure
107
- html = render_report(trace) # offline, minimal disclosure
108
- ```
109
-
110
- The built-in `refund_processed` verifier checks payment, integer amount,
111
- currency, exact count, commit deadline and a persistence window. Unavailable,
112
- incomplete, wrongly scoped or stale readback produces `unknown`. Effect origin is
113
- a separate conclusion: `confirmed`, `other_actor` or `unknown`.
114
-
115
- Traces round-trip through a portable, versioned JSON schema (`0.1`):
116
- `Trace.from_json(trace.to_json())` rejects unknown fields, broken identity links
117
- and dependency cycles. `consequa_x.schema.get_trace_schema()` returns the bundled
118
- JSON Schema.
119
-
120
- ## Capture a real agent's tool call
121
-
122
- `TraceRecorder` records an action while your agent runs, and `RecordVerifier`
123
- checks any outcome that leaves records behind (rows, orders, tickets, files):
124
-
125
- ```python
126
- from consequa_x import OutcomeContract, RecordVerifier, TraceRecorder, explain
127
-
128
- contract = OutcomeContract("refund-o-17", "record_present",
129
- {"order_id": "o-17", "amount_minor": 5000, "currency": "INR"},
130
- deadline_ms=200, stability_ms=50)
131
88
  verifier = RecordVerifier(scope_fields=("order_id",))
89
+ rec = TraceRecorder("run-17", actor="support-agent", time_base="relative_ms")
90
+ refund = rec.register(
91
+ "refund-o-17", "issue_refund", {"order_id": "o-17"}, at_ms=0,
92
+ contract=OutcomeContract(contract_id="refund-o-17", predicate="record_present",
93
+ expected={"order_id": "o-17", "amount_minor": 5000},
94
+ deadline_ms=200, stability_ms=50))
132
95
 
133
- rec = TraceRecorder("run-17", actor="support-agent")
134
- rec.register("refund-o-17", "issue_refund", {"order_id": "o-17"}, contract=contract)
135
96
  try:
136
- with rec.attempt(data={"tool": "issue_refund"}) as attempt:
137
- issue_refund("o-17", attempt_id=attempt.attempt_id) # your tool
138
- rec.assessment("success") # what the agent reported
97
+ with refund.attempt(at_ms=5) as attempt: # your tool call goes here
98
+ attempt.at_ms = 30
99
+ raise TimeoutError # ...and it timed out
139
100
  except TimeoutError:
140
- rec.assessment("failure")
141
- rec.verify(read_refunds_for("o-17"), verifier=verifier) # your read-only readback
142
- print(explain(rec.trace(), target_matcher=verifier.target_matcher()).diagnoses)
101
+ refund.assessment("failure", at_ms=31) # what the agent reported
102
+
103
+ # Captured independently, after the horizon, from a read-only replica of the orders table.
104
+ readback = Observation(
105
+ observation_id="read-1", source="orders-replica", observed_at_ms=300, as_of_ms=300,
106
+ scope={"order_id": "o-17"},
107
+ records=({"effect_id": "rf-1", "order_id": "o-17", "amount_minor": 5000,
108
+ "committed_at_ms": 20, "action_id": "refund-o-17",
109
+ "attempt_id": "refund-o-17:attempt-1", "actor": "support-agent"},))
110
+ receipt = refund.verify(readback, verifier=verifier)
111
+
112
+ result = explain(rec.trace()).for_action("refund-o-17")
113
+ assert receipt.status.value == "satisfied" # the refund really happened
114
+ assert receipt.origin.value == "confirmed" # and this action produced it
115
+ assert result.assessment_alignment == "contradicted" # the agent said failure
116
+ print(result.codes) # ('ASSESSMENT_DIVERGENCE', 'ACKNOWLEDGEMENT_EFFECT_DIVERGENCE')
117
+ html = render_report(rec.trace()) # offline, minimal disclosure
143
118
  ```
144
119
 
145
- `capture_tool` decorates a synchronous tool so every call is recorded the same
146
- way. The
147
- [agent-loop example](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/examples/agent_loop_sqlite.py)
148
- runs this end to end against a real SQLite database, offline, in three modes:
149
- a timeout after the refund committed, a refund made by someone else, and a
150
- readback too stale to decide.
120
+ A run can register several actions; each `register` returns that action's
121
+ `ActionCapture`, and its attempts, assessments and verifications stay separate.
122
+ `capture_tool` decorates a tool (synchronous or `async def`) so every call is
123
+ recorded. `poll_readback` re-reads a lagging source until the outcome is known,
124
+ and `retry_advice` turns the receipt into a retry decision.
125
+
126
+ The built-in verifiers are `RecordVerifier` (rows, orders, tickets, files:
127
+ `record_present`) and `RefundVerifier` (`refund_processed`). Both check values,
128
+ count, commit deadline and persistence; unavailable, incomplete, wrongly scoped or
129
+ stale readback produces `unknown`. Effect origin is a separate conclusion:
130
+ `confirmed`, `other_actor` or `unknown`. Verifiers also record which effects match
131
+ the target and domain findings (excess, duplicate, mismatched, reversed or
132
+ removed effects); `explain` reads those receipts without any domain logic of its
133
+ own.
134
+
135
+ The [agent-loop example](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/examples/agent_loop_sqlite.py)
136
+ runs this end to end against a real SQLite database, offline, in three modes: a
137
+ timeout after the refund committed, a refund made by someone else, and a readback
138
+ too stale to decide. The
139
+ [framework guide](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/docs/framework-integration.md)
140
+ shows where the calls go in OpenAI Agents SDK, LangChain, Claude Agent SDK and MCP
141
+ tool loops.
142
+
143
+ Traces round-trip through a portable, versioned JSON schema (`1.0`):
144
+ `Trace.from_json(trace.to_json())` rejects unknown fields, broken identity links
145
+ and dependency cycles, and every captured payload is read-only.
151
146
 
152
147
  ## Bring your own domain
153
148
 
154
- Supply a read-only `Verifier` for your predicate, and a `TargetMatcher` when the
155
- agent's reported success or failure should be judged against that contract:
149
+ Write a read-only `Verifier` for your predicate. It returns a
150
+ `VerificationResult` with the state, origin and the effects it saw; to let the
151
+ agent's report be judged, it also records `target_effect_ids` (this action's
152
+ effects that match the full target):
156
153
 
157
154
  ```python
158
155
  receipt = verify(action, observation, verifier=MyVerifier())
159
- explanation = explain(trace, target_matcher=MyTargetMatcher())
160
156
  ```
161
157
 
162
158
  Your application owns source trust, scope, completeness, timing and provenance;
163
- Consequa-X checks that receipts bind to the supplied contract and observation,
164
- and keeps the evidence structured. The
159
+ Consequa-X checks that receipts bind to the supplied action, contract and
160
+ observation, and keeps the evidence structured. The
165
161
  [artifact-delivery example](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/examples/artifact_delivery.py)
166
- implements both adapters with public APIs only, and the
162
+ implements a verifier with public APIs only, and the
167
163
  [integration guide](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/docs/application-integration.md)
168
164
  describes the boundary in detail. The
169
165
  [stable API](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/docs/stable-api.md)
170
- page lists what version 1 guarantees.
166
+ page lists what version 2 guarantees.
167
+
168
+ ## Upgrading from 1.x
169
+
170
+ Version 2.0 changes the trace schema to `1.0` (several actions per trace, a
171
+ declared time base, keyword-only and read-only records) and moves domain
172
+ interpretation into verifiers. Convert saved 1.x traces with
173
+ `consequa-x migrate OLD.json --output NEW.json` or `migrate_trace()`. The
174
+ [migration guide](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/docs/migrating-to-2.0.md)
175
+ lists every change and its replacement.
171
176
 
172
177
  ## Command line
173
178
 
@@ -175,12 +180,17 @@ page lists what version 1 guarantees.
175
180
  | --- | --- |
176
181
  | `consequa-x demo --output DIR` | Write the twelve synthetic refund scenarios: traces, explanations, schema and manifest |
177
182
  | `consequa-x explain TRACE [--output FILE]` | Explain an existing trace without replay; prints JSON or writes a new file |
178
- | `consequa-x report TRACE --output FILE` | Render a self-contained HTML report; add `--include-details` to embed the full capture |
183
+ | `consequa-x report TRACE --output FILE` | Render a self-contained HTML report; `--include-details` embeds the full capture, `--redact KEY` blanks named keys in it |
184
+ | `consequa-x summarize PATH... [--format markdown]` | Count outcomes, alignment and divergences across trace files, folders and `.jsonl` stores |
185
+ | `consequa-x compare A B` | Compare two traces action by action |
186
+ | `consequa-x verify TRACE --readback OBS --output NEW` | Verify a captured readback file with a built-in verifier |
187
+ | `consequa-x validate TRACE...` / `consequa-x migrate OLD --output NEW` | Check traces; convert schema `0.1` traces |
179
188
  | `consequa-x experiment …` and research commands | Paired replay, decision attribution and calibration studies (see Research) |
180
189
 
181
190
  ## What it does not do
182
191
 
183
192
  - It does not watch or control a live agent; your application captures the records.
193
+ `retry_advice` is advice derived from captured evidence, not a guarantee.
184
194
  - It does not authenticate a readback source or prove that provider metadata is true.
185
195
  - An evidence-graph link records provenance, not causal necessity.
186
196
  - Default reports omit captured strings and identifiers, but keep times, counts
@@ -189,8 +199,8 @@ page lists what version 1 guarantees.
189
199
 
190
200
  ## Research
191
201
 
192
- The repository also contains declared research interfaces, separate from the
193
- stable core: isolated replay, contrastive and Shapley-style decision attribution,
202
+ The repository also contains declared research interfaces, imported from
203
+ `consequa_x.research`: isolated replay, contrastive and Shapley-style decision attribution,
194
204
  calibration studies, an optional local Ollama adapter, and the retained evidence
195
205
  for each study. Results so far are synthetic or exploratory local-model studies
196
206
  and are reported with their denominators and limits. Start with the
@@ -49,98 +49,103 @@ evaluation only; never pass it to an explainer.
49
49
 
50
50
  ## Use it in Python
51
51
 
52
- Register the intended outcome before the action, capture what happened, read the
53
- outcome back from a trusted read-only source, then verify and explain:
52
+ Register each consequential action with its intended outcome before running it,
53
+ let the recorder capture what happens, read the outcome back from a trusted
54
+ read-only source, then verify and explain:
54
55
 
55
56
  ```python
56
57
  from consequa_x import (
57
- Action, AgentAssessment, AssessmentStatus, Observation, OutcomeContract,
58
- Trace, explain, render_report, verify,
58
+ Observation, OutcomeContract, RecordVerifier, TraceRecorder, explain, render_report,
59
59
  )
60
60
 
61
- expected = {"payment_id": "p1", "amount_minor": 1000, "currency": "INR"}
62
- contract = OutcomeContract("contract-1", "refund_processed", expected,
63
- deadline_ms=50, stability_ms=20)
64
- action = Action("refund-1", "trace-1", "agent", "refund", expected, 0, contract)
65
-
66
- # In an application, capture this independently from a trusted read-only source.
67
- observation = Observation(
68
- "readback-1", "independent-ledger", 100, 100, {"payment_id": "p1"},
69
- ({**expected, "effect_id": "effect-1", "committed_at_ms": 25,
70
- "status": "processed", "action_id": "refund-1", "actor": "agent"},),
71
- )
72
- receipt = verify(action, observation)
73
- trace = Trace("trace-1", action, (), (),
74
- AgentAssessment(AssessmentStatus.FAILURE, 10),
75
- (observation,), (receipt,))
76
-
77
- assert receipt.status.value == "satisfied" # the refund really happened
78
- assert receipt.origin.value == "confirmed" # and this action produced it
79
- assert explain(trace).assessment_alignment == "contradicted" # the agent said failure
80
- html = render_report(trace) # offline, minimal disclosure
81
- ```
82
-
83
- The built-in `refund_processed` verifier checks payment, integer amount,
84
- currency, exact count, commit deadline and a persistence window. Unavailable,
85
- incomplete, wrongly scoped or stale readback produces `unknown`. Effect origin is
86
- a separate conclusion: `confirmed`, `other_actor` or `unknown`.
87
-
88
- Traces round-trip through a portable, versioned JSON schema (`0.1`):
89
- `Trace.from_json(trace.to_json())` rejects unknown fields, broken identity links
90
- and dependency cycles. `consequa_x.schema.get_trace_schema()` returns the bundled
91
- JSON Schema.
92
-
93
- ## Capture a real agent's tool call
94
-
95
- `TraceRecorder` records an action while your agent runs, and `RecordVerifier`
96
- checks any outcome that leaves records behind (rows, orders, tickets, files):
97
-
98
- ```python
99
- from consequa_x import OutcomeContract, RecordVerifier, TraceRecorder, explain
100
-
101
- contract = OutcomeContract("refund-o-17", "record_present",
102
- {"order_id": "o-17", "amount_minor": 5000, "currency": "INR"},
103
- deadline_ms=200, stability_ms=50)
104
61
  verifier = RecordVerifier(scope_fields=("order_id",))
62
+ rec = TraceRecorder("run-17", actor="support-agent", time_base="relative_ms")
63
+ refund = rec.register(
64
+ "refund-o-17", "issue_refund", {"order_id": "o-17"}, at_ms=0,
65
+ contract=OutcomeContract(contract_id="refund-o-17", predicate="record_present",
66
+ expected={"order_id": "o-17", "amount_minor": 5000},
67
+ deadline_ms=200, stability_ms=50))
105
68
 
106
- rec = TraceRecorder("run-17", actor="support-agent")
107
- rec.register("refund-o-17", "issue_refund", {"order_id": "o-17"}, contract=contract)
108
69
  try:
109
- with rec.attempt(data={"tool": "issue_refund"}) as attempt:
110
- issue_refund("o-17", attempt_id=attempt.attempt_id) # your tool
111
- rec.assessment("success") # what the agent reported
70
+ with refund.attempt(at_ms=5) as attempt: # your tool call goes here
71
+ attempt.at_ms = 30
72
+ raise TimeoutError # ...and it timed out
112
73
  except TimeoutError:
113
- rec.assessment("failure")
114
- rec.verify(read_refunds_for("o-17"), verifier=verifier) # your read-only readback
115
- print(explain(rec.trace(), target_matcher=verifier.target_matcher()).diagnoses)
74
+ refund.assessment("failure", at_ms=31) # what the agent reported
75
+
76
+ # Captured independently, after the horizon, from a read-only replica of the orders table.
77
+ readback = Observation(
78
+ observation_id="read-1", source="orders-replica", observed_at_ms=300, as_of_ms=300,
79
+ scope={"order_id": "o-17"},
80
+ records=({"effect_id": "rf-1", "order_id": "o-17", "amount_minor": 5000,
81
+ "committed_at_ms": 20, "action_id": "refund-o-17",
82
+ "attempt_id": "refund-o-17:attempt-1", "actor": "support-agent"},))
83
+ receipt = refund.verify(readback, verifier=verifier)
84
+
85
+ result = explain(rec.trace()).for_action("refund-o-17")
86
+ assert receipt.status.value == "satisfied" # the refund really happened
87
+ assert receipt.origin.value == "confirmed" # and this action produced it
88
+ assert result.assessment_alignment == "contradicted" # the agent said failure
89
+ print(result.codes) # ('ASSESSMENT_DIVERGENCE', 'ACKNOWLEDGEMENT_EFFECT_DIVERGENCE')
90
+ html = render_report(rec.trace()) # offline, minimal disclosure
116
91
  ```
117
92
 
118
- `capture_tool` decorates a synchronous tool so every call is recorded the same
119
- way. The
120
- [agent-loop example](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/examples/agent_loop_sqlite.py)
121
- runs this end to end against a real SQLite database, offline, in three modes:
122
- a timeout after the refund committed, a refund made by someone else, and a
123
- readback too stale to decide.
93
+ A run can register several actions; each `register` returns that action's
94
+ `ActionCapture`, and its attempts, assessments and verifications stay separate.
95
+ `capture_tool` decorates a tool (synchronous or `async def`) so every call is
96
+ recorded. `poll_readback` re-reads a lagging source until the outcome is known,
97
+ and `retry_advice` turns the receipt into a retry decision.
98
+
99
+ The built-in verifiers are `RecordVerifier` (rows, orders, tickets, files:
100
+ `record_present`) and `RefundVerifier` (`refund_processed`). Both check values,
101
+ count, commit deadline and persistence; unavailable, incomplete, wrongly scoped or
102
+ stale readback produces `unknown`. Effect origin is a separate conclusion:
103
+ `confirmed`, `other_actor` or `unknown`. Verifiers also record which effects match
104
+ the target and domain findings (excess, duplicate, mismatched, reversed or
105
+ removed effects); `explain` reads those receipts without any domain logic of its
106
+ own.
107
+
108
+ The [agent-loop example](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/examples/agent_loop_sqlite.py)
109
+ runs this end to end against a real SQLite database, offline, in three modes: a
110
+ timeout after the refund committed, a refund made by someone else, and a readback
111
+ too stale to decide. The
112
+ [framework guide](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/docs/framework-integration.md)
113
+ shows where the calls go in OpenAI Agents SDK, LangChain, Claude Agent SDK and MCP
114
+ tool loops.
115
+
116
+ Traces round-trip through a portable, versioned JSON schema (`1.0`):
117
+ `Trace.from_json(trace.to_json())` rejects unknown fields, broken identity links
118
+ and dependency cycles, and every captured payload is read-only.
124
119
 
125
120
  ## Bring your own domain
126
121
 
127
- Supply a read-only `Verifier` for your predicate, and a `TargetMatcher` when the
128
- agent's reported success or failure should be judged against that contract:
122
+ Write a read-only `Verifier` for your predicate. It returns a
123
+ `VerificationResult` with the state, origin and the effects it saw; to let the
124
+ agent's report be judged, it also records `target_effect_ids` (this action's
125
+ effects that match the full target):
129
126
 
130
127
  ```python
131
128
  receipt = verify(action, observation, verifier=MyVerifier())
132
- explanation = explain(trace, target_matcher=MyTargetMatcher())
133
129
  ```
134
130
 
135
131
  Your application owns source trust, scope, completeness, timing and provenance;
136
- Consequa-X checks that receipts bind to the supplied contract and observation,
137
- and keeps the evidence structured. The
132
+ Consequa-X checks that receipts bind to the supplied action, contract and
133
+ observation, and keeps the evidence structured. The
138
134
  [artifact-delivery example](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/examples/artifact_delivery.py)
139
- implements both adapters with public APIs only, and the
135
+ implements a verifier with public APIs only, and the
140
136
  [integration guide](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/docs/application-integration.md)
141
137
  describes the boundary in detail. The
142
138
  [stable API](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/docs/stable-api.md)
143
- page lists what version 1 guarantees.
139
+ page lists what version 2 guarantees.
140
+
141
+ ## Upgrading from 1.x
142
+
143
+ Version 2.0 changes the trace schema to `1.0` (several actions per trace, a
144
+ declared time base, keyword-only and read-only records) and moves domain
145
+ interpretation into verifiers. Convert saved 1.x traces with
146
+ `consequa-x migrate OLD.json --output NEW.json` or `migrate_trace()`. The
147
+ [migration guide](https://github.com/Devrajsinh-Jhala/consequa-x/blob/main/docs/migrating-to-2.0.md)
148
+ lists every change and its replacement.
144
149
 
145
150
  ## Command line
146
151
 
@@ -148,12 +153,17 @@ page lists what version 1 guarantees.
148
153
  | --- | --- |
149
154
  | `consequa-x demo --output DIR` | Write the twelve synthetic refund scenarios: traces, explanations, schema and manifest |
150
155
  | `consequa-x explain TRACE [--output FILE]` | Explain an existing trace without replay; prints JSON or writes a new file |
151
- | `consequa-x report TRACE --output FILE` | Render a self-contained HTML report; add `--include-details` to embed the full capture |
156
+ | `consequa-x report TRACE --output FILE` | Render a self-contained HTML report; `--include-details` embeds the full capture, `--redact KEY` blanks named keys in it |
157
+ | `consequa-x summarize PATH... [--format markdown]` | Count outcomes, alignment and divergences across trace files, folders and `.jsonl` stores |
158
+ | `consequa-x compare A B` | Compare two traces action by action |
159
+ | `consequa-x verify TRACE --readback OBS --output NEW` | Verify a captured readback file with a built-in verifier |
160
+ | `consequa-x validate TRACE...` / `consequa-x migrate OLD --output NEW` | Check traces; convert schema `0.1` traces |
152
161
  | `consequa-x experiment …` and research commands | Paired replay, decision attribution and calibration studies (see Research) |
153
162
 
154
163
  ## What it does not do
155
164
 
156
165
  - It does not watch or control a live agent; your application captures the records.
166
+ `retry_advice` is advice derived from captured evidence, not a guarantee.
157
167
  - It does not authenticate a readback source or prove that provider metadata is true.
158
168
  - An evidence-graph link records provenance, not causal necessity.
159
169
  - Default reports omit captured strings and identifiers, but keep times, counts
@@ -162,8 +172,8 @@ page lists what version 1 guarantees.
162
172
 
163
173
  ## Research
164
174
 
165
- The repository also contains declared research interfaces, separate from the
166
- stable core: isolated replay, contrastive and Shapley-style decision attribution,
175
+ The repository also contains declared research interfaces, imported from
176
+ `consequa_x.research`: isolated replay, contrastive and Shapley-style decision attribution,
167
177
  calibration studies, an optional local Ollama adapter, and the retained evidence
168
178
  for each study. Results so far are synthetic or exploratory local-model studies
169
179
  and are reported with their denominators and limits. Start with the