arbiter-engine 0.2.7__tar.gz → 0.2.8__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 (334) hide show
  1. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/.github/workflows/release.yml +26 -14
  2. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/CHANGELOG.md +39 -0
  3. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/CITATION.cff +1 -1
  4. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/MODELING.md +15 -2
  5. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/PKG-INFO +1 -1
  6. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/api.py +25 -1
  7. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/assumptions.py +19 -1
  8. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/water_tank.yaml +1 -1
  9. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/inference/hypothesis.py +23 -2
  10. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/inference/runner.py +79 -8
  11. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/scripts/benchmark_check.py +1 -1
  12. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/traverser.py +1 -1
  13. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/evidence/alpha1_evidence_pack.md +2 -2
  14. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/evidence/l5_surprise_synthesis.md +3 -3
  15. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/water_tank.yaml +1 -1
  16. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/pyproject.toml +1 -1
  17. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecaster_is_judged_by_the_same_eight_axioms.py +1 -1
  18. arbiter_engine-0.2.8/tests/test_a_posterior_says_which_reading_it_left_out.py +207 -0
  19. arbiter_engine-0.2.8/tests/test_the_alpha_figure_comes_from_the_record.py +108 -0
  20. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_evidence_floor_is_declared_or_disclosed.py +23 -7
  21. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_stance_page_says_what_no_other_page_says.py +14 -51
  22. arbiter_engine-0.2.7/.github/PUBLISH_FROM_CI +0 -43
  23. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/.gitattributes +0 -0
  24. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  25. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  26. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/.github/workflows/tests.yml +0 -0
  27. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/.github/workflows/verify-published.yml +0 -0
  28. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/.github/workflows/verify-tag-artifact.yml +0 -0
  29. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/.gitignore +0 -0
  30. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/AI_ATTRIBUTION.md +0 -0
  31. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/AUTHORS.md +0 -0
  32. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/BRIDGES.md +0 -0
  33. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/CODE_OF_CONDUCT.md +0 -0
  34. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/COMPATIBILITY.md +0 -0
  35. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/CONTRIBUTING.md +0 -0
  36. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/LICENSE +0 -0
  37. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/NOTICE +0 -0
  38. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/PRIVACY.md +0 -0
  39. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/README.md +0 -0
  40. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/ROADMAP.md +0 -0
  41. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/SECURITY.md +0 -0
  42. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/STANCE.md +0 -0
  43. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/SUPPORT.md +0 -0
  44. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/TRADEMARK.md +0 -0
  45. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/__init__.py +0 -0
  46. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/axiom_thresholds.py +0 -0
  47. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/causal/__init__.py +0 -0
  48. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/causal/discovery.py +0 -0
  49. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/causal/granger.py +0 -0
  50. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/causal/leadlag.py +0 -0
  51. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/clock.py +0 -0
  52. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/derived/__init__.py +0 -0
  53. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/derived/indicator.py +0 -0
  54. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/derived/parser.py +0 -0
  55. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/envelope.py +0 -0
  56. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/battery_pack.yaml +0 -0
  57. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/factory_line.yaml +0 -0
  58. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/kubernetes_node.yaml +0 -0
  59. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/margin_book.yaml +0 -0
  60. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/pump_tank_dynamics.yaml +0 -0
  61. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/pump_tank_planning.yaml +0 -0
  62. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/substation_feeder.yaml +0 -0
  63. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/examples/substation_feeder_surprises.yaml +0 -0
  64. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/fire_frequency.py +0 -0
  65. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/forecast/__init__.py +0 -0
  66. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/forecast/contract.py +0 -0
  67. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/forecast/envelope.py +0 -0
  68. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/forecast/ingest.py +0 -0
  69. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/forecast/monitor.py +0 -0
  70. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/forecast/shadow.py +0 -0
  71. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/history/__init__.py +0 -0
  72. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/history/calendar.py +0 -0
  73. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/history/observation.py +0 -0
  74. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/history/observation_production.py +0 -0
  75. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/history/observation_source_wiring.py +0 -0
  76. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/history/readiness.py +0 -0
  77. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/history/sqlite_store.py +0 -0
  78. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/inference/__init__.py +0 -0
  79. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/inference/causal.py +0 -0
  80. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/inference/ve.py +0 -0
  81. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/interfaces.py +0 -0
  82. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/mcp/__init__.py +0 -0
  83. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/mcp/server.py +0 -0
  84. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/__init__.py +0 -0
  85. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axiom_verdicts_production.py +0 -0
  86. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/__init__.py +0 -0
  87. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/boundedness.py +0 -0
  88. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/connectivity.py +0 -0
  89. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/conservation.py +0 -0
  90. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/consistency.py +0 -0
  91. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/extensions.py +0 -0
  92. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/homeostasis.py +0 -0
  93. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/monotonicity.py +0 -0
  94. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/peers.py +0 -0
  95. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/responsiveness.py +0 -0
  96. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/roles.py +0 -0
  97. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/axioms/stability.py +0 -0
  98. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/domain_loader.py +0 -0
  99. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/entail.py +0 -0
  100. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/loader.py +0 -0
  101. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/prediction_production.py +0 -0
  102. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/reasoner.py +0 -0
  103. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/ontology/schemas/health_meta_ontology.ttl +0 -0
  104. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/producers/__init__.py +0 -0
  105. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/producers/baseline_learner.py +0 -0
  106. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/projection/__init__.py +0 -0
  107. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/projection/projector.py +0 -0
  108. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/projection/runner.py +0 -0
  109. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/propagation/__init__.py +0 -0
  110. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/propagation/impact_estimator.py +0 -0
  111. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/propagation/lp_confidence.py +0 -0
  112. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/propagation/mcts_root_cause.py +0 -0
  113. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/propagation/root_cause.py +0 -0
  114. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/propagation/weight_learner.py +0 -0
  115. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/rca/__init__.py +0 -0
  116. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/rca/greedy_set_cover.py +0 -0
  117. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/replay.py +0 -0
  118. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/residual/__init__.py +0 -0
  119. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/residual/predict_vs_mirror.py +0 -0
  120. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/residual/sqlite_ledger.py +0 -0
  121. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/schema/envelope.schema.json +0 -0
  122. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/scripts/__init__.py +0 -0
  123. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/scripts/surprise_benchmark.py +0 -0
  124. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/subenvelope.py +0 -0
  125. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/surprises.py +0 -0
  126. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/temporal/__init__.py +0 -0
  127. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/temporal/temporal_edge.py +0 -0
  128. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/temporal/trend_projection.py +0 -0
  129. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/__init__.py +0 -0
  130. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/action_clears_problem.py +0 -0
  131. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/actions.py +0 -0
  132. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/builder.py +0 -0
  133. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/gap.py +0 -0
  134. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/hypothesis_generator.py +0 -0
  135. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/hypothesis_production.py +0 -0
  136. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/kernel_pipeline_executor.py +0 -0
  137. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/monte_carlo_predictor.py +0 -0
  138. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/optimization_production.py +0 -0
  139. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/pipeline_production.py +0 -0
  140. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/planner.py +0 -0
  141. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/rollout.py +0 -0
  142. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/topology.py +0 -0
  143. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/topology_optimizer.py +0 -0
  144. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/transition_learner.py +0 -0
  145. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/twin/traverser_production.py +0 -0
  146. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/arbiter_engine/types.py +0 -0
  147. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/evidence/README.md +0 -0
  148. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/evidence/customer_deployment_runbook.md +0 -0
  149. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/evidence/extended_fault_scenarios.md +0 -0
  150. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/evidence/observability_handoff_guide.md +0 -0
  151. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/evidence/tech_brief.md +0 -0
  152. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/evidence/use-case-catalogue.md +0 -0
  153. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/battery_pack.yaml +0 -0
  154. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/factory_line.yaml +0 -0
  155. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/kubernetes_node.yaml +0 -0
  156. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/margin_book.yaml +0 -0
  157. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/pump_tank_dynamics.yaml +0 -0
  158. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/pump_tank_planning.yaml +0 -0
  159. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/substation_feeder.yaml +0 -0
  160. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/examples/substation_feeder_surprises.yaml +0 -0
  161. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/schema/envelope.schema.json +0 -0
  162. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/conftest.py +0 -0
  163. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_balance_may_cross_an_entity_boundary.py +0 -0
  164. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_bound_can_belong_to_one_instance.py +0 -0
  165. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_calibration_figure_names_its_population.py +0 -0
  166. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_calibration_says_how_many_trajectories_it_saw.py +0 -0
  167. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_chain_of_lags_says_how_it_was_composed.py +0 -0
  168. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_changelog_section_says_whether_you_can_install_it.py +0 -0
  169. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_check_that_cannot_run_says_so.py +0 -0
  170. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_check_that_declines_is_heard.py +0 -0
  171. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_checker_asks_no_entity_type.py +0 -0
  172. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_clean_rollout_still_counts_what_it_evaluated.py +0 -0
  173. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_companion_beside_the_models_is_not_one.py +0 -0
  174. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_correct_gain_is_not_contradicted_by_a_lagged_series.py +0 -0
  175. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_coupling_block_is_checked_too.py +0 -0
  176. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_coupling_is_graded_on_its_own_projections.py +0 -0
  177. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_coupling_learns_how_its_projections_fared.py +0 -0
  178. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_bad_state_is_not_silent.py +0 -0
  179. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_calendar_is_actually_read.py +0 -0
  180. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_edge_reaches_the_published_surface.py +0 -0
  181. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_floor_is_checked_on_every_path.py +0 -0
  182. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_gain_moves_the_downstream_value.py +0 -0
  183. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_number_is_the_first_breach.py +0 -0
  184. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_offset_waits_for_the_delay.py +0 -0
  185. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_source_matches_the_guide.py +0 -0
  186. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_declared_spread_reaches_the_value.py +0 -0
  187. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_decline_states_the_number_it_computed.py +0 -0
  188. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_derived_indicator_is_computed_not_fed.py +0 -0
  189. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_discipline_reports_its_own_denominator.py +0 -0
  190. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_dropped_declaration_is_not_silent.py +0 -0
  191. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_filed_source_says_who_is_not_a_producer.py +0 -0
  192. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_finding_says_where_to_look_next.py +0 -0
  193. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_batch_is_filed_or_accounted_for.py +0 -0
  194. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_check_that_refused_says_so_out_loud.py +0 -0
  195. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_is_graded_at_its_horizon.py +0 -0
  196. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_is_measured_against_a_random_walk.py +0 -0
  197. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_is_scored_not_just_stored.py +0 -0
  198. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_keeps_its_doubt.py +0 -0
  199. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_keeps_its_doubt_when_its_median_stands_still.py +0 -0
  200. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_states_its_own_distribution.py +0 -0
  201. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_forecast_widens_and_says_what_it_assumed.py +0 -0
  202. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_freeze_is_found_inside_its_window.py +0 -0
  203. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_latency_without_a_threshold_says_so.py +0 -0
  204. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_lazy_import_did_not_move_a_name.py +0 -0
  205. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_lead_lag_p_value_means_what_it_says.py +0 -0
  206. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_learned_gain_is_a_proposal.py +0 -0
  207. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_margin_is_measured_against_every_line.py +0 -0
  208. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_nested_block_is_checked_too.py +0 -0
  209. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_pair_can_beat_the_best_single_action.py +0 -0
  210. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_per_instance_bound_reaches_every_reader.py +0 -0
  211. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_plan_files_the_row_that_is_a_forecast.py +0 -0
  212. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_plan_ranks_only_when_told_how.py +0 -0
  213. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_posterior_is_arithmetic_not_assumption.py +0 -0
  214. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_prediction_ledger_outlives_the_process.py +0 -0
  215. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_projection_declines_rather_than_guessing.py +0 -0
  216. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_proposal_says_what_adopting_it_would_have_caught.py +0 -0
  217. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_proposed_spread_says_what_it_assumed.py +0 -0
  218. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_ranking_says_how_close_the_call_was.py +0 -0
  219. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_reading_can_be_impossible_without_being_extreme.py +0 -0
  220. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_refused_block_says_which_rule_it_was_on.py +0 -0
  221. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_refused_coupling_is_a_question_not_a_silence.py +0 -0
  222. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_response_model_nobody_recognises_is_not_exponential.py +0 -0
  223. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_rollout_can_be_graded_later.py +0 -0
  224. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_rollout_carries_a_transient_past_the_action_step.py +0 -0
  225. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_rollout_steps_the_model_forward.py +0 -0
  226. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_second_action_on_a_moved_property_superposes.py +0 -0
  227. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_sentence_the_changelog_closed_is_gone.py +0 -0
  228. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_set_aside_forecast_says_who_set_it_aside.py +0 -0
  229. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_source_pinned_at_zero_passes_no_doubt_on.py +0 -0
  230. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_spread_nobody_declared_is_not_a_declared_zero.py +0 -0
  231. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_starved_join_says_which_operand_starved.py +0 -0
  232. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_surprise_the_store_never_saw_is_not_a_miss.py +0 -0
  233. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_tie_break_knows_which_side_of_the_line.py +0 -0
  234. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_tie_is_broken_by_the_declared_margin.py +0 -0
  235. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_transition_into_an_acted_property_still_arrives.py +0 -0
  236. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_two_stage_chain_composes_exactly.py +0 -0
  237. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_value_is_final_before_anything_reads_it.py +0 -0
  238. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_vertical_may_adopt_and_this_engine_may_not.py +0 -0
  239. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_warning_is_a_finding_on_every_surface.py +0 -0
  240. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_what_if_is_evaluated_at_the_horizon_it_asked_for.py +0 -0
  241. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_window_cannot_see_past_the_frozen_clock.py +0 -0
  242. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_withdrawn_check_is_not_silent.py +0 -0
  243. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_a_zero_allowance_still_reports.py +0 -0
  244. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_absent_data_regressions.py +0 -0
  245. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_agreement_declines_what_it_used_to_guess.py +0 -0
  246. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_an_action_may_arrive_as_a_mapping.py +0 -0
  247. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_an_action_on_a_coupled_property_reads_the_value_it_has.py +0 -0
  248. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_an_action_outlives_the_forecast_it_overrides.py +0 -0
  249. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_an_action_that_never_runs_says_so.py +0 -0
  250. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_an_imagined_breach_is_not_a_live_one_in_the_telemetry.py +0 -0
  251. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_an_imagined_finding_is_not_a_live_one.py +0 -0
  252. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_an_invented_time_course_says_so.py +0 -0
  253. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_an_undeclared_transition_projects_nothing.py +0 -0
  254. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_closed_reader.py +0 -0
  255. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_connectivity_required_property.py +0 -0
  256. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_conservation_absent_output.py +0 -0
  257. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_cross_signal_consistency.py +0 -0
  258. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_decline_contract.py +0 -0
  259. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_did_you_mean_is_case_insensitive.py +0 -0
  260. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_discovery_proposes_and_never_promotes.py +0 -0
  261. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_documented_paths_the_suite_never_ran.py +0 -0
  262. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_entailment_derives_and_refuses.py +0 -0
  263. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_envelope_schema.py +0 -0
  264. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_every_assumption_stamp_has_one_name.py +0 -0
  265. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_every_declared_tool_has_a_wrapper.py +0 -0
  266. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_every_decline_reason_has_a_producer.py +0 -0
  267. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_every_discipline_decline_has_one_name.py +0 -0
  268. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_every_forecast_gets_a_yardstick.py +0 -0
  269. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_every_reader_takes_the_same_history.py +0 -0
  270. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_every_shipped_example_actually_runs.py +0 -0
  271. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_every_simulation_decline_has_an_input.py +0 -0
  272. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_gated_cell_declines.py +0 -0
  273. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_history_outlives_the_process_and_the_weekend.py +0 -0
  274. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_homeostasis_setpoint.py +0 -0
  275. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_importing_the_package_does_not_pull_an_optional_extra.py +0 -0
  276. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_lower_bounds.py +0 -0
  277. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_mcp_feeders.py +0 -0
  278. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_mcp_roundtrip.py +0 -0
  279. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_monotonicity_reversals.py +0 -0
  280. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_no_axiom_retires_a_check.py +0 -0
  281. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_no_cell_is_judged_twice.py +0 -0
  282. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_no_identifier_names_a_private_record.py +0 -0
  283. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_no_test_anchors_time_at_import.py +0 -0
  284. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_one_declared_spread_is_counted_once.py +0 -0
  285. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_one_question_gets_one_projection.py +0 -0
  286. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_one_set_of_names_the_model_reads.py +0 -0
  287. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_role_comes_from_the_model.py +0 -0
  288. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_slow_oscillation.py +0 -0
  289. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_axioms_run_over_the_forecast_itself.py +0 -0
  290. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_causal_verb_has_a_model_to_run_against.py +0 -0
  291. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_clock_can_be_moved.py +0 -0
  292. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_config_blocks_are_documented.py +0 -0
  293. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_documented_priorities_are_the_computed_ones.py +0 -0
  294. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_engine_declares_no_domain_it_does_not_serve.py +0 -0
  295. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_engine_invents_no_indicators.py +0 -0
  296. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_engine_races_its_own_forecasts.py +0 -0
  297. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_engine_says_what_its_approximation_cost.py +0 -0
  298. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_engine_scores_its_own_declared_spread.py +0 -0
  299. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_engine_ships_an_opponent_worth_beating.py +0 -0
  300. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_engines_own_forecast_is_not_a_producers.py +0 -0
  301. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_envelope_legs_are_documented.py +0 -0
  302. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_forecasts_leg_says_how_many_were_expected.py +0 -0
  303. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_indicator_keys_are_documented.py +0 -0
  304. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_ingest_tag_names_the_caller_not_the_funnel.py +0 -0
  305. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_lane_cleans_up_after_itself.py +0 -0
  306. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_ledger_is_reachable_without_a_deep_import.py +0 -0
  307. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_legal_files_ship_together.py +0 -0
  308. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_namespaces_point_at_a_graph_that_ships.py +0 -0
  309. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_policy_document_and_the_changelog_agree.py +0 -0
  310. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_published_cascade_formula_is_the_one_that_runs.py +0 -0
  311. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_question_ranking_is_one_scale.py +0 -0
  312. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_rate_arm_declines_what_it_used_to_guess.py +0 -0
  313. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_readme_decline_count_is_derived.py +0 -0
  314. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_readme_name_count_is_derived.py +0 -0
  315. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_readme_names_the_forms_load_model_takes.py +0 -0
  316. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_rename_table_names_real_things.py +0 -0
  317. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_sample_floor_says_whether_it_is_reachable.py +0 -0
  318. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_schema_names_every_sub_envelope.py +0 -0
  319. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_schema_names_what_a_sub_envelope_carries.py +0 -0
  320. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_session_keeps_its_own_ledger.py +0 -0
  321. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_verb_that_used_the_number_names_it.py +0 -0
  322. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_the_world_model_can_be_exercised_over_mcp.py +0 -0
  323. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_threshold_overrides.py +0 -0
  324. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_timestamped_ingestion.py +0 -0
  325. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_traverse_vocabularies.py +0 -0
  326. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_trend_widens_and_does_not_point.py +0 -0
  327. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_two_actions_at_two_instants_are_not_one.py +0 -0
  328. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_two_bare_series_are_joinable.py +0 -0
  329. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_two_claims_on_one_edge_are_two_questions.py +0 -0
  330. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_two_offset_couplings_from_one_source_are_both_charged.py +0 -0
  331. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_two_plans_that_cost_the_same_compare_equal.py +0 -0
  332. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_two_rules_can_close_a_loop_neither_closes_alone.py +0 -0
  333. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_two_settings_of_one_property_do_not_add.py +0 -0
  334. {arbiter_engine-0.2.7 → arbiter_engine-0.2.8}/tests/test_unread_properties.py +0 -0
