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,683 +0,0 @@
1
- #!/usr/bin/env python3
2
- """iter-0033c gate table — NEW L2 vs NEW L1 on /devlyn:resolve.
3
-
4
- Reads:
5
- - manifest (immutable; built by build-pair-eligible-manifest.py)
6
- - per-fixture judge.json files under <results-dir>/<fixture>/<arm>/judge.json
7
- - per-fixture timing/result.json files for wall + disqualifier signals
8
- - per-fixture pipeline.state.json (under <work-dir>/.devlyn/runs/) for
9
- pair_judge sub-verdict — required for Gate 6 + Gate 8.
10
-
11
- Emits:
12
- - gates.json — machine-readable {gate_id, status, evidence}
13
- - gates.md — human-readable summary table
14
-
15
- Gates per iter-0033c §"Acceptance gate":
16
- 1a smoke (mode wiring) — recorded externally; pre-suite gate
17
- 1b smoke (codex avail) — recorded externally; pre-suite gate
18
- 1c smoke (impl confound) — recorded externally; pre-suite gate
19
- 2 no-regression vs L1 (gated arm): every fixture (l2_gated − solo) ≥ −3
20
- 3 lift on pair-eligible (gated arm, SHIP-BLOCKER): on frozen pair-eligible set,
21
- count fixtures with (l2_gated − solo) ≥ +5; require count ≥ gate3_threshold_count.
22
- 4 hard-floor: zero l2_gated disqualifier on previously-clean l1 fixtures;
23
- zero l2_gated CRITICAL/HIGH design.* / security.* on previously-clean l1;
24
- zero l2_gated watchdog timeouts (where l1 didn't time out).
25
- 5 efficiency: per-fixture l2_gated_wall / l1_wall ≤ 2.0× (≤ 3.0× allowed only
26
- when l2 catches a categorical rescue l1 missed).
27
- 6 trigger discipline (fixture-level): for each pair-eligible fixture, if
28
- l2_forced lifts ≥ +5 OR catches categorical rescue, AND forced is not
29
- impl-confounded, AND forced.pair_judge present → l2_gated MUST also have
30
- recognized pair_judge verdict on that fixture.
31
- 7 attribution (4-class, data-only): per-fixture classify into
32
- {no_material_lift, implementation_confounded, tool_or_trigger_lift,
33
- deliberation_lift}. Reporting only; not pass/fail.
34
- 8 artifact contract: recognized pair_judge verdict for every fixture where
35
- pair fired; pair findings distinguishable from solo judge findings.
36
-
37
- Ship-blockers: 1a, 1b, 1c, 2, 3, 4, 6.
38
- Quality gates: 5, 8 (failure → root-cause iter; Phase 4 holds).
39
- Data-only: 7.
40
- """
41
- import argparse
42
- import json
43
- import sys
44
- from pathlib import Path
45
-
46
- SCRIPT_DIR = Path(__file__).resolve().parent
47
- if str(SCRIPT_DIR) not in sys.path:
48
- sys.path.insert(0, str(SCRIPT_DIR))
49
-
50
- from pair_evidence_contract import (
51
- is_score,
52
- is_strict_number,
53
- loads_strict_json_object,
54
- reject_json_constant,
55
- )
56
-
57
- PAIR_VERDICTS = {"PASS", "PASS_WITH_ISSUES", "NEEDS_WORK", "BLOCKED", "FAIL"}
58
-
59
-
60
- def exact_bool(value: object) -> bool | None:
61
- return value if isinstance(value, bool) else None
62
-
63
-
64
- def bool_flag(value: object, *, default: bool = False) -> bool:
65
- if value is None:
66
- return default
67
- parsed = exact_bool(value)
68
- return parsed if parsed is not None else True
69
-
70
-
71
- def is_pair_judge_verdict(value: object) -> bool:
72
- return value in PAIR_VERDICTS
73
-
74
-
75
- def load_judge(results_dir: Path, fixture: str) -> dict | None:
76
- p = results_dir / fixture / "judge.json"
77
- if not p.is_file():
78
- return None
79
- try:
80
- data = loads_strict_json_object(p.read_text())
81
- except (ValueError, json.JSONDecodeError):
82
- return None
83
- return data
84
-
85
-
86
- def load_result(results_dir: Path, fixture: str, arm: str) -> dict | None:
87
- p = results_dir / fixture / arm / "result.json"
88
- if not p.is_file():
89
- return None
90
- try:
91
- data = loads_strict_json_object(p.read_text())
92
- except (ValueError, json.JSONDecodeError):
93
- return None
94
- return data
95
-
96
-
97
- def load_state(work_dir_root: Path, run_id: str, fixture: str, arm: str) -> dict | None:
98
- """state.json lives in /tmp/bench-{run_id}-{fixture}-{arm}/.devlyn/runs/<rs-id>/."""
99
- work = work_dir_root / f"bench-{run_id}-{fixture}-{arm}"
100
- runs = work / ".devlyn" / "runs"
101
- if not runs.is_dir():
102
- return None
103
- candidates = sorted(runs.glob("*/pipeline.state.json"))
104
- if not candidates:
105
- return None
106
- try:
107
- data = loads_strict_json_object(candidates[-1].read_text())
108
- except (ValueError, json.JSONDecodeError):
109
- return None
110
- return data
111
-
112
-
113
- def archive_run_dir(work_dir_root: Path, run_id: str, fixture: str, arm: str) -> Path | None:
114
- """The .devlyn/runs/<rs-id>/ where pipeline archived per-run artifacts."""
115
- work = work_dir_root / f"bench-{run_id}-{fixture}-{arm}"
116
- runs = work / ".devlyn" / "runs"
117
- if not runs.is_dir():
118
- return None
119
- cands = sorted(runs.glob("*/pipeline.state.json"))
120
- if not cands:
121
- return None
122
- return cands[-1].parent
123
-
124
-
125
- def changed_files(results_dir: Path, fixture: str, arm: str) -> set[str]:
126
- """Read changed-files.txt for an arm; returns set of file paths."""
127
- p = results_dir / fixture / arm / "changed-files.txt"
128
- if not p.is_file():
129
- return set()
130
- return {ln.strip() for ln in p.read_text().splitlines() if ln.strip()}
131
-
132
-
133
- def pair_findings_distinguishable(work_dir_root: Path, run_id: str, fixture: str, arm: str) -> bool:
134
- """True iff archive has at least one per-engine pair-judge artifact for >=2 engines.
135
-
136
- Gate 8: pair findings must be distinguishable from solo judge findings. The
137
- orchestrator (Claude reading SKILL.md) was observed across smokes 1a + 1c
138
- (fixed-diff) writing per-judge artifacts under several naming conventions:
139
-
140
- * `verify-judge-<engine>.md` (smoke 1a, full pair mode)
141
- * `verify.judge.<engine>.findings.jsonl` (smoke 1c, verify-only pair mode)
142
- * `verify.judge.<engine>.summary.json` (verify-only mode auxiliary)
143
-
144
- Detection treats "two distinct engine identifiers across any of those patterns"
145
- as distinguishable. archive_run.py moves both `verify-judge-*.md` (added in
146
- iter-0033c) and any `*.findings.jsonl` (existing pre-iter-0033c) so both
147
- conventions land in the run dir. Either missing distinct engines = broken.
148
- """
149
- archive = archive_run_dir(work_dir_root, run_id, fixture, arm)
150
- if archive is None:
151
- return False
152
- import re
153
- # Collect engine identifiers across all observed pair-judge naming patterns.
154
- patterns = [
155
- re.compile(r"^verify-judge-(?P<engine>[a-z0-9_]+)\.md$"),
156
- re.compile(r"^verify\.judge\.(?P<engine>[a-z0-9_]+)\.findings\.jsonl$"),
157
- re.compile(r"^verify\.judge\.(?P<engine>[a-z0-9_]+)\.summary\.json$"),
158
- ]
159
- engines = set()
160
- for f in archive.iterdir():
161
- if not f.is_file():
162
- continue
163
- for pat in patterns:
164
- m = pat.match(f.name)
165
- if m:
166
- engines.add(m.group("engine"))
167
- break
168
- return len(engines) >= 2
169
-
170
-
171
- def impl_confounded_for_fixture(results_dir: Path, fixture: str) -> bool:
172
- """True iff symmetric difference of changed-files between solo_claude and
173
- l2_forced is non-empty. Drives Gate 7 attribution + Gate 3 carve-out
174
- decision per Codex R-final-smoke Q5 (option (b): fixed-diff for confounded
175
- fixtures only, NOT a wholesale pivot).
176
-
177
- Smoke 1c (2026-05-02) confirmed F2 confounds (solo touched 1 file, l2_forced
178
- touched 2 — l2_forced added a test file solo skipped) while F3 does not
179
- (both arms touched the same 2 files).
180
- """
181
- solo = changed_files(results_dir, fixture, "solo_claude")
182
- forced = changed_files(results_dir, fixture, "l2_forced")
183
- if not solo or not forced:
184
- return False # arm absent → can't claim confound
185
- return solo.symmetric_difference(forced) != set()
186
-
187
-
188
- def fixture_short(name: str) -> str:
189
- return name.split("-", 1)[0] if "-" in name else name
190
-
191
-
192
- def find_results_dir_fixtures(results_dir: Path) -> list[str]:
193
- return sorted(d.name for d in results_dir.iterdir() if d.is_dir())
194
-
195
-
196
- def get_score(judge: dict, arm: str) -> int | None:
197
- """Score for a given arm, only when the arm is present in `_blind_mapping`.
198
-
199
- `scores_by_arm` is accepted only as a decoded view of the blind A/B/C slots;
200
- a score for an arm absent from the blind mapping is not score evidence.
201
- """
202
- if not judge:
203
- return None
204
- raw_mapping = judge.get("_blind_mapping")
205
- mapping = raw_mapping if isinstance(raw_mapping, dict) else {}
206
- letter = next((k for k, v in mapping.items() if v == arm), None)
207
- if not letter:
208
- return None
209
- raw_scores = judge.get("scores_by_arm")
210
- sba = raw_scores if isinstance(raw_scores, dict) else {}
211
- if is_score(sba.get(arm)):
212
- return sba[arm]
213
- legacy = judge.get(f"{letter.lower()}_score")
214
- return legacy if is_score(legacy) else None
215
-
216
-
217
- def get_disqualifier(judge: dict, arm: str) -> bool:
218
- """DQ flag for a given arm. Prefer `disqualifiers_by_arm` written by judge.sh
219
- line 314-323; fall back to blind A/B/C with case-correct letter."""
220
- if not judge:
221
- return False
222
- raw_mapping = judge.get("_blind_mapping")
223
- mapping = raw_mapping if isinstance(raw_mapping, dict) else {}
224
- letter = next((k for k, v in mapping.items() if v == arm), None)
225
- if not letter:
226
- raw_scores = judge.get("scores_by_arm")
227
- sba = raw_scores if isinstance(raw_scores, dict) else {}
228
- raw_dba = judge.get("disqualifiers_by_arm")
229
- if raw_dba is not None and not isinstance(raw_dba, dict):
230
- return True
231
- dba = raw_dba if isinstance(raw_dba, dict) else {}
232
- return arm in sba or arm in dba
233
- raw_dba = judge.get("disqualifiers_by_arm")
234
- if raw_dba is not None and not isinstance(raw_dba, dict):
235
- return True
236
- dba = raw_dba if isinstance(raw_dba, dict) else {}
237
- if arm in dba:
238
- entry = dba[arm]
239
- return bool_flag(
240
- entry.get("disqualifier") if isinstance(entry, dict) else entry
241
- )
242
- raw_dqs = judge.get("disqualifiers")
243
- if raw_dqs is not None and not isinstance(raw_dqs, dict):
244
- return True
245
- dqs = raw_dqs if isinstance(raw_dqs, dict) else {}
246
- return bool_flag(dqs.get(letter))
247
-
248
-
249
- def gate_2_no_regression(rows: list[dict]) -> dict:
250
- failures = []
251
- for row in rows:
252
- if row["solo_score"] is None or row["l2_gated_score"] is None:
253
- continue
254
- delta = row["l2_gated_score"] - row["solo_score"]
255
- if delta < -3:
256
- failures.append({"fixture": row["fixture"], "delta": delta})
257
- return {
258
- "gate": "2-no-regression",
259
- "status": "PASS" if not failures else "FAIL",
260
- "rule": "every fixture: (l2_gated − solo) ≥ −3",
261
- "failures": failures,
262
- }
263
-
264
-
265
- def gate_3_lift(rows: list[dict], manifest: dict) -> dict:
266
- eligible = set(manifest["fixtures_pair_eligible"])
267
- threshold = manifest["gate3_threshold_count"]
268
- total = manifest["gate3_total"]
269
- counted = []
270
- for row in rows:
271
- fx = fixture_short(row["fixture"])
272
- if fx not in eligible:
273
- continue
274
- if row["solo_score"] is None or row["l2_gated_score"] is None:
275
- continue
276
- delta = row["l2_gated_score"] - row["solo_score"]
277
- counted.append({"fixture": row["fixture"], "delta": delta, "lift_ge5": delta >= 5})
278
- n_lift = sum(1 for c in counted if c["lift_ge5"])
279
- return {
280
- "gate": "3-lift-on-pair-eligible",
281
- "ship_blocker": True,
282
- "status": "PASS" if n_lift >= threshold else "FAIL",
283
- "rule": f"lift ≥ +5 on ≥ {threshold} of {total} pair-eligible fixtures",
284
- "lift_count": n_lift,
285
- "threshold": threshold,
286
- "total": total,
287
- "details": counted,
288
- }
289
-
290
-
291
- def classify_l2_disqualifier(row: dict, mechanical_findings: list[dict]) -> str:
292
- """Bucket why L2 disqualified a previously-clean L1 fixture (Codex R-final-fdfd Q2).
293
-
294
- Buckets:
295
- - `mechanical_failed`: deterministic spec-verify gate ALSO flagged a
296
- disqualifier-class finding → real product defect → Gate 4 FAIL.
297
- - `target_env_reproduced`: pair-JUDGE finding manually reproduced in the
298
- target env (post-suite human adjudication). Gate 4 FAIL.
299
- - `pair_sandbox_only`: pair-JUDGE surfaced a CRITICAL/HIGH finding that
300
- mechanical did NOT trigger. Could be valid-but-environment-conditional
301
- (e.g. EPERM handling on systems where ~/.claude is unreadable); Codex's
302
- smoke 1c-fixed on F2 was textbook of this. Logged as Gate 7 evidence,
303
- NOT Gate 4 FAIL.
304
-
305
- Default classification = `pair_sandbox_only` when mechanical didn't fail.
306
- `target_env_reproduced` requires post-hoc manual override (no auto-reproducer).
307
- """
308
- has_mechanical_dq = any(
309
- (f.get("severity") in ("CRITICAL", "HIGH")
310
- and f.get("source") in ("mechanical", "spec-verify"))
311
- or f.get("disqualifier") is True
312
- for f in mechanical_findings
313
- )
314
- if has_mechanical_dq:
315
- return "mechanical_failed"
316
- return "pair_sandbox_only"
317
-
318
-
319
- def load_mechanical_findings(work_dir_root: Path, run_id: str, fixture: str, arm: str) -> list[dict]:
320
- archive = archive_run_dir(work_dir_root, run_id, fixture, arm)
321
- if archive is None:
322
- return []
323
- p = archive / "verify-mechanical.findings.jsonl"
324
- if not p.is_file():
325
- return []
326
- out = []
327
- for ln in p.read_text().splitlines():
328
- ln = ln.strip()
329
- if not ln:
330
- continue
331
- try:
332
- parsed = json.loads(ln, parse_constant=reject_json_constant)
333
- except json.JSONDecodeError:
334
- continue
335
- if isinstance(parsed, dict):
336
- out.append(parsed)
337
- return out
338
-
339
-
340
- def gate_4_hard_floor(rows: list[dict], work_dir_root: Path, run_id: str) -> dict:
341
- failures = []
342
- sandbox_only = []
343
- for row in rows:
344
- if row["solo_dq"]:
345
- continue
346
- if row["l2_gated_dq"]:
347
- mech = load_mechanical_findings(work_dir_root, run_id, row["fixture"], "l2_gated")
348
- classification = classify_l2_disqualifier(row, mech)
349
- entry = {"fixture": row["fixture"], "kind": "l2_gated_dq_on_clean_l1",
350
- "classification": classification}
351
- if classification in ("mechanical_failed", "target_env_reproduced"):
352
- failures.append(entry)
353
- else:
354
- sandbox_only.append(entry)
355
- if row["l2_gated_timeout"] and not row["solo_timeout"]:
356
- failures.append({"fixture": row["fixture"], "kind": "l2_gated_timeout_only",
357
- "classification": "timeout"})
358
- return {
359
- "gate": "4-hard-floor",
360
- "ship_blocker": True,
361
- "status": "PASS" if not failures else "FAIL",
362
- "rule": ("zero l2_gated dq / timeout on previously-clean l1 fixtures, "
363
- "where dq is classified as mechanical_failed OR target_env_reproduced "
364
- "(pair_sandbox_only logged as Gate 7 evidence per Codex R-final-fdfd Q2)"),
365
- "failures": failures,
366
- "pair_sandbox_only_logged": sandbox_only,
367
- }
368
-
369
-
370
- def gate_5_efficiency(rows: list[dict]) -> dict:
371
- failures = []
372
- details = []
373
- for row in rows:
374
- if row["solo_wall"] is None or row["l2_gated_wall"] is None:
375
- continue
376
- if row["solo_wall"] == 0:
377
- continue
378
- ratio = row["l2_gated_wall"] / row["solo_wall"]
379
- details.append({"fixture": row["fixture"], "ratio": round(ratio, 2)})
380
- if ratio > 2.0:
381
- failures.append({"fixture": row["fixture"], "ratio": round(ratio, 2)})
382
- return {
383
- "gate": "5-efficiency",
384
- "status": "PASS" if not failures else "FAIL",
385
- "rule": "per-fixture l2_gated_wall / l1_wall ≤ 2.0×",
386
- "failures": failures,
387
- "details": details,
388
- }
389
-
390
-
391
- def gate_6_trigger_discipline(rows: list[dict], manifest: dict) -> dict:
392
- eligible = set(manifest["fixtures_pair_eligible"])
393
- failures = []
394
- for row in rows:
395
- fx = fixture_short(row["fixture"])
396
- if fx not in eligible:
397
- continue
398
- if row["l2_forced_score"] is None:
399
- continue
400
- forced_lift = (
401
- row["l2_forced_score"] is not None and row["solo_score"] is not None
402
- and (row["l2_forced_score"] - row["solo_score"] >= 5)
403
- )
404
- forced_rescue = bool(row["solo_dq"] and not row["l2_forced_dq"])
405
- forced_pair_present = bool(row["l2_forced_pair_judge_present"])
406
- gated_pair_present = bool(row["l2_gated_pair_judge_present"])
407
- if (forced_lift or forced_rescue) and forced_pair_present and not gated_pair_present:
408
- failures.append({
409
- "fixture": row["fixture"],
410
- "forced_lift": forced_lift,
411
- "forced_rescue": forced_rescue,
412
- "gated_pair_judge_present": gated_pair_present,
413
- })
414
- return {
415
- "gate": "6-trigger-discipline",
416
- "ship_blocker": True,
417
- "status": "PASS" if not failures else "FAIL",
418
- "rule": "if forced lifts ≥ +5 (or rescues) → gated must also fire pair on that fixture",
419
- "failures": failures,
420
- }
421
-
422
-
423
- def gate_7_attribution(rows: list[dict], manifest: dict) -> dict:
424
- """4-class classification per fixture; data-gathering only.
425
-
426
- no_material_lift — solo and l2 verdicts equivalent within ±2
427
- implementation_confounded — IMPLEMENT diffs differ materially (smoke 1c flagged)
428
- tool_or_trigger_lift — mechanical/coverage finding caused axis change
429
- deliberation_lift — pair_judge surfaces verdict-binding finding absent from solo
430
- """
431
- classes = []
432
- for row in rows:
433
- fx = fixture_short(row["fixture"])
434
- cls = "no_material_lift" # default
435
- if row["impl_confounded"]:
436
- cls = "implementation_confounded"
437
- elif row["solo_score"] is not None and row["l2_gated_score"] is not None:
438
- delta = row["l2_gated_score"] - row["solo_score"]
439
- if abs(delta) <= 2:
440
- cls = "no_material_lift"
441
- elif row["pair_judge_unique_finding"]:
442
- cls = "deliberation_lift"
443
- elif row["mechanical_finding_drove_change"]:
444
- cls = "tool_or_trigger_lift"
445
- else:
446
- cls = "deliberation_lift" if delta >= 5 else "no_material_lift"
447
- classes.append({"fixture": row["fixture"], "class": cls,
448
- "pair_eligible": fx in set(manifest["fixtures_pair_eligible"])})
449
- return {
450
- "gate": "7-attribution",
451
- "data_only": True,
452
- "status": "DATA",
453
- "classes": classes,
454
- }
455
-
456
-
457
- def gate_8_artifact_contract(rows: list[dict]) -> dict:
458
- failures = []
459
- for row in rows:
460
- # If pair fired (forced arm has pair_judge present) but artifact missing
461
- if row["pair_fired"] and not row["pair_findings_distinguishable"]:
462
- failures.append({"fixture": row["fixture"],
463
- "missing": "pair_judge_findings_distinguishable"})
464
- return {
465
- "gate": "8-artifact-contract",
466
- "status": "PASS" if not failures else "FAIL",
467
- "rule": "recognized pair_judge verdict when fired; pair findings distinguishable from solo",
468
- "failures": failures,
469
- }
470
-
471
-
472
- def build_rows(results_dir: Path, work_dir_root: Path, run_id: str) -> list[dict]:
473
- fixtures = find_results_dir_fixtures(results_dir)
474
- rows = []
475
- for fx in fixtures:
476
- judge = load_judge(results_dir, fx)
477
- solo_r = load_result(results_dir, fx, "solo_claude")
478
- gated_r = load_result(results_dir, fx, "l2_gated")
479
- forced_r = load_result(results_dir, fx, "l2_forced")
480
- gated_state = load_state(work_dir_root, run_id, fx, "l2_gated")
481
- forced_state = load_state(work_dir_root, run_id, fx, "l2_forced")
482
-
483
- def pair_judge_present(state: dict | None) -> bool:
484
- if not isinstance(state, dict):
485
- return False
486
- phases = state.get("phases")
487
- if not isinstance(phases, dict):
488
- return False
489
- verify = phases.get("verify")
490
- if not isinstance(verify, dict):
491
- return False
492
- sub = verify.get("sub_verdicts")
493
- if not isinstance(sub, dict):
494
- return False
495
- return is_pair_judge_verdict(sub.get("pair_judge"))
496
-
497
- # Pair findings distinguishability — checked from archive of whichever
498
- # arm fired pair-mode. l2_forced always fires (when present); l2_gated
499
- # only on natural triggers. Use l2_forced as the audit anchor when
500
- # available; fall back to l2_gated.
501
- pair_anchor_arm = "l2_forced" if forced_state else (
502
- "l2_gated" if gated_state else None
503
- )
504
- pair_findings_ok = (
505
- pair_findings_distinguishable(work_dir_root, run_id, fx, pair_anchor_arm)
506
- if pair_anchor_arm else False
507
- )
508
- rows.append({
509
- "fixture": fx,
510
- "solo_score": get_score(judge, "solo_claude"),
511
- "l2_gated_score": get_score(judge, "l2_gated"),
512
- "l2_forced_score": get_score(judge, "l2_forced"),
513
- "solo_dq": get_disqualifier(judge, "solo_claude"),
514
- "l2_gated_dq": get_disqualifier(judge, "l2_gated"),
515
- "l2_forced_dq": get_disqualifier(judge, "l2_forced"),
516
- "solo_wall": strict_elapsed_seconds(solo_r),
517
- "l2_gated_wall": strict_elapsed_seconds(gated_r),
518
- "solo_timeout": timeout_flag(solo_r),
519
- "l2_gated_timeout": timeout_flag(gated_r),
520
- "l2_gated_pair_judge_present": pair_judge_present(gated_state),
521
- "l2_forced_pair_judge_present": pair_judge_present(forced_state),
522
- "pair_fired": pair_judge_present(gated_state) or pair_judge_present(forced_state),
523
- "pair_findings_distinguishable": pair_findings_ok,
524
- "impl_confounded": impl_confounded_for_fixture(results_dir, fx),
525
- # Below remain conservative defaults — populating them needs cross-
526
- # finding diffs (verify.findings.jsonl from each pair-judge file)
527
- # that are out of scope for this iter's compare script. Recorded
528
- # as TODO for follow-up; iter-0033c attribution downgrades fixtures
529
- # with these defaults to no_material_lift unless the score delta
530
- # itself is ≥+5 (then deliberation_lift), so the conservative
531
- # default never inflates the count.
532
- "pair_judge_unique_finding": False,
533
- "mechanical_finding_drove_change": False,
534
- })
535
- return rows
536
-
537
-
538
- def strict_elapsed_seconds(result: dict | None) -> float | int | None:
539
- if not result:
540
- return None
541
- value = result.get("elapsed_seconds")
542
- return value if is_strict_number(value) else None
543
-
544
-
545
- def timeout_flag(result: dict | None) -> bool:
546
- if not result:
547
- return False
548
- return bool_flag(result.get("timed_out"))
549
-
550
-
551
- def validate_manifest(manifest: object) -> tuple[dict | None, str | None]:
552
- if not isinstance(manifest, dict):
553
- return None, "manifest malformed: expected object"
554
- raw_eligible = manifest.get("fixtures_pair_eligible")
555
- if not isinstance(raw_eligible, list) or not all(isinstance(fx, str) for fx in raw_eligible):
556
- return None, "manifest malformed: fixtures_pair_eligible must be a string array"
557
- if not raw_eligible:
558
- return None, "manifest malformed: fixtures_pair_eligible must not be empty"
559
- threshold = manifest.get("gate3_threshold_count")
560
- total = manifest.get("gate3_total")
561
- if not isinstance(threshold, int) or isinstance(threshold, bool) or threshold <= 0:
562
- return None, "manifest malformed: gate3_threshold_count must be a positive integer"
563
- if not isinstance(total, int) or isinstance(total, bool) or total <= 0:
564
- return None, "manifest malformed: gate3_total must be a positive integer"
565
- if total != len(raw_eligible):
566
- return None, "manifest malformed: gate3_total must equal fixtures_pair_eligible length"
567
- if threshold > total:
568
- return None, "manifest malformed: gate3_threshold_count must be <= gate3_total"
569
- rule = manifest.get("selection_rule")
570
- if rule is not None:
571
- if not isinstance(rule, dict):
572
- return None, "manifest malformed: selection_rule must be an object"
573
- rejected = rule.get("rejected_excluded")
574
- reasons = rule.get("rejected_excluded_reasons")
575
- if rejected is not None:
576
- if not isinstance(rejected, list) or not all(isinstance(fx, str) for fx in rejected):
577
- return None, "manifest malformed: selection_rule.rejected_excluded must be a string array"
578
- if reasons is not None:
579
- if (
580
- not isinstance(reasons, dict)
581
- or not all(isinstance(fx, str) and isinstance(reason, str) and reason for fx, reason in reasons.items())
582
- ):
583
- return None, "manifest malformed: selection_rule.rejected_excluded_reasons must map fixture ids to non-empty strings"
584
- if rejected is not None and set(reasons) != set(rejected):
585
- return None, "manifest malformed: selection_rule.rejected_excluded_reasons keys must match rejected_excluded"
586
- return manifest, None
587
-
588
-
589
- def render_markdown(gates: list[dict], rows: list[dict]) -> str:
590
- lines = ["# iter-0033c gate table\n"]
591
- lines.append("| fixture | solo | l2_gated | Δ | l2_forced | l2g pair? | l2f pair? | wall_ratio |")
592
- lines.append("|---|---|---|---|---|---|---|---|")
593
- for r in rows:
594
- delta = (
595
- r["l2_gated_score"] - r["solo_score"]
596
- if r["l2_gated_score"] is not None and r["solo_score"] is not None
597
- else None
598
- )
599
- ratio = (
600
- round(r["l2_gated_wall"] / r["solo_wall"], 2)
601
- if r["l2_gated_wall"] is not None and r["solo_wall"]
602
- else None
603
- )
604
- lines.append(
605
- f"| {r['fixture']} | {r['solo_score']} | {r['l2_gated_score']} | "
606
- f"{('+' if delta and delta > 0 else '') + str(delta) if delta is not None else '-'} | "
607
- f"{r['l2_forced_score']} | "
608
- f"{'✓' if r['l2_gated_pair_judge_present'] else '✗'} | "
609
- f"{'✓' if r['l2_forced_pair_judge_present'] else '✗'} | "
610
- f"{ratio if ratio is not None else '-'} |"
611
- )
612
- lines.append("\n## Gates\n")
613
- for g in gates:
614
- ship = " (SHIP-BLOCKER)" if g.get("ship_blocker") else ""
615
- lines.append(f"- **{g['gate']}{ship}**: {g['status']} — {g.get('rule', '')}")
616
- if g.get("failures"):
617
- for f in g["failures"]:
618
- lines.append(f" - FAIL: {f}")
619
- return "\n".join(lines) + "\n"
620
-
621
-
622
- def main() -> int:
623
- ap = argparse.ArgumentParser()
624
- ap.add_argument("--manifest", required=True)
625
- ap.add_argument("--results-dir", required=True)
626
- ap.add_argument("--work-dir-root", default="/tmp",
627
- help="parent dir of bench-* WORK_DIRs (default: /tmp)")
628
- ap.add_argument("--run-id", required=True,
629
- help="benchmark run id used by run-fixture.sh (matches WORK_DIR prefix)")
630
- ap.add_argument("--out-json", required=True)
631
- ap.add_argument("--out-md", required=True)
632
- args = ap.parse_args()
633
-
634
- try:
635
- raw_manifest = json.loads(
636
- Path(args.manifest).read_text(),
637
- parse_constant=reject_json_constant,
638
- )
639
- except (ValueError, json.JSONDecodeError) as exc:
640
- print(f"error: manifest malformed: invalid JSON: {exc}", file=sys.stderr)
641
- return 2
642
- manifest, manifest_error = validate_manifest(raw_manifest)
643
- if manifest is None:
644
- print(f"error: {manifest_error}", file=sys.stderr)
645
- return 2
646
- rows = build_rows(Path(args.results_dir), Path(args.work_dir_root), args.run_id)
647
-
648
- gates = [
649
- gate_2_no_regression(rows),
650
- gate_3_lift(rows, manifest),
651
- gate_4_hard_floor(rows, Path(args.work_dir_root), args.run_id),
652
- gate_5_efficiency(rows),
653
- gate_6_trigger_discipline(rows, manifest),
654
- gate_7_attribution(rows, manifest),
655
- gate_8_artifact_contract(rows),
656
- ]
657
-
658
- out = {
659
- "iter": "0033c",
660
- "manifest_sha256": manifest["manifest_sha256"],
661
- "manifest_head": manifest.get("head"),
662
- "rows": rows,
663
- "gates": gates,
664
- "ship_blockers_failed": [g["gate"] for g in gates
665
- if g.get("ship_blocker") and g["status"] == "FAIL"],
666
- "quality_gates_failed": [g["gate"] for g in gates
667
- if not g.get("ship_blocker") and g["status"] == "FAIL"],
668
- }
669
- Path(args.out_json).write_text(json.dumps(out, indent=2) + "\n")
670
- Path(args.out_md).write_text(render_markdown(gates, rows))
671
-
672
- print(f"[compare] gates -> {args.out_json}")
673
- print(f"[compare] markdown -> {args.out_md}")
674
- failed = out["ship_blockers_failed"]
675
- if failed:
676
- print(f"[compare] SHIP-BLOCKER FAIL: {failed}")
677
- return 1
678
- print("[compare] all ship-blockers PASS")
679
- return 0
680
-
681
-
682
- if __name__ == "__main__":
683
- sys.exit(main())