devlyn-cli 3.2.0 → 3.3.0

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 (424) hide show
  1. package/AGENTS.md +2 -2
  2. package/CLAUDE.md +2 -2
  3. package/README.md +22 -113
  4. package/bin/devlyn.js +13 -225
  5. package/bin/instruction-templates.json +752 -1
  6. package/bin/instructions.js +101 -17
  7. package/config/skills/_shared/adapters/grok.md +1 -2
  8. package/config/skills/_shared/archive_run.py +15 -3
  9. package/config/skills/_shared/judge-role-evidence.py +21 -1
  10. package/config/skills/_shared/runtime-principles.md +1 -1
  11. package/config/skills/_shared/spec-verify-check.py +0 -653
  12. package/config/skills/_shared/state-phase-write.py +371 -30
  13. package/config/skills/_shared/task-complete.py +635 -143
  14. package/config/skills/_shared/terminal-claim-check.py +229 -4
  15. package/config/skills/_shared/verify-merge-findings.py +93 -2
  16. package/config/skills/devlyn:ideate/SKILL.md +3 -4
  17. package/config/skills/devlyn:ideate/references/elicitation.md +2 -35
  18. package/config/skills/devlyn:ideate/references/from-spec-mode.md +3 -18
  19. package/config/skills/devlyn:ideate/references/project-mode.md +1 -16
  20. package/config/skills/devlyn:ideate/references/spec-template.md +0 -2
  21. package/config/skills/devlyn:queue/SKILL.md +6 -4
  22. package/config/skills/devlyn:resolve/SKILL.md +16 -25
  23. package/config/skills/devlyn:resolve/references/free-form-mode.md +0 -14
  24. package/config/skills/devlyn:resolve/references/outer-loop.md +1 -1
  25. package/config/skills/devlyn:resolve/references/phases/build-gate.md +1 -1
  26. package/config/skills/devlyn:resolve/references/phases/cleanup.md +3 -3
  27. package/config/skills/devlyn:resolve/references/phases/implement.md +1 -1
  28. package/config/skills/devlyn:resolve/references/phases/probe-derive.md +1 -16
  29. package/config/skills/devlyn:resolve/references/phases/verify.md +4 -9
  30. package/config/skills/devlyn:resolve/references/state-schema.md +9 -7
  31. package/config/skills/devlyn:resolve/references/task-completion.md +46 -37
  32. package/{config/skills → optional-skills}/code-health-standards/SKILL.md +4 -13
  33. package/{config/skills → optional-skills}/code-review-standards/SKILL.md +4 -12
  34. package/{config/skills → optional-skills}/root-cause-analysis/SKILL.md +4 -12
  35. package/{config/skills → optional-skills}/ui-implementation-standards/SKILL.md +4 -14
  36. package/package.json +1 -57
  37. package/benchmark/auto-resolve/BENCHMARK-DESIGN.md +0 -289
  38. package/benchmark/auto-resolve/BENCHMARK-RESULTS.md +0 -341
  39. package/benchmark/auto-resolve/README.md +0 -696
  40. package/benchmark/auto-resolve/RUBRIC.md +0 -177
  41. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/NOTES.md +0 -34
  42. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/expected.json +0 -68
  43. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/metadata.json +0 -10
  44. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/setup.sh +0 -4
  45. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/spec.md +0 -44
  46. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/task.ko.txt +0 -8
  47. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/task.txt +0 -8
  48. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/NOTES.md +0 -68
  49. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/expected.json +0 -60
  50. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/metadata.json +0 -10
  51. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/setup.sh +0 -17
  52. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/spec.md +0 -51
  53. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/task.txt +0 -9
  54. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/verifiers/invalid.js +0 -29
  55. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/verifiers/parallel.js +0 -50
  56. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/NOTES.md +0 -75
  57. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/expected.json +0 -52
  58. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/metadata.json +0 -10
  59. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/setup.sh +0 -171
  60. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/spec.md +0 -50
  61. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/task.txt +0 -9
  62. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/NOTES.md +0 -89
  63. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/expected.json +0 -74
  64. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/metadata.json +0 -10
  65. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/setup.sh +0 -251
  66. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/spec.md +0 -57
  67. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/task.txt +0 -13
  68. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/verifiers/replay-malformed-body.js +0 -64
  69. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/NOTES.md +0 -104
  70. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/expected.json +0 -46
  71. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/metadata.json +0 -10
  72. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/setup.sh +0 -336
  73. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/spec.md +0 -51
  74. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/task.txt +0 -9
  75. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/NOTES.md +0 -38
  76. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/expected.json +0 -64
  77. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/metadata.json +0 -10
  78. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/setup.sh +0 -32
  79. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/spec.md +0 -63
  80. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/task.txt +0 -7
  81. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/exact-success.js +0 -54
  82. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/no-hardcoded-pricing.js +0 -47
  83. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/stock-error.js +0 -45
  84. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/NOTES.md +0 -57
  85. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/expected-pair-plan-registry.json +0 -170
  86. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/expected.json +0 -84
  87. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/metadata.json +0 -21
  88. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/pair-plan.sample-fail.json +0 -214
  89. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/pair-plan.sample-pass.json +0 -223
  90. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/setup.sh +0 -5
  91. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/spec.md +0 -55
  92. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/task.txt +0 -14
  93. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/NOTES.md +0 -39
  94. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/expected.json +0 -62
  95. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/metadata.json +0 -10
  96. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/setup.sh +0 -2
  97. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/spec.md +0 -67
  98. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/task.txt +0 -7
  99. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/verifiers/error-order.js +0 -55
  100. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/verifiers/priority-blocked.js +0 -48
  101. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/NOTES.md +0 -35
  102. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/expected.json +0 -56
  103. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/metadata.json +0 -10
  104. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/setup.sh +0 -2
  105. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/spec.md +0 -64
  106. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/task.txt +0 -7
  107. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/verifiers/conflicting-duplicate.js +0 -34
  108. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/verifiers/idempotent-close.js +0 -41
  109. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/NOTES.md +0 -39
  110. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/expected.json +0 -56
  111. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/metadata.json +0 -10
  112. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/setup.sh +0 -2
  113. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/spec.md +0 -76
  114. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/task.txt +0 -7
  115. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/verifiers/priority-rollback.js +0 -64
  116. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/verifiers/single-warehouse-fefo.js +0 -66
  117. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/NOTES.md +0 -40
  118. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/expected.json +0 -66
  119. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/metadata.json +0 -10
  120. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/setup.sh +0 -36
  121. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/spec.md +0 -71
  122. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/task.txt +0 -7
  123. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/catalog-source.js +0 -57
  124. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/exact-success.js +0 -63
  125. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/stock-error.js +0 -34
  126. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/NOTES.md +0 -31
  127. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/expected.json +0 -68
  128. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/metadata.json +0 -10
  129. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/setup.sh +0 -17
  130. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/spec.md +0 -68
  131. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/task.txt +0 -7
  132. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/conflicting-duplicate.js +0 -29
  133. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/exact-payout.js +0 -58
  134. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/rules-source.js +0 -56
  135. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/NOTES.md +0 -35
  136. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/expected-pair-plan-registry.json +0 -162
  137. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/expected.json +0 -67
  138. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/metadata.json +0 -19
  139. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/setup.sh +0 -4
  140. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/spec.md +0 -55
  141. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/task.txt +0 -9
  142. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/NOTES.md +0 -34
  143. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/expected.json +0 -57
  144. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/metadata.json +0 -10
  145. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/setup.sh +0 -2
  146. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/spec.md +0 -67
  147. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/task.txt +0 -7
  148. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/verifiers/duplicate-event-error.js +0 -35
  149. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/verifiers/priority-transfer-rollback.js +0 -53
  150. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/NOTES.md +0 -38
  151. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/expected.json +0 -57
  152. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/metadata.json +0 -10
  153. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/setup.sh +0 -2
  154. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/spec.md +0 -70
  155. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/task.txt +0 -3
  156. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/verifiers/duplicate-renewal-error.js +0 -42
  157. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/verifiers/priority-credit-rollback.js +0 -70
  158. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/NOTES.md +0 -47
  159. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/expected.json +0 -57
  160. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/metadata.json +0 -10
  161. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/setup.sh +0 -6
  162. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/spec.md +0 -48
  163. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/task.txt +0 -9
  164. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/NOTES.md +0 -45
  165. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/expected.json +0 -65
  166. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/metadata.json +0 -10
  167. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/setup.sh +0 -55
  168. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/spec.md +0 -48
  169. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/task.txt +0 -7
  170. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/NOTES.md +0 -43
  171. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/expected.json +0 -77
  172. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/metadata.json +0 -10
  173. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/setup.sh +0 -4
  174. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/spec.md +0 -48
  175. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/task.txt +0 -10
  176. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/NOTES.md +0 -57
  177. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/expected.json +0 -76
  178. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/metadata.json +0 -10
  179. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/setup.sh +0 -36
  180. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/spec.md +0 -45
  181. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/task.txt +0 -7
  182. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/NOTES.md +0 -53
  183. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/expected.json +0 -63
  184. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/metadata.json +0 -10
  185. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/setup.sh +0 -4
  186. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/spec.md +0 -48
  187. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/task.txt +0 -1
  188. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/NOTES.md +0 -105
  189. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/expected.json +0 -74
  190. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/metadata.json +0 -10
  191. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/setup.sh +0 -28
  192. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/spec.md +0 -59
  193. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/task.txt +0 -5
  194. package/benchmark/auto-resolve/fixtures/SCHEMA.md +0 -188
  195. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/NOTES.md +0 -37
  196. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/RETIRED.md +0 -13
  197. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/expected.json +0 -56
  198. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/metadata.json +0 -10
  199. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/setup.sh +0 -18
  200. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/spec.md +0 -69
  201. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/task.txt +0 -7
  202. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/verifiers/exact-proration.js +0 -48
  203. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/verifiers/rules-source-and-conflict.js +0 -79
  204. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/NOTES.md +0 -54
  205. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/RETIRED.md +0 -7
  206. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/expected.json +0 -67
  207. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/metadata.json +0 -10
  208. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/setup.sh +0 -2
  209. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/spec.md +0 -67
  210. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/task.txt +0 -5
  211. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/policy-precedence.js +0 -72
  212. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/validation-and-immutability.js +0 -43
  213. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/validation-boundary.js +0 -116
  214. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/NOTES.md +0 -35
  215. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/RETIRED.md +0 -12
  216. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/expected.json +0 -58
  217. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/metadata.json +0 -10
  218. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/setup.sh +0 -2
  219. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/spec.md +0 -73
  220. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/task.txt +0 -17
  221. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/verifiers/mixed-idempotent-settlement.js +0 -53
  222. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/verifiers/rejection-boundaries.js +0 -74
  223. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/NOTES.md +0 -28
  224. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/RETIRED.md +0 -15
  225. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/expected.json +0 -57
  226. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/metadata.json +0 -10
  227. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/setup.sh +0 -50
  228. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/spec.md +0 -59
  229. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/task.txt +0 -9
  230. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/verifiers/ordered-settlement.js +0 -88
  231. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/verifiers/reject-invalid.js +0 -45
  232. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/NOTES.md +0 -30
  233. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/RETIRED.md +0 -16
  234. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/expected.json +0 -64
  235. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/metadata.json +0 -10
  236. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/setup.sh +0 -21
  237. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/spec.md +0 -58
  238. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/task.txt +0 -9
  239. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/dup-op.js +0 -54
  240. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/priority-apply.js +0 -82
  241. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/rollback.js +0 -77
  242. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/NOTES.md +0 -99
  243. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/RETIRED.md +0 -18
  244. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/expected.json +0 -64
  245. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/metadata.json +0 -10
  246. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/setup.sh +0 -4
  247. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/spec.md +0 -61
  248. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/task.txt +0 -9
  249. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/correctness-small.js +0 -135
  250. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/scale.js +0 -100
  251. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/validation.js +0 -75
  252. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/NOTES.md +0 -93
  253. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/RETIRED.md +0 -16
  254. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/expected.json +0 -65
  255. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/metadata.json +0 -10
  256. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/setup.sh +0 -18
  257. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/spec.md +0 -66
  258. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/task.txt +0 -9
  259. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/correctness-small.js +0 -84
  260. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/scale.js +0 -184
  261. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/validation.js +0 -74
  262. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/NOTES.md +0 -60
  263. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/RETIRED.md +0 -29
  264. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/expected.json +0 -73
  265. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/metadata.json +0 -10
  266. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/setup.sh +0 -28
  267. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/spec.md +0 -58
  268. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/task.txt +0 -5
  269. package/benchmark/auto-resolve/fixtures/test-repo/README.md +0 -12
  270. package/benchmark/auto-resolve/fixtures/test-repo/bin/cli.js +0 -63
  271. package/benchmark/auto-resolve/fixtures/test-repo/package-lock.json +0 -823
  272. package/benchmark/auto-resolve/fixtures/test-repo/package.json +0 -22
  273. package/benchmark/auto-resolve/fixtures/test-repo/playwright.config.js +0 -17
  274. package/benchmark/auto-resolve/fixtures/test-repo/server/index.js +0 -37
  275. package/benchmark/auto-resolve/fixtures/test-repo/tests/cli.test.js +0 -25
  276. package/benchmark/auto-resolve/fixtures/test-repo/tests/server.test.js +0 -58
  277. package/benchmark/auto-resolve/fixtures/test-repo/web/index.html +0 -37
  278. package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.json +0 -25
  279. package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.md +0 -10
  280. package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.json +0 -18
  281. package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.md +0 -9
  282. package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.json +0 -18
  283. package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.md +0 -9
  284. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F16-cli-quote-tax-rules/l2_risk_probes/result.json +0 -28
  285. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F23-cli-fulfillment-wave/l2_risk_probes/result.json +0 -28
  286. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F25-cli-cart-promotion-rules/l2_risk_probes/result.json +0 -30
  287. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/full-pipeline-pair-gate.json +0 -82
  288. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/full-pipeline-pair-gate.md +0 -18
  289. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/headroom-gate.json +0 -46
  290. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/headroom-gate.md +0 -17
  291. package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.json +0 -28
  292. package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.md +0 -15
  293. package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.json +0 -28
  294. package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.md +0 -15
  295. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/F21-cli-scheduler-priority/l2_risk_probes/result.json +0 -32
  296. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.json +0 -44
  297. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.md +0 -16
  298. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.json +0 -18
  299. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.md +0 -9
  300. package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.json +0 -28
  301. package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.md +0 -15
  302. package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.json +0 -28
  303. package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.md +0 -15
  304. package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.json +0 -28
  305. package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.md +0 -15
  306. package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.json +0 -28
  307. package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.md +0 -15
  308. package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.json +0 -28
  309. package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.md +0 -15
  310. package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.json +0 -28
  311. package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.md +0 -15
  312. package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.json +0 -28
  313. package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.md +0 -15
  314. package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.json +0 -28
  315. package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.md +0 -15
  316. package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.json +0 -28
  317. package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.md +0 -15
  318. package/benchmark/auto-resolve/run-real-benchmark.md +0 -303
  319. package/benchmark/auto-resolve/scripts/audit-headroom-rejections.py +0 -441
  320. package/benchmark/auto-resolve/scripts/audit-pair-evidence.py +0 -1256
  321. package/benchmark/auto-resolve/scripts/build-pair-eligible-manifest.py +0 -306
  322. package/benchmark/auto-resolve/scripts/check-f9-artifacts.py +0 -268
  323. package/benchmark/auto-resolve/scripts/collect-swebench-predictions.py +0 -108
  324. package/benchmark/auto-resolve/scripts/compile-report.py +0 -493
  325. package/benchmark/auto-resolve/scripts/fetch-swebench-instances.py +0 -129
  326. package/benchmark/auto-resolve/scripts/frozen-verify-gate.py +0 -434
  327. package/benchmark/auto-resolve/scripts/full-pipeline-pair-gate.py +0 -612
  328. package/benchmark/auto-resolve/scripts/headroom-gate.py +0 -378
  329. package/benchmark/auto-resolve/scripts/iter-0033c-compare.py +0 -683
  330. package/benchmark/auto-resolve/scripts/iter-0033c-l1-summary.py +0 -97
  331. package/benchmark/auto-resolve/scripts/judge-opus-pass.sh +0 -542
  332. package/benchmark/auto-resolve/scripts/judge.sh +0 -565
  333. package/benchmark/auto-resolve/scripts/oracle-scope-tier-a.py +0 -256
  334. package/benchmark/auto-resolve/scripts/oracle-scope-tier-b.py +0 -287
  335. package/benchmark/auto-resolve/scripts/oracle-test-fidelity.py +0 -328
  336. package/benchmark/auto-resolve/scripts/pair-candidate-frontier.py +0 -469
  337. package/benchmark/auto-resolve/scripts/pair-plan-idgen.py +0 -401
  338. package/benchmark/auto-resolve/scripts/pair-plan-lint.py +0 -475
  339. package/benchmark/auto-resolve/scripts/pair-rejected-fixtures.sh +0 -103
  340. package/benchmark/auto-resolve/scripts/pair_evidence_contract.py +0 -270
  341. package/benchmark/auto-resolve/scripts/prepare-swebench-frozen-case.py +0 -273
  342. package/benchmark/auto-resolve/scripts/prepare-swebench-frozen-corpus.py +0 -148
  343. package/benchmark/auto-resolve/scripts/prepare-swebench-solver-worktree.py +0 -210
  344. package/benchmark/auto-resolve/scripts/recent-benchmark-summary.py +0 -232
  345. package/benchmark/auto-resolve/scripts/run-fixture.sh +0 -1024
  346. package/benchmark/auto-resolve/scripts/run-frozen-verify-pair.sh +0 -683
  347. package/benchmark/auto-resolve/scripts/run-full-pipeline-pair-candidate.sh +0 -458
  348. package/benchmark/auto-resolve/scripts/run-headroom-candidate.sh +0 -336
  349. package/benchmark/auto-resolve/scripts/run-iter-0033c.sh +0 -208
  350. package/benchmark/auto-resolve/scripts/run-suite.sh +0 -251
  351. package/benchmark/auto-resolve/scripts/run-swebench-frozen-corpus.sh +0 -310
  352. package/benchmark/auto-resolve/scripts/run-swebench-solver-batch.sh +0 -257
  353. package/benchmark/auto-resolve/scripts/ship-gate.py +0 -391
  354. package/benchmark/auto-resolve/scripts/solo-ceiling-avoidance.py +0 -53
  355. package/benchmark/auto-resolve/scripts/solo-headroom-hypothesis.py +0 -77
  356. package/benchmark/auto-resolve/scripts/swebench-frozen-matrix.py +0 -478
  357. package/benchmark/auto-resolve/scripts/test-audit-headroom-rejections.sh +0 -288
  358. package/benchmark/auto-resolve/scripts/test-audit-pair-evidence.sh +0 -1672
  359. package/benchmark/auto-resolve/scripts/test-benchmark-arg-parsing.sh +0 -1015
  360. package/benchmark/auto-resolve/scripts/test-build-pair-eligible-manifest.sh +0 -491
  361. package/benchmark/auto-resolve/scripts/test-check-f9-artifacts.sh +0 -91
  362. package/benchmark/auto-resolve/scripts/test-frozen-verify-gate.sh +0 -517
  363. package/benchmark/auto-resolve/scripts/test-full-pipeline-pair-gate.sh +0 -610
  364. package/benchmark/auto-resolve/scripts/test-headroom-gate.sh +0 -401
  365. package/benchmark/auto-resolve/scripts/test-iter-0033c-compare.sh +0 -525
  366. package/benchmark/auto-resolve/scripts/test-iter-0033c-l1-summary.sh +0 -254
  367. package/benchmark/auto-resolve/scripts/test-lint-fixtures.sh +0 -580
  368. package/benchmark/auto-resolve/scripts/test-pair-candidate-frontier.sh +0 -591
  369. package/benchmark/auto-resolve/scripts/test-run-full-pipeline-pair-candidate.sh +0 -497
  370. package/benchmark/auto-resolve/scripts/test-run-headroom-candidate.sh +0 -401
  371. package/benchmark/auto-resolve/scripts/test-run-swebench-solver-batch.sh +0 -111
  372. package/benchmark/auto-resolve/scripts/test-ship-gate.sh +0 -1189
  373. package/benchmark/auto-resolve/scripts/test-swebench-frozen-case.sh +0 -1221
  374. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/NOTES.md +0 -28
  375. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/expected.json +0 -63
  376. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/metadata.json +0 -10
  377. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/setup.sh +0 -3
  378. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/spec.md +0 -47
  379. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/task.txt +0 -1
  380. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/NOTES.md +0 -34
  381. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/expected.json +0 -53
  382. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/metadata.json +0 -10
  383. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/setup.sh +0 -3
  384. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/spec.md +0 -50
  385. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/task.txt +0 -1
  386. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/verifiers/duplicate-order-error.js +0 -27
  387. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/verifiers/priority-stock-reservation.js +0 -44
  388. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/NOTES.md +0 -34
  389. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/expected.json +0 -55
  390. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/metadata.json +0 -10
  391. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/setup.sh +0 -3
  392. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/spec.md +0 -52
  393. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/task.txt +0 -1
  394. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/verifiers/duplicate-ticket-error.js +0 -29
  395. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/verifiers/priority-agent-assignment.js +0 -48
  396. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/NOTES.md +0 -34
  397. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/expected.json +0 -55
  398. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/metadata.json +0 -10
  399. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/setup.sh +0 -3
  400. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/spec.md +0 -55
  401. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/task.txt +0 -1
  402. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/verifiers/duplicate-return-error.js +0 -43
  403. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/verifiers/priority-return-routing.js +0 -70
  404. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/NOTES.md +0 -37
  405. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/expected.json +0 -54
  406. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/metadata.json +0 -10
  407. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/setup.sh +0 -3
  408. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/spec.md +0 -59
  409. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/task.txt +0 -1
  410. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/verifiers/credit-ledger-priority.js +0 -98
  411. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/verifiers/duplicate-charge-error.js +0 -38
  412. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/NOTES.md +0 -36
  413. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/expected.json +0 -56
  414. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/metadata.json +0 -10
  415. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/setup.sh +0 -3
  416. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/spec.md +0 -59
  417. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/task.txt +0 -1
  418. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/verifiers/duplicate-refund-error.js +0 -41
  419. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/verifiers/priority-refund-ledger.js +0 -65
  420. package/config/skills/_shared/pair-plan-schema.md +0 -298
  421. package/scripts/lint-fixtures.sh +0 -352
  422. package/scripts/lint-shadow-fixtures.sh +0 -58
  423. package/scripts/lint-skills.sh +0 -4265
  424. package/scripts/test-owner-phases.py +0 -193
