devlyn-cli 3.2.1 → 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 (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 +6 -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 +635 -143
  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 +46 -37
  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,1024 +0,0 @@
1
- #!/usr/bin/env bash
2
- # run-fixture.sh — run ONE fixture, ONE arm, end-to-end. Self-contained.
3
- #
4
- # Prepares a fresh work dir, applies setup, invokes the arm via `claude -p`
5
- # subprocess (isolated session), then captures artifacts + runs verification.
6
- #
7
- # Usage:
8
- # run-fixture.sh --fixture <FID> --arm <variant|solo_claude|bare|l2_gated|l2_risk_probes|l2_forced> --run-id <ID>
9
- # run-fixture.sh --fixture <FID> --arm <variant|solo_claude|bare|l2_gated|l2_risk_probes|l2_forced> --run-id <ID> --dry-run
10
- #
11
- # Outputs to benchmark/auto-resolve/results/<run-id>/<fixture>/<arm>/:
12
- # input.md, transcript.txt, diff.patch, changed-files.txt, verify.json,
13
- # timing.json, result.json, setup.log (if setup ran)
14
-
15
- set -euo pipefail
16
-
17
- usage() {
18
- echo "usage: $0 --fixture <FID> --arm <variant|solo_claude|bare|l2_gated|l2_risk_probes|l2_forced> --run-id <ID> [--resolve-skill new] [--engines-config '<json>'] [--dry-run]"
19
- exit 1
20
- }
21
-
22
- require_value() {
23
- local flag="$1"
24
- local value="${2:-}"
25
- if [ -z "$value" ] || [[ "$value" == --* ]]; then
26
- echo "$flag requires a value" >&2
27
- exit 1
28
- fi
29
- }
30
-
31
- kill_worktree_processes() {
32
- local work_dir="$1"
33
- local signal="$2"
34
- local physical_work_dir current_pgid
35
- physical_work_dir="$(cd "$work_dir" 2>/dev/null && pwd -P || printf '%s' "$work_dir")"
36
- current_pgid="$(ps -o pgid= -p "$$" | tr -d ' ')"
37
- ps -axo pid=,pgid=,command= \
38
- | awk -v p1="$work_dir" -v p2="$physical_work_dir" -v self="$$" -v current_pgid="$current_pgid" '
39
- $1 != self && $2 != current_pgid && (index($0, p1) || index($0, p2)) { print $2 }
40
- ' \
41
- | sort -u \
42
- | while IFS= read -r pgid; do
43
- [ -n "$pgid" ] || continue
44
- kill "-$signal" -- "-$pgid" 2>/dev/null || true
45
- done
46
- }
47
-
48
- FIXTURE=""; ARM=""; RUN_ID=""; DRY_RUN=0
49
- RESOLVE_SKILL="new"
50
- ENGINES_CONFIG=""
51
- while [ $# -gt 0 ]; do
52
- case "$1" in
53
- --fixture) require_value "$1" "${2:-}"; FIXTURE="$2"; shift 2;;
54
- --arm) require_value "$1" "${2:-}"; ARM="$2"; shift 2;;
55
- --run-id) require_value "$1" "${2:-}"; RUN_ID="$2"; shift 2;;
56
- --resolve-skill) require_value "$1" "${2:-}"; RESOLVE_SKILL="$2"; shift 2;;
57
- --engines-config) require_value "$1" "${2:-}"; ENGINES_CONFIG="$2"; shift 2;;
58
- --dry-run) DRY_RUN=1; shift;;
59
- *) usage;;
60
- esac
61
- done
62
- [ -n "$FIXTURE" ] && [ -n "$ARM" ] && [ -n "$RUN_ID" ] || usage
63
-
64
- # iter-0038: --engines-config stages a machine-config role pin
65
- # (.devlyn/engines.json) into the arm worktree so engine COMBINATIONS are
66
- # runnable as variant-class arms without new hardcoded arm names. Existing
67
- # arm launch commands are untouched when the flag is absent. Fail fast on
68
- # malformed JSON, and refuse bare (no skills — the config would be dead
69
- # weight that silently mis-labels the arm as configured).
70
- if [ -n "$ENGINES_CONFIG" ]; then
71
- if [ "$ARM" = "bare" ]; then
72
- echo "--engines-config is invalid for the bare arm (no skills consume it)" >&2
73
- exit 1
74
- fi
75
- if ! printf '%s' "$ENGINES_CONFIG" | python3 -c 'import json,sys; json.load(sys.stdin)' 2>/dev/null; then
76
- echo "--engines-config is not valid JSON" >&2
77
- exit 1
78
- fi
79
- fi
80
- # iter-0019/0037: 3 smoke arms — variant (L2: Claude orchestrator + risk-probes pair path),
81
- # solo_claude (L1: Claude orchestrator, codex blocked by shim+wrapper enforcement),
82
- # bare (L0: direct claude -p, no skill, no codex).
83
- # iter-0033c (Codex R0-infra adoption, 2026-05-02): two L2 diagnostic arms for /devlyn:resolve —
84
- # l2_gated (--engine claude, no --pair-verify; pair fires only on natural triggers),
85
- # l2_risk_probes (--engine claude --risk-probes; pair converts visible Verification bullets to executable probes before IMPLEMENT),
86
- # l2_forced (--engine claude --pair-verify; retired because it leaks pair-awareness before IMPLEMENT).
87
- [ "$ARM" = "variant" ] || [ "$ARM" = "solo_claude" ] || [ "$ARM" = "bare" ] \
88
- || [ "$ARM" = "l2_gated" ] || [ "$ARM" = "l2_risk_probes" ] || [ "$ARM" = "l2_forced" ] || \
89
- { echo "arm must be variant|solo_claude|bare|l2_gated|l2_risk_probes|l2_forced"; exit 1; }
90
- # iter-0033c (Codex R0-infra Q2): l2_* arms require NEW skill surface (only NEW
91
- # `/devlyn:resolve` honors --pair-verify; OLD `/devlyn:auto-resolve` would silently
92
- # ignore the flag and produce mis-attributed L2 numbers).
93
- if { [ "$ARM" = "l2_gated" ] || [ "$ARM" = "l2_risk_probes" ] || [ "$ARM" = "l2_forced" ]; } && [ "$RESOLVE_SKILL" != "new" ]; then
94
- echo "l2_* arms require --resolve-skill new (got '$RESOLVE_SKILL')"; exit 1
95
- fi
96
- if [ "$ARM" = "l2_forced" ]; then
97
- echo "l2_forced is retired: it puts --pair-verify in the initial prompt, so IMPLEMENT can become pair-aware before the diff is frozen. Use scripts/run-frozen-verify-pair.sh for leak-free VERIFY-pair measurement." >&2
98
- exit 1
99
- fi
100
- # iter-0034 Phase 4 cutover (2026-05-03): OLD `/devlyn:auto-resolve` was
101
- # deleted. Only `new` (= /devlyn:resolve --spec) is supported. The flag stays
102
- # an accepted no-op so historical runners (run-iter-0033c.sh:137) keep working
103
- # unchanged. `old` is hard-errored — silently downgrading to `new` would
104
- # produce mis-attributed results in any pre-cutover replay attempt.
105
- if [ "$RESOLVE_SKILL" = "old" ]; then
106
- echo "--resolve-skill old is no longer supported: /devlyn:auto-resolve was deleted in the iter-0034 Phase 4 cutover. Use --resolve-skill new (default) or omit the flag." >&2
107
- exit 1
108
- fi
109
- [ "$RESOLVE_SKILL" = "new" ] || \
110
- { echo "--resolve-skill must be 'new' (got '$RESOLVE_SKILL')"; exit 1; }
111
-
112
- BENCH_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
113
- REPO_ROOT="$(cd "$BENCH_ROOT/../.." && pwd)"
114
-
115
- FIX_DIR=""
116
- for candidate in "$BENCH_ROOT/fixtures/$FIXTURE" "$BENCH_ROOT/shadow-fixtures/$FIXTURE"; do
117
- if [ -d "$candidate" ]; then FIX_DIR="$candidate"; break; fi
118
- done
119
- [ -n "$FIX_DIR" ] || { echo "fixture not found in fixtures/ or shadow-fixtures/: $FIXTURE"; exit 1; }
120
-
121
- META="$FIX_DIR/metadata.json"
122
- EXPECTED="$FIX_DIR/expected.json"
123
- SPEC="$FIX_DIR/spec.md"
124
- TASK="$FIX_DIR/task.txt"
125
- SETUP="$FIX_DIR/setup.sh"
126
- for f in "$META" "$EXPECTED" "$SPEC" "$TASK"; do
127
- [ -f "$f" ] || { echo "fixture missing required file: $f (see SCHEMA.md)"; exit 1; }
128
- done
129
-
130
- TIMEOUT=$(python3 - "$META" "$BENCH_ROOT/scripts" <<'PY'
131
- import pathlib
132
- import sys
133
-
134
- sys.path.insert(0, sys.argv[2])
135
- from pair_evidence_contract import loads_strict_json_object
136
-
137
- metadata = loads_strict_json_object(pathlib.Path(sys.argv[1]).read_text())
138
- timeout = metadata.get("timeout_seconds")
139
- if not isinstance(timeout, int) or isinstance(timeout, bool) or timeout <= 0:
140
- raise SystemExit("metadata timeout_seconds must be a positive integer")
141
- print(timeout)
142
- PY
143
- )
144
- if [ "$ARM" = "variant" ] || [ "$ARM" = "l2_risk_probes" ]; then
145
- # This arm adds a bounded Codex probe-derive phase before IMPLEMENT and a
146
- # bounded Codex pair-JUDGE during VERIFY. The full-pipeline gate still
147
- # enforces wall-time efficiency by pair/solo ratio; this budget prevents a
148
- # false timeout before the mandatory second judge can emit its contract line.
149
- TIMEOUT=$((TIMEOUT + 600))
150
- fi
151
-
152
- RESULT_DIR="$BENCH_ROOT/results/$RUN_ID/$FIXTURE/$ARM"
153
- mkdir -p "$RESULT_DIR"
154
-
155
- # Fresh copy of test-repo — order matters. We copy arm-env files (skills,
156
- # CLAUDE.md) BEFORE the baseline commit so they do NOT appear in the diff
157
- # the arm produces. That keeps diff.patch focused on the arm's actual code
158
- # changes, so forbidden-pattern scans and judge rubrics see only real work.
159
- WORK_DIR="/tmp/bench-${RUN_ID}-${FIXTURE}-${ARM}"
160
- rm -rf "$WORK_DIR"
161
- cp -R "$BENCH_ROOT/fixtures/test-repo" "$WORK_DIR"
162
-
163
- # All skill-driven arms (variant / solo_claude / l2_gated / l2_risk_probes /
164
- # l2_forced) get devlyn skills + project CLAUDE.md pre-baseline + codex shim
165
- # + monitored wrapper. Bare gets nothing (no skill, no shim, no env).
166
- #
167
- # iter-0019: solo_claude (L1) shares variant-arm staging because the L1 arm
168
- # runs the same orchestrator on the same skills — only difference is codex
169
- # is blocked. Shim catches PATH resolution; wrapper catches direct-path
170
- # invocations.
171
- # iter-0033c/0037 (Codex R0-infra Q6 + risk probes): pair arms share variant
172
- # staging (codex unblocked, shim+wrapper routing). The smoke `variant` arm now
173
- # follows the current measured risk-probes path rather than an older
174
- # auto-engine implement route.
175
- if [ "$ARM" = "variant" ] || [ "$ARM" = "solo_claude" ] \
176
- || [ "$ARM" = "l2_gated" ] || [ "$ARM" = "l2_risk_probes" ] || [ "$ARM" = "l2_forced" ]; then
177
- mkdir -p "$WORK_DIR/.claude"
178
- if [ -d "$REPO_ROOT/.claude/skills" ]; then
179
- cp -R "$REPO_ROOT/.claude/skills" "$WORK_DIR/.claude/skills"
180
- else
181
- echo "warning: $REPO_ROOT/.claude/skills missing — $ARM may lack project skills" >&2
182
- fi
183
- if [ -f "$REPO_ROOT/CLAUDE.md" ]; then
184
- cp "$REPO_ROOT/CLAUDE.md" "$WORK_DIR/CLAUDE.md"
185
- fi
186
- # iter-0038: stage the engine role pin (validated above) pre-baseline.
187
- # test-repo/.gitignore covers .devlyn, so it never enters scaffold commits
188
- # or arm diffs. A verbatim copy lands in RESULT_DIR as run evidence.
189
- if [ -n "$ENGINES_CONFIG" ]; then
190
- mkdir -p "$WORK_DIR/.devlyn"
191
- printf '%s\n' "$ENGINES_CONFIG" > "$WORK_DIR/.devlyn/engines.json"
192
- printf '%s\n' "$ENGINES_CONFIG" > "$RESULT_DIR/engines-config.json"
193
- fi
194
- # Stage the codex PATH shim. Required for both variant (route to monitored
195
- # wrapper) and solo_claude (CODEX_BLOCKED enforcement at PATH layer).
196
- if command -v codex >/dev/null 2>&1; then
197
- CODEX_REAL_BIN="$(command -v codex)"
198
- SHIM_SRC="$REPO_ROOT/scripts/codex-shim/codex"
199
- WRAPPER_SRC="$REPO_ROOT/config/skills/_shared/codex-monitored.sh"
200
- if [ ! -x "$SHIM_SRC" ] || [ ! -r "$WRAPPER_SRC" ]; then
201
- echo "fatal: iter-0009 shim/wrapper missing at $SHIM_SRC / $WRAPPER_SRC" >&2
202
- exit 1
203
- fi
204
- mkdir -p "$WORK_DIR/.devlyn-bin"
205
- cp "$SHIM_SRC" "$WORK_DIR/.devlyn-bin/codex"
206
- chmod +x "$WORK_DIR/.devlyn-bin/codex"
207
- CODEX_MONITORED_PATH="$WORK_DIR/.claude/skills/_shared/codex-monitored.sh"
208
- [ -r "$CODEX_MONITORED_PATH" ] || {
209
- echo "fatal: codex-monitored.sh not present in staged skills at $CODEX_MONITORED_PATH" >&2
210
- exit 1
211
- }
212
- export CODEX_REAL_BIN CODEX_MONITORED_PATH
213
- SNAPSHOT_PATH=$(grep -m1 '^export PATH=' \
214
- "$HOME/.claude/shell-snapshots/snapshot-zsh-"*.sh 2>/dev/null \
215
- | head -1 | sed 's/^[^=]*=//' | tr -d '"' || true)
216
- [ -n "$SNAPSHOT_PATH" ] || SNAPSHOT_PATH="$PATH"
217
- INJECTED_PATH="$WORK_DIR/.devlyn-bin:$SNAPSHOT_PATH"
218
- # iter-0019: arm-specific env. variant gets the codex routing pair;
219
- # solo_claude gets CODEX_BLOCKED=1 (shim + wrapper both refuse).
220
- # iter-0019.5 (Codex R1, 2026-04-28): when ARM=solo_claude, OMIT
221
- # CODEX_REAL_BIN + CODEX_MONITORED_PATH from the env block. Reason: the
222
- # absolute path to the real codex binary in CODEX_REAL_BIN is itself
223
- # the bypass weapon — the orchestrator could `Bash("$CODEX_REAL_BIN
224
- # exec ...")` and reach real codex, defeating both shim (PATH lookup
225
- # bypassed) and wrapper (codex-monitored.sh not invoked). With the
226
- # var absent: shim's BLOCKED check fires before it needs CODEX_REAL_BIN
227
- # so removing it doesn't break the shim path; if CODEX_BLOCKED is
228
- # somehow unset later, the shim now FAILS CLOSED (no real binary to
229
- # delegate to) rather than silently delegating. The earlier "diagnostic
230
- # visibility" justification was inverted — it handed the orchestrator
231
- # the bypass weapon. Across iter-0019 paid 5-fixture run the bypass
232
- # was OPEN but never exercised; this closes the surface preemptively
233
- # before iter-0020's 9-fixture L0/L1/L2 run.
234
- # iter-0033c/0037 (Codex R0-infra Q5 + risk probes): l2_* arms are codex-UNBLOCKED
235
- # (codex must be reachable for VERIFY pair-JUDGE). They take the variant
236
- # path: ARM_CODEX_BLOCKED=0 → python writer omits CODEX_BLOCKED from env
237
- # entirely (the shim refuses on any non-empty value, so 0 ≠ unset).
238
- if [ "$ARM" = "solo_claude" ]; then
239
- ARM_CODEX_BLOCKED=1
240
- else
241
- ARM_CODEX_BLOCKED=0
242
- fi
243
- python3 - "$WORK_DIR/.claude/settings.json" \
244
- "$INJECTED_PATH" "$CODEX_REAL_BIN" "$CODEX_MONITORED_PATH" "$ARM_CODEX_BLOCKED" "$ARM" <<'PY'
245
- import json, sys
246
- out_path, path_val, real_bin, monitored, codex_blocked, arm = sys.argv[1:7]
247
- env = {
248
- "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
249
- "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
250
- "DISABLE_AUTOUPDATER": "1",
251
- "PATH": path_val,
252
- }
253
- if codex_blocked == "1":
254
- # iter-0019.5: solo_claude (L1 arm) — codex blocked at binary layer.
255
- # Do NOT export CODEX_REAL_BIN / CODEX_MONITORED_PATH to the
256
- # orchestrator subshell; those vars become bypass weapons under any
257
- # CODEX_BLOCKED enforcement gap.
258
- env["CODEX_BLOCKED"] = "1"
259
- else:
260
- # variant / pair arms — codex routes through wrapper for risk-probe
261
- # derivation and VERIFY pair-JUDGE; both vars are required by the
262
- # shim/wrapper handshake.
263
- env["CODEX_REAL_BIN"] = real_bin
264
- env["CODEX_MONITORED_PATH"] = monitored
265
- if arm in ("variant", "l2_risk_probes"):
266
- # Risk-probe derivation is a bounded contract-conversion step. A long
267
- # Codex run is a harness failure, not useful extra quality signal.
268
- env["CODEX_MONITORED_TIMEOUT_SEC"] = "300"
269
- data = {"env": env}
270
- with open(out_path, "w") as f:
271
- json.dump(data, f, indent=2)
272
- f.write("\n")
273
- PY
274
- else
275
- echo "warning: codex not on PATH — $ARM cannot exercise iter-0009 wrapper / iter-0019 BLOCKED enforcement" >&2
276
- CODEX_REAL_BIN=""
277
- CODEX_MONITORED_PATH=""
278
- fi
279
- fi
280
-
281
- (cd "$WORK_DIR" \
282
- && git init -q \
283
- && git add -A \
284
- && git -c user.email=b@b -c user.name=b commit -q -m baseline) \
285
- || { echo "baseline git init failed"; exit 1; }
286
-
287
- # Native security-review Skill expects `refs/remotes/origin/HEAD` to identify
288
- # the diff surface. Fresh `git init` has no remote, which made a prior F8 run
289
- # spend ~56 minutes inside CRITIC recovering this manually. Configure a
290
- # synthetic origin pointing at the work dir itself (no network I/O) and
291
- # wire origin/HEAD → origin/<current-branch> so security-review resolves
292
- # immediately.
293
- (
294
- cd "$WORK_DIR"
295
- git remote add origin "$WORK_DIR" 2>/dev/null || true
296
- BRANCH=$(git branch --show-current 2>/dev/null || echo master)
297
- git update-ref "refs/remotes/origin/$BRANCH" HEAD 2>/dev/null || true
298
- git symbolic-ref refs/remotes/origin/HEAD "refs/remotes/origin/$BRANCH" 2>/dev/null || true
299
- ) >/dev/null 2>&1 || true
300
-
301
- # Fixture-specific setup (applied post-baseline so the diff shows fixture
302
- # framing as part of the arm's environment, not its work product). Commit
303
- # failures here break arm-only diff isolation, so fail loudly.
304
- if [ -f "$SETUP" ] && [ -s "$SETUP" ]; then
305
- chmod +x "$SETUP"
306
- if ! (cd "$WORK_DIR" && "$SETUP") > "$RESULT_DIR/setup.log" 2>&1; then
307
- echo "setup.sh failed; see $RESULT_DIR/setup.log"
308
- exit 1
309
- fi
310
- if ! (cd "$WORK_DIR" \
311
- && git add -A \
312
- && git -c user.email=b@b -c user.name=b commit -q --allow-empty -m "fixture-setup"); then
313
- echo "fixture-setup commit failed — arm diff isolation broken"
314
- exit 1
315
- fi
316
- fi
317
-
318
- # iter-0019.6: stage normalized .devlyn/spec-verify.json for BUILD_GATE.
319
- # Only commands safe to reveal before IMPLEMENT may be staged here. Commands
320
- # that reference BENCH_FIXTURE_DIR are hidden post-run oracles; staging their
321
- # path leaks verifier names into the arm and lets agents search for answer-key
322
- # files. Those commands still run in the post-run verifier below.
323
- if [ "$ARM" = "variant" ] || [ "$ARM" = "solo_claude" ] \
324
- || [ "$ARM" = "l2_gated" ] || [ "$ARM" = "l2_risk_probes" ] || [ "$ARM" = "l2_forced" ]; then
325
- python3 - "$EXPECTED" "$WORK_DIR/.devlyn/spec-verify.json" "$BENCH_ROOT/scripts" <<'PY'
326
- import json, os, pathlib, sys
327
- sys.path.insert(0, sys.argv[3])
328
- from pair_evidence_contract import loads_strict_json_object
329
-
330
- expected = loads_strict_json_object(pathlib.Path(sys.argv[1]).read_text())
331
- out_path = sys.argv[2]
332
- visible_commands = [
333
- cmd for cmd in expected.get("verification_commands", [])
334
- if "BENCH_FIXTURE_DIR" not in str(cmd.get("cmd", ""))
335
- ]
336
- normalized = {"verification_commands": visible_commands}
337
- os.makedirs(os.path.dirname(out_path), exist_ok=True)
338
- if not visible_commands:
339
- raise SystemExit(0)
340
- with open(out_path, "w") as f:
341
- json.dump(normalized, f, indent=2)
342
- f.write("\n")
343
- PY
344
- fi
345
-
346
- # Build arm-specific prompt + place arm-specific environment files. Anything
347
- # that's "benchmark scaffolding" (spec path placement, prompt wrapper) is
348
- # committed to the work repo as a separate pre-model commit so the model's
349
- # diff shows only its own work.
350
- #
351
- # Per-arm prompt selection is:
352
- # 1. Fixture-id-aware for F9 (end-to-end novice fixture, no pre-placed spec).
353
- # 2. Spec-mode `/devlyn:resolve --spec <path>` for the rest (post iter-0034
354
- # Phase 4 cutover the OLD `/devlyn:auto-resolve` route was deleted).
355
- PROMPT_FILE="$RESULT_DIR/input.md"
356
- # Variant uses the current measured risk-probes pair path; solo_claude uses
357
- # --engine claude explicitly so the orchestrator routes every implementation
358
- # phase to Claude and never tries to invoke codex. The CODEX_BLOCKED shim
359
- # enforces this at the binary layer if the orchestrator misroutes. Both arms
360
- # pass the engine flag explicitly so they survive future runtime-default
361
- # changes (post iter-0020 close-out: default flipped to claude).
362
- if [ "$ARM" = "variant" ] || [ "$ARM" = "solo_claude" ] \
363
- || [ "$ARM" = "l2_gated" ] || [ "$ARM" = "l2_risk_probes" ] || [ "$ARM" = "l2_forced" ]; then
364
- case "$ARM" in
365
- solo_claude)
366
- # L1 = pure solo by construction. --no-risk-probes --no-pair so a high-risk
367
- # spec measures Claude-only solo deterministically instead of auto-escalating
368
- # toward the (blocked) OTHER engine. Without these, a security/high-risk
369
- # fixture auto-triggers the cross-engine path and the arm mis-measures L1.
370
- ENGINE_CLAUSE="--engine claude --no-risk-probes --no-pair"
371
- ENGINE_PROMPT_HINT="Run with \`--engine claude --no-risk-probes --no-pair\` for every phase — pure solo L1. Codex must not be invoked — the harness has blocked it at the binary layer for this run."
372
- ;;
373
- variant)
374
- ENGINE_CLAUSE="--engine claude --risk-probes"
375
- ENGINE_PROMPT_HINT="Run with \`--engine claude --risk-probes\` so the smoke L2 arm uses the current measured pair path: Claude implements, Codex derives bounded visible-verification probes and can act as VERIFY pair-JUDGE."
376
- ;;
377
- l2_gated)
378
- # NEW L2 with natural pair-mode triggers. Claude does IMPLEMENT;
379
- # pair-JUDGE in VERIFY fires per /devlyn:resolve PHASE 5 policy
380
- # (high complexity, coverage_failed, or warning-level mechanical
381
- # findings; never after HIGH/CRITICAL mechanical blockers). Codex
382
- # remains available as the OTHER-engine pair-JUDGE candidate.
383
- ENGINE_CLAUSE="--engine claude"
384
- ENGINE_PROMPT_HINT="Run with \`--engine claude\` and let the orchestrator's pair-mode (VERIFY) trigger naturally per its policy. Codex is available as the OTHER-engine pair-JUDGE — the harness has not blocked it. Do NOT pass \`--pair-verify\`; this arm measures gated triggering."
385
- ;;
386
- l2_risk_probes)
387
- # NEW L2 probe-derive arm. Claude plans/implements; Codex is used before
388
- # IMPLEMENT only to derive bounded executable probes from visible
389
- # Verification bullets. BUILD_GATE and VERIFY execute those probes
390
- # mechanically via spec-verify-check.py.
391
- ENGINE_CLAUSE="--engine claude --risk-probes"
392
- ENGINE_PROMPT_HINT="Run with \`--engine claude --risk-probes\`. Codex is available as the OTHER-engine probe derivation and pair-JUDGE engine. The probe phase may only derive executable checks from visible \`## Verification\` text; it must not read hidden fixture/verifier paths."
393
- ;;
394
- l2_forced)
395
- # iter-0033c: NEW L2 forced — pair-JUDGE always fires. Diagnostic arm
396
- # for Gate 6 fixture-level cross-check + Gate 7 attribution causality.
397
- ENGINE_CLAUSE="--engine claude --pair-verify"
398
- ENGINE_PROMPT_HINT="Run with \`--engine claude --pair-verify\` so VERIFY pair-mode fires unconditionally. Codex is the OTHER-engine pair-JUDGE."
399
- ;;
400
- esac
401
- if [ "$FIXTURE" = "F9-e2e-ideate-to-resolve" ]; then
402
- # F9 NEW chain (iter-0033a): /devlyn:ideate --quick → /devlyn:resolve
403
- # --spec <emitted-path>. No pre-placed spec; the variant arm generates it
404
- # via ideate. No preflight (folded into resolve's VERIFY phase).
405
- #
406
- # --quick is mandatory in autonomous (claude -p) mode: default ideate
407
- # invokes interactive Q&A which has no human to answer in a benchmark
408
- # subprocess — the agent asks questions and stops. --quick uses
409
- # single-turn assume-and-confirm: AI synthesizes the spec from the goal
410
- # plus an explicit assumptions block, so the chain proceeds end-to-end
411
- # without user input. Smoke 3 (iter-0033a, 2026-04-30) caught this:
412
- # default-mode F9 produced empty diffs after 54s of Q&A waiting.
413
- cat > "$PROMPT_FILE" <<EOF
414
- You are a first-time devlyn-cli user. You have a vague idea and want the 2-skill harness to take it from unstructured ask to shipped, verified feature. Run the chain:
415
-
416
- 1. Invoke \`/devlyn:ideate --quick ${ENGINE_CLAUSE}\` to turn the idea into a verifiable spec. \`--quick\` is mandatory: this is an autonomous run with no human to answer interactive questions, so ideate must synthesize the spec single-turn from the goal text and emit assumptions explicitly. The skill announces \`spec ready — /devlyn:resolve --spec <emitted-path>\` when done. The emitted spec lives at \`docs/specs/<id>-<slug>/spec.md\` with a sibling \`spec.expected.json\`.
417
- 2. Take the emitted spec path verbatim from the announce line and invoke \`/devlyn:resolve --spec <that-path> ${ENGINE_CLAUSE}\` to run PLAN → IMPLEMENT → BUILD_GATE → CLEANUP → VERIFY (VERIFY is the fresh-subagent final phase — there is no separate preflight skill in the 2-skill design).
418
-
419
- ${ENGINE_PROMPT_HINT}
420
-
421
- Follow the skills to completion. Do not short-circuit. Do not invoke \`/devlyn:auto-resolve\` or \`/devlyn:preflight\` — they are not part of the 2-skill chain. Do not stop after ideate; the chain only counts as complete after \`/devlyn:resolve\` returns a terminal verdict.
422
-
423
- After the whole chain, briefly report: (a) the spec path ideate produced, (b) the resolve terminal verdict, (c) whether VERIFY surfaced any findings.
424
-
425
- RAW IDEA:
426
- $(cat "$TASK")
427
- EOF
428
- else
429
- # Spec-mode /devlyn:resolve: spec pre-placed at the canonical roadmap path
430
- # the harness has used since iter-0019. Pre-Phase-4 this branch shared
431
- # staging with the OLD /devlyn:auto-resolve route; iter-0034 deleted the
432
- # OLD branch and this is now the only non-F9 path.
433
- mkdir -p "$WORK_DIR/docs/roadmap/phase-1"
434
- cp "$SPEC" "$WORK_DIR/docs/roadmap/phase-1/$FIXTURE.md"
435
- # iter-0049: fixtures keep spec.md prose-only (expected.json is the real
436
- # carrier, validated separately by lint-fixtures.sh) but spec-verify-check.py
437
- # now locates the visible ## Verification prose via a <!-- devlyn:verification -->
438
- # sentinel rather than the English header text. Inject it into the WORK_DIR
439
- # copy only, so risk-probe derived_from substring matching still finds the
440
- # section during a run; the source fixture repo stays untouched.
441
- if ! grep -q '<!-- devlyn:verification -->' "$WORK_DIR/docs/roadmap/phase-1/$FIXTURE.md"; then
442
- sed -i.bak 's/^## Verification\b/<!-- devlyn:verification -->\n## Verification/' \
443
- "$WORK_DIR/docs/roadmap/phase-1/$FIXTURE.md"
444
- rm -f "$WORK_DIR/docs/roadmap/phase-1/$FIXTURE.md.bak"
445
- fi
446
- cat > "$PROMPT_FILE" <<EOF
447
- Use the \`/devlyn:resolve --spec docs/roadmap/phase-1/$FIXTURE.md ${ENGINE_CLAUSE}\` skill to implement the spec. ${ENGINE_PROMPT_HINT}
448
-
449
- The 2-skill design folds verification into resolve's VERIFY phase — there is no separate \`/devlyn:preflight\`, \`/devlyn:auto-resolve\`, or other 3-skill orchestrator at HEAD.
450
-
451
- After the pipeline finishes, report the terminal verdict and list of files changed so the benchmark runner can capture state.
452
- EOF
453
- fi
454
- else
455
- # Bare — same prompt for F9 as any other fixture: task.txt with anti-skill rules.
456
- cat > "$PROMPT_FILE" <<EOF
457
- You are acting as a smart engineer implementing the following request directly. No skill pipeline.
458
-
459
- HARD RULES:
460
- - Do NOT invoke any \`/devlyn:*\` skill (no auto-resolve, evaluate, review, clean, update-docs, team-*, etc.).
461
- - Do NOT invoke native \`simplify\` or \`security-review\` skills.
462
- - Use only direct tools: Read, Write, Edit, Grep, Glob, Bash.
463
- - Write code to satisfy the request. Run the verification commands the user implies. Fix failures until they pass.
464
-
465
- REQUEST:
466
- $(cat "$TASK")
467
- EOF
468
- fi
469
-
470
- # Commit scaffolding so the upcoming arm-only diff excludes it. A failure
471
- # here means arm work would appear mixed with scaffolding in the diff — fail
472
- # loudly rather than silently producing corrupted data.
473
- if ! (cd "$WORK_DIR" \
474
- && git add -A \
475
- && git -c user.email=b@b -c user.name=b commit -q --allow-empty -m "bench-scaffold"); then
476
- echo "bench-scaffold commit failed — arm diff isolation broken"
477
- exit 1
478
- fi
479
- # Capture the scaffold commit SHA so the arm-only diff can be computed even
480
- # when the arm makes its own commits internally (e.g. variant's auto-resolve
481
- # pipeline commits after each phase). Diffing against HEAD would miss those.
482
- SCAFFOLD_SHA=$(cd "$WORK_DIR" && git rev-parse HEAD)
483
-
484
- # Timing start
485
- T_START=$(date +%s)
486
- cat > "$RESULT_DIR/timing.json" <<EOF
487
- {
488
- "run_id": "$RUN_ID",
489
- "fixture": "$FIXTURE",
490
- "arm": "$ARM",
491
- "work_dir": "$WORK_DIR",
492
- "start_epoch": $T_START
493
- }
494
- EOF
495
-
496
- # --- Invocation -------------------------------------------------------------
497
- # Exit code is captured so infrastructure failures don't silently look like
498
- # a weak diff. See invoke_exit in result.json.
499
- INVOKE_EXIT=0
500
- # iter-0012: WATCHDOG_FIRED is the truth source for `timed_out` in result.json.
501
- # Set to 1 only when the watchdog flag file existed at post-wait check
502
- # (lines 332-336). Initialized here so the `set -u` `export` below at the
503
- # Python aggregator works in both branches (dry-run never sets it).
504
- WATCHDOG_FIRED=0
505
- if [ $DRY_RUN -eq 1 ]; then
506
- echo "[run-fixture] DRY RUN — prepared $WORK_DIR, skipping model invocation" \
507
- > "$RESULT_DIR/transcript.txt"
508
- else
509
- command -v claude >/dev/null 2>&1 || {
510
- echo "claude CLI not on PATH — cannot invoke arm"; exit 1;
511
- }
512
- # Arm uses real HOME so Claude auth (macOS Keychain + ~/.claude session
513
- # state) works. Fixtures that need HOME isolation override it inline in
514
- # their verification commands (e.g. F2 uses `HOME=/nonexistent` per command).
515
- # Variant-arm skills are resolved from $WORK_DIR/.claude/skills (project
516
- # scope), so bare-arm runs never see them regardless of HOME.
517
- #
518
- # Portable wall-clock watchdog. macOS lacks GNU `timeout` by default; the
519
- # earlier fallback ran arms unbounded, which produced a multi-hour F7 hang
520
- # when the inner `codex exec` raced against a lingering codex-mcp-server.
521
- # We background the arm in its own process group (`set -m` + `exec`) so the
522
- # watchdog can `kill -- -PGID` and reap codex/codex-mcp-server descendants
523
- # together with the parent. A flag file disambiguates timeout from natural
524
- # exit; on timeout we set INVOKE_EXIT=124 (GNU timeout convention) so the
525
- # downstream `invoke_failure` logic routes the run into BLOCKED. iter-0012:
526
- # the same flag also flips WATCHDOG_FIRED=1, which is exported and consumed
527
- # by the Python aggregator below to derive result.json.timed_out — so a
528
- # natural exit at or past the budget is no longer mislabeled as timeout.
529
- #
530
- # MCP/config isolation (iter 0004). The harness's `claude -p` subprocess
531
- # must not load the operator's user-level MCP/plugins/settings (pencil,
532
- # codex-cli, telegram, vercel, ...). Project policy is "MCP/plugins are not in
533
- # the loop"; loading user config inside the arm is uncontrolled environment
534
- # leaking into the experiment. `--setting-sources project,local` keeps user
535
- # plugin enablement out of the run but Claude Code still reads the installed
536
- # plugin registry for autoupdate. Official Claude Code settings document
537
- # `DISABLE_AUTOUPDATER=1` / `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` as the
538
- # supported way to disable that background traffic, while preserving OAuth
539
- # auth from the real HOME. `--strict-mcp-config` + an empty `mcpServers` object
540
- # forces a hermetic MCP set. Skills still resolve via the project
541
- # `.claude/skills` staged into the worktree.
542
- # `--debug-file` records per-arm init/runtime so the next hang has a
543
- # location, not a guess.
544
- TIMEOUT_FLAG="$RESULT_DIR/.timed_out"
545
- rm -f "$TIMEOUT_FLAG"
546
-
547
- set +e
548
- set -m
549
- (
550
- cd "$WORK_DIR"
551
- # iter-0009 + iter-0019: prepend codex shim PATH for any arm that staged
552
- # one. variant routes through codex-monitored.sh; solo_claude refuses on
553
- # CODEX_BLOCKED=1; bare has no shim.
554
- # iter-0033c/0037 (Codex R0-infra Q6 + risk probes): l2_* arms ALSO need the shim
555
- # PATH — they route Claude IMPLEMENT but Codex pair-JUDGE in VERIFY hits
556
- # `codex exec` through the wrapper for starvation safety.
557
- if { [ "$ARM" = "variant" ] || [ "$ARM" = "solo_claude" ] \
558
- || [ "$ARM" = "l2_gated" ] || [ "$ARM" = "l2_risk_probes" ] || [ "$ARM" = "l2_forced" ]; } \
559
- && [ -x "$WORK_DIR/.devlyn-bin/codex" ]; then
560
- export PATH="$WORK_DIR/.devlyn-bin:$PATH"
561
- [ "$ARM" = "solo_claude" ] && export CODEX_BLOCKED=1
562
- fi
563
- # iter-0019.6: BUILD_GATE's spec-verify-check.py uses BENCH_WORKDIR for
564
- # commands that escape the work-dir (e.g. F9's outside-repo check via
565
- # `cd /tmp && node $BENCH_WORKDIR/bin/cli.js gitstats`). Mirror exactly
566
- # what the post-run verifier (run-fixture.sh:431-434) sets so the gate
567
- # sees the same environment shape.
568
- export BENCH_WORKDIR="$WORK_DIR"
569
- # Python helper scripts run inside the benchmark worktree. Do not let them
570
- # rewrite tracked __pycache__ artifacts and pollute the arm-only diff.
571
- export PYTHONDONTWRITEBYTECODE=1
572
- # Official Claude Code setting: disable background plugin/autoupdate traffic
573
- # before process startup. Project settings env is not early enough for all
574
- # startup paths.
575
- export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
576
- export DISABLE_AUTOUPDATER=1
577
- exec claude \
578
- -p "$(cat "$PROMPT_FILE")" \
579
- --dangerously-skip-permissions \
580
- --effort xhigh \
581
- --setting-sources project,local \
582
- --strict-mcp-config \
583
- --mcp-config '{"mcpServers":{}}' \
584
- --debug-file "$RESULT_DIR/claude-debug.log"
585
- ) > "$RESULT_DIR/transcript.txt" 2>&1 &
586
- CHILD_PID=$!
587
- set +m
588
-
589
- (
590
- deadline=$((T_START + TIMEOUT))
591
- while kill -0 "$CHILD_PID" 2>/dev/null; do
592
- now=$(date +%s)
593
- if [ "$now" -ge "$deadline" ]; then
594
- : > "$TIMEOUT_FLAG"
595
- kill -TERM -- "-$CHILD_PID" 2>/dev/null
596
- kill_worktree_processes "$WORK_DIR" TERM
597
- sleep 5
598
- kill -KILL -- "-$CHILD_PID" 2>/dev/null
599
- kill_worktree_processes "$WORK_DIR" KILL
600
- exit 0
601
- fi
602
- remaining=$((deadline - now))
603
- [ "$remaining" -gt 30 ] && sleep 30 || sleep "$remaining"
604
- done
605
- ) &
606
- WATCHDOG_PID=$!
607
-
608
- wait "$CHILD_PID"
609
- INVOKE_EXIT=$?
610
-
611
- kill -TERM "$WATCHDOG_PID" 2>/dev/null || true
612
- wait "$WATCHDOG_PID" 2>/dev/null || true
613
-
614
- if [ -f "$TIMEOUT_FLAG" ]; then
615
- INVOKE_EXIT=124
616
- WATCHDOG_FIRED=1
617
- rm -f "$TIMEOUT_FLAG"
618
- kill_worktree_processes "$WORK_DIR" TERM
619
- sleep 1
620
- kill_worktree_processes "$WORK_DIR" KILL
621
- echo "[run-fixture] arm timed out after ${TIMEOUT}s — INVOKE_EXIT=124" >&2
622
- else
623
- # A clean `claude -p` exit can still leave OTHER-engine pair-JUDGE
624
- # descendants alive; reap any process group rooted in this arm worktree.
625
- kill_worktree_processes "$WORK_DIR" TERM
626
- sleep 1
627
- kill_worktree_processes "$WORK_DIR" KILL
628
- fi
629
- set -e
630
- fi
631
-
632
- T_END=$(date +%s)
633
- ELAPSED=$((T_END - T_START))
634
-
635
- # Restore tracked Python bytecode to the scaffold commit and remove only
636
- # untracked bytecode. Helper invocations must not count as model work, but
637
- # deleting tracked scaffold files would also pollute changed-files.txt.
638
- (cd "$WORK_DIR" \
639
- && git restore --source "$SCAFFOLD_SHA" -- .claude/skills/_shared/__pycache__ 2>/dev/null || true)
640
- cleanup_roots=()
641
- [ -d "$WORK_DIR/.claude" ] && cleanup_roots+=("$WORK_DIR/.claude")
642
- [ -d "$WORK_DIR/.devlyn" ] && cleanup_roots+=("$WORK_DIR/.devlyn")
643
- if [ ${#cleanup_roots[@]} -gt 0 ]; then
644
- find "${cleanup_roots[@]}" -type f \( -name '*.pyc' -o -name '*.pyo' \) -print0 \
645
- | while IFS= read -r -d '' py_file; do
646
- rel="${py_file#$WORK_DIR/}"
647
- if ! (cd "$WORK_DIR" && git ls-files --error-unmatch "$rel" >/dev/null 2>&1); then
648
- rm -f "$py_file"
649
- fi
650
- done
651
- find "${cleanup_roots[@]}" -type d -name __pycache__ -empty -delete || true
652
- fi
653
-
654
- # Capture the ARM-ONLY diff against the scaffold commit. Variant's
655
- # auto-resolve pipeline commits internally after each phase, so diffing
656
- # against HEAD would miss committed work. Diffing against SCAFFOLD_SHA after
657
- # `git add -A` picks up both scaffold..HEAD committed deltas AND any
658
- # staged-but-not-yet-committed leftovers (unstaged or untracked).
659
- (cd "$WORK_DIR" \
660
- && git add -A 2>/dev/null \
661
- && git diff "$SCAFFOLD_SHA") > "$RESULT_DIR/diff.patch" 2>&1 || true
662
- (cd "$WORK_DIR" \
663
- && git diff "$SCAFFOLD_SHA" --name-only) > "$RESULT_DIR/changed-files.txt" 2>&1 || true
664
-
665
- # Deterministic oracles. Hard/flag findings are merged into verify.json below.
666
- python3 "$BENCH_ROOT/scripts/oracle-test-fidelity.py" \
667
- --work "$WORK_DIR" --scaffold "$SCAFFOLD_SHA" \
668
- > "$RESULT_DIR/oracle-test-fidelity.json" 2>/dev/null || \
669
- echo '{"oracle":"test-fidelity","findings":[],"error":"oracle invocation failed"}' \
670
- > "$RESULT_DIR/oracle-test-fidelity.json"
671
-
672
- python3 "$BENCH_ROOT/scripts/oracle-scope-tier-a.py" \
673
- --work "$WORK_DIR" --scaffold "$SCAFFOLD_SHA" --expected "$EXPECTED" \
674
- > "$RESULT_DIR/oracle-scope-tier-a.json" 2>/dev/null || \
675
- echo '{"oracle":"scope-tier-a","findings":[],"error":"oracle invocation failed"}' \
676
- > "$RESULT_DIR/oracle-scope-tier-a.json"
677
-
678
- python3 "$BENCH_ROOT/scripts/oracle-scope-tier-b.py" \
679
- --work "$WORK_DIR" --scaffold "$SCAFFOLD_SHA" --expected "$EXPECTED" \
680
- > "$RESULT_DIR/oracle-scope-tier-b.json" 2>/dev/null || \
681
- echo '{"oracle":"scope-tier-b","findings":[],"error":"oracle invocation failed"}' \
682
- > "$RESULT_DIR/oracle-scope-tier-b.json"
683
-
684
- if { [ "$ARM" = "variant" ] || [ "$ARM" = "solo_claude" ] \
685
- || [ "$ARM" = "l2_gated" ] || [ "$ARM" = "l2_risk_probes" ]; } \
686
- && [ -f "$WORK_DIR/.devlyn/pipeline.state.json" ] \
687
- && [ -f "$WORK_DIR/.claude/skills/_shared/verify-merge-findings.py" ]; then
688
- if [ -f "$WORK_DIR/.devlyn/codex-judge.stdout" ] \
689
- && [ -f "$WORK_DIR/.claude/skills/_shared/collect-codex-findings.py" ]; then
690
- if ! python3 "$WORK_DIR/.claude/skills/_shared/collect-codex-findings.py" \
691
- --devlyn-dir "$WORK_DIR/.devlyn" \
692
- > "$RESULT_DIR/collect-codex-findings.log" 2>&1; then
693
- echo "[run-fixture] Codex pair findings collection failed; see $RESULT_DIR/collect-codex-findings.log" >&2
694
- fi
695
- fi
696
- if ! python3 "$WORK_DIR/.claude/skills/_shared/verify-merge-findings.py" \
697
- --devlyn-dir "$WORK_DIR/.devlyn" --write-state \
698
- > "$RESULT_DIR/verify-merge-normalize.log" 2>&1; then
699
- echo "[run-fixture] verify merge normalization failed; see $RESULT_DIR/verify-merge-normalize.log" >&2
700
- fi
701
- fi
702
-
703
- if { [ "$ARM" = "variant" ] || [ "$ARM" = "solo_claude" ] \
704
- || [ "$ARM" = "l2_gated" ] || [ "$ARM" = "l2_risk_probes" ]; } && [ -d "$WORK_DIR/.devlyn" ]; then
705
- run_dir=$(find "$WORK_DIR/.devlyn/runs" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort | tail -1 || true)
706
- if [ -n "$run_dir" ]; then
707
- rm -rf "$RESULT_DIR/run-archive"
708
- cp -R "$run_dir" "$RESULT_DIR/run-archive"
709
- [ -f "$RESULT_DIR/run-archive/pipeline.state.json" ] \
710
- || [ ! -f "$WORK_DIR/.devlyn/pipeline.state.json" ] \
711
- || cp "$WORK_DIR/.devlyn/pipeline.state.json" "$RESULT_DIR/run-archive/pipeline.state.json"
712
- else
713
- rm -rf "$RESULT_DIR/run-archive"
714
- mkdir -p "$RESULT_DIR/run-archive"
715
- find "$WORK_DIR/.devlyn" -maxdepth 1 -type f -exec cp {} "$RESULT_DIR/run-archive/" \;
716
- fi
717
- fi
718
-
719
- # Run verification commands + forbidden pattern scan + deps check. Uses
720
- # the operator's real HOME (same as the arm saw). Fixtures that need HOME
721
- # isolation override it inline per verification command.
722
- python3 - "$EXPECTED" "$RESULT_DIR" "$WORK_DIR" "$BENCH_ROOT/scripts" <<'PY'
723
- import json, os, pathlib, re, subprocess, sys
724
-
725
- sys.path.insert(0, sys.argv[4])
726
- from pair_evidence_contract import loads_strict_json_object
727
-
728
- expected = loads_strict_json_object(pathlib.Path(sys.argv[1]).read_text())
729
- result_dir = sys.argv[2]
730
- work = sys.argv[3]
731
-
732
- verify_env = os.environ.copy()
733
- # Expose the work-dir path so fixtures whose verification needs to reference
734
- # the work root can do so portably (e.g. F9's out-of-repo check).
735
- verify_env["BENCH_WORKDIR"] = work
736
- # Hidden benchmark verifiers live in the fixture directory, outside the arm's
737
- # work tree. This keeps oracle code from becoming implementation context.
738
- verify_env["BENCH_FIXTURE_DIR"] = os.path.dirname(os.path.abspath(sys.argv[1]))
739
-
740
- verify = {"commands": [], "forbidden_pattern_hits": [], "deps_added": 0,
741
- "max_deps_added": expected.get("max_deps_added", 0),
742
- "missing_required_files": [], "forbidden_files_present": [],
743
- "oracle_findings": [], "oracle_disqualifier": False}
744
-
745
- for vc in expected.get("verification_commands", []):
746
- try:
747
- timeout_sec = vc.get("timeout_sec", 60)
748
- if (
749
- isinstance(timeout_sec, bool)
750
- or not isinstance(timeout_sec, int)
751
- or not 1 <= timeout_sec <= 600
752
- ):
753
- raise ValueError(
754
- "timeout_sec must be an integer from 1 to 600 (bool is not allowed)"
755
- )
756
- proc = subprocess.run(vc["cmd"], cwd=work, shell=True, env=verify_env,
757
- capture_output=True, text=True,
758
- timeout=timeout_sec)
759
- out = (proc.stdout or "") + (proc.stderr or "")
760
- ok_exit = proc.returncode == vc.get("exit_code", 0)
761
- ok_contains = all(s in out for s in vc.get("stdout_contains", []))
762
- ok_not = not any(s in out for s in vc.get("stdout_not_contains", []))
763
- verify["commands"].append({
764
- "cmd": vc["cmd"],
765
- "expected_exit": vc.get("exit_code", 0),
766
- "actual_exit": proc.returncode,
767
- "pass": bool(ok_exit and ok_contains and ok_not),
768
- "reason": None if (ok_exit and ok_contains and ok_not)
769
- else ("exit" if not ok_exit
770
- else ("missing_contains" if not ok_contains else "unexpected_text")),
771
- "stdout_tail": out[-500:],
772
- })
773
- except subprocess.TimeoutExpired:
774
- verify["commands"].append({"cmd": vc["cmd"], "pass": False, "reason": "timeout"})
775
- except Exception as e:
776
- verify["commands"].append({"cmd": vc["cmd"], "pass": False,
777
- "reason": f"error:{e.__class__.__name__}:{e}"})
778
-
779
- # Forbidden pattern scan over diff.patch. Each pattern may declare a `files`
780
- # allowlist; when present, we slice the diff to only those files' hunks.
781
- diff_text = ""
782
- try:
783
- with open(os.path.join(result_dir, "diff.patch")) as fh:
784
- diff_text = fh.read()
785
- except Exception:
786
- pass
787
-
788
- def slice_diff_to_files(diff, files):
789
- """Return the subset of a unified diff touching any of `files`.
790
- Hunks outside the allowlist are dropped."""
791
- if not files:
792
- return diff
793
- out, keep = [], False
794
- for line in diff.splitlines(keepends=True):
795
- if line.startswith("diff --git "):
796
- keep = any(f in line for f in files)
797
- if keep:
798
- out.append(line)
799
- return "".join(out)
800
-
801
- for fp in expected.get("forbidden_patterns", []):
802
- scope = slice_diff_to_files(diff_text, fp.get("files") or [])
803
- if re.search(fp["pattern"], scope):
804
- verify["forbidden_pattern_hits"].append({
805
- "pattern": fp["pattern"],
806
- "severity": fp.get("severity", "warning"),
807
- "description": fp.get("description", ""),
808
- "scoped_to": fp.get("files") or "all",
809
- })
810
-
811
- # Deps added count (naive: count top-level added lines under dependencies keys)
812
- try:
813
- proc = subprocess.run(["git", "diff", "HEAD", "--", "package.json"],
814
- cwd=work, capture_output=True, text=True)
815
- in_deps = False
816
- for line in (proc.stdout or "").splitlines():
817
- if line.startswith("+ ") or line.startswith("- "):
818
- continue
819
- if '"dependencies"' in line or '"devDependencies"' in line:
820
- in_deps = True
821
- elif line.strip().startswith("}"):
822
- in_deps = False
823
- elif in_deps and line.startswith("+") and not line.startswith("+++"):
824
- if re.search(r'"[^"]+"\s*:\s*"[^"]+"', line):
825
- verify["deps_added"] += 1
826
- except Exception:
827
- pass
828
-
829
- # Required / forbidden files
830
- try:
831
- with open(os.path.join(result_dir, "changed-files.txt")) as fh:
832
- changed = [l.strip() for l in fh.read().splitlines() if l.strip()]
833
- except Exception:
834
- changed = []
835
- verify["missing_required_files"] = [
836
- f for f in expected.get("required_files", [])
837
- if not os.path.exists(os.path.join(work, f))
838
- ]
839
- verify["forbidden_files_present"] = [
840
- f for f in expected.get("forbidden_files", []) if f in changed
841
- ]
842
-
843
- total = len(verify["commands"])
844
- passed = sum(1 for r in verify["commands"] if r.get("pass"))
845
- verify["commands_passed"] = passed
846
- verify["commands_total"] = total
847
- verify["verify_score"] = (passed / total) if total else 1.0
848
-
849
- for oracle_file in (
850
- "oracle-scope-tier-a.json",
851
- "oracle-scope-tier-b.json",
852
- "oracle-test-fidelity.json",
853
- ):
854
- oracle_path = os.path.join(result_dir, oracle_file)
855
- try:
856
- raw_oracle = loads_strict_json_object(pathlib.Path(oracle_path).read_text())
857
- except (OSError, ValueError) as exc:
858
- oracle_name = oracle_file.removesuffix(".json")
859
- verify["oracle_findings"].append({
860
- "oracle": oracle_name,
861
- "type": "oracle-error",
862
- "severity": "hard",
863
- "verdict": "Deterministic oracle failed or emitted an invalid artifact",
864
- "error": f"oracle artifact malformed or unreadable: {exc}",
865
- })
866
- verify["oracle_disqualifier"] = True
867
- continue
868
- data = raw_oracle
869
- oracle_name = data.get("oracle") or oracle_file.removesuffix(".json")
870
- if not isinstance(oracle_name, str) or not oracle_name:
871
- oracle_name = oracle_file.removesuffix(".json")
872
- oracle_error = data.get("error")
873
- if isinstance(oracle_error, str) and oracle_error:
874
- verify["oracle_findings"].append({
875
- "oracle": oracle_name,
876
- "type": "oracle-error",
877
- "severity": "hard",
878
- "verdict": "Deterministic oracle failed or emitted an invalid artifact",
879
- "error": oracle_error,
880
- })
881
- verify["oracle_disqualifier"] = True
882
- raw_findings = data.get("findings")
883
- findings = raw_findings if isinstance(raw_findings, list) else []
884
- for finding in findings:
885
- if not isinstance(finding, dict):
886
- continue
887
- item = dict(finding)
888
- item["oracle"] = oracle_name
889
- verify["oracle_findings"].append(item)
890
- if item.get("severity") in ("disqualifier", "hard", "flag"):
891
- verify["oracle_disqualifier"] = True
892
-
893
- verify["disqualifier"] = (
894
- any(h["severity"] == "disqualifier" for h in verify["forbidden_pattern_hits"])
895
- or verify["deps_added"] > verify["max_deps_added"]
896
- or bool(verify["missing_required_files"])
897
- or bool(verify["forbidden_files_present"])
898
- or verify["oracle_disqualifier"]
899
- )
900
-
901
- json.dump(verify, open(os.path.join(result_dir, "verify.json"), "w"), indent=2)
902
- PY
903
-
904
- # Timing + aggregate
905
- export INVOKE_EXIT WATCHDOG_FIRED
906
- python3 - "$RESULT_DIR" "$FIXTURE" "$ARM" "$RUN_ID" "$T_END" "$ELAPSED" "$TIMEOUT" "$BENCH_ROOT/scripts" <<'PY'
907
- import json, os, pathlib, sys
908
- result_dir, fixture, arm, run_id = sys.argv[1:5]
909
- t_end, elapsed, timeout = int(sys.argv[5]), int(sys.argv[6]), int(sys.argv[7])
910
-
911
- sys.path.insert(0, sys.argv[8])
912
- from pair_evidence_contract import loads_strict_json_object
913
-
914
- timing = loads_strict_json_object(pathlib.Path(result_dir, "timing.json").read_text())
915
- timing["end_epoch"] = t_end
916
- timing["elapsed_seconds"] = elapsed
917
- timing["timeout_seconds"] = timeout
918
- # iter-0012: derive from watchdog signal, not elapsed wall time. Natural
919
- # exits at-or-past the budget (budget == elapsed, or up to ~5s past due to
920
- # SIGTERM grace) are no longer mislabeled as timeouts. Source of truth is
921
- # WATCHDOG_FIRED, set in run-fixture.sh when TIMEOUT_FLAG existed post-wait.
922
- timing["timed_out"] = os.environ.get("WATCHDOG_FIRED", "0") == "1"
923
- json.dump(timing, open(os.path.join(result_dir, "timing.json"), "w"), indent=2)
924
-
925
- def as_dict(value):
926
- return value if isinstance(value, dict) else {}
927
-
928
- verify = as_dict(loads_strict_json_object(pathlib.Path(result_dir, "verify.json").read_text()))
929
- try:
930
- with open(os.path.join(result_dir, "diff.patch")) as f: diff_size = len(f.read())
931
- except Exception: diff_size = 0
932
- try:
933
- with open(os.path.join(result_dir, "changed-files.txt")) as f:
934
- changed = [l for l in f.read().splitlines() if l.strip()]
935
- except Exception:
936
- changed = []
937
-
938
- state = {}
939
- state_path = os.path.join(result_dir, "run-archive", "pipeline.state.json")
940
- if os.path.isfile(state_path):
941
- state = as_dict(loads_strict_json_object(pathlib.Path(state_path).read_text()))
942
- phases = as_dict(state.get("phases"))
943
- verify_phase = as_dict(phases.get("verify"))
944
- legacy_verify = as_dict(state.get("verify"))
945
- sub_verdicts = verify_phase.get("sub_verdicts")
946
- pair_trigger = verify_phase.get("pair_trigger") or legacy_verify.get("pair_trigger")
947
- PAIR_VERDICTS = {"PASS", "PASS_WITH_ISSUES", "NEEDS_WORK", "BLOCKED", "FAIL"}
948
-
949
- def has_pair_judge_verdict(sub_verdicts):
950
- return isinstance(sub_verdicts, dict) and (
951
- sub_verdicts.get("judge_codex") in PAIR_VERDICTS
952
- or sub_verdicts.get("pair_judge") in PAIR_VERDICTS
953
- )
954
-
955
- pair_mode = has_pair_judge_verdict(sub_verdicts) or verify_phase.get("pair_mode") is True
956
-
957
- invoke_exit = int(os.environ.get("INVOKE_EXIT", "0"))
958
- plugin_contamination = False
959
- plugin_contamination_reason = None
960
- debug_path = os.path.join(result_dir, "claude-debug.log")
961
- try:
962
- with open(debug_path, errors="replace") as f:
963
- debug_text = f.read()
964
- except OSError:
965
- debug_text = ""
966
- if (
967
- "Plugin autoupdate: checking installed plugins" in debug_text
968
- or "Caching plugin from source:" in debug_text
969
- or "Cloned repository from " in debug_text
970
- or "Successfully cached plugin " in debug_text
971
- or "Found 8 plugins (8 enabled" in debug_text
972
- ):
973
- if "Plugin autoupdate: skipped (auto-updater disabled)" not in debug_text:
974
- plugin_contamination = True
975
- plugin_contamination_reason = "plugin_contamination"
976
-
977
- invoke_failure = (
978
- (invoke_exit not in (0,) and not timing["timed_out"])
979
- or plugin_contamination
980
- )
981
- invoke_failure_reason = None
982
- if plugin_contamination:
983
- invoke_failure_reason = plugin_contamination_reason
984
- elif invoke_failure:
985
- transcript_path = os.path.join(result_dir, "transcript.txt")
986
- haystack = ""
987
- for path in (transcript_path, debug_path):
988
- try:
989
- with open(path, errors="replace") as f:
990
- haystack += "\n" + f.read()
991
- except OSError:
992
- pass
993
- if "You've hit your limit" in haystack or "rate_limit_error" in haystack:
994
- invoke_failure_reason = "provider_limit"
995
-
996
- result = {
997
- "fixture": fixture,
998
- "arm": arm,
999
- "run_id": run_id,
1000
- "disqualifier": verify.get("disqualifier", False),
1001
- "oracle_disqualifier": verify.get("oracle_disqualifier", False),
1002
- "oracle_findings_count": len(verify.get("oracle_findings", [])),
1003
- "verify_score": verify.get("verify_score", 0.0),
1004
- "commands_passed": verify.get("commands_passed", 0),
1005
- "commands_total": verify.get("commands_total", 0),
1006
- "diff_bytes": diff_size,
1007
- "files_changed": len(changed),
1008
- "elapsed_seconds": elapsed,
1009
- "timed_out": timing["timed_out"],
1010
- "environment_contamination": plugin_contamination,
1011
- "environment_contamination_reason": plugin_contamination_reason,
1012
- "invoke_exit": invoke_exit,
1013
- "invoke_failure": invoke_failure,
1014
- "invoke_failure_reason": invoke_failure_reason,
1015
- "terminal_verdict": as_dict(phases.get("final_report")).get("verdict"),
1016
- "verify_verdict": verify_phase.get("verdict"),
1017
- "pair_trigger": pair_trigger,
1018
- "pair_mode": pair_mode,
1019
- }
1020
- json.dump(result, open(os.path.join(result_dir, "result.json"), "w"), indent=2)
1021
- print(json.dumps(result, indent=2))
1022
- PY
1023
-
1024
- echo "[run-fixture] done: $RESULT_DIR"