loopmath 0.2.5__tar.gz → 0.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (384) hide show
  1. {loopmath-0.2.5/src/loopmath.egg-info → loopmath-0.2.6}/PKG-INFO +2 -2
  2. {loopmath-0.2.5 → loopmath-0.2.6}/README.md +1 -1
  3. {loopmath-0.2.5 → loopmath-0.2.6}/pyproject.toml +1 -1
  4. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/__init__.py +1 -1
  5. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/atrium.py +1 -1
  6. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/paseo.py +1 -1
  7. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/commands.py +2 -2
  8. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/design.py +7 -6
  9. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/fit.py +12 -5
  10. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/forest.py +2 -2
  11. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/pricing.py +1 -1
  12. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/priors.py +18 -18
  13. loopmath-0.2.6/src/loopmath/builder/__init__.py +1 -0
  14. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/builder/context.py +4 -4
  15. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/builder/predict.py +5 -5
  16. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/cli_registry.py +7 -6
  17. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/cli_support.py +28 -37
  18. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/e0/arms.py +19 -22
  19. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/e0/figures.py +1 -1
  20. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/fit.py +36 -44
  21. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/fit_assembly.py +7 -7
  22. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/fit_predict.py +1 -1
  23. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/fit_pricing.py +9 -9
  24. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/fit_transfer.py +4 -4
  25. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/grade.py +5 -5
  26. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/grade_support.py +1 -1
  27. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/dataset_build.py +1 -1
  28. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/dataset_core.py +1 -1
  29. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/extract.py +14 -14
  30. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/html_common.py +32 -9
  31. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/html_costcurve.py +7 -3
  32. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/html_force.py +7 -3
  33. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/html_swimlanes.py +1 -1
  34. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labels.py +1 -1
  35. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/launch.py +1 -1
  36. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/launch_origin.py +2 -2
  37. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/ocp_support.py +4 -4
  38. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/scan.py +5 -5
  39. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/schema.py +1 -1
  40. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/__init__.py +13 -15
  41. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/base.py +1 -1
  42. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/claude_code.py +4 -4
  43. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/codex.py +13 -13
  44. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/codex_support.py +2 -2
  45. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/logmatch/artifacts.py +10 -5
  46. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/logmatch/clip.py +35 -21
  47. loopmath-0.2.6/src/loopmath/logmatch/codex_window.py +164 -0
  48. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/logmatch/match.py +3 -1
  49. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/logmatch/settle.py +18 -11
  50. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/logmatch/tariff.py +1 -1
  51. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/migrate.py +2 -2
  52. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/onboard/commands.py +4 -4
  53. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/onboard/history.py +3 -3
  54. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/onboard/label.py +1 -1
  55. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/onboard/usual.py +1 -1
  56. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/output.py +1 -1
  57. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/price.py +1 -1
  58. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/prices.toml +1 -1
  59. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/__init__.py +5 -5
  60. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/benchmarks.py +4 -4
  61. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/benchmarks.toml +114 -115
  62. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/build.py +1 -1
  63. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/commands.py +2 -2
  64. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/e0.py +4 -4
  65. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/history.py +1 -1
  66. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/lanes.py +3 -3
  67. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/ocpdoc.py +10 -10
  68. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/registry.py +3 -3
  69. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/rq1.py +22 -22
  70. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/show.py +3 -3
  71. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/sweep.py +1 -1
  72. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/validate.py +3 -3
  73. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/receipts.py +1 -1
  74. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/commands.py +21 -20
  75. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/curve.py +1 -1
  76. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/engine.py +12 -12
  77. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/message.py +7 -7
  78. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/receipts.py +1 -1
  79. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/storeread.py +6 -6
  80. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/report/terminal.py +10 -12
  81. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/report/terminal_read.py +5 -6
  82. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/share/commands.py +14 -2
  83. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/share/export.py +4 -4
  84. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/share/import_.py +1 -1
  85. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/skills/loopmath-record-run/SKILL.md +1 -1
  86. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/skills/reference.md +1 -1
  87. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/commands.py +4 -4
  88. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/config.py +13 -3
  89. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/finish.py +24 -23
  90. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/fitjob.py +4 -4
  91. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/home.py +2 -2
  92. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/ids.py +1 -1
  93. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/record.py +38 -10
  94. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/runs.py +2 -2
  95. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/status.py +1 -1
  96. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/surface.py +6 -7
  97. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/surface_bootstrap.py +4 -4
  98. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/surface_estimation.py +15 -16
  99. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/surface_frame.py +1 -1
  100. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/surface_presentation.py +7 -8
  101. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/surface_support.py +4 -5
  102. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/taskmodel.py +1 -1
  103. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/types.py +2 -2
  104. loopmath-0.2.6/src/loopmath/views/__init__.py +4 -0
  105. loopmath-0.2.6/src/loopmath/views/assets/README.md +1 -0
  106. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/base.css +2 -2
  107. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/builder.css +1 -1
  108. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/builder.js +5 -5
  109. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/common.js +40 -8
  110. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/estimates.js +6 -6
  111. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/graph.css +1 -1
  112. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/graph.js +2 -2
  113. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/plans.css +2 -2
  114. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/plans.js +23 -23
  115. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/results.css +1 -1
  116. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/results.js +11 -11
  117. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/runs.js +2 -2
  118. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/viz.css +2 -2
  119. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/viz.js +5 -5
  120. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/common.py +9 -16
  121. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/plans.py +4 -4
  122. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/posterior.py +25 -25
  123. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/runs.py +31 -15
  124. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/__init__.py +1 -1
  125. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/candidates.py +3 -3
  126. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/commands.py +2 -2
  127. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/graphview.py +2 -2
  128. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/ids.py +3 -3
  129. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/models.py +3 -3
  130. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/ocp.py +4 -4
  131. {loopmath-0.2.5 → loopmath-0.2.6/src/loopmath.egg-info}/PKG-INFO +2 -2
  132. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath.egg-info/SOURCES.txt +1 -0
  133. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapters.py +1 -1
  134. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_analyze_cli.py +1 -1
  135. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_arms.py +11 -5
  136. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_cli_graph.py +9 -9
  137. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_cli_prices.py +1 -1
  138. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_fit.py +6 -6
  139. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_grade.py +6 -6
  140. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_artifacts.py +4 -4
  141. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_cache.py +38 -38
  142. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_codexio.py +4 -4
  143. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_dataset.py +12 -12
  144. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_extract.py +8 -8
  145. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_glue.py +2 -2
  146. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_html.py +1 -1
  147. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_launch.py +3 -3
  148. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_ocp.py +15 -15
  149. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_ingest.py +17 -22
  150. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_ingest_claude_code.py +3 -3
  151. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_ingest_codex.py +8 -8
  152. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_ingest_ocp.py +5 -3
  153. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_price.py +2 -2
  154. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_receipts.py +1 -1
  155. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_release_paths.py +1 -1
  156. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_research_cli.py +1 -1
  157. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_scoring.py +5 -5
  158. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_starting_prior.py +2 -2
  159. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_surface.py +20 -20
  160. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_taskmodel_features.py +1 -1
  161. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_terminal.py +36 -40
  162. loopmath-0.2.5/src/loopmath/builder/__init__.py +0 -1
  163. loopmath-0.2.5/src/loopmath/views/__init__.py +0 -4
  164. loopmath-0.2.5/src/loopmath/views/assets/README.md +0 -1
  165. {loopmath-0.2.5 → loopmath-0.2.6}/LICENSE +0 -0
  166. {loopmath-0.2.5 → loopmath-0.2.6}/setup.cfg +0 -0
  167. {loopmath-0.2.5 → loopmath-0.2.6}/spec/ocp-v0.2.schema.json +0 -0
  168. {loopmath-0.2.5 → loopmath-0.2.6}/spec/ocp-v0.schema.json +0 -0
  169. {loopmath-0.2.5 → loopmath-0.2.6}/spec/ocp_conformance.py +0 -0
  170. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/__main__.py +0 -0
  171. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapter_host.py +0 -0
  172. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/__init__.py +0 -0
  173. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/base.py +0 -0
  174. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/bb.py +0 -0
  175. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/bb_support.py +0 -0
  176. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/omp.py +0 -0
  177. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/omp_facts.py +0 -0
  178. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/omp_links.py +0 -0
  179. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/omp_parse.py +0 -0
  180. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/omp_types.py +0 -0
  181. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/opencode.py +0 -0
  182. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/opencode_store.py +0 -0
  183. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/orca.py +0 -0
  184. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/otel_genai.py +0 -0
  185. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/otel_genai_graph.py +0 -0
  186. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/otel_genai_parse.py +0 -0
  187. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/otel_genai_types.py +0 -0
  188. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/otel_genai_usage.py +0 -0
  189. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/pi.py +0 -0
  190. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/pi_support.py +0 -0
  191. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/adapters/registry.py +0 -0
  192. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/__init__.py +0 -0
  193. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/block.py +0 -0
  194. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/check_pymc.py +0 -0
  195. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/compose.py +0 -0
  196. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/features.py +0 -0
  197. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/gaussian.py +0 -0
  198. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/lookahead.py +0 -0
  199. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/outcome.py +0 -0
  200. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/belief/state.py +0 -0
  201. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/builder/cli.py +0 -0
  202. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/builder/server.py +0 -0
  203. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/cli.py +0 -0
  204. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/cli_graph.py +0 -0
  205. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/e0/__init__.py +0 -0
  206. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/e0/cli_e0.py +0 -0
  207. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/e0/estimate.py +0 -0
  208. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/e0/estimate_rework.py +0 -0
  209. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/e0/io.py +0 -0
  210. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/e0/verb.py +0 -0
  211. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/explain.py +0 -0
  212. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/fit_bayes.py +0 -0
  213. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/fit_masks.py +0 -0
  214. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/gaps.py +0 -0
  215. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/gitwalk.py +0 -0
  216. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/__init__.py +0 -0
  217. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/artifact_git.py +0 -0
  218. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/artifact_join.py +0 -0
  219. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/artifact_kinds.py +0 -0
  220. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/artifacts.py +0 -0
  221. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/bashparse.py +0 -0
  222. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/bashread_ops.py +0 -0
  223. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/bashwrite_ops.py +0 -0
  224. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/bashwrites.py +0 -0
  225. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/cache.py +0 -0
  226. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/codexio.py +0 -0
  227. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/codexio_parse.py +0 -0
  228. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/codexio_scan.py +0 -0
  229. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/dataset.py +0 -0
  230. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/dataset_edges.py +0 -0
  231. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/dataset_nodes.py +0 -0
  232. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/html_css.py +0 -0
  233. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/html_data.py +0 -0
  234. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/html_render.py +0 -0
  235. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler.py +0 -0
  236. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_cli.py +0 -0
  237. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_common.py +0 -0
  238. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_parse.py +0 -0
  239. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_pricing.py +0 -0
  240. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt.py +0 -0
  241. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_build.py +0 -0
  242. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_dispatch.py +0 -0
  243. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_evidence.py +0 -0
  244. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_node.py +0 -0
  245. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_records.py +0 -0
  246. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_result.py +0 -0
  247. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_session.py +0 -0
  248. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_tools.py +0 -0
  249. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_validation.py +0 -0
  250. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_prompt_workflow.py +0 -0
  251. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_report.py +0 -0
  252. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/labeler_score.py +0 -0
  253. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/ocp.py +0 -0
  254. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/ocp_emit.py +0 -0
  255. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/render.py +0 -0
  256. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/runfile.py +0 -0
  257. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/runfile_export.py +0 -0
  258. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/runfile_finish.py +0 -0
  259. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/graph/token_completeness.py +0 -0
  260. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/claude_code_support.py +0 -0
  261. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/ocp.py +0 -0
  262. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/ocp_analysis.py +0 -0
  263. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/ocp_artifact.py +0 -0
  264. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/ocp_common.py +0 -0
  265. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/ocp_convert.py +0 -0
  266. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ingest/ocp_projection.py +0 -0
  267. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/logmatch/__init__.py +0 -0
  268. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/logmatch/costs.py +0 -0
  269. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/__init__.py +0 -0
  270. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/canonical.py +0 -0
  271. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/commands.py +0 -0
  272. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/conformance.py +0 -0
  273. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/contractv3.py +0 -0
  274. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/emit.py +0 -0
  275. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/graph_contract.py +0 -0
  276. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/membership.py +0 -0
  277. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/schema/ocp-v0.2.schema.json +0 -0
  278. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/schema/ocp-v0.3.schema.json +0 -0
  279. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/schema/ocp-v0.schema.json +0 -0
  280. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/ocp/version.py +0 -0
  281. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/onboard/__init__.py +0 -0
  282. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/pool.py +0 -0
  283. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/price_validation.py +0 -0
  284. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/bundle/README.md +0 -0
  285. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/bundle/e0.jsonl.gz +0 -0
  286. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/bundle/lanes.jsonl.gz +0 -0
  287. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/bundle/manifest.json +0 -0
  288. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/bundle/rq1.jsonl.gz +0 -0
  289. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/bundle/sweep.jsonl.gz +0 -0
  290. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/priors/reduce.py +0 -0
  291. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/__init__.py +0 -0
  292. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/backlog.py +0 -0
  293. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/gain.py +0 -0
  294. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/search.py +0 -0
  295. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/recommend/stats.py +0 -0
  296. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/report/__init__.py +0 -0
  297. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/report/eligibility.py +0 -0
  298. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/report/html.py +0 -0
  299. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/report/terminal_exclusions.py +0 -0
  300. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/report/terminal_format.py +0 -0
  301. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/research_defaults.py +0 -0
  302. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/research_paths.py +0 -0
  303. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/scoring.py +0 -0
  304. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/scoring_comparisons.py +0 -0
  305. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/scoring_predictive.py +0 -0
  306. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/share/__init__.py +0 -0
  307. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/__init__.py +0 -0
  308. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/commands.py +0 -0
  309. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/doctor.py +0 -0
  310. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/install.py +0 -0
  311. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/skills/loopmath/SKILL.md +0 -0
  312. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/skills/loopmath-import-runs/SKILL.md +0 -0
  313. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/skills/loopmath-onboard/SKILL.md +0 -0
  314. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/skills/loopmath-plan-task/SKILL.md +0 -0
  315. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/skill/skills/loopmath-update-fit/SKILL.md +0 -0
  316. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/__init__.py +0 -0
  317. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/budget.py +0 -0
  318. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/lock.py +0 -0
  319. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/report.py +0 -0
  320. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/run_names.py +0 -0
  321. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/store/verify.py +0 -0
  322. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/builder.html +0 -0
  323. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/views/assets/estimates.css +0 -0
  324. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/catalog/best_of_n.toml +0 -0
  325. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/catalog/implement_review.toml +0 -0
  326. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/catalog/plan_implement.toml +0 -0
  327. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/catalog/plan_implement_review.toml +0 -0
  328. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/catalog/solo.toml +0 -0
  329. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/catalog/swarm.toml +0 -0
  330. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/diff.py +0 -0
  331. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/format.py +0 -0
  332. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/infer.py +0 -0
  333. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath/workflows/shapes.py +0 -0
  334. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath.egg-info/dependency_links.txt +0 -0
  335. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath.egg-info/entry_points.txt +0 -0
  336. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath.egg-info/requires.txt +0 -0
  337. {loopmath-0.2.5 → loopmath-0.2.6}/src/loopmath.egg-info/top_level.txt +0 -0
  338. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_atrium.py +0 -0
  339. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_bb.py +0 -0
  340. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_omp.py +0 -0
  341. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_opencode.py +0 -0
  342. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_opencode_output_tokens.py +0 -0
  343. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_orca.py +0 -0
  344. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_otel_genai.py +0 -0
  345. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_paseo.py +0 -0
  346. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapter_services.py +0 -0
  347. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_adapters_pi.py +0 -0
  348. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_cli_help_lists.py +0 -0
  349. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_cli_labeler.py +0 -0
  350. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_cli_main_module.py +0 -0
  351. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_cli_plan_hidden.py +0 -0
  352. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_cli_startup.py +0 -0
  353. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_color_proof.py +0 -0
  354. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_duplicate_guards.py +0 -0
  355. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_estimate.py +0 -0
  356. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_explain.py +0 -0
  357. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_gaps.py +0 -0
  358. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_gitwalk.py +0 -0
  359. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_artifact_recording.py +0 -0
  360. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_bashwrites.py +0 -0
  361. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_labeler.py +0 -0
  362. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_labeler_evidence_matrix.py +0 -0
  363. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_render.py +0 -0
  364. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_runfile.py +0 -0
  365. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_graph_schema.py +0 -0
  366. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_home_guard.py +0 -0
  367. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_html.py +0 -0
  368. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_io.py +0 -0
  369. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_not_run_summary.py +0 -0
  370. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_ocp_conformance.py +0 -0
  371. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_ocp_golden.py +0 -0
  372. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_ocp_open_enums.py +0 -0
  373. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_ocp_versioning.py +0 -0
  374. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_output_html_path.py +0 -0
  375. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_packaging_metadata.py +0 -0
  376. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_packaging_smoke.py +0 -0
  377. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_pool.py +0 -0
  378. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_preview_script.py +0 -0
  379. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_report_eligibility.py +0 -0
  380. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_runfile_privacy_n1b.py +0 -0
  381. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_split_rebindings.py +0 -0
  382. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_types_control.py +0 -0
  383. {loopmath-0.2.5 → loopmath-0.2.6}/tests/test_types_piece_prediction.py +0 -0
  384. {loopmath-0.2.5 → loopmath-0.2.6}/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.5
