devlyn-cli 3.2.1 → 3.3.1

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 (423) hide show
  1. package/AGENTS.md +2 -2
  2. package/CLAUDE.md +2 -2
  3. package/README.md +12 -102
  4. package/bin/devlyn.js +13 -225
  5. package/bin/instruction-templates.json +8 -0
  6. package/config/skills/_shared/adapters/grok.md +1 -2
  7. package/config/skills/_shared/archive_run.py +15 -3
  8. package/config/skills/_shared/judge-role-evidence.py +21 -1
  9. package/config/skills/_shared/runtime-principles.md +1 -1
  10. package/config/skills/_shared/spec-verify-check.py +0 -653
  11. package/config/skills/_shared/state-phase-write.py +351 -25
  12. package/config/skills/_shared/task-complete.py +660 -150
  13. package/config/skills/_shared/terminal-claim-check.py +229 -4
  14. package/config/skills/_shared/verify-merge-findings.py +93 -2
  15. package/config/skills/devlyn:ideate/SKILL.md +3 -4
  16. package/config/skills/devlyn:ideate/references/elicitation.md +2 -35
  17. package/config/skills/devlyn:ideate/references/from-spec-mode.md +3 -18
  18. package/config/skills/devlyn:ideate/references/project-mode.md +1 -16
  19. package/config/skills/devlyn:ideate/references/spec-template.md +0 -2
  20. package/config/skills/devlyn:queue/SKILL.md +6 -4
  21. package/config/skills/devlyn:resolve/SKILL.md +16 -25
  22. package/config/skills/devlyn:resolve/references/free-form-mode.md +0 -14
  23. package/config/skills/devlyn:resolve/references/outer-loop.md +1 -1
  24. package/config/skills/devlyn:resolve/references/phases/build-gate.md +1 -1
  25. package/config/skills/devlyn:resolve/references/phases/cleanup.md +3 -3
  26. package/config/skills/devlyn:resolve/references/phases/implement.md +1 -1
  27. package/config/skills/devlyn:resolve/references/phases/probe-derive.md +1 -16
  28. package/config/skills/devlyn:resolve/references/phases/verify.md +4 -9
  29. package/config/skills/devlyn:resolve/references/state-schema.md +9 -7
  30. package/config/skills/devlyn:resolve/references/task-completion.md +47 -38
  31. package/{config/skills → optional-skills}/code-health-standards/SKILL.md +4 -13
  32. package/{config/skills → optional-skills}/code-review-standards/SKILL.md +4 -12
  33. package/{config/skills → optional-skills}/root-cause-analysis/SKILL.md +4 -12
  34. package/{config/skills → optional-skills}/ui-implementation-standards/SKILL.md +4 -14
  35. package/package.json +1 -57
  36. package/benchmark/auto-resolve/BENCHMARK-DESIGN.md +0 -289
  37. package/benchmark/auto-resolve/BENCHMARK-RESULTS.md +0 -341
  38. package/benchmark/auto-resolve/README.md +0 -696
  39. package/benchmark/auto-resolve/RUBRIC.md +0 -177
  40. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/NOTES.md +0 -34
  41. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/expected.json +0 -68
  42. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/metadata.json +0 -10
  43. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/setup.sh +0 -4
  44. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/spec.md +0 -44
  45. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/task.ko.txt +0 -8
  46. package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/task.txt +0 -8
  47. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/NOTES.md +0 -68
  48. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/expected.json +0 -60
  49. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/metadata.json +0 -10
  50. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/setup.sh +0 -17
  51. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/spec.md +0 -51
  52. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/task.txt +0 -9
  53. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/verifiers/invalid.js +0 -29
  54. package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/verifiers/parallel.js +0 -50
  55. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/NOTES.md +0 -75
  56. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/expected.json +0 -52
  57. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/metadata.json +0 -10
  58. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/setup.sh +0 -171
  59. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/spec.md +0 -50
  60. package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/task.txt +0 -9
  61. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/NOTES.md +0 -89
  62. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/expected.json +0 -74
  63. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/metadata.json +0 -10
  64. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/setup.sh +0 -251
  65. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/spec.md +0 -57
  66. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/task.txt +0 -13
  67. package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/verifiers/replay-malformed-body.js +0 -64
  68. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/NOTES.md +0 -104
  69. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/expected.json +0 -46
  70. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/metadata.json +0 -10
  71. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/setup.sh +0 -336
  72. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/spec.md +0 -51
  73. package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/task.txt +0 -9
  74. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/NOTES.md +0 -38
  75. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/expected.json +0 -64
  76. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/metadata.json +0 -10
  77. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/setup.sh +0 -32
  78. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/spec.md +0 -63
  79. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/task.txt +0 -7
  80. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/exact-success.js +0 -54
  81. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/no-hardcoded-pricing.js +0 -47
  82. package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/stock-error.js +0 -45
  83. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/NOTES.md +0 -57
  84. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/expected-pair-plan-registry.json +0 -170
  85. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/expected.json +0 -84
  86. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/metadata.json +0 -21
  87. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/pair-plan.sample-fail.json +0 -214
  88. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/pair-plan.sample-pass.json +0 -223
  89. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/setup.sh +0 -5
  90. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/spec.md +0 -55
  91. package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/task.txt +0 -14
  92. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/NOTES.md +0 -39
  93. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/expected.json +0 -62
  94. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/metadata.json +0 -10
  95. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/setup.sh +0 -2
  96. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/spec.md +0 -67
  97. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/task.txt +0 -7
  98. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/verifiers/error-order.js +0 -55
  99. package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/verifiers/priority-blocked.js +0 -48
  100. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/NOTES.md +0 -35
  101. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/expected.json +0 -56
  102. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/metadata.json +0 -10
  103. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/setup.sh +0 -2
  104. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/spec.md +0 -64
  105. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/task.txt +0 -7
  106. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/verifiers/conflicting-duplicate.js +0 -34
  107. package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/verifiers/idempotent-close.js +0 -41
  108. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/NOTES.md +0 -39
  109. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/expected.json +0 -56
  110. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/metadata.json +0 -10
  111. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/setup.sh +0 -2
  112. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/spec.md +0 -76
  113. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/task.txt +0 -7
  114. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/verifiers/priority-rollback.js +0 -64
  115. package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/verifiers/single-warehouse-fefo.js +0 -66
  116. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/NOTES.md +0 -40
  117. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/expected.json +0 -66
  118. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/metadata.json +0 -10
  119. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/setup.sh +0 -36
  120. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/spec.md +0 -71
  121. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/task.txt +0 -7
  122. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/catalog-source.js +0 -57
  123. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/exact-success.js +0 -63
  124. package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/stock-error.js +0 -34
  125. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/NOTES.md +0 -31
  126. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/expected.json +0 -68
  127. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/metadata.json +0 -10
  128. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/setup.sh +0 -17
  129. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/spec.md +0 -68
  130. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/task.txt +0 -7
  131. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/conflicting-duplicate.js +0 -29
  132. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/exact-payout.js +0 -58
  133. package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/rules-source.js +0 -56
  134. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/NOTES.md +0 -35
  135. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/expected-pair-plan-registry.json +0 -162
  136. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/expected.json +0 -67
  137. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/metadata.json +0 -19
  138. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/setup.sh +0 -4
  139. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/spec.md +0 -55
  140. package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/task.txt +0 -9
  141. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/NOTES.md +0 -34
  142. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/expected.json +0 -57
  143. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/metadata.json +0 -10
  144. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/setup.sh +0 -2
  145. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/spec.md +0 -67
  146. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/task.txt +0 -7
  147. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/verifiers/duplicate-event-error.js +0 -35
  148. package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/verifiers/priority-transfer-rollback.js +0 -53
  149. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/NOTES.md +0 -38
  150. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/expected.json +0 -57
  151. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/metadata.json +0 -10
  152. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/setup.sh +0 -2
  153. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/spec.md +0 -70
  154. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/task.txt +0 -3
  155. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/verifiers/duplicate-renewal-error.js +0 -42
  156. package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/verifiers/priority-credit-rollback.js +0 -70
  157. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/NOTES.md +0 -47
  158. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/expected.json +0 -57
  159. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/metadata.json +0 -10
  160. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/setup.sh +0 -6
  161. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/spec.md +0 -48
  162. package/benchmark/auto-resolve/fixtures/F4-web-browser-design/task.txt +0 -9
  163. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/NOTES.md +0 -45
  164. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/expected.json +0 -65
  165. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/metadata.json +0 -10
  166. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/setup.sh +0 -55
  167. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/spec.md +0 -48
  168. package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/task.txt +0 -7
  169. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/NOTES.md +0 -43
  170. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/expected.json +0 -77
  171. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/metadata.json +0 -10
  172. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/setup.sh +0 -4
  173. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/spec.md +0 -48
  174. package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/task.txt +0 -10
  175. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/NOTES.md +0 -57
  176. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/expected.json +0 -76
  177. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/metadata.json +0 -10
  178. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/setup.sh +0 -36
  179. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/spec.md +0 -45
  180. package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/task.txt +0 -7
  181. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/NOTES.md +0 -53
  182. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/expected.json +0 -63
  183. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/metadata.json +0 -10
  184. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/setup.sh +0 -4
  185. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/spec.md +0 -48
  186. package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/task.txt +0 -1
  187. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/NOTES.md +0 -105
  188. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/expected.json +0 -74
  189. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/metadata.json +0 -10
  190. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/setup.sh +0 -28
  191. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/spec.md +0 -59
  192. package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/task.txt +0 -5
  193. package/benchmark/auto-resolve/fixtures/SCHEMA.md +0 -188
  194. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/NOTES.md +0 -37
  195. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/RETIRED.md +0 -13
  196. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/expected.json +0 -56
  197. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/metadata.json +0 -10
  198. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/setup.sh +0 -18
  199. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/spec.md +0 -69
  200. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/task.txt +0 -7
  201. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/verifiers/exact-proration.js +0 -48
  202. package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/verifiers/rules-source-and-conflict.js +0 -79
  203. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/NOTES.md +0 -54
  204. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/RETIRED.md +0 -7
  205. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/expected.json +0 -67
  206. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/metadata.json +0 -10
  207. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/setup.sh +0 -2
  208. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/spec.md +0 -67
  209. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/task.txt +0 -5
  210. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/policy-precedence.js +0 -72
  211. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/validation-and-immutability.js +0 -43
  212. package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/validation-boundary.js +0 -116
  213. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/NOTES.md +0 -35
  214. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/RETIRED.md +0 -12
  215. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/expected.json +0 -58
  216. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/metadata.json +0 -10
  217. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/setup.sh +0 -2
  218. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/spec.md +0 -73
  219. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/task.txt +0 -17
  220. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/verifiers/mixed-idempotent-settlement.js +0 -53
  221. package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/verifiers/rejection-boundaries.js +0 -74
  222. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/NOTES.md +0 -28
  223. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/RETIRED.md +0 -15
  224. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/expected.json +0 -57
  225. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/metadata.json +0 -10
  226. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/setup.sh +0 -50
  227. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/spec.md +0 -59
  228. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/task.txt +0 -9
  229. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/verifiers/ordered-settlement.js +0 -88
  230. package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/verifiers/reject-invalid.js +0 -45
  231. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/NOTES.md +0 -30
  232. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/RETIRED.md +0 -16
  233. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/expected.json +0 -64
  234. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/metadata.json +0 -10
  235. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/setup.sh +0 -21
  236. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/spec.md +0 -58
  237. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/task.txt +0 -9
  238. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/dup-op.js +0 -54
  239. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/priority-apply.js +0 -82
  240. package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/rollback.js +0 -77
  241. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/NOTES.md +0 -99
  242. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/RETIRED.md +0 -18
  243. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/expected.json +0 -64
  244. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/metadata.json +0 -10
  245. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/setup.sh +0 -4
  246. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/spec.md +0 -61
  247. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/task.txt +0 -9
  248. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/correctness-small.js +0 -135
  249. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/scale.js +0 -100
  250. package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/validation.js +0 -75
  251. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/NOTES.md +0 -93
  252. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/RETIRED.md +0 -16
  253. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/expected.json +0 -65
  254. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/metadata.json +0 -10
  255. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/setup.sh +0 -18
  256. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/spec.md +0 -66
  257. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/task.txt +0 -9
  258. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/correctness-small.js +0 -84
  259. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/scale.js +0 -184
  260. package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/validation.js +0 -74
  261. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/NOTES.md +0 -60
  262. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/RETIRED.md +0 -29
  263. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/expected.json +0 -73
  264. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/metadata.json +0 -10
  265. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/setup.sh +0 -28
  266. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/spec.md +0 -58
  267. package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/task.txt +0 -5
  268. package/benchmark/auto-resolve/fixtures/test-repo/README.md +0 -12
  269. package/benchmark/auto-resolve/fixtures/test-repo/bin/cli.js +0 -63
  270. package/benchmark/auto-resolve/fixtures/test-repo/package-lock.json +0 -823
  271. package/benchmark/auto-resolve/fixtures/test-repo/package.json +0 -22
  272. package/benchmark/auto-resolve/fixtures/test-repo/playwright.config.js +0 -17
  273. package/benchmark/auto-resolve/fixtures/test-repo/server/index.js +0 -37
  274. package/benchmark/auto-resolve/fixtures/test-repo/tests/cli.test.js +0 -25
  275. package/benchmark/auto-resolve/fixtures/test-repo/tests/server.test.js +0 -58
  276. package/benchmark/auto-resolve/fixtures/test-repo/web/index.html +0 -37
  277. package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.json +0 -25
  278. package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.md +0 -10
  279. package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.json +0 -18
  280. package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.md +0 -9
  281. package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.json +0 -18
  282. package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.md +0 -9
  283. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F16-cli-quote-tax-rules/l2_risk_probes/result.json +0 -28
  284. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F23-cli-fulfillment-wave/l2_risk_probes/result.json +0 -28
  285. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F25-cli-cart-promotion-rules/l2_risk_probes/result.json +0 -30
  286. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/full-pipeline-pair-gate.json +0 -82
  287. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/full-pipeline-pair-gate.md +0 -18
  288. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/headroom-gate.json +0 -46
  289. package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/headroom-gate.md +0 -17
  290. package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.json +0 -28
  291. package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.md +0 -15
  292. package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.json +0 -28
  293. package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.md +0 -15
  294. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/F21-cli-scheduler-priority/l2_risk_probes/result.json +0 -32
  295. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.json +0 -44
  296. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.md +0 -16
  297. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.json +0 -18
  298. package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.md +0 -9
  299. package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.json +0 -28
  300. package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.md +0 -15
  301. package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.json +0 -28
  302. package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.md +0 -15
  303. package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.json +0 -28
  304. package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.md +0 -15
  305. package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.json +0 -28
  306. package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.md +0 -15
  307. package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.json +0 -28
  308. package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.md +0 -15
  309. package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.json +0 -28
  310. package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.md +0 -15
  311. package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.json +0 -28
  312. package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.md +0 -15
  313. package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.json +0 -28
  314. package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.md +0 -15
  315. package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.json +0 -28
  316. package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.md +0 -15
  317. package/benchmark/auto-resolve/run-real-benchmark.md +0 -303
  318. package/benchmark/auto-resolve/scripts/audit-headroom-rejections.py +0 -441
  319. package/benchmark/auto-resolve/scripts/audit-pair-evidence.py +0 -1256
  320. package/benchmark/auto-resolve/scripts/build-pair-eligible-manifest.py +0 -306
  321. package/benchmark/auto-resolve/scripts/check-f9-artifacts.py +0 -268
  322. package/benchmark/auto-resolve/scripts/collect-swebench-predictions.py +0 -108
  323. package/benchmark/auto-resolve/scripts/compile-report.py +0 -493
  324. package/benchmark/auto-resolve/scripts/fetch-swebench-instances.py +0 -129
  325. package/benchmark/auto-resolve/scripts/frozen-verify-gate.py +0 -434
  326. package/benchmark/auto-resolve/scripts/full-pipeline-pair-gate.py +0 -612
  327. package/benchmark/auto-resolve/scripts/headroom-gate.py +0 -378
  328. package/benchmark/auto-resolve/scripts/iter-0033c-compare.py +0 -683
  329. package/benchmark/auto-resolve/scripts/iter-0033c-l1-summary.py +0 -97
  330. package/benchmark/auto-resolve/scripts/judge-opus-pass.sh +0 -542
  331. package/benchmark/auto-resolve/scripts/judge.sh +0 -565
  332. package/benchmark/auto-resolve/scripts/oracle-scope-tier-a.py +0 -256
  333. package/benchmark/auto-resolve/scripts/oracle-scope-tier-b.py +0 -287
  334. package/benchmark/auto-resolve/scripts/oracle-test-fidelity.py +0 -328
  335. package/benchmark/auto-resolve/scripts/pair-candidate-frontier.py +0 -469
  336. package/benchmark/auto-resolve/scripts/pair-plan-idgen.py +0 -401
  337. package/benchmark/auto-resolve/scripts/pair-plan-lint.py +0 -475
  338. package/benchmark/auto-resolve/scripts/pair-rejected-fixtures.sh +0 -103
  339. package/benchmark/auto-resolve/scripts/pair_evidence_contract.py +0 -270
  340. package/benchmark/auto-resolve/scripts/prepare-swebench-frozen-case.py +0 -273
  341. package/benchmark/auto-resolve/scripts/prepare-swebench-frozen-corpus.py +0 -148
  342. package/benchmark/auto-resolve/scripts/prepare-swebench-solver-worktree.py +0 -210
  343. package/benchmark/auto-resolve/scripts/recent-benchmark-summary.py +0 -232
  344. package/benchmark/auto-resolve/scripts/run-fixture.sh +0 -1024
  345. package/benchmark/auto-resolve/scripts/run-frozen-verify-pair.sh +0 -683
  346. package/benchmark/auto-resolve/scripts/run-full-pipeline-pair-candidate.sh +0 -458
  347. package/benchmark/auto-resolve/scripts/run-headroom-candidate.sh +0 -336
  348. package/benchmark/auto-resolve/scripts/run-iter-0033c.sh +0 -208
  349. package/benchmark/auto-resolve/scripts/run-suite.sh +0 -251
  350. package/benchmark/auto-resolve/scripts/run-swebench-frozen-corpus.sh +0 -310
  351. package/benchmark/auto-resolve/scripts/run-swebench-solver-batch.sh +0 -257
  352. package/benchmark/auto-resolve/scripts/ship-gate.py +0 -391
  353. package/benchmark/auto-resolve/scripts/solo-ceiling-avoidance.py +0 -53
  354. package/benchmark/auto-resolve/scripts/solo-headroom-hypothesis.py +0 -77
  355. package/benchmark/auto-resolve/scripts/swebench-frozen-matrix.py +0 -478
  356. package/benchmark/auto-resolve/scripts/test-audit-headroom-rejections.sh +0 -288
  357. package/benchmark/auto-resolve/scripts/test-audit-pair-evidence.sh +0 -1672
  358. package/benchmark/auto-resolve/scripts/test-benchmark-arg-parsing.sh +0 -1015
  359. package/benchmark/auto-resolve/scripts/test-build-pair-eligible-manifest.sh +0 -491
  360. package/benchmark/auto-resolve/scripts/test-check-f9-artifacts.sh +0 -91
  361. package/benchmark/auto-resolve/scripts/test-frozen-verify-gate.sh +0 -517
  362. package/benchmark/auto-resolve/scripts/test-full-pipeline-pair-gate.sh +0 -610
  363. package/benchmark/auto-resolve/scripts/test-headroom-gate.sh +0 -401
  364. package/benchmark/auto-resolve/scripts/test-iter-0033c-compare.sh +0 -525
  365. package/benchmark/auto-resolve/scripts/test-iter-0033c-l1-summary.sh +0 -254
  366. package/benchmark/auto-resolve/scripts/test-lint-fixtures.sh +0 -580
  367. package/benchmark/auto-resolve/scripts/test-pair-candidate-frontier.sh +0 -591
  368. package/benchmark/auto-resolve/scripts/test-run-full-pipeline-pair-candidate.sh +0 -497
  369. package/benchmark/auto-resolve/scripts/test-run-headroom-candidate.sh +0 -401
  370. package/benchmark/auto-resolve/scripts/test-run-swebench-solver-batch.sh +0 -111
  371. package/benchmark/auto-resolve/scripts/test-ship-gate.sh +0 -1189
  372. package/benchmark/auto-resolve/scripts/test-swebench-frozen-case.sh +0 -1221
  373. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/NOTES.md +0 -28
  374. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/expected.json +0 -63
  375. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/metadata.json +0 -10
  376. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/setup.sh +0 -3
  377. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/spec.md +0 -47
  378. package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/task.txt +0 -1
  379. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/NOTES.md +0 -34
  380. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/expected.json +0 -53
  381. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/metadata.json +0 -10
  382. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/setup.sh +0 -3
  383. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/spec.md +0 -50
  384. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/task.txt +0 -1
  385. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/verifiers/duplicate-order-error.js +0 -27
  386. package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/verifiers/priority-stock-reservation.js +0 -44
  387. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/NOTES.md +0 -34
  388. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/expected.json +0 -55
  389. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/metadata.json +0 -10
  390. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/setup.sh +0 -3
  391. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/spec.md +0 -52
  392. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/task.txt +0 -1
  393. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/verifiers/duplicate-ticket-error.js +0 -29
  394. package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/verifiers/priority-agent-assignment.js +0 -48
  395. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/NOTES.md +0 -34
  396. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/expected.json +0 -55
  397. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/metadata.json +0 -10
  398. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/setup.sh +0 -3
  399. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/spec.md +0 -55
  400. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/task.txt +0 -1
  401. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/verifiers/duplicate-return-error.js +0 -43
  402. package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/verifiers/priority-return-routing.js +0 -70
  403. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/NOTES.md +0 -37
  404. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/expected.json +0 -54
  405. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/metadata.json +0 -10
  406. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/setup.sh +0 -3
  407. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/spec.md +0 -59
  408. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/task.txt +0 -1
  409. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/verifiers/credit-ledger-priority.js +0 -98
  410. package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/verifiers/duplicate-charge-error.js +0 -38
  411. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/NOTES.md +0 -36
  412. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/expected.json +0 -56
  413. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/metadata.json +0 -10
  414. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/setup.sh +0 -3
  415. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/spec.md +0 -59
  416. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/task.txt +0 -1
  417. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/verifiers/duplicate-refund-error.js +0 -41
  418. package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/verifiers/priority-refund-ledger.js +0 -65
  419. package/config/skills/_shared/pair-plan-schema.md +0 -298
  420. package/scripts/lint-fixtures.sh +0 -352
  421. package/scripts/lint-shadow-fixtures.sh +0 -58
  422. package/scripts/lint-skills.sh +0 -4265
  423. package/scripts/test-owner-phases.py +0 -238
