loopmath 0.3.0__tar.gz → 0.3.1__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.1}/PKG-INFO +10 -11
- {loopmath-0.3.0 → loopmath-0.3.1}/README.md +9 -10
- {loopmath-0.3.0 → loopmath-0.3.1}/pyproject.toml +1 -1
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/__init__.py +1 -1
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/cli.py +42 -16
- loopmath-0.3.1/src/loopmath/cli_errors.py +121 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/cli_registry.py +22 -11
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/compare/__init__.py +56 -17
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/compare/calibration.py +3 -2
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/compare/checks.py +56 -13
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/compare/commands.py +31 -20
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/compare/model.py +2 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/output.py +5 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/budget.py +40 -6
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/commands.py +23 -9
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/status.py +5 -4
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/common.py +14 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/runs.py +7 -4
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath.egg-info/PKG-INFO +10 -11
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath.egg-info/SOURCES.txt +5 -1
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_cli_graph.py +1 -1
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_research_cli.py +2 -1
- loopmath-0.3.1/tests/test_v031_help_labels.py +124 -0
- loopmath-0.3.1/tests/test_v031_json_errors_and_costs.py +202 -0
- loopmath-0.3.1/tests/test_v031_new_findings.py +132 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/LICENSE +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/setup.cfg +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/spec/ocp-v0.2.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/spec/ocp-v0.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/spec/ocp_conformance.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/__main__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapter_host.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/atrium.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/base.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/bb.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/bb_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/harness2.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/omp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/omp_facts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/omp_links.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/omp_parse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/omp_types.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/opencode.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/opencode_store.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/orca.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/otel_genai.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/otel_genai_graph.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/otel_genai_parse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/otel_genai_types.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/otel_genai_usage.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/paseo.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/pi.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/pi_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/adapters/registry.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/block.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/check_pymc.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/compose.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/design.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/features.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/fit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/forest.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/gaussian.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/lookahead.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/outcome.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/pricing.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/priors.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/rules.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/belief/state.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/builder/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/builder/cli.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/builder/context.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/builder/predict.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/builder/server.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/cli_graph.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/cli_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/compare/_thresholds.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/compare/calibrate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/e0/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/e0/arms.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/e0/cli_e0.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/e0/estimate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/e0/estimate_rework.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/e0/figures.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/e0/io.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/e0/verb.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/explain.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/fit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/fit_assembly.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/fit_bayes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/fit_masks.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/fit_predict.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/fit_pricing.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/fit_transfer.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/gaps.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/gate/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/gate/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/gate/core.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/gate/inputs.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/gitwalk.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/grade.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/grade_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/artifact_git.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/artifact_join.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/artifact_kinds.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/artifacts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/bashparse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/bashread_ops.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/bashwrite_ops.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/bashwrites.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/cache.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/codexio.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/codexio_parse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/codexio_scan.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/dataset.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/dataset_build.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/dataset_core.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/dataset_edges.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/dataset_nodes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/extract.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/html_common.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/html_costcurve.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/html_css.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/html_data.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/html_force.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/html_render.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/html_swimlanes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_cli.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_common.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_parse.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_pricing.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_build.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_dispatch.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_evidence.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_node.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_records.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_result.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_session.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_tools.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_validation.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_prompt_workflow.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_report.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labeler_score.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/labels.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/launch.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/launch_origin.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/ocp_emit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/ocp_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/render.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/runfile.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/runfile_export.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/runfile_finish.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/scan.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/schema.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/graph/token_completeness.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/harness/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/harness/layouts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/harness_runs/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/harness_runs/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/harness_runs/costs.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/harness_runs/ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/harness_runs/rows.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/harness_runs/streams.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/base.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/claude_code.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/claude_code_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/codex.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/codex_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/ocp_analysis.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/ocp_artifact.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/ocp_common.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/ocp_convert.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ingest/ocp_projection.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/logmatch/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/logmatch/artifacts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/logmatch/clip.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/logmatch/codex_window.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/logmatch/costs.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/logmatch/match.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/logmatch/settle.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/logmatch/tariff.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/canonical.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/conformance.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/contractv3.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/emit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/graph_contract.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/membership.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/migrate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/schema/ocp-v0.2.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/schema/ocp-v0.3.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/schema/ocp-v0.schema.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/ocp/version.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/onboard/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/onboard/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/onboard/history.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/onboard/label.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/onboard/usual.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/pool.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/price.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/price_validation.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/prices.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/benchmarks.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/benchmarks.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/build.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/bundle/README.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/bundle/e0.jsonl.gz +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/bundle/lanes.jsonl.gz +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/bundle/manifest.json +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/bundle/rq1.jsonl.gz +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/bundle/sweep.jsonl.gz +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/e0.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/history.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/lanes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/ocpdoc.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/reduce.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/registry.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/rq1.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/show.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/sweep.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/priors/validate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/receipts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/backlog.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/curve.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/engine.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/gain.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/message.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/receipts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/search.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/stats.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/storeread.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/recommend/variants.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/report/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/report/eligibility.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/report/html.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/report/terminal.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/report/terminal_exclusions.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/report/terminal_format.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/report/terminal_read.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/research_defaults.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/research_paths.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/scoring.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/scoring_comparisons.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/scoring_predictive.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/share/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/share/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/share/export.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/share/import_.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/doctor.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/install.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/skills/loopmath/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/skills/loopmath-import-runs/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/skills/loopmath-onboard/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/skills/loopmath-plan-task/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/skills/loopmath-record-run/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/skills/loopmath-update-fit/SKILL.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/skill/skills/reference.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/config.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/finish.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/fitjob.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/home.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/ids.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/lock.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/record.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/report.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/rule_change.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/run_names.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/runs.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/store/verify.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/surface.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/surface_bootstrap.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/surface_estimation.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/surface_frame.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/surface_presentation.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/surface_support.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/taskmodel.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/types.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/README.md +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/base.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/builder.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/builder.html +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/builder.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/common.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/estimates.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/estimates.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/graph.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/graph.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/plans.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/plans.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/results.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/results.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/runs.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/viz.css +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/assets/viz.js +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/plans.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/views/posterior.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/__init__.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/candidates.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/catalog/best_of_n.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/catalog/implement_review.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/catalog/plan_implement.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/catalog/plan_implement_review.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/catalog/solo.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/catalog/swarm.toml +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/commands.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/diff.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/format.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/graphview.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/ids.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/infer.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/models.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath/workflows/shapes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath.egg-info/dependency_links.txt +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath.egg-info/entry_points.txt +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath.egg-info/requires.txt +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/src/loopmath.egg-info/top_level.txt +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_atrium.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_bb.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_harness2.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_omp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_opencode.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_opencode_output_tokens.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_orca.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_otel_genai.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_paseo.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapter_services.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapters.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_adapters_pi.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_analyze_cli.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_arms.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_cli_help_lists.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_cli_labeler.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_cli_main_module.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_cli_plan_hidden.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_cli_prices.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_cli_startup.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_color_proof.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_duplicate_guards.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_estimate.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_explain.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_fit.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_gaps.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_gitwalk.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_grade.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_artifact_recording.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_artifacts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_bashwrites.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_cache.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_codexio.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_dataset.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_extract.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_glue.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_html.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_labeler.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_labeler_evidence_matrix.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_launch.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_render.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_runfile.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_graph_schema.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_home_guard.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_html.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_ingest.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_ingest_claude_code.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_ingest_codex.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_ingest_ocp.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_io.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_not_run_summary.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_ocp_conformance.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_ocp_golden.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_ocp_open_enums.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_ocp_versioning.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_output_html_path.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_packaging_metadata.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_packaging_smoke.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_pool.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_preview_script.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_price.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_receipts.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_release_paths.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_report_eligibility.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_runfile_privacy_n1b.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_scoring.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_split_rebindings.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_starting_prior.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_surface.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_taskmodel_features.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_terminal.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_types_control.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_types_piece_prediction.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_v01_scaffold.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_v027_p3.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/tests/test_v027_small_fixes.py +0 -0
- {loopmath-0.3.0 → loopmath-0.3.1}/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.1
|
|
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
|
|
@@ -177,9 +177,12 @@ The workflow builder is the one page that is served: `loopmath builder --type bu
|
|
|
177
177
|
| Record | `run` (start, record, import, attempt, artifact, finish, sessions), `outcome`, `budget`, `config` (get, set) |
|
|
178
178
|
| Learn | `fit`, `onboard`, `share`, `prior` |
|
|
179
179
|
| View | `runs`, `posterior`, `status`, `report`, `doctor`, `verify-receipts` |
|
|
180
|
+
| Harness testing | `check-change`, `gate` (experimental) |
|
|
180
181
|
| Skill | `skill` (install, uninstall, show) |
|
|
181
182
|
| OCP | `ocp` (validate, migrate) |
|
|
182
|
-
| Log analysis | `analyze`, `graph`, `adapt`, `prices`, `validate-prices
|
|
183
|
+
| Log analysis | `analyze`, `graph`, `adapt`, `prices`, `validate-prices` |
|
|
184
|
+
|
|
185
|
+
These are experimental: `gate`, the harness-part estimates (`recommend --harness-variants` and `--harness`, `posterior --level harness` and its `factors`), `adapt harness2` 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.
|
|
183
186
|
|
|
184
187
|
Each command prints a short summary by default. With `--json`, it prints exactly one JSON object instead. One exception: `loopmath adapt harness2` and `adapt harness-runs` 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
188
|
|
|
@@ -255,21 +258,21 @@ A run can record the harness it ran with: the prompt files, skills, plugins, MCP
|
|
|
255
258
|
`cost` is the multiplier on an attempt's dollars when the part is present; `success` is percentage points.
|
|
256
259
|
- `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
260
|
|
|
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).
|
|
261
|
+
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
262
|
|
|
260
263
|
## Testing a harness change
|
|
261
264
|
|
|
262
265
|
These commands are for changing the harness your agents run with and finding out whether the change paid off.
|
|
263
266
|
|
|
264
267
|
- **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).
|
|
268
|
+
- **`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
269
|
- **`check-change`.** It decides whether a change beats what it replaces, on paired runs:
|
|
267
270
|
- `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;
|
|
271
|
+
- `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 pair is worth less than its price, from 4 scored pairs on (`--min-pairs K`); `--json` keeps `verdict: reject` there, with `comparison.stop` and `comparison.decided: false`;
|
|
269
272
|
- `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
273
|
|
|
271
274
|
`--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
|
-
- **`gate
|
|
275
|
+
- **`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. In Python, `loopmath.gate.decide(base_runs, cand_runs, GatePolicy(...))` gives the same answer and writes nothing; `loopmath gate --help` lists the rest.
|
|
273
276
|
|
|
274
277
|
## The store and privacy
|
|
275
278
|
|
|
@@ -302,11 +305,7 @@ loopmath graph --workspace NAME --format html --out graph.html
|
|
|
302
305
|
|
|
303
306
|
`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
307
|
|
|
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.
|
|
308
|
+
`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
309
|
|
|
311
310
|
## More
|
|
312
311
|
|
|
@@ -139,9 +139,12 @@ The workflow builder is the one page that is served: `loopmath builder --type bu
|
|
|
139
139
|
| Record | `run` (start, record, import, attempt, artifact, finish, sessions), `outcome`, `budget`, `config` (get, set) |
|
|
140
140
|
| Learn | `fit`, `onboard`, `share`, `prior` |
|
|
141
141
|
| View | `runs`, `posterior`, `status`, `report`, `doctor`, `verify-receipts` |
|
|
142
|
+
| Harness testing | `check-change`, `gate` (experimental) |
|
|
142
143
|
| Skill | `skill` (install, uninstall, show) |
|
|
143
144
|
| OCP | `ocp` (validate, migrate) |
|
|
144
|
-
| Log analysis | `analyze`, `graph`, `adapt`, `prices`, `validate-prices
|
|
145
|
+
| Log analysis | `analyze`, `graph`, `adapt`, `prices`, `validate-prices` |
|
|
146
|
+
|
|
147
|
+
These are experimental: `gate`, the harness-part estimates (`recommend --harness-variants` and `--harness`, `posterior --level harness` and its `factors`), `adapt harness2` 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.
|
|
145
148
|
|
|
146
149
|
Each command prints a short summary by default. With `--json`, it prints exactly one JSON object instead. One exception: `loopmath adapt harness2` and `adapt harness-runs` 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
150
|
|
|
@@ -217,21 +220,21 @@ A run can record the harness it ran with: the prompt files, skills, plugins, MCP
|
|
|
217
220
|
`cost` is the multiplier on an attempt's dollars when the part is present; `success` is percentage points.
|
|
218
221
|
- `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
222
|
|
|
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).
|
|
223
|
+
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
224
|
|
|
222
225
|
## Testing a harness change
|
|
223
226
|
|
|
224
227
|
These commands are for changing the harness your agents run with and finding out whether the change paid off.
|
|
225
228
|
|
|
226
229
|
- **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).
|
|
230
|
+
- **`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
231
|
- **`check-change`.** It decides whether a change beats what it replaces, on paired runs:
|
|
229
232
|
- `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;
|
|
233
|
+
- `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 pair is worth less than its price, from 4 scored pairs on (`--min-pairs K`); `--json` keeps `verdict: reject` there, with `comparison.stop` and `comparison.decided: false`;
|
|
231
234
|
- `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
235
|
|
|
233
236
|
`--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
|
-
- **`gate
|
|
237
|
+
- **`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. In Python, `loopmath.gate.decide(base_runs, cand_runs, GatePolicy(...))` gives the same answer and writes nothing; `loopmath gate --help` lists the rest.
|
|
235
238
|
|
|
236
239
|
## The store and privacy
|
|
237
240
|
|
|
@@ -264,11 +267,7 @@ loopmath graph --workspace NAME --format html --out graph.html
|
|
|
264
267
|
|
|
265
268
|
`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
269
|
|
|
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.
|
|
270
|
+
`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
271
|
|
|
273
272
|
## More
|
|
274
273
|
|
|
@@ -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.1"
|
|
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"
|
|
@@ -245,11 +245,8 @@ def _add_prices(sub) -> None:
|
|
|
245
245
|
|
|
246
246
|
|
|
247
247
|
def _add_scan(sub) -> None:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
help="explain the implicit scan used by graph and analyze",
|
|
251
|
-
description="Scanning is implicit in loopmath graph and loopmath analyze.",
|
|
252
|
-
)
|
|
248
|
+
# Hidden: no help= keeps it off `loopmath --help`, and HIDDEN keeps it off the usage line.
|
|
249
|
+
p = sub.add_parser("scan", description="Scanning is implicit in loopmath graph and loopmath analyze.")
|
|
253
250
|
p.set_defaults(func=_cli_support.scan_verb)
|
|
254
251
|
|
|
255
252
|
|
|
@@ -328,7 +325,7 @@ def adapt_verb(args: argparse.Namespace) -> int:
|
|
|
328
325
|
return 1
|
|
329
326
|
if not isinstance(doc, dict):
|
|
330
327
|
raise TypeError(f"adapter {args.name!r} emit() must return a dict")
|
|
331
|
-
if not doc.get("nodes") and not doc.get("attempts"):
|
|
328
|
+
if not doc.get("nodes") and not doc.get("attempts") and not doc.get("events"): # atrium emits events only
|
|
332
329
|
where = ", ".join(str(p) for p in selection.stores) if selection.stores else "its default location"
|
|
333
330
|
print(f"error: adapt {args.name} found no sessions in {where}; nothing written. Point --store at the "
|
|
334
331
|
f"{args.name} data, or widen --since, --until, --session and --workspace", file=sys.stderr)
|
|
@@ -350,12 +347,16 @@ def adapt_verb(args: argparse.Namespace) -> int:
|
|
|
350
347
|
|
|
351
348
|
def _add_adapt(sub) -> None:
|
|
352
349
|
p = sub.add_parser(
|
|
353
|
-
"adapt", help="emit OCP v0.2 from an ADE store (loopmath ocp migrate converts it to v0.3),
|
|
350
|
+
"adapt", help="emit OCP v0.2 from an ADE store (experimental; loopmath ocp migrate converts it to v0.3), "
|
|
351
|
+
"or import runs",
|
|
354
352
|
description="Emit OCP v0.2 from an ADE store, or import rollouts as OCP v0.3 runs with a run importer: "
|
|
355
353
|
"`loopmath adapt harness2 RUNS --out DIR` reads a harness2 runs folder, and "
|
|
356
354
|
"`loopmath adapt harness-runs FILE --out DIR` reads a loopmath.harness-runs/0.1 file "
|
|
357
|
-
"(spec/harness-runs.md). Then `loopmath run import DIR --finish` stores them."
|
|
358
|
-
|
|
355
|
+
"(spec/harness-runs.md). Then `loopmath run import DIR --finish` stores them. "
|
|
356
|
+
"Experimental: the ADE adapters and the harness2 importer, which are tested on synthetic "
|
|
357
|
+
"fixtures written from each tool's format, not yet on real data.")
|
|
358
|
+
p.add_argument("name", metavar="NAME", help="registered adapter name (experimental), or a run importer: "
|
|
359
|
+
"harness2 (experimental), harness-runs")
|
|
359
360
|
p.add_argument("path", nargs="?", default=None, metavar="PATH",
|
|
360
361
|
help="the input: the same as --store PATH for an adapter; the runs folder (harness2) or rows file (harness-runs) for a run importer")
|
|
361
362
|
p.add_argument("--out", default=None, help="write here instead of stdout (any path; missing folders are created; an existing file is replaced). Run importers: a folder, one <run id>.ocp.json per rollout")
|
|
@@ -429,9 +430,9 @@ def _add_validate_prices(sub) -> None:
|
|
|
429
430
|
def _add_fit(sub) -> None:
|
|
430
431
|
from .research_defaults import E1A_OBSERVE, SEED
|
|
431
432
|
|
|
433
|
+
# Hidden: no help= keeps it off `loopmath --help`, and HIDDEN keeps it off the usage line.
|
|
432
434
|
research = sub.add_parser(
|
|
433
435
|
"research",
|
|
434
|
-
help="verbs for research data folders that are not part of the package (loopmath research --help)",
|
|
435
436
|
description="Verbs for research data folders that are not part of the package: sweep run records "
|
|
436
437
|
"(research fit, research transfer-test; they need the bayes extra) and a session corpus "
|
|
437
438
|
"(research analyze-e0; it needs the e0 extra). Name a folder with --sweep-dir or --corpus, "
|
|
@@ -463,21 +464,38 @@ def _add_fit(sub) -> None:
|
|
|
463
464
|
|
|
464
465
|
def verify_receipts_verb(args: argparse.Namespace) -> int:
|
|
465
466
|
"""No ledger: check the store's receipts. A ledger path: verify that research receipt ledger."""
|
|
467
|
+
from .output import emit_json
|
|
468
|
+
|
|
469
|
+
schema = "loopmath.verify-receipts/1"
|
|
466
470
|
if args.ledger is None:
|
|
467
471
|
from .store.home import Store
|
|
468
472
|
from .store.verify import verify_lines, verify_store
|
|
469
473
|
|
|
470
474
|
res = verify_store(Store(args.home))
|
|
471
|
-
|
|
472
|
-
|
|
475
|
+
lines = verify_lines(res)
|
|
476
|
+
code = 1 if res["failed"] else 0
|
|
477
|
+
if args.json:
|
|
478
|
+
emit_json(schema, {"ok": not code, **res, "message": "\n".join(lines), **({"exit": code} if code else {})})
|
|
479
|
+
else:
|
|
480
|
+
print("\n".join(lines))
|
|
481
|
+
return code
|
|
473
482
|
from .receipts import ReceiptViolation, verify_receipts
|
|
474
483
|
|
|
475
484
|
try:
|
|
476
485
|
result = verify_receipts(args.ledger, args.frozen)
|
|
477
486
|
except ReceiptViolation as exc:
|
|
478
|
-
|
|
487
|
+
if args.json:
|
|
488
|
+
emit_json(schema, {"ok": False, "ledger_path": str(args.ledger), "message": f"FAIL: {exc}",
|
|
489
|
+
"error": str(exc), "exit": 1})
|
|
490
|
+
else:
|
|
491
|
+
print(f"FAIL: {exc}")
|
|
479
492
|
return 1
|
|
480
|
-
|
|
493
|
+
if args.json:
|
|
494
|
+
emit_json(schema, {"ok": True, "ledger_path": str(args.ledger), "message": result.message(),
|
|
495
|
+
"ledger": {"receipts": result.receipts, "task_arms": result.task_arms,
|
|
496
|
+
"spend_usd": float(result.spend_usd), "prereg": str(result.prereg)}})
|
|
497
|
+
else:
|
|
498
|
+
print(result.message())
|
|
481
499
|
return 0
|
|
482
500
|
|
|
483
501
|
|
|
@@ -496,11 +514,17 @@ def _add_verify_receipts(sub) -> None:
|
|
|
496
514
|
help="with a ledger: its checksum file (default: FROZEN.txt beside the ledger)",
|
|
497
515
|
)
|
|
498
516
|
p.add_argument("--home", default=None, metavar="PATH", help="the store (default: $LOOPMATH_HOME or ~/.loopmath)")
|
|
517
|
+
p.add_argument("--json", action="store_true", help="print one JSON object on stdout")
|
|
499
518
|
p.set_defaults(func=verify_receipts_verb)
|
|
500
519
|
|
|
501
520
|
|
|
502
521
|
def main(argv: list[str] | None = None) -> int:
|
|
503
|
-
|
|
522
|
+
from .cli_errors import JsonErrorParser, run_guarded, wants_json
|
|
523
|
+
|
|
524
|
+
raw_args = sys.argv[1:] if argv is None else argv
|
|
525
|
+
JsonErrorParser.json_mode = wants_json(raw_args) # under --json a usage error is a JSON object on stdout too
|
|
526
|
+
JsonErrorParser.argv = list(raw_args)
|
|
527
|
+
parser = JsonErrorParser(
|
|
504
528
|
prog="loopmath", description="loopmath: which agent workflow to run next, priced from your own logs"
|
|
505
529
|
)
|
|
506
530
|
parser.add_argument("--version", action="version", version=f"loopmath {__version__}")
|
|
@@ -537,7 +561,6 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
537
561
|
|
|
538
562
|
sub.metavar = _registry.command_metavar(sub) # hidden commands stay callable, off the usage line
|
|
539
563
|
|
|
540
|
-
raw_args = sys.argv[1:] if argv is None else argv
|
|
541
564
|
if raw_args and raw_args[0] == "view":
|
|
542
565
|
parser.error(
|
|
543
566
|
"loopmath view is unavailable; use 'loopmath graph --format dot' with "
|
|
@@ -550,6 +573,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
550
573
|
from .ingest.base import use_home
|
|
551
574
|
|
|
552
575
|
use_home(getattr(args, "home", None)) # the caches follow --home as the store does
|
|
576
|
+
if getattr(args, "json", False) is True:
|
|
577
|
+
words = [args.command] + [str(v) for k, v in vars(args).items() if k.endswith("_command") and v]
|
|
578
|
+
return run_guarded(args.func, args, " ".join(words))
|
|
553
579
|
return args.func(args)
|
|
554
580
|
|
|
555
581
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""With `--json`, an error is one JSON object on stdout too.
|
|
2
|
+
|
|
3
|
+
A command that fails under `--json` without printing anything on stdout gets one object there,
|
|
4
|
+
`{"schema": "loopmath.error/1", "ok": false, "command": ..., "error": ..., "exit": ...}`, with the same
|
|
5
|
+
exit code and the same message on stderr as before. That covers a usage error from the parser (exit 2),
|
|
6
|
+
a handler that reports its error on stderr only, and an uncaught exception (the traceback still prints).
|
|
7
|
+
A command that printed its own JSON error, as `run import`, `check-change` and `gate` do, is left alone.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import sys
|
|
14
|
+
from typing import Any, Callable
|
|
15
|
+
|
|
16
|
+
from .output import emit_json
|
|
17
|
+
|
|
18
|
+
SCHEMA = "loopmath.error/1"
|
|
19
|
+
_MAX_KEPT = 64_000 # characters of stderr kept for the message
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def wants_json(argv: list[str]) -> bool:
|
|
23
|
+
"""`--json` among the options, before any `--`."""
|
|
24
|
+
for token in argv:
|
|
25
|
+
if token == "--":
|
|
26
|
+
return False
|
|
27
|
+
if token == "--json":
|
|
28
|
+
return True
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def emit_error(command: str, message: str, code: int, hint: str | None = None) -> None:
|
|
33
|
+
emit_json(SCHEMA, {"ok": False, "command": command, "error": message, **({"hint": hint} if hint else {}),
|
|
34
|
+
"exit": code})
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class JsonErrorParser(argparse.ArgumentParser):
|
|
38
|
+
"""An ArgumentParser whose usage errors, under --json, also print one JSON object on stdout."""
|
|
39
|
+
|
|
40
|
+
json_mode = False # set by cli.main for each command line
|
|
41
|
+
argv: list[str] = []
|
|
42
|
+
|
|
43
|
+
def error(self, message: str): # type: ignore[override]
|
|
44
|
+
if JsonErrorParser.json_mode:
|
|
45
|
+
self.print_usage(sys.stderr)
|
|
46
|
+
print(f"{self.prog}: error: {message}", file=sys.stderr)
|
|
47
|
+
emit_error(_command(self.prog, JsonErrorParser.argv), message, 2)
|
|
48
|
+
sys.exit(2)
|
|
49
|
+
super().error(message)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _command(prog: str, argv: list[str]) -> str:
|
|
53
|
+
"""The command a usage error belongs to: the parser's words after `loopmath`, else the first word given."""
|
|
54
|
+
words = prog.split()[1:]
|
|
55
|
+
if not words and argv and not argv[0].startswith("-"):
|
|
56
|
+
words = [argv[0]]
|
|
57
|
+
return " ".join(words) or "loopmath"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class _Watch:
|
|
61
|
+
"""A pass-through text stream that remembers whether anything was written, and keeps what was."""
|
|
62
|
+
|
|
63
|
+
def __init__(self, inner, keep: bool) -> None:
|
|
64
|
+
self._inner = inner
|
|
65
|
+
self._keep = keep
|
|
66
|
+
self.wrote = False
|
|
67
|
+
self.kept: list[str] = []
|
|
68
|
+
self._size = 0
|
|
69
|
+
|
|
70
|
+
def write(self, text: str) -> int:
|
|
71
|
+
if text:
|
|
72
|
+
self.wrote = True
|
|
73
|
+
if self._keep and self._size < _MAX_KEPT:
|
|
74
|
+
self.kept.append(text)
|
|
75
|
+
self._size += len(text)
|
|
76
|
+
return self._inner.write(text)
|
|
77
|
+
|
|
78
|
+
def __getattr__(self, name: str) -> Any:
|
|
79
|
+
return getattr(self._inner, name)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _message(stderr_text: str, command: str, code: int) -> tuple[str, str | None]:
|
|
83
|
+
"""The error lines on stderr (else its last line), and its `hint:` lines."""
|
|
84
|
+
lines = [line.strip() for line in stderr_text.splitlines() if line.strip()]
|
|
85
|
+
errors = [line.split("error: ", 1)[1] for line in lines if line.startswith("error: ") or ": error: " in line]
|
|
86
|
+
hints = [line[len("hint: "):] for line in lines if line.startswith("hint: ")]
|
|
87
|
+
hint = "; ".join(hints) or None
|
|
88
|
+
if errors:
|
|
89
|
+
return "; ".join(errors), hint
|
|
90
|
+
rest = [line for line in lines if not line.startswith("hint: ")]
|
|
91
|
+
if rest:
|
|
92
|
+
return rest[-1], hint
|
|
93
|
+
return f"loopmath {command} stopped with exit {code}", hint
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def run_guarded(func: Callable[[argparse.Namespace], int], args: argparse.Namespace, command: str) -> int:
|
|
97
|
+
"""Run a handler under --json: when it fails and stdout stayed empty, print the error object there."""
|
|
98
|
+
out_watch, err_watch = _Watch(sys.stdout, keep=False), _Watch(sys.stderr, keep=True)
|
|
99
|
+
old_out, old_err = sys.stdout, sys.stderr
|
|
100
|
+
sys.stdout, sys.stderr = out_watch, err_watch
|
|
101
|
+
try:
|
|
102
|
+
code = func(args)
|
|
103
|
+
except SystemExit as exc:
|
|
104
|
+
sys.stdout, sys.stderr = old_out, old_err
|
|
105
|
+
code = exc.code if isinstance(exc.code, int) else (0 if exc.code is None else 1)
|
|
106
|
+
if code and not out_watch.wrote:
|
|
107
|
+
text = "".join(err_watch.kept) or (exc.code if isinstance(exc.code, str) else "")
|
|
108
|
+
message, hint = _message(text, command, code)
|
|
109
|
+
emit_error(command, message, code, hint)
|
|
110
|
+
raise
|
|
111
|
+
except Exception as exc:
|
|
112
|
+
sys.stdout, sys.stderr = old_out, old_err
|
|
113
|
+
if not out_watch.wrote:
|
|
114
|
+
emit_error(command, f"{type(exc).__name__}: {exc}", 1)
|
|
115
|
+
raise
|
|
116
|
+
finally:
|
|
117
|
+
sys.stdout, sys.stderr = old_out, old_err
|
|
118
|
+
if code and not out_watch.wrote:
|
|
119
|
+
message, hint = _message("".join(err_watch.kept), command, code)
|
|
120
|
+
emit_error(command, message, code, hint)
|
|
121
|
+
return code
|
|
@@ -66,7 +66,9 @@ def _check_args(p: argparse.ArgumentParser) -> None:
|
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
def _add_compare(sub) -> None:
|
|
69
|
-
p = sub.add_parser("check-change", help="paired A/B check of a change: start, status, from-runs"
|
|
69
|
+
p = sub.add_parser("check-change", help="paired A/B check of a change: start, status, from-runs",
|
|
70
|
+
description="A paired A/B check of a change. Start a check, record its pairs, and read its "
|
|
71
|
+
"comparison and verdict.")
|
|
70
72
|
cs = p.add_subparsers(dest="check_command", required=True, metavar="{start,status,from-runs}")
|
|
71
73
|
|
|
72
74
|
def decision(q: argparse.ArgumentParser) -> None:
|
|
@@ -85,6 +87,11 @@ def _add_compare(sub) -> None:
|
|
|
85
87
|
"long-lived change)")
|
|
86
88
|
q.add_argument("--value-usd", type=float, default=None, metavar="USD", help="dollars per point of gain "
|
|
87
89
|
"(default: the base's cost per accepted result)")
|
|
90
|
+
q.add_argument("--value-stop", action="store_true", default=None, help="stop the check, keeping the base, "
|
|
91
|
+
"when the next pair is worth less than its price (by default the value and the price are "
|
|
92
|
+
"shown at each look and the check goes on to a decision, its cap or its budget)")
|
|
93
|
+
q.add_argument("--min-pairs", type=int, default=None, metavar="K", help="with --value-stop, the scored "
|
|
94
|
+
"pairs before the value may stop the check (default 4; 1 stops from the first pair)")
|
|
88
95
|
|
|
89
96
|
q = cs.add_parser("start", help="start a check: its pairs, each pair's arm order, and the next pair to run")
|
|
90
97
|
q.add_argument("--base", required=True, metavar="LABEL|PATH", help="the base arm: a label, or its harness tree")
|
|
@@ -186,10 +193,10 @@ def _add_planning(sub) -> None:
|
|
|
186
193
|
p.add_argument("--brief", action="store_true", help="with --json: a short object for agents (task, reference, "
|
|
187
194
|
"goal, rescue, choices, message, rec, and page with --html); the stored recommendation keeps everything")
|
|
188
195
|
p.add_argument("--harness-variants", nargs="?", const=5, type=int, default=None, metavar="N",
|
|
189
|
-
help="also price the N harness trees recorded on your runs that the fit saw most
|
|
190
|
-
"default pick and the goal, against the bare harness label on the same draws")
|
|
191
|
-
p.add_argument("--harness", default=None, metavar="PATH|auto", help="also price this harness tree
|
|
192
|
-
"variant (auto: the git working tree of this folder), even when no run recorded it")
|
|
196
|
+
help="experimental: also price the N harness trees recorded on your runs that the fit saw most "
|
|
197
|
+
"(default 5), for the default pick and the goal, against the bare harness label on the same draws")
|
|
198
|
+
p.add_argument("--harness", default=None, metavar="PATH|auto", help="experimental: also price this harness tree "
|
|
199
|
+
"as the first variant (auto: the git working tree of this folder), even when no run recorded it")
|
|
193
200
|
_common(p, html=True)
|
|
194
201
|
p.set_defaults(func=_lazy("loopmath.recommend.commands:recommend"))
|
|
195
202
|
|
|
@@ -445,9 +452,11 @@ def _add_learning(sub) -> None:
|
|
|
445
452
|
_common(p)
|
|
446
453
|
p.set_defaults(func=_lazy("loopmath.share.commands:share"))
|
|
447
454
|
|
|
448
|
-
p = sub.add_parser("prior", help="the shipped prior bundle: show,
|
|
449
|
-
ps = p.add_subparsers(dest="prior_command", required=True)
|
|
450
|
-
|
|
455
|
+
p = sub.add_parser("prior", help="the shipped prior bundle: show, import-shared, remove-shared")
|
|
456
|
+
ps = p.add_subparsers(dest="prior_command", required=True, metavar="{show,import-shared,remove-shared}")
|
|
457
|
+
# Hidden, as `plan` is: no help= keeps `build` out of `loopmath prior --help`; it stays callable by name.
|
|
458
|
+
q = ps.add_parser("build", description="Rebuild the packaged prior bundle from the sweep, E0 and RQ1 inputs. "
|
|
459
|
+
"It needs research data folders that do not ship with the package.")
|
|
451
460
|
q.add_argument("--out", default=None, metavar="DIR", help="where to write the bundle (required)")
|
|
452
461
|
q.add_argument("--sweep-dir", action="append", default=None, metavar="PATH", help="sweep results, one folder per batch; repeatable (default: LOOPMATH_SWEEP_DIR, one folder)")
|
|
453
462
|
q.add_argument("--e0-corpus", default=None, metavar="PATH", help="E0 corpus (default: LOOPMATH_E0_CORPUS)")
|
|
@@ -483,7 +492,7 @@ def _add_viewing(sub) -> None:
|
|
|
483
492
|
|
|
484
493
|
p = sub.add_parser("posterior", help="current estimates by level and by workflow graph")
|
|
485
494
|
p.add_argument("--level", default="all",
|
|
486
|
-
choices=["model", "effort", "role", "topology", "type", "repo", "feature", "harness", "all"], help="which level of estimates (default all; harness: the harness labels and their parts)")
|
|
495
|
+
choices=["model", "effort", "role", "topology", "type", "repo", "feature", "harness", "all"], help="which level of estimates (default all; harness: the harness labels and their parts, experimental)")
|
|
487
496
|
p.add_argument("--head", default=None, metavar="cost|tokens|success|gate|score:NAME", help="which quantity to show")
|
|
488
497
|
p.add_argument("--target", default=None, metavar="NAME>=X", help="score target for the chance to reach and the cost per accepted result, as for recommend (default: the latest recommendation's target for this type and repo)")
|
|
489
498
|
p.add_argument("--workflow", default=None, metavar="CFG|FILE.toml", help="also draw this configuration with each piece's estimate: a cfg_ id from loopmath recommend --json or your runs, or FILE.toml with settings")
|
|
@@ -546,7 +555,7 @@ def _add_viewing(sub) -> None:
|
|
|
546
555
|
q.set_defaults(func=_lazy("loopmath.ocp.commands:migrate"))
|
|
547
556
|
|
|
548
557
|
|
|
549
|
-
HIDDEN = frozenset({"plan", "analyze-e0"}) # callable, but not listed by `loopmath --help`
|
|
558
|
+
HIDDEN = frozenset({"plan", "analyze-e0", "scan", "research"}) # callable, but not listed by `loopmath --help`
|
|
550
559
|
|
|
551
560
|
|
|
552
561
|
def command_metavar(sub) -> str:
|
|
@@ -555,7 +564,9 @@ def command_metavar(sub) -> str:
|
|
|
555
564
|
|
|
556
565
|
|
|
557
566
|
def _add_gate(sub) -> None:
|
|
558
|
-
p = sub.add_parser("gate", help="adopt or reject a harness change: compare base and candidate runs"
|
|
567
|
+
p = sub.add_parser("gate", help="experimental: adopt or reject a harness change: compare base and candidate runs",
|
|
568
|
+
description="Experimental: adopt or reject a harness change, comparing base and candidate "
|
|
569
|
+
"runs, for a loop that adopts changes on its own.")
|
|
559
570
|
p.add_argument("--base", required=True, metavar="SEL",
|
|
560
571
|
help="the base arm: digest:sha256:<hex> or sha256:<hex> (the harness record's digest; a prefix of "
|
|
561
572
|
"one digest will do), a cfg_ configuration id, or arm:<name> or arm=<name>")
|