3
+ Version: 0.2.6
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
@@ -118,7 +118,7 @@ loopmath run start --rec REC --choice goal --base-commit SHA --json # the h
118
118
  loopmath run record --run RUN --session UUID --verified tests=pass --json # costs from the logs, the outcome, the receipt
119
119
  ```
120
120
 
121
- With `--run`, a session that started before the run counts from the run's start (a Codex session counts whole). Give `--since TS` and `--until TS` when the task's work began or ended at another time; `--since` before `run start` moves the run's start back. When no commit is found after the base commit, the record says where it looked; pass `--commit SHA`. `loopmath run sessions` lists the recent Claude Code and Codex sessions in this folder with their ids, for `--session ID`. Outside a Claude Code tool shell, `--session self` uses the one Claude Code session in this folder active in the last 10 minutes.
121
+ With `--run`, a session that started before the run counts from the run's start; a Codex session counts the usage it logged inside the window: each reading's increase at the reading's time, a reading on either edge included. Give `--since TS` and `--until TS` when the task's work began or ended at another time; `--since` before `run start` moves the run's start back. When no commit is found after the base commit, the record says where it looked; pass `--commit SHA`. `loopmath run sessions` lists the recent Claude Code and Codex sessions in this folder with their ids, for `--session ID`. Outside a Claude Code tool shell, `--session self` uses the one Claude Code session in this folder active in the last 10 minutes.
122
122
 
123
123
  The same loop step by step, for a tool of your own:
124
124
 
@@ -80,7 +80,7 @@ loopmath run start --rec REC --choice goal --base-commit SHA --json # the h
80
80
  loopmath run record --run RUN --session UUID --verified tests=pass --json # costs from the logs, the outcome, the receipt
81
81
  ```
