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.
- package/AGENTS.md +2 -2
- package/CLAUDE.md +2 -2
- package/README.md +12 -102
- package/bin/devlyn.js +13 -225
- package/bin/instruction-templates.json +6 -0
- package/config/skills/_shared/adapters/grok.md +1 -2
- package/config/skills/_shared/archive_run.py +15 -3
- package/config/skills/_shared/judge-role-evidence.py +21 -1
- package/config/skills/_shared/runtime-principles.md +1 -1
- package/config/skills/_shared/spec-verify-check.py +0 -653
- package/config/skills/_shared/state-phase-write.py +351 -25
- package/config/skills/_shared/task-complete.py +635 -143
- package/config/skills/_shared/terminal-claim-check.py +229 -4
- package/config/skills/_shared/verify-merge-findings.py +93 -2
- package/config/skills/devlyn:ideate/SKILL.md +3 -4
- package/config/skills/devlyn:ideate/references/elicitation.md +2 -35
- package/config/skills/devlyn:ideate/references/from-spec-mode.md +3 -18
- package/config/skills/devlyn:ideate/references/project-mode.md +1 -16
- package/config/skills/devlyn:ideate/references/spec-template.md +0 -2
- package/config/skills/devlyn:queue/SKILL.md +6 -4
- package/config/skills/devlyn:resolve/SKILL.md +16 -25
- package/config/skills/devlyn:resolve/references/free-form-mode.md +0 -14
- package/config/skills/devlyn:resolve/references/outer-loop.md +1 -1
- package/config/skills/devlyn:resolve/references/phases/build-gate.md +1 -1
- package/config/skills/devlyn:resolve/references/phases/cleanup.md +3 -3
- package/config/skills/devlyn:resolve/references/phases/implement.md +1 -1
- package/config/skills/devlyn:resolve/references/phases/probe-derive.md +1 -16
- package/config/skills/devlyn:resolve/references/phases/verify.md +4 -9
- package/config/skills/devlyn:resolve/references/state-schema.md +9 -7
- package/config/skills/devlyn:resolve/references/task-completion.md +46 -37
- package/{config/skills → optional-skills}/code-health-standards/SKILL.md +4 -13
- package/{config/skills → optional-skills}/code-review-standards/SKILL.md +4 -12
- package/{config/skills → optional-skills}/root-cause-analysis/SKILL.md +4 -12
- package/{config/skills → optional-skills}/ui-implementation-standards/SKILL.md +4 -14
- package/package.json +1 -57
- package/benchmark/auto-resolve/BENCHMARK-DESIGN.md +0 -289
- package/benchmark/auto-resolve/BENCHMARK-RESULTS.md +0 -341
- package/benchmark/auto-resolve/README.md +0 -696
- package/benchmark/auto-resolve/RUBRIC.md +0 -177
- package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/NOTES.md +0 -34
- package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/expected.json +0 -68
- package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/setup.sh +0 -4
- package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/spec.md +0 -44
- package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/task.ko.txt +0 -8
- package/benchmark/auto-resolve/fixtures/F1-cli-trivial-flag/task.txt +0 -8
- package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/NOTES.md +0 -68
- package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/expected.json +0 -60
- package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/setup.sh +0 -17
- package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/spec.md +0 -51
- package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/verifiers/invalid.js +0 -29
- package/benchmark/auto-resolve/fixtures/F10-persist-write-collision/verifiers/parallel.js +0 -50
- package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/NOTES.md +0 -75
- package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/expected.json +0 -52
- package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/setup.sh +0 -171
- package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/spec.md +0 -50
- package/benchmark/auto-resolve/fixtures/F11-batch-import-all-or-nothing/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/NOTES.md +0 -89
- package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/expected.json +0 -74
- package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/setup.sh +0 -251
- package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/spec.md +0 -57
- package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/task.txt +0 -13
- package/benchmark/auto-resolve/fixtures/F12-webhook-raw-body-signature/verifiers/replay-malformed-body.js +0 -64
- package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/NOTES.md +0 -104
- package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/expected.json +0 -46
- package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/setup.sh +0 -336
- package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/spec.md +0 -51
- package/benchmark/auto-resolve/fixtures/F15-frozen-diff-race-review/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/NOTES.md +0 -38
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/expected.json +0 -64
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/setup.sh +0 -32
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/spec.md +0 -63
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/exact-success.js +0 -54
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/no-hardcoded-pricing.js +0 -47
- package/benchmark/auto-resolve/fixtures/F16-cli-quote-tax-rules/verifiers/stock-error.js +0 -45
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/NOTES.md +0 -57
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/expected-pair-plan-registry.json +0 -170
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/expected.json +0 -84
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/metadata.json +0 -21
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/pair-plan.sample-fail.json +0 -214
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/pair-plan.sample-pass.json +0 -223
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/setup.sh +0 -5
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/spec.md +0 -55
- package/benchmark/auto-resolve/fixtures/F2-cli-medium-subcommand/task.txt +0 -14
- package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/NOTES.md +0 -39
- package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/expected.json +0 -62
- package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/setup.sh +0 -2
- package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/spec.md +0 -67
- package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/verifiers/error-order.js +0 -55
- package/benchmark/auto-resolve/fixtures/F21-cli-scheduler-priority/verifiers/priority-blocked.js +0 -48
- package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/NOTES.md +0 -35
- package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/expected.json +0 -56
- package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/setup.sh +0 -2
- package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/spec.md +0 -64
- package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/verifiers/conflicting-duplicate.js +0 -34
- package/benchmark/auto-resolve/fixtures/F22-cli-ledger-close/verifiers/idempotent-close.js +0 -41
- package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/NOTES.md +0 -39
- package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/expected.json +0 -56
- package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/setup.sh +0 -2
- package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/spec.md +0 -76
- package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/verifiers/priority-rollback.js +0 -64
- package/benchmark/auto-resolve/fixtures/F23-cli-fulfillment-wave/verifiers/single-warehouse-fefo.js +0 -66
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/NOTES.md +0 -40
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/expected.json +0 -66
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/setup.sh +0 -36
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/spec.md +0 -71
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/catalog-source.js +0 -57
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/exact-success.js +0 -63
- package/benchmark/auto-resolve/fixtures/F25-cli-cart-promotion-rules/verifiers/stock-error.js +0 -34
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/NOTES.md +0 -31
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/expected.json +0 -68
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/setup.sh +0 -17
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/spec.md +0 -68
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/conflicting-duplicate.js +0 -29
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/exact-payout.js +0 -58
- package/benchmark/auto-resolve/fixtures/F26-cli-payout-ledger-rules/verifiers/rules-source.js +0 -56
- package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/NOTES.md +0 -35
- package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/expected-pair-plan-registry.json +0 -162
- package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/expected.json +0 -67
- package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/metadata.json +0 -19
- package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/setup.sh +0 -4
- package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/spec.md +0 -55
- package/benchmark/auto-resolve/fixtures/F3-backend-contract-risk/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/NOTES.md +0 -34
- package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/expected.json +0 -57
- package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/setup.sh +0 -2
- package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/spec.md +0 -67
- package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/verifiers/duplicate-event-error.js +0 -35
- package/benchmark/auto-resolve/fixtures/F31-cli-seat-rebalance/verifiers/priority-transfer-rollback.js +0 -53
- package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/NOTES.md +0 -38
- package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/expected.json +0 -57
- package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/setup.sh +0 -2
- package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/spec.md +0 -70
- package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/task.txt +0 -3
- package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/verifiers/duplicate-renewal-error.js +0 -42
- package/benchmark/auto-resolve/fixtures/F32-cli-subscription-renewal/verifiers/priority-credit-rollback.js +0 -70
- package/benchmark/auto-resolve/fixtures/F4-web-browser-design/NOTES.md +0 -47
- package/benchmark/auto-resolve/fixtures/F4-web-browser-design/expected.json +0 -57
- package/benchmark/auto-resolve/fixtures/F4-web-browser-design/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F4-web-browser-design/setup.sh +0 -6
- package/benchmark/auto-resolve/fixtures/F4-web-browser-design/spec.md +0 -48
- package/benchmark/auto-resolve/fixtures/F4-web-browser-design/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/NOTES.md +0 -45
- package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/expected.json +0 -65
- package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/setup.sh +0 -55
- package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/spec.md +0 -48
- package/benchmark/auto-resolve/fixtures/F5-fix-loop-red-green/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/NOTES.md +0 -43
- package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/expected.json +0 -77
- package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/setup.sh +0 -4
- package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/spec.md +0 -48
- package/benchmark/auto-resolve/fixtures/F6-dep-audit-native-module/task.txt +0 -10
- package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/NOTES.md +0 -57
- package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/expected.json +0 -76
- package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/setup.sh +0 -36
- package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/spec.md +0 -45
- package/benchmark/auto-resolve/fixtures/F7-out-of-scope-trap/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/NOTES.md +0 -53
- package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/expected.json +0 -63
- package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/setup.sh +0 -4
- package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/spec.md +0 -48
- package/benchmark/auto-resolve/fixtures/F8-known-limit-ambiguous/task.txt +0 -1
- package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/NOTES.md +0 -105
- package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/expected.json +0 -74
- package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/setup.sh +0 -28
- package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/spec.md +0 -59
- package/benchmark/auto-resolve/fixtures/F9-e2e-ideate-to-resolve/task.txt +0 -5
- package/benchmark/auto-resolve/fixtures/SCHEMA.md +0 -188
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/NOTES.md +0 -37
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/RETIRED.md +0 -13
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/expected.json +0 -56
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/setup.sh +0 -18
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/spec.md +0 -69
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/task.txt +0 -7
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/verifiers/exact-proration.js +0 -48
- package/benchmark/auto-resolve/fixtures/retired/F27-cli-subscription-proration/verifiers/rules-source-and-conflict.js +0 -79
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/NOTES.md +0 -54
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/RETIRED.md +0 -7
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/expected.json +0 -67
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/setup.sh +0 -2
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/spec.md +0 -67
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/task.txt +0 -5
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/policy-precedence.js +0 -72
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/validation-and-immutability.js +0 -43
- package/benchmark/auto-resolve/fixtures/retired/F28-cli-return-authorization/verifiers/validation-boundary.js +0 -116
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/NOTES.md +0 -35
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/RETIRED.md +0 -12
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/expected.json +0 -58
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/setup.sh +0 -2
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/spec.md +0 -73
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/task.txt +0 -17
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/verifiers/mixed-idempotent-settlement.js +0 -53
- package/benchmark/auto-resolve/fixtures/retired/F30-cli-credit-hold-settlement/verifiers/rejection-boundaries.js +0 -74
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/NOTES.md +0 -28
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/RETIRED.md +0 -15
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/expected.json +0 -57
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/setup.sh +0 -50
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/spec.md +0 -59
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/verifiers/ordered-settlement.js +0 -88
- package/benchmark/auto-resolve/fixtures/retired/F34-cli-settle-inflight/verifiers/reject-invalid.js +0 -45
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/NOTES.md +0 -30
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/RETIRED.md +0 -16
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/expected.json +0 -64
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/setup.sh +0 -21
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/spec.md +0 -58
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/dup-op.js +0 -54
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/priority-apply.js +0 -82
- package/benchmark/auto-resolve/fixtures/retired/F35-cli-apply-journal/verifiers/rollback.js +0 -77
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/NOTES.md +0 -99
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/RETIRED.md +0 -18
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/expected.json +0 -64
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/setup.sh +0 -4
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/spec.md +0 -61
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/correctness-small.js +0 -135
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/scale.js +0 -100
- package/benchmark/auto-resolve/fixtures/retired/F36-cli-session-admission/verifiers/validation.js +0 -75
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/NOTES.md +0 -93
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/RETIRED.md +0 -16
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/expected.json +0 -65
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/setup.sh +0 -18
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/spec.md +0 -66
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/task.txt +0 -9
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/correctness-small.js +0 -84
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/scale.js +0 -184
- package/benchmark/auto-resolve/fixtures/retired/F37-cli-rule-lookup/verifiers/validation.js +0 -74
- package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/NOTES.md +0 -60
- package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/RETIRED.md +0 -29
- package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/expected.json +0 -73
- package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/metadata.json +0 -10
- package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/setup.sh +0 -28
- package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/spec.md +0 -58
- package/benchmark/auto-resolve/fixtures/retired/F9-e2e-ideate-to-preflight/task.txt +0 -5
- package/benchmark/auto-resolve/fixtures/test-repo/README.md +0 -12
- package/benchmark/auto-resolve/fixtures/test-repo/bin/cli.js +0 -63
- package/benchmark/auto-resolve/fixtures/test-repo/package-lock.json +0 -823
- package/benchmark/auto-resolve/fixtures/test-repo/package.json +0 -22
- package/benchmark/auto-resolve/fixtures/test-repo/playwright.config.js +0 -17
- package/benchmark/auto-resolve/fixtures/test-repo/server/index.js +0 -37
- package/benchmark/auto-resolve/fixtures/test-repo/tests/cli.test.js +0 -25
- package/benchmark/auto-resolve/fixtures/test-repo/tests/server.test.js +0 -58
- package/benchmark/auto-resolve/fixtures/test-repo/web/index.html +0 -37
- package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.json +0 -25
- package/benchmark/auto-resolve/results/20260507-f10-f11-tier1-full-pipeline/headroom-gate.md +0 -10
- package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.json +0 -18
- package/benchmark/auto-resolve/results/20260508-f22-exact-error-headroom/headroom-gate.md +0 -9
- package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.json +0 -18
- package/benchmark/auto-resolve/results/20260508-f26-headroom/headroom-gate.md +0 -9
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F16-cli-quote-tax-rules/l2_risk_probes/result.json +0 -28
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F23-cli-fulfillment-wave/l2_risk_probes/result.json +0 -28
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/F25-cli-cart-promotion-rules/l2_risk_probes/result.json +0 -30
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/full-pipeline-pair-gate.json +0 -82
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/full-pipeline-pair-gate.md +0 -18
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/headroom-gate.json +0 -46
- package/benchmark/auto-resolve/results/20260510-f16-f23-f25-combined-proof/headroom-gate.md +0 -17
- package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260511-f12-webhook-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260511-f15-concurrency-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/F21-cli-scheduler-priority/l2_risk_probes/result.json +0 -32
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.json +0 -44
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/full-pipeline-pair-gate.md +0 -16
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.json +0 -18
- package/benchmark/auto-resolve/results/20260511-f21-current-riskprobes-v1/headroom-gate.md +0 -9
- package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260511-f3-http-error-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260512-f2-medium-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260512-f31-seat-rebalance-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260512-f32-subscription-renewal-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260512-f4-web-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260512-f5-fixloop-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260512-f6-checksum-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260512-f7-scope-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.json +0 -28
- package/benchmark/auto-resolve/results/20260512-f9-e2e-headroom/headroom-gate.md +0 -15
- package/benchmark/auto-resolve/run-real-benchmark.md +0 -303
- package/benchmark/auto-resolve/scripts/audit-headroom-rejections.py +0 -441
- package/benchmark/auto-resolve/scripts/audit-pair-evidence.py +0 -1256
- package/benchmark/auto-resolve/scripts/build-pair-eligible-manifest.py +0 -306
- package/benchmark/auto-resolve/scripts/check-f9-artifacts.py +0 -268
- package/benchmark/auto-resolve/scripts/collect-swebench-predictions.py +0 -108
- package/benchmark/auto-resolve/scripts/compile-report.py +0 -493
- package/benchmark/auto-resolve/scripts/fetch-swebench-instances.py +0 -129
- package/benchmark/auto-resolve/scripts/frozen-verify-gate.py +0 -434
- package/benchmark/auto-resolve/scripts/full-pipeline-pair-gate.py +0 -612
- package/benchmark/auto-resolve/scripts/headroom-gate.py +0 -378
- package/benchmark/auto-resolve/scripts/iter-0033c-compare.py +0 -683
- package/benchmark/auto-resolve/scripts/iter-0033c-l1-summary.py +0 -97
- package/benchmark/auto-resolve/scripts/judge-opus-pass.sh +0 -542
- package/benchmark/auto-resolve/scripts/judge.sh +0 -565
- package/benchmark/auto-resolve/scripts/oracle-scope-tier-a.py +0 -256
- package/benchmark/auto-resolve/scripts/oracle-scope-tier-b.py +0 -287
- package/benchmark/auto-resolve/scripts/oracle-test-fidelity.py +0 -328
- package/benchmark/auto-resolve/scripts/pair-candidate-frontier.py +0 -469
- package/benchmark/auto-resolve/scripts/pair-plan-idgen.py +0 -401
- package/benchmark/auto-resolve/scripts/pair-plan-lint.py +0 -475
- package/benchmark/auto-resolve/scripts/pair-rejected-fixtures.sh +0 -103
- package/benchmark/auto-resolve/scripts/pair_evidence_contract.py +0 -270
- package/benchmark/auto-resolve/scripts/prepare-swebench-frozen-case.py +0 -273
- package/benchmark/auto-resolve/scripts/prepare-swebench-frozen-corpus.py +0 -148
- package/benchmark/auto-resolve/scripts/prepare-swebench-solver-worktree.py +0 -210
- package/benchmark/auto-resolve/scripts/recent-benchmark-summary.py +0 -232
- package/benchmark/auto-resolve/scripts/run-fixture.sh +0 -1024
- package/benchmark/auto-resolve/scripts/run-frozen-verify-pair.sh +0 -683
- package/benchmark/auto-resolve/scripts/run-full-pipeline-pair-candidate.sh +0 -458
- package/benchmark/auto-resolve/scripts/run-headroom-candidate.sh +0 -336
- package/benchmark/auto-resolve/scripts/run-iter-0033c.sh +0 -208
- package/benchmark/auto-resolve/scripts/run-suite.sh +0 -251
- package/benchmark/auto-resolve/scripts/run-swebench-frozen-corpus.sh +0 -310
- package/benchmark/auto-resolve/scripts/run-swebench-solver-batch.sh +0 -257
- package/benchmark/auto-resolve/scripts/ship-gate.py +0 -391
- package/benchmark/auto-resolve/scripts/solo-ceiling-avoidance.py +0 -53
- package/benchmark/auto-resolve/scripts/solo-headroom-hypothesis.py +0 -77
- package/benchmark/auto-resolve/scripts/swebench-frozen-matrix.py +0 -478
- package/benchmark/auto-resolve/scripts/test-audit-headroom-rejections.sh +0 -288
- package/benchmark/auto-resolve/scripts/test-audit-pair-evidence.sh +0 -1672
- package/benchmark/auto-resolve/scripts/test-benchmark-arg-parsing.sh +0 -1015
- package/benchmark/auto-resolve/scripts/test-build-pair-eligible-manifest.sh +0 -491
- package/benchmark/auto-resolve/scripts/test-check-f9-artifacts.sh +0 -91
- package/benchmark/auto-resolve/scripts/test-frozen-verify-gate.sh +0 -517
- package/benchmark/auto-resolve/scripts/test-full-pipeline-pair-gate.sh +0 -610
- package/benchmark/auto-resolve/scripts/test-headroom-gate.sh +0 -401
- package/benchmark/auto-resolve/scripts/test-iter-0033c-compare.sh +0 -525
- package/benchmark/auto-resolve/scripts/test-iter-0033c-l1-summary.sh +0 -254
- package/benchmark/auto-resolve/scripts/test-lint-fixtures.sh +0 -580
- package/benchmark/auto-resolve/scripts/test-pair-candidate-frontier.sh +0 -591
- package/benchmark/auto-resolve/scripts/test-run-full-pipeline-pair-candidate.sh +0 -497
- package/benchmark/auto-resolve/scripts/test-run-headroom-candidate.sh +0 -401
- package/benchmark/auto-resolve/scripts/test-run-swebench-solver-batch.sh +0 -111
- package/benchmark/auto-resolve/scripts/test-ship-gate.sh +0 -1189
- package/benchmark/auto-resolve/scripts/test-swebench-frozen-case.sh +0 -1221
- package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/NOTES.md +0 -28
- package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/expected.json +0 -63
- package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/metadata.json +0 -10
- package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/setup.sh +0 -3
- package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/spec.md +0 -47
- package/benchmark/auto-resolve/shadow-fixtures/S1-cli-lang-flag/task.txt +0 -1
- package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/NOTES.md +0 -34
- package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/expected.json +0 -53
- package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/metadata.json +0 -10
- package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/setup.sh +0 -3
- package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/spec.md +0 -50
- package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/task.txt +0 -1
- package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/verifiers/duplicate-order-error.js +0 -27
- package/benchmark/auto-resolve/shadow-fixtures/S2-cli-inventory-reservation/verifiers/priority-stock-reservation.js +0 -44
- package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/NOTES.md +0 -34
- package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/expected.json +0 -55
- package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/metadata.json +0 -10
- package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/setup.sh +0 -3
- package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/spec.md +0 -52
- package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/task.txt +0 -1
- package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/verifiers/duplicate-ticket-error.js +0 -29
- package/benchmark/auto-resolve/shadow-fixtures/S3-cli-ticket-assignment/verifiers/priority-agent-assignment.js +0 -48
- package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/NOTES.md +0 -34
- package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/expected.json +0 -55
- package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/metadata.json +0 -10
- package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/setup.sh +0 -3
- package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/spec.md +0 -55
- package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/task.txt +0 -1
- package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/verifiers/duplicate-return-error.js +0 -43
- package/benchmark/auto-resolve/shadow-fixtures/S4-cli-return-routing/verifiers/priority-return-routing.js +0 -70
- package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/NOTES.md +0 -37
- package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/expected.json +0 -54
- package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/metadata.json +0 -10
- package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/setup.sh +0 -3
- package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/spec.md +0 -59
- package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/task.txt +0 -1
- package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/verifiers/credit-ledger-priority.js +0 -98
- package/benchmark/auto-resolve/shadow-fixtures/S5-cli-credit-grant-ledger/verifiers/duplicate-charge-error.js +0 -38
- package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/NOTES.md +0 -36
- package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/expected.json +0 -56
- package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/metadata.json +0 -10
- package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/setup.sh +0 -3
- package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/spec.md +0 -59
- package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/task.txt +0 -1
- package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/verifiers/duplicate-refund-error.js +0 -41
- package/benchmark/auto-resolve/shadow-fixtures/S6-cli-refund-window-ledger/verifiers/priority-refund-ledger.js +0 -65
- package/config/skills/_shared/pair-plan-schema.md +0 -298
- package/scripts/lint-fixtures.sh +0 -352
- package/scripts/lint-shadow-fixtures.sh +0 -58
- package/scripts/lint-skills.sh +0 -4265
- package/scripts/test-owner-phases.py +0 -238
|
@@ -39,6 +39,7 @@ import sys
|
|
|
39
39
|
import tempfile
|
|
40
40
|
|
|
41
41
|
VALID_VERDICTS = {"PASS", "PASS_WITH_ISSUES", "FAIL", "NEEDS_WORK", "BLOCKED"}
|
|
42
|
+
FINAL_VERDICTS = {"PASS", "PASS_WITH_ISSUES", "NEEDS_WORK", "BLOCKED"}
|
|
42
43
|
VALID_TRIGGERS = {"build_gate", "cleanup", "verify"}
|
|
43
44
|
SPAWN_TRIGGERS = VALID_TRIGGERS | {"plan"}
|
|
44
45
|
PHASE_NAMES = {"plan", "probe_derive", "implement", "surface_close", "build_gate", "cleanup", "verify", "final_report"}
|
|
@@ -1682,8 +1683,109 @@ def freeze_roles(state: dict, work: pathlib.Path, default_engine: str) -> dict:
|
|
|
1682
1683
|
return resolved
|
|
1683
1684
|
|
|
1684
1685
|
|
|
1686
|
+
REPAIR_PHASES = ("implement", "build_gate", "cleanup", "verify")
|
|
1687
|
+
|
|
1688
|
+
|
|
1689
|
+
def valid_phase_gate_progress(progress: object) -> bool:
|
|
1690
|
+
if not isinstance(progress, dict):
|
|
1691
|
+
return False
|
|
1692
|
+
total, current, statuses = progress.get("total"), progress.get("current"), progress.get("statuses")
|
|
1693
|
+
return (type(total) is int and total > 1 and type(current) is int and 1 <= current <= total
|
|
1694
|
+
and isinstance(statuses, list) and len(statuses) == total
|
|
1695
|
+
and all(status in ("PASS", "FAIL", None) for status in statuses)
|
|
1696
|
+
and all(status == "PASS" for status in statuses[:current - 1]))
|
|
1697
|
+
|
|
1698
|
+
|
|
1699
|
+
def repair_predecessor(state: dict) -> tuple[str, dict] | None:
|
|
1700
|
+
"""Select the most recent started repair boundary by invocation identity."""
|
|
1701
|
+
phases = state.get("phases", {})
|
|
1702
|
+
selected = None
|
|
1703
|
+
for order, name in enumerate(REPAIR_PHASES):
|
|
1704
|
+
entry = phases.get(name) if isinstance(phases, dict) else None
|
|
1705
|
+
if not isinstance(entry, dict) or entry.get("started_at") is None:
|
|
1706
|
+
continue
|
|
1707
|
+
round_ = entry.get("round")
|
|
1708
|
+
if type(round_) is not int or round_ < 0:
|
|
1709
|
+
raise SystemExit("BLOCKED:repair-edge-invalid")
|
|
1710
|
+
if selected is None or (round_, order) > selected[0]:
|
|
1711
|
+
selected = ((round_, order), name, entry)
|
|
1712
|
+
return None if selected is None else (selected[1], selected[2])
|
|
1713
|
+
|
|
1714
|
+
|
|
1715
|
+
def repair_admission(state: dict, phase: str, round_: int,
|
|
1716
|
+
triggered_by: str | None, source_phase: str | None = None) -> None:
|
|
1717
|
+
if phase not in REPAIR_PHASES:
|
|
1718
|
+
return
|
|
1719
|
+
predecessor = repair_predecessor(state)
|
|
1720
|
+
if phase == "implement":
|
|
1721
|
+
if predecessor is None:
|
|
1722
|
+
if triggered_by not in {None, "plan"}:
|
|
1723
|
+
raise SystemExit("BLOCKED:repair-trigger-mismatch: expected=null supplied=" + triggered_by)
|
|
1724
|
+
expected_round, origin = 0, None
|
|
1725
|
+
else:
|
|
1726
|
+
prior_name, prior = predecessor
|
|
1727
|
+
if prior.get("completed_at") is None or (source_phase is not None and prior_name != source_phase):
|
|
1728
|
+
raise SystemExit("BLOCKED:repair-edge-invalid")
|
|
1729
|
+
verdict = prior.get("verdict")
|
|
1730
|
+
origin = None
|
|
1731
|
+
if prior_name in {"build_gate", "cleanup", "verify"}:
|
|
1732
|
+
valid = (verdict == "NEEDS_WORK" and isinstance(prior.get("merged"), dict)
|
|
1733
|
+
and prior["merged"].get("verdict") == "NEEDS_WORK") if prior_name == "verify" else verdict == "FAIL"
|
|
1734
|
+
if prior_name == "cleanup":
|
|
1735
|
+
valid = valid and orchestrator_phase(prior, "cleanup")
|
|
1736
|
+
if not valid:
|
|
1737
|
+
raise SystemExit("BLOCKED:repair-edge-invalid")
|
|
1738
|
+
origin = prior_name
|
|
1739
|
+
elif prior_name == "implement":
|
|
1740
|
+
progress = prior.get("exec")
|
|
1741
|
+
if not valid_phase_gate_progress(progress):
|
|
1742
|
+
raise SystemExit("BLOCKED:repair-edge-invalid")
|
|
1743
|
+
current, statuses = progress["current"], progress["statuses"]
|
|
1744
|
+
status = statuses[current - 1]
|
|
1745
|
+
if verdict == "FAIL" and status == "FAIL":
|
|
1746
|
+
origin = "phase_gate"
|
|
1747
|
+
elif verdict not in {"PASS", "PASS_WITH_ISSUES"} or current < 2 or status is not None:
|
|
1748
|
+
raise SystemExit("BLOCKED:repair-edge-invalid")
|
|
1749
|
+
else:
|
|
1750
|
+
raise SystemExit("BLOCKED:repair-edge-invalid")
|
|
1751
|
+
expected_round = prior["round"] + 1
|
|
1752
|
+
expected_trigger = origin if origin in VALID_TRIGGERS else None
|
|
1753
|
+
if triggered_by != expected_trigger:
|
|
1754
|
+
supplied = triggered_by if triggered_by is not None else "null"
|
|
1755
|
+
expected = expected_trigger if expected_trigger is not None else "null"
|
|
1756
|
+
raise SystemExit(f"BLOCKED:repair-trigger-mismatch: expected={expected} supplied={supplied}")
|
|
1757
|
+
if round_ != expected_round:
|
|
1758
|
+
raise SystemExit(f"BLOCKED:implement-round-nonmonotonic: expected={expected_round} supplied={round_}")
|
|
1759
|
+
if origin is not None:
|
|
1760
|
+
rounds = state.get("rounds")
|
|
1761
|
+
if (not isinstance(rounds, dict) or type(rounds.get("global")) is not int
|
|
1762
|
+
or rounds["global"] < 0 or type(rounds.get("max_rounds")) is not int
|
|
1763
|
+
or rounds["max_rounds"] < 1):
|
|
1764
|
+
raise SystemExit("BLOCKED:rounds-malformed")
|
|
1765
|
+
if rounds["global"] >= rounds["max_rounds"]:
|
|
1766
|
+
raise SystemExit(
|
|
1767
|
+
f"BLOCKED:repair-budget-exhausted: global={rounds['global']} "
|
|
1768
|
+
f"max_rounds={rounds['max_rounds']} origin={origin}"
|
|
1769
|
+
)
|
|
1770
|
+
rounds["global"] += 1
|
|
1771
|
+
elif phase in {"build_gate", "cleanup", "verify"}:
|
|
1772
|
+
implement = (state.get("phases") or {}).get("implement")
|
|
1773
|
+
expected_round = implement.get("round", 0) if isinstance(implement, dict) and implement.get("started_at") else 0
|
|
1774
|
+
if round_ != expected_round:
|
|
1775
|
+
raise SystemExit(f"BLOCKED:phase-round-mismatch: phase={phase} expected={expected_round} supplied={round_}")
|
|
1776
|
+
|
|
1777
|
+
|
|
1778
|
+
def validate_verdict(phase: str, verdict: str | None) -> None:
|
|
1779
|
+
allowed = (verdict in FINAL_VERDICTS or (
|
|
1780
|
+
isinstance(verdict, str) and verdict.startswith("BLOCKED:") and len(verdict) > 8
|
|
1781
|
+
)) if phase == "final_report" else verdict in VALID_VERDICTS
|
|
1782
|
+
if verdict is not None and not allowed:
|
|
1783
|
+
raise SystemExit(f"error: invalid verdict for phases.{phase}: {verdict}")
|
|
1784
|
+
|
|
1785
|
+
|
|
1685
1786
|
def do_spawn(state: dict, phase: str, round_: int, triggered_by: str | None,
|
|
1686
1787
|
pre_sha: str | None, engine: str | None, model: str | None, *,
|
|
1788
|
+
source_phase: str | None = None,
|
|
1687
1789
|
input_patch_sha256: str | None = None,
|
|
1688
1790
|
prompt_sha256: str | None = None,
|
|
1689
1791
|
untracked_before: list[str] | None = None,
|
|
@@ -1804,6 +1906,7 @@ def do_spawn(state: dict, phase: str, round_: int, triggered_by: str | None,
|
|
|
1804
1906
|
raise SystemExit(
|
|
1805
1907
|
f"error: phases.{phase} has an open span — complete it before respawn"
|
|
1806
1908
|
)
|
|
1909
|
+
repair_admission(state, phase, round_, triggered_by, source_phase)
|
|
1807
1910
|
append_phase_history(entry, phase)
|
|
1808
1911
|
if phase in WORKER_SESSION_ARTIFACT_PHASES:
|
|
1809
1912
|
entry.pop("invocation_receipt", None)
|
|
@@ -1812,7 +1915,10 @@ def do_spawn(state: dict, phase: str, round_: int, triggered_by: str | None,
|
|
|
1812
1915
|
entry["completed_at"] = None
|
|
1813
1916
|
entry["duration_ms"] = None
|
|
1814
1917
|
entry["round"] = round_
|
|
1815
|
-
entry["triggered_by"] =
|
|
1918
|
+
entry["triggered_by"] = (
|
|
1919
|
+
"cleanup" if phase == "implement" and triggered_by is None
|
|
1920
|
+
and entry.get("triggered_by") == "cleanup" else triggered_by
|
|
1921
|
+
)
|
|
1816
1922
|
entry["verdict"] = None
|
|
1817
1923
|
entry["artifacts"] = {"findings_file": None, "log_file": None}
|
|
1818
1924
|
entry["sub_verdicts"] = None
|
|
@@ -1881,6 +1987,7 @@ def do_complete(state: dict, phase: str, verdict: str | None,
|
|
|
1881
1987
|
entry = phases.get(phase)
|
|
1882
1988
|
if not isinstance(entry, dict) or not entry.get("started_at"):
|
|
1883
1989
|
raise SystemExit(f"error: phases.{phase} was never spawned (no started_at) — cannot complete")
|
|
1990
|
+
validate_verdict(phase, verdict)
|
|
1884
1991
|
if phase == "verify" and verdict is not None:
|
|
1885
1992
|
raise SystemExit(
|
|
1886
1993
|
"error: phases.verify.verdict is owned by verify-merge-findings.py "
|
|
@@ -1954,6 +2061,13 @@ def do_complete(state: dict, phase: str, verdict: str | None,
|
|
|
1954
2061
|
entry["verdict"] = verdict
|
|
1955
2062
|
else:
|
|
1956
2063
|
raise SystemExit(f"error: --verdict is required to complete phases.{phase}")
|
|
2064
|
+
if phase == "implement" and verdict == "FAIL" and isinstance(entry.get("exec"), dict):
|
|
2065
|
+
progress = entry["exec"]
|
|
2066
|
+
total, current, statuses = progress.get("total"), progress.get("current"), progress.get("statuses")
|
|
2067
|
+
if (type(total) is not int or total <= 1 or type(current) is not int
|
|
2068
|
+
or not 1 <= current <= total or not isinstance(statuses, list) or len(statuses) != total):
|
|
2069
|
+
raise SystemExit("BLOCKED:repair-edge-invalid")
|
|
2070
|
+
statuses[current - 1] = "FAIL"
|
|
1957
2071
|
if phase == "plan":
|
|
1958
2072
|
if state.get("version") == "3.0":
|
|
1959
2073
|
bind_plan_output(state, devlyn)
|
|
@@ -2104,7 +2218,9 @@ def do_transition(
|
|
|
2104
2218
|
"""
|
|
2105
2219
|
if next_phase not in LEGAL_TRANSITIONS.get(phase, set()):
|
|
2106
2220
|
raise SystemExit(f"error: illegal phase transition: {phase} -> {next_phase}")
|
|
2107
|
-
|
|
2221
|
+
validate_verdict(phase, verdict)
|
|
2222
|
+
if (phase == "implement" and next_phase != "implement"
|
|
2223
|
+
and state.get("phases", {}).get(phase, {}).get("triggered_by") == "cleanup"):
|
|
2108
2224
|
bypasses = state.get("bypasses", [])
|
|
2109
2225
|
required = "build_gate" if "build-gate" not in bypasses else (
|
|
2110
2226
|
"cleanup" if "cleanup" not in bypasses else "verify"
|
|
@@ -2131,6 +2247,7 @@ def do_transition(
|
|
|
2131
2247
|
do_spawn(
|
|
2132
2248
|
candidate, next_phase, next_round, next_triggered_by,
|
|
2133
2249
|
next_pre_sha, next_engine, next_model,
|
|
2250
|
+
source_phase=phase,
|
|
2134
2251
|
input_patch_sha256=next_input_patch_sha256,
|
|
2135
2252
|
prompt_sha256=next_prompt_sha256,
|
|
2136
2253
|
untracked_before=next_untracked_before,
|
|
@@ -2209,6 +2326,14 @@ def final_report_self_test() -> None:
|
|
|
2209
2326
|
assert result.returncode == 1 and "BLOCKED:final-report-invalid" in result.stderr, (case, result)
|
|
2210
2327
|
assert state_path.read_bytes() == before, case
|
|
2211
2328
|
report.write_bytes(valid)
|
|
2329
|
+
for invalid in ("FAIL", "BLOCKED:", "unknown"):
|
|
2330
|
+
result = subprocess.run(command + ["complete", "--verdict", invalid,
|
|
2331
|
+
"--log-file", ".devlyn/final-report.md"],
|
|
2332
|
+
cwd=work, capture_output=True, text=True, encoding="utf-8")
|
|
2333
|
+
assert result.returncode == 1 and result.stderr.strip() == (
|
|
2334
|
+
f"error: invalid verdict for phases.final_report: {invalid}"
|
|
2335
|
+
), result.stderr
|
|
2336
|
+
assert state_path.read_bytes() == before
|
|
2212
2337
|
result = subprocess.run(command + ["complete", "--verdict", "PASS_WITH_ISSUES", "--log-file", ".devlyn/final-report.md"],
|
|
2213
2338
|
cwd=work, capture_output=True, text=True, encoding="utf-8")
|
|
2214
2339
|
assert result.returncode == 0, result.stderr
|
|
@@ -2254,12 +2379,142 @@ def final_report_self_test() -> None:
|
|
|
2254
2379
|
assert (target / "pipeline.state.json").read_bytes() == completed_bytes
|
|
2255
2380
|
assert not state_path.exists() and not report.exists()
|
|
2256
2381
|
assert outside.read_bytes() == valid
|
|
2257
|
-
print("PASS iter-0126 final report:
|
|
2382
|
+
print("PASS iter-0126 final report: 14 invalid CLI completions preserve state; exact binding/archive; 6 archive refusals preserve artifacts")
|
|
2383
|
+
|
|
2384
|
+
|
|
2385
|
+
def repair_admission_self_test() -> None:
|
|
2386
|
+
stamp = "2026-01-01T00:00:00.000Z"
|
|
2387
|
+
origins = {
|
|
2388
|
+
"build_gate": ({"started_at": stamp, "completed_at": stamp, "round": 0, "verdict": "FAIL"}, "build_gate"),
|
|
2389
|
+
"cleanup": ({"started_at": stamp, "completed_at": stamp, "round": 0, "verdict": "FAIL",
|
|
2390
|
+
"execution_kind": "orchestrator_commands"}, "cleanup"),
|
|
2391
|
+
"verify": ({"started_at": stamp, "completed_at": stamp, "round": 0,
|
|
2392
|
+
"verdict": "NEEDS_WORK", "merged": {"verdict": "NEEDS_WORK"}}, "verify"),
|
|
2393
|
+
"phase_gate": ({"started_at": stamp, "completed_at": stamp, "round": 0, "verdict": "FAIL",
|
|
2394
|
+
"exec": {"total": 2, "current": 1, "statuses": ["FAIL", None]}}, None),
|
|
2395
|
+
}
|
|
2396
|
+
for origin, (entry, trigger) in origins.items():
|
|
2397
|
+
source = "implement" if origin == "phase_gate" else origin
|
|
2398
|
+
initial = {"phases": {source: entry}, "rounds": {"global": 0, "max_rounds": 1}}
|
|
2399
|
+
for wrong in ((None, "plan", "verify") if trigger else ("plan", "verify")):
|
|
2400
|
+
if wrong == trigger:
|
|
2401
|
+
continue
|
|
2402
|
+
candidate = copy.deepcopy(initial)
|
|
2403
|
+
try:
|
|
2404
|
+
do_spawn(candidate, "implement", 1, wrong, None, None, None)
|
|
2405
|
+
except SystemExit as exc:
|
|
2406
|
+
assert "repair-trigger-mismatch" in str(exc), (origin, wrong, exc)
|
|
2407
|
+
else:
|
|
2408
|
+
raise AssertionError((origin, wrong))
|
|
2409
|
+
for malformed in ({"global": True, "max_rounds": 1}, {"global": -1, "max_rounds": 1},
|
|
2410
|
+
{"global": 0, "max_rounds": True}, {"global": 0, "max_rounds": 0}, None):
|
|
2411
|
+
candidate = copy.deepcopy(initial)
|
|
2412
|
+
candidate["rounds"] = malformed
|
|
2413
|
+
try:
|
|
2414
|
+
do_spawn(candidate, "implement", 1, trigger, None, None, None)
|
|
2415
|
+
except SystemExit as exc:
|
|
2416
|
+
assert str(exc) == "BLOCKED:rounds-malformed"
|
|
2417
|
+
else:
|
|
2418
|
+
raise AssertionError((origin, malformed))
|
|
2419
|
+
candidate = copy.deepcopy(initial)
|
|
2420
|
+
try:
|
|
2421
|
+
do_spawn(candidate, "implement", 2, trigger, None, None, None)
|
|
2422
|
+
except SystemExit as exc:
|
|
2423
|
+
assert "implement-round-nonmonotonic" in str(exc)
|
|
2424
|
+
else:
|
|
2425
|
+
raise AssertionError((origin, "skipped round"))
|
|
2426
|
+
candidate = copy.deepcopy(initial)
|
|
2427
|
+
do_spawn(candidate, "implement", 1, trigger, None, None, None)
|
|
2428
|
+
assert candidate["rounds"]["global"] == 1, origin
|
|
2429
|
+
try:
|
|
2430
|
+
do_spawn(candidate, "implement", 1, trigger, None, None, None)
|
|
2431
|
+
except SystemExit:
|
|
2432
|
+
pass
|
|
2433
|
+
else:
|
|
2434
|
+
raise AssertionError((origin, "duplicate admission"))
|
|
2435
|
+
exhausted = copy.deepcopy(initial)
|
|
2436
|
+
exhausted["rounds"]["global"] = 1
|
|
2437
|
+
try:
|
|
2438
|
+
do_spawn(exhausted, "implement", 1, trigger, None, None, None)
|
|
2439
|
+
except SystemExit as exc:
|
|
2440
|
+
assert str(exc) == f"BLOCKED:repair-budget-exhausted: global=1 max_rounds=1 origin={origin}"
|
|
2441
|
+
else:
|
|
2442
|
+
raise AssertionError((origin, "shared exhaustion"))
|
|
2443
|
+
phased = {"rounds": {"global": 0, "max_rounds": 1}, "phases": {}}
|
|
2444
|
+
do_spawn(phased, "implement", 0, None, None, None, None)
|
|
2445
|
+
phased["phases"]["implement"].update({"completed_at": stamp, "verdict": "PASS",
|
|
2446
|
+
"exec": {"total": 2, "current": 2, "statuses": ["PASS", None]}})
|
|
2447
|
+
do_spawn(phased, "implement", 1, None, None, None, None)
|
|
2448
|
+
assert phased["rounds"]["global"] == 0
|
|
2449
|
+
do_complete(phased, "implement", "FAIL", None, None, None, None, None)
|
|
2450
|
+
assert phased["phases"]["implement"]["exec"]["statuses"] == ["PASS", "FAIL"]
|
|
2451
|
+
do_spawn(phased, "implement", 2, None, None, None, None)
|
|
2452
|
+
assert phased["rounds"]["global"] == 1
|
|
2453
|
+
do_complete(phased, "implement", "PASS", None, None, None, None, None)
|
|
2454
|
+
do_spawn(phased, "verify", 2, None, None, None, None)
|
|
2455
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
2456
|
+
transition_state = {"rounds": {"global": 0, "max_rounds": 1}, "phases": {}}
|
|
2457
|
+
do_spawn(transition_state, "implement", 0, None, None, None, None)
|
|
2458
|
+
transition_state["phases"]["implement"]["exec"] = {
|
|
2459
|
+
"total": 2, "current": 1, "statuses": [None, None],
|
|
2460
|
+
}
|
|
2461
|
+
transitioned = do_transition(
|
|
2462
|
+
transition_state, "implement", "implement", "FAIL", None, None, None,
|
|
2463
|
+
None, None, None, pathlib.Path(tmp), 1, None, None, None, None,
|
|
2464
|
+
)
|
|
2465
|
+
assert transition_state["rounds"]["global"] == 0
|
|
2466
|
+
assert transitioned["rounds"]["global"] == 1
|
|
2467
|
+
assert transitioned["phases"]["implement"]["exec"]["statuses"][0] == "FAIL"
|
|
2468
|
+
print("PASS repair admission: four origins, trigger/counter/round refusal, phased invocation and last repair")
|
|
2469
|
+
|
|
2470
|
+
|
|
2471
|
+
def cleanup_origin_phase_gate_reentry_self_test() -> None:
|
|
2472
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
2473
|
+
stamp = "2026-01-01T00:00:00.000Z"
|
|
2474
|
+
state = {"version": "3.0", "rounds": {"global": 1, "max_rounds": 4}, "phases": {
|
|
2475
|
+
"cleanup": {"started_at": stamp, "completed_at": stamp, "round": 0,
|
|
2476
|
+
"verdict": "FAIL", "execution_kind": "orchestrator_commands"},
|
|
2477
|
+
"implement": {"started_at": stamp, "completed_at": None, "round": 1,
|
|
2478
|
+
"triggered_by": "cleanup", "verdict": None,
|
|
2479
|
+
"exec": {"total": 2, "current": 1, "statuses": [None, None]}},
|
|
2480
|
+
}}
|
|
2481
|
+
try:
|
|
2482
|
+
do_transition(
|
|
2483
|
+
state, "implement", "verify", "PASS", None, None, None,
|
|
2484
|
+
None, None, None, pathlib.Path(tmp), 1, None, None, None, None,
|
|
2485
|
+
)
|
|
2486
|
+
except SystemExit as exc:
|
|
2487
|
+
assert str(exc) == "BLOCKED:owner-cleanup-repair-route: next phase must be build_gate", exc
|
|
2488
|
+
else:
|
|
2489
|
+
raise AssertionError("cleanup repair bypassed BUILD_GATE")
|
|
2490
|
+
transitioned = do_transition(
|
|
2491
|
+
state, "implement", "implement", "FAIL", None, None, None,
|
|
2492
|
+
None, None, None, pathlib.Path(tmp), 2, None, None, None, None,
|
|
2493
|
+
)
|
|
2494
|
+
assert transitioned["rounds"]["global"] == 2
|
|
2495
|
+
assert transitioned["phases"]["implement"]["exec"]["statuses"] == ["FAIL", None]
|
|
2496
|
+
assert transitioned["phases"]["implement"]["triggered_by"] == "cleanup"
|
|
2497
|
+
try:
|
|
2498
|
+
do_transition(
|
|
2499
|
+
transitioned, "implement", "verify", "PASS", None, None, None,
|
|
2500
|
+
None, None, None, pathlib.Path(tmp), 2, None, None, None, None,
|
|
2501
|
+
)
|
|
2502
|
+
except SystemExit as exc:
|
|
2503
|
+
assert str(exc) == "BLOCKED:owner-cleanup-repair-route: next phase must be build_gate", exc
|
|
2504
|
+
else:
|
|
2505
|
+
raise AssertionError("phase-gate retry bypassed BUILD_GATE")
|
|
2506
|
+
spawned = copy.deepcopy(state)
|
|
2507
|
+
do_complete(spawned, "implement", "FAIL", None, None, None, None, None)
|
|
2508
|
+
do_spawn(spawned, "implement", 2, None, None, None, None)
|
|
2509
|
+
assert spawned["rounds"]["global"] == 2
|
|
2510
|
+
assert spawned["phases"]["implement"]["triggered_by"] == "cleanup"
|
|
2258
2511
|
|
|
2259
2512
|
|
|
2260
2513
|
def self_test() -> int:
|
|
2261
2514
|
import time
|
|
2262
2515
|
|
|
2516
|
+
repair_admission_self_test()
|
|
2517
|
+
cleanup_origin_phase_gate_reentry_self_test()
|
|
2263
2518
|
final_report_self_test()
|
|
2264
2519
|
with tempfile.TemporaryDirectory() as tmp:
|
|
2265
2520
|
work = pathlib.Path(tmp); devlyn = work / ".devlyn"; devlyn.mkdir()
|
|
@@ -2535,7 +2790,7 @@ def self_test() -> int:
|
|
|
2535
2790
|
assert archived_legacy == old_receipt
|
|
2536
2791
|
print("PASS iter-0089 PLAN ledger: P-0089-1/2/6")
|
|
2537
2792
|
|
|
2538
|
-
write_state(state_path, {"phases": {}})
|
|
2793
|
+
write_state(state_path, {"phases": {}, "rounds": {"global": 0, "max_rounds": 4}})
|
|
2539
2794
|
|
|
2540
2795
|
# Round 0: spawn -> complete.
|
|
2541
2796
|
state = read_state(state_path)
|
|
@@ -2555,9 +2810,14 @@ def self_test() -> int:
|
|
|
2555
2810
|
expected_ms = round((parse_iso(entry["completed_at"]) - parse_iso(entry["started_at"])).total_seconds() * 1000)
|
|
2556
2811
|
assert entry["duration_ms"] == expected_ms, (entry["duration_ms"], expected_ms)
|
|
2557
2812
|
|
|
2558
|
-
# Round 1:
|
|
2813
|
+
# Round 1: VERIFY finding admits a fix-loop respawn.
|
|
2559
2814
|
time.sleep(0.05)
|
|
2560
2815
|
state = read_state(state_path)
|
|
2816
|
+
do_spawn(state, "verify", 0, None, None, None, None)
|
|
2817
|
+
state["phases"]["verify"].update({
|
|
2818
|
+
"completed_at": now_iso(), "verdict": "NEEDS_WORK",
|
|
2819
|
+
"merged": {"verdict": "NEEDS_WORK"},
|
|
2820
|
+
})
|
|
2561
2821
|
do_spawn(state, "implement", 1, "verify", None, None, None)
|
|
2562
2822
|
write_state(state_path, state)
|
|
2563
2823
|
respawned = read_state(state_path)["phases"]["implement"]
|
|
@@ -2596,6 +2856,10 @@ def self_test() -> int:
|
|
|
2596
2856
|
evidence_spec_dir = evidence_work / "docs" / "evidence"
|
|
2597
2857
|
evidence_devlyn.mkdir(parents=True)
|
|
2598
2858
|
evidence_spec_dir.mkdir(parents=True)
|
|
2859
|
+
subprocess.run(["git", "init", "-q"], cwd=evidence_work, check=True)
|
|
2860
|
+
subprocess.run(["git", "config", "user.name", "Evidence test"], cwd=evidence_work, check=True)
|
|
2861
|
+
subprocess.run(["git", "config", "user.email", "evidence@example.invalid"], cwd=evidence_work, check=True)
|
|
2862
|
+
(evidence_work / ".gitignore").write_text(".devlyn/\n", encoding="utf-8")
|
|
2599
2863
|
(evidence_spec_dir / "spec.md").write_text("# Evidence fixture\n", encoding="utf-8")
|
|
2600
2864
|
(evidence_spec_dir / "spec.expected.json").write_text(json.dumps({
|
|
2601
2865
|
"verification_commands": [{"cmd": "printf build-gate"}],
|
|
@@ -2607,9 +2871,12 @@ def self_test() -> int:
|
|
|
2607
2871
|
"stdout_contains": ["red-before-fix"],
|
|
2608
2872
|
}],
|
|
2609
2873
|
}) + "\n", encoding="utf-8")
|
|
2874
|
+
subprocess.run(["git", "add", "."], cwd=evidence_work, check=True)
|
|
2875
|
+
subprocess.run(["git", "commit", "-qm", "fixture"], cwd=evidence_work, check=True)
|
|
2610
2876
|
evidence_state = {
|
|
2611
2877
|
"run_id": "rs-state-evidence",
|
|
2612
2878
|
"source": {"type": "spec", "spec_path": "docs/evidence/spec.md"},
|
|
2879
|
+
"rounds": {"global": 0, "max_rounds": 4},
|
|
2613
2880
|
"phases": {"implement": None, "build_gate": None},
|
|
2614
2881
|
}
|
|
2615
2882
|
do_spawn(evidence_state, "implement", 0, None, None, "codex", None)
|
|
@@ -2736,11 +3003,28 @@ def self_test() -> int:
|
|
|
2736
3003
|
return build_carrier
|
|
2737
3004
|
|
|
2738
3005
|
build_carrier_0 = write_build_gate_results()
|
|
2739
|
-
completed = build_gate_state_cli("complete", "--verdict", "
|
|
3006
|
+
completed = build_gate_state_cli("complete", "--verdict", "FAIL")
|
|
2740
3007
|
assert completed.returncode == 0, completed.stderr
|
|
2741
3008
|
completed_state = read_state(evidence_state_path)
|
|
2742
3009
|
assert completed_state["process_evidence"] == [carrier, build_carrier_0]
|
|
2743
3010
|
|
|
3011
|
+
repaired = evidence_state_cli("spawn", "--round", "1", "--triggered-by", "build_gate")
|
|
3012
|
+
assert repaired.returncode == 0, repaired.stderr
|
|
3013
|
+
captured = subprocess.run(
|
|
3014
|
+
[sys.executable, runner_script, "--devlyn-dir", ".devlyn", "run",
|
|
3015
|
+
"--phase", "implement", "--id", "red-first"],
|
|
3016
|
+
cwd=evidence_work, capture_output=True, text=True, encoding="utf-8",
|
|
3017
|
+
)
|
|
3018
|
+
assert captured.returncode == 0, captured.stderr
|
|
3019
|
+
completed = evidence_state_cli("complete", "--verdict", "PASS")
|
|
3020
|
+
assert completed.returncode == 0, completed.stderr
|
|
3021
|
+
implement_carrier_1 = read_state(evidence_state_path)["process_evidence"][-1]
|
|
3022
|
+
subprocess.run(["git", "commit", "--allow-empty", "-qm", "chore(pipeline): implement fix round 1"],
|
|
3023
|
+
cwd=evidence_work, check=True)
|
|
3024
|
+
(evidence_devlyn / "build_gate.findings.jsonl").write_text("", encoding="utf-8")
|
|
3025
|
+
durable = evidence_state_cli("durability-enforce", "--round", "1", "--origin-phase", "build_gate")
|
|
3026
|
+
assert durable.returncode == 0, durable.stderr
|
|
3027
|
+
|
|
2744
3028
|
spawned = build_gate_state_cli(
|
|
2745
3029
|
"spawn", "--round", "1", "--triggered-by", "build_gate",
|
|
2746
3030
|
)
|
|
@@ -2748,12 +3032,12 @@ def self_test() -> int:
|
|
|
2748
3032
|
build_carrier_1 = write_build_gate_results()
|
|
2749
3033
|
transitioned = build_gate_state_cli(
|
|
2750
3034
|
"transition", "--verdict", "PASS", "--next-phase", "cleanup",
|
|
2751
|
-
"--next-round", "
|
|
3035
|
+
"--next-round", "1", "--next-pre-sha", "test-pre-sha",
|
|
2752
3036
|
)
|
|
2753
3037
|
assert transitioned.returncode == 0, transitioned.stderr
|
|
2754
3038
|
build_bound_state = read_state(evidence_state_path)
|
|
2755
3039
|
assert build_bound_state["process_evidence"] == [
|
|
2756
|
-
carrier, build_carrier_0, build_carrier_1,
|
|
3040
|
+
carrier, build_carrier_0, implement_carrier_1, build_carrier_1,
|
|
2757
3041
|
]
|
|
2758
3042
|
assert build_bound_state["phases"]["cleanup"]["started_at"] is not None
|
|
2759
3043
|
print("PASS iter-0111 BUILD_GATE evidence: completion/transition state binding")
|
|
@@ -2762,6 +3046,7 @@ def self_test() -> int:
|
|
|
2762
3046
|
# bytes only, so a failed entry or capability denial could still be completed
|
|
2763
3047
|
# with caller-supplied PASS.
|
|
2764
3048
|
build_bound_state["phases"]["build_gate"] = None
|
|
3049
|
+
build_bound_state["phases"]["implement"]["round"] = 2
|
|
2765
3050
|
do_spawn(build_bound_state, "build_gate", 2, "build_gate", None, None, None)
|
|
2766
3051
|
write_state(evidence_state_path, build_bound_state)
|
|
2767
3052
|
mismatch = runner.normalize_obligation({
|
|
@@ -2807,6 +3092,7 @@ def self_test() -> int:
|
|
|
2807
3092
|
assert failed.returncode == 0, failed.stderr
|
|
2808
3093
|
|
|
2809
3094
|
failed_state = read_state(evidence_state_path)
|
|
3095
|
+
failed_state["phases"]["implement"]["round"] = 3
|
|
2810
3096
|
do_spawn(failed_state, "build_gate", 3, "build_gate", None, None, None)
|
|
2811
3097
|
write_state(evidence_state_path, failed_state)
|
|
2812
3098
|
denied = runner.normalize_obligation({
|
|
@@ -2919,7 +3205,7 @@ def self_test() -> int:
|
|
|
2919
3205
|
completed = read_state(state_file)["phases"]["build_gate"]
|
|
2920
3206
|
command_identity(completed)
|
|
2921
3207
|
assert completed["verdict"] == "PASS" and completed["completed_at"]
|
|
2922
|
-
result = cli("build_gate", "spawn", "--round", "
|
|
3208
|
+
result = cli("build_gate", "spawn", "--round", "0")
|
|
2923
3209
|
assert result.returncode == 0, result.stderr
|
|
2924
3210
|
reentered = read_state(state_file)["phases"]["build_gate"]
|
|
2925
3211
|
command_identity(reentered)
|
|
@@ -2968,6 +3254,11 @@ def self_test() -> int:
|
|
|
2968
3254
|
old = legacy_done["phases"]["build_gate"]
|
|
2969
3255
|
assert "execution_kind" not in old and old["model_effective"] is None
|
|
2970
3256
|
assert old["invocation_receipt"]["sandbox_network_access"] is True
|
|
3257
|
+
legacy_done["phases"]["implement"] = {
|
|
3258
|
+
"started_at": now_iso(), "completed_at": now_iso(), "round": 1,
|
|
3259
|
+
"verdict": "PASS",
|
|
3260
|
+
}
|
|
3261
|
+
write_state(state_file, legacy_done)
|
|
2971
3262
|
result = cli("build_gate", "spawn", "--round", "1")
|
|
2972
3263
|
assert result.returncode == 0, result.stderr
|
|
2973
3264
|
fresh = read_state(state_file)["phases"]["build_gate"]
|
|
@@ -3251,6 +3542,10 @@ def self_test() -> int:
|
|
|
3251
3542
|
print("PASS iter-0119 nonzero receipt persists BLOCKED before exit1; transition stays atomic")
|
|
3252
3543
|
|
|
3253
3544
|
candidate = read_state(state_file)
|
|
3545
|
+
candidate["phases"]["implement"] = {
|
|
3546
|
+
"started_at": now_iso(), "completed_at": now_iso(), "round": 1,
|
|
3547
|
+
"verdict": "PASS",
|
|
3548
|
+
}
|
|
3254
3549
|
do_spawn(candidate, "build_gate", 1, None, None, None, None)
|
|
3255
3550
|
write_state(state_file, candidate)
|
|
3256
3551
|
next_relative = runner.manifest_relative_path(candidate, "build_gate")
|
|
@@ -3272,6 +3567,10 @@ def self_test() -> int:
|
|
|
3272
3567
|
# A post-spec denial refresh must preserve earlier failed checks.
|
|
3273
3568
|
refresh_state = copy.deepcopy(fixture)
|
|
3274
3569
|
refresh_state["phases"]["build_gate"] = None
|
|
3570
|
+
refresh_state["phases"]["implement"] = {
|
|
3571
|
+
"started_at": now_iso(), "completed_at": now_iso(), "round": 2,
|
|
3572
|
+
"verdict": "PASS",
|
|
3573
|
+
}
|
|
3275
3574
|
do_spawn(refresh_state, "build_gate", 2, None, None, None, None, devlyn=active)
|
|
3276
3575
|
write_state(state_file, refresh_state)
|
|
3277
3576
|
refresh_relative = runner.manifest_relative_path(refresh_state, "build_gate")
|
|
@@ -3321,7 +3620,7 @@ def self_test() -> int:
|
|
|
3321
3620
|
active.mkdir(parents=True)
|
|
3322
3621
|
state_file = active / "pipeline.state.json"
|
|
3323
3622
|
results_file = active / "spec-verify.results.json"
|
|
3324
|
-
checker = pathlib.Path(__file__).with_name("spec-verify-check.py")
|
|
3623
|
+
checker = pathlib.Path(__file__).resolve().with_name("spec-verify-check.py")
|
|
3325
3624
|
env = {key: value for key, value in os.environ.items() if key not in {
|
|
3326
3625
|
"BENCH_WORKDIR", "SPEC_VERIFY_PHASE", "SPEC_VERIFY_FINDINGS_FILE",
|
|
3327
3626
|
"SPEC_VERIFY_FINDING_PREFIX",
|
|
@@ -3341,7 +3640,10 @@ def self_test() -> int:
|
|
|
3341
3640
|
fixture = {
|
|
3342
3641
|
"version": "3.0", "run_id": "rs-preflight-build-gate",
|
|
3343
3642
|
"source": {"type": "generated", "criteria_path": ".devlyn/criteria.generated.md"},
|
|
3344
|
-
"phases": {"build_gate": None
|
|
3643
|
+
"phases": {"build_gate": None, "implement": {
|
|
3644
|
+
"started_at": now_iso(), "completed_at": now_iso(),
|
|
3645
|
+
"round": round_, "verdict": "PASS",
|
|
3646
|
+
}}, "process_evidence": copy.deepcopy(history) or None,
|
|
3345
3647
|
}
|
|
3346
3648
|
do_spawn(fixture, "build_gate", round_, None, None, None, None)
|
|
3347
3649
|
write_state(state_file, fixture)
|
|
@@ -3623,7 +3925,7 @@ def self_test() -> int:
|
|
|
3623
3925
|
assert state_path.read_bytes() == open_next_before
|
|
3624
3926
|
print("PASS self-test transition open-span guard: rejected without mutation")
|
|
3625
3927
|
|
|
3626
|
-
# A completed FAIL
|
|
3928
|
+
# A completed FAIL span must be retained before a same-invocation respawn
|
|
3627
3929
|
# resets the live record.
|
|
3628
3930
|
write_state(state_path, {"phases": {}})
|
|
3629
3931
|
state = read_state(state_path)
|
|
@@ -3635,7 +3937,7 @@ def self_test() -> int:
|
|
|
3635
3937
|
write_state(state_path, state)
|
|
3636
3938
|
failed_round = read_state(state_path)["phases"]["build_gate"]
|
|
3637
3939
|
state = read_state(state_path)
|
|
3638
|
-
do_spawn(state, "build_gate",
|
|
3940
|
+
do_spawn(state, "build_gate", 0, None, None, None, None)
|
|
3639
3941
|
write_state(state_path, state)
|
|
3640
3942
|
respawned_fail = read_state(state_path)["phases"]["build_gate"]
|
|
3641
3943
|
assert respawned_fail["verdict"] is None
|
|
@@ -3671,7 +3973,7 @@ def self_test() -> int:
|
|
|
3671
3973
|
assert "owned by verify-merge-findings.py" in str(e)
|
|
3672
3974
|
else:
|
|
3673
3975
|
raise AssertionError("complete() must reject an explicit --verdict for VERIFY")
|
|
3674
|
-
do_spawn(state, "verify",
|
|
3976
|
+
do_spawn(state, "verify", 0, None, None, None, None)
|
|
3675
3977
|
assert state["phases"]["verify"]["judge_durations_ms"] is None
|
|
3676
3978
|
|
|
3677
3979
|
# Non-VERIFY phases require --verdict explicitly; complete() must not
|
|
@@ -3740,21 +4042,21 @@ def self_test() -> int:
|
|
|
3740
4042
|
assert (devlyn / "codex-primary-judge.prompt.md").exists(), "judge prompts must survive VERIFY spawn"
|
|
3741
4043
|
assert (devlyn / "spec-verify.json").exists(), "non-VERIFY-round files must survive"
|
|
3742
4044
|
|
|
3743
|
-
#
|
|
4045
|
+
# Normal phase-gated IMPLEMENT advancement must preserve `exec`
|
|
3744
4046
|
# (routing truth for large runs, state-schema.md line 55) — spawn
|
|
3745
4047
|
# merges into the existing entry rather than replacing it wholesale.
|
|
3746
4048
|
write_state(state_path, {"phases": {}})
|
|
3747
4049
|
state = read_state(state_path)
|
|
3748
4050
|
do_spawn(state, "implement", 0, None, None, "claude", None)
|
|
3749
4051
|
state["phases"]["implement"]["exec"] = {
|
|
3750
|
-
"total": 3, "current": 3, "statuses": ["PASS", "PASS",
|
|
4052
|
+
"total": 3, "current": 3, "statuses": ["PASS", "PASS", None], "commits": ["a", "b", "c"],
|
|
3751
4053
|
}
|
|
3752
4054
|
write_state(state_path, state)
|
|
3753
4055
|
state = read_state(state_path)
|
|
3754
4056
|
do_complete(state, "implement", "PASS", None, None, None, None, None)
|
|
3755
4057
|
write_state(state_path, state)
|
|
3756
4058
|
state = read_state(state_path)
|
|
3757
|
-
do_spawn(state, "implement", 1,
|
|
4059
|
+
do_spawn(state, "implement", 1, None, None, None, None)
|
|
3758
4060
|
write_state(state_path, state)
|
|
3759
4061
|
respawned_exec = read_state(state_path)["phases"]["implement"]
|
|
3760
4062
|
assert respawned_exec["exec"]["current"] == 3, "spawn must not clobber unowned fields like exec"
|
|
@@ -3765,6 +4067,7 @@ def self_test() -> int:
|
|
|
3765
4067
|
# Existing history is append-only; a respawn must not clobber prior
|
|
3766
4068
|
# entries that were already preserved from older rounds.
|
|
3767
4069
|
write_state(state_path, {
|
|
4070
|
+
"rounds": {"global": 0, "max_rounds": 4},
|
|
3768
4071
|
"phases": {
|
|
3769
4072
|
"implement": {
|
|
3770
4073
|
"started_at": "2026-01-01T00:00:02.000Z",
|
|
@@ -3773,6 +4076,7 @@ def self_test() -> int:
|
|
|
3773
4076
|
"round": 2,
|
|
3774
4077
|
"triggered_by": "verify",
|
|
3775
4078
|
"verdict": "FAIL",
|
|
4079
|
+
"exec": {"total": 2, "current": 2, "statuses": ["PASS", "FAIL"]},
|
|
3776
4080
|
"engine": "codex",
|
|
3777
4081
|
"history": [{
|
|
3778
4082
|
"started_at": "2026-01-01T00:00:00.000Z",
|
|
@@ -3784,7 +4088,7 @@ def self_test() -> int:
|
|
|
3784
4088
|
}
|
|
3785
4089
|
})
|
|
3786
4090
|
state = read_state(state_path)
|
|
3787
|
-
do_spawn(state, "implement", 3,
|
|
4091
|
+
do_spawn(state, "implement", 3, None, None, None, None)
|
|
3788
4092
|
write_state(state_path, state)
|
|
3789
4093
|
history_preserved = read_state(state_path)["phases"]["implement"]["history"]
|
|
3790
4094
|
assert len(history_preserved) == 2
|
|
@@ -4670,9 +4974,13 @@ def self_test() -> int:
|
|
|
4670
4974
|
)
|
|
4671
4975
|
assert reroute_error and "model reroute" in reroute_error
|
|
4672
4976
|
assert rerouted_state["phases"]["implement"]["verdict"] == "BLOCKED"
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4977
|
+
try:
|
|
4978
|
+
do_spawn(rerouted_state, "implement", 1, None, None, "codex", receipt_model,
|
|
4979
|
+
prompt_sha256=receipt_prompt_sha, devlyn=receipt_devlyn)
|
|
4980
|
+
except SystemExit as exc:
|
|
4981
|
+
assert str(exc) == "BLOCKED:repair-edge-invalid"
|
|
4982
|
+
else:
|
|
4983
|
+
raise AssertionError("BLOCKED IMPLEMENT was admitted as a product repair")
|
|
4676
4984
|
|
|
4677
4985
|
plan_prompt = receipt_devlyn / "plan.prompt.0"
|
|
4678
4986
|
plan_prompt.write_text("plan exactly\n", encoding="utf-8")
|
|
@@ -5158,7 +5466,10 @@ def self_test() -> int:
|
|
|
5158
5466
|
{"a.txt": "old\n"}, "build_gate",
|
|
5159
5467
|
[{"id": "BG", "file": "a.txt", "line": 8}],
|
|
5160
5468
|
)
|
|
5161
|
-
build_state["phases"]["implement"] = {
|
|
5469
|
+
build_state["phases"]["implement"] = {
|
|
5470
|
+
"started_at": now_iso(), "completed_at": now_iso(), "round": 1,
|
|
5471
|
+
"triggered_by": "build_gate", "verdict": "PASS",
|
|
5472
|
+
}
|
|
5162
5473
|
write_state(build_devlyn / "pipeline.state.json", build_state)
|
|
5163
5474
|
subprocess.run(
|
|
5164
5475
|
[sys.executable, str(pathlib.Path(__file__).resolve()), "--devlyn-dir", ".devlyn",
|
|
@@ -5415,7 +5726,7 @@ def self_test() -> int:
|
|
|
5415
5726
|
return 0
|
|
5416
5727
|
|
|
5417
5728
|
|
|
5418
|
-
def
|
|
5729
|
+
def _main_unlocked() -> int:
|
|
5419
5730
|
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
5420
5731
|
ap.add_argument("--devlyn-dir", default=".devlyn")
|
|
5421
5732
|
ap.add_argument("--phase", choices=sorted(PHASE_NAMES))
|
|
@@ -5435,7 +5746,7 @@ def main() -> int:
|
|
|
5435
5746
|
spawn_p.add_argument("--model", default=None)
|
|
5436
5747
|
|
|
5437
5748
|
complete_p = sub.add_parser("complete")
|
|
5438
|
-
complete_p.add_argument("--verdict",
|
|
5749
|
+
complete_p.add_argument("--verdict", default=None)
|
|
5439
5750
|
complete_p.add_argument("--post-sha", default=None)
|
|
5440
5751
|
complete_p.add_argument("--findings-file", default=None)
|
|
5441
5752
|
complete_p.add_argument("--log-file", default=None)
|
|
@@ -5444,7 +5755,7 @@ def main() -> int:
|
|
|
5444
5755
|
complete_p.add_argument("--engine-session-log", default=None)
|
|
5445
5756
|
|
|
5446
5757
|
transition_p = sub.add_parser("transition")
|
|
5447
|
-
transition_p.add_argument("--verdict",
|
|
5758
|
+
transition_p.add_argument("--verdict", default=None)
|
|
5448
5759
|
transition_p.add_argument("--post-sha", default=None)
|
|
5449
5760
|
transition_p.add_argument("--findings-file", default=None)
|
|
5450
5761
|
transition_p.add_argument("--log-file", default=None)
|
|
@@ -5658,6 +5969,21 @@ def main() -> int:
|
|
|
5658
5969
|
return 0
|
|
5659
5970
|
|
|
5660
5971
|
|
|
5972
|
+
def main() -> int:
|
|
5973
|
+
if "--self-test" in sys.argv[1:]:
|
|
5974
|
+
return _main_unlocked()
|
|
5975
|
+
selector = argparse.ArgumentParser(add_help=False)
|
|
5976
|
+
selector.add_argument("--devlyn-dir", default=".devlyn")
|
|
5977
|
+
known, _ = selector.parse_known_args()
|
|
5978
|
+
devlyn = pathlib.Path(known.devlyn_dir).resolve()
|
|
5979
|
+
if not devlyn.is_dir():
|
|
5980
|
+
sys.stderr.write(f"error: {devlyn} is not a directory\n")
|
|
5981
|
+
return 1
|
|
5982
|
+
lock = runpy.run_path(str(pathlib.Path(__file__).with_name("platform-support.py")))["file_lock"]
|
|
5983
|
+
with lock(devlyn / "pipeline.state.lock", blocking=True):
|
|
5984
|
+
return _main_unlocked()
|
|
5985
|
+
|
|
5986
|
+
|
|
5661
5987
|
if __name__ == "__main__":
|
|
5662
5988
|
runpy.run_path(str(pathlib.Path(__file__).with_name("platform-support.py")))["configure_utf8"]()
|
|
5663
5989
|
raise SystemExit(main())
|