loopmath 0.3.0__tar.gz → 0.3.2__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.
- {loopmath-0.3.0 → loopmath-0.3.2}/PKG-INFO +43 -15
- {loopmath-0.3.0 → loopmath-0.3.2}/README.md +42 -14
- {loopmath-0.3.0 → loopmath-0.3.2}/pyproject.toml +1 -1
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/__init__.py +1 -1
- loopmath-0.3.2/src/loopmath/adapters/evolve.py +137 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/harness2.py +233 -17
- loopmath-0.3.2/src/loopmath/adapters/openevolve.py +305 -0
- loopmath-0.3.2/src/loopmath/adapters/plugin_eval.py +585 -0
- loopmath-0.3.2/src/loopmath/adapters/shinkaevolve.py +358 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/commands.py +17 -1
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/design.py +31 -1
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/fit.py +11 -0
- loopmath-0.3.2/src/loopmath/belief/integrated.py +139 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/lookahead.py +23 -2
- loopmath-0.3.2/src/loopmath/belief/sampled.py +416 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/state.py +79 -16
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/cli.py +75 -18
- loopmath-0.3.2/src/loopmath/cli_errors.py +121 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/cli_registry.py +272 -19
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/compare/__init__.py +188 -50
- loopmath-0.3.2/src/loopmath/compare/_thresholds_sw.py +439 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/compare/calibrate.py +16 -2
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/compare/calibration.py +16 -3
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/compare/checks.py +144 -16
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/compare/commands.py +97 -27
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/compare/model.py +283 -69
- loopmath-0.3.2/src/loopmath/compare/units.py +119 -0
- loopmath-0.3.2/src/loopmath/compare/value.py +564 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/gate/__init__.py +3 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/gate/commands.py +15 -1
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/gate/core.py +58 -15
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/gate/inputs.py +10 -6
- loopmath-0.3.2/src/loopmath/gate/winner.py +762 -0
- loopmath-0.3.2/src/loopmath/gate/winner_cli.py +141 -0
- loopmath-0.3.2/src/loopmath/gate/winner_model.py +305 -0
- loopmath-0.3.2/src/loopmath/harness_runs/commands.py +385 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/harness_runs/ocp.py +177 -37
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/harness_runs/rows.py +82 -14
- loopmath-0.3.2/src/loopmath/integrations/__init__.py +5 -0
- loopmath-0.3.2/src/loopmath/integrations/dspy.py +634 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/output.py +5 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/commands.py +13 -0
- loopmath-0.3.2/src/loopmath/recommend_role.py +356 -0
- loopmath-0.3.2/src/loopmath/screen/__init__.py +13 -0
- loopmath-0.3.2/src/loopmath/screen/commands.py +416 -0
- loopmath-0.3.2/src/loopmath/screen/design.py +97 -0
- loopmath-0.3.2/src/loopmath/screen/edits.py +169 -0
- loopmath-0.3.2/src/loopmath/screen/model.py +356 -0
- loopmath-0.3.2/src/loopmath/screen/record.py +250 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/budget.py +40 -6
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/commands.py +42 -17
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/config.py +8 -3
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/record.py +6 -2
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/status.py +5 -4
- loopmath-0.3.2/src/loopmath/tasks/__init__.py +13 -0
- loopmath-0.3.2/src/loopmath/tasks/commands.py +231 -0
- loopmath-0.3.2/src/loopmath/tasks/data.py +176 -0
- loopmath-0.3.2/src/loopmath/tasks/fit.py +225 -0
- loopmath-0.3.2/src/loopmath/tasks/posterior.py +196 -0
- loopmath-0.3.2/src/loopmath/tasks/select.py +211 -0
- loopmath-0.3.2/src/loopmath/tasks/sim.py +34 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/types.py +7 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/common.py +14 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/posterior.py +5 -1
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/runs.py +7 -4
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath.egg-info/PKG-INFO +43 -15
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath.egg-info/SOURCES.txt +33 -1
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_cli_graph.py +1 -1
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_research_cli.py +2 -1
- loopmath-0.3.2/tests/test_v031_help_labels.py +125 -0
- loopmath-0.3.2/tests/test_v031_json_errors_and_costs.py +202 -0
- loopmath-0.3.2/tests/test_v031_new_findings.py +132 -0
- loopmath-0.3.0/src/loopmath/harness_runs/commands.py +0 -215
- {loopmath-0.3.0 → loopmath-0.3.2}/LICENSE +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/setup.cfg +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/spec/ocp-v0.2.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/spec/ocp-v0.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/spec/ocp_conformance.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/__main__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapter_host.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/atrium.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/base.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/bb.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/bb_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/omp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/omp_facts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/omp_links.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/omp_parse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/omp_types.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/opencode.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/opencode_store.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/orca.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/otel_genai.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/otel_genai_graph.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/otel_genai_parse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/otel_genai_types.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/otel_genai_usage.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/paseo.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/pi.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/pi_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/adapters/registry.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/block.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/check_pymc.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/compose.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/features.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/forest.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/gaussian.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/outcome.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/pricing.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/priors.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/belief/rules.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/builder/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/builder/cli.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/builder/context.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/builder/predict.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/builder/server.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/cli_graph.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/cli_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/compare/_thresholds.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/e0/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/e0/arms.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/e0/cli_e0.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/e0/estimate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/e0/estimate_rework.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/e0/figures.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/e0/io.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/e0/verb.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/explain.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/fit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/fit_assembly.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/fit_bayes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/fit_masks.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/fit_predict.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/fit_pricing.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/fit_transfer.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/gaps.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/gitwalk.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/grade.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/grade_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/artifact_git.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/artifact_join.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/artifact_kinds.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/artifacts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/bashparse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/bashread_ops.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/bashwrite_ops.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/bashwrites.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/cache.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/codexio.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/codexio_parse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/codexio_scan.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/dataset.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/dataset_build.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/dataset_core.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/dataset_edges.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/dataset_nodes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/extract.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/html_common.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/html_costcurve.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/html_css.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/html_data.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/html_force.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/html_render.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/html_swimlanes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_cli.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_common.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_parse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_pricing.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_build.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_dispatch.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_evidence.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_node.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_records.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_result.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_session.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_tools.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_validation.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_prompt_workflow.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_report.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labeler_score.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/labels.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/launch.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/launch_origin.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/ocp_emit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/ocp_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/render.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/runfile.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/runfile_export.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/runfile_finish.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/scan.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/schema.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/graph/token_completeness.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/harness/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/harness/layouts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/harness_runs/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/harness_runs/costs.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/harness_runs/streams.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/base.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/claude_code.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/claude_code_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/codex.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/codex_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/ocp_analysis.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/ocp_artifact.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/ocp_common.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/ocp_convert.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ingest/ocp_projection.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/logmatch/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/logmatch/artifacts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/logmatch/clip.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/logmatch/codex_window.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/logmatch/costs.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/logmatch/match.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/logmatch/settle.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/logmatch/tariff.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/canonical.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/conformance.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/contractv3.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/emit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/graph_contract.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/membership.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/migrate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/schema/ocp-v0.2.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/schema/ocp-v0.3.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/schema/ocp-v0.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/ocp/version.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/onboard/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/onboard/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/onboard/history.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/onboard/label.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/onboard/usual.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/pool.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/price.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/price_validation.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/prices.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/benchmarks.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/benchmarks.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/build.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/bundle/README.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/bundle/e0.jsonl.gz +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/bundle/lanes.jsonl.gz +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/bundle/manifest.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/bundle/rq1.jsonl.gz +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/bundle/sweep.jsonl.gz +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/e0.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/history.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/lanes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/ocpdoc.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/reduce.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/registry.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/rq1.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/show.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/sweep.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/priors/validate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/receipts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/backlog.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/curve.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/engine.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/gain.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/message.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/receipts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/search.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/stats.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/storeread.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/recommend/variants.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/report/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/report/eligibility.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/report/html.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/report/terminal.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/report/terminal_exclusions.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/report/terminal_format.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/report/terminal_read.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/research_defaults.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/research_paths.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/scoring.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/scoring_comparisons.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/scoring_predictive.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/share/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/share/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/share/export.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/share/import_.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/doctor.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/install.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/skills/loopmath/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/skills/loopmath-import-runs/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/skills/loopmath-onboard/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/skills/loopmath-plan-task/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/skills/loopmath-record-run/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/skills/loopmath-update-fit/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/skill/skills/reference.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/finish.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/fitjob.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/home.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/ids.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/lock.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/report.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/rule_change.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/run_names.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/runs.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/store/verify.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/surface.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/surface_bootstrap.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/surface_estimation.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/surface_frame.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/surface_presentation.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/surface_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/taskmodel.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/README.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/base.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/builder.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/builder.html +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/builder.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/common.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/estimates.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/estimates.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/graph.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/graph.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/plans.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/plans.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/results.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/results.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/runs.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/viz.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/assets/viz.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/views/plans.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/candidates.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/catalog/best_of_n.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/catalog/implement_review.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/catalog/plan_implement.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/catalog/plan_implement_review.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/catalog/solo.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/catalog/swarm.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/diff.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/format.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/graphview.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/ids.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/infer.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/models.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath/workflows/shapes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath.egg-info/dependency_links.txt +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath.egg-info/entry_points.txt +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath.egg-info/requires.txt +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/src/loopmath.egg-info/top_level.txt +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_atrium.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_bb.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_harness2.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_omp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_opencode.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_opencode_output_tokens.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_orca.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_otel_genai.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_paseo.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapter_services.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapters.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_adapters_pi.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_analyze_cli.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_arms.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_cli_help_lists.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_cli_labeler.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_cli_main_module.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_cli_plan_hidden.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_cli_prices.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_cli_startup.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_color_proof.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_duplicate_guards.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_estimate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_explain.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_fit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_gaps.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_gitwalk.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_grade.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_artifact_recording.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_artifacts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_bashwrites.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_cache.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_codexio.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_dataset.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_extract.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_glue.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_html.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_labeler.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_labeler_evidence_matrix.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_launch.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_render.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_runfile.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_graph_schema.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_home_guard.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_html.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_ingest.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_ingest_claude_code.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_ingest_codex.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_ingest_ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_io.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_not_run_summary.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_ocp_conformance.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_ocp_golden.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_ocp_open_enums.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_ocp_versioning.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_output_html_path.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_packaging_metadata.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_packaging_smoke.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_pool.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_preview_script.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_price.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_receipts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_release_paths.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_report_eligibility.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_runfile_privacy_n1b.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_scoring.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_split_rebindings.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_starting_prior.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_surface.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_taskmodel_features.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_terminal.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_types_control.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_types_piece_prediction.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_v01_scaffold.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_v027_p3.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_v027_small_fixes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.2}/tests/test_v028_sol_6_1.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loopmath
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Plan agent workflows, record how they went, and learn from your own Claude Code and Codex logs
|
|
5
5
|
Author: QKV Labs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -174,14 +174,19 @@ The workflow builder is the one page that is served: `loopmath builder --type bu
|
|
|
174
174
|
| Group | Commands |
|
|
175
175
|
|---|---|
|
|
176
176
|
| Plan | `task-types`, `workflows` (list, show, validate, diff), `recommend`, `builder` |
|
|
177
|
-
| Record | `run` (start, record, import, attempt, artifact, finish, sessions), `outcome`, `budget`, `config` (get, set) |
|
|
177
|
+
| Record | `run` (start, record, import, attempt, artifact, finish, sessions), `outcome`, `budget`, `config` (get, set, unset) |
|
|
178
178
|
| Learn | `fit`, `onboard`, `share`, `prior` |
|
|
179
179
|
| View | `runs`, `posterior`, `status`, `report`, `doctor`, `verify-receipts` |
|
|
180
|
+
| Harness testing | `check-change`, `gate` (experimental), `screen` (experimental) |
|
|
181
|
+
| Task subsets | `tasks pick` (experimental) |
|
|
182
|
+
| Evolution loops | `adapt openevolve` (experimental), `adapt shinkaevolve` (experimental), `recommend --role` (experimental) |
|
|
180
183
|
| Skill | `skill` (install, uninstall, show) |
|
|
181
184
|
| OCP | `ocp` (validate, migrate) |
|
|
182
|
-
| Log analysis | `analyze`, `graph`, `adapt`, `prices`, `validate-prices
|
|
185
|
+
| Log analysis | `analyze`, `graph`, `adapt`, `prices`, `validate-prices` |
|
|
183
186
|
|
|
184
|
-
|
|
187
|
+
These are experimental: `gate`, `screen`, `tasks pick`, the harness-part estimates (`recommend --harness-variants` and `--harness`, `posterior --level harness` and its `factors`), `recommend --role`, `adapt harness2`, `adapt plugin-eval`, `adapt openevolve`, `adapt shinkaevolve` and the ADE adapters (`adapt NAME`). Each works and has tests, but has had little use on real work so far, and its output may change. Their help says so too. The gate at DSPy's GEPA and MIPROv2 accept step (`loopmath.integrations.dspy`, Python) is experimental too: it is tested against fakes written from their source, not yet with DSPy installed, and its guide says so. So is the opt-in cost model for arms that mix models, `loopmath.compare.model.COST_MODEL = "sw"`, a Python setting with no help of its own; the [changelog](CHANGELOG.md) describes it.
|
|
188
|
+
|
|
189
|
+
Each command prints a short summary by default. With `--json`, it prints exactly one JSON object instead. One exception: `loopmath adapt harness2`, `adapt harness-runs`, `adapt plugin-eval`, `adapt openevolve` and `adapt shinkaevolve` without `--out` print the run documents on stdout, one per line, so their `--json` summary goes to stderr. Exit codes: 0 ok, 1 user error, 2 not found, 3 not implemented, 4 store locked, 5 no fit yet. For flags, `loopmath <command> --help` is the source of truth. Commands, pages and the `runs` table show times in your local time with the zone; the store and `--json` keep UTC. A cost under a cent shows one significant digit (`$0.004`), and an exact zero is `$0`.
|
|
185
190
|
|
|
186
191
|
A workflow is a TOML file. `loopmath workflows list` shows the catalog and yours, which live in `~/.loopmath/workflows/`. It also lists the shapes loopmath composes from catalog pieces, such as `team`; `loopmath workflows show team` says what one is and that `recommend` does not offer it. This one implements, then sends a rejected change back once or twice:
|
|
187
192
|
|
|
@@ -255,21 +260,44 @@ A run can record the harness it ran with: the prompt files, skills, plugins, MCP
|
|
|
255
260
|
`cost` is the multiplier on an attempt's dollars when the part is present; `success` is percentage points.
|
|
256
261
|
- `loopmath recommend --harness-variants` prices the trees your runs used most, and `--harness PATH` prices one more, against the bare harness label on the same draws.
|
|
257
262
|
|
|
258
|
-
An estimate is the change when a named part is present, pooled over its versions. It is not the effect of one edit, and a part on every run cannot be told apart from its harness. The cost ranges met their stated coverage in simulation. **The success ranges did not:** they pull effects toward 0, and their 80% ranges held 60% of nonzero effects. So they are marked `calibrated: false`, and so are the success-side numbers of a priced variant. With no record anywhere, every number is what it was before. The record, the digest, the factor map and the limits are in [`spec/harness.md`](spec/harness.md).
|
|
263
|
+
An estimate is the change when a named part is present, pooled over its versions. It is not the effect of one edit, and a part on every run cannot be told apart from its harness. The cost ranges met their stated coverage in simulation. **The success ranges did not:** they pull effects toward 0, and their 80% ranges held 60% of nonzero effects. So they are marked `calibrated: false`, and so are the success-side numbers of a priced variant, and the harness-part estimates are experimental. With no record anywhere, every number is what it was before. The record, the digest, the factor map and the limits are in [`spec/harness.md`](spec/harness.md).
|
|
259
264
|
|
|
260
265
|
## Testing a harness change
|
|
261
266
|
|
|
262
267
|
These commands are for changing the harness your agents run with and finding out whether the change paid off.
|
|
263
268
|
|
|
264
269
|
- **Harness parts and `--harness`.** Record the tree each run used (`run finish --harness auto` or `PATH`), and the fit learns each named part's effect, as in the section above. `recommend --harness-variants` prices the trees your runs used on the configurations those runs used, and on the recommendation's own when their harness has data on the parts; `--harness PATH` prices one more tree. Where a tree is not priced, `not_priced` in `--json` says why. The record, the factor map and the limits are in [`spec/harness.md`](spec/harness.md).
|
|
265
|
-
- **`adapt harness-runs` and `adapt harness2`.** These bring in runs another tool made. `loopmath adapt harness-runs FILE --out DIR` reads a `loopmath.harness-runs/0.1` file, one JSON line per rollout, as an auto-research loop, a plugin eval suite or a hand A/B writes it. `loopmath adapt harness2 RUNS --out DIR` reads a harness2 runs folder. Then `loopmath run import DIR --finish` stores the runs. The row format, outcomes, costs and worked examples are in [`spec/harness-runs.md`](spec/harness-runs.md).
|
|
270
|
+
- **`adapt harness-runs` and `adapt harness2`.** These bring in runs another tool made; `adapt harness2` is experimental, since its input format is read from harness2's code and it has not yet read a real runs folder. `loopmath adapt harness-runs FILE --out DIR` reads a `loopmath.harness-runs/0.1` file, one JSON line per rollout, as an auto-research loop, a plugin eval suite or a hand A/B writes it. `loopmath adapt harness2 RUNS --out DIR` reads a harness2 runs folder. Then `loopmath run import DIR --finish` stores the runs. The row format, outcomes, costs and worked examples are in [`spec/harness-runs.md`](spec/harness-runs.md).
|
|
266
271
|
- **`check-change`.** It decides whether a change beats what it replaces, on paired runs:
|
|
267
|
-
- `check-change start --base LABEL|PATH --cand LABEL|PATH --n N` plans the pairs and each pair's arm order;
|
|
268
|
-
- `check-change status` gives the comparison, the verdict once there is one (adopt or reject) and the next pair worth running;
|
|
272
|
+
- `check-change start --base LABEL|PATH --cand LABEL|PATH --n N` plans the pairs and each pair's arm order; `--base-set PIECE=HARNESS:MODEL:EFFORT` and `--cand-set` give each arm a setting, as `run record --set` takes it, for a model switch on one harness: the arm's runs take its set, a run with another is refused, and `status` names the models each arm ran and any run whose log ran another;
|
|
273
|
+
- `check-change status` gives the comparison, the verdict once there is one (adopt or reject) and the next pair worth running. A check that reaches its cap or spends its budget before the evidence decides keeps the base and says `not decided`. The next pair's value and price are shown at each look and stop nothing; `--value-stop` also stops a check, not decided, when the next pairs are certified not worth their price (every plan's net below 0 by more than two standard errors, or no look left can change the action), from 4 scored pairs on (`--min-pairs K`). Use it when a pair is dear against what an adoption is worth over `--horizon-runs`: in simulated checks at the default 50 future runs it saved 6 to 16 pairs a check but dropped 26 to 40 percent of the adoptions of real cuts, and at 500 it dropped none (`check-change start --help`); `--pair-price 0` compares the value with 0 for runs on a subscription, and the budget still counts the runs' recorded dollars; `--json` keeps `verdict: reject` there, with `comparison.stop` and `comparison.decided: false`;
|
|
269
274
|
- `check-change from-runs --base-label SEL --cand-label SEL` compares finished runs already in the store, picked by a run label, a configuration id, a harness digest (a prefix of one digest will do) or `arm:NAME`.
|
|
270
275
|
|
|
271
|
-
`--rule` names what counts as accepted, and `loopmath check-change --help` lists the rest. A run with no dollar cost leaves the decision to the outcomes alone, and the cost reads unknown; a budget or `--objective cpar` then refuses, naming the runs without one.
|
|
272
|
-
- **`
|
|
276
|
+
`--rule` names what counts as accepted, and `loopmath check-change --help` lists the rest. A run with no dollar cost leaves the decision to the outcomes alone, and the cost reads unknown; a budget or `--objective cpar` then refuses, naming the runs without one. When an arm's runs mix a cheap model and a dear one, the default cost model can put the dearer arm's cost per run below the cheaper arm's. In Python, the experimental setting `loopmath.compare.model.COST_MODEL = "sw"` reads such costs for `compare.paired` and `gate.decide`; the [changelog](CHANGELOG.md) gives its limits.
|
|
277
|
+
- **`screen` (experimental).** It tests many harness edits at once. Put each edit in its own subfolder of an edits folder, laid over a base harness tree: `PATH` copies a file, `PATH.append` appends to one, `PATH.remove` removes lines. Then:
|
|
278
|
+
- `screen start --base DIR --edits DIR --tasks FILE.jsonl --rule tests` plans one fold-over pair per task: arm A runs the edits its row turns on, arm B the others, so task difficulty cancels in the pair and every session informs every edit. 12, 16, 20, 24 or 32 pairs for up to one edit fewer give orthogonal rows;
|
|
279
|
+
- `screen next` prints the next pair, with both arms' trees built in the store; `run record --screen SCR --arm A|B` records a run into it;
|
|
280
|
+
- `screen status` ranks the edits on success and cost with 80% ranges and P(helps), names the predicted-best subset with its predicted cut in cost per accepted result (often a subset no arm ran), and prices the next pair. `--on EDIT,EDIT` predicts any subset;
|
|
281
|
+
- `screen apply --best --out DIR` writes the base with the best edits, ready for a `check-change` against the base.
|
|
282
|
+
|
|
283
|
+
The model is a Bayesian linear model of each pair's difference, on success and on log cost, with its noise and effect scales weighed over a grid rather than plugged in. A finished orthogonal screen weighs two views, a few edits matter (sparse) or all matter a little (dense), by how well each explains the pairs, and `best.by_view` shows both. On a dozen pairs each edit's effect is shrunk toward 0 (a large one by about half with 8 edits in 12 pairs), but in simulations at that size, of 1,000 screens drawn from effect settings no tuning used, 964 picked a best subset and its 80% range held the true cut in 812 (84%), a few points high where the true cut was small and low where it was large; the other 36 picked no edit, which predicts no change. The prediction is against the base, which no arm runs, so an effect of having the edited files at all is not in it. The [screen guide](docs/screen.md) has a worked example and the limits.
|
|
284
|
+
- **`gate` (experimental).** For a loop that adopts changes on its own, it answers: adopt the candidate, reject it and keep the base, or replicate N more pairs first. `loopmath gate --base SEL --cand SEL` reads the finished runs in the store, or `--runs FILE` reads `loopmath.harness-runs/0.1` rows; an arm is a harness digest (`sha256:<hex>` or `digest:sha256:<hex>`, a prefix of one digest will do), a configuration id, or `arm=NAME` or `arm:NAME`; `check-change from-runs` takes the same forms. `--rule` takes the built-in rules or a rule defined under `[rules.NAME]` in the store's config, which the gate reads as `check-change` does, also with `--runs`. The comparison of the paired runs is `check-change`'s, and the gate adds the loop's policy to it: no adoption before `--r-min` pairs, looks at 1, 2, 4 and so on pairs up to `--r-max`, a `--floor` (the smallest gain worth adopting, 0 or more; under an acceptance rule it applies to the cut in cost per accepted result, a fraction, or to the gain in the chance of acceptance when a cost is unknown), and the dollars, `--budget-usd` for both arms (0 buys no further pair) and `--max-cost-ratio`. With no evidence, an arm that matches no run or a rule that scores no pair, it exits 1 and names the arms or the rules that are there. Both dollar flags need every run's cost and refuse, naming the runs without one; with neither flag, a run with no recorded cost leaves the decision to the outcomes alone, and the cost reads unknown, never $0. When nothing resolves within the cap or the budget, it keeps the base and says where it stopped. The next pairs' value is the one `check-change` states, valued on the way the gate goes on (its looks, `--r-min` and the 95% band), and named beside the number; `check-change from-runs` cannot express the gate's looks, so on the same runs the two numbers can differ. The value stop is on by default: from `--min-pairs` scored pairs (default 4) the gate rejects, keeping the base, when the next pairs are certified not worth their price, as `check-change --value-stop` does, and a cost that is not finite never stops it; `--no-value-stop` turns it off, and the value and its price are then shown and stop nothing. Under a score rule the value needs `--value-usd` (dollars per point of gain), as in `check-change`. In Python, `loopmath.gate.decide(base_runs, cand_runs, GatePolicy(...))` gives the same answer and writes nothing; `loopmath gate --help` lists the rest.
|
|
285
|
+
- **`gate winner` (experimental).** A search loop that scores N candidates and keeps the best overstates the winner's gain: the best of N noisy scores is high partly by luck. `loopmath gate winner --scores FILE` reads every score the search made (a CSV or `loopmath.gate-winner-scores/0.1` JSON; candidates known only as no better than the best so far count too) and gives the winner's gain over its parent, shrunk by what the scores say about the candidates' spread and the noise, with 80% and 95% ranges and P(beats parent). `--runs FILE` reads harness-runs rows instead, one arm per candidate, with the spread from their replicates. Give the spread of one evaluation across reruns with `--spread X`, or an assumed range with `--spread LO:HI`. The action is `gate`'s on confirmatory runs of the parent and the winner (`--confirm FILE`), never on the search's own scores, and a projection says how likely the gate is to adopt and how many more pairs it will want. In Python, `loopmath.gate.winner.assess`; the method, the formats and the output are in [spec/gate-winner.md](spec/gate-winner.md), with worked examples from published searches in `spec/examples/gate-winner/`.
|
|
286
|
+
- **DSPy's GEPA and MIPROv2 (experimental).** An optimizer's accept step keeps a new prompt when its score on a few instances goes up, and on 3 instances that keeps many children that are no better. `loopmath.integrations.dspy` puts the gate there: `GEPAAcceptance` is a GEPA acceptance criterion (run GEPA with `use_merge=False`), `MIPROv2Gate` confirms MIPROv2's replacements, and `run([], [], more)` gates any compiled program against its student. It imports neither dspy nor gepa. See [the DSPy guide](docs/dspy.md).
|
|
287
|
+
- **`tasks pick` (experimental).** For a loop or a benchmark run that scores each candidate on some of a suite's tasks instead of all of them. `loopmath tasks pick --candidate SEL` reads the finished runs in the store, or `--runs FILE`, learns each task's difficulty and discrimination from the other arms' runs on it, and prints the candidate's suite score so far (the mean over the suite of its chance of acceptance per task) with 80% and 95% ranges, whether to stop, and the next task to run. From a store it writes that task's file and the `run record --task-file F --label KEY=VALUE --home PATH` line that records the run in the same store, so the next call sees it. It stops when the 80% range is as narrow as one full run of the suite would give (`--halfwidth` sets another target), or, with `--versus X` or `--versus-arm SEL`, when the score is above or below a number or another arm's at `--level`. `--method` chooses the next task: the largest expected cut in the score's variance (`loss`, the default), information, one fixed order, random, or stratified by predicted rate. The default learns fastest when the candidate behaves like the arms in the history; for a candidate unlike all of them (a new model rather than a change to a known one's harness), `--method stratified` or `random` keeps the ranges closer to honest at the cost of more sessions. A candidate that scores below every arm in the history (a broken setup, say) is estimated too high by every method, and its ranges can miss even at the full suite; under `--method random` the accepted count over the sessions, which every call prints, is the check. Every estimate prints both warnings, in text and in `--json` (`estimate.warnings`). With `--versus`, a bar within about 0.01 of the candidate's score is close to a coin toss for any method; to ask whether a change helps by at least some margin, pass the bar plus that margin. The ranges are approximate: they leave out the history fit's own error shared with other arms. `loopmath tasks pick --help` lists the rest.
|
|
288
|
+
|
|
289
|
+
## Does my plugin pay?
|
|
290
|
+
|
|
291
|
+
`claude plugin eval` grades a plugin's eval cases with and without the plugin. `adapt plugin-eval` (experimental) reads its result, and loopmath turns the per-case deltas into a decision: adopt, reject, or run more, with ranges on the gain and, given each run's cost with `--cost-from`, on cost per accepted result. It reads the file only and never runs Claude Code.
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
loopmath adapt plugin-eval result.json --out ocp --rows rows.jsonl
|
|
295
|
+
loopmath run import ocp --finish
|
|
296
|
+
loopmath check-change from-runs --base-label arm:without --cand-label arm:with
|
|
297
|
+
loopmath gate --base arm:without --cand arm:with --runs rows.jsonl --rule accept --r-max 24
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Runs of one case are not new tasks: to learn whether the plugin helps on tasks like your cases, add cases rather than runs. With grades (`--outcome score`), `check-change from-runs` compares case by case. The import's summary prints the last two filled in for your suite. Which cases are compared, what the suite's one cost can and cannot say, how far the answer reaches and the provisional input layout are in [`docs/plugin-eval.md`](docs/plugin-eval.md).
|
|
273
301
|
|
|
274
302
|
## The store and privacy
|
|
275
303
|
|
|
@@ -302,16 +330,16 @@ loopmath graph --workspace NAME --format html --out graph.html
|
|
|
302
330
|
|
|
303
331
|
`loopmath prices` lists the packaged prices with the source of each. A rate marked "rate not confirmed" is a best public-price guess, and every dollar figure priced with one says so.
|
|
304
332
|
|
|
305
|
-
`graph` rebuilds which agent launched which, and which session read what another wrote. Its formats are `ocp`, `json`, `dot`, `run` and `html`. `graph --out FILE` and `adapt NAME --out FILE` write to any path you name: missing folders are created, an existing file is replaced, and a directory is refused. `graph` prints a short summary on stderr; `--verbose` adds every count of what was skipped or could not be placed. See the [workflow graph guide](docs/graph.md) and [adapters](docs/adapters.md).
|
|
306
|
-
|
|
307
|
-
## Research data
|
|
308
|
-
|
|
309
|
-
`loopmath research --help` lists the verbs that read research data folders, which are not part of the package: `research fit` and `research transfer-test` read sweep run records, and `research analyze-e0` reads a session corpus. Name the folders with a flag (`--sweep-dir`, `--corpus`), an environment variable (`LOOPMATH_SWEEP_DIR`, `LOOPMATH_E0_CORPUS`), or config (`loopmath config set research.sweep_dir PATH`, `research.e0_corpus`). `research fit` and `research transfer-test` need the `bayes` extra (`pip install 'loopmath[bayes]'`), and `research analyze-e0` the `e0` extra. `loopmath prior build --out DIR` rebuilds the packaged prior bundle from research data folders like these; `loopmath prior build --help` names them.
|
|
333
|
+
`graph` rebuilds which agent launched which, and which session read what another wrote. Its formats are `ocp`, `json`, `dot`, `run` and `html`. `graph --out FILE` and `adapt NAME --out FILE` write to any path you name: missing folders are created, an existing file is replaced, and a directory is refused. `graph` prints a short summary on stderr; `--verbose` adds every count of what was skipped or could not be placed. See the [workflow graph guide](docs/graph.md) and [adapters](docs/adapters.md). The ADE adapters (`adapt NAME`) are experimental: each is tested on a synthetic fixture written from its tool's format, not yet on a real store.
|
|
310
334
|
|
|
311
335
|
## More
|
|
312
336
|
|
|
313
337
|
- [OCP, the run format](spec/OCP.md): the normative note that goes with the [schemas](spec/), and the [versioning promise](spec/VERSIONING.md)
|
|
338
|
+
- [DSPy's GEPA and MIPROv2](docs/dspy.md): `loopmath.integrations.dspy`, the gate at their accept step
|
|
339
|
+
- [Plugin eval results](docs/plugin-eval.md): `adapt plugin-eval`
|
|
340
|
+
- [OpenEvolve and ShinkaEvolve runs](docs/adapters.md#openevolve-and-shinkaevolve): `adapt openevolve`, `adapt shinkaevolve` and `recommend --role`, which model to give an evolution loop's mutator
|
|
314
341
|
- [Release policy](docs/release.md)
|
|
342
|
+
- [Screening harness edits](docs/screen.md): `screen`, with a worked 48-session example and its limits
|
|
315
343
|
- [Packaged model prices](src/loopmath/prices.toml)
|
|
316
344
|
|
|
317
345
|
## License
|
|
@@ -136,14 +136,19 @@ The workflow builder is the one page that is served: `loopmath builder --type bu
|
|
|
136
136
|
| Group | Commands |
|
|
137
137
|
|---|---|
|
|
138
138
|
| Plan | `task-types`, `workflows` (list, show, validate, diff), `recommend`, `builder` |
|
|
139
|
-
| Record | `run` (start, record, import, attempt, artifact, finish, sessions), `outcome`, `budget`, `config` (get, set) |
|
|
139
|
+
| Record | `run` (start, record, import, attempt, artifact, finish, sessions), `outcome`, `budget`, `config` (get, set, unset) |
|
|
140
140
|
| Learn | `fit`, `onboard`, `share`, `prior` |
|
|
141
141
|
| View | `runs`, `posterior`, `status`, `report`, `doctor`, `verify-receipts` |
|
|
142
|
+
| Harness testing | `check-change`, `gate` (experimental), `screen` (experimental) |
|
|
143
|
+
| Task subsets | `tasks pick` (experimental) |
|
|
144
|
+
| Evolution loops | `adapt openevolve` (experimental), `adapt shinkaevolve` (experimental), `recommend --role` (experimental) |
|
|
142
145
|
| Skill | `skill` (install, uninstall, show) |
|
|
143
146
|
| OCP | `ocp` (validate, migrate) |
|
|
144
|
-
| Log analysis | `analyze`, `graph`, `adapt`, `prices`, `validate-prices
|
|
147
|
+
| Log analysis | `analyze`, `graph`, `adapt`, `prices`, `validate-prices` |
|
|
145
148
|
|
|
146
|
-
|
|
149
|
+
These are experimental: `gate`, `screen`, `tasks pick`, the harness-part estimates (`recommend --harness-variants` and `--harness`, `posterior --level harness` and its `factors`), `recommend --role`, `adapt harness2`, `adapt plugin-eval`, `adapt openevolve`, `adapt shinkaevolve` and the ADE adapters (`adapt NAME`). Each works and has tests, but has had little use on real work so far, and its output may change. Their help says so too. The gate at DSPy's GEPA and MIPROv2 accept step (`loopmath.integrations.dspy`, Python) is experimental too: it is tested against fakes written from their source, not yet with DSPy installed, and its guide says so. So is the opt-in cost model for arms that mix models, `loopmath.compare.model.COST_MODEL = "sw"`, a Python setting with no help of its own; the [changelog](CHANGELOG.md) describes it.
|
|
150
|
+
|
|
151
|
+
Each command prints a short summary by default. With `--json`, it prints exactly one JSON object instead. One exception: `loopmath adapt harness2`, `adapt harness-runs`, `adapt plugin-eval`, `adapt openevolve` and `adapt shinkaevolve` without `--out` print the run documents on stdout, one per line, so their `--json` summary goes to stderr. Exit codes: 0 ok, 1 user error, 2 not found, 3 not implemented, 4 store locked, 5 no fit yet. For flags, `loopmath <command> --help` is the source of truth. Commands, pages and the `runs` table show times in your local time with the zone; the store and `--json` keep UTC. A cost under a cent shows one significant digit (`$0.004`), and an exact zero is `$0`.
|
|
147
152
|
|
|
148
153
|
A workflow is a TOML file. `loopmath workflows list` shows the catalog and yours, which live in `~/.loopmath/workflows/`. It also lists the shapes loopmath composes from catalog pieces, such as `team`; `loopmath workflows show team` says what one is and that `recommend` does not offer it. This one implements, then sends a rejected change back once or twice:
|
|
149
154
|
|
|
@@ -217,21 +222,44 @@ A run can record the harness it ran with: the prompt files, skills, plugins, MCP
|
|
|
217
222
|
`cost` is the multiplier on an attempt's dollars when the part is present; `success` is percentage points.
|
|
218
223
|
- `loopmath recommend --harness-variants` prices the trees your runs used most, and `--harness PATH` prices one more, against the bare harness label on the same draws.
|
|
219
224
|
|
|
220
|
-
An estimate is the change when a named part is present, pooled over its versions. It is not the effect of one edit, and a part on every run cannot be told apart from its harness. The cost ranges met their stated coverage in simulation. **The success ranges did not:** they pull effects toward 0, and their 80% ranges held 60% of nonzero effects. So they are marked `calibrated: false`, and so are the success-side numbers of a priced variant. With no record anywhere, every number is what it was before. The record, the digest, the factor map and the limits are in [`spec/harness.md`](spec/harness.md).
|
|
225
|
+
An estimate is the change when a named part is present, pooled over its versions. It is not the effect of one edit, and a part on every run cannot be told apart from its harness. The cost ranges met their stated coverage in simulation. **The success ranges did not:** they pull effects toward 0, and their 80% ranges held 60% of nonzero effects. So they are marked `calibrated: false`, and so are the success-side numbers of a priced variant, and the harness-part estimates are experimental. With no record anywhere, every number is what it was before. The record, the digest, the factor map and the limits are in [`spec/harness.md`](spec/harness.md).
|
|
221
226
|
|
|
222
227
|
## Testing a harness change
|
|
223
228
|
|
|
224
229
|
These commands are for changing the harness your agents run with and finding out whether the change paid off.
|
|
225
230
|
|
|
226
231
|
- **Harness parts and `--harness`.** Record the tree each run used (`run finish --harness auto` or `PATH`), and the fit learns each named part's effect, as in the section above. `recommend --harness-variants` prices the trees your runs used on the configurations those runs used, and on the recommendation's own when their harness has data on the parts; `--harness PATH` prices one more tree. Where a tree is not priced, `not_priced` in `--json` says why. The record, the factor map and the limits are in [`spec/harness.md`](spec/harness.md).
|
|
227
|
-
- **`adapt harness-runs` and `adapt harness2`.** These bring in runs another tool made. `loopmath adapt harness-runs FILE --out DIR` reads a `loopmath.harness-runs/0.1` file, one JSON line per rollout, as an auto-research loop, a plugin eval suite or a hand A/B writes it. `loopmath adapt harness2 RUNS --out DIR` reads a harness2 runs folder. Then `loopmath run import DIR --finish` stores the runs. The row format, outcomes, costs and worked examples are in [`spec/harness-runs.md`](spec/harness-runs.md).
|
|
232
|
+
- **`adapt harness-runs` and `adapt harness2`.** These bring in runs another tool made; `adapt harness2` is experimental, since its input format is read from harness2's code and it has not yet read a real runs folder. `loopmath adapt harness-runs FILE --out DIR` reads a `loopmath.harness-runs/0.1` file, one JSON line per rollout, as an auto-research loop, a plugin eval suite or a hand A/B writes it. `loopmath adapt harness2 RUNS --out DIR` reads a harness2 runs folder. Then `loopmath run import DIR --finish` stores the runs. The row format, outcomes, costs and worked examples are in [`spec/harness-runs.md`](spec/harness-runs.md).
|
|
228
233
|
- **`check-change`.** It decides whether a change beats what it replaces, on paired runs:
|
|
229
|
-
- `check-change start --base LABEL|PATH --cand LABEL|PATH --n N` plans the pairs and each pair's arm order;
|
|
230
|
-
- `check-change status` gives the comparison, the verdict once there is one (adopt or reject) and the next pair worth running;
|
|
234
|
+
- `check-change start --base LABEL|PATH --cand LABEL|PATH --n N` plans the pairs and each pair's arm order; `--base-set PIECE=HARNESS:MODEL:EFFORT` and `--cand-set` give each arm a setting, as `run record --set` takes it, for a model switch on one harness: the arm's runs take its set, a run with another is refused, and `status` names the models each arm ran and any run whose log ran another;
|
|
235
|
+
- `check-change status` gives the comparison, the verdict once there is one (adopt or reject) and the next pair worth running. A check that reaches its cap or spends its budget before the evidence decides keeps the base and says `not decided`. The next pair's value and price are shown at each look and stop nothing; `--value-stop` also stops a check, not decided, when the next pairs are certified not worth their price (every plan's net below 0 by more than two standard errors, or no look left can change the action), from 4 scored pairs on (`--min-pairs K`). Use it when a pair is dear against what an adoption is worth over `--horizon-runs`: in simulated checks at the default 50 future runs it saved 6 to 16 pairs a check but dropped 26 to 40 percent of the adoptions of real cuts, and at 500 it dropped none (`check-change start --help`); `--pair-price 0` compares the value with 0 for runs on a subscription, and the budget still counts the runs' recorded dollars; `--json` keeps `verdict: reject` there, with `comparison.stop` and `comparison.decided: false`;
|
|
231
236
|
- `check-change from-runs --base-label SEL --cand-label SEL` compares finished runs already in the store, picked by a run label, a configuration id, a harness digest (a prefix of one digest will do) or `arm:NAME`.
|
|
232
237
|
|
|
233
|
-
`--rule` names what counts as accepted, and `loopmath check-change --help` lists the rest. A run with no dollar cost leaves the decision to the outcomes alone, and the cost reads unknown; a budget or `--objective cpar` then refuses, naming the runs without one.
|
|
234
|
-
- **`
|
|
238
|
+
`--rule` names what counts as accepted, and `loopmath check-change --help` lists the rest. A run with no dollar cost leaves the decision to the outcomes alone, and the cost reads unknown; a budget or `--objective cpar` then refuses, naming the runs without one. When an arm's runs mix a cheap model and a dear one, the default cost model can put the dearer arm's cost per run below the cheaper arm's. In Python, the experimental setting `loopmath.compare.model.COST_MODEL = "sw"` reads such costs for `compare.paired` and `gate.decide`; the [changelog](CHANGELOG.md) gives its limits.
|
|
239
|
+
- **`screen` (experimental).** It tests many harness edits at once. Put each edit in its own subfolder of an edits folder, laid over a base harness tree: `PATH` copies a file, `PATH.append` appends to one, `PATH.remove` removes lines. Then:
|
|
240
|
+
- `screen start --base DIR --edits DIR --tasks FILE.jsonl --rule tests` plans one fold-over pair per task: arm A runs the edits its row turns on, arm B the others, so task difficulty cancels in the pair and every session informs every edit. 12, 16, 20, 24 or 32 pairs for up to one edit fewer give orthogonal rows;
|
|
241
|
+
- `screen next` prints the next pair, with both arms' trees built in the store; `run record --screen SCR --arm A|B` records a run into it;
|
|
242
|
+
- `screen status` ranks the edits on success and cost with 80% ranges and P(helps), names the predicted-best subset with its predicted cut in cost per accepted result (often a subset no arm ran), and prices the next pair. `--on EDIT,EDIT` predicts any subset;
|
|
243
|
+
- `screen apply --best --out DIR` writes the base with the best edits, ready for a `check-change` against the base.
|
|
244
|
+
|
|
245
|
+
The model is a Bayesian linear model of each pair's difference, on success and on log cost, with its noise and effect scales weighed over a grid rather than plugged in. A finished orthogonal screen weighs two views, a few edits matter (sparse) or all matter a little (dense), by how well each explains the pairs, and `best.by_view` shows both. On a dozen pairs each edit's effect is shrunk toward 0 (a large one by about half with 8 edits in 12 pairs), but in simulations at that size, of 1,000 screens drawn from effect settings no tuning used, 964 picked a best subset and its 80% range held the true cut in 812 (84%), a few points high where the true cut was small and low where it was large; the other 36 picked no edit, which predicts no change. The prediction is against the base, which no arm runs, so an effect of having the edited files at all is not in it. The [screen guide](docs/screen.md) has a worked example and the limits.
|
|
246
|
+
- **`gate` (experimental).** For a loop that adopts changes on its own, it answers: adopt the candidate, reject it and keep the base, or replicate N more pairs first. `loopmath gate --base SEL --cand SEL` reads the finished runs in the store, or `--runs FILE` reads `loopmath.harness-runs/0.1` rows; an arm is a harness digest (`sha256:<hex>` or `digest:sha256:<hex>`, a prefix of one digest will do), a configuration id, or `arm=NAME` or `arm:NAME`; `check-change from-runs` takes the same forms. `--rule` takes the built-in rules or a rule defined under `[rules.NAME]` in the store's config, which the gate reads as `check-change` does, also with `--runs`. The comparison of the paired runs is `check-change`'s, and the gate adds the loop's policy to it: no adoption before `--r-min` pairs, looks at 1, 2, 4 and so on pairs up to `--r-max`, a `--floor` (the smallest gain worth adopting, 0 or more; under an acceptance rule it applies to the cut in cost per accepted result, a fraction, or to the gain in the chance of acceptance when a cost is unknown), and the dollars, `--budget-usd` for both arms (0 buys no further pair) and `--max-cost-ratio`. With no evidence, an arm that matches no run or a rule that scores no pair, it exits 1 and names the arms or the rules that are there. Both dollar flags need every run's cost and refuse, naming the runs without one; with neither flag, a run with no recorded cost leaves the decision to the outcomes alone, and the cost reads unknown, never $0. When nothing resolves within the cap or the budget, it keeps the base and says where it stopped. The next pairs' value is the one `check-change` states, valued on the way the gate goes on (its looks, `--r-min` and the 95% band), and named beside the number; `check-change from-runs` cannot express the gate's looks, so on the same runs the two numbers can differ. The value stop is on by default: from `--min-pairs` scored pairs (default 4) the gate rejects, keeping the base, when the next pairs are certified not worth their price, as `check-change --value-stop` does, and a cost that is not finite never stops it; `--no-value-stop` turns it off, and the value and its price are then shown and stop nothing. Under a score rule the value needs `--value-usd` (dollars per point of gain), as in `check-change`. In Python, `loopmath.gate.decide(base_runs, cand_runs, GatePolicy(...))` gives the same answer and writes nothing; `loopmath gate --help` lists the rest.
|
|
247
|
+
- **`gate winner` (experimental).** A search loop that scores N candidates and keeps the best overstates the winner's gain: the best of N noisy scores is high partly by luck. `loopmath gate winner --scores FILE` reads every score the search made (a CSV or `loopmath.gate-winner-scores/0.1` JSON; candidates known only as no better than the best so far count too) and gives the winner's gain over its parent, shrunk by what the scores say about the candidates' spread and the noise, with 80% and 95% ranges and P(beats parent). `--runs FILE` reads harness-runs rows instead, one arm per candidate, with the spread from their replicates. Give the spread of one evaluation across reruns with `--spread X`, or an assumed range with `--spread LO:HI`. The action is `gate`'s on confirmatory runs of the parent and the winner (`--confirm FILE`), never on the search's own scores, and a projection says how likely the gate is to adopt and how many more pairs it will want. In Python, `loopmath.gate.winner.assess`; the method, the formats and the output are in [spec/gate-winner.md](spec/gate-winner.md), with worked examples from published searches in `spec/examples/gate-winner/`.
|
|
248
|
+
- **DSPy's GEPA and MIPROv2 (experimental).** An optimizer's accept step keeps a new prompt when its score on a few instances goes up, and on 3 instances that keeps many children that are no better. `loopmath.integrations.dspy` puts the gate there: `GEPAAcceptance` is a GEPA acceptance criterion (run GEPA with `use_merge=False`), `MIPROv2Gate` confirms MIPROv2's replacements, and `run([], [], more)` gates any compiled program against its student. It imports neither dspy nor gepa. See [the DSPy guide](docs/dspy.md).
|
|
249
|
+
- **`tasks pick` (experimental).** For a loop or a benchmark run that scores each candidate on some of a suite's tasks instead of all of them. `loopmath tasks pick --candidate SEL` reads the finished runs in the store, or `--runs FILE`, learns each task's difficulty and discrimination from the other arms' runs on it, and prints the candidate's suite score so far (the mean over the suite of its chance of acceptance per task) with 80% and 95% ranges, whether to stop, and the next task to run. From a store it writes that task's file and the `run record --task-file F --label KEY=VALUE --home PATH` line that records the run in the same store, so the next call sees it. It stops when the 80% range is as narrow as one full run of the suite would give (`--halfwidth` sets another target), or, with `--versus X` or `--versus-arm SEL`, when the score is above or below a number or another arm's at `--level`. `--method` chooses the next task: the largest expected cut in the score's variance (`loss`, the default), information, one fixed order, random, or stratified by predicted rate. The default learns fastest when the candidate behaves like the arms in the history; for a candidate unlike all of them (a new model rather than a change to a known one's harness), `--method stratified` or `random` keeps the ranges closer to honest at the cost of more sessions. A candidate that scores below every arm in the history (a broken setup, say) is estimated too high by every method, and its ranges can miss even at the full suite; under `--method random` the accepted count over the sessions, which every call prints, is the check. Every estimate prints both warnings, in text and in `--json` (`estimate.warnings`). With `--versus`, a bar within about 0.01 of the candidate's score is close to a coin toss for any method; to ask whether a change helps by at least some margin, pass the bar plus that margin. The ranges are approximate: they leave out the history fit's own error shared with other arms. `loopmath tasks pick --help` lists the rest.
|
|
250
|
+
|
|
251
|
+
## Does my plugin pay?
|
|
252
|
+
|
|
253
|
+
`claude plugin eval` grades a plugin's eval cases with and without the plugin. `adapt plugin-eval` (experimental) reads its result, and loopmath turns the per-case deltas into a decision: adopt, reject, or run more, with ranges on the gain and, given each run's cost with `--cost-from`, on cost per accepted result. It reads the file only and never runs Claude Code.
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
loopmath adapt plugin-eval result.json --out ocp --rows rows.jsonl
|
|
257
|
+
loopmath run import ocp --finish
|
|
258
|
+
loopmath check-change from-runs --base-label arm:without --cand-label arm:with
|
|
259
|
+
loopmath gate --base arm:without --cand arm:with --runs rows.jsonl --rule accept --r-max 24
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Runs of one case are not new tasks: to learn whether the plugin helps on tasks like your cases, add cases rather than runs. With grades (`--outcome score`), `check-change from-runs` compares case by case. The import's summary prints the last two filled in for your suite. Which cases are compared, what the suite's one cost can and cannot say, how far the answer reaches and the provisional input layout are in [`docs/plugin-eval.md`](docs/plugin-eval.md).
|
|
235
263
|
|
|
236
264
|
## The store and privacy
|
|
237
265
|
|
|
@@ -264,16 +292,16 @@ loopmath graph --workspace NAME --format html --out graph.html
|
|
|
264
292
|
|
|
265
293
|
`loopmath prices` lists the packaged prices with the source of each. A rate marked "rate not confirmed" is a best public-price guess, and every dollar figure priced with one says so.
|
|
266
294
|
|
|
267
|
-
`graph` rebuilds which agent launched which, and which session read what another wrote. Its formats are `ocp`, `json`, `dot`, `run` and `html`. `graph --out FILE` and `adapt NAME --out FILE` write to any path you name: missing folders are created, an existing file is replaced, and a directory is refused. `graph` prints a short summary on stderr; `--verbose` adds every count of what was skipped or could not be placed. See the [workflow graph guide](docs/graph.md) and [adapters](docs/adapters.md).
|
|
268
|
-
|
|
269
|
-
## Research data
|
|
270
|
-
|
|
271
|
-
`loopmath research --help` lists the verbs that read research data folders, which are not part of the package: `research fit` and `research transfer-test` read sweep run records, and `research analyze-e0` reads a session corpus. Name the folders with a flag (`--sweep-dir`, `--corpus`), an environment variable (`LOOPMATH_SWEEP_DIR`, `LOOPMATH_E0_CORPUS`), or config (`loopmath config set research.sweep_dir PATH`, `research.e0_corpus`). `research fit` and `research transfer-test` need the `bayes` extra (`pip install 'loopmath[bayes]'`), and `research analyze-e0` the `e0` extra. `loopmath prior build --out DIR` rebuilds the packaged prior bundle from research data folders like these; `loopmath prior build --help` names them.
|
|
295
|
+
`graph` rebuilds which agent launched which, and which session read what another wrote. Its formats are `ocp`, `json`, `dot`, `run` and `html`. `graph --out FILE` and `adapt NAME --out FILE` write to any path you name: missing folders are created, an existing file is replaced, and a directory is refused. `graph` prints a short summary on stderr; `--verbose` adds every count of what was skipped or could not be placed. See the [workflow graph guide](docs/graph.md) and [adapters](docs/adapters.md). The ADE adapters (`adapt NAME`) are experimental: each is tested on a synthetic fixture written from its tool's format, not yet on a real store.
|
|
272
296
|
|
|
273
297
|
## More
|
|
274
298
|
|
|
275
299
|
- [OCP, the run format](spec/OCP.md): the normative note that goes with the [schemas](spec/), and the [versioning promise](spec/VERSIONING.md)
|
|
300
|
+
- [DSPy's GEPA and MIPROv2](docs/dspy.md): `loopmath.integrations.dspy`, the gate at their accept step
|
|
301
|
+
- [Plugin eval results](docs/plugin-eval.md): `adapt plugin-eval`
|
|
302
|
+
- [OpenEvolve and ShinkaEvolve runs](docs/adapters.md#openevolve-and-shinkaevolve): `adapt openevolve`, `adapt shinkaevolve` and `recommend --role`, which model to give an evolution loop's mutator
|
|
276
303
|
- [Release policy](docs/release.md)
|
|
304
|
+
- [Screening harness edits](docs/screen.md): `screen`, with a worked 48-session example and its limits
|
|
277
305
|
- [Packaged model prices](src/loopmath/prices.toml)
|
|
278
306
|
|
|
279
307
|
## License
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "loopmath"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.2"
|
|
8
8
|
description = "Plan agent workflows, record how they went, and learn from your own Claude Code and Codex logs"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""What the OpenEvolve and ShinkaEvolve readers share: one harness-runs row per LLM call whose model was recorded
|
|
2
|
+
(an OpenEvolve child, a ShinkaEvolve patch query), with one `mutator` stage, and the outcome "this call wrote a child
|
|
3
|
+
that beat its parent".
|
|
4
|
+
|
|
5
|
+
The rows use the run log's `stages` (spec/harness-runs.md): the role is `mutator`, the piece `mutate`, the agent
|
|
6
|
+
the tool's name and the model the one that wrote the child. A verdict outcome says whether the child's score beat
|
|
7
|
+
its parent's by more than `min_delta`; `delta`, `parent_score` and `child_score` ride along as numbers. Nothing
|
|
8
|
+
the tool wrote as text (code, diffs, prompts, replies, feedback, logs) is copied: ext holds ids, numbers and
|
|
9
|
+
loopmath's own labels.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import hashlib
|
|
15
|
+
import math
|
|
16
|
+
import re
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from typing import Any, Callable
|
|
20
|
+
|
|
21
|
+
ROLE = "mutator"
|
|
22
|
+
PIECE = "mutate"
|
|
23
|
+
EXPERIMENT_LABEL = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FormatError(ValueError):
|
|
27
|
+
"""An input the reader does not recognise, named with what it found."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class NeedsFlag(ValueError):
|
|
31
|
+
"""The input is readable, but a flag must say what it does not: the message names the flag."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class TablePrice:
|
|
36
|
+
"""A price table the caller loaded, as a reader uses it: `usd(model, tokens)` gives the dollars for one
|
|
37
|
+
model's tokens (keys `in`, `cache_read`, `cache_write` and `out`), or None when the table has no row for the
|
|
38
|
+
model; `path` and `as_of` name the table in the notes. The readers never load or price a table themselves."""
|
|
39
|
+
|
|
40
|
+
usd: Callable[[str, dict[str, int]], float | None]
|
|
41
|
+
path: str
|
|
42
|
+
as_of: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Result:
|
|
47
|
+
rows: list[dict[str, Any]]
|
|
48
|
+
format: str
|
|
49
|
+
skipped: list[dict[str, str]] = field(default_factory=list) # [{run_id, reason}]
|
|
50
|
+
counts: dict[str, int] = field(default_factory=dict)
|
|
51
|
+
experiments: list[dict[str, Any]] = field(default_factory=list)
|
|
52
|
+
notes: list[str] = field(default_factory=list)
|
|
53
|
+
accounting: dict[str, float] = field(default_factory=dict) # dollars: total, attributed, unattributed
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def number(value: Any) -> float | None:
|
|
57
|
+
"""A finite float, or None for anything else (bools are flags, not scores)."""
|
|
58
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
59
|
+
return None
|
|
60
|
+
value = float(value)
|
|
61
|
+
return value if math.isfinite(value) else None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def iso(epoch: Any) -> str | None:
|
|
65
|
+
"""Epoch seconds as RFC 3339 UTC, or None."""
|
|
66
|
+
value = number(epoch)
|
|
67
|
+
if value is None or value <= 0:
|
|
68
|
+
return None
|
|
69
|
+
return datetime.fromtimestamp(value, tz=timezone.utc).isoformat().replace("+00:00", "Z")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def experiment_id(prefix: str, root_id: str) -> str:
|
|
73
|
+
"""A stable experiment id from the run's first program: `<prefix>-` and 16 hex."""
|
|
74
|
+
return f"{prefix}-" + hashlib.sha256(root_id.encode("utf-8")).hexdigest()[:16]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def check_label(label: str | None) -> None:
|
|
78
|
+
if label is not None and not EXPERIMENT_LABEL.match(label):
|
|
79
|
+
raise NeedsFlag(f"--experiment {label!r}: letters, digits, '.', '_' or '-', at most 64")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def improved(child: float | None, parent: float | None, min_delta: float) -> tuple[bool, float | None]:
|
|
83
|
+
"""Whether the child beat its parent by more than `min_delta`, and the delta (None when either is unknown)."""
|
|
84
|
+
if child is None or parent is None:
|
|
85
|
+
return False, None
|
|
86
|
+
delta = child - parent
|
|
87
|
+
return delta > min_delta, delta
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def mutator_row(*, tool: str, ns: str, experiment: str, child_id: str, task_id: str, task: dict[str, Any],
|
|
91
|
+
model: str, effort: str | None, accepted: bool, delta: float | None, parent_score: float | None,
|
|
92
|
+
child_score: float | None, metric: str | None, cost: dict[str, Any] | None, status: str,
|
|
93
|
+
started: str | None, ended: str | None, stage_started: str | None, stage_ended: str | None,
|
|
94
|
+
stage: str, note: dict[str, Any]) -> dict[str, Any]:
|
|
95
|
+
"""One harness-runs row (spec/harness-runs.md) for one child: a single mutator stage."""
|
|
96
|
+
from ..harness_runs.rows import SCHEMA
|
|
97
|
+
|
|
98
|
+
outcome: dict[str, Any] = {"accepted": accepted, "by": tool}
|
|
99
|
+
if metric:
|
|
100
|
+
outcome["metric"] = metric
|
|
101
|
+
for key, value in (("delta", delta), ("parent_score", parent_score), ("child_score", child_score)):
|
|
102
|
+
if value is not None:
|
|
103
|
+
outcome[key] = value
|
|
104
|
+
st: dict[str, Any] = {"role": ROLE, "piece": PIECE, "status": status}
|
|
105
|
+
if cost:
|
|
106
|
+
st["cost"] = cost
|
|
107
|
+
if stage_started:
|
|
108
|
+
st["started_at"] = stage_started
|
|
109
|
+
if stage_ended:
|
|
110
|
+
st["ended_at"] = stage_ended
|
|
111
|
+
row: dict[str, Any] = {"schema": SCHEMA, "id": f"{tool}:{experiment}:{child_id}", "task_id": task_id,
|
|
112
|
+
"task": task, "experiment": experiment, "status": status, "agent": tool, "model": model,
|
|
113
|
+
"outcome": outcome, "stages": [st],
|
|
114
|
+
"tool": {"name": tool, "run_id": child_id, "task_id": task_id, "stage": stage},
|
|
115
|
+
"ext": {ns: {k: v for k, v in note.items() if v is not None}}}
|
|
116
|
+
if effort:
|
|
117
|
+
row["effort"] = effort
|
|
118
|
+
if started:
|
|
119
|
+
row["started_at"] = started
|
|
120
|
+
if ended:
|
|
121
|
+
row["ended_at"] = ended
|
|
122
|
+
return row
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def task_of(task_id: str, task_type: str | None, repo: str | None) -> dict[str, Any]:
|
|
126
|
+
return {"type": task_type or "research", "repo": repo or task_id}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def experiments(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
130
|
+
counts: dict[str, int] = {}
|
|
131
|
+
for r in rows:
|
|
132
|
+
counts[r["experiment"]] = counts.get(r["experiment"], 0) + 1
|
|
133
|
+
return [{"experiment": e, "runs": n} for e, n in sorted(counts.items())]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def plural(n: int, word: str, many: str | None = None) -> str:
|
|
137
|
+
return f"{n} {word if n == 1 else (many or word + 's')}"
|