@@ -1,1256 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Composite audit for pair-evidence readiness.
3
-
4
- This is the release/handoff guard for solo<pair benchmark evidence. It runs:
5
- 1. pair-candidate-frontier.py --fail-on-unmeasured
6
- 2. audit-headroom-rejections.py
7
-
8
- Both checks are provider-free and operate only on fixtures, the rejected
9
- registry, and local gate summary artifacts.
10
- """
11
- from __future__ import annotations
12
-
13
- import argparse
14
- import json
15
- import math
16
- import pathlib
17
- import subprocess
18
- import sys
19
- import tempfile
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
-
25
- from pair_evidence_contract import (
26
- best_pair_evidence,
27
- has_historical_pair_trigger_reason,
28
- is_historical_pair_trigger_reason,
29
- is_strict_int,
30
- is_strict_number,
31
- loads_strict_json_object,
32
- )
33
-
34
-
35
- FRONTIER_SUMMARY_KEYS = [
36
- "verdict",
37
- "min_pair_margin",
38
- "max_pair_solo_wall_ratio",
39
- "fixtures_total",
40
- "rejected_count",
41
- "candidate_count",
42
- "pair_evidence_count",
43
- "unmeasured_count",
44
- "pair_margin_avg",
45
- "pair_margin_min",
46
- "pair_solo_wall_ratio_avg",
47
- "pair_solo_wall_ratio_max",
48
- ]
49
- def run_check(
50
- name: str,
51
- args: list[str],
52
- *,
53
- stdout_path: pathlib.Path | None = None,
54
- stderr_path: pathlib.Path | None = None,
55
- ) -> int:
56
- print(f"[audit] {name}", flush=True)
57
- proc = subprocess.run(args, text=True, capture_output=True)
58
- if stdout_path is not None:
59
- stdout_path.write_text(proc.stdout, encoding="utf8")
60
- if stderr_path is not None:
61
- stderr_path.write_text(proc.stderr, encoding="utf8")
62
- if proc.stdout:
63
- print(proc.stdout, end="", flush=True)
64
- if proc.stderr:
65
- print(proc.stderr, end="", file=sys.stderr, flush=True)
66
- if proc.returncode == 0:
67
- print(f"[audit] {name}: PASS", flush=True)
68
- else:
69
- print(f"[audit] {name}: FAIL", file=sys.stderr, flush=True)
70
- return proc.returncode
71
-
72
-
73
- def write_audit_report(
74
- *,
75
- out_dir: pathlib.Path,
76
- frontier_status: int,
77
- headroom_status: int,
78
- min_pair_evidence: int,
79
- min_pair_margin: int,
80
- max_pair_solo_wall_ratio: float,
81
- frontier_report_status: int,
82
- frontier_stdout_status: int,
83
- headroom_report_status: int,
84
- pair_evidence_status: int,
85
- pair_evidence_quality_status: int,
86
- pair_trigger_reason_status: int,
87
- pair_evidence_hypothesis_status: int,
88
- pair_evidence_hypothesis_trigger_status: int,
89
- require_hypothesis_trigger: bool,
90
- fixtures_root: pathlib.Path,
91
- ) -> None:
92
- frontier_summary = load_summary(out_dir / "frontier.json", FRONTIER_SUMMARY_KEYS)
93
- pair_evidence_rows = load_pair_evidence_rows(out_dir / "frontier.json")
94
- pair_evidence_count = frontier_summary.get("pair_evidence_count")
95
- pair_margins = [
96
- row["pair_margin"]
97
- for row in pair_evidence_rows
98
- if is_strict_int(row.get("pair_margin"))
99
- ]
100
- wall_ratios = [
101
- row["pair_solo_wall_ratio"]
102
- for row in pair_evidence_rows
103
- if is_strict_number(row.get("pair_solo_wall_ratio"))
104
- ]
105
- trigger_reason_rows = [
106
- row
107
- for row in pair_evidence_rows
108
- if isinstance(row.get("pair_trigger_reasons"), list)
109
- ]
110
- canonical_trigger_rows = [
111
- row
112
- for row in trigger_reason_rows
113
- if row.get("pair_trigger_has_canonical_reason") is True
114
- ]
115
- historical_alias_trigger_rows = [
116
- row
117
- for row in trigger_reason_rows
118
- if has_historical_pair_trigger_reason(row["pair_trigger_reasons"])
119
- ]
120
- historical_alias_details = pair_trigger_historical_alias_details(
121
- historical_alias_trigger_rows,
122
- )
123
- hypothesis_rows = pair_evidence_hypothesis_rows(
124
- out_dir / "frontier.json",
125
- fixtures_root,
126
- )
127
- hypothesis_passing_rows = [
128
- row for row in hypothesis_rows if row.get("has_actionable_hypothesis") is True
129
- ]
130
- hypothesis_trigger_rows = pair_evidence_hypothesis_trigger_rows(
131
- out_dir / "frontier.json",
132
- fixtures_root,
133
- )
134
- hypothesis_trigger_matched_rows = [
135
- row
136
- for row in hypothesis_trigger_rows
137
- if row.get("has_actionable_hypothesis") is True
138
- and row.get("has_hypothesis_trigger") is True
139
- ]
140
- hypothesis_trigger_gap_details = pair_evidence_hypothesis_trigger_gap_details(
141
- hypothesis_trigger_rows,
142
- )
143
- frontier_stdout_metrics = load_frontier_stdout_metrics(
144
- out_dir / "frontier.json",
145
- out_dir / "frontier.stdout",
146
- )
147
- headroom_audit_summary = load_headroom_audit_summary(
148
- out_dir / "headroom-audit.json",
149
- )
150
- report = {
151
- "verdict": (
152
- "PASS"
153
- if (
154
- frontier_status == 0
155
- and headroom_status == 0
156
- and frontier_report_status == 0
157
- and frontier_stdout_status == 0
158
- and headroom_report_status == 0
159
- and pair_evidence_status == 0
160
- and pair_evidence_quality_status == 0
161
- and pair_trigger_reason_status == 0
162
- and pair_evidence_hypothesis_status == 0
163
- and pair_evidence_hypothesis_trigger_status == 0
164
- )
165
- else "FAIL"
166
- ),
167
- "min_pair_evidence": min_pair_evidence,
168
- "min_pair_margin": min_pair_margin,
169
- "max_pair_solo_wall_ratio": max_pair_solo_wall_ratio,
170
- "frontier_summary": frontier_summary,
171
- "pair_evidence_rows": pair_evidence_rows,
172
- "artifacts": {
173
- "frontier_json": "frontier.json",
174
- "frontier_stdout": "frontier.stdout",
175
- "frontier_stderr": "frontier.stderr",
176
- "headroom_audit_json": "headroom-audit.json",
177
- "headroom_rejections_stdout": "headroom-rejections.stdout",
178
- "headroom_rejections_stderr": "headroom-rejections.stderr",
179
- "audit_json": "audit.json",
180
- },
181
- "checks": {
182
- "frontier": {
183
- "status": "PASS" if frontier_status == 0 else "FAIL",
184
- "exit_code": frontier_status,
185
- "report": str(out_dir / "frontier.json"),
186
- },
187
- "headroom_rejections": {
188
- "status": (
189
- "PASS"
190
- if headroom_status == 0 and headroom_report_status == 0
191
- else "FAIL"
192
- ),
193
- "exit_code": headroom_status,
194
- "report_check_exit_code": headroom_report_status,
195
- "report": str(out_dir / "headroom-audit.json"),
196
- **headroom_audit_summary,
197
- },
198
- "frontier_report": {
199
- "status": "PASS" if frontier_report_status == 0 else "FAIL",
200
- "exit_code": frontier_report_status,
201
- "verdict": frontier_summary.get("verdict"),
202
- "unmeasured_count": frontier_summary.get("unmeasured_count"),
203
- },
204
- "frontier_stdout": {
205
- "status": "PASS" if frontier_stdout_status == 0 else "FAIL",
206
- "exit_code": frontier_stdout_status,
207
- "report": str(out_dir / "frontier.stdout"),
208
- **frontier_stdout_metrics,
209
- },
210
- "min_pair_evidence": {
211
- "status": "PASS" if pair_evidence_status == 0 else "FAIL",
212
- "exit_code": pair_evidence_status,
213
- "required": min_pair_evidence,
214
- "actual": pair_evidence_count,
215
- "actual_rows": len(pair_evidence_rows),
216
- "rows_match_count": (
217
- is_strict_int(pair_evidence_count)
218
- and len(pair_evidence_rows) == pair_evidence_count
219
- ),
220
- },
221
- "pair_evidence_quality": {
222
- "status": "PASS" if pair_evidence_quality_status == 0 else "FAIL",
223
- "exit_code": pair_evidence_quality_status,
224
- "min_pair_margin_required": min_pair_margin,
225
- "min_pair_margin_actual": min(pair_margins) if pair_margins else None,
226
- "max_pair_solo_wall_ratio_allowed": max_pair_solo_wall_ratio,
227
- "max_pair_solo_wall_ratio_actual": (
228
- round(max(wall_ratios), 2) if wall_ratios else None
229
- ),
230
- "summary_min_pair_margin": frontier_summary.get("pair_margin_min"),
231
- "summary_max_pair_solo_wall_ratio": frontier_summary.get("pair_solo_wall_ratio_max"),
232
- },
233
- "pair_trigger_reasons": {
234
- "status": "PASS" if pair_trigger_reason_status == 0 else "FAIL",
235
- "exit_code": pair_trigger_reason_status,
236
- "summary_pair_evidence_count": pair_evidence_count,
237
- "canonical_rows": len(canonical_trigger_rows),
238
- "historical_alias_rows": len(historical_alias_trigger_rows),
239
- "historical_alias_details": historical_alias_details,
240
- "exposed_rows": len(trigger_reason_rows),
241
- "total_rows": len(pair_evidence_rows),
242
- "rows_match_count": (
243
- is_strict_int(pair_evidence_count)
244
- and len(pair_evidence_rows) == pair_evidence_count
245
- ),
246
- },
247
- "pair_evidence_hypotheses": {
248
- "status": "PASS" if pair_evidence_hypothesis_status == 0 else "FAIL",
249
- "exit_code": pair_evidence_hypothesis_status,
250
- "documented_rows": len(hypothesis_passing_rows),
251
- "total_rows": len(pair_evidence_rows),
252
- "rows": hypothesis_rows,
253
- },
254
- "pair_evidence_hypothesis_triggers": {
255
- "status": (
256
- "PASS"
257
- if len(hypothesis_trigger_matched_rows) == len(hypothesis_passing_rows)
258
- else ("FAIL" if require_hypothesis_trigger else "WARN")
259
- ),
260
- "exit_code": pair_evidence_hypothesis_trigger_status,
261
- "required": require_hypothesis_trigger,
262
- "matched_rows": len(hypothesis_trigger_matched_rows),
263
- "documented_rows": len(hypothesis_passing_rows),
264
- "total_rows": len(pair_evidence_rows),
265
- "gap_details": hypothesis_trigger_gap_details,
266
- "rows": hypothesis_trigger_rows,
267
- },
268
- },
269
- }
270
- (out_dir / "audit.json").write_text(json.dumps(report, indent=2) + "\n", encoding="utf8")
271
-
272
-
273
- def load_summary(path: pathlib.Path, keys: list[str]) -> dict[str, object]:
274
- try:
275
- data = loads_strict_json_object(path.read_text())
276
- except (OSError, ValueError, json.JSONDecodeError):
277
- return {}
278
- return {key: data.get(key) for key in keys if key in data}
279
-
280
-
281
- def pair_trigger_historical_alias_details(
282
- rows: list[dict[str, object]],
283
- ) -> list[dict[str, object]]:
284
- return [
285
- {
286
- "fixture": row["fixture"],
287
- "aliases": [
288
- reason
289
- for reason in row["pair_trigger_reasons"]
290
- if isinstance(reason, str)
291
- and is_historical_pair_trigger_reason(reason)
292
- ],
293
- }
294
- for row in rows
295
- if isinstance(row.get("pair_trigger_reasons"), list)
296
- ]
297
-
298
-
299
- def pair_evidence_hypothesis_trigger_gap_details(
300
- rows: list[dict[str, object]],
301
- ) -> list[dict[str, object]]:
302
- return [
303
- {
304
- "fixture": row["fixture"],
305
- "pair_trigger_reasons": [
306
- reason
307
- for reason in row["pair_trigger_reasons"]
308
- if isinstance(reason, str)
309
- ],
310
- }
311
- for row in rows
312
- if row.get("has_actionable_hypothesis") is True
313
- and row.get("has_hypothesis_trigger") is not True
314
- and isinstance(row.get("pair_trigger_reasons"), list)
315
- ]
316
-
317
-
318
- def load_headroom_audit_summary(path: pathlib.Path) -> dict[str, object]:
319
- try:
320
- data = loads_strict_json_object(path.read_text())
321
- except (OSError, ValueError, json.JSONDecodeError):
322
- return {
323
- "verdict": None,
324
- "unrecorded_failure_count": None,
325
- "unsupported_registry_rejection_count": None,
326
- }
327
- unrecorded = data.get("unrecorded_failures")
328
- unsupported = data.get("unsupported_registry_rejections")
329
- return {
330
- "verdict": data.get("verdict"),
331
- "unrecorded_failure_count": (
332
- len(unrecorded) if isinstance(unrecorded, list) else None
333
- ),
334
- "unsupported_registry_rejection_count": (
335
- len(unsupported) if isinstance(unsupported, list) else None
336
- ),
337
- }
338
-
339
-
340
- def check_headroom_audit_report(headroom_json: pathlib.Path) -> int:
341
- summary = load_headroom_audit_summary(headroom_json)
342
- verdict = summary.get("verdict")
343
- unrecorded_count = summary.get("unrecorded_failure_count")
344
- unsupported_count = summary.get("unsupported_registry_rejection_count")
345
- if verdict != "PASS":
346
- print(f"headroom audit verdict {verdict!r} is not PASS", file=sys.stderr)
347
- return 1
348
- if not is_strict_int(unrecorded_count):
349
- print(
350
- "headroom audit unrecorded failure count missing or malformed",
351
- file=sys.stderr,
352
- )
353
- return 1
354
- if not is_strict_int(unsupported_count):
355
- print(
356
- "headroom audit unsupported registry rejection count missing or malformed",
357
- file=sys.stderr,
358
- )
359
- return 1
360
- if unrecorded_count != 0:
361
- print(
362
- f"headroom audit has {unrecorded_count} unrecorded failure(s)",
363
- file=sys.stderr,
364
- )
365
- return 1
366
- if unsupported_count != 0:
367
- print(
368
- f"headroom audit has {unsupported_count} unsupported registry rejection(s)",
369
- file=sys.stderr,
370
- )
371
- return 1
372
- return 0
373
-
374
-
375
- def print_headroom_rejections_summary(
376
- headroom_json: pathlib.Path,
377
- *,
378
- status: int,
379
- ) -> None:
380
- summary = load_headroom_audit_summary(headroom_json)
381
- print(
382
- "headroom_rejections={status} verdict={verdict} "
383
- "unrecorded={unrecorded} unsupported={unsupported}".format(
384
- status="PASS" if status == 0 else "FAIL",
385
- verdict=summary.get("verdict") or "MISSING",
386
- unrecorded=format_count(summary.get("unrecorded_failure_count")),
387
- unsupported=format_count(
388
- summary.get("unsupported_registry_rejection_count")
389
- ),
390
- ),
391
- flush=True,
392
- )
393
-
394
-
395
- def load_frontier_stdout_metrics(
396
- frontier_json: pathlib.Path,
397
- frontier_stdout: pathlib.Path,
398
- ) -> dict[str, object]:
399
- expected_rows = len(load_pair_evidence_rows(frontier_json))
400
- try:
401
- stdout = frontier_stdout.read_text(encoding="utf8")
402
- except OSError:
403
- return {
404
- "summary_rows": None,
405
- "aggregate_rows": None,
406
- "final_verdict_rows": None,
407
- "expected_rows": expected_rows,
408
- "stdout_rows": None,
409
- "trigger_rows": None,
410
- "hypothesis_trigger_rows": None,
411
- "rows_match_count": False,
412
- "trigger_rows_match_count": False,
413
- "hypothesis_trigger_rows_match_count": False,
414
- }
415
- summary = load_summary(
416
- frontier_json,
417
- [
418
- "verdict",
419
- "fixtures_total",
420
- "rejected_count",
421
- "candidate_count",
422
- "pair_evidence_count",
423
- "unmeasured_count",
424
- "pair_margin_avg",
425
- "pair_margin_min",
426
- "pair_solo_wall_ratio_avg",
427
- "pair_solo_wall_ratio_max",
428
- ],
429
- )
430
- summary_rows = None
431
- aggregate_rows = None
432
- final_verdict_rows = None
433
- if all(key in summary for key in [
434
- "verdict",
435
- "fixtures_total",
436
- "rejected_count",
437
- "candidate_count",
438
- "pair_evidence_count",
439
- "unmeasured_count",
440
- ]):
441
- expected_summary = (
442
- "fixtures={fixtures_total} rejected={rejected_count} candidates={candidate_count} "
443
- "pair_evidence={pair_evidence_count} unmeasured={unmeasured_count} verdict={verdict}"
444
- ).format(**summary)
445
- summary_rows = stdout.splitlines().count(expected_summary)
446
- if summary["verdict"] in {"PASS", "FAIL"}:
447
- final_verdict_rows = stdout.splitlines().count(
448
- f"{summary['verdict']} pair-candidate-frontier"
449
- )
450
- if all(key in summary for key in [
451
- "pair_margin_avg",
452
- "pair_margin_min",
453
- "pair_solo_wall_ratio_avg",
454
- "pair_solo_wall_ratio_max",
455
- ]):
456
- expected_aggregate = (
457
- "pair_margin_avg={avg} pair_margin_min={min_margin} "
458
- "wall_avg={wall_avg} wall_max={wall_max}"
459
- ).format(
460
- avg=format_decimal_margin(summary.get("pair_margin_avg")),
461
- min_margin=format_margin(summary.get("pair_margin_min")),
462
- wall_avg=format_wall_ratio(summary.get("pair_solo_wall_ratio_avg")),
463
- wall_max=format_wall_ratio(summary.get("pair_solo_wall_ratio_max")),
464
- )
465
- aggregate_rows = stdout.splitlines().count(expected_aggregate)
466
- stdout_rows = len(
467
- [
468
- line
469
- for line in stdout.splitlines()
470
- if "verdict=pair_evidence_passed" in line
471
- ]
472
- )
473
- trigger_rows = len(
474
- [
475
- line
476
- for line in stdout.splitlines()
477
- if "verdict=pair_evidence_passed" in line and " triggers=" in line
478
- ]
479
- )
480
- hypothesis_trigger_rows = len(
481
- [
482
- line
483
- for line in stdout.splitlines()
484
- if "verdict=pair_evidence_passed" in line and " hypothesis_trigger=" in line
485
- ]
486
- )
487
- return {
488
- "summary_rows": summary_rows,
489
- "aggregate_rows": aggregate_rows,
490
- "final_verdict_rows": final_verdict_rows,
491
- "expected_rows": expected_rows,
492
- "stdout_rows": stdout_rows,
493
- "trigger_rows": trigger_rows,
494
- "hypothesis_trigger_rows": hypothesis_trigger_rows,
495
- "rows_match_count": stdout_rows == expected_rows,
496
- "trigger_rows_match_count": trigger_rows == expected_rows,
497
- "hypothesis_trigger_rows_match_count": hypothesis_trigger_rows == expected_rows,
498
- }
499
-
500
-
501
- def load_pair_evidence_rows(path: pathlib.Path) -> list[dict[str, object]]:
502
- try:
503
- data = loads_strict_json_object(path.read_text())
504
- except (OSError, ValueError, json.JSONDecodeError):
505
- return []
506
- rows = data.get("rows")
507
- if not isinstance(rows, list):
508
- return []
509
- evidence_rows: list[dict[str, object]] = []
510
- for row in rows:
511
- if not isinstance(row, dict) or row.get("status") != "pair_evidence_passed":
512
- continue
513
- fixture = row.get("fixture")
514
- evidence = row.get("passing_pair_evidence")
515
- if not isinstance(fixture, str) or not isinstance(evidence, list):
516
- continue
517
- best = best_pair_evidence(evidence)
518
- if best is not None:
519
- evidence_rows.append({
520
- "fixture": fixture,
521
- "verdict": "pair_evidence_passed",
522
- **best,
523
- })
524
- return evidence_rows
525
-
526
-
527
- def check_min_pair_evidence(frontier_json: pathlib.Path, minimum: int) -> int:
528
- summary = load_summary(frontier_json, ["pair_evidence_count"])
529
- count = summary.get("pair_evidence_count")
530
- if not is_strict_int(count):
531
- print("pair evidence count missing or malformed from frontier report", file=sys.stderr)
532
- return 1
533
- if count < minimum:
534
- print(
535
- f"pair evidence count {count} below required minimum {minimum}",
536
- file=sys.stderr,
537
- )
538
- return 1
539
- rows = load_pair_evidence_rows(frontier_json)
540
- if len(rows) != count:
541
- print(
542
- f"pair evidence rows {len(rows)} do not match summary count {count}",
543
- file=sys.stderr,
544
- )
545
- return 1
546
- return 0
547
-
548
-
549
- def check_pair_evidence_quality(
550
- frontier_json: pathlib.Path,
551
- *,
552
- min_pair_margin: int,
553
- max_pair_solo_wall_ratio: float,
554
- ) -> int:
555
- rows = load_pair_evidence_rows(frontier_json)
556
- if not rows:
557
- print("pair evidence quality check has no complete rows", file=sys.stderr)
558
- return 1
559
- low_margin = [
560
- row["fixture"]
561
- for row in rows
562
- if row["pair_margin"] < min_pair_margin
563
- ]
564
- if low_margin:
565
- print(
566
- "pair evidence margin below minimum for fixture(s): "
567
- + ", ".join(low_margin),
568
- file=sys.stderr,
569
- )
570
- return 1
571
- high_wall = [
572
- row["fixture"]
573
- for row in rows
574
- if row["pair_solo_wall_ratio"] > max_pair_solo_wall_ratio
575
- ]
576
- if high_wall:
577
- print(
578
- "pair evidence wall ratio above maximum for fixture(s): "
579
- + ", ".join(high_wall),
580
- file=sys.stderr,
581
- )
582
- return 1
583
- summary = load_summary(
584
- frontier_json,
585
- ["pair_margin_min", "pair_solo_wall_ratio_max"],
586
- )
587
- actual_min_margin = min(row["pair_margin"] for row in rows)
588
- actual_max_wall = round(max(row["pair_solo_wall_ratio"] for row in rows), 2)
589
- if summary.get("pair_margin_min") != actual_min_margin:
590
- print("frontier pair_margin_min does not match pair evidence rows", file=sys.stderr)
591
- return 1
592
- if summary.get("pair_solo_wall_ratio_max") != actual_max_wall:
593
- print(
594
- "frontier pair_solo_wall_ratio_max does not match pair evidence rows",
595
- file=sys.stderr,
596
- )
597
- return 1
598
- return 0
599
-
600
-
601
- def print_pair_evidence_quality(
602
- frontier_json: pathlib.Path,
603
- *,
604
- min_pair_margin: int,
605
- max_pair_solo_wall_ratio: float,
606
- status: int,
607
- ) -> None:
608
- rows = load_pair_evidence_rows(frontier_json)
609
- pair_margins = [
610
- row["pair_margin"]
611
- for row in rows
612
- if is_strict_int(row.get("pair_margin"))
613
- ]
614
- wall_ratios = [
615
- row["pair_solo_wall_ratio"]
616
- for row in rows
617
- if is_strict_number(row.get("pair_solo_wall_ratio"))
618
- ]
619
- print(
620
- "pair_evidence_quality={status} min_pair_margin_actual={actual_margin} "
621
- "min_pair_margin_required={required_margin} max_wall_actual={actual_wall} "
622
- "max_wall_allowed={allowed_wall}".format(
623
- status="PASS" if status == 0 else "FAIL",
624
- actual_margin=format_margin(min(pair_margins) if pair_margins else None),
625
- required_margin=format_margin(min_pair_margin),
626
- actual_wall=format_wall_ratio(max(wall_ratios) if wall_ratios else None),
627
- allowed_wall=format_wall_ratio(max_pair_solo_wall_ratio),
628
- ),
629
- flush=True,
630
- )
631
-
632
-
633
- def check_pair_trigger_reasons(frontier_json: pathlib.Path) -> int:
634
- rows = load_pair_evidence_rows(frontier_json)
635
- summary = load_summary(frontier_json, ["pair_evidence_count"])
636
- count = summary.get("pair_evidence_count")
637
- if not is_strict_int(count):
638
- print("pair trigger reason count missing or malformed from frontier report", file=sys.stderr)
639
- return 1
640
- if len(rows) != count:
641
- print(
642
- f"pair trigger reason rows {len(rows)} do not match summary count {count}",
643
- file=sys.stderr,
644
- )
645
- return 1
646
- missing = [
647
- row["fixture"]
648
- for row in rows
649
- if not isinstance(row.get("pair_trigger_reasons"), list)
650
- ]
651
- malformed = [
652
- row["fixture"]
653
- for row in rows
654
- if isinstance(row.get("pair_trigger_reasons"), list)
655
- and row.get("pair_trigger_has_canonical_reason") is not True
656
- ]
657
- if missing:
658
- print(
659
- "pair trigger reasons missing for fixture(s): "
660
- + ", ".join(missing),
661
- file=sys.stderr,
662
- )
663
- return 1
664
- if malformed:
665
- print(
666
- "pair trigger reasons missing canonical trigger for fixture(s): "
667
- + ", ".join(malformed),
668
- file=sys.stderr,
669
- )
670
- return 1
671
- return 0
672
-
673
-
674
- def print_pair_trigger_reasons_summary(
675
- frontier_json: pathlib.Path,
676
- *,
677
- status: int,
678
- ) -> None:
679
- rows = load_pair_evidence_rows(frontier_json)
680
- summary = load_summary(frontier_json, ["pair_evidence_count"])
681
- count = summary.get("pair_evidence_count")
682
- rows_match = is_strict_int(count) and len(rows) == count
683
- exposed = [
684
- row
685
- for row in rows
686
- if isinstance(row.get("pair_trigger_reasons"), list)
687
- ]
688
- canonical = [
689
- row
690
- for row in exposed
691
- if row.get("pair_trigger_has_canonical_reason") is True
692
- ]
693
- historical_alias = [
694
- row
695
- for row in exposed
696
- if has_historical_pair_trigger_reason(row["pair_trigger_reasons"])
697
- ]
698
- historical_alias_details = pair_trigger_historical_alias_details(historical_alias)
699
- print(
700
- "pair_trigger_reasons={status} canonical={canonical} historical_alias={historical_alias} "
701
- "exposed={exposed} total={total} summary={summary} rows_match={rows_match}".format(
702
- status="PASS" if status == 0 else "FAIL",
703
- canonical=len(canonical),
704
- historical_alias=len(historical_alias),
705
- exposed=len(exposed),
706
- total=len(rows),
707
- summary=format_count(count),
708
- rows_match=str(rows_match).lower(),
709
- ),
710
- flush=True,
711
- )
712
- if historical_alias_details:
713
- details = [
714
- f"{row['fixture']}={','.join(row['aliases'])}"
715
- for row in historical_alias_details
716
- ]
717
- print(
718
- "pair_trigger_historical_aliases=" + ";".join(details),
719
- flush=True,
720
- )
721
-
722
-
723
- def fixture_has_actionable_hypothesis(fixtures_root: pathlib.Path, fixture: str) -> bool:
724
- checker = SCRIPT_DIR / "solo-headroom-hypothesis.py"
725
- fixture_dir = fixtures_root / fixture
726
- proc = subprocess.run(
727
- [
728
- sys.executable,
729
- str(checker),
730
- "--expected-json",
731
- str(fixture_dir / "expected.json"),
732
- str(fixture_dir / "spec.md"),
733
- ],
734
- text=True,
735
- capture_output=True,
736
- )
737
- return proc.returncode == 0
738
-
739
-
740
- def pair_evidence_hypothesis_rows(
741
- frontier_json: pathlib.Path,
742
- fixtures_root: pathlib.Path,
743
- ) -> list[dict[str, object]]:
744
- rows = load_pair_evidence_rows(frontier_json)
745
- return [
746
- {
747
- "fixture": row["fixture"],
748
- "has_actionable_hypothesis": fixture_has_actionable_hypothesis(
749
- fixtures_root,
750
- str(row["fixture"]),
751
- ),
752
- }
753
- for row in rows
754
- ]
755
-
756
-
757
- def pair_evidence_hypothesis_trigger_rows(
758
- frontier_json: pathlib.Path,
759
- fixtures_root: pathlib.Path,
760
- ) -> list[dict[str, object]]:
761
- rows = load_pair_evidence_rows(frontier_json)
762
- result: list[dict[str, object]] = []
763
- for row in rows:
764
- reasons = row.get("pair_trigger_reasons")
765
- has_actionable = fixture_has_actionable_hypothesis(
766
- fixtures_root,
767
- str(row["fixture"]),
768
- )
769
- has_hypothesis_trigger = (
770
- isinstance(reasons, list)
771
- and "spec.solo_headroom_hypothesis" in reasons
772
- )
773
- result.append(
774
- {
775
- "fixture": row["fixture"],
776
- "has_actionable_hypothesis": has_actionable,
777
- "has_hypothesis_trigger": has_hypothesis_trigger,
778
- "pair_trigger_reasons": reasons if isinstance(reasons, list) else [],
779
- }
780
- )
781
- return result
782
-
783
-
784
- def check_pair_evidence_hypotheses(
785
- frontier_json: pathlib.Path,
786
- fixtures_root: pathlib.Path,
787
- ) -> int:
788
- rows = pair_evidence_hypothesis_rows(frontier_json, fixtures_root)
789
- missing = [
790
- str(row["fixture"])
791
- for row in rows
792
- if row.get("has_actionable_hypothesis") is not True
793
- ]
794
- if missing:
795
- print(
796
- "pair evidence hypotheses missing for fixture(s): "
797
- + ", ".join(missing),
798
- file=sys.stderr,
799
- )
800
- return 1
801
- return 0
802
-
803
-
804
- def check_pair_evidence_hypothesis_triggers(
805
- frontier_json: pathlib.Path,
806
- fixtures_root: pathlib.Path,
807
- *,
808
- required: bool,
809
- ) -> int:
810
- rows = pair_evidence_hypothesis_trigger_rows(frontier_json, fixtures_root)
811
- gaps = pair_evidence_hypothesis_trigger_gap_details(rows)
812
- if required and gaps:
813
- print(
814
- "pair evidence hypothesis triggers missing for fixture(s): "
815
- + ", ".join(str(row["fixture"]) for row in gaps),
816
- file=sys.stderr,
817
- )
818
- return 1
819
- return 0
820
-
821
-
822
- def print_pair_evidence_hypotheses_summary(
823
- frontier_json: pathlib.Path,
824
- fixtures_root: pathlib.Path,
825
- *,
826
- status: int,
827
- ) -> None:
828
- rows = pair_evidence_hypothesis_rows(frontier_json, fixtures_root)
829
- documented = [
830
- row for row in rows if row.get("has_actionable_hypothesis") is True
831
- ]
832
- print(
833
- "pair_evidence_hypotheses={status} documented={documented} total={total}".format(
834
- status="PASS" if status == 0 else "FAIL",
835
- documented=len(documented),
836
- total=len(rows),
837
- ),
838
- flush=True,
839
- )
840
-
841
-
842
- def print_pair_evidence_hypothesis_triggers_summary(
843
- frontier_json: pathlib.Path,
844
- fixtures_root: pathlib.Path,
845
- *,
846
- required: bool,
847
- ) -> None:
848
- rows = pair_evidence_hypothesis_trigger_rows(frontier_json, fixtures_root)
849
- documented = [
850
- row for row in rows if row.get("has_actionable_hypothesis") is True
851
- ]
852
- matched = [
853
- row
854
- for row in documented
855
- if row.get("has_hypothesis_trigger") is True
856
- ]
857
- status = "PASS" if len(matched) == len(documented) else ("FAIL" if required else "WARN")
858
- print(
859
- "pair_evidence_hypothesis_triggers={status} matched={matched} "
860
- "documented={documented} total={total}".format(
861
- status=status,
862
- matched=len(matched),
863
- documented=len(documented),
864
- total=len(rows),
865
- ),
866
- flush=True,
867
- )
868
- gap_details = pair_evidence_hypothesis_trigger_gap_details(rows)
869
- if gap_details:
870
- details = [
871
- f"{row['fixture']}={','.join(row['pair_trigger_reasons'])}"
872
- for row in gap_details
873
- ]
874
- print(
875
- "pair_evidence_hypothesis_trigger_gaps=" + ";".join(details),
876
- flush=True,
877
- )
878
-
879
-
880
- def check_frontier_report(frontier_json: pathlib.Path) -> int:
881
- summary = load_summary(frontier_json, ["verdict", "unmeasured_count"])
882
- verdict = summary.get("verdict")
883
- unmeasured_count = summary.get("unmeasured_count")
884
- if verdict != "PASS":
885
- print(f"frontier verdict {verdict!r} is not PASS", file=sys.stderr)
886
- return 1
887
- if not is_strict_int(unmeasured_count):
888
- print("frontier unmeasured count missing or malformed", file=sys.stderr)
889
- return 1
890
- if unmeasured_count != 0:
891
- print(
892
- f"frontier has {unmeasured_count} unmeasured candidate fixture(s)",
893
- file=sys.stderr,
894
- )
895
- return 1
896
- return 0
897
-
898
-
899
- def check_frontier_stdout(frontier_json: pathlib.Path, frontier_stdout: pathlib.Path) -> int:
900
- try:
901
- stdout = frontier_stdout.read_text(encoding="utf8")
902
- except OSError:
903
- print("frontier stdout artifact missing", file=sys.stderr)
904
- return 1
905
- summary = load_summary(
906
- frontier_json,
907
- [
908
- "verdict",
909
- "fixtures_total",
910
- "rejected_count",
911
- "candidate_count",
912
- "pair_evidence_count",
913
- "unmeasured_count",
914
- "pair_margin_avg",
915
- "pair_margin_min",
916
- "pair_solo_wall_ratio_avg",
917
- "pair_solo_wall_ratio_max",
918
- ],
919
- )
920
- required_keys = {
921
- "verdict",
922
- "fixtures_total",
923
- "rejected_count",
924
- "candidate_count",
925
- "pair_evidence_count",
926
- "unmeasured_count",
927
- }
928
- if not required_keys.issubset(set(summary)):
929
- print("frontier stdout check missing summary fields", file=sys.stderr)
930
- return 1
931
- count_keys = {
932
- "fixtures_total",
933
- "rejected_count",
934
- "candidate_count",
935
- "pair_evidence_count",
936
- "unmeasured_count",
937
- }
938
- if any(not is_strict_int(summary.get(key)) for key in count_keys):
939
- print("frontier stdout summary counts malformed", file=sys.stderr)
940
- return 1
941
- required_summary = (
942
- "fixtures={fixtures_total} rejected={rejected_count} candidates={candidate_count} "
943
- "pair_evidence={pair_evidence_count} unmeasured={unmeasured_count} verdict={verdict}"
944
- ).format(**summary)
945
- summary_count = stdout.splitlines().count(required_summary)
946
- if summary_count != 1:
947
- print("frontier stdout summary score row count is not exactly 1", file=sys.stderr)
948
- return 1
949
- pair_evidence_count = summary.get("pair_evidence_count")
950
- if pair_evidence_count > 0:
951
- aggregate_keys = {
952
- "pair_margin_avg",
953
- "pair_margin_min",
954
- "pair_solo_wall_ratio_avg",
955
- "pair_solo_wall_ratio_max",
956
- }
957
- if not aggregate_keys.issubset(set(summary)):
958
- print("frontier stdout check missing aggregate fields", file=sys.stderr)
959
- return 1
960
- if (
961
- not is_strict_number(summary.get("pair_margin_avg"))
962
- or not is_strict_int(summary.get("pair_margin_min"))
963
- or not is_strict_number(summary.get("pair_solo_wall_ratio_avg"))
964
- or not is_strict_number(summary.get("pair_solo_wall_ratio_max"))
965
- ):
966
- print("frontier stdout aggregate fields malformed", file=sys.stderr)
967
- return 1
968
- required_aggregate = (
969
- "pair_margin_avg={avg} pair_margin_min={min_margin} "
970
- "wall_avg={wall_avg} wall_max={wall_max}"
971
- ).format(
972
- avg=format_decimal_margin(summary.get("pair_margin_avg")),
973
- min_margin=format_margin(summary.get("pair_margin_min")),
974
- wall_avg=format_wall_ratio(summary.get("pair_solo_wall_ratio_avg")),
975
- wall_max=format_wall_ratio(summary.get("pair_solo_wall_ratio_max")),
976
- )
977
- aggregate_count = stdout.splitlines().count(required_aggregate)
978
- if aggregate_count != 1:
979
- print("frontier stdout aggregate score row count is not exactly 1", file=sys.stderr)
980
- return 1
981
- expected_rows = load_pair_evidence_rows(frontier_json)
982
- stdout_score_rows = [
983
- line
984
- for line in stdout.splitlines()
985
- if "verdict=pair_evidence_passed" in line
986
- ]
987
- if len(stdout_score_rows) != len(expected_rows):
988
- print(
989
- f"frontier stdout score row count {len(stdout_score_rows)} "
990
- f"does not match frontier evidence row count {len(expected_rows)}",
991
- file=sys.stderr,
992
- )
993
- return 1
994
- for row in expected_rows:
995
- required_row = (
996
- "{fixture}: bare={bare_score} solo_claude={solo_score} pair={pair_score} "
997
- "arm={pair_arm} margin={pair_margin:+d} wall={wall} run={run_id} "
998
- "verdict=pair_evidence_passed triggers={triggers} "
999
- "hypothesis_trigger={hypothesis_trigger}"
1000
- ).format(
1001
- **row,
1002
- wall=format_wall_ratio(row.get("pair_solo_wall_ratio")),
1003
- triggers=format_trigger_reasons(row.get("pair_trigger_reasons")),
1004
- hypothesis_trigger=format_bool(row.get("pair_trigger_has_hypothesis_reason")),
1005
- )
1006
- if required_row not in stdout:
1007
- print(f"frontier stdout missing score row for {row['fixture']}", file=sys.stderr)
1008
- return 1
1009
- if summary["verdict"] not in {"PASS", "FAIL"}:
1010
- print("frontier stdout verdict malformed", file=sys.stderr)
1011
- return 1
1012
- final_verdict_count = stdout.splitlines().count(
1013
- f"{summary['verdict']} pair-candidate-frontier"
1014
- )
1015
- if final_verdict_count != 1:
1016
- print("frontier stdout final verdict row count is not exactly 1", file=sys.stderr)
1017
- return 1
1018
- return 0
1019
-
1020
-
1021
- def format_wall_ratio(value: object) -> str:
1022
- return f"{value:.2f}x" if is_strict_number(value) else ""
1023
-
1024
-
1025
- def format_decimal_margin(value: object) -> str:
1026
- return f"{value:+.2f}" if is_strict_number(value) else ""
1027
-
1028
-
1029
- def format_margin(value: object) -> str:
1030
- return f"{value:+d}" if is_strict_int(value) else ""
1031
-
1032
-
1033
- def format_count(value: object) -> str:
1034
- return str(value) if is_strict_int(value) else "MISSING"
1035
-
1036
-
1037
- def format_trigger_reasons(value: object) -> str:
1038
- if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
1039
- return ""
1040
- return ",".join(value)
1041
-
1042
-
1043
- def format_bool(value: object) -> str:
1044
- return str(value).lower() if isinstance(value, bool) else ""
1045
-
1046
-
1047
- def main() -> int:
1048
- parser = argparse.ArgumentParser()
1049
- parser.add_argument(
1050
- "--fixtures-root",
1051
- type=pathlib.Path,
1052
- default=pathlib.Path("benchmark/auto-resolve/fixtures"),
1053
- )
1054
- parser.add_argument(
1055
- "--registry",
1056
- type=pathlib.Path,
1057
- default=SCRIPT_DIR / "pair-rejected-fixtures.sh",
1058
- )
1059
- parser.add_argument(
1060
- "--results-root",
1061
- type=pathlib.Path,
1062
- default=pathlib.Path("benchmark/auto-resolve/results"),
1063
- )
1064
- parser.add_argument(
1065
- "--out-dir",
1066
- type=pathlib.Path,
1067
- help="optional directory for audit.json, frontier.json, and headroom-audit.json",
1068
- )
1069
- parser.add_argument(
1070
- "--min-pair-evidence",
1071
- type=int,
1072
- default=4,
1073
- help="minimum active fixtures with passing pair evidence required for PASS",
1074
- )
1075
- parser.add_argument(
1076
- "--min-pair-margin",
1077
- type=int,
1078
- default=5,
1079
- help="minimum pair-over-solo margin required to count passing pair evidence",
1080
- )
1081
- parser.add_argument(
1082
- "--max-pair-solo-wall-ratio",
1083
- type=float,
1084
- default=3.0,
1085
- help="maximum pair/solo wall-time ratio allowed to count passing pair evidence",
1086
- )
1087
- parser.add_argument(
1088
- "--require-hypothesis-trigger",
1089
- action="store_true",
1090
- help=(
1091
- "fail if pair-evidence fixtures with actionable solo-headroom hypotheses "
1092
- "do not expose spec.solo_headroom_hypothesis in trigger reasons"
1093
- ),
1094
- )
1095
- args = parser.parse_args()
1096
- if args.min_pair_evidence < 1:
1097
- print("error: --min-pair-evidence must be >= 1", file=sys.stderr)
1098
- return 2
1099
- if args.min_pair_margin < 1:
1100
- print("error: --min-pair-margin must be >= 1", file=sys.stderr)
1101
- return 2
1102
- if not math.isfinite(args.max_pair_solo_wall_ratio) or args.max_pair_solo_wall_ratio <= 0:
1103
- print("error: --max-pair-solo-wall-ratio must be finite and > 0", file=sys.stderr)
1104
- return 2
1105
-
1106
- out_dir = args.out_dir
1107
- temp_dir = None
1108
- if out_dir is None:
1109
- temp_dir = tempfile.TemporaryDirectory()
1110
- report_dir = pathlib.Path(temp_dir.name)
1111
- else:
1112
- out_dir.mkdir(parents=True, exist_ok=True)
1113
- report_dir = out_dir
1114
-
1115
- common = [
1116
- "--fixtures-root",
1117
- str(args.fixtures_root),
1118
- "--registry",
1119
- str(args.registry),
1120
- "--results-root",
1121
- str(args.results_root),
1122
- ]
1123
- frontier_cmd = [
1124
- sys.executable,
1125
- str(SCRIPT_DIR / "pair-candidate-frontier.py"),
1126
- *common,
1127
- "--fail-on-unmeasured",
1128
- "--min-pair-margin",
1129
- str(args.min_pair_margin),
1130
- "--max-pair-solo-wall-ratio",
1131
- str(args.max_pair_solo_wall_ratio),
1132
- "--out-json",
1133
- str(report_dir / "frontier.json"),
1134
- ]
1135
- headroom_cmd = [
1136
- sys.executable,
1137
- str(SCRIPT_DIR / "audit-headroom-rejections.py"),
1138
- *common,
1139
- "--min-pair-margin",
1140
- str(args.min_pair_margin),
1141
- "--max-pair-solo-wall-ratio",
1142
- str(args.max_pair_solo_wall_ratio),
1143
- "--out-json",
1144
- str(report_dir / "headroom-audit.json"),
1145
- ]
1146
-
1147
- frontier_stdout_path = report_dir / "frontier.stdout"
1148
- frontier_stderr_path = report_dir / "frontier.stderr"
1149
- headroom_stdout_path = report_dir / "headroom-rejections.stdout"
1150
- headroom_stderr_path = report_dir / "headroom-rejections.stderr"
1151
- frontier_status = run_check(
1152
- "frontier",
1153
- frontier_cmd,
1154
- stdout_path=frontier_stdout_path,
1155
- stderr_path=frontier_stderr_path,
1156
- )
1157
- headroom_status = run_check(
1158
- "headroom-rejections",
1159
- headroom_cmd,
1160
- stdout_path=headroom_stdout_path,
1161
- stderr_path=headroom_stderr_path,
1162
- )
1163
- frontier_report_status = check_frontier_report(report_dir / "frontier.json")
1164
- frontier_stdout_status = check_frontier_stdout(
1165
- report_dir / "frontier.json",
1166
- frontier_stdout_path,
1167
- )
1168
- headroom_report_status = check_headroom_audit_report(
1169
- report_dir / "headroom-audit.json",
1170
- )
1171
- print_headroom_rejections_summary(
1172
- report_dir / "headroom-audit.json",
1173
- status=headroom_report_status,
1174
- )
1175
- pair_evidence_status = check_min_pair_evidence(
1176
- report_dir / "frontier.json",
1177
- args.min_pair_evidence,
1178
- )
1179
- pair_evidence_quality_status = check_pair_evidence_quality(
1180
- report_dir / "frontier.json",
1181
- min_pair_margin=args.min_pair_margin,
1182
- max_pair_solo_wall_ratio=args.max_pair_solo_wall_ratio,
1183
- )
1184
- print_pair_evidence_quality(
1185
- report_dir / "frontier.json",
1186
- min_pair_margin=args.min_pair_margin,
1187
- max_pair_solo_wall_ratio=args.max_pair_solo_wall_ratio,
1188
- status=pair_evidence_quality_status,
1189
- )
1190
- pair_trigger_reason_status = check_pair_trigger_reasons(
1191
- report_dir / "frontier.json",
1192
- )
1193
- print_pair_trigger_reasons_summary(
1194
- report_dir / "frontier.json",
1195
- status=pair_trigger_reason_status,
1196
- )
1197
- pair_evidence_hypothesis_status = check_pair_evidence_hypotheses(
1198
- report_dir / "frontier.json",
1199
- args.fixtures_root,
1200
- )
1201
- print_pair_evidence_hypotheses_summary(
1202
- report_dir / "frontier.json",
1203
- args.fixtures_root,
1204
- status=pair_evidence_hypothesis_status,
1205
- )
1206
- pair_evidence_hypothesis_trigger_status = check_pair_evidence_hypothesis_triggers(
1207
- report_dir / "frontier.json",
1208
- args.fixtures_root,
1209
- required=args.require_hypothesis_trigger,
1210
- )
1211
- print_pair_evidence_hypothesis_triggers_summary(
1212
- report_dir / "frontier.json",
1213
- args.fixtures_root,
1214
- required=args.require_hypothesis_trigger,
1215
- )
1216
- if out_dir:
1217
- write_audit_report(
1218
- out_dir=out_dir,
1219
- frontier_status=frontier_status,
1220
- headroom_status=headroom_status,
1221
- min_pair_evidence=args.min_pair_evidence,
1222
- min_pair_margin=args.min_pair_margin,
1223
- max_pair_solo_wall_ratio=args.max_pair_solo_wall_ratio,
1224
- frontier_report_status=frontier_report_status,
1225
- frontier_stdout_status=frontier_stdout_status,
1226
- headroom_report_status=headroom_report_status,
1227
- pair_evidence_status=pair_evidence_status,
1228
- pair_evidence_quality_status=pair_evidence_quality_status,
1229
- pair_trigger_reason_status=pair_trigger_reason_status,
1230
- pair_evidence_hypothesis_status=pair_evidence_hypothesis_status,
1231
- pair_evidence_hypothesis_trigger_status=pair_evidence_hypothesis_trigger_status,
1232
- require_hypothesis_trigger=args.require_hypothesis_trigger,
1233
- fixtures_root=args.fixtures_root,
1234
- )
1235
- if temp_dir is not None:
1236
- temp_dir.cleanup()
1237
- if (
1238
- frontier_status != 0
1239
- or headroom_status != 0
1240
- or frontier_report_status != 0
1241
- or frontier_stdout_status != 0
1242
- or headroom_report_status != 0
1243
- or pair_evidence_status != 0
1244
- or pair_evidence_quality_status != 0
1245
- or pair_trigger_reason_status != 0
1246
- or pair_evidence_hypothesis_status != 0
1247
- or pair_evidence_hypothesis_trigger_status != 0
1248
- ):
1249
- print("FAIL audit-pair-evidence", file=sys.stderr, flush=True)
1250
- return 1
1251
- print("PASS audit-pair-evidence")
1252
- return 0
1253
-
1254
-
1255
- if __name__ == "__main__":
1256
- sys.exit(main())