82
82
 
83
- With `--run`, a session that started before the run counts from the run's start (a Codex session counts whole). Give `--since TS` and `--until TS` when the task's work began or ended at another time; `--since` before `run start` moves the run's start back. When no commit is found after the base commit, the record says where it looked; pass `--commit SHA`. `loopmath run sessions` lists the recent Claude Code and Codex sessions in this folder with their ids, for `--session ID`. Outside a Claude Code tool shell, `--session self` uses the one Claude Code session in this folder active in the last 10 minutes.
83
+ With `--run`, a session that started before the run counts from the run's start; a Codex session counts the usage it logged inside the window: each reading's increase at the reading's time, a reading on either edge included. Give `--since TS` and `--until TS` when the task's work began or ended at another time; `--since` before `run start` moves the run's start back. When no commit is found after the base commit, the record says where it looked; pass `--commit SHA`. `loopmath run sessions` lists the recent Claude Code and Codex sessions in this folder with their ids, for `--session ID`. Outside a Claude Code tool shell, `--session self` uses the one Claude Code session in this folder active in the last 10 minutes.
84
84
 
85
85
  The same loop step by step, for a tool of your own:
86
86
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "loopmath"
7
- version = "0.2.5"
7
+ version = "0.2.6"
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"
@@ -5,4 +5,4 @@ The command line (`loopmath`, or its alias `loop`) is the interface; see the
5
5
  README and `loopmath --help`.
