openlanes 3.0.0 → 3.1.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.
- checksums.yaml +4 -4
- data/Brewfile +42 -0
- data/agents/bb8-resolver.md +49 -0
- data/agents/c3po-operator.md +62 -0
- data/agents/chewie-verifier.md +85 -0
- data/agents/hansolo-reviewer.md +20 -15
- data/agents/lando-broker.md +81 -19
- data/agents/leah-researcher.md +6 -6
- data/agents/luke-backend.md +41 -20
- data/agents/{palpatine-planner.md → luthen-rael-planner.md} +15 -13
- data/agents/palpatine-risk-assessor.md +112 -0
- data/agents/r2d2-mechanic.md +5 -1
- data/agents/rey-frontend.md +38 -19
- data/agents/yoda-writer.md +7 -7
- data/evals/cases/chewie-verifier/verify-medium/fail/plan/plan.md +7 -0
- data/evals/cases/chewie-verifier/verify-medium/fail/plan/pull-requests.md +10 -0
- data/evals/cases/chewie-verifier/verify-medium/fail/plan/spec.md +5 -0
- data/evals/cases/chewie-verifier/verify-medium/fail/plan/verification/1/final.png +0 -0
- data/evals/cases/chewie-verifier/verify-medium/fail/plan/verification/1/report.md +7 -0
- data/evals/cases/chewie-verifier/verify-medium/fail/repo/bin/greet +1 -0
- data/evals/cases/chewie-verifier/verify-medium/fail/run.json +1 -0
- data/evals/cases/chewie-verifier/verify-medium/pass/plan/plan.md +7 -0
- data/evals/cases/chewie-verifier/verify-medium/pass/plan/pull-requests.md +10 -0
- data/evals/cases/chewie-verifier/verify-medium/pass/plan/spec.md +5 -0
- data/evals/cases/chewie-verifier/verify-medium/pass/plan/verification/1/final.png +0 -0
- data/evals/cases/chewie-verifier/verify-medium/pass/plan/verification/1/report.md +14 -0
- data/evals/cases/chewie-verifier/verify-medium/pass/plan/verification/1/step-01.png +0 -0
- data/evals/cases/chewie-verifier/verify-medium/pass/repo/bin/greet +1 -0
- data/evals/cases/chewie-verifier/verify-medium/pass/run.json +1 -0
- data/evals/cases/chewie-verifier/verify-medium.yml +48 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/fail/plan/implementation-plan.md +5 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/fail/plan/plan.md +3 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/fail/plan/pull-requests.md +12 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/fail/plan/spec.md +5 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/fail/repo/lib/cart.rb +10 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/fail/repo/spec/cart_spec.rb +9 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/fail/run.json +1 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/pass/plan/implementation-plan.md +5 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/pass/plan/plan.md +3 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/pass/plan/pull-requests.md +15 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/pass/plan/spec.md +5 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/pass/repo/lib/cart.rb +10 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/pass/repo/spec/cart_spec.rb +9 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum/pass/run.json +1 -0
- data/evals/cases/hansolo-reviewer/coverage-minimum.yml +76 -0
- data/evals/cases/luthen-rael-planner/plan-deep/fail/plan/plan.md +8 -0
- data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep/pass/plan/plan.md +2 -2
- data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep.yml +1 -1
- data/evals/cases/luthen-rael-planner/plan-fast/fail/plan/plan.md +12 -0
- data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/pass/plan/plan.md +2 -2
- data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast.yml +1 -1
- data/evals/cases/palpatine-risk-assessor/risk-medium/fail/plan/plan.md +20 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/fail/plan/pull-requests.md +5 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/fail/plan/spec.md +5 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/fail/repo/db/migrate/20261020120000_drop_legacy_email.rb +5 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/fail/run.json +1 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/pass/plan/plan.md +25 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/pass/plan/pull-requests.md +5 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/pass/plan/spec.md +5 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/pass/repo/db/migrate/20261020120000_drop_legacy_email.rb +5 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium/pass/run.json +1 -0
- data/evals/cases/palpatine-risk-assessor/risk-medium.yml +47 -0
- data/evals/cases/yoda-writer/spec-medium/pass/plan/spec.md +1 -1
- data/lib/openlanes/adapters/base.rb +24 -3
- data/lib/openlanes/adapters/capabilities.rb +29 -0
- data/lib/openlanes/adapters/claude.rb +32 -10
- data/lib/openlanes/adapters/codex.rb +9 -0
- data/lib/openlanes/adapters/pi.rb +10 -0
- data/lib/openlanes/adapters/stream.rb +145 -1
- data/lib/openlanes/adapters.rb +26 -2
- data/lib/openlanes/agents/agent.rb +4 -3
- data/lib/openlanes/agents/roster.rb +14 -2
- data/lib/openlanes/agents/routing.rb +43 -3
- data/lib/openlanes/apps/definition.rb +152 -0
- data/lib/openlanes/apps/framing.rb +114 -0
- data/lib/openlanes/apps/listing.rb +60 -0
- data/lib/openlanes/apps/ports.rb +168 -0
- data/lib/openlanes/apps/probe.rb +63 -0
- data/lib/openlanes/apps/process_spec.rb +39 -0
- data/lib/openlanes/apps/runner.rb +174 -0
- data/lib/openlanes/apps/status.rb +62 -0
- data/lib/openlanes/apps/supervisor.rb +277 -0
- data/lib/openlanes/apps/watcher.rb +50 -0
- data/lib/openlanes/apps.rb +41 -0
- data/lib/openlanes/cli/api/api.rb +10 -0
- data/lib/openlanes/cli/api/subcommands/docs.rb +63 -0
- data/lib/openlanes/cli/app/app.rb +31 -0
- data/lib/openlanes/cli/app/subcommands/start.rb +114 -0
- data/lib/openlanes/cli/app/subcommands/status.rb +38 -0
- data/lib/openlanes/cli/app/subcommands/stop.rb +44 -0
- data/lib/openlanes/cli/base.rb +89 -16
- data/lib/openlanes/cli/coverage/coverage.rb +68 -0
- data/lib/openlanes/cli/create/create.rb +11 -265
- data/lib/openlanes/cli/eval/eval.rb +4 -1
- data/lib/openlanes/cli/init/init.rb +39 -2
- data/lib/openlanes/cli/linear/subcommands/import.rb +1 -1
- data/lib/openlanes/cli/linear/subcommands/projects.rb +1 -1
- data/lib/openlanes/cli/list_plans/list_plans.rb +8 -2
- data/lib/openlanes/cli/mail/subcommands/ack.rb +52 -0
- data/lib/openlanes/cli/mail/subcommands/render.rb +31 -0
- data/lib/openlanes/cli/mail/subcommands/send.rb +26 -0
- data/lib/openlanes/cli/resync/subcommands/dirs.rb +53 -12
- data/lib/openlanes/cli/resync/subcommands/prs.rb +8 -69
- data/lib/openlanes/cli/review/review.rb +72 -0
- data/lib/openlanes/cli/run/run.rb +22 -454
- data/lib/openlanes/cli/setup/setup.rb +51 -0
- data/lib/openlanes/cli/start/start.rb +7 -64
- data/lib/openlanes/cli/state/state.rb +1 -1
- data/lib/openlanes/cli/state/subcommands/resume.rb +61 -0
- data/lib/openlanes/cli/state/subcommands/sign.rb +1 -1
- data/lib/openlanes/cli/unblock/unblock.rb +11 -205
- data/lib/openlanes/cli/web/web.rb +94 -8
- data/lib/openlanes/cli/worktree/worktree.rb +1 -1
- data/lib/openlanes/cli/worktree/worktree_prune.rb +77 -0
- data/lib/openlanes/cli.rb +37 -2
- data/lib/openlanes/coverage/cobertura.rb +49 -0
- data/lib/openlanes/coverage/config.rb +194 -0
- data/lib/openlanes/coverage/istanbul.rb +31 -0
- data/lib/openlanes/coverage/lcov.rb +38 -0
- data/lib/openlanes/coverage/report.rb +71 -0
- data/lib/openlanes/coverage/simplecov.rb +70 -0
- data/lib/openlanes/coverage.rb +33 -0
- data/lib/openlanes/engine/agent_locks.rb +231 -0
- data/lib/openlanes/engine/arbitration.rb +25 -0
- data/lib/openlanes/engine/board.rb +36 -5
- data/lib/openlanes/engine/conversions.rb +160 -0
- data/lib/openlanes/engine/coverage_gate.rb +217 -0
- data/lib/openlanes/engine/dispatcher.rb +1229 -116
- data/lib/openlanes/engine/event_journal/replay.rb +96 -0
- data/lib/openlanes/engine/event_journal.rb +148 -0
- data/lib/openlanes/engine/extensions.rb +271 -0
- data/lib/openlanes/engine/merging.rb +459 -0
- data/lib/openlanes/engine/narrator.rb +107 -0
- data/lib/openlanes/engine/plan_locks.rb +114 -0
- data/lib/openlanes/engine/pr_outcomes.rb +161 -0
- data/lib/openlanes/engine/pr_watch.rb +83 -0
- data/lib/openlanes/engine/progress_log.rb +38 -105
- data/lib/openlanes/engine/quality_question.rb +125 -0
- data/lib/openlanes/engine/rebase.rb +200 -0
- data/lib/openlanes/engine/run_workflow.rb +776 -0
- data/lib/openlanes/engine/runner.rb +186 -15
- data/lib/openlanes/engine/start_workflow.rb +77 -0
- data/lib/openlanes/engine/state_file.rb +101 -34
- data/lib/openlanes/engine/tally.rb +1 -1
- data/lib/openlanes/engine/verification/app_session.rb +104 -0
- data/lib/openlanes/engine/verification/driver.rb +314 -0
- data/lib/openlanes/engine/verification/outcome.rb +516 -0
- data/lib/openlanes/engine/verification/report.rb +95 -0
- data/lib/openlanes/engine/verification/round.rb +100 -0
- data/lib/openlanes/engine/verification/screenshots.rb +120 -0
- data/lib/openlanes/engine/verification.rb +57 -0
- data/lib/openlanes/engine.rb +30 -1
- data/lib/openlanes/execution/boundary.rb +212 -0
- data/lib/openlanes/execution/child.rb +4 -2
- data/lib/openlanes/execution/clock.rb +36 -6
- data/lib/openlanes/execution/control.rb +32 -0
- data/lib/openlanes/execution/executor.rb +114 -35
- data/lib/openlanes/execution/process_start.rb +63 -0
- data/lib/openlanes/execution/prompt.rb +236 -34
- data/lib/openlanes/execution/transcript.rb +159 -8
- data/lib/openlanes/execution.rb +21 -0
- data/lib/openlanes/lifecycle/adoption.rb +25 -8
- data/lib/openlanes/lifecycle/aging.rb +166 -0
- data/lib/openlanes/lifecycle/allocator.rb +216 -0
- data/lib/openlanes/lifecycle/brief.rb +32 -2
- data/lib/openlanes/lifecycle/completion.rb +12 -6
- data/lib/openlanes/lifecycle/create_workflow.rb +272 -0
- data/lib/openlanes/lifecycle/creator.rb +39 -11
- data/lib/openlanes/lifecycle/lanes.rb +1 -1
- data/lib/openlanes/lifecycle/resync.rb +170 -42
- data/lib/openlanes/lifecycle/unblock_workflow.rb +234 -0
- data/lib/openlanes/lifecycle/unblocker.rb +76 -2
- data/lib/openlanes/lifecycle.rb +37 -1
- data/lib/openlanes/linear/mapping.rb +4 -1
- data/lib/openlanes/linear/unit.rb +1 -1
- data/lib/openlanes/live/chat.rb +665 -0
- data/lib/openlanes/live/choice_converter.rb +166 -0
- data/lib/openlanes/live/choice_judge.rb +109 -0
- data/lib/openlanes/live/claims.rb +9 -1
- data/lib/openlanes/live/command.rb +13 -10
- data/lib/openlanes/live/commands.rb +221 -31
- data/lib/openlanes/live/copilot/brief.rb +128 -0
- data/lib/openlanes/live/copilot/cards.rb +226 -0
- data/lib/openlanes/live/copilot/operator.rb +259 -0
- data/lib/openlanes/live/copilot/slash.rb +82 -0
- data/lib/openlanes/live/copilot.rb +290 -0
- data/lib/openlanes/live/event_bus/base.rb +52 -0
- data/lib/openlanes/live/event_bus/memory_bus.rb +14 -0
- data/lib/openlanes/live/event_bus/redis_bus.rb +10 -0
- data/lib/openlanes/live/event_bus/transaction.rb +27 -0
- data/lib/openlanes/live/event_bus.rb +29 -0
- data/lib/openlanes/live/interviewer.rb +146 -0
- data/lib/openlanes/live/keys.rb +114 -0
- data/lib/openlanes/live/mail.rb +143 -0
- data/lib/openlanes/live/memory_store.rb +36 -1
- data/lib/openlanes/live/memory_subscriber.rb +40 -0
- data/lib/openlanes/live/news.rb +61 -0
- data/lib/openlanes/live/node.rb +20 -0
- data/lib/openlanes/live/outage.rb +225 -0
- data/lib/openlanes/live/outage_report.rb +89 -0
- data/lib/openlanes/live/publisher.rb +42 -2
- data/lib/openlanes/live/run_record.rb +39 -1
- data/lib/openlanes/live/run_state.rb +19 -2
- data/lib/openlanes/live/session.rb +38 -4
- data/lib/openlanes/live/snapshot.rb +103 -42
- data/lib/openlanes/live/stations.rb +12 -8
- data/lib/openlanes/live/store.rb +36 -1
- data/lib/openlanes/live/subscriber.rb +93 -0
- data/lib/openlanes/live.rb +71 -1
- data/lib/openlanes/plans/blocked_file.rb +117 -0
- data/lib/openlanes/plans/choices.rb +228 -0
- data/lib/openlanes/plans/dependencies.rb +304 -0
- data/lib/openlanes/plans/feature.rb +75 -16
- data/lib/openlanes/plans/journal.rb +169 -0
- data/lib/openlanes/plans/last_known_state.rb +82 -0
- data/lib/openlanes/plans/lock.rb +171 -0
- data/lib/openlanes/plans/mailbox.rb +145 -4
- data/lib/openlanes/plans/plan_state.rb +590 -9
- data/lib/openlanes/plans/pull_request_machine.rb +163 -0
- data/lib/openlanes/plans/review_label.rb +139 -0
- data/lib/openlanes/plans/spec.rb +239 -3
- data/lib/openlanes/plans/state_machine.rb +63 -30
- data/lib/openlanes/plans/status.rb +60 -11
- data/lib/openlanes/plans/subject.rb +4 -0
- data/lib/openlanes/plans/tree.rb +101 -10
- data/lib/openlanes/plans/verdict.rb +124 -0
- data/lib/openlanes/plans.rb +19 -0
- data/lib/openlanes/presentation/api_docs.rb +76 -0
- data/lib/openlanes/presentation/documentation.rb +105 -12
- data/lib/openlanes/presentation/pr_changes.rb +90 -0
- data/lib/openlanes/presentation/reporter.rb +66 -7
- data/lib/openlanes/presentation.rb +20 -1
- data/lib/openlanes/setup/check.rb +16 -0
- data/lib/openlanes/setup/machine.rb +179 -0
- data/lib/openlanes/setup.rb +34 -0
- data/lib/openlanes/support/clock.rb +57 -0
- data/lib/openlanes/support/config.rb +318 -6
- data/lib/openlanes/support/log.rb +206 -0
- data/lib/openlanes/support/refusal.rb +15 -0
- data/lib/openlanes/support/runtime.rb +61 -0
- data/lib/openlanes/support/spawner.rb +51 -0
- data/lib/openlanes/support/ui.rb +110 -21
- data/lib/openlanes/vcs/ci_gate.rb +31 -0
- data/lib/openlanes/vcs/github.rb +362 -12
- data/lib/openlanes/vcs/label_migration.rb +132 -0
- data/lib/openlanes/vcs/label_sync.rb +75 -0
- data/lib/openlanes/vcs/publisher.rb +287 -7
- data/lib/openlanes/vcs/refresh.rb +337 -0
- data/lib/openlanes/vcs/rename_date.rb +83 -0
- data/lib/openlanes/vcs/review.rb +66 -0
- data/lib/openlanes/vcs/worktree.rb +210 -14
- data/lib/openlanes/vcs.rb +18 -0
- data/lib/openlanes/version.rb +1 -1
- data/lib/openlanes/web/admin_auth.rb +104 -0
- data/lib/openlanes/web/app.rb +213 -15
- data/lib/openlanes/web/context.rb +33 -9
- data/lib/openlanes/web/demo.rb +39 -14
- data/lib/openlanes/web/demo_board.rb +63 -5
- data/lib/openlanes/web/events.rb +137 -9
- data/lib/openlanes/web/launcher.rb +70 -8
- data/lib/openlanes/web/listen.rb +133 -0
- data/lib/openlanes/web/plan_files.rb +136 -0
- data/lib/openlanes/web/request_log.rb +108 -0
- data/lib/openlanes/web/server.rb +31 -10
- data/lib/openlanes/web/stores.rb +11 -0
- data/lib/openlanes/web.rb +19 -4
- data/lib/openlanes.rb +45 -7
- data/openlanes.gemspec +9 -4
- data/schemas/journal-event.schema.json +72 -0
- data/schemas/plan-state.schema.json +163 -2
- data/skills/openlanes/SKILL.md +2 -2
- data/skills/openlanes-documents/SKILL.md +4 -4
- data/skills/openlanes-documents/references/blocked-template.md +2 -2
- data/skills/openlanes-documents/references/closing-templates.md +1 -1
- data/skills/openlanes-documents/references/plan-template.md +12 -4
- data/skills/openlanes-documents/references/retroactive-spec.md +1 -1
- data/skills/openlanes-run/SKILL.md +5 -5
- data/skills/openlanes-spec/SKILL.md +1 -1
- data/skills/openlanes-status/SKILL.md +1 -1
- data/web/dist/assets/favicon-BFJEKKAE.png +0 -0
- data/web/dist/assets/{index-Bp7W-ZTq.js → index-690ayLS4.js} +2 -2
- data/web/dist/assets/index-6ZmW4slI.js +199 -0
- data/web/dist/assets/{index-Byyxbh_z.js → index-B5KzlXp1.js} +2 -2
- data/web/dist/assets/{index-8dNhRmiO.js → index-BAo_v9bU.js} +2 -2
- data/web/dist/assets/{index-2xPC-eHt.js → index-BDm7g-xG.js} +2 -2
- data/web/dist/assets/{index-D0rmJN3h.js → index-BFH9bC7v.js} +2 -2
- data/web/dist/assets/{index-OR-odsl9.js → index-BVHPV7z4.js} +2 -2
- data/web/dist/assets/{index-C0VmkrO-.js → index-B_CPqjnA.js} +2 -2
- data/web/dist/assets/{index-DDI7zoAS.js → index-BhwBH5ct.js} +2 -2
- data/web/dist/assets/{index-BdR34cXC.js → index-Bq5AdwEt.js} +2 -2
- data/web/dist/assets/{index-uM88Njdj.js → index-CAW6ClM2.js} +2 -2
- data/web/dist/assets/index-Cp86u27u.css +1 -0
- data/web/dist/assets/{index-P3vSkiU-.js → index-Culm4pMO.js} +2 -2
- data/web/dist/assets/{index-DGLUuEXi.js → index-DElDJfp1.js} +2 -2
- data/web/dist/assets/{index-CYxcbpRM.js → index-Dja7osNW.js} +2 -2
- data/web/dist/assets/{index-popk9wbW.js → index-DjhU3FzU.js} +2 -2
- data/web/dist/assets/{index-mRV4XhJZ.js → index-Dutbxj4o.js} +2 -2
- data/web/dist/assets/{index-DB4QwuYn.js → index-RkDhPMDt.js} +2 -2
- data/web/dist/assets/{index-B7-O9ujQ.js → index-_mptZjlC.js} +2 -2
- data/web/dist/assets/logo-DXDCUiD8.png +0 -0
- data/web/dist/index.html +3 -3
- data/web/src/api/fixtures/board.json +203 -28
- metadata +1053 -881
- data/bin/factory-e2e +0 -157
- data/bin/plan-number +0 -164
- data/bin/setup +0 -26
- data/evals/cases/palpatine-planner/plan-deep/fail/plan/plan.md +0 -8
- data/evals/cases/palpatine-planner/plan-fast/fail/plan/plan.md +0 -12
- data/web/dist/assets/favicon-C8pAuqqr.png +0 -0
- data/web/dist/assets/index-CI74IkWP.js +0 -198
- data/web/dist/assets/index-a4EHR-u4.css +0 -1
- data/web/dist/assets/logo-on-dark-DI9gCdN4.png +0 -0
- data/web/dist/assets/logo-on-light-BciwqxcJ.png +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep/fail/plan/plan-backend.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep/fail/plan/spec.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep/fail/run.json +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep/pass/plan/plan-backend.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep/pass/plan/plan-frontend.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep/pass/plan/spec.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-deep/pass/run.json +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/fail/plan/plan-backend.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/fail/plan/plan-frontend.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/fail/plan/spec.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/fail/run.json +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/pass/plan/plan-backend.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/pass/plan/plan-frontend.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/pass/plan/spec.md +0 -0
- /data/evals/cases/{palpatine-planner → luthen-rael-planner}/plan-fast/pass/run.json +0 -0
- /data/exe/{depot → ol} +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenLanes
|
|
4
|
+
module Engine
|
|
5
|
+
# Sees a rebase that {Vcs::Refresh} left stopped on conflicts through to
|
|
6
|
+
# the end, with agents doing the resolving and the harness doing every
|
|
7
|
+
# git step that moves HEAD.
|
|
8
|
+
#
|
|
9
|
+
# The owner's words (decision D10): "I don't want to be manually dealing
|
|
10
|
+
# with git rebases onto main from worktrees and conflict resolution. This
|
|
11
|
+
# is the job for the agent, whichever is best suited to do it." So a
|
|
12
|
+
# conflict never becomes a question or a blocked plan. The order is:
|
|
13
|
+
#
|
|
14
|
+
# 1. the agent the dispatcher assigned, when it is one that builds, since
|
|
15
|
+
# it knows what the branch is for;
|
|
16
|
+
# 2. bb8-resolver, the agent whose only job this is;
|
|
17
|
+
# 3. abort, which restores the branch exactly, and bb8-resolver again on
|
|
18
|
+
# a later run, one step up {LADDER} each time;
|
|
19
|
+
# 4. once the ladder is spent, nothing until the default branch moves:
|
|
20
|
+
# the plan works on its old base meanwhile, and the log says so.
|
|
21
|
+
#
|
|
22
|
+
# Agents only edit and `git add`; {Execution::Executor::FORBIDDEN_COMMANDS}
|
|
23
|
+
# withholds `git rebase` and `git commit`, and HEAD stays still across
|
|
24
|
+
# each agent's run because the harness continues the rebase between
|
|
25
|
+
# runs, outside the window {Execution::Boundary} checks.
|
|
26
|
+
class Rebase
|
|
27
|
+
# The agent whose only job is a stopped rebase.
|
|
28
|
+
RESOLVER = "bb8-resolver"
|
|
29
|
+
|
|
30
|
+
# Phases whose agents wrote the branch, and so try its conflicts first.
|
|
31
|
+
IMPLEMENTER_PHASES = %w[build frontend].freeze
|
|
32
|
+
|
|
33
|
+
# Commits that may stop on conflicts in one run. A branch that conflicts
|
|
34
|
+
# on more of its commits than this is better served by an abort and a
|
|
35
|
+
# fresh, escalated attempt than by one agent grinding through the lot.
|
|
36
|
+
STOPS = 10
|
|
37
|
+
|
|
38
|
+
# What bb8-resolver runs as on each successive failure of the same
|
|
39
|
+
# conflict: its own frontmatter first, then up a model tier, then up in
|
|
40
|
+
# effort too. Its length is how many times a plan's rebase is aborted
|
|
41
|
+
# before it waits for the default branch to move.
|
|
42
|
+
LADDER = [{}, { model: "opus", effort: "high" }, { model: "opus", effort: "max" }].freeze
|
|
43
|
+
|
|
44
|
+
# How one plan's rebase has gone across runs.
|
|
45
|
+
#
|
|
46
|
+
# @!attribute [r] failures
|
|
47
|
+
# @return [Integer] aborts so far, the step on {LADDER}
|
|
48
|
+
# @!attribute [r] held_at
|
|
49
|
+
# @return [String, nil] the default-branch commit the ladder ran out on
|
|
50
|
+
Tally = Data.define(:failures, :held_at)
|
|
51
|
+
|
|
52
|
+
# @param agents [OpenLanes::Agents::Registry]
|
|
53
|
+
# @param executor [#call] {Execution::Executor}, or the suite's stand-in
|
|
54
|
+
def initialize(agents:, executor:)
|
|
55
|
+
@agents = agents
|
|
56
|
+
@executor = executor
|
|
57
|
+
@mutex = Mutex.new
|
|
58
|
+
@tallies = {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# The default-branch commit a plan's rebase is waiting to see move past.
|
|
62
|
+
#
|
|
63
|
+
# @param ordinal [#to_s]
|
|
64
|
+
# @return [String, nil]
|
|
65
|
+
def hold(ordinal) = tally(ordinal).held_at
|
|
66
|
+
|
|
67
|
+
# @param ordinal [#to_s]
|
|
68
|
+
# @return [OpenLanes::Engine::Rebase::Tally]
|
|
69
|
+
def tally(ordinal) = @mutex.synchronize { @tallies.fetch(ordinal.to_s) { Tally.new(failures: 0, held_at: nil) } }
|
|
70
|
+
|
|
71
|
+
# Who resolves, in order, given how often this plan's rebase has failed.
|
|
72
|
+
# The assigned agent goes first only on a first attempt: once it has
|
|
73
|
+
# failed on a conflict, the escalated resolver is the better bet.
|
|
74
|
+
#
|
|
75
|
+
# @param agent [OpenLanes::Agents::Agent] the one the dispatcher assigned
|
|
76
|
+
# @param ordinal [#to_s]
|
|
77
|
+
# @return [Array<OpenLanes::Agents::Agent>]
|
|
78
|
+
def resolvers(agent, ordinal)
|
|
79
|
+
failures = tally(ordinal).failures
|
|
80
|
+
first = failures.zero? && agent.name != RESOLVER && IMPLEMENTER_PHASES.include?(agent.phase) ? [agent] : []
|
|
81
|
+
step = LADDER[failures]
|
|
82
|
+
resolver = @agents.find(RESOLVER)
|
|
83
|
+
first + (step && resolver ? [resolver.with(**step)] : [])
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Resolve, continue, repeat, until the rebase ends one way or the other.
|
|
87
|
+
#
|
|
88
|
+
# @param task [OpenLanes::Engine::Runner::Task] its `refresh` is the
|
|
89
|
+
# stopped rebase
|
|
90
|
+
# @param handle [OpenLanes::Execution::Executor::Handle]
|
|
91
|
+
# @yieldparam progress [OpenLanes::Execution::Transcript::Progress]
|
|
92
|
+
# @return [OpenLanes::Execution::Executor::Result] the spend of every
|
|
93
|
+
# resolver run, and one line on how it ended
|
|
94
|
+
def resolve(task, handle:, &on_progress)
|
|
95
|
+
ordinal = task.subject.feature.ordinal.to_s
|
|
96
|
+
stopped = task.refresh
|
|
97
|
+
refresh = Vcs::Refresh.new(path: task.root)
|
|
98
|
+
renew(ordinal, stopped.target)
|
|
99
|
+
spent = []
|
|
100
|
+
stops = 1
|
|
101
|
+
why = "no resolver could run"
|
|
102
|
+
|
|
103
|
+
resolvers(task.agent, ordinal).each do |resolver|
|
|
104
|
+
loop do
|
|
105
|
+
UI.log("rebase: #{resolver.name} resolving #{stopped.files.join(", ")}", plan: ordinal)
|
|
106
|
+
result = @executor.call(resolver,
|
|
107
|
+
task.subject,
|
|
108
|
+
root: task.root, round: task.round, handle:,
|
|
109
|
+
rebase: stopped,
|
|
110
|
+
&on_progress)
|
|
111
|
+
spent << result
|
|
112
|
+
return finish(ordinal, spent, why: "stopped by request", refresh:) if handle.killed
|
|
113
|
+
return finish(ordinal, spent, why: "#{resolver.name}: #{result.note}", refresh:) unless refresh.rebasing?
|
|
114
|
+
|
|
115
|
+
left = refresh.unresolved
|
|
116
|
+
unless result.ok && left.empty?
|
|
117
|
+
why = result.ok ? "#{resolver.name} left #{left.join(", ")} unresolved" : "#{resolver.name}: #{result.note}"
|
|
118
|
+
stopped = stopped.with(files: left.empty? ? stopped.files : left)
|
|
119
|
+
break
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
after = refresh.continue
|
|
123
|
+
return finish(ordinal, spent, resolved_by: resolver.name, base: stopped.base, stops:) if after.kind == :rebased
|
|
124
|
+
return finish(ordinal, spent, why: after.reason, refresh:) unless after.conflicted?
|
|
125
|
+
|
|
126
|
+
stops += 1
|
|
127
|
+
return finish(ordinal, spent, why: "more than #{STOPS} commits conflicted", refresh:) if stops > STOPS
|
|
128
|
+
|
|
129
|
+
stopped = after
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
finish(ordinal, spent, why:, refresh:)
|
|
133
|
+
rescue StandardError => e
|
|
134
|
+
finish(ordinal, spent || [], why: "#{e.class}: #{e.message}", refresh:)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
# The default branch moved since the ladder ran out: start it again.
|
|
140
|
+
#
|
|
141
|
+
# @return [void]
|
|
142
|
+
def renew(ordinal, target)
|
|
143
|
+
@mutex.synchronize do
|
|
144
|
+
held = @tallies[ordinal]&.held_at
|
|
145
|
+
@tallies.delete(ordinal) if held && held != target
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @param resolved_by [String, nil] who resolved the last conflict, on success
|
|
150
|
+
# @param why [String, nil] why it failed, otherwise
|
|
151
|
+
# @param refresh [OpenLanes::Vcs::Refresh, nil] aborted on failure
|
|
152
|
+
# @return [OpenLanes::Execution::Executor::Result]
|
|
153
|
+
def finish(ordinal, spent, resolved_by: nil, base: nil, stops: 1, why: nil, refresh: nil)
|
|
154
|
+
if resolved_by
|
|
155
|
+
@mutex.synchronize { @tallies.delete(ordinal) }
|
|
156
|
+
note = "rebased onto #{base}; #{resolved_by} resolved #{stops} conflicted commit#{"s" unless stops == 1}; " \
|
|
157
|
+
"plan work resumes next run"
|
|
158
|
+
UI.log("rebase: #{note}", plan: ordinal)
|
|
159
|
+
return result(spent, passed: true, note:)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
aborted = refresh&.abort!(why.to_s)
|
|
163
|
+
result(spent, passed: false, note: failed(ordinal, aborted&.base || "the default branch", aborted&.target, why))
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# One more failure on the tally, and the line that says what comes next.
|
|
167
|
+
#
|
|
168
|
+
# @return [String]
|
|
169
|
+
def failed(ordinal, base, target, why)
|
|
170
|
+
tally = @mutex.synchronize do
|
|
171
|
+
failures = @tallies.fetch(ordinal) { Tally.new(failures: 0, held_at: nil) }.failures + 1
|
|
172
|
+
@tallies[ordinal] = Tally.new(failures:, held_at: failures >= LADDER.size ? target : nil)
|
|
173
|
+
end
|
|
174
|
+
nxt = if tally.held_at
|
|
175
|
+
"REBASE PENDING: retries exhausted; the plan works on its old base until #{base} moves"
|
|
176
|
+
else
|
|
177
|
+
step = LADDER[tally.failures]
|
|
178
|
+
"#{RESOLVER} retries on a later run as #{step.values.join("/")}"
|
|
179
|
+
end
|
|
180
|
+
note = "rebase onto #{base} aborted, branch restored (#{why}); #{nxt}"
|
|
181
|
+
UI.log("rebase: #{note}", plan: ordinal)
|
|
182
|
+
note
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @param spent [Array<OpenLanes::Execution::Executor::Result>]
|
|
186
|
+
# @return [OpenLanes::Execution::Executor::Result]
|
|
187
|
+
def result(spent, passed:, note:)
|
|
188
|
+
sum = ->(field) { spent.sum { it.respond_to?(field) ? it.public_send(field).to_i : 0 } }
|
|
189
|
+
Execution::Executor::Result.new(ok: passed,
|
|
190
|
+
note:,
|
|
191
|
+
up: sum.call(:up),
|
|
192
|
+
down: sum.call(:down),
|
|
193
|
+
subagents: sum.call(:subagents),
|
|
194
|
+
delegated: sum.call(:delegated),
|
|
195
|
+
seconds: spent.sum { it.respond_to?(:seconds) ? it.seconds.to_f : 0.0 },
|
|
196
|
+
cached: sum.call(:cached))
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|