arbiter-engine 0.2.7__tar.gz → 0.2.9__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.
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/.github/workflows/release.yml +26 -14
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/CHANGELOG.md +59 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/CITATION.cff +1 -1
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/MODELING.md +15 -2
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/PKG-INFO +1 -1
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/api.py +36 -3
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/assumptions.py +19 -1
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/water_tank.yaml +1 -1
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/history/calendar.py +5 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/history/sqlite_store.py +18 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/inference/hypothesis.py +23 -2
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/inference/runner.py +79 -8
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/residual/predict_vs_mirror.py +42 -13
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/scripts/benchmark_check.py +1 -1
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/traverser.py +1 -1
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/evidence/alpha1_evidence_pack.md +2 -2
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/evidence/l5_surprise_synthesis.md +3 -3
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/water_tank.yaml +1 -1
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/pyproject.toml +1 -1
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecaster_is_judged_by_the_same_eight_axioms.py +1 -1
- arbiter_engine-0.2.9/tests/test_a_posterior_says_which_reading_it_left_out.py +207 -0
- arbiter_engine-0.2.9/tests/test_every_history_the_engine_ships_can_be_described.py +260 -0
- arbiter_engine-0.2.9/tests/test_the_alpha_figure_comes_from_the_record.py +108 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_evidence_floor_is_declared_or_disclosed.py +23 -7
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_stance_page_says_what_no_other_page_says.py +14 -51
- arbiter_engine-0.2.7/.github/PUBLISH_FROM_CI +0 -43
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/.gitattributes +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/.github/workflows/tests.yml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/.github/workflows/verify-published.yml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/.github/workflows/verify-tag-artifact.yml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/.gitignore +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/AI_ATTRIBUTION.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/AUTHORS.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/BRIDGES.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/CODE_OF_CONDUCT.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/COMPATIBILITY.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/CONTRIBUTING.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/LICENSE +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/NOTICE +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/PRIVACY.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/README.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/ROADMAP.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/SECURITY.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/STANCE.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/SUPPORT.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/TRADEMARK.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/axiom_thresholds.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/causal/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/causal/discovery.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/causal/granger.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/causal/leadlag.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/clock.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/derived/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/derived/indicator.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/derived/parser.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/envelope.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/battery_pack.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/factory_line.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/kubernetes_node.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/margin_book.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/pump_tank_dynamics.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/pump_tank_planning.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/substation_feeder.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/examples/substation_feeder_surprises.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/fire_frequency.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/forecast/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/forecast/contract.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/forecast/envelope.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/forecast/ingest.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/forecast/monitor.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/forecast/shadow.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/history/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/history/observation.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/history/observation_production.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/history/observation_source_wiring.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/history/readiness.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/inference/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/inference/causal.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/inference/ve.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/interfaces.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/mcp/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/mcp/server.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axiom_verdicts_production.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/boundedness.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/connectivity.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/conservation.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/consistency.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/extensions.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/homeostasis.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/monotonicity.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/peers.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/responsiveness.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/roles.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/axioms/stability.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/domain_loader.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/entail.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/loader.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/prediction_production.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/reasoner.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/ontology/schemas/health_meta_ontology.ttl +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/producers/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/producers/baseline_learner.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/projection/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/projection/projector.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/projection/runner.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/propagation/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/propagation/impact_estimator.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/propagation/lp_confidence.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/propagation/mcts_root_cause.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/propagation/root_cause.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/propagation/weight_learner.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/rca/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/rca/greedy_set_cover.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/replay.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/residual/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/residual/sqlite_ledger.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/schema/envelope.schema.json +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/scripts/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/scripts/surprise_benchmark.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/subenvelope.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/surprises.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/temporal/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/temporal/temporal_edge.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/temporal/trend_projection.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/__init__.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/action_clears_problem.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/actions.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/builder.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/gap.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/hypothesis_generator.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/hypothesis_production.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/kernel_pipeline_executor.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/monte_carlo_predictor.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/optimization_production.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/pipeline_production.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/planner.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/rollout.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/topology.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/topology_optimizer.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/transition_learner.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/twin/traverser_production.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/arbiter_engine/types.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/evidence/README.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/evidence/customer_deployment_runbook.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/evidence/extended_fault_scenarios.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/evidence/observability_handoff_guide.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/evidence/tech_brief.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/evidence/use-case-catalogue.md +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/battery_pack.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/factory_line.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/kubernetes_node.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/margin_book.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/pump_tank_dynamics.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/pump_tank_planning.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/substation_feeder.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/examples/substation_feeder_surprises.yaml +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/schema/envelope.schema.json +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/conftest.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_balance_may_cross_an_entity_boundary.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_bound_can_belong_to_one_instance.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_calibration_figure_names_its_population.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_calibration_says_how_many_trajectories_it_saw.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_chain_of_lags_says_how_it_was_composed.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_changelog_section_says_whether_you_can_install_it.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_check_that_cannot_run_says_so.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_check_that_declines_is_heard.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_checker_asks_no_entity_type.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_clean_rollout_still_counts_what_it_evaluated.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_companion_beside_the_models_is_not_one.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_correct_gain_is_not_contradicted_by_a_lagged_series.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_coupling_block_is_checked_too.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_coupling_is_graded_on_its_own_projections.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_coupling_learns_how_its_projections_fared.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_bad_state_is_not_silent.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_calendar_is_actually_read.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_edge_reaches_the_published_surface.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_floor_is_checked_on_every_path.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_gain_moves_the_downstream_value.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_number_is_the_first_breach.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_offset_waits_for_the_delay.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_source_matches_the_guide.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_declared_spread_reaches_the_value.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_decline_states_the_number_it_computed.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_derived_indicator_is_computed_not_fed.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_discipline_reports_its_own_denominator.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_dropped_declaration_is_not_silent.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_filed_source_says_who_is_not_a_producer.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_finding_says_where_to_look_next.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_batch_is_filed_or_accounted_for.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_check_that_refused_says_so_out_loud.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_is_graded_at_its_horizon.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_is_measured_against_a_random_walk.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_is_scored_not_just_stored.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_keeps_its_doubt.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_keeps_its_doubt_when_its_median_stands_still.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_states_its_own_distribution.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_forecast_widens_and_says_what_it_assumed.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_freeze_is_found_inside_its_window.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_latency_without_a_threshold_says_so.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_lazy_import_did_not_move_a_name.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_lead_lag_p_value_means_what_it_says.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_learned_gain_is_a_proposal.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_margin_is_measured_against_every_line.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_nested_block_is_checked_too.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_pair_can_beat_the_best_single_action.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_per_instance_bound_reaches_every_reader.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_plan_files_the_row_that_is_a_forecast.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_plan_ranks_only_when_told_how.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_posterior_is_arithmetic_not_assumption.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_prediction_ledger_outlives_the_process.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_projection_declines_rather_than_guessing.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_proposal_says_what_adopting_it_would_have_caught.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_proposed_spread_says_what_it_assumed.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_ranking_says_how_close_the_call_was.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_reading_can_be_impossible_without_being_extreme.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_refused_block_says_which_rule_it_was_on.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_refused_coupling_is_a_question_not_a_silence.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_response_model_nobody_recognises_is_not_exponential.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_rollout_can_be_graded_later.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_rollout_carries_a_transient_past_the_action_step.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_rollout_steps_the_model_forward.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_second_action_on_a_moved_property_superposes.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_sentence_the_changelog_closed_is_gone.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_set_aside_forecast_says_who_set_it_aside.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_source_pinned_at_zero_passes_no_doubt_on.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_spread_nobody_declared_is_not_a_declared_zero.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_starved_join_says_which_operand_starved.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_surprise_the_store_never_saw_is_not_a_miss.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_tie_break_knows_which_side_of_the_line.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_tie_is_broken_by_the_declared_margin.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_transition_into_an_acted_property_still_arrives.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_two_stage_chain_composes_exactly.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_value_is_final_before_anything_reads_it.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_vertical_may_adopt_and_this_engine_may_not.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_warning_is_a_finding_on_every_surface.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_what_if_is_evaluated_at_the_horizon_it_asked_for.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_window_cannot_see_past_the_frozen_clock.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_withdrawn_check_is_not_silent.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_a_zero_allowance_still_reports.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_absent_data_regressions.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_agreement_declines_what_it_used_to_guess.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_an_action_may_arrive_as_a_mapping.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_an_action_on_a_coupled_property_reads_the_value_it_has.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_an_action_outlives_the_forecast_it_overrides.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_an_action_that_never_runs_says_so.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_an_imagined_breach_is_not_a_live_one_in_the_telemetry.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_an_imagined_finding_is_not_a_live_one.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_an_invented_time_course_says_so.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_an_undeclared_transition_projects_nothing.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_closed_reader.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_connectivity_required_property.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_conservation_absent_output.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_cross_signal_consistency.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_decline_contract.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_did_you_mean_is_case_insensitive.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_discovery_proposes_and_never_promotes.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_documented_paths_the_suite_never_ran.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_entailment_derives_and_refuses.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_envelope_schema.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_every_assumption_stamp_has_one_name.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_every_declared_tool_has_a_wrapper.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_every_decline_reason_has_a_producer.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_every_discipline_decline_has_one_name.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_every_forecast_gets_a_yardstick.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_every_reader_takes_the_same_history.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_every_shipped_example_actually_runs.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_every_simulation_decline_has_an_input.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_gated_cell_declines.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_history_outlives_the_process_and_the_weekend.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_homeostasis_setpoint.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_importing_the_package_does_not_pull_an_optional_extra.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_lower_bounds.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_mcp_feeders.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_mcp_roundtrip.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_monotonicity_reversals.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_no_axiom_retires_a_check.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_no_cell_is_judged_twice.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_no_identifier_names_a_private_record.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_no_test_anchors_time_at_import.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_one_declared_spread_is_counted_once.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_one_question_gets_one_projection.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_one_set_of_names_the_model_reads.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_role_comes_from_the_model.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_slow_oscillation.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_axioms_run_over_the_forecast_itself.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_causal_verb_has_a_model_to_run_against.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_clock_can_be_moved.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_config_blocks_are_documented.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_documented_priorities_are_the_computed_ones.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_engine_declares_no_domain_it_does_not_serve.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_engine_invents_no_indicators.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_engine_races_its_own_forecasts.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_engine_says_what_its_approximation_cost.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_engine_scores_its_own_declared_spread.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_engine_ships_an_opponent_worth_beating.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_engines_own_forecast_is_not_a_producers.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_envelope_legs_are_documented.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_forecasts_leg_says_how_many_were_expected.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_indicator_keys_are_documented.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_ingest_tag_names_the_caller_not_the_funnel.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_lane_cleans_up_after_itself.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_ledger_is_reachable_without_a_deep_import.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_legal_files_ship_together.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_namespaces_point_at_a_graph_that_ships.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_policy_document_and_the_changelog_agree.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_published_cascade_formula_is_the_one_that_runs.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_question_ranking_is_one_scale.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_rate_arm_declines_what_it_used_to_guess.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_readme_decline_count_is_derived.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_readme_name_count_is_derived.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_readme_names_the_forms_load_model_takes.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_rename_table_names_real_things.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_sample_floor_says_whether_it_is_reachable.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_schema_names_every_sub_envelope.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_schema_names_what_a_sub_envelope_carries.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_session_keeps_its_own_ledger.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_verb_that_used_the_number_names_it.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_the_world_model_can_be_exercised_over_mcp.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_threshold_overrides.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_timestamped_ingestion.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_traverse_vocabularies.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_trend_widens_and_does_not_point.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_two_actions_at_two_instants_are_not_one.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_two_bare_series_are_joinable.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_two_claims_on_one_edge_are_two_questions.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_two_offset_couplings_from_one_source_are_both_charged.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_two_plans_that_cost_the_same_compare_equal.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_two_rules_can_close_a_loop_neither_closes_alone.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/tests/test_two_settings_of_one_property_do_not_add.py +0 -0
- {arbiter_engine-0.2.7 → arbiter_engine-0.2.9}/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
|
|
113
|
-
#
|
|
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
|
-
#
|
|
116
|
-
#
|
|
117
|
-
#
|
|
118
|
-
#
|
|
119
|
-
# already
|
|
120
|
-
#
|
|
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
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
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,65 @@ useful-looking document and the less trustworthy one.
|
|
|
34
34
|
|
|
35
35
|
## [Unreleased]
|
|
36
36
|
|
|
37
|
+
## [0.2.9] — 2026-09-25
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **`model_describe` and `gaps` work over every history this package ships.**
|
|
42
|
+
Both asked the session's history for `series_keys()`, which only the
|
|
43
|
+
in-memory store implemented, so a session built over
|
|
44
|
+
`SqliteObservationHistory` or `CalendarHistory` raised `AttributeError`, and
|
|
45
|
+
`rollout` ran with no seeded past (`precondition_unmet`). Both stores now list
|
|
46
|
+
their series. A history of the caller's own that implements only the five
|
|
47
|
+
`ObservationHistory` methods gets `unconsumed_observations: None` -- the report
|
|
48
|
+
could not be made -- rather than a crash or an empty list.
|
|
49
|
+
- **A forecast is graded through whatever history the session reads.** The
|
|
50
|
+
ledger read the in-memory store's private series and skipped any other, so
|
|
51
|
+
a forecast made over `SqliteObservationHistory`, in a model declaring a
|
|
52
|
+
`calendar:`, or in a model with any derived indicator matured `ungradeable`
|
|
53
|
+
every time: measured, none of twelve graded in each case, against twelve of
|
|
54
|
+
twelve on the plain store. It reads through `get_values` now, and all four
|
|
55
|
+
grade alike. No calibration figure moves for a session that graded before.
|
|
56
|
+
|
|
57
|
+
## [0.2.8] — 2026-09-25
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
|
|
61
|
+
- **`target_reading_set_aside` (`TARGET_READING_SET_ASIDE`), a stamp on every
|
|
62
|
+
`inference` answer whose target had a reading of its own.** `infer` answers whether everything ELSE implicates
|
|
63
|
+
the target, so the target's own reading is left out -- and nothing said so. A
|
|
64
|
+
supply at 9.0 kV, below its own critical floor, got the posterior it gets at a
|
|
65
|
+
healthy 11.0 kV. `checked.target_reading` now carries what the reading would
|
|
66
|
+
have set (`faulty` or `clean`) and the target's own worst severity at any
|
|
67
|
+
level. No posterior changes.
|
|
68
|
+
- **Each `hypothesize` candidate carries `own_reading`**, the same row for that
|
|
69
|
+
candidate: `None` when it was unread, which is the case `evidence_needed` is
|
|
70
|
+
written for. Measured before it: a feeder read CLEAN ranked first at 0.962
|
|
71
|
+
with its own current named as the evidence needed.
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- **`hypothesize` carries the stamps of the inferences behind its ranking.** It
|
|
76
|
+
dropped all of them, so a ranking computed against this engine's default
|
|
77
|
+
evidence floor arrived without `evidence_severity_not_declared`, while the
|
|
78
|
+
verb's own docstring told the reader to check for that stamp.
|
|
79
|
+
- **An intervention on the target answers exactly.** `infer(t, do={t: v})`
|
|
80
|
+
returned the same posterior for `v = 0` and `v = 1` (0.984981 on the
|
|
81
|
+
specimen), because the forced value was set aside with the reading. It
|
|
82
|
+
returns `v` now, with method `intervention`, and files nothing.
|
|
83
|
+
- **Twelve published sentences that read as broken English.** Seven docstrings
|
|
84
|
+
and comments (`Until this returned two values`, `the ONLY rule until, and`)
|
|
85
|
+
and five lines of `evidence/` (`the recursive-examination framework
|
|
86
|
+
framework`) came out of the step that removes internal references. They now
|
|
87
|
+
read as sentences. No behaviour changes.
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
- **Releases are uploaded to the index by hand, and `.github/PUBLISH_FROM_CI` is
|
|
92
|
+
retired.** The CI publish job it enabled runs on the tag, after the upload the
|
|
93
|
+
release procedure makes first, so it never published a file. CI still builds
|
|
94
|
+
each release from its tag and attaches the files to the GitHub Release.
|
|
95
|
+
|
|
37
96
|
## [0.2.7] — 2026-09-25
|
|
38
97
|
|
|
39
98
|
### 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.
|
|
12
|
+
version: "0.2.9"
|
|
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.
|
|
3
|
+
Version: 0.2.9
|
|
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
|
|
@@ -536,7 +536,7 @@ class EngineSession:
|
|
|
536
536
|
})
|
|
537
537
|
return records
|
|
538
538
|
|
|
539
|
-
def unconsumed_observations(self) -> List[Dict[str, Any]]:
|
|
539
|
+
def unconsumed_observations(self) -> Optional[List[Dict[str, Any]]]:
|
|
540
540
|
"""Series this session holds that no declared indicator will ever read.
|
|
541
541
|
|
|
542
542
|
This rules the second half of issue #1. ``add_observations``
|
|
@@ -564,9 +564,18 @@ class EngineSession:
|
|
|
564
564
|
# report on the other input surface counted them as read. One
|
|
565
565
|
# declaration, two verdicts, and the only way to silence the wrong one
|
|
566
566
|
# was to declare the property a second time.
|
|
567
|
+
# A HISTORY THAT CANNOT LIST ITS SERIES CANNOT BE ASKED
|
|
568
|
+
# THIS, and the answer says so rather than crashing or claiming the
|
|
569
|
+
# empty list. `series_keys` is not one of the ABC's five methods; both
|
|
570
|
+
# stores this engine ships implement it, and a caller's own need not.
|
|
571
|
+
# `None` is the engine's convention for *not measured*, and every
|
|
572
|
+
# consumer in the family already reads this key through `or []`.
|
|
573
|
+
keys = getattr(self.history, "series_keys", None)
|
|
574
|
+
if keys is None:
|
|
575
|
+
return None
|
|
567
576
|
declared: Dict[str, set] = self.readable_properties()
|
|
568
577
|
records: List[Dict[str, Any]] = []
|
|
569
|
-
for entity_id, prop in
|
|
578
|
+
for entity_id, prop in keys():
|
|
570
579
|
entity = self.entities.get(entity_id)
|
|
571
580
|
if entity is None:
|
|
572
581
|
reason = "unknown_entity"
|
|
@@ -2526,7 +2535,19 @@ def hypothesize(session: EngineSession, entity_id: str,
|
|
|
2526
2535
|
specimen: with both panels in the warning band the supply outranks the
|
|
2527
2536
|
feeder 0.0104 to 0.0018, and with both below critical the feeder ranks first
|
|
2528
2537
|
at 0.962. Same topology, same question, opposite answers — so read the
|
|
2529
|
-
`evidence_severity_not_declared` stamp before reading this ranking.
|
|
2538
|
+
`evidence_severity_not_declared` stamp before reading this ranking. Since
|
|
2539
|
+
the ranking carries it: every stamp an inference behind a candidate
|
|
2540
|
+
applied rides on `hypothesis.assumptions`. Before that the verb dropped
|
|
2541
|
+
them, and this paragraph told the reader to look for a stamp that was never
|
|
2542
|
+
there.
|
|
2543
|
+
|
|
2544
|
+
**A candidate's own reading is not part of its score.** Each posterior
|
|
2545
|
+
answers whether the REST of the graph implicates that candidate, and
|
|
2546
|
+
`own_reading` says what the candidate's own meter already said -- `None`
|
|
2547
|
+
when unread, which is the case `evidence_needed` is for. Measured: a feeder
|
|
2548
|
+
read clean at 300 A ranked first at 0.962 with its own current named as the
|
|
2549
|
+
evidence needed. The ranking is right about the graph; the row now shows
|
|
2550
|
+
the reading beside it.
|
|
2530
2551
|
"""
|
|
2531
2552
|
from arbiter_engine.inference.hypothesis import (
|
|
2532
2553
|
hypothesize as _hypothesize)
|
|
@@ -2586,6 +2607,18 @@ def infer(session: EngineSession, target: str,
|
|
|
2586
2607
|
Without `report_above` there is no finding, on the same rule as `project`
|
|
2587
2608
|
and `discover`: the posterior is computed and reported, and whether it is
|
|
2588
2609
|
alarming is not the engine's to decide.
|
|
2610
|
+
|
|
2611
|
+
THE TARGET'S OWN READING IS NOT EVIDENCE. The question answered is whether
|
|
2612
|
+
everything ELSE implicates the target -- conditioning on its own state would
|
|
2613
|
+
return 1 or 0 by construction. When the target had a reading, the answer is
|
|
2614
|
+
stamped `target_reading_set_aside` and `checked.target_reading` says what
|
|
2615
|
+
the reading would have set (`faulty` or `clean`) and the target's own worst
|
|
2616
|
+
severity at any level. A supply in critical breach and the same supply
|
|
2617
|
+
healthy get one posterior between them; that is correct for the question,
|
|
2618
|
+
and until a later change nothing on the answer said which question it was.
|
|
2619
|
+
|
|
2620
|
+
An intervention on the target itself answers exactly: `do={t: v}` returns
|
|
2621
|
+
`v`, method `intervention`, and files nothing.
|
|
2589
2622
|
"""
|
|
2590
2623
|
if session.model is None:
|
|
2591
2624
|
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.
|
|
@@ -226,3 +226,8 @@ class CalendarHistory(ObservationHistory):
|
|
|
226
226
|
|
|
227
227
|
def get_observation_count(self, entity_id: str, property_name: str) -> int:
|
|
228
228
|
return self.inner.get_observation_count(entity_id, property_name)
|
|
229
|
+
|
|
230
|
+
def series_keys(self):
|
|
231
|
+
"""The series the history underneath holds -- a calendar changes which
|
|
232
|
+
instants count, never which series exist."""
|
|
233
|
+
return self.inner.series_keys()
|
|
@@ -135,6 +135,24 @@ class SqliteObservationHistory(ObservationHistory):
|
|
|
135
135
|
return [(_from_seconds(ts), text if text is not None else str(number))
|
|
136
136
|
for ts, number, text in rows]
|
|
137
137
|
|
|
138
|
+
def series_keys(self) -> List[Tuple[str, str]]:
|
|
139
|
+
"""Every ``(entity_id, property_name)`` this store holds a series for.
|
|
140
|
+
|
|
141
|
+
Not one of the ABC's five, and the in-memory store had it
|
|
142
|
+
from on -- so every reader that enumerates a history was
|
|
143
|
+
written against the default and this class was never asked. Two of
|
|
144
|
+
them crashed on it: `model_describe` and `gaps` raised AttributeError
|
|
145
|
+
for any session built over this store, and `rollout` ran with no
|
|
146
|
+
imagined past behind it, declining `precondition_unmet`. Nothing
|
|
147
|
+
downstream had constructed one until a vertical kept its readings
|
|
148
|
+
across runs, which is the one use this class exists for.
|
|
149
|
+
"""
|
|
150
|
+
with self._lock:
|
|
151
|
+
rows = self._connection.execute(
|
|
152
|
+
"SELECT DISTINCT entity_id, property FROM obs"
|
|
153
|
+
" ORDER BY entity_id, property").fetchall()
|
|
154
|
+
return [(str(entity_id), str(prop)) for entity_id, prop in rows]
|
|
155
|
+
|
|
138
156
|
def get_observations(self, entity_id: str, start: datetime,
|
|
139
157
|
end: datetime) -> List[Observation]:
|
|
140
158
|
with self._lock:
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 --
|
|
245
|
-
# choice that did not touch
|
|
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)})
|
|
@@ -48,7 +48,7 @@ import uuid
|
|
|
48
48
|
from collections import deque
|
|
49
49
|
from dataclasses import dataclass, field
|
|
50
50
|
from datetime import datetime, timedelta
|
|
51
|
-
from ..clock import as_naive_utc, now_utc
|
|
51
|
+
from ..clock import as_naive_utc, as_of, now_utc
|
|
52
52
|
# the id the yardstick is filed under, read from the module that
|
|
53
53
|
# owns it rather than re-spelled here. Two spellings of one literal is how
|
|
54
54
|
# the exclusion below would silently stop excluding anything.
|
|
@@ -698,18 +698,47 @@ class PredictionLedger:
|
|
|
698
698
|
property_name: str,
|
|
699
699
|
start: datetime,
|
|
700
700
|
end: datetime,
|
|
701
|
+
now: Optional[datetime] = None,
|
|
701
702
|
) -> List[Tuple[datetime, float]]:
|
|
702
|
-
"""Numeric observations for (entity, property) in [start, end],
|
|
703
|
-
|
|
703
|
+
"""Numeric observations for (entity, property) in [start, end], read
|
|
704
|
+
through each history's own `get_values` -- never its internals.
|
|
705
|
+
|
|
706
|
+
This read the in-memory store's private `_history` dict and
|
|
707
|
+
SKIPPED any history without one. `check` hands the ledger the session's
|
|
708
|
+
READING history, which is the raw store only when the model declares
|
|
709
|
+
neither a calendar nor a derived indicator; otherwise it is a view, and
|
|
710
|
+
so is the durable store. Measured, the same twenty minutes of one tank
|
|
711
|
+
filing a forecast a minute: the in-memory store graded 12 of 12; the
|
|
712
|
+
durable store, a declared calendar and a model with one derived
|
|
713
|
+
indicator graded NONE, all twelve `ungradeable`. A calibration figure
|
|
714
|
+
that could never be anything but empty, reported without an error --
|
|
715
|
+
beside a comment in `check` saying the view was passed so that a
|
|
716
|
+
derived forecast WOULD have something to score against.
|
|
717
|
+
|
|
718
|
+
`get_values` is the one reader every history implements, and it ends
|
|
719
|
+
at the engine's clock -- so the read is pinned to the GRADING instant,
|
|
720
|
+
`now`, which a caller may pass explicitly and which the private dict
|
|
721
|
+
never consulted. The window reaches back to `start`, a microsecond
|
|
722
|
+
wider because the stores bound it exclusively, and is then cut to
|
|
723
|
+
exactly `[start, end]`. A calendar view answers a window in OPEN time,
|
|
724
|
+
which reaches further back in wall time, and the cut is what makes
|
|
725
|
+
that harmless.
|
|
726
|
+
"""
|
|
704
727
|
out: List[Tuple[datetime, float]] = []
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
728
|
+
at = as_naive_utc(now) if now is not None else now_utc()
|
|
729
|
+
span = at - start + timedelta(microseconds=1)
|
|
730
|
+
if span.total_seconds() <= 0:
|
|
731
|
+
return out
|
|
732
|
+
with as_of(at):
|
|
733
|
+
for history in histories or []:
|
|
734
|
+
reader = getattr(history, "get_values", None)
|
|
735
|
+
if reader is None:
|
|
736
|
+
continue
|
|
737
|
+
for ts, value in reader(entity_id, property_name, span) or []:
|
|
738
|
+
if (isinstance(ts, datetime)
|
|
739
|
+
and isinstance(value, (int, float))
|
|
740
|
+
and start <= ts <= end):
|
|
741
|
+
out.append((ts, float(value)))
|
|
713
742
|
return out
|
|
714
743
|
|
|
715
744
|
def _grade_distribution_record(
|
|
@@ -743,7 +772,7 @@ class PredictionLedger:
|
|
|
743
772
|
return None
|
|
744
773
|
observations = self._observations_for(
|
|
745
774
|
list(histories), record.entity_id, record.indicator or "",
|
|
746
|
-
record.predicted_at, window_end)
|
|
775
|
+
record.predicted_at, window_end, now)
|
|
747
776
|
if not observations:
|
|
748
777
|
record.verdict = GRADE_UNGRADEABLE
|
|
749
778
|
record.graded_at = now
|
|
@@ -779,7 +808,7 @@ class PredictionLedger:
|
|
|
779
808
|
return None
|
|
780
809
|
observations = self._observations_for(
|
|
781
810
|
list(histories), record.entity_id, record.indicator or "",
|
|
782
|
-
record.predicted_at, window_end)
|
|
811
|
+
record.predicted_at, window_end, now)
|
|
783
812
|
target = record.predicted_at + timedelta(seconds=record.horizon_s)
|
|
784
813
|
# AND EACH READING GRADES ONE HORIZON, not every horizon
|
|
785
814
|
# whose window contains it. This took the observation closest to the
|
|
@@ -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
|