6
6
  """
7
7
 
8
- __version__ = "0.2.5"
8
+ __version__ = "0.2.6"
@@ -10,7 +10,7 @@ does not infer a spawn relation from a subagent-stop record or infer acceptance
10
10
  from segment closure, session end, or subagent stop.
11
11
 
12
12
  The source has no token counts, cost, model, effort, stable task node, or
13
- acceptance result. Its locally inspected data is stale since 2026-08-14 and
13
+ acceptance result. Locally inspected data can be weeks stale, and
14
14
  its pane-to-session coverage is sparse. SQL projection excludes prompt,
15
15
  message, title, body, transcript, actor, and task text before rows reach
16
16
  Python; none is retained or emitted.
@@ -9,7 +9,7 @@ evidence that the work was accepted.
9
9
  The vendor-session join is mechanically supported when
10
10
  ``persistence.sessionId`` equals both available native corroborators,
11
11
  ``persistence.nativeHandle`` and ``runtimeInfo.sessionId``. The matching value
12
- is emitted as ``attempt.session``. Read-only inspection on 2026-09-02 also
12
+ is emitted as ``attempt.session``. A read-only inspection also
13
13
  matched every local value to a Claude or Codex session filename without reading
14
14
  transcripts. This is a verified identity correlation, not a graph relation.
15
15
  Paseo records no distinct launcher or parent endpoint, so this adapter emits no
@@ -1,11 +1,11 @@
1
- """Handler for `loopmath fit` (spec 02, lane 5).
1
+ """Handler for `loopmath fit` (spec 02).
2
2
 
3
3
  `loopmath fit [--background] [--full] [--no-prior] [--without SOURCE ...] [--home PATH] [--json]`
4
4
 
5
5
  - Foreground: builds the belief (belief.fit.fit), waiting up to 30 s for a running fit to
6
6
  finish (exit 4 after that), and prints a summary or `loopmath.fit/1` JSON.
7
7
  - `--background`: detaches and returns at once through the store's fit trigger
8
- (`store.fitjob.spawn_fit`, lane 7), so a running fit queues one more fit instead of
8
+ (`store.fitjob.spawn_fit`), so a running fit queues one more fit instead of
9
9
  racing it.
10
10
  - A `--without` name that matches no source exits 2 and lists the known names; a fit
11
11
  with nothing to fit writes nothing, leaves `fits/latest` where it was and exits 1.
@@ -36,7 +36,7 @@ HEURISTIC_COST_WEIGHT = 0.7 # spec 04 section 2 and D27: allocated (heuristic)
36
36
  USER_SOURCES = ("live", "backlog", "history", "user", "orchestrator", "onboard", "habit", "")
37
37
  HORIZON_NODES = ("horizon:timebox", "horizon:log2h") # fixed terms of a timeboxed run (spec 04 section 1)
38
38
  RQ1_EXT = "dev.loopmath.rq1" # loopmath-exp's ext key; its horizon_s is the fallback for older RQ1 runs
39
- # 0.2.1 (F5, spec 04 section 1): in the cost and tokens rows of a timeboxed task the terms a timebox caps (effort
39
+ # 0.2.1 (spec 04 section 1): in the cost and tokens rows of a timeboxed task the terms a timebox caps (effort
40
40
  # and shape) enter at this value, not 1. Set from the RQ1 2 h runs: open-ended effort terms gave max over low x2.64
41
41
  # (solo luna) and x2.23 (solo sol) against x1.34 and x1.27 observed; ln 1.34 / ln 2.64 = ln 1.27 / ln 2.23 = 0.30.
42
42
  TIMEBOX_EFFORT_COST = 0.3
@@ -465,7 +465,7 @@ def source_label(doc: dict) -> str:
465
465
  share = ext.get("dev.loopmath.share")
466
466
  if isinstance(share, dict):
467
467
  # the importer writes `source` and `task.org` as `shared:<org_hash>` already: never prefix twice. With no
468
- # org at all the source is `shared`, the name `fit --without shared` takes (0.2.3, 22X review N1)
468
+ # org at all the source is `shared`, the name `fit --without shared` takes (0.2.3)
469
469
  org = (share.get("source") or share.get("org") or share.get("org_hash") or (run.get("task") or {}).get("org")
470
470
  or "")
471
471
  name = str(org).removeprefix("shared:")
@@ -640,7 +640,7 @@ def _ocp_fields(cost: dict) -> dict[str, float]:
640
640
 
641
641
  def _price_split(split: dict, table) -> float | None:
642
642
  """Dollars for `{model: OCP token fields}`, each model at its own current rate, summed; None when any