@@ -109,15 +109,26 @@ jobs:
109
109
  GH_TOKEN: ${{ github.token }}
110
110
  run: gh release upload "$GITHUB_REF_NAME" dist/* --clobber
111
111
 
112
- # PUBLISHING IS OFF UNTIL THE INDEX SIDE EXISTS, and the marker is a file
113
- # rather than a setting so the change that turns it on is reviewable.
112
+ # PUBLISHING IS DONE BY HAND, BY RULING, and the marker is a file rather
113
+ # than a setting so the change that turns it back on is reviewable.
114
114
  #
115
- # Trusted Publishing needs a publisher registered on PyPI naming this
116
- # repository, this workflow file and this environment. Until that is
117
- # registered, minting fails -- and a job that is red on every release day
118
- # is one people learn to skip, which is the argument `verify-published.yml`
119
- # already makes about itself. So: build and attach always, publish only
120
- # when the marker says the other side is ready.
115
+ # It was ON from 2026-09-03 to 2026-09-25 and never published a file. The
116
+ # job below runs on the TAG; the release runbook uploads BEFORE the tag,
117
+ # so the repository never advertises a version the index does not serve.
118
+ # For a release made that way the publish job can only ever meet a version
119
+ # already on the index, so approving it could only fail -- and fourteen tag
120
+ # runs, v0.1.11 through v0.2.7, sat waiting with no conclusion instead.
121
+ # That is the two paths being mutually exclusive, not somebody forgetting
122
+ # to click.
123
+ #
124
+ # BOTH SETTINGS IT NEEDS STILL EXIST, and settings do not show in a diff,
125
+ # so they are stated here rather than lost with the marker. A `pypi`
126
+ # environment with a required reviewer (measured 2026-09-25), and a PyPI
127
+ # trusted publisher naming this repository, this workflow and that
128
+ # environment (verified at registration on 2026-09-03; no upload has ever
129
+ # used it). Turning publishing back on is a change to the UPLOAD ORDER
130
+ # first -- tag, then publish -- and the marker second. The marker alone
131
+ # restores exactly the waits described above.
121
132
  - name: Is publishing from CI enabled?
122
133
  id: gate
123
134
  run: |
@@ -126,12 +137,13 @@ jobs:
126
137
  else
127
138
  echo "enabled=false" >> "$GITHUB_OUTPUT"
128
139
  echo "::notice::CI built these artifacts from the tag and attached them" \
129
- "to the Release. Publishing to the index is still done by hand." \
130
- "To hand it over: create the 'pypi' environment in this" \
131
- "repository's settings, register a PyPI trusted publisher naming" \
132
- "this repository, release.yml and that environment, then add" \
133
- ".github/PUBLISH_FROM_CI. The publish job stays behind the" \
134
- "environment's approval either way."
140
+ "to the Release. Publishing to the index is done by hand, before" \
141
+ "the tag, so this repository never advertises a version the index" \
142
+ "does not serve. The 'pypi' environment and the PyPI trusted" \
143
+ "publisher both still exist. To hand publishing to CI, change the" \
144
+ "release order to tag-then-publish FIRST, then add" \
145
+ ".github/PUBLISH_FROM_CI: the marker alone brings back a publish" \
146
+ "job that can only meet a version already on the index."
135
147
  fi
136
148
 
137
149
  # The bytes that were attached, kept for the publish job. NOT a rebuild
@@ -34,6 +34,45 @@ useful-looking document and the less trustworthy one.
34
34
 
35
35
  ## [Unreleased]
36
36
 
37
+ ## [0.2.8] — 2026-09-25
38
+
39
+ ### Added
40
+
41
+ - **`target_reading_set_aside` (`TARGET_READING_SET_ASIDE`), a stamp on every
42
+ `inference` answer whose target had a reading of its own.** `infer` answers whether everything ELSE implicates
43
+ the target, so the target's own reading is left out -- and nothing said so. A
44
+ supply at 9.0 kV, below its own critical floor, got the posterior it gets at a
45
+ healthy 11.0 kV. `checked.target_reading` now carries what the reading would
46
+ have set (`faulty` or `clean`) and the target's own worst severity at any
47
+ level. No posterior changes.
48
+ - **Each `hypothesize` candidate carries `own_reading`**, the same row for that
49
+ candidate: `None` when it was unread, which is the case `evidence_needed` is
50
+ written for. Measured before it: a feeder read CLEAN ranked first at 0.962
51
+ with its own current named as the evidence needed.
52
+
53
+ ### Fixed
54
+
55
+ - **`hypothesize` carries the stamps of the inferences behind its ranking.** It
56
+ dropped all of them, so a ranking computed against this engine's default
57
+ evidence floor arrived without `evidence_severity_not_declared`, while the
58
+ verb's own docstring told the reader to check for that stamp.
59
+ - **An intervention on the target answers exactly.** `infer(t, do={t: v})`
60
+ returned the same posterior for `v = 0` and `v = 1` (0.984981 on the
61
+ specimen), because the forced value was set aside with the reading. It
62
+ returns `v` now, with method `intervention`, and files nothing.
63
+ - **Twelve published sentences that read as broken English.** Seven docstrings
64
+ and comments (`Until this returned two values`, `the ONLY rule until, and`)
65
+ and five lines of `evidence/` (`the recursive-examination framework
66
+ framework`) came out of the step that removes internal references. They now
67
+ read as sentences. No behaviour changes.
68
+
69
+ ### Changed
70
+
71
+ - **Releases are uploaded to the index by hand, and `.github/PUBLISH_FROM_CI` is
72
+ retired.** The CI publish job it enabled runs on the tag, after the upload the
73
+ release procedure makes first, so it never published a file. CI still builds
74
+ each release from its tag and attaches the files to the GitHub Release.
75
+
37
76
  ## [0.2.7] — 2026-09-25
38
77
 
39
78
  ### Added
@@ -9,7 +9,7 @@ authors:
9
9
  alias: "james-sheen"
10
10
  - name: "The Arbiter Project Authors"
11
11
  license: Apache-2.0
12
- version: "0.2.7"
12
+ version: "0.2.8"
13
13
  date-released: "2026-09-25"
14
14
  repository-code: "https://github.com/james-sheen/arbiter"
15
15
  url: "https://github.com/james-sheen/arbiter"
@@ -1105,7 +1105,18 @@ this is a verb rather than a filter over `traverse`. On the shipped specimen,
1105
1105
  asking about the supply with the feeder OBSERVED faulty gives 0.679054 and with
1106
1106
  the feeder FORCED faulty gives 0.05 -- the same node in the same state, a factor
1107
1107
  of thirteen apart. Seeing a thing fail is evidence about what feeds it; breaking
1108
- it yourself is not.
1108
+ it yourself is not. Forcing the TARGET itself answers exactly: `do={t: v}`
1109
+ returns `v`, method `intervention`.
1110
+
1111
+ **The target's own reading is not evidence about the target.** `infer` asks
1112
+ whether everything ELSE implicates it, because conditioning on its own state
1113
+ would answer 1 or 0 by construction. So a supply in critical breach and the
1114
+ same supply healthy get one posterior between them -- correct for that
1115
+ question, and the answer says so: `target_reading_set_aside` is stamped whenever
1116
+ the target had a reading, and `checked.target_reading` names the state it would
1117
+ have set and the target's own worst severity. `hypothesize` carries the same
1118
+ row per candidate as `own_reading`, so a candidate the graph implicates can be
1119
+ read beside what its own meter already said.
1109
1120
 
1110
1121
  ### What counts as faulty evidence: `causal.evidence_severity`
1111
1122
 
@@ -1249,7 +1260,8 @@ which number to distrust.
1249
1260
  They arrive in an `assumptions` list on the `simulation` and `plan`
1250
1261
  sub-envelopes, and on each `plan` candidate where the candidates differ. An
1251
1262
  EMPTY list means the engine made none of them, which is a different claim from
1252
- the key being absent.
1263
+ the key being absent. The `inference` sub-envelope -- what `infer` and
1264
+ `hypothesize` return -- carries the list only when it has something in it.
1253
1265
 
1254
1266
  | stamp | what it discloses |
1255
1267
  |---|---|
@@ -1257,6 +1269,7 @@ the key being absent.
1257
1269
  | `time_course_not_declared` | the time course crossed was not declared; this engine supplied a delay or a time constant, or both |
1258
1270
  | `evidence_severity_not_declared` | `infer` read the last check against THIS engine's severity floor, because the model declared none. Declare `causal.evidence_severity:` to choose it |
1259
1271
  | `evidence_severity_unusable` | the model DID declare `causal.evidence_severity:` and this engine could not use it, so the floor above was its own. Carried beside the stamp above, never instead of it; `model_describe` names the value that was refused |
1272
+ | `target_reading_set_aside` | the queried entity had a reading of its own and the posterior was computed without it -- the question answered is whether everything else implicates it. `checked.target_reading` says what the reading would have set, and the target's own worst severity |
1260
1273
  | `steady_state_reached` | the horizon outlasted the transient, so the value reported is the settled one |
1261
1274
  | `series_edges_composed_exactly` | two couplings in series were composed by the exact cascade response |
1262
1275
  | `series_edges_compose_by_product` | two couplings in series were composed by multiplying response fractions, which is an approximation |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: arbiter-engine
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: A detection engine that reports what it did not check.
5
5
  Project-URL: Homepage, https://github.com/james-sheen/arbiter
6
6
  Project-URL: Source, https://github.com/james-sheen/arbiter
@@ -2526,7 +2526,19 @@ def hypothesize(session: EngineSession, entity_id: str,
2526
2526
  specimen: with both panels in the warning band the supply outranks the
2527
2527
  feeder 0.0104 to 0.0018, and with both below critical the feeder ranks first
2528
2528
  at 0.962. Same topology, same question, opposite answers — so read the
2529
- `evidence_severity_not_declared` stamp before reading this ranking.
2529
+ `evidence_severity_not_declared` stamp before reading this ranking. Since
2530
+ the ranking carries it: every stamp an inference behind a candidate
2531
+ applied rides on `hypothesis.assumptions`. Before that the verb dropped
2532
+ them, and this paragraph told the reader to look for a stamp that was never
2533
+ there.
2534
+
2535
+ **A candidate's own reading is not part of its score.** Each posterior
2536
+ answers whether the REST of the graph implicates that candidate, and
2537
+ `own_reading` says what the candidate's own meter already said -- `None`
2538
+ when unread, which is the case `evidence_needed` is for. Measured: a feeder
2539
+ read clean at 300 A ranked first at 0.962 with its own current named as the
2540
+ evidence needed. The ranking is right about the graph; the row now shows
2541
+ the reading beside it.
2530
2542
  """
2531
2543
  from arbiter_engine.inference.hypothesis import (
2532
2544
  hypothesize as _hypothesize)
@@ -2586,6 +2598,18 @@ def infer(session: EngineSession, target: str,
2586
2598
  Without `report_above` there is no finding, on the same rule as `project`
2587
2599
  and `discover`: the posterior is computed and reported, and whether it is
2588
2600
  alarming is not the engine's to decide.
2601
+
2602
+ THE TARGET'S OWN READING IS NOT EVIDENCE. The question answered is whether
2603
+ everything ELSE implicates the target -- conditioning on its own state would
2604
+ return 1 or 0 by construction. When the target had a reading, the answer is
2605
+ stamped `target_reading_set_aside` and `checked.target_reading` says what
2606
+ the reading would have set (`faulty` or `clean`) and the target's own worst
2607
+ severity at any level. A supply in critical breach and the same supply
2608
+ healthy get one posterior between them; that is correct for the question,
2609
+ and until a later change nothing on the answer said which question it was.
2610
+
2611
+ An intervention on the target itself answers exactly: `do={t: v}` returns
2612
+ `v`, method `intervention`, and files nothing.
2589
2613
  """
2590
2614
  if session.model is None:
2591
2615
  return unavailable_envelope("no domain model loaded")
@@ -6,7 +6,7 @@ not a warning and not a finding: it is the engine saying which of its own
6
6
  choices the value in front of you depends on, so that a reader who disagrees
7
7
  with the choice knows which number to distrust.
8
8
 
9
- WHY THEY LIVE HERE. Until they lived as bare string literals at twenty
9
+ WHY THEY LIVE HERE. Until a later change they lived as bare string literals at twenty
10
10
  sites across four modules, and nothing anywhere held the list. The cost was not
11
11
  hypothetical. An outside comparison of this engine reproduced all three of its
12
12
  ENUMERATED vocabularies exactly -- the fourteen decline reasons, the six gap
@@ -85,6 +85,23 @@ EVIDENCE_SEVERITY_NOT_DECLARED = "evidence_severity_not_declared"
85
85
  #: duplicate what is there.
86
86
  EVIDENCE_SEVERITY_UNUSABLE = "evidence_severity_unusable"
87
87
 
88
+ #: The queried entity had a reading of its own, and the posterior was computed
89
+ #: WITHOUT it. `infer` answers *does the rest of the graph implicate this
90
+ #: entity*, which is the informative question -- conditioning on the target's
91
+ #: own state would answer 1 or 0 by construction -- and the engine chose that
92
+ #: question on the caller's behalf. What the reading said is on the answer as
93
+ #: `checked.target_reading`: the state it would have set, and the target's own
94
+ #: worst severity at any level.
95
+ #:
96
+ #:, and it went unsaid for as long as the verb has existed. Measured on
97
+ #: the substation specimen with the feeder unread: the supply at 9.0 kV, below
98
+ #: its own critical floor, gets exactly the posterior it gets at a healthy
99
+ #: 11.0 kV, 0.670634 both times, because the one reading that would have
100
+ #: settled the question was the one left out. The other direction is worse. A feeder READ CLEAN is ranked
101
+ #: first by `hypothesize` at 0.962 with its own meter named as the evidence
102
+ #: needed -- the reading it had already taken and set aside.
103
+ TARGET_READING_SET_ASIDE = "target_reading_set_aside"
104
+
88
105
  #: The horizon was long enough that the transient had finished, so the value
89
106
  #: reported is the steady state rather than a point on the way to it.
90
107
  STEADY_STATE_REACHED = "steady_state_reached"
@@ -239,6 +256,7 @@ ASSUMPTION_STAMPS: Tuple[str, ...] = (
239
256
  TIME_COURSE_NOT_DECLARED,
240
257
  EVIDENCE_SEVERITY_NOT_DECLARED,
241
258
  EVIDENCE_SEVERITY_UNUSABLE,
259
+ TARGET_READING_SET_ASIDE,
242
260
  STEADY_STATE_REACHED,
243
261
  SERIES_EDGES_COMPOSED_EXACTLY,
244
262
  SERIES_EDGES_COMPOSE_BY_PRODUCT,
@@ -75,7 +75,7 @@ domain:
75
75
  #
76
76
  # The role rule is single-value: `role: percentage` range-checks this
77
77
  # reading against 0-100 and would behave identically if `level_pct` were
78
- # deleted from the model. That was the ONLY rule until, and this
78
+ # deleted from the model. That was the ONLY rule until a later change, and this
79
79
  # comment said `two independent readings that should agree` until
80
80
  # 2026-08-16 -- the reading the use-case catalogue carried in eleven
81
81
  # places, and the reason both were reported from outside on the same day.
@@ -46,6 +46,7 @@ from __future__ import annotations
46
46
 
47
47
  from typing import Any, Dict, List, Optional, Set, Tuple
48
48
 
49
+ from ..assumptions import ASSUMPTION_STAMPS
49
50
  from ..subenvelope import Decline, SubEnvelope
50
51
  from .causal import CausalGraph, causal_subgraph
51
52
  from .runner import Query, run_inference
@@ -151,10 +152,18 @@ def hypothesize(session: Any, entity_id: str, *,
151
152
  return SubEnvelope("inference", checked, not_checked=declines), []
152
153
 
153
154
  ranked: List[Dict[str, Any]] = []
155
+ # THE STAMPS OF EVERY INFERENCE THIS RANKING RESTS ON. Each
156
+ # candidate is scored by `run_inference`, which stamps the defaults it
157
+ # applied; this verb used to drop them all, so a ranking computed against
158
+ # the engine's evidence floor carried no `evidence_severity_not_declared`
159
+ # while the verb's own docstring told the reader to check for it.
160
+ stamps: set = set()
154
161
  for node, hops in candidates:
155
162
  sub = run_inference(session, Query(target=node), report_above=None)
156
163
  payload = sub.to_dict()
157
- posterior = (payload.get("checked") or {}).get("posterior")
164
+ checked_here = payload.get("checked") or {}
165
+ posterior = checked_here.get("posterior")
166
+ stamps.update(payload.get("assumptions") or ())
158
167
  entity_type = graph.entity_type.get(node, "")
159
168
  properties = _readable_properties(session.model, entity_type)
160
169
  ranked.append({
@@ -165,6 +174,13 @@ def hypothesize(session: Any, entity_id: str, *,
165
174
  # The FIRST declared readable property, which is the model's own
166
175
  # ordering rather than this verb's opinion about which matters.
167
176
  "evidence_needed": (f"{node}.{properties[0]}" if properties else None),
177
+ # WHAT THAT READING ALREADY SAID, when it was taken. The
178
+ # posterior above leaves the candidate's own reading out, which is
179
+ # what makes it a ranking at all; without this row a feeder READ
180
+ # CLEAN came back first at 0.962 with its own meter as the evidence
181
+ # needed, sending an operator to take a reading already taken.
182
+ # `None` means unread -- the case `evidence_needed` is written for.
183
+ "own_reading": checked_here.get("target_reading"),
168
184
  "test_action": _test_action(session.model, entity_type),
169
185
  })
170
186
 
@@ -179,4 +195,9 @@ def hypothesize(session: Any, entity_id: str, *,
179
195
  if r["posterior"] is not None and r["posterior"] >= report_above]
180
196
  checked["reported"] = len(ranked)
181
197
 
182
- return SubEnvelope("inference", checked, not_checked=declines), ranked
198
+ # In the vocabulary's own order, so one set of findings always reports one
199
+ # list; a stamp outside the published tuple sorts last rather than vanishing.
200
+ order = {stamp: i for i, stamp in enumerate(ASSUMPTION_STAMPS)}
201
+ assumptions = tuple(sorted(stamps, key=lambda s: (order.get(s, len(order)), s)))
202
+ return (SubEnvelope("inference", checked, not_checked=declines,
203
+ assumptions=assumptions), ranked)
@@ -23,7 +23,8 @@ from typing import (Any, Dict, FrozenSet, List, Optional, Sequence, Set,
23
23
  Tuple)
24
24
 
25
25
  from ..assumptions import (EVIDENCE_SEVERITY_NOT_DECLARED,
26
- EVIDENCE_SEVERITY_UNUSABLE)
26
+ EVIDENCE_SEVERITY_UNUSABLE,
27
+ TARGET_READING_SET_ASIDE)
27
28
  from ..subenvelope import Decline, SubEnvelope
28
29
  from ..twin.gap import GAP_CONFIDENCE_THRESHOLDS as _GAP_WEIGHT
29
30
  from ..twin.topology import GapType, TopologyGap, TopologyQuestion
@@ -79,7 +80,7 @@ def evidence_severities(model) -> Tuple[FrozenSet[str], bool, bool]:
79
80
  see; `[critical, hihg]` therefore becomes the default rather than quietly
80
81
  becoming `[critical]`.
81
82
 
82
- `unusable` SEPARATES TRYING FROM NOT TRYING. Until this returned
83
+ `unusable` SEPARATES TRYING FROM NOT TRYING. Until a later change this returned
83
84
  two values and an author who mistyped a severity got the same bare
84
85
  `evidence_severity_not_declared` as one who declared nothing -- the reading
85
86
  an outside review reproduced was *not declared*, therefore *my file did not
@@ -137,6 +138,40 @@ def evidence_from(session, graph: CausalGraph,
137
138
  return observed, unobserved
138
139
 
139
140
 
141
+ #: Severity order for reporting the worst one, most urgent first. Ties in
142
+ #: `priority_score` (WARNING and MEDIUM share one) fall to declaration order, so
143
+ #: the same findings always report the same word.
144
+ _SEVERITY_ORDER: Tuple[Severity, ...] = tuple(Severity)
145
+
146
+
147
+ def _own_severity(session, entity_id: str) -> Optional[str]:
148
+ """The most urgent severity the last check gave `entity_id`, at ANY level.
149
+
150
+ Deliberately not filtered by the evidence floor. A panel carrying a warning
151
+ under an engine floor of `high` counted as CLEAN, and a reader looking at
152
+ the set-aside reading is owed both facts -- what the floor made of it and
153
+ what the check actually said -- or *clean* reads as *nothing was found*.
154
+ """
155
+ result = getattr(session, "_last_result", None)
156
+ if result is None:
157
+ return None
158
+ worst: Optional[Severity] = None
159
+ for problem in list(getattr(result, "problems", ()) or ()) + \
160
+ list(getattr(result, "warnings", ()) or ()):
161
+ if str(getattr(problem, "entity_id", "")) != entity_id:
162
+ continue
163
+ raw = getattr(problem, "severity", None)
164
+ try:
165
+ severity = Severity(str(getattr(raw, "value", raw)).lower())
166
+ except ValueError:
167
+ continue
168
+ if worst is None or (
169
+ (severity.priority_score, _SEVERITY_ORDER.index(severity))
170
+ < (worst.priority_score, _SEVERITY_ORDER.index(worst))):
171
+ worst = severity
172
+ return None if worst is None else worst.value
173
+
174
+
140
175
  def _surgery(graph: CausalGraph, do: Dict[str, int]) -> CausalGraph:
141
176
  """Cut the incoming edges of every intervened node. THIS is the difference
142
177
  between `do(x)` and observing x, and the reason a hypothetical cannot be
@@ -220,6 +255,22 @@ def run_inference(session, query: Query,
220
255
  evidence={"cycle": loop}))
221
256
  return SubEnvelope("inference", checked, findings, declines, questions)
222
257
 
258
+ # AN INTERVENTION ON THE TARGET IS THE ANSWER. `do(x=v)` sets x,
259
+ # so P(x faulty | do(x=v)) is v, whatever the evidence says and however the
260
+ # graph is wired. Until this branch the intervened value went into the
261
+ # evidence and was then popped with the target's reading below, so the
262
+ # surgery cut x's parents and the elimination answered for an x nobody had
263
+ # set: measured on the specimen, `do={fdr-1: 0}` and `do={fdr-1: 1}` both
264
+ # returned 0.984981. Answered here, BEFORE the backdoor check, because an
265
+ # answer that reads no edge cannot be unidentifiable. Nothing is filed and
266
+ # nothing is reported: a value the caller forced is not a prediction about
267
+ # the world, and a finding would report the caller's own act back to them.
268
+ if query.target in query.do:
269
+ checked["answered"] = 1
270
+ checked["method"] = "intervention"
271
+ checked["posterior"] = float(query.do[query.target])
272
+ return SubEnvelope("inference", checked, findings, declines, questions)
273
+
223
274
  working = _surgery(graph, query.do) if query.do else graph
224
275
  latent = _open_backdoor_latent(working, query)
225
276
  if latent is not None:
@@ -235,17 +286,34 @@ def run_inference(session, query: Query,
235
286
  observed, unobserved = evidence_from(session, working, severities)
236
287
  for node in query.do:
237
288
  observed[node] = query.do[node]
238
- observed.pop(query.target, None)
289
+ # The target's own state is left out, because conditioning on it answers 1
290
+ # or 0 by construction. What is asked is whether EVERYTHING ELSE implicates
291
+ # it -- and since the answer says that is what was asked.
292
+ set_aside = observed.pop(query.target, None)
239
293
  checked["evidence"] = len(observed)
240
294
  checked["unobserved"] = len(unobserved)
241
295
  # from here down the answer depends on WHICH severities counted
242
- # as faulty, so every exit below carries the disclosure. The two returns
296
+ # as faulty, so every exit below carries the disclosure. The returns
243
297
  # above this line are refusals decided from the GRAPH alone -- a cycle, an
244
- # open backdoor -- and read no evidence, so stamping them would name a
245
- # choice that did not touch the answer.
298
+ # open backdoor -- or an intervention that fixes the answer, and read no
299
+ # evidence, so stamping them would name a choice that did not touch it.
246
300
  stamps: Tuple[str, ...] = () if floor_declared else (
247
301
  (EVIDENCE_SEVERITY_NOT_DECLARED,)
248
302
  + ((EVIDENCE_SEVERITY_UNUSABLE,) if floor_unusable else ()))
303
+ # AND WHICH READING WAS NOT USED. A target in breach answered
304
+ # exactly as a healthy one did, with nothing on the envelope saying its own
305
+ # reading had been excluded: measured, the supply at 9.0 kV and at 11.0 kV
306
+ # both 0.670634. Stamped whenever the target HAD a reading, clean as well
307
+ # as faulty -- a clean reading set aside misleads just as far, the other
308
+ # way. `severity` is the target's own worst finding at any level, beside
309
+ # the state the evidence floor made of it, because under the engine's floor
310
+ # a warning counts as clean and *clean* alone would read as *nothing found*.
311
+ target_reading: Optional[Dict[str, Any]] = None
312
+ if set_aside is not None:
313
+ target_reading = {"state": "faulty" if set_aside else "clean",
314
+ "severity": _own_severity(session, query.target)}
315
+ checked["target_reading"] = target_reading
316
+ stamps = stamps + (TARGET_READING_SET_ASIDE,)
249
317
 
250
318
  relevant = _relevant_edges(working, query, observed)
251
319
  defaulted = sorted(f"{s}->{t}" for (s, t) in relevant
@@ -313,14 +381,14 @@ def run_inference(session, query: Query,
313
381
  elif posterior >= report_above:
314
382
  findings.append(_posterior_finding(
315
383
  session, working, query, posterior, observed, unobserved,
316
- report_above))
384
+ report_above, target_reading))
317
385
 
318
386
  return SubEnvelope("inference", checked, findings, declines, questions,
319
387
  assumptions=stamps)
320
388
 
321
389
 
322
390
  def _posterior_finding(session, graph, query, posterior, observed,
323
- unobserved, report_above):
391
+ unobserved, report_above, target_reading=None):
324
392
  from ..interfaces import Problem
325
393
  entity = session.entities.get(query.target)
326
394
  return Problem.from_entity(
@@ -335,6 +403,9 @@ def _posterior_finding(session, graph, query, posterior, observed,
335
403
  "cpt_sources": graph.sources(),
336
404
  "evidence_set": sorted(observed),
337
405
  "unobserved": sorted(unobserved),
406
+ # a finding travels without its envelope, so the
407
+ # reading it did not use travels with it.
408
+ "target_reading": target_reading,
338
409
  "root_prior": ROOT_PRIOR,
339
410
  "root_prior_source": "default",
340
411
  "do": dict(query.do)})
@@ -138,7 +138,7 @@ def build(entities: int, observations: int, breach_fraction: float):
138
138
  """A fresh session with `entities` units, a `breach_fraction` of them bad.
139
139
 
140
140
  Returns the session and the two halves of its construction cost SEPARATELY.
141
- They were one number until, and the number was unreadable: loading
141
+ They were one number until a later change, and the number was unreadable: loading
142
142
  the model is flat in ENTITY count -- it loads a model, not entities -- while
143
143
  the feed is linear, so a single `build` figure hides which half a consumer
144
144
  pays again on the next cycle. That is the question the cost table gets
@@ -1744,7 +1744,7 @@ class TopologyTraverser:
1744
1744
  builder seeded, which is how a walk that evaluated one invariant came
1745
1745
  to report four.
1746
1746
 
1747
- **BOUNDEDNESS only** — and, until, only HALF of BOUNDEDNESS.
1747
+ **BOUNDEDNESS only** — and, until a later change, only HALF of BOUNDEDNESS.
1748
1748
  The body compared against `warning` and `critical` and never against
1749
1749
  `lower_warning` / `lower_critical`, so a declared FLOOR was invisible
1750
1750
  here while `UnifiedAxiomReasoner` reported it: a traversal over a
@@ -121,9 +121,9 @@ Detail is in the hypothesis log's Day-7/8/9 entries. A per-day Day-7 fault-watch
121
121
 
122
122
  ---
123
123
 
124
- ## Methodology — the recursive-examination framework 5-tier exercise framework
124
+ ## Methodology — recursive-examination 5-tier exercise framework
125
125
 
126
- Alpha-1 work was structured by the recursive-examination framework (Recursive Examination as Engine) framework — a 5-tier session-level exercise discipline that generates L5 surprises by repeatedly stressing substrate rather than auditing it (auditing reveals known unknowns; exercising reveals unknown unknowns):
126
+ Alpha-1 work was structured by the recursive-examination (Recursive Examination as Engine) framework — a 5-tier session-level exercise discipline that generates L5 surprises by repeatedly stressing substrate rather than auditing it (auditing reveals known unknowns; exercising reveals unknown unknowns):
127
127
 
128
128
  - **T0**: Data-flow probe (every session start; ~60s) — catches silent-fall-through before per-axis work.
129
129
  - **T1**: Diagnostic-signature library match — 18 catalogued signatures save ~30-60 min per match.
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## What is an L5 surprise?
10
10
 
11
- L5 surprises are observations that the planned probes had not anticipated. The Alpha's the recursive-examination framework 5-tier exercise framework (T0 data-flow probe → T1 signature library → T2 adversarial cycle → T3 diagnostic-iteration loop → T4 cross-pattern leverage → T5 codification) defines its own surface — the questions each tier knows to ask. L5 surprises are observations that fall outside that surface: they emerge from exercising substrate, not auditing it, and they typically arise during T5 (session-end codification) when the operator notices a pattern that the framework's own probes couldn't see.
11
+ L5 surprises are observations that the planned probes had not anticipated. The Alpha's recursive-examination 5-tier exercise framework (T0 data-flow probe → T1 signature library → T2 adversarial cycle → T3 diagnostic-iteration loop → T4 cross-pattern leverage → T5 codification) defines its own surface — the questions each tier knows to ask. L5 surprises are observations that fall outside that surface: they emerge from exercising substrate, not auditing it, and they typically arise during T5 (session-end codification) when the operator notices a pattern that the framework's own probes couldn't see.
12
12
 
13
13
  Why partners should care: L5 surprises are the substrate's claim to *real* coverage of cascade behavior in production-like AI/ML serving deployments. If the only findings were from planned probes, the substrate would be reporting back to its own designer. Surprises are the unplanned dividend — observations a partner couldn't have anticipated from the system's documentation alone.
14
14
 
@@ -134,7 +134,7 @@ For each L5 surprise (Days 1-11 = 16; Day-12 the second evaluation round RUN add
134
134
  - **Anticipated by the probe design, before the load generator was built**: 0 of 16.
135
135
  - **Anticipated after the triggering fault event**: 2 of 16 — the cumulative-counter self-clear and the MONOTONICITY axiom-family shift were predictable from the prior day's findings.
136
136
 
137
- So the vast majority were genuinely open-world: the substrate produced findings that the planned probes (the recursive-examination framework T0-T5 + Spikes 1-5) did NOT anticipate.
137
+ So the vast majority were genuinely open-world: the substrate produced findings that the planned probes (recursive-examination T0-T5 + Spikes 1-5) did NOT anticipate.
138
138
 
139
139
  **Detector anchor (replay) — the result that runs the other way.** Every figure above measures what the *designers* foresaw. None of them says anything about what the *detector* caught, and that number is unfavourable: **0 of the 16 Alpha-1 surprises were machine-surfaced at the time.** A human found all sixteen (the replay verdict, not published — see the provenance list at the end of this document).
140
140
 
@@ -216,7 +216,7 @@ request. They are not links; the records themselves can be supplied directly:
216
216
 
217
217
  **Operator working notes, not documents** — pattern codifications held outside the repository. Listed
218
218
  because they are where these findings were generalised, not as retrievable sources:
219
- alpha-recursive-examination (T1 catalog + the recursive-examination framework framework); the methodology notes
219
+ alpha-recursive-examination (T1 catalog + recursive-examination framework); the methodology notes
220
220
  (Pattern N 3-branch family + asymmetric-recovery sub-shape); the methodology notes
221
221
  (Pattern N load-bearing); the methodology notes (Compression-B 12× yield).
222
222
 
@@ -75,7 +75,7 @@ domain:
75
75
  #
76
76
  # The role rule is single-value: `role: percentage` range-checks this
77
77
  # reading against 0-100 and would behave identically if `level_pct` were
78
- # deleted from the model. That was the ONLY rule until, and this
78
+ # deleted from the model. That was the ONLY rule until a later change, and this
79
79
  # comment said `two independent readings that should agree` until
80
80
  # 2026-08-16 -- the reading the use-case catalogue carried in eleven
81
81
  # places, and the reason both were reported from outside on the same day.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "arbiter-engine"
7
- version = "0.2.7"
7
+ version = "0.2.8"
8
8
  description = "A detection engine that reports what it did not check."
9
9
  requires-python = ">=3.10"
10
10
  license = "Apache-2.0"
@@ -380,7 +380,7 @@ def test_a_declaration_that_names_models_without_expecting_one_counts_nothing():
380
380
 
381
381
 
382
382
  def test_the_kind_filter_is_now_a_live_path():
383
- """IT WAS DEFENCE UNTIL, AND THIS TEST SAID SO AND SAID WHY.
383
+ """IT WAS DEFENCE UNTIL a later change, AND THIS TEST SAID SO AND SAID WHY.
384
384
 
385
385
  The version here was named `..._is_defence_and_not_a_live_path`, reported
386
386
  that no test claimed to exercise the filter, and gave the measurement