@@ -1,612 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Gate full-pipeline L2/pair evidence against L1 solo.
3
-
4
- This is stricter than headroom-gate.py. Headroom only says a candidate set is
5
- worth measuring. This gate says the measured L2 arm is usable evidence:
6
- bare and solo leave headroom with complete comparable artifacts, the selected
7
- pair arm is evidence-clean, pair mode actually fired for a canonical trigger
8
- reason, and the blind judge scores the selected pair arm materially above
9
- solo_claude.
10
- """
11
- from __future__ import annotations
12
-
13
- import argparse
14
- import json
15
- import os
16
- import pathlib
17
- import re
18
- import sys
19
- from typing import Any
20
-
21
- SCRIPT_DIR = pathlib.Path(__file__).resolve().parent
22
- if str(SCRIPT_DIR) not in sys.path:
23
- sys.path.insert(0, str(SCRIPT_DIR))
24
- FIXTURES_ROOT = SCRIPT_DIR.parent / "fixtures"
25
-
26
- from pair_evidence_contract import (
27
- ALLOWED_PAIR_ARMS,
28
- all_known_pair_trigger_reasons,
29
- has_canonical_pair_trigger_reason,
30
- has_known_pair_trigger_reason,
31
- is_score,
32
- is_strict_number,
33
- loads_strict_json_object,
34
- path_has_actionable_solo_headroom_hypothesis,
35
- )
36
-
37
- KNOWN_ARMS = {"bare", "solo_claude"} | ALLOWED_PAIR_ARMS
38
- PASS_VERDICTS = {"PASS", "PASS_WITH_ISSUES"}
39
- REJECTED_REGISTRY = pathlib.Path(__file__).with_name("pair-rejected-fixtures.sh")
40
-
41
-
42
- def load_json(path: pathlib.Path) -> tuple[dict[str, Any] | None, str | None]:
43
- if not path.is_file():
44
- return None, "missing"
45
- try:
46
- data = loads_strict_json_object(path.read_text())
47
- except (ValueError, json.JSONDecodeError):
48
- return None, "malformed"
49
- return data, None
50
-
51
-
52
- def fixture_short(name: str) -> str:
53
- return name.split("-", 1)[0] if "-" in name else name
54
-
55
-
56
- def rejected_registry_path() -> pathlib.Path:
57
- override = os.environ.get("PAIR_REJECTED_FIXTURES_REGISTRY")
58
- return pathlib.Path(override) if override else REJECTED_REGISTRY
59
-
60
-
61
- def load_rejected_short_ids(path: pathlib.Path) -> set[str]:
62
- if not path.is_file():
63
- raise ValueError(f"rejected fixture registry missing: {path}")
64
- rejected = set()
65
- for line in path.read_text().splitlines():
66
- match = re.match(r"\s*([FS]\d+)-\*\|([FS]\d+)\)", line)
67
- if match and match.group(1) == match.group(2):
68
- rejected.add(match.group(1))
69
- if not rejected:
70
- raise ValueError(f"rejected fixture registry has no fixture entries: {path}")
71
- return rejected
72
-
73
-
74
- def score_for(judge: dict[str, Any], arm: str) -> int | None:
75
- mapping = judge.get("_blind_mapping")
76
- if not isinstance(mapping, dict):
77
- return None
78
- if arm not in {mapped for slot, mapped in mapping.items() if slot in {"A", "B", "C"}}:
79
- return None
80
- raw_scores = judge.get("scores_by_arm")
81
- scores = raw_scores if isinstance(raw_scores, dict) else {}
82
- value = scores.get(arm)
83
- return value if is_score(value) else None
84
-
85
-
86
- def verify_score_clean(payload: dict[str, Any] | None) -> bool:
87
- if payload is None:
88
- return False
89
- value = payload.get("verify_score")
90
- return is_strict_number(value) and value >= 1.0
91
-
92
-
93
- def bool_flag_failure(value: Any, true_reason: str, malformed_reason: str) -> str | None:
94
- if value is True:
95
- return true_reason
96
- if value is False or value is None:
97
- return None
98
- return malformed_reason
99
-
100
-
101
- def pair_trigger_failures(result: dict[str, Any] | None, arm: str) -> list[str]:
102
- if result is None:
103
- return []
104
- trigger = result.get("pair_trigger")
105
- if not isinstance(trigger, dict):
106
- return [f"{arm} pair_trigger missing or malformed"]
107
- eligible = trigger.get("eligible")
108
- reasons = trigger.get("reasons")
109
- skipped_reason = trigger.get("skipped_reason")
110
- if not isinstance(eligible, bool):
111
- return [f"{arm} pair_trigger.eligible malformed"]
112
- if not isinstance(reasons, list) or not all(isinstance(reason, str) for reason in reasons):
113
- return [f"{arm} pair_trigger.reasons malformed"]
114
- if skipped_reason is not None and not isinstance(skipped_reason, str):
115
- return [f"{arm} pair_trigger.skipped_reason malformed"]
116
- if eligible is not True:
117
- return [f"{arm} pair_trigger not eligible"]
118
- if not reasons:
119
- return [f"{arm} pair_trigger eligible with empty reasons"]
120
- if not has_known_pair_trigger_reason(reasons):
121
- return [f"{arm} pair_trigger reasons missing known trigger reason"]
122
- if not all_known_pair_trigger_reasons(reasons):
123
- return [f"{arm} pair_trigger reasons contain unknown trigger reason"]
124
- if not has_canonical_pair_trigger_reason(reasons):
125
- return [f"{arm} pair_trigger reasons missing canonical trigger reason"]
126
- if skipped_reason is not None:
127
- return [f"{arm} pair_trigger eligible with skipped_reason"]
128
- return []
129
-
130
-
131
- def pair_trigger_eligible(result: dict[str, Any] | None) -> bool:
132
- if result is None:
133
- return False
134
- trigger = result.get("pair_trigger")
135
- return (
136
- isinstance(trigger, dict)
137
- and trigger.get("eligible") is True
138
- and isinstance(trigger.get("reasons"), list)
139
- and bool(trigger.get("reasons"))
140
- and all(isinstance(reason, str) for reason in trigger.get("reasons", []))
141
- and all_known_pair_trigger_reasons(trigger.get("reasons", []))
142
- and has_canonical_pair_trigger_reason(trigger.get("reasons", []))
143
- and trigger.get("skipped_reason") is None
144
- )
145
-
146
-
147
- def pair_trigger_reasons(result: dict[str, Any] | None) -> list[str]:
148
- if result is None:
149
- return []
150
- trigger = result.get("pair_trigger")
151
- if not isinstance(trigger, dict):
152
- return []
153
- reasons = trigger.get("reasons")
154
- if not isinstance(reasons, list) or not all(isinstance(reason, str) for reason in reasons):
155
- return []
156
- return reasons
157
-
158
-
159
- def fixture_spec_has_solo_headroom_hypothesis(fixture: str) -> bool:
160
- return path_has_actionable_solo_headroom_hypothesis(FIXTURES_ROOT / fixture / "spec.md")
161
-
162
-
163
- def skill_verdict_failures(result: dict[str, Any] | None, arm: str) -> list[str]:
164
- if result is None or arm == "bare":
165
- return []
166
- failures: list[str] = []
167
- terminal = result.get("terminal_verdict")
168
- verify = result.get("verify_verdict")
169
- if terminal not in PASS_VERDICTS:
170
- failures.append(f"{arm} terminal verdict not pass")
171
- if verify not in PASS_VERDICTS:
172
- failures.append(f"{arm} verify verdict not pass")
173
- return failures
174
-
175
-
176
- def axis_validation_counts(judge: dict[str, Any]) -> tuple[dict[str, int], int]:
177
- raw_mapping = judge.get("_blind_mapping")
178
- mapping = raw_mapping if isinstance(raw_mapping, dict) else {}
179
- raw_validation = judge.get("_axis_validation")
180
- validation = raw_validation if isinstance(raw_validation, dict) else {}
181
- cells = validation.get("out_of_range_cells") or []
182
- declared_count = validation.get("out_of_range_count")
183
- total_invalid = max(
184
- declared_count if isinstance(declared_count, int) else 0,
185
- len(cells) if isinstance(cells, list) else 0,
186
- )
187
- breakdown_to_letter = {
188
- "a_breakdown": "A",
189
- "b_breakdown": "B",
190
- "c_breakdown": "C",
191
- }
192
- counts: dict[str, int] = {}
193
- mapped_count = 0
194
- if not isinstance(cells, list):
195
- return counts, total_invalid
196
- for cell in cells:
197
- if not isinstance(cell, dict):
198
- continue
199
- letter = breakdown_to_letter.get(cell.get("breakdown"))
200
- arm = mapping.get(letter) if letter else None
201
- if arm in KNOWN_ARMS:
202
- counts[arm] = counts.get(arm, 0) + 1
203
- mapped_count += 1
204
- return counts, max(0, total_invalid - mapped_count)
205
-
206
-
207
- def axis_invalid_count(judge: dict[str, Any], arm: str) -> int:
208
- counts, _ = axis_validation_counts(judge)
209
- return counts.get(arm, 0)
210
-
211
-
212
- def axis_unmapped_invalid_count(judge: dict[str, Any]) -> int:
213
- _, unmapped = axis_validation_counts(judge)
214
- return unmapped
215
-
216
-
217
- def blind_mapping_failures(judge: dict[str, Any], required_arms: set[str]) -> list[str]:
218
- mapping = judge.get("_blind_mapping")
219
- if not isinstance(mapping, dict):
220
- return ["judge blind mapping missing"]
221
- mapped_arms = {arm for key, arm in mapping.items() if key in {"A", "B", "C"}}
222
- missing = sorted(required_arms - mapped_arms)
223
- if missing:
224
- return [f"judge blind mapping missing arm(s): {', '.join(missing)}"]
225
- return []
226
-
227
-
228
- def clean_failures(
229
- fixture_dir: pathlib.Path,
230
- judge: dict[str, Any],
231
- arm: str,
232
- *,
233
- require_correctness: bool,
234
- ) -> list[str]:
235
- failures: list[str] = []
236
- result, result_error = load_json(fixture_dir / arm / "result.json")
237
- verify, verify_error = load_json(fixture_dir / arm / "verify.json")
238
- diff = fixture_dir / arm / "diff.patch"
239
- if result_error:
240
- failures.append(f"{arm} result.json {result_error}")
241
- if verify_error:
242
- failures.append(f"{arm} verify.json {verify_error}")
243
- if not diff.is_file():
244
- failures.append(f"{arm} diff.patch missing")
245
-
246
- raw_dq_by_arm = judge.get("disqualifiers_by_arm")
247
- dq_by_arm = raw_dq_by_arm if isinstance(raw_dq_by_arm, dict) else {}
248
- dq_entry = dq_by_arm.get(arm)
249
- dq_value = dq_entry.get("disqualifier") if isinstance(dq_entry, dict) else dq_entry
250
- judge_dq_failure = bool_flag_failure(
251
- dq_value,
252
- f"{arm} judge disqualifier",
253
- f"{arm} judge disqualifier malformed",
254
- )
255
- if judge_dq_failure:
256
- failures.append(judge_dq_failure)
257
- axis_invalid = axis_invalid_count(judge, arm)
258
- if axis_invalid > 0:
259
- failures.append(f"{arm} judge axis-invalid ({axis_invalid})")
260
- if result is not None:
261
- for field, true_reason in (
262
- ("disqualifier", f"{arm} result disqualifier"),
263
- ("timed_out", f"{arm} timed out"),
264
- ("environment_contamination", f"{arm} environment contamination"),
265
- ):
266
- failure = bool_flag_failure(
267
- result.get(field),
268
- true_reason,
269
- f"{arm} result {field} malformed",
270
- )
271
- if failure:
272
- failures.append(failure)
273
- invoke_failure = bool_flag_failure(
274
- result.get("invoke_failure"),
275
- f"{arm} invoke failure",
276
- f"{arm} result invoke_failure malformed",
277
- )
278
- if invoke_failure == f"{arm} invoke failure":
279
- reason = result.get("invoke_failure_reason")
280
- if isinstance(reason, str) and reason:
281
- failures.append(f"{arm} invoke failure ({reason})")
282
- else:
283
- failures.append(invoke_failure)
284
- elif invoke_failure:
285
- failures.append(invoke_failure)
286
- if require_correctness:
287
- failures.extend(skill_verdict_failures(result, arm))
288
- if verify is not None:
289
- verify_dq_failure = bool_flag_failure(
290
- verify.get("disqualifier"),
291
- f"{arm} verify disqualifier",
292
- f"{arm} verify disqualifier malformed",
293
- )
294
- if verify_dq_failure:
295
- failures.append(verify_dq_failure)
296
- if require_correctness and verify is not None and not verify_score_clean(verify):
297
- failures.append(f"{arm} verify_score < 1.0")
298
- return failures
299
-
300
-
301
- def elapsed_ratio(pair_result: dict[str, Any] | None, solo_result: dict[str, Any] | None) -> float | None:
302
- if pair_result is None or solo_result is None:
303
- return None
304
- pair_elapsed = pair_result.get("elapsed_seconds")
305
- solo_elapsed = solo_result.get("elapsed_seconds")
306
- if not is_strict_number(pair_elapsed) or not is_strict_number(solo_elapsed):
307
- return None
308
- return pair_elapsed / solo_elapsed
309
-
310
-
311
- def provider_limited(result: dict[str, Any] | None) -> bool:
312
- return result is not None and result.get("invoke_failure_reason") == "provider_limit"
313
-
314
-
315
- def evaluate_fixture(
316
- fixture_dir: pathlib.Path,
317
- *,
318
- rejected_short_ids: set[str],
319
- pair_arm: str,
320
- bare_max: int,
321
- solo_max: int,
322
- min_bare_headroom: int,
323
- min_solo_headroom: int,
324
- min_pair_margin: int,
325
- max_pair_solo_wall_ratio: float | None,
326
- require_hypothesis_trigger: bool,
327
- ) -> dict[str, Any]:
328
- judge, judge_error = load_json(fixture_dir / "judge.json")
329
- if judge is None:
330
- return {
331
- "fixture": fixture_dir.name,
332
- "status": "FAIL",
333
- "reason": f"judge.json {judge_error}",
334
- }
335
-
336
- bare = score_for(judge, "bare")
337
- solo = score_for(judge, "solo_claude")
338
- pair = score_for(judge, pair_arm)
339
- bare_headroom = bare_max - bare if isinstance(bare, int) else None
340
- solo_headroom = solo_max - solo if isinstance(solo, int) else None
341
- solo_result, _ = load_json(fixture_dir / "solo_claude" / "result.json")
342
- pair_result, _ = load_json(fixture_dir / pair_arm / "result.json")
343
- ratio = elapsed_ratio(pair_result, solo_result)
344
- pair_provider_limited = provider_limited(pair_result)
345
- if pair_provider_limited:
346
- ratio = None
347
-
348
- reasons: list[str] = []
349
- if fixture_short(fixture_dir.name) in rejected_short_ids:
350
- reasons.append("fixture rejected for pair-candidate runs")
351
- if bare is None:
352
- reasons.append("bare score missing")
353
- elif bare > bare_max:
354
- reasons.append(f"bare score {bare} > {bare_max}")
355
- elif bare_headroom is not None and bare_headroom < min_bare_headroom:
356
- reasons.append(f"bare headroom {bare_headroom} < {min_bare_headroom}")
357
- if solo is None:
358
- reasons.append("solo_claude score missing")
359
- elif solo > solo_max:
360
- reasons.append(f"solo_claude score {solo} > {solo_max}")
361
- elif solo_headroom is not None and solo_headroom < min_solo_headroom:
362
- reasons.append(f"solo_claude headroom {solo_headroom} < {min_solo_headroom}")
363
- if pair_provider_limited:
364
- pass
365
- elif pair is None:
366
- reasons.append(f"{pair_arm} score missing")
367
- elif solo is not None and pair - solo < min_pair_margin:
368
- reasons.append(f"{pair_arm} margin {pair - solo:+d} < +{min_pair_margin}")
369
- unmapped_axis_invalid = axis_unmapped_invalid_count(judge)
370
- if unmapped_axis_invalid > 0:
371
- reasons.append(f"judge axis-invalid unmapped ({unmapped_axis_invalid})")
372
- reasons.extend(blind_mapping_failures(judge, {"bare", "solo_claude", pair_arm}))
373
-
374
- reasons.extend(clean_failures(fixture_dir, judge, "bare", require_correctness=False))
375
- reasons.extend(clean_failures(fixture_dir, judge, "solo_claude", require_correctness=False))
376
- reasons.extend(clean_failures(fixture_dir, judge, pair_arm, require_correctness=True))
377
-
378
- pair_mode = None if pair_result is None else pair_result.get("pair_mode")
379
- if pair_mode is not True and not pair_provider_limited:
380
- reasons.append(f"{pair_arm} pair_mode not true")
381
- if not pair_provider_limited:
382
- reasons.extend(pair_trigger_failures(pair_result, pair_arm))
383
- if (
384
- require_hypothesis_trigger
385
- and
386
- fixture_spec_has_solo_headroom_hypothesis(fixture_dir.name)
387
- and "spec.solo_headroom_hypothesis" not in pair_trigger_reasons(pair_result)
388
- ):
389
- reasons.append(f"{pair_arm} pair_trigger missing spec.solo_headroom_hypothesis")
390
-
391
- if max_pair_solo_wall_ratio is not None and not pair_provider_limited:
392
- if ratio is None:
393
- reasons.append("pair/solo wall ratio missing")
394
- elif ratio > max_pair_solo_wall_ratio:
395
- reasons.append(f"pair/solo wall ratio {ratio:.2f} > {max_pair_solo_wall_ratio:.2f}")
396
-
397
- return {
398
- "fixture": fixture_dir.name,
399
- "status": "PASS" if not reasons else "FAIL",
400
- "bare_score": bare,
401
- "bare_headroom": bare_headroom,
402
- "solo_score": solo,
403
- "solo_headroom": solo_headroom,
404
- "pair_score": pair,
405
- "pair_margin": (
406
- None if pair_provider_limited
407
- else pair - solo if isinstance(pair, int) and isinstance(solo, int)
408
- else None
409
- ),
410
- "pair_mode": pair_mode,
411
- "pair_trigger_eligible": pair_trigger_eligible(pair_result),
412
- "pair_trigger_reasons": pair_trigger_reasons(pair_result),
413
- "pair_trigger_has_canonical_reason": has_canonical_pair_trigger_reason(
414
- pair_trigger_reasons(pair_result)
415
- ),
416
- "pair_trigger_has_hypothesis_reason": (
417
- "spec.solo_headroom_hypothesis" in pair_trigger_reasons(pair_result)
418
- ),
419
- "pair_solo_wall_ratio": ratio,
420
- "reason": "; ".join(reasons),
421
- }
422
-
423
-
424
- def fmt_ratio(value: Any) -> str:
425
- return f"{value:.2f}x" if isinstance(value, (int, float)) else "n/a"
426
-
427
-
428
- def fmt_margin(value: Any) -> str:
429
- return f"{value:+.1f}" if isinstance(value, (int, float)) else "n/a"
430
-
431
-
432
- def fmt_trigger_reasons(value: Any) -> str:
433
- if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
434
- return ""
435
- return ",".join(value)
436
-
437
-
438
- def write_md(path: pathlib.Path, report: dict[str, Any]) -> None:
439
- lines = [
440
- f"# Full-Pipeline Pair Gate - {report['run_id']}",
441
- "",
442
- f"Verdict: **{report['verdict']}**",
443
- "",
444
- f"Fixtures passed: {report['fixtures_passed']}/{report['fixtures_total']} "
445
- f"(minimum required: {report['min_fixtures']})",
446
- "",
447
- f"Rule: at least {report['min_fixtures']} fixtures; bare <= {report['bare_max']}; "
448
- f"bare headroom >= {report['min_bare_headroom_required']}; "
449
- f"solo_claude <= {report['solo_max']}; "
450
- f"solo_claude headroom >= {report['min_solo_headroom_required']}; "
451
- f"{report['pair_arm']} evidence-clean; pair_mode true; "
452
- "pair_trigger eligible with canonical reason; "
453
- f"{report['pair_arm']} - solo_claude >= {report['min_pair_margin']}.",
454
- f"Average pair margin: {fmt_margin(report['avg_pair_margin'])}",
455
- f"Allowed pair/solo wall ratio: {fmt_ratio(report['max_pair_solo_wall_ratio'])}",
456
- f"Maximum observed pair/solo wall ratio: {fmt_ratio(report['max_observed_pair_solo_wall_ratio'])}",
457
- f"Average pair/solo wall ratio: {fmt_ratio(report['avg_pair_solo_wall_ratio'])}",
458
- f"Hypothesis trigger required: {str(report['require_hypothesis_trigger']).lower()}",
459
- "",
460
- "| Fixture | Bare | Bare headroom | Solo_claude | Solo_claude headroom | Pair | Margin | Pair mode | Hypothesis trigger | Triggers | Wall ratio | Status | Reason |",
461
- "|---|---:|---:|---:|---:|---:|---:|---|---|---|---:|---|---|",
462
- ]
463
- for row in report["rows"]:
464
- margin = row.get("pair_margin")
465
- margin_text = f"{margin:+d}" if isinstance(margin, int) else "n/a"
466
- lines.append(
467
- f"| {row['fixture']} | {row.get('bare_score')} | {row.get('bare_headroom')} | "
468
- f"{row.get('solo_score')} | {row.get('solo_headroom')} | "
469
- f"{row.get('pair_score')} | {margin_text} | {str(row.get('pair_mode')).lower()} | "
470
- f"{str(row.get('pair_trigger_has_hypothesis_reason')).lower()} | "
471
- f"{fmt_trigger_reasons(row.get('pair_trigger_reasons'))} | "
472
- f"{fmt_ratio(row.get('pair_solo_wall_ratio'))} | {row['status']} | {row.get('reason', '')} |"
473
- )
474
- lines.append("")
475
- path.write_text("\n".join(lines), encoding="utf8")
476
-
477
-
478
- def positive_float(value: str) -> float:
479
- parsed = float(value)
480
- if not is_strict_number(parsed):
481
- raise argparse.ArgumentTypeError("value must be finite and > 0")
482
- return parsed
483
-
484
-
485
- def positive_int(value: str) -> int:
486
- parsed = int(value)
487
- if parsed <= 0:
488
- raise argparse.ArgumentTypeError("value must be > 0")
489
- return parsed
490
-
491
-
492
- def non_negative_int(value: str) -> int:
493
- parsed = int(value)
494
- if parsed < 0:
495
- raise argparse.ArgumentTypeError("value must be >= 0")
496
- return parsed
497
-
498
-
499
- def main() -> int:
500
- parser = argparse.ArgumentParser()
501
- parser.add_argument("--run-id", required=True)
502
- parser.add_argument("--results-root", default="benchmark/auto-resolve/results", type=pathlib.Path)
503
- parser.add_argument("--bare-max", type=int, default=60)
504
- parser.add_argument("--solo-max", type=int, default=80)
505
- parser.add_argument("--min-bare-headroom", type=non_negative_int, default=5)
506
- parser.add_argument("--min-solo-headroom", type=non_negative_int, default=5)
507
- parser.add_argument("--min-pair-margin", type=positive_int, default=5)
508
- parser.add_argument("--min-fixtures", type=positive_int, default=2)
509
- parser.add_argument("--pair-arm", default="l2_risk_probes")
510
- parser.add_argument("--max-pair-solo-wall-ratio", type=positive_float, default=3.0)
511
- parser.add_argument(
512
- "--require-hypothesis-trigger",
513
- action="store_true",
514
- help="require fixtures with actionable solo-headroom hypotheses to expose spec.solo_headroom_hypothesis in pair_trigger.reasons",
515
- )
516
- parser.add_argument("--out-json", type=pathlib.Path)
517
- parser.add_argument("--out-md", type=pathlib.Path)
518
- args = parser.parse_args()
519
-
520
- if args.pair_arm == "l2_forced":
521
- print(
522
- "pair-arm l2_forced is retired: it leaks pair-awareness before IMPLEMENT; "
523
- "use l2_risk_probes for current proof runs or l2_gated for diagnostics.",
524
- file=sys.stderr,
525
- )
526
- return 2
527
- if args.pair_arm not in ALLOWED_PAIR_ARMS:
528
- print(
529
- f"pair-arm must be one of {sorted(ALLOWED_PAIR_ARMS)}: {args.pair_arm}",
530
- file=sys.stderr,
531
- )
532
- return 2
533
-
534
- run_root = args.results_root / args.run_id
535
- if not run_root.is_dir():
536
- print(f"no results dir: {run_root}", file=sys.stderr)
537
- return 2
538
-
539
- try:
540
- rejected_short_ids = load_rejected_short_ids(rejected_registry_path())
541
- except ValueError as exc:
542
- print(str(exc), file=sys.stderr)
543
- return 2
544
- rows = [
545
- evaluate_fixture(
546
- fixture_dir,
547
- rejected_short_ids=rejected_short_ids,
548
- pair_arm=args.pair_arm,
549
- bare_max=args.bare_max,
550
- solo_max=args.solo_max,
551
- min_bare_headroom=args.min_bare_headroom,
552
- min_solo_headroom=args.min_solo_headroom,
553
- min_pair_margin=args.min_pair_margin,
554
- max_pair_solo_wall_ratio=args.max_pair_solo_wall_ratio,
555
- require_hypothesis_trigger=args.require_hypothesis_trigger,
556
- )
557
- for fixture_dir in sorted(p for p in run_root.iterdir() if p.is_dir())
558
- ]
559
- pass_count = sum(1 for row in rows if row["status"] == "PASS")
560
- fixture_count_ok = len(rows) >= args.min_fixtures
561
- verdict = "PASS" if rows and fixture_count_ok and pass_count == len(rows) else "FAIL"
562
- ratios = [
563
- row["pair_solo_wall_ratio"]
564
- for row in rows
565
- if is_strict_number(row.get("pair_solo_wall_ratio"))
566
- ]
567
- margins = [
568
- row["pair_margin"]
569
- for row in rows
570
- if isinstance(row.get("pair_margin"), int)
571
- ]
572
- rule = (
573
- "headroom candidates only; "
574
- f"bare headroom >= {args.min_bare_headroom}; "
575
- f"solo_claude headroom >= {args.min_solo_headroom}; "
576
- f"{args.pair_arm} must be evidence-clean, pair_mode true, "
577
- "pair_trigger eligible with a canonical reason, and beat solo_claude "
578
- "by the configured margin"
579
- )
580
- report = {
581
- "run_id": args.run_id,
582
- "rule": rule,
583
- "verdict": verdict,
584
- "fixtures_total": len(rows),
585
- "fixtures_passed": pass_count,
586
- "min_fixtures": args.min_fixtures,
587
- "fixture_count_ok": fixture_count_ok,
588
- "bare_max": args.bare_max,
589
- "solo_max": args.solo_max,
590
- "min_bare_headroom_required": args.min_bare_headroom,
591
- "min_solo_headroom_required": args.min_solo_headroom,
592
- "min_pair_margin": args.min_pair_margin,
593
- "pair_arm": args.pair_arm,
594
- "require_hypothesis_trigger": args.require_hypothesis_trigger,
595
- "max_pair_solo_wall_ratio": args.max_pair_solo_wall_ratio,
596
- "max_observed_pair_solo_wall_ratio": max(ratios) if ratios else None,
597
- "avg_pair_margin": (sum(margins) / len(margins)) if margins else None,
598
- "avg_pair_solo_wall_ratio": (sum(ratios) / len(ratios)) if ratios else None,
599
- "rows": rows,
600
- }
601
-
602
- if args.out_json:
603
- args.out_json.write_text(json.dumps(report, indent=2) + "\n", encoding="utf8")
604
- if args.out_md:
605
- write_md(args.out_md, report)
606
- else:
607
- print(json.dumps(report, indent=2))
608
- return 0 if verdict == "PASS" else 1
609
-
610
-
611
- if __name__ == "__main__":
612
- sys.exit(main())