643
- part is unpriced or unlabelled (lane 02's `price.price_model_tokens`)."""
643
+ part is unpriced or unlabelled (`price.price_model_tokens`)."""
644
644
  from ..price import price_model_tokens
645
645
 
646
646
  usd = price_model_tokens(split, table).get("usd")
@@ -721,10 +721,11 @@ def _check_name(a: dict, node: dict) -> str | None:
721
721
 
722
722
 
723
723
  def parse_run(doc: dict, *, now: datetime | None = None, rule: AcceptanceRule | None = None,
724
- origin: str | None = None) -> ParsedRun:
724
+ origin: str | None = None, q_by_tier: dict[str, float] | None = None) -> ParsedRun:
725
725
  """Everything the fit needs from one finished OCP v0.3 run document. Raises Unusable.
726
726
 
727
- `origin` is where the fit found the document (`data_source`); None reads its label.
727
+ `origin` is where the fit found the document (`data_source`); None reads its label. `q_by_tier` is
728
+ `outcome.q.<tier>` from config.toml, passed to `outcome_evidence` (None: the built-in q).
728
729
  """
729
730
  from .outcome import outcome_evidence
730
731
 
@@ -786,7 +787,7 @@ def parse_run(doc: dict, *, now: datetime | None = None, rule: AcceptanceRule |
786
787
 
787
788
  gates = _gate_observations(doc, st, by_piece_round)
788
789
  use_rule = rule or rule_from_doc(doc)
789
- evidence = outcome_evidence(doc, use_rule, now=now or datetime.now().astimezone())
790
+ evidence = outcome_evidence(doc, use_rule, now=now or datetime.now().astimezone(), q_by_tier=q_by_tier)
790
791
  score_meta: dict[str, dict] = {}
791
792
  for s in run.get("signals") or []:
792
793
  if s.get("kind") == "score" and isinstance(s.get("value"), (int, float)) and not isinstance(s.get("value"), bool):
@@ -4,7 +4,7 @@ Spec 04 section 4. `fit()` reads finished OCP v0.3 runs (the store, the shipped
4
4
  shared imports), builds the rows of every head, sets the level scales by empirical Bayes,
5
5
  and writes `fits/<fit>.partial/{state.npz, meta.json, design.json}`, renamed to
6
6
  `fits/<fit>/` when complete; `fits/latest` then points at it. A non-blocking `flock` on
7
- `fits/.fit.lock` stops two fits running together (lane 7's trigger has its own job lock on
7
+ `fits/.fit.lock` stops two fits running together (the store's fit trigger has its own job lock on
8
8
  `fits/.lock`, so the two never wait on each other).
9
9
  """
10
10
 
@@ -136,7 +136,7 @@ def _nothing_to_fit(no_prior: bool, without: tuple[str, ...], dropped: dict[str,
136
136
  # ---------------------------------------------------------------- reading documents
137
137
 
138
138
  def store_docs(home: Path) -> Iterator[dict]:
139
- """Finished run documents in the store, through lane 7's reader (it decides what is finished)."""
139
+ """Finished run documents in the store, through the store's reader (it decides what is finished)."""
140
140
  from ..store.home import Store
141
141
 
142
142
  yield from Store(home).finished_docs()
@@ -183,7 +183,8 @@ def _excluded(source: str, without: tuple[str, ...]) -> bool:
183
183
 
184
184
 
185
185
  def collect_rows(docs: Iterable[dict | tuple[str | None, dict]], *, without: tuple[str, ...] = (),
186
- now: datetime | None = None, features: FeatureSet | None = None):
186
+ now: datetime | None = None, features: FeatureSet | None = None,
187
+ q_by_tier: dict[str, float] | None = None):
187
188
  """Rows of every head from run documents, plus counts of what was used and dropped.
188
189
 
189
190
  `docs` yields documents, or `(origin, document)` pairs from `fit()`: `user` for the store
@@ -199,6 +200,7 @@ def collect_rows(docs: Iterable[dict | tuple[str | None, dict]], *, without: tup
199
200
  their document names, when that is not `user`. Features are read with `features` (the
200
201
  built-ins by default); only admitted values keep their `feature:` terms, and `tally`
201
202
  (features.FeatureTally) holds the support, the admitted values and the recorded horizons.
203
+ `q_by_tier` (`outcome.q.<tier>` from config.toml) reaches each run's outcome; None is the built-in q.
202
204
  """
203
205
  now = now or datetime.now().astimezone()
204
206
  features = features or FeatureSet()
@@ -232,7 +234,7 @@ def collect_rows(docs: Iterable[dict | tuple[str | None, dict]], *, without: tup
232
234
  dropped[SHIPPED_COPY] += 1
233
235
  continue
234
236
  try:
235
- pr = parse_run(doc, now=now, origin=origin)
237
+ pr = parse_run(doc, now=now, origin=origin, q_by_tier=q_by_tier)
236
238
  except Unusable as exc:
237
239
  dropped[str(exc)] += 1
238
240
  continue
@@ -532,6 +534,11 @@ def fit(home: Path, *, no_prior: bool = False, without: tuple[str, ...] = (), fu
532
534
  features, features_error = FeatureSet.from_config(config.get("features")), None
533
535
  except FeatureConfigError as exc: # a hand-edited config.toml: fit with the built-ins, say why
534
536
  features, features_error = FeatureSet(), str(exc)
537
+ from ..store.config import Config
538
+ from .outcome import TIER_Q
539
+
540
+ # `outcome.q.<tier>`, read as `run finish` reads it; unset, the built-in q (0.2.6, fix (1))
541
+ q_by_tier = {tier: Config(home / "config.toml", config).q_for_tier(tier) for tier in TIER_Q}
535
542
 
536
543
  def all_docs() -> Iterator[dict | tuple[str | None, dict]]:
537
544
  if docs is not None:
@@ -544,7 +551,7 @@ def fit(home: Path, *, no_prior: bool = False, without: tuple[str, ...] = (), fu
544
551
  yield from prior_data.shared_docs(home)
545
552
 
546
553
  (heads_rows, score_info, dropped, runs_by_source, config_support, checks, overlap,
547
- labels, tally) = collect_rows(all_docs(), without=without, now=now, features=features)
554
+ labels, tally) = collect_rows(all_docs(), without=without, now=now, features=features, q_by_tier=q_by_tier)
548
555
  removed = {k[len("without "):] for k in dropped if k.startswith("without ")}
549
556
  unknown, known = unknown_sources(without, set(runs_by_source) | removed, bundle_dir)
550
557
  if unknown:
@@ -85,7 +85,7 @@ ROLE_WORDS = ("planner", "implementer", "reviewer", "tester", "referee", "worker
85
85
 
86
86
 
87
87
  def canonical_role(role: str | None) -> str:
88
- """Fold a role word into the types.py vocabulary through `workflows.normalize_role()` (lane 4);
88
+ """Fold a role word into the types.py vocabulary through `workflows.normalize_role()`;
89
89
  no role is `worker`."""
90
90
  return _canonical_role(str(role or "").strip().lower())
91
91
 
@@ -107,7 +107,7 @@ _GPT_RE = re.compile(r"^gpt-(\d+(?:\.\d+)?)(?:-(.+))?$")
107
107
  def canonical_model_id(model: str | None) -> str:
108
108
  """One spelling per model: `claude-opus-5-5`, `claude-haiku-4-5`, `gpt-6-astra`.
109
109
 
110
- Passes through `ingest.base.canonical_model` first (lane 2 owns its aliases), then
110
+ Passes through `ingest.base.canonical_model` first (it owns the aliases), then
111
111
  rewrites Claude ids to the `claude-<family>-<major>[-<minor>]` form of spec 04.
112
112
  """
113
113
  from ..ingest.base import canonical_model
@@ -1,4 +1,4 @@
1
- """The price offset on the cost head (spec 04 section 2, 0.2.2, lane 22C).
1
+ """The price offset on the cost head (spec 04 section 2, 0.2.2).
2
2
 
3
3
  A model's run cost moves with its list price. Every cost row of a model version `m` (not the tokens, gate,
4
4
  success or score rows) carries the fixed term `price:offset` with the value
@@ -1,17 +1,17 @@
1
1
  """Prior bundle and benchmark prior factors (spec 04 section 5).
2
2
 
3
- - The shipped bundle (lane 11) enters the fit as data, each run under its `source` node
4
- (`sweep`, `e0`, `rq1`, `repo_history`). `bundle_docs()` reads it through lane 11's
3
+ - The shipped bundle enters the fit as data, each run under its `source` node
4
+ (`sweep`, `e0`, `rq1`, `repo_history`). `bundle_docs()` reads it through
5
5
  `loopmath.priors.bundle_docs()`; `bundle_entries()` pairs each run with its manifest
6
6
  source, so a store run with a shipped label stays the user's.
7
- - Shared imports (lane 8) enter under their `shared:<org>` source through
7
+ - Shared imports enter under their `shared:<org>` source through
8
8
  `share.import_.shared_runs(home)`.
9
9
  - Benchmark priors (`priors/benchmarks.toml`, layout D36) become Gaussian prior factors on
10
10
  version nodes: success head, the logit gap to the benchmark's reference model with variance
11
11
  `1 / (w p (1 - p))`; cost head, the log cost ratio with variance `1 / w`; tokens head
12
12
  (`kind = "tokens"`, published total tokens for the evaluation), the log token ratio
13
- with variance `1 / w`. `w` is one model's share of the benchmark's weight `w_b` (0.2.3,
14
- lane 23K): the `weight` of its `[[benchmark]]` table (default 5, about 5 runs), shared by
13
+ with variance `1 / w`. `w` is one model's share of the benchmark's weight `w_b` (0.2.3):
14
+ the `weight` of its `[[benchmark]]` table (default 5, about 5 runs), shared by
15
15
  the model's k results on that benchmark, so each gets `w_b / k` and a model with five
16
16
  efforts pulls as hard in total as a model with one. Config `benchmark_prior_weight`, when
17
17
  set, is one `w_b` for every benchmark; 0 turns the factors off.
@@ -33,38 +33,38 @@ PACKAGE_PRIORS = Path(__file__).resolve().parent.parent / "priors"
33
33
 
34
34
 
35
35
  def bundle_docs(bundle_dir: Path | None = None, without: tuple[str, ...] = ()) -> Iterator[dict]:
36
- """OCP v0.3 run documents of the shipped prior bundle, or of `bundle_dir` (lane 11's reader)."""
37
- from .. import priors as lane11
36
+ """OCP v0.3 run documents of the shipped prior bundle, or of `bundle_dir`, read by `loopmath.priors`."""
37
+ from .. import priors as bundle
38
38
 
39
- yield from lane11.bundle_docs(without=tuple(without), directory=bundle_dir)
39
+ yield from bundle.bundle_docs(without=tuple(without), directory=bundle_dir)
40
40
 
41
41
 
42
42
  def bundle_entries(bundle_dir: Path | None = None) -> Iterator[tuple[str, dict]]:
43
43
  """(manifest source, document) for every shipped run: the fit's source for it (spec 04 section 1)."""
44
- from .. import priors as lane11
44
+ from .. import priors as bundle
45
45
 
46
- yield from lane11.bundle_entries(directory=bundle_dir)
46
+ yield from bundle.bundle_entries(directory=bundle_dir)
47
47
 
48
48
 
49
49
  def run_id_of(doc: dict) -> str:
50
- from .. import priors as lane11
50
+ from .. import priors as bundle
51
51
 
52
- return lane11.run_id_of(doc)
52
+ return bundle.run_id_of(doc)
53
53
 
54
54
 
55
55
  def bundle_sources(bundle_dir: Path | None = None) -> list[str]:
56
- """Source names in the shipped prior bundle, or in `bundle_dir` (lane 11's manifest).
56
+ """Source names in the shipped prior bundle, or in `bundle_dir` (from its manifest).
57
57
 
58
58
  Read from `manifest()`: the package attribute `sources` turns into the `priors.sources`
59
59
  submodule once anything imports it, so calling it fails in a full run.
60
60
  """
61
- from .. import priors as lane11
61
+ from .. import priors as bundle
62
62
 
63
- return list(lane11.manifest(bundle_dir).get("sources") or {})
63
+ return list(bundle.manifest(bundle_dir).get("sources") or {})
64
64
 
65
65
 
66
66
  def shared_docs(home: Path) -> Iterator[dict]:
67
- """Runs imported from other organizations (lane 8's `shared_runs`)."""
67
+ """Runs imported from other organizations (`share.import_.shared_runs`)."""
68
68
  from ..share.import_ import shared_runs
69
69
 
70
70
  yield from shared_runs(home)
@@ -116,7 +116,7 @@ def benchmark_factors(path: Path | None = None, *, weight: float | None = None)
116
116
 
117
117
  `weight` (config `benchmark_prior_weight`) overrides every benchmark's own weight; None reads
118
118
  each benchmark's `weight`. A model's k results on one benchmark share its weight: each factor
119
- has weight `w_b / k` (B1). A benchmark with weight 0 or less gives no factors.
119
+ has weight `w_b / k`. A benchmark with weight 0 or less gives no factors.
120
120
  """
121
121
  path = path or benchmark_path()
122
122
  if not path.is_file():
@@ -154,7 +154,7 @@ def benchmark_factors(path: Path | None = None, *, weight: float | None = None)
154
154
  elif kind in ("cost", "tokens") and value > 0 and ref_value > 0: # Tokens feed the tokens head only
155
155
  by_model.setdefault(model, []).append((kind, terms, math.log(value / ref_value), r))
156
156
  for model, items in by_model.items():
157
- w = w_b / len(items) # one budget per model and benchmark (B1)
157
+ w = w_b / len(items) # one budget per model and benchmark
158
158
  share = f"weight {w_b:g} over {len(items)}" if len(items) > 1 else f"weight {w_b:g}"
159
159
  for head, terms, mean, r in items:
160
160
  note = (f"{bid}: {model} {float(r['value'])} against {ref} {ref_value} ({share}; "
@@ -0,0 +1 @@
1
+ """`loopmath builder`: the workflow builder, a local page over the fitted model (spec 02, `builder`)."""
@@ -66,8 +66,8 @@ def rec_task(stored: dict[str, Any], args: argparse.Namespace) -> tuple[Task, bo
66
66
 
67
67
  def asked_task(task: Task, id_given: bool, belief: Any, rule: AcceptanceRule, configs: list) -> Task:
68
68
  """The task as `recommend` asks the belief: the user's when they named it, else `commands.question_task`
69
- keyed as `recommend` keys it, by `commands.question_key(belief)` once `recommend` has it (0.2.1 lane
70
- 21F: the fit's input seed key, so identical fits agree), else by the fit id as in 0.2.0."""
69
+ keyed as `recommend` keys it, by `commands.question_key(belief)` once `recommend` has it (0.2.1:
70
+ the fit's input seed key, so identical fits agree), else by the fit id as in 0.2.0."""
71
71
  if id_given:
72
72
  return task
73
73
  key_of = getattr(C, "question_key", None)
@@ -307,7 +307,7 @@ def candidate_entry(rec: Recommendation, c: Any) -> dict[str, Any]:
307
307
 
308
308
 
309
309
  def rescue_entry(session: Session, cfg: Configuration) -> dict[str, Any]:
310
- """The rescue workflow as a candidate when the search did not offer it (22W N1, 0.2.3): predicted as a
310
+ """The rescue workflow as a candidate when the search did not offer it (0.2.3): predicted as a
311
311
  candidate is, under the recommendation's task, rule and rescue, with origin `rescue`."""
312
312
  rec = session.rec
313
313
  bands: dict[str, Any] = {}
@@ -334,7 +334,7 @@ def context_payload(session: Session) -> dict[str, Any]:
334
334
  offer = [c for c in rec.candidates if c.config.id not in retired]
335
335
  top = offer[:TOP_CANDIDATES]
336
336
  ids = {c.config.id for c in top}
337
- # every option's workflows (the pair's exploration one too) and the rescue workflow, whatever the cutoff (22R B3)
337
+ # every option's workflows (the pair's exploration one too) and the rescue workflow, whatever the cutoff
338
338
  wanted = {rec.usual.config.id, *(i for ch in core["choices"]
339
339
  for i in (ch["config"], *ch.get("members", ()), ch.get("explore_config")) if i)}
340
340
  if rec.rescue_config is not None:
@@ -2,7 +2,7 @@
2
2
 
3
3
  A posted configuration is checked (shape, unknown model, width below 1, a gate whose `on_fail` names no
4
4
  piece, a piece without a setting) and gets its id as loopmath computes it (`workflows.ids.config_id`, via
5
- lane 4's `configuration_from_any`), whatever id it was sent with. It is predicted with the fit's
5
+ `configuration_from_any`), whatever id it was sent with. It is predicted with the fit's
6
6
  `predict_many` path under the recommendation's task, rule and rescue (`engine.predict_with_medians`, which
7
7
  also gives the run cost median the numbers show) and formatted by `Recommendation.numbers()`, so a
8
8
  candidate's numbers here equal its numbers in `recommend --json`. Answers are cached by config id.
@@ -26,7 +26,7 @@ from .context import Session, known_models, numbers_of, with_numbers
26
26
  CACHE_SIZE = 512
27
27
  MAX_MANY = 200 # configurations in one `predict_many` body
28
28
  PIECE_KEYS = ("id", "role")
29
- # 0.2.1 lane 21M: `predict_with_medians` fills `bands` by id and `numbers()` reads `rec.bands`
29
+ # 0.2.1: `predict_with_medians` fills `bands` by id and `numbers()` reads `rec.bands`
30
30
  _BANDS = "bands" in inspect.signature(engine.predict_with_medians).parameters
31
31
 
32
32
 
@@ -136,7 +136,7 @@ def clean(obj: dict[str, Any]) -> tuple[dict[str, Any], list[dict[str, Any]]]:
136
136
  return {**obj, "id": None, "settings": settings}, notes
137
137
 
138
138
 
139
- # P3-21 (0.2.4): a review gate that names a repairer at a round limit of 1 is one of loopmath's own shapes (reviewed
139
+ # 0.2.4: a review gate that names a repairer at a round limit of 1 is one of loopmath's own shapes (reviewed
140
140
  # once; the search builds it and may recommend it). The workflow file check warns about it; the builder page instead
141
141
  # says on the gate what 1 round means, so that warning is not shown here.
142
142
  REVIEW_ONCE = "budget_rounds is 1"
@@ -154,7 +154,7 @@ def parse_config(obj: Any, models: dict[str, int]
154
154
  data, warnings = clean(obj)
155
155
  try:
156
156
  cfg = configuration_from_any(data)
157
- except Exception as exc: # noqa: BLE001 - lane 4's decoder raises several kinds; the page gets the words
157
+ except Exception as exc: # noqa: BLE001 - the workflows decoder raises several kinds; the page gets the words
158
158
  return None, [err(None, f"config: {exc}")], []
159
159
  errors = notes_of(validate_workflow(cfg.workflow) + validate_settings(cfg.workflow, cfg.settings), cfg)
160
160
  for p in cfg.workflow.pieces:
@@ -251,7 +251,7 @@ def failed(cfg: Configuration, exc: Exception) -> dict[str, Any]:
251
251
 
252
252
  def answer_each(session: Session, cfgs: list[Configuration]) -> list[dict[str, Any]]:
253
253
  """`answer_all` in one engine call; when that call fails, one call per configuration, so the configuration
254
- that fails gets its own error and the others their answers, however many were cached (22R B2)."""
254
+ that fails gets its own error and the others their answers, however many were cached."""
255
255
  try:
256
256
  return answer_all(session, cfgs)
257
257
  except Exception as exc: # noqa: BLE001 - retried one by one below
@@ -1,9 +1,9 @@
1
1
  """The 0.1.0 command registry (design/0.1/02-commands.md).
2
2
 
3
3
  Every command and flag of the spec is declared here, and each command points to a
4
- handler `module:function` in the owning lane's package. Handlers are imported only
5
- when their command runs, so a lane's unfinished package never breaks `--help` or
6
- another command. Lanes do not edit this file; a new flag is a request to lane 9.
4
+ handler `module:function` in the owning package. Handlers are imported only
5
+ when their command runs, so a package that fails to import never breaks `--help` or
6
+ another command. A new flag is declared here first.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
@@ -226,7 +226,8 @@ def _add_recording(sub) -> None:
226
226
  q.add_argument("--session", action="append", default=[], metavar="[PIECE=]ID", help="a session that worked on the run: a "
227
227
  "Claude Code session id, a Codex thread id, or self for this Claude Code session (repeatable); "
228
228
  "PIECE= names its piece; run sessions lists the ids. Only the part of a session inside the window "
229
- "counts (a Codex session counts whole)")
229
+ "counts; a Codex session counts the usage it logged inside the window: each reading's increase at "
230
+ "the reading's time, a reading on either edge included")
230
231
  q.add_argument("--cwd", action="append", default=[], metavar="[PIECE=]PATH", help="a folder an agent with no session id "
231
232
  "worked in; its log is matched at finish by folder and time (repeatable)")
232
233
  q.add_argument("--since", default=None, metavar="TS", help="when the work began (e.g. 2h or a local ISO time); with "
@@ -374,7 +375,7 @@ def _add_viewing(sub) -> None:
374
375
  p.add_argument("run_id", nargs="?", default=None, metavar="RUN", help="show this one run in full, as --run RUN")
375
376
  p.add_argument("--type", dest="task_type", default=None, metavar="T", help="only this task type (see loopmath task-types)")
376
377
  p.add_argument("--repo", default=None, metavar="R", help="only this repository")
377
- p.add_argument("--since", default=None, metavar="D", help="only runs started since then: 90d, 2w, 12h, or a date or time such as 2026-09-01 or 2026-09-01T09:00 (local time)")
378
+ p.add_argument("--since", default=None, metavar="D", help="only runs started since then: 90d, 2w, 12h, or a date or time such as 2026-01-15 or 2026-01-15T09:30 (local time)")
378
379
  p.add_argument("--slate", default=None, metavar="SLT", help="only this slate")
379
380
  p.add_argument("--run", default=None, metavar="RUN", help="show this one run in full; RUN is an id from the run column of loopmath runs")
380
381
  p.add_argument("--wide", action="store_true", help="also show each run's cfg_ configuration id")
@@ -460,7 +461,7 @@ def register(sub) -> None:
460
461
  _add_recording(sub)
461
462
  _add_learning(sub)
462
463
  _add_viewing(sub)
463
- importlib.import_module("loopmath.builder.cli").add(sub) # `builder` (0.2.1 lane 21W)
464
+ importlib.import_module("loopmath.builder.cli").add(sub) # `builder` (0.2.1)
464
465
 
465
466
 
466
467
  # Handler targets, for the registry test: every one must import and be callable.
@@ -70,22 +70,16 @@ def _extremes_ratio(df, result, price_mod) -> dict:
70
70
  and mixing an adjusted dollar figure with an unadjusted token figure would
71
71
  make the ratio of the two meaningless.
72
72
 
73
- FIX 1 (reviewer round, blocker): the token sum and the dollar sum for a
74
- configuration used to each skip their own missing values independently
75
- (`.sum(skipna=True)`) while the accepted-run denominator still counted
76
- EVERY accepted run in that configuration. A configuration with some
77
- unpriced or untokenized runs then got a numerator built from a subset and
78
- a denominator built from the whole, understating both ratios -- and the
79
- two halves could rest on DIFFERENT subsets (whichever rows happened to
80
- have a token count vs. whichever had a price), making their ratio (the
81
- amplifier itself) meaningless. Fixed by restricting each configuration to
82
- the rows where BOTH `total_tokens` and `usd` are known and computing the
83
- token sum, the dollar sum, AND the accepted-run count from that one same
84
- restricted set, so numerator and denominator always describe the same
85
- runs.
86
-
87
- The reviewer asked instead for the whole comparison to be withheld
88
- whenever a single value anywhere is missing. That is NOT what this does:
73
+ Each configuration is restricted to the rows where BOTH `total_tokens`
74
+ and `usd` are known, and the token sum, the dollar sum AND the
75
+ accepted-run count all come from that one restricted set, so numerator
76
+ and denominator always describe the same runs. Summing each stream over
77
+ its own known values (`.sum(skipna=True)`) against a count of EVERY
78
+ accepted run would understate both ratios for a configuration with some
79
+ unpriced or untokenized runs, and the two halves could rest on DIFFERENT
80
+ subsets, making their ratio (the amplifier itself) meaningless.
81
+
82
+ The comparison is not withheld when a single value anywhere is missing:
89
83
  the amplifier is the headline finding of the report, and dropping it
90
84
  entirely because one run out of sixty lacks a price would hide more than
91
85
  it protects. Disclosing the exact basis is the honest middle ground: when
@@ -93,9 +87,8 @@ def _extremes_ratio(df, result, price_mod) -> dict:
93
87
  known-outcome row count, `basis_note` on the returned dict says exactly
94
88
  how many runs the numbers rest on and why, for both configurations if
95
89
  they differ, and naming neither when nothing was dropped (measured
96
- against tonight's real data: nothing is, so this key is not set in the
97
- real run -- see the FIX 1 note above `drops` for what "dropped" means
98
- now and why it changed).
90
+ against real data: nothing is, so this key is not set in the
91
+ real run -- see the note above `drops` for what "dropped" means).
99
92
  """
100
93
  table = result.get("table")
101
94
  n_configs = 0 if table is None else len(table)
@@ -114,19 +107,17 @@ def _extremes_ratio(df, result, price_mod) -> dict:
114
107
  # both-known restriction actually dropped a ROW, in `(dear, cheap)`
115
108
  # order; feeds `basis_note` below.
116
109
  #
117
- # FIX 1 (final reviewer round, blocker): this used to compare ACCEPTED
118
- # counts (`n_acc` against `n_acc_all`) to decide whether anything was
119
- # dropped. But the numerator (`tokens`, `usd` below) is a sum over every
110
+ # Whether anything was dropped is decided on ROW counts (`len(sub)`
111
+ # against `len(sub_all)`), not on ACCEPTED counts (`n_acc` against
112
+ # `n_acc_all`). The numerator (`tokens`, `usd` below) is a sum over every
120
113
  # row in `sub` -- accepted AND rejected alike, per the comment on
121
- # `sub_all` -- while `n_acc`/`n_acc_all` only ever count accepted rows.
122
- # A REJECTED row that has a known acceptance outcome but is missing its
123
- # token count or dollar cost gets dropped from `sub` by the both-known
124
- # filter below, shrinking the numerator, yet it was never part of either
125
- # accepted count to begin with, so `n_acc == n_acc_all` stayed true and
126
- # `basis_note` never fired: the understated spend went unreported. Fixed
127
- # by comparing ROW counts (`len(sub)` against `len(sub_all)`), which is
128
- # the actual population the numerator sums over, and wording the note as
129
- # runs actually used out of runs considered rather than "accepted" runs.
114
+ # `sub_all`. A REJECTED row that has a known acceptance outcome but is
115
+ # missing its token count or dollar cost is dropped from `sub` by the
116
+ # both-known filter below, shrinking the numerator, yet it is in neither
117
+ # accepted count, so comparing accepted counts would never fire
118
+ # `basis_note` and the understated spend would go unreported. The note
119
+ # is worded as runs actually used out of runs considered, not "accepted"
120
+ # runs.
130
121
  drops: list[tuple[str, int, int]] = []
131
122
  for arm in (dear, cheap):
132
123
  # `sub_all`: every row for this configuration with a KNOWN acceptance
@@ -145,7 +136,7 @@ def _extremes_ratio(df, result, price_mod) -> dict:
145
136
  )
146
137
  }
147
138
 
148
- # FIX 1: restrict to rows where BOTH streams are known, then take the
139
+ # Restrict to rows where BOTH streams are known, then take the
149
140
  # token sum, the dollar sum, AND the accepted count from this SAME
150
141
  # restricted set -- never a subset for one half and the whole set for
151
142
  # the other.
@@ -210,11 +201,11 @@ def _extremes_ratio(df, result, price_mod) -> dict:
210
201
  pair["label"] = f"{dear} vs {cheap}"
211
202
  pair["basis"] = "per accepted run"
212
203
  if drops:
213
- # FIX 1: name exactly what was dropped rather than staying silent
214
- # about it; report both configurations' counts when they differ, one
215
- # when only one configuration lost any rows. Worded as runs actually
216
- # used out of runs considered (row counts), not accepted counts --
217
- # see the FIX 1 note above `drops` for why that distinction matters.
204
+ # Name exactly what was dropped rather than staying silent about it;
205
+ # report both configurations' counts when they differ, one when only
206
+ # one configuration lost any rows. Worded as runs actually used out
207
+ # of runs considered (row counts), not accepted counts -- see the
208
+ # note above `drops` for why that distinction matters.
218
209
  pieces = [f"{n_used} of {n_considered} runs considered ({a})" for a, n_used, n_considered in drops]
219
210
  pair["basis_note"] = (
220
211
  "computed on " + " and ".join(pieces) + " where both the token count "
@@ -84,34 +84,31 @@ def session_arm(session: dict) -> str | None:
84
84
  return f"{model}·{effort}"
85
85
 
86
86
 
87
- # Project-family collapse: worktree lanes fold into families (design doc
88
- # section 2). Verified against the corpus inventory on 2026-08-30:
89
- # - trailing digits are lane numbers (bots-c1..c6, project-fable2..3)
90
- # - -remediation / -vX.Y-recovery are herdr-dagr recovery lanes
91
- # - pitauri-t1..t7 ("pitauri-teams" sibling set per census section 7) and
92
- # pitauri-ft1..ft5 ("pit-followups") carry task-name suffixes, e.g.
93
- # pitauri-t1-settings, so they collapse by prefix, not by trailing digit
94
- # - -fable / -sol suffixes are paired model lanes of the same remediation
95
- # project (interactions / interactions-fable3 / interactions-sol3 and five
96
- # more base names under herdr-dagr-remediation); they collapse to the base
97
- # so the deliberate fable-vs-sol A/B lanes land in shared cells
98
- _LANE_RE = re.compile(r"(-remediation.*|-v[\d.]+-recovery.*|\d+$)")
99
- _MODEL_LANE_RE = re.compile(r"-(fable|sol)$")
100
- _PITAURI_TEAM_RE = re.compile(r"^pitauri-t\d")
101
- _PITAURI_FT_RE = re.compile(r"^pitauri-ft\d")
87
+ # Project-family collapse: worktree copies fold into families (design doc
88
+ # section 2):
89
+ # - trailing digits are copy numbers (app-c1..c6, app2..3)
90
+ # - -remediation / -vX.Y-recovery are recovery copies
91
+ # - task sibling sets `<base>-t<N>` and follow-up sets `<base>-ft<N>` may carry
92
+ # task-name suffixes (app-t1-settings), so they collapse by prefix, not by
93
+ # trailing digit: to `<base>-teams` and `<base>-ft`
94
+ # - -fable / -sol suffixes are paired model copies of the same remediation
95
+ # project (app / app-fable3 / app-sol3); they collapse to the base so the
96
+ # deliberate fable-vs-sol A/B copies land in shared cells
97
+ _COPY_RE = re.compile(r"(-remediation.*|-v[\d.]+-recovery.*|\d+$)")
98
+ _MODEL_COPY_RE = re.compile(r"-(fable|sol)$")
99
+ _SIBLING_RE = re.compile(r"^(?P<base>.+?)-(?P<kind>ft|t)\d+(?:-|$)")
102
100
 
103
101
 
104
102
  def project_family(project: str | None) -> str:
105
103
  if not project:
106
104
  return "unknown"
107
105
  base = project.rstrip("/").split("/")[-1]
108
- if _PITAURI_FT_RE.match(base):
109
- return "pitauri-ft"
110
- if _PITAURI_TEAM_RE.match(base):
111
- return "pitauri-teams"
112
- base = _LANE_RE.sub("", base)
113
- base = _MODEL_LANE_RE.sub("", base)
114
- if base.startswith("video-"): # video-analyzer <-> video-orchestrator
106
+ sibling = _SIBLING_RE.match(base)
107
+ if sibling:
108
+ return sibling["base"] + ("-ft" if sibling["kind"] == "ft" else "-teams")
109
+ base = _COPY_RE.sub("", base)
110
+ base = _MODEL_COPY_RE.sub("", base)
111
+ if base.startswith("video-"): # every video-* project is one family
115
112
  base = "video"
116
113
  return base or "unknown"
117
114
 
@@ -307,7 +307,7 @@ def write_report(
307
307
  "",
308
308
  f"- real main sessions: {len(segments['main'])}",
309
309
  (
310
- f"- harvest fleet stubs, Aug 23-24 (primary_model null AND "
310
+ f"- fleet stubs (primary_model null AND "
311
311
  f"error_events >= 1): {len(segments['fleet_stubs'])}"
312
312
  ),
313
313
  (