loopmath 0.2.4__tar.gz → 0.2.5__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.2.4/src/loopmath.egg-info → loopmath-0.2.5}/PKG-INFO +3 -3
- {loopmath-0.2.4 → loopmath-0.2.5}/README.md +2 -2
- {loopmath-0.2.4 → loopmath-0.2.5}/pyproject.toml +1 -1
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/__init__.py +1 -1
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/opencode.py +1 -1
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/pi.py +2 -2
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/design.py +9 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/cli_support.py +3 -13
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/html_common.py +1 -1
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/contractv3.py +13 -4
- loopmath-0.2.5/src/loopmath/ocp/graph_contract.py +86 -0
- loopmath-0.2.5/src/loopmath/ocp/membership.py +69 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/migrate.py +18 -11
- loopmath-0.2.5/src/loopmath/report/eligibility.py +45 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/report/html.py +5 -2
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/report/terminal.py +3 -18
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/fitjob.py +5 -1
- {loopmath-0.2.4 → loopmath-0.2.5/src/loopmath.egg-info}/PKG-INFO +3 -3
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath.egg-info/SOURCES.txt +4 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_opencode.py +9 -9
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_opencode_output_tokens.py +1 -1
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapters_pi.py +9 -9
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_analyze_cli.py +61 -4
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_html.py +9 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_home_guard.py +1 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_html.py +16 -0
- loopmath-0.2.5/tests/test_report_eligibility.py +40 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_terminal.py +2 -2
- {loopmath-0.2.4 → loopmath-0.2.5}/LICENSE +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/setup.cfg +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/spec/ocp-v0.2.schema.json +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/spec/ocp-v0.schema.json +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/spec/ocp_conformance.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/__main__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapter_host.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/atrium.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/base.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/bb.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/bb_support.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/omp.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/omp_facts.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/omp_links.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/omp_parse.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/omp_types.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/opencode_store.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/orca.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/otel_genai.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/otel_genai_graph.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/otel_genai_parse.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/otel_genai_types.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/otel_genai_usage.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/paseo.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/pi_support.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/adapters/registry.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/block.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/check_pymc.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/compose.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/features.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/fit.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/forest.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/gaussian.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/lookahead.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/outcome.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/pricing.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/priors.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/belief/state.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/builder/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/builder/cli.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/builder/context.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/builder/predict.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/builder/server.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/cli.py +1 -1
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/cli_graph.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/cli_registry.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/e0/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/e0/arms.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/e0/cli_e0.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/e0/estimate.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/e0/estimate_rework.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/e0/figures.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/e0/io.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/e0/verb.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/explain.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/fit.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/fit_assembly.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/fit_bayes.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/fit_masks.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/fit_predict.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/fit_pricing.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/fit_transfer.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/gaps.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/gitwalk.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/grade.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/grade_support.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/artifact_git.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/artifact_join.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/artifact_kinds.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/artifacts.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/bashparse.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/bashread_ops.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/bashwrite_ops.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/bashwrites.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/cache.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/codexio.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/codexio_parse.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/codexio_scan.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/dataset.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/dataset_build.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/dataset_core.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/dataset_edges.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/dataset_nodes.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/extract.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/html_costcurve.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/html_css.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/html_data.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/html_force.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/html_render.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/html_swimlanes.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_cli.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_common.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_parse.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_pricing.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_build.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_dispatch.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_evidence.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_node.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_records.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_result.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_session.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_tools.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_validation.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_prompt_workflow.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_report.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labeler_score.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/labels.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/launch.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/launch_origin.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/ocp.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/ocp_emit.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/ocp_support.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/render.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/runfile.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/runfile_export.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/runfile_finish.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/scan.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/schema.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/graph/token_completeness.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/base.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/claude_code.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/claude_code_support.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/codex.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/codex_support.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/ocp.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/ocp_analysis.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/ocp_artifact.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/ocp_common.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/ocp_convert.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ingest/ocp_projection.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/logmatch/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/logmatch/artifacts.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/logmatch/clip.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/logmatch/costs.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/logmatch/match.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/logmatch/settle.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/logmatch/tariff.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/canonical.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/conformance.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/emit.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/schema/ocp-v0.2.schema.json +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/schema/ocp-v0.3.schema.json +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/schema/ocp-v0.schema.json +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/ocp/version.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/onboard/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/onboard/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/onboard/history.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/onboard/label.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/onboard/usual.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/output.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/pool.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/price.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/price_validation.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/prices.toml +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/benchmarks.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/benchmarks.toml +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/build.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/bundle/README.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/bundle/e0.jsonl.gz +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/bundle/lanes.jsonl.gz +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/bundle/manifest.json +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/bundle/rq1.jsonl.gz +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/bundle/sweep.jsonl.gz +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/e0.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/history.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/lanes.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/ocpdoc.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/reduce.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/registry.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/rq1.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/show.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/sweep.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/priors/validate.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/receipts.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/backlog.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/curve.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/engine.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/gain.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/message.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/receipts.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/search.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/stats.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/recommend/storeread.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/report/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/report/terminal_exclusions.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/report/terminal_format.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/report/terminal_read.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/research_defaults.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/research_paths.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/scoring.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/scoring_comparisons.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/scoring_predictive.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/share/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/share/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/share/export.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/share/import_.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/doctor.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/install.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/skills/loopmath/SKILL.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/skills/loopmath-import-runs/SKILL.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/skills/loopmath-onboard/SKILL.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/skills/loopmath-plan-task/SKILL.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/skills/loopmath-record-run/SKILL.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/skills/loopmath-update-fit/SKILL.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/skill/skills/reference.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/budget.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/config.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/finish.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/home.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/ids.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/lock.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/record.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/report.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/run_names.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/runs.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/status.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/store/verify.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/surface.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/surface_bootstrap.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/surface_estimation.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/surface_frame.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/surface_presentation.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/surface_support.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/taskmodel.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/types.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/README.md +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/base.css +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/builder.css +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/builder.html +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/builder.js +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/common.js +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/estimates.css +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/estimates.js +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/graph.css +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/graph.js +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/plans.css +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/plans.js +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/results.css +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/results.js +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/runs.js +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/viz.css +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/assets/viz.js +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/common.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/plans.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/posterior.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/views/runs.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/__init__.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/candidates.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/catalog/best_of_n.toml +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/catalog/implement_review.toml +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/catalog/plan_implement.toml +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/catalog/plan_implement_review.toml +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/catalog/solo.toml +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/catalog/swarm.toml +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/commands.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/diff.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/format.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/graphview.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/ids.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/infer.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/models.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/ocp.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath/workflows/shapes.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath.egg-info/dependency_links.txt +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath.egg-info/entry_points.txt +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath.egg-info/requires.txt +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/src/loopmath.egg-info/top_level.txt +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_atrium.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_bb.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_omp.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_orca.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_otel_genai.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_paseo.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapter_services.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_adapters.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_arms.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_cli_graph.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_cli_help_lists.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_cli_labeler.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_cli_main_module.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_cli_plan_hidden.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_cli_prices.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_cli_startup.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_color_proof.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_duplicate_guards.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_estimate.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_explain.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_fit.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_gaps.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_gitwalk.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_grade.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_artifact_recording.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_artifacts.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_bashwrites.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_cache.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_codexio.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_dataset.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_extract.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_glue.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_labeler.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_labeler_evidence_matrix.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_launch.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_ocp.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_render.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_runfile.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_graph_schema.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_ingest.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_ingest_claude_code.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_ingest_codex.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_ingest_ocp.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_io.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_not_run_summary.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_ocp_conformance.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_ocp_golden.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_ocp_open_enums.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_ocp_versioning.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_output_html_path.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_packaging_metadata.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_packaging_smoke.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_pool.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_preview_script.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_price.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_receipts.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_release_paths.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_research_cli.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_runfile_privacy_n1b.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_scoring.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_split_rebindings.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_starting_prior.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_surface.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_taskmodel_features.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_types_control.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_types_piece_prediction.py +0 -0
- {loopmath-0.2.4 → loopmath-0.2.5}/tests/test_v01_scaffold.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loopmath
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
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
|
|
@@ -241,7 +241,7 @@ The store is `~/.loopmath`. Set `LOOPMATH_HOME` or pass `--home` to put it elsew
|
|
|
241
241
|
- signals, receipts and fits;
|
|
242
242
|
- your config and your workflows;
|
|
243
243
|
- the views you write;
|
|
244
|
-
- the caches: the log parse cache, and the folder where `fit --full` compiles its model (when that compile fails, it also leaves the C code it tried in the system temp folder and prints where).
|
|
244
|
+
- the caches: the log parse cache, and the folder where `fit --full` compiles its model (when that compile fails, it also leaves the C code it tried in a `pytensor_compilation_error_*` file in the system temp folder and prints where).
|
|
245
245
|
|
|
246
246
|
Do not edit the store by hand; use the commands. `loopmath verify-receipts` checks the receipts in your store; give a file to verify a research receipt ledger instead.
|
|
247
247
|
|
|
@@ -260,7 +260,7 @@ loopmath analyze # cost per accepted run from the last 14 days o
|
|
|
260
260
|
loopmath graph --workspace NAME --format html --out graph.html
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
-
`analyze` reads `~/.claude/projects` and `~/.codex/sessions`. It grades the outcome evidence it finds, prices known models, and prints cost per accepted run with an 80% band. It also lists what it could not use and why; when no workflow configuration
|
|
263
|
+
`analyze` reads `~/.claude/projects` and `~/.codex/sessions`. It grades the outcome evidence it finds, prices known models, and prints cost per accepted run with an 80% band. It also lists what it could not use and why; when no workflow configuration is eligible to compare, it names the largest comparison exclusion, its count, and what to try. `loopmath analyze --json` prints the same as one JSON object, and `--home PATH` keeps its parse cache in that store.
|
|
264
264
|
|
|
265
265
|
`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
266
|
|
|
@@ -203,7 +203,7 @@ The store is `~/.loopmath`. Set `LOOPMATH_HOME` or pass `--home` to put it elsew
|
|
|
203
203
|
- signals, receipts and fits;
|
|
204
204
|
- your config and your workflows;
|
|
205
205
|
- the views you write;
|
|
206
|
-
- the caches: the log parse cache, and the folder where `fit --full` compiles its model (when that compile fails, it also leaves the C code it tried in the system temp folder and prints where).
|
|
206
|
+
- the caches: the log parse cache, and the folder where `fit --full` compiles its model (when that compile fails, it also leaves the C code it tried in a `pytensor_compilation_error_*` file in the system temp folder and prints where).
|
|
207
207
|
|
|
208
208
|
Do not edit the store by hand; use the commands. `loopmath verify-receipts` checks the receipts in your store; give a file to verify a research receipt ledger instead.
|
|
209
209
|
|
|
@@ -222,7 +222,7 @@ loopmath analyze # cost per accepted run from the last 14 days o
|
|
|
222
222
|
loopmath graph --workspace NAME --format html --out graph.html
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
-
`analyze` reads `~/.claude/projects` and `~/.codex/sessions`. It grades the outcome evidence it finds, prices known models, and prints cost per accepted run with an 80% band. It also lists what it could not use and why; when no workflow configuration
|
|
225
|
+
`analyze` reads `~/.claude/projects` and `~/.codex/sessions`. It grades the outcome evidence it finds, prices known models, and prints cost per accepted run with an 80% band. It also lists what it could not use and why; when no workflow configuration is eligible to compare, it names the largest comparison exclusion, its count, and what to try. `loopmath analyze --json` prints the same as one JSON object, and `--home PATH` keeps its parse cache in that store.
|
|
226
226
|
|
|
227
227
|
`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.
|
|
228
228
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "loopmath"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.5"
|
|
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"
|
|
@@ -103,7 +103,7 @@ _sys.modules[__name__].__class__ = _OpenCodeModule
|
|
|
103
103
|
del _ModuleType, _OpenCodeModule, _opencode_store, _sys
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
_EXT = "dev.
|
|
106
|
+
_EXT = "dev.loopmath.adapter.opencode"
|
|
107
107
|
|
|
108
108
|
def _session_created(session: _SessionSource) -> datetime | None:
|
|
109
109
|
return _timestamp(_as_mapping(session.info.get("time")).get("created"))
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
One Pi session is one OCP node with one attempt. Usage-bearing entries are
|
|
4
4
|
measured requests: their token and USD fields are summed into the attempt's
|
|
5
|
-
core cost record, while ``dev.
|
|
5
|
+
core cost record, while ``dev.loopmath.adapter.pi.usage_records`` retains the exact
|
|
6
6
|
per-entry breakdown that OCP v0.2 cannot express. Model labels on assistant
|
|
7
7
|
messages are verified; thinking effort is reported by Pi's explicit
|
|
8
8
|
``thinking_level_change`` entries.
|
|
@@ -57,7 +57,7 @@ from .registry import register
|
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
_DEFAULT_STORE = Path.home() / ".pi" / "agent" / "sessions"
|
|
60
|
-
_EXT = "dev.
|
|
60
|
+
_EXT = "dev.loopmath.adapter.pi"
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
class _PiModule(_ModuleType):
|
|
@@ -741,10 +741,19 @@ def parse_run(doc: dict, *, now: datetime | None = None, rule: AcceptanceRule |
|
|
|
741
741
|
|
|
742
742
|
nodes = {n.get("id"): n for n in doc.get("nodes") or [] if isinstance(n, dict)}
|
|
743
743
|
node_vertex = {i: n.get("vertex") for i, n in nodes.items()}
|
|
744
|
+
from ..ocp.membership import imported_node_map
|
|
745
|
+
|
|
746
|
+
imported_vertices = imported_node_map(doc)
|
|
744
747
|
attempts: list[AttemptObs] = []
|
|
745
748
|
by_piece_round: dict[tuple[str, int], list[dict]] = {}
|
|
746
749
|
for a in doc.get("attempts") or []:
|
|
747
750
|
piece = a.get("vertex") or node_vertex.get(a.get("node"))
|
|
751
|
+
node = nodes.get(a.get("node"))
|
|
752
|
+
if (piece not in st.settings and node is not None and "vertex" not in a and "vertex" not in node
|
|
753
|
+
and _check_name(a, node) is None):
|
|
754
|
+
mapped = imported_vertices.get(a.get("node"))
|
|
755
|
+
if isinstance(mapped, str) and mapped in st.settings:
|
|
756
|
+
piece = mapped
|
|
748
757
|
if piece not in st.settings:
|
|
749
758
|
name = _check_name(a, nodes.get(a.get("node")) or {})
|
|
750
759
|
if name is not None:
|
|
@@ -100,19 +100,9 @@ def _extremes_ratio(df, result, price_mod) -> dict:
|
|
|
100
100
|
table = result.get("table")
|
|
101
101
|
n_configs = 0 if table is None else len(table)
|
|
102
102
|
if n_configs < 2:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"no workflow configuration met the minimum run count, so there "
|
|
107
|
-
"is nothing to compare"
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
return {
|
|
111
|
-
"unavailable": (
|
|
112
|
-
"only one workflow configuration met the minimum run count, so "
|
|
113
|
-
"there is nothing to compare"
|
|
114
|
-
)
|
|
115
|
-
}
|
|
103
|
+
from .report.eligibility import unavailable_lines
|
|
104
|
+
|
|
105
|
+
return {"unavailable": "; ".join(unavailable_lines(result, n_configs))}
|
|
116
106
|
ordered = table.sort_values("cost_per_accepted")
|
|
117
107
|
cheap = ordered.iloc[0]["arm"]
|
|
118
108
|
dear = ordered.iloc[-1]["arm"]
|
|
@@ -86,7 +86,7 @@ const App = (() => {
|
|
|
86
86
|
const small = Number(v.toFixed(digits));
|
|
87
87
|
if (small >= 10 ** (1 - digits)) digits -= 1; // 0.00096 rounds up to $0.001, still one digit
|
|
88
88
|
if (small < 0.01) return '$' + small.toFixed(digits);
|
|
89
|
-
|
|
89
|
+
return '$' + small.toFixed(2); // 0.0096 rounds up to $0.01
|
|
90
90
|
}
|
|
91
91
|
return '$' + v.toFixed(v < 0.1 ? 3 : 2);
|
|
92
92
|
},
|
|
@@ -31,9 +31,9 @@ Mapping summary (contract v3 -> OCP 0.1):
|
|
|
31
31
|
task.policy -> node.ext["dev.dagr.policy"] (typed, inert)
|
|
32
32
|
attempt.locator -> dropped (volatile runtime address)
|
|
33
33
|
attempt.liveness -> dropped (live-view data, not telemetry)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
graph export ext -> per-attempt costs and full model/harness/effort,
|
|
35
|
+
joined by task and attempt IDs. Plain contracts
|
|
36
|
+
without this telemetry keep costs absent.
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
39
|
from __future__ import annotations
|
|
@@ -146,6 +146,9 @@ def convert(doc):
|
|
|
146
146
|
raise ValueError("input is not a dagr contract run document")
|
|
147
147
|
# The contract's own version key is 'dagr' (the pre-rename product name).
|
|
148
148
|
contract_version = doc.get("dagr", 1)
|
|
149
|
+
from .graph_contract import telemetry
|
|
150
|
+
|
|
151
|
+
graph_records = telemetry(doc)
|
|
149
152
|
|
|
150
153
|
run_in = doc["run"]
|
|
151
154
|
run = {"id": run_in.get("id", "unknown-run")}
|
|
@@ -159,6 +162,9 @@ def convert(doc):
|
|
|
159
162
|
"source_contract": f"dagr/{contract_version}",
|
|
160
163
|
}
|
|
161
164
|
put(producer, "emitted_at", doc.get("generated_at"))
|
|
165
|
+
if graph_records is not None:
|
|
166
|
+
producer["capabilities"] = {"groups": True, "events": True, "edges_dep": True,
|
|
167
|
+
"cost_usd": True, "cost_tokens": True, "outcome_evidence": True}
|
|
162
168
|
|
|
163
169
|
groups = []
|
|
164
170
|
for prj in doc.get("projects", []) or []:
|
|
@@ -190,7 +196,10 @@ def convert(doc):
|
|
|
190
196
|
edges.append({"from": src, "to": tid, "kind": "dep"})
|
|
191
197
|
|
|
192
198
|
for att in task.get("attempts", []) or []:
|
|
193
|
-
|
|
199
|
+
converted = convert_attempt(tid, att)
|
|
200
|
+
if graph_records is not None:
|
|
201
|
+
converted.update(graph_records[tid, converted["id"]])
|
|
202
|
+
attempts.append(converted)
|
|
194
203
|
|
|
195
204
|
events = []
|
|
196
205
|
for ev in doc.get("events", []) or []:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Read the per-attempt telemetry carried by loopmath's graph contract export."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import copy
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
KEYS = ("dev.loopmath.graph", "dev.dagr.graph")
|
|
8
|
+
TOKEN_FIELDS = ("input_tokens", "cached_input_tokens", "cache_creation_tokens", "cache_creation_5m_tokens",
|
|
9
|
+
"cache_creation_1h_tokens", "output_tokens", "reasoning_tokens", "requests")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def extension(obj: dict) -> dict | None:
|
|
13
|
+
ext = obj.get("ext") or {}
|
|
14
|
+
if not isinstance(ext, dict):
|
|
15
|
+
return None
|
|
16
|
+
found = [ext[key] for key in KEYS if key in ext]
|
|
17
|
+
if not found:
|
|
18
|
+
return None
|
|
19
|
+
if any(not isinstance(value, dict) for value in found) or any(value != found[0] for value in found[1:]):
|
|
20
|
+
raise ValueError("graph extension is malformed or has conflicting namespaces")
|
|
21
|
+
return found[0]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _cost(value: object) -> dict:
|
|
25
|
+
if not isinstance(value, dict):
|
|
26
|
+
raise ValueError("graph cost must be an object")
|
|
27
|
+
for key in (*TOKEN_FIELDS, "usd"):
|
|
28
|
+
if key not in value:
|
|
29
|
+
continue
|
|
30
|
+
number = value[key]
|
|
31
|
+
if (isinstance(number, bool) or not isinstance(number, (int, float)) or not math.isfinite(number)
|
|
32
|
+
or number < 0 or (key != "usd" and not isinstance(number, int))):
|
|
33
|
+
raise ValueError(f"graph cost {key} must be a finite nonnegative {'number' if key == 'usd' else 'integer'}")
|
|
34
|
+
for key, kind in (("basis", str), ("ext", dict), ("tariff", dict)):
|
|
35
|
+
if key in value and not isinstance(value[key], kind):
|
|
36
|
+
raise ValueError(f"graph cost {key} has the wrong type")
|
|
37
|
+
buckets = ("cache_creation_5m_tokens", "cache_creation_1h_tokens")
|
|
38
|
+
if all(key in value for key in (*buckets, "cache_creation_tokens")):
|
|
39
|
+
if sum(value[key] for key in buckets) != value["cache_creation_tokens"]:
|
|
40
|
+
raise ValueError("graph cost cache retention buckets disagree with their total")
|
|
41
|
+
return copy.deepcopy(value)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def telemetry(doc: dict) -> dict[tuple[str, str], dict] | None:
|
|
45
|
+
"""Return exact task/attempt records; None leaves an ordinary contract unchanged.
|
|
46
|
+
|
|
47
|
+
Identical duplicate representations are harmless. Conflicting or dangling
|
|
48
|
+
references are errors, never a reason to choose or allocate an amount.
|
|
49
|
+
"""
|
|
50
|
+
graph = extension(doc)
|
|
51
|
+
if graph is None:
|
|
52
|
+
return None
|
|
53
|
+
records = {}
|
|
54
|
+
for task in doc.get("tasks") or []:
|
|
55
|
+
tid = task.get("id", "?")
|
|
56
|
+
attempts = task.get("attempts") or []
|
|
57
|
+
meta = extension(task) or {}
|
|
58
|
+
settings = {key: meta[key] for key in ("model", "harness", "effort") if key in meta and meta[key] is not None}
|
|
59
|
+
if settings and len(attempts) != 1:
|
|
60
|
+
raise ValueError(f"graph task {tid!r} has ambiguous task-level model metadata")
|
|
61
|
+
if "model" in settings and (not isinstance(settings["model"], dict)
|
|
62
|
+
or not isinstance(settings["model"].get("raw"), str)):
|
|
63
|
+
raise ValueError("graph model metadata must be a model reference")
|
|
64
|
+
for key in ("harness", "effort"):
|
|
65
|
+
if key in settings and not isinstance(settings[key], str):
|
|
66
|
+
raise ValueError(f"graph {key} metadata must be a string")
|
|
67
|
+
for attempt in attempts:
|
|
68
|
+
aid = attempt.get("id", f"{tid}.a{attempt.get('n', '?')}")
|
|
69
|
+
if not isinstance(tid, str) or not isinstance(aid, str) or (tid, aid) in records:
|
|
70
|
+
raise ValueError("graph task/attempt identity is invalid or duplicated")
|
|
71
|
+
records[tid, aid] = copy.deepcopy(settings)
|
|
72
|
+
costs = graph.get("costs", [])
|
|
73
|
+
if not isinstance(costs, list):
|
|
74
|
+
raise ValueError("graph costs must be an array")
|
|
75
|
+
for row in costs:
|
|
76
|
+
if not isinstance(row, dict) or not isinstance(row.get("task"), str) or not isinstance(row.get("attempt"), str):
|
|
77
|
+
raise ValueError("graph cost needs task and attempt identities")
|
|
78
|
+
pair = (row["task"], row["attempt"])
|
|
79
|
+
if pair not in records:
|
|
80
|
+
raise ValueError(f"graph cost references an unknown task/attempt pair: {pair!r}")
|
|
81
|
+
cost = _cost(row.get("cost"))
|
|
82
|
+
old = records[pair].get("cost")
|
|
83
|
+
if old is not None and old != cost:
|
|
84
|
+
raise ValueError(f"graph cost records conflict for {pair!r}")
|
|
85
|
+
records[pair]["cost"] = cost
|
|
86
|
+
return records
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Read saved import membership without guessing roles or repairing explicit references."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
_ADAPTERS = {"pi", "opencode", "omp", "otel-genai", "paseo", "orca", "bb", "atrium"}
|
|
7
|
+
_MIGRATED_FROM = {"0.1", "0.2", "dagr/1", "dagr/2", "dagr/3"}
|
|
8
|
+
_COST_FIELDS = ("usd", "input_tokens", "cached_input_tokens", "cache_creation_tokens", "output_tokens")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def imported_node_map(doc: dict) -> dict:
|
|
12
|
+
"""Saved node map only for recognized migration or adapter/graph provenance."""
|
|
13
|
+
run = doc.get("run") or {}
|
|
14
|
+
ext = run.get("ext") or {}
|
|
15
|
+
marker = ext.get("dev.loopmath.migration") if isinstance(ext, dict) else None
|
|
16
|
+
migrated = (isinstance(marker, dict) and isinstance(marker.get("from"), str)
|
|
17
|
+
and marker["from"] in _MIGRATED_FROM)
|
|
18
|
+
producer = doc.get("producer") or {}
|
|
19
|
+
name = producer.get("name") if isinstance(producer, dict) else None
|
|
20
|
+
adapter = isinstance(name, str) and name in {
|
|
21
|
+
f"{prefix}/adapter-{a}" for prefix in ("loopmath", "dagr") for a in _ADAPTERS}
|
|
22
|
+
top_ext = doc.get("ext") or {}
|
|
23
|
+
graph = name in ("loopmath", "dagr") and isinstance(top_ext, dict) and any(
|
|
24
|
+
isinstance(top_ext.get(key), dict) for key in ("dev.loopmath.graph", "dev.dagr.graph"))
|
|
25
|
+
config = run.get("configuration") or {}
|
|
26
|
+
mapping = config.get("node_vertex") if isinstance(config, dict) else None
|
|
27
|
+
return mapping if (migrated or adapter or graph) and isinstance(mapping, dict) else {}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def model_work(attempt: dict) -> bool:
|
|
31
|
+
"""The membership counterpart of the fitter's costless, model-free check rule."""
|
|
32
|
+
setting = attempt.get("setting") or {}
|
|
33
|
+
if attempt.get("model") or (isinstance(setting, dict) and setting.get("model")):
|
|
34
|
+
return True
|
|
35
|
+
cost = attempt.get("cost") or {}
|
|
36
|
+
return isinstance(cost, dict) and any(
|
|
37
|
+
isinstance(cost.get(key), (int, float)) and cost[key] > 0 for key in _COST_FIELDS)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def materialize(doc: dict[str, Any]) -> None:
|
|
41
|
+
"""Fill absent vertices from a validated saved map, on the migration's private copy."""
|
|
42
|
+
mapping = imported_node_map(doc)
|
|
43
|
+
if not mapping:
|
|
44
|
+
return
|
|
45
|
+
config = (doc.get("run") or {}).get("configuration") or {}
|
|
46
|
+
workflow = config.get("workflow") or {}
|
|
47
|
+
settings = config.get("settings") or {}
|
|
48
|
+
if not isinstance(workflow, dict) or not isinstance(settings, dict):
|
|
49
|
+
return
|
|
50
|
+
pieces = {p.get("id") for p in workflow.get("pieces") or [] if isinstance(p, dict) and isinstance(p.get("id"), str)}
|
|
51
|
+
by_node: dict[str, list[dict]] = {}
|
|
52
|
+
for attempt in doc.get("attempts") if isinstance(doc.get("attempts"), list) else []:
|
|
53
|
+
if isinstance(attempt, dict) and isinstance(attempt.get("node"), str):
|
|
54
|
+
by_node.setdefault(attempt["node"], []).append(attempt)
|
|
55
|
+
for node in doc.get("nodes") if isinstance(doc.get("nodes"), list) else []:
|
|
56
|
+
if not isinstance(node, dict) or "vertex" in node:
|
|
57
|
+
continue
|
|
58
|
+
nid = node.get("id")
|
|
59
|
+
piece = mapping.get(nid) if isinstance(nid, str) else None
|
|
60
|
+
if not isinstance(piece, str) or piece not in pieces or piece not in settings:
|
|
61
|
+
continue
|
|
62
|
+
attempts = by_node.get(nid, [])
|
|
63
|
+
# A shared node must not give a model-free check (or an explicit empty
|
|
64
|
+
# vertex) membership indirectly through node.vertex.
|
|
65
|
+
eligible = [a for a in attempts if "vertex" not in a and model_work(a)]
|
|
66
|
+
if eligible and len(eligible) == len(attempts):
|
|
67
|
+
node["vertex"] = piece
|
|
68
|
+
for attempt in eligible:
|
|
69
|
+
attempt["vertex"] = piece
|
|
@@ -11,7 +11,8 @@ only when absent:
|
|
|
11
11
|
- `run.task` from `run.labels` (`task`, `type`, `repo`) when any is present:
|
|
12
12
|
`id` from the `task` label, else the run id; `labeled_by` is
|
|
13
13
|
`{how: inferred, tier: heuristic}`;
|
|
14
|
-
-
|
|
14
|
+
- absent model-attempt vertices from the configuration's saved node mapping,
|
|
15
|
+
then `node.vertex` from unanimous `role.value` when still absent;
|
|
15
16
|
- `run.provenance = {kind: logged, chooser: habit}`;
|
|
16
17
|
- `run.configuration = {source: habit}`, plus the inferred workflow and
|
|
17
18
|
settings when `infer` returns one (lane 04's `loopmath.workflows.infer`),
|
|
@@ -106,6 +107,22 @@ def migrate_doc(doc: dict[str, Any], *, infer: Infer | None = infer_configuratio
|
|
|
106
107
|
task["labeled_by"] = {"how": "inferred", "tier": "heuristic"}
|
|
107
108
|
run["task"] = task
|
|
108
109
|
|
|
110
|
+
run.setdefault("provenance", {"kind": "logged", "chooser": "habit"})
|
|
111
|
+
if "configuration" not in run:
|
|
112
|
+
inferred = infer(source if origin else out) if infer is not None else None
|
|
113
|
+
if inferred is not None:
|
|
114
|
+
configuration, confidence = inferred
|
|
115
|
+
configuration = dict(configuration, source="habit")
|
|
116
|
+
configuration.setdefault("ext", {})[INFERRED_KEY] = {"confidence": confidence, "tier": "heuristic"}
|
|
117
|
+
run["configuration"] = configuration
|
|
118
|
+
else:
|
|
119
|
+
run["configuration"] = {"source": "habit"}
|
|
120
|
+
|
|
121
|
+
# The exact inference map takes precedence over a role word generated
|
|
122
|
+
# below, while vertices explicitly present in the input remain untouched.
|
|
123
|
+
from .membership import materialize
|
|
124
|
+
|
|
125
|
+
materialize(out)
|
|
109
126
|
roles: dict[str, set[str]] = {}
|
|
110
127
|
for attempt in _list(out.get("attempts")):
|
|
111
128
|
if not isinstance(attempt, dict):
|
|
@@ -119,14 +136,4 @@ def migrate_doc(doc: dict[str, Any], *, infer: Infer | None = infer_configuratio
|
|
|
119
136
|
if isinstance(nid, str) and "vertex" not in node and len(roles.get(nid, ())) == 1:
|
|
120
137
|
node["vertex"] = next(iter(roles[nid]))
|
|
121
138
|
|
|
122
|
-
run.setdefault("provenance", {"kind": "logged", "chooser": "habit"})
|
|
123
|
-
if "configuration" not in run:
|
|
124
|
-
inferred = infer(source if origin else out) if infer is not None else None
|
|
125
|
-
if inferred is not None:
|
|
126
|
-
configuration, confidence = inferred
|
|
127
|
-
configuration = dict(configuration, source="habit")
|
|
128
|
-
configuration.setdefault("ext", {})[INFERRED_KEY] = {"confidence": confidence, "tier": "heuristic"}
|
|
129
|
-
run["configuration"] = configuration
|
|
130
|
-
else:
|
|
131
|
-
run["configuration"] = {"source": "habit"}
|
|
132
139
|
return out
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Explain comparison eligibility using the surface's existing exclusion counts."""
|
|
2
|
+
from collections import Counter
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
COMPARISON = (
|
|
6
|
+
"configuration below min_overlap_cells",
|
|
7
|
+
"run's task-mix cell not shared with another configuration",
|
|
8
|
+
"configuration below min_n",
|
|
9
|
+
)
|
|
10
|
+
EARLY = ("unknown acceptance", "no usable cost value", "no model label")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def unavailable_lines(surface: dict, n_configs: int = 0) -> list[str]:
|
|
14
|
+
if n_configs == 1:
|
|
15
|
+
return ["only one workflow configuration is eligible, so there is nothing to compare"]
|
|
16
|
+
counts = Counter()
|
|
17
|
+
for item in surface.get("exclusions") or []:
|
|
18
|
+
counts[item.get("reason")] += max(0, int(item.get("n") or 0))
|
|
19
|
+
candidates = sum(counts[r] for r in COMPARISON) + int(surface.get("n_rows_used") or 0)
|
|
20
|
+
choices = [r for r in COMPARISON if counts[r] > 0]
|
|
21
|
+
population = f"{candidates:,} runs with known outcomes, usable cost and model labels"
|
|
22
|
+
if not choices:
|
|
23
|
+
choices = [r for r in EARLY if counts[r] > 0]
|
|
24
|
+
population = f"{int(surface.get('n_rows') or sum(counts.values())):,} runs read"
|
|
25
|
+
if not choices:
|
|
26
|
+
return ["no workflow configuration is eligible, so there is nothing to compare"]
|
|
27
|
+
# A tie follows the fixed presentation order above, never input ordering.
|
|
28
|
+
reason = max(choices, key=counts.__getitem__)
|
|
29
|
+
description, remedy = {
|
|
30
|
+
COMPARISON[0]: ("their configurations lack enough shared task-mix cells (overlap)",
|
|
31
|
+
"read more comparable history with shared task-mix cells (--all or a larger --since)"),
|
|
32
|
+
COMPARISON[1]: ("their task-mix cells are not shared with another configuration",
|
|
33
|
+
"read more comparable history with shared task-mix cells (--all or a larger --since)"),
|
|
34
|
+
COMPARISON[2]: (f"their configurations fall below the minimum run count (--min-n {surface.get('min_n', 5)})",
|
|
35
|
+
"read more history (--all or a larger --since), or lower the minimum (--min-n 3)"),
|
|
36
|
+
EARLY[0]: ("they have unknown acceptance outcomes", "see which evidence counted as acceptance (--grading)"),
|
|
37
|
+
EARLY[1]: ("they have no usable cost", "check missing prices or token counts for the selected cost basis"),
|
|
38
|
+
EARLY[2]: ("they have no model label", "check that the imported history records model names"),
|
|
39
|
+
}[reason]
|
|
40
|
+
lines = [f"no workflow configuration is eligible; the largest exclusion is {counts[reason]:,} of {population}: {description}"]
|
|
41
|
+
early = [f"{counts[r]:,} {r}" for r in EARLY if counts[r]]
|
|
42
|
+
if reason in COMPARISON and early:
|
|
43
|
+
lines.append("before comparison: " + ", ".join(early))
|
|
44
|
+
lines.append("to get a table: " + remedy)
|
|
45
|
+
return lines
|
|
@@ -14,6 +14,8 @@ from pathlib import Path
|
|
|
14
14
|
from typing import Any, Iterable
|
|
15
15
|
|
|
16
16
|
from .terminal import PRIVACY_LINE, beat3_exclusions
|
|
17
|
+
from .eligibility import unavailable_lines
|
|
18
|
+
from ..surface_presentation import shown_band_support_note
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
_STYLE = """
|
|
@@ -130,7 +132,7 @@ def _configuration_table(surface: dict[str, Any]) -> str:
|
|
|
130
132
|
key=_sort_value,
|
|
131
133
|
)
|
|
132
134
|
if not rows:
|
|
133
|
-
return '<p class="note">
|
|
135
|
+
return "".join(f'<p class="note">{escape(line)}</p>' for line in unavailable_lines(surface))
|
|
134
136
|
|
|
135
137
|
scale_low, scale_high = _band_positions(rows)
|
|
136
138
|
span = scale_high - scale_low
|
|
@@ -237,8 +239,9 @@ def render(
|
|
|
237
239
|
exclusions = "".join(f"<li>{escape(line.strip())}</li>" for line in exclusion_lines)
|
|
238
240
|
notices = [
|
|
239
241
|
surface.get(key)
|
|
240
|
-
for key in ("tier_note", "overlap_caveat", "band_note"
|
|
242
|
+
for key in ("tier_note", "overlap_caveat", "band_note")
|
|
241
243
|
]
|
|
244
|
+
notices.append(shown_band_support_note(surface))
|
|
242
245
|
notices.extend(price_warnings or [])
|
|
243
246
|
notes_html = "".join(f'<p class="note">{escape(str(note))}</p>' for note in notices if note)
|
|
244
247
|
coverage_text = coverage_line or "No grading coverage available."
|
|
@@ -137,24 +137,9 @@ PRIVACY_LINE = "Local only. Nothing uploaded."
|
|
|
137
137
|
|
|
138
138
|
def _no_configuration_lines(surface: dict) -> list[str]:
|
|
139
139
|
"""Why the table is empty, from the surface's own exclusion counts, and what to try."""
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
head = (f" no workflow configuration has {min_n} runs with a known outcome (the minimum, --min-n)"
|
|
144
|
-
if min_n else " no configurations met the minimum run count")
|
|
145
|
-
lines = [head]
|
|
146
|
-
parts = []
|
|
147
|
-
for reason, words in (("unknown acceptance", "had no acceptance evidence in the logs"),
|
|
148
|
-
("configuration below min_n", "are in configurations with fewer runs than that"),
|
|
149
|
-
("no usable cost value", "had no usable cost"),
|
|
150
|
-
("no model label", "had no model name")):
|
|
151
|
-
if counts.get(reason):
|
|
152
|
-
parts.append(f"{_fmt_int(counts[reason])} {words}")
|
|
153
|
-
if parts and n_rows:
|
|
154
|
-
lines.append(f" of {_fmt_int(n_rows)} runs read, " + ", ".join(parts))
|
|
155
|
-
lines.append(" to get a table: read more history (--all, or a larger --since), lower the minimum "
|
|
156
|
-
"(--min-n 3), or see which evidence counted as acceptance (--grading)")
|
|
157
|
-
return lines
|
|
140
|
+
from .eligibility import unavailable_lines
|
|
141
|
+
|
|
142
|
+
return [" " + line for line in unavailable_lines(surface)]
|
|
158
143
|
|
|
159
144
|
|
|
160
145
|
def beat2_configurations(
|
|
@@ -113,7 +113,8 @@ def spawn_fit(home: Path, *, no_prior: bool | None = None, without: tuple[str, .
|
|
|
113
113
|
|
|
114
114
|
With neither `no_prior` nor `without` given (the store's own refits) the fit leaves out what the
|
|
115
115
|
latest fit left out, decided when it runs; `fit --background` passes its flags, even empty ones."""
|
|
116
|
-
|
|
116
|
+
# Anchor paths in the caller before the child changes its working directory.
|
|
117
|
+
home = Path(home).expanduser().resolve()
|
|
117
118
|
fits = _fits(home)
|
|
118
119
|
inherit = no_prior is None and without is None
|
|
119
120
|
opts = _opts(bool(no_prior), without or (), full, inherit=inherit)
|
|
@@ -132,6 +133,9 @@ def spawn_fit(home: Path, *, no_prior: bool | None = None, without: tuple[str, .
|
|
|
132
133
|
if full:
|
|
133
134
|
cmd.append("--full")
|
|
134
135
|
env = dict(os.environ)
|
|
136
|
+
env["LOOPMATH_HOME"] = str(home)
|
|
137
|
+
if env.get("LOOPMATH_CACHE_DIR"):
|
|
138
|
+
env["LOOPMATH_CACHE_DIR"] = str(Path(env["LOOPMATH_CACHE_DIR"]).expanduser().resolve())
|
|
135
139
|
root = _package_root()
|
|
136
140
|
env["PYTHONPATH"] = root + (os.pathsep + env["PYTHONPATH"] if env.get("PYTHONPATH") else "")
|
|
137
141
|
log_path = fits / "fit.log"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: loopmath
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
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
|
|
@@ -241,7 +241,7 @@ The store is `~/.loopmath`. Set `LOOPMATH_HOME` or pass `--home` to put it elsew
|
|
|
241
241
|
- signals, receipts and fits;
|
|
242
242
|
- your config and your workflows;
|
|
243
243
|
- the views you write;
|
|
244
|
-
- the caches: the log parse cache, and the folder where `fit --full` compiles its model (when that compile fails, it also leaves the C code it tried in the system temp folder and prints where).
|
|
244
|
+
- the caches: the log parse cache, and the folder where `fit --full` compiles its model (when that compile fails, it also leaves the C code it tried in a `pytensor_compilation_error_*` file in the system temp folder and prints where).
|
|
245
245
|
|
|
246
246
|
Do not edit the store by hand; use the commands. `loopmath verify-receipts` checks the receipts in your store; give a file to verify a research receipt ledger instead.
|
|
247
247
|
|
|
@@ -260,7 +260,7 @@ loopmath analyze # cost per accepted run from the last 14 days o
|
|
|
260
260
|
loopmath graph --workspace NAME --format html --out graph.html
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
-
`analyze` reads `~/.claude/projects` and `~/.codex/sessions`. It grades the outcome evidence it finds, prices known models, and prints cost per accepted run with an 80% band. It also lists what it could not use and why; when no workflow configuration
|
|
263
|
+
`analyze` reads `~/.claude/projects` and `~/.codex/sessions`. It grades the outcome evidence it finds, prices known models, and prints cost per accepted run with an 80% band. It also lists what it could not use and why; when no workflow configuration is eligible to compare, it names the largest comparison exclusion, its count, and what to try. `loopmath analyze --json` prints the same as one JSON object, and `--home PATH` keeps its parse cache in that store.
|
|
264
264
|
|
|
265
265
|
`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
266
|
|
|
@@ -180,6 +180,8 @@ src/loopmath/ocp/commands.py
|
|
|
180
180
|
src/loopmath/ocp/conformance.py
|
|
181
181
|
src/loopmath/ocp/contractv3.py
|
|
182
182
|
src/loopmath/ocp/emit.py
|
|
183
|
+
src/loopmath/ocp/graph_contract.py
|
|
184
|
+
src/loopmath/ocp/membership.py
|
|
183
185
|
src/loopmath/ocp/migrate.py
|
|
184
186
|
src/loopmath/ocp/version.py
|
|
185
187
|
src/loopmath/ocp/schema/ocp-v0.2.schema.json
|
|
@@ -223,6 +225,7 @@ src/loopmath/recommend/search.py
|
|
|
223
225
|
src/loopmath/recommend/stats.py
|
|
224
226
|
src/loopmath/recommend/storeread.py
|
|
225
227
|
src/loopmath/report/__init__.py
|
|
228
|
+
src/loopmath/report/eligibility.py
|
|
226
229
|
src/loopmath/report/html.py
|
|
227
230
|
src/loopmath/report/terminal.py
|
|
228
231
|
src/loopmath/report/terminal_exclusions.py
|
|
@@ -361,6 +364,7 @@ tests/test_preview_script.py
|
|
|
361
364
|
tests/test_price.py
|
|
362
365
|
tests/test_receipts.py
|
|
363
366
|
tests/test_release_paths.py
|
|
367
|
+
tests/test_report_eligibility.py
|
|
364
368
|
tests/test_research_cli.py
|
|
365
369
|
tests/test_runfile_privacy_n1b.py
|
|
366
370
|
tests/test_scoring.py
|