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
data/bin/factory-e2e
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# vim: ft=bash
|
|
3
|
-
#
|
|
4
|
-
# The factory integration test: a throwaway git repository, `openlanes start`
|
|
5
|
-
# serving it, and Cypress building a tic-tac-toe page through the control
|
|
6
|
-
# room with real agents. It spends real tokens, so it refuses to run unless
|
|
7
|
-
# OPENLANES_FACTORY_E2E=1 says so, and nothing in `just test`, `just web-e2e`
|
|
8
|
-
# or CircleCI calls it.
|
|
9
|
-
#
|
|
10
|
-
# OPENLANES_FACTORY_E2E=1 just factory-e2e
|
|
11
|
-
#
|
|
12
|
-
# Settings, each with a default:
|
|
13
|
-
#
|
|
14
|
-
# FACTORY_PORT control room port (4777)
|
|
15
|
-
# FACTORY_REDIS_URL Redis for the board and its commands (redis://localhost:6379/11)
|
|
16
|
-
# FACTORY_MODEL model every agent runs on (haiku)
|
|
17
|
-
# FACTORY_TIMEOUT seconds before one agent is abandoned (600)
|
|
18
|
-
# FACTORY_MINUTES minutes Cypress waits for the build (15)
|
|
19
|
-
#
|
|
20
|
-
# What it leaves behind is log/factory-e2e/: the progress log, the last
|
|
21
|
-
# board, every plan's state.json and spec.md, the built index.html, and the
|
|
22
|
-
# tail of each agent's trace. The temp repository and the `openlanes start` it
|
|
23
|
-
# launched are always torn down, pass or fail; nothing it did not start is
|
|
24
|
-
# touched.
|
|
25
|
-
|
|
26
|
-
set -euo pipefail
|
|
27
|
-
|
|
28
|
-
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
29
|
-
|
|
30
|
-
if [[ "${OPENLANES_FACTORY_E2E:-}" != "1" ]]; then
|
|
31
|
-
echo "factory-e2e runs real agents and spends real tokens." >&2
|
|
32
|
-
echo "Set OPENLANES_FACTORY_E2E=1 to run it: OPENLANES_FACTORY_E2E=1 just factory-e2e" >&2
|
|
33
|
-
exit 64
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
port="${FACTORY_PORT:-4777}"
|
|
37
|
-
redis_url="${FACTORY_REDIS_URL:-redis://localhost:6379/11}"
|
|
38
|
-
model="${FACTORY_MODEL:-haiku}"
|
|
39
|
-
timeout="${FACTORY_TIMEOUT:-600}"
|
|
40
|
-
minutes="${FACTORY_MINUTES:-15}"
|
|
41
|
-
artifacts="${repo}/log/factory-e2e"
|
|
42
|
-
|
|
43
|
-
command -v claude >/dev/null || { echo "claude is not on PATH; the factory has no agents to run." >&2; exit 69; }
|
|
44
|
-
if command -v redis-cli >/dev/null && ! redis-cli -u "${redis_url}" ping >/dev/null 2>&1; then
|
|
45
|
-
echo "Redis does not answer on ${redis_url}." >&2
|
|
46
|
-
exit 69
|
|
47
|
-
fi
|
|
48
|
-
# Another server on the port (the user's own `openlanes web` on 11000, say) is
|
|
49
|
-
# left alone: the run refuses rather than share or kill it.
|
|
50
|
-
if lsof -nP -iTCP:"${port}" -sTCP:LISTEN >/dev/null 2>&1; then
|
|
51
|
-
echo "Port ${port} is taken; set FACTORY_PORT to a free one." >&2
|
|
52
|
-
exit 69
|
|
53
|
-
fi
|
|
54
|
-
[[ -f "${repo}/web/dist/index.html" ]] || { echo "web/dist is missing; run just web-build first." >&2; exit 66; }
|
|
55
|
-
|
|
56
|
-
tmp="$(mktemp -d "${TMPDIR:-/tmp}/openlanes-factory.XXXXXX")"
|
|
57
|
-
project="${tmp}/tictactoe"
|
|
58
|
-
started_at="${tmp}/.started"
|
|
59
|
-
pid=""
|
|
60
|
-
|
|
61
|
-
openlanes() { "${repo}/exe/openlanes" "$@"; }
|
|
62
|
-
|
|
63
|
-
collect() {
|
|
64
|
-
rm -rf "${artifacts}"
|
|
65
|
-
mkdir -p "${artifacts}/plans" "${artifacts}/traces"
|
|
66
|
-
curl -sf "http://127.0.0.1:${port}/api/board" -o "${artifacts}/board.json" 2>/dev/null || true
|
|
67
|
-
cp "${tmp}/progress.log" "${artifacts}/" 2>/dev/null || true
|
|
68
|
-
cp "${tmp}/start.log" "${artifacts}/" 2>/dev/null || true
|
|
69
|
-
if [[ -d "${project}/.plans" ]]; then
|
|
70
|
-
for plan in "${project}"/.plans/*/; do
|
|
71
|
-
name="$(basename "${plan}")"
|
|
72
|
-
mkdir -p "${artifacts}/plans/${name}"
|
|
73
|
-
cp "${plan}"*.md "${plan}"state.json "${artifacts}/plans/${name}/" 2>/dev/null || true
|
|
74
|
-
done
|
|
75
|
-
fi
|
|
76
|
-
find "${project}" -name index.html -not -path '*/.git/*' -not -path '*/node_modules/*' \
|
|
77
|
-
-exec cp {} "${artifacts}/" \; 2>/dev/null || true
|
|
78
|
-
(cd "${project}" 2>/dev/null && git status --short >"${artifacts}/git-status.txt") || true
|
|
79
|
-
# The traces live outside every repository (Executor::TRACE_DIR); keep the
|
|
80
|
-
# tail of the ones this run wrote.
|
|
81
|
-
traces="${TMPDIR:-/tmp}"
|
|
82
|
-
traces="${traces%/}/openlanes-traces"
|
|
83
|
-
if [[ -d "${traces}" ]]; then
|
|
84
|
-
find "${traces}" -type f -newer "${started_at}" | while read -r trace; do
|
|
85
|
-
tail -n 40 "${trace}" >"${artifacts}/traces/$(basename "${trace}")"
|
|
86
|
-
done
|
|
87
|
-
fi
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
teardown() {
|
|
91
|
-
status=$?
|
|
92
|
-
trap - EXIT INT TERM
|
|
93
|
-
collect || true
|
|
94
|
-
if [[ -n "${pid}" ]] && kill -0 "${pid}" 2>/dev/null; then
|
|
95
|
-
# TERM is `openlanes start`'s graceful stop: its agents are told to stop and
|
|
96
|
-
# its control room goes down with it. KILL only if it outlives that.
|
|
97
|
-
kill -TERM "${pid}" 2>/dev/null || true
|
|
98
|
-
for _ in $(seq 1 30); do kill -0 "${pid}" 2>/dev/null || break; sleep 1; done
|
|
99
|
-
kill -KILL "${pid}" 2>/dev/null || true
|
|
100
|
-
fi
|
|
101
|
-
rm -rf "${tmp}"
|
|
102
|
-
echo "factory-e2e: artifacts in ${artifacts}" >&2
|
|
103
|
-
exit "${status}"
|
|
104
|
-
}
|
|
105
|
-
trap teardown EXIT
|
|
106
|
-
trap 'exit 130' INT
|
|
107
|
-
trap 'exit 143' TERM
|
|
108
|
-
|
|
109
|
-
touch "${started_at}"
|
|
110
|
-
|
|
111
|
-
# A repository with one commit and `openlanes init`, and no remote: nothing this
|
|
112
|
-
# run does can reach GitHub.
|
|
113
|
-
mkdir -p "${project}"
|
|
114
|
-
(
|
|
115
|
-
cd "${project}"
|
|
116
|
-
git init -q -b main
|
|
117
|
-
git config user.name "Factory Test"
|
|
118
|
-
git config user.email "factory-test@example.invalid"
|
|
119
|
-
cat >README.md <<'MARKDOWN'
|
|
120
|
-
# Tic Tac Toe
|
|
121
|
-
|
|
122
|
-
A static web page: plain HTML, CSS and JavaScript in the repository root,
|
|
123
|
-
opened straight from disk. There is no package manager, no build step and no
|
|
124
|
-
test runner; check a change by reading it and opening the page.
|
|
125
|
-
MARKDOWN
|
|
126
|
-
git add README.md
|
|
127
|
-
git commit -q -m "Initial commit"
|
|
128
|
-
openlanes init -y -q --project "Tic Tac Toe" -j 1
|
|
129
|
-
git add -A
|
|
130
|
-
git commit -q -m "Initialize openlanes"
|
|
131
|
-
)
|
|
132
|
-
|
|
133
|
-
# `--isolation shared` is the existing local-only mode: one tree, no branch
|
|
134
|
-
# per plan, and no publisher, so nothing is pushed and no pull request is
|
|
135
|
-
# opened. The finished build stays at 🟡 Building, done for this run: 🟢
|
|
136
|
-
# Ready for Review needs an open pull request.
|
|
137
|
-
(
|
|
138
|
-
cd "${project}"
|
|
139
|
-
exec "${repo}/exe/openlanes" start --no-open \
|
|
140
|
-
--port "${port}" --redis-url "${redis_url}" \
|
|
141
|
-
--isolation shared -j 1 \
|
|
142
|
-
--model "${model}" --timeout "${timeout}" \
|
|
143
|
-
--log "${tmp}/progress.log"
|
|
144
|
-
) >"${tmp}/start.log" 2>&1 &
|
|
145
|
-
pid=$!
|
|
146
|
-
|
|
147
|
-
for _ in $(seq 1 120); do
|
|
148
|
-
curl -sf "http://127.0.0.1:${port}/healthz" >/dev/null 2>&1 && break
|
|
149
|
-
kill -0 "${pid}" 2>/dev/null || { echo "openlanes start exited:" >&2; cat "${tmp}/start.log" >&2; exit 1; }
|
|
150
|
-
sleep 0.5
|
|
151
|
-
done
|
|
152
|
-
curl -sf "http://127.0.0.1:${port}/healthz" >/dev/null || { echo "The control room never answered on ${port}." >&2; exit 1; }
|
|
153
|
-
|
|
154
|
-
cd "${repo}/web"
|
|
155
|
-
npx cypress run --config-file cypress.factory.config.ts \
|
|
156
|
-
--config "baseUrl=http://127.0.0.1:${port}" \
|
|
157
|
-
--env "projectDir=${project},minutes=${minutes}"
|
data/bin/plan-number
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
3
|
-
# plan-number - resolve the .plans folder a branch belongs to, or fail.
|
|
4
|
-
#
|
|
5
|
-
# plan-number # resolve for the current branch
|
|
6
|
-
# plan-number -b <branch> # ...for another branch
|
|
7
|
-
# plan-number -D <dir> # .plans lives elsewhere (default: ./.plans)
|
|
8
|
-
# plan-number --title "Foo" # print "[003] Foo" instead of "003"
|
|
9
|
-
# # (or "[018.01] Foo" for a retroactive plan)
|
|
10
|
-
# plan-number --none # print "XXX": this PR implements no plan
|
|
11
|
-
#
|
|
12
|
-
# Exit 0 and print the number on stdout when exactly one plan can be resolved.
|
|
13
|
-
# Exit non-zero and explain on stderr otherwise. It never picks a winner from
|
|
14
|
-
# several candidates and never invents one.
|
|
15
|
-
#
|
|
16
|
-
# == Why it refuses rather than guesses
|
|
17
|
-
#
|
|
18
|
-
# The number ends up in a PR title, and pull-requests.md is generated pfrom those
|
|
19
|
-
# titles. A wrong number does not announce itself: it files the work under a
|
|
20
|
-
# plan that did not do it, and the plan that did looks untouched. That is worse
|
|
21
|
-
# than an unprefixed title, which at least reads as "unknown" rather than as a
|
|
22
|
-
# confident lie. Every failure below is therefore loud.
|
|
23
|
-
#
|
|
24
|
-
# == The two shapes of number
|
|
25
|
-
#
|
|
26
|
-
# NNN an ordinary plan, specified before it was built.
|
|
27
|
-
# NNN.MM a RETROACTIVE plan: the work shipped first and the specification
|
|
28
|
-
# was written afterwards to describe what exists. It sits in the
|
|
29
|
-
# sequence where the work landed - 018.01 merged after 018 and
|
|
30
|
-
# before 019.
|
|
31
|
-
#
|
|
32
|
-
# NNN.MM does NOT mean "part of NNN". The dot reads as containment in most
|
|
33
|
-
# numbering schemes and here it does not: 018.01 is a sibling of 018 that
|
|
34
|
-
# arrived later, with no relationship to its subject. See .plans/README.md.
|
|
35
|
-
#
|
|
36
|
-
# The decimal is ALWAYS two digits. One digit would sort into the middle of the
|
|
37
|
-
# two-digit range - 018.09 < 018.1 < 018.10 - so a single mixed-width folder
|
|
38
|
-
# silently reorders the index. Two digits also give 99 slots per gap, which
|
|
39
|
-
# retires the question of running out: a gap closes the moment the next plan is
|
|
40
|
-
# created, since nothing merging today can land between 018 and 019.
|
|
41
|
-
#
|
|
42
|
-
# == The resolution order, strongest signal first
|
|
43
|
-
#
|
|
44
|
-
# 1. The BRANCH NAME. `kig/021-add-tune-appender` says 021 and says it before a
|
|
45
|
-
# single line is written, which is the only moment the author certainly knows
|
|
46
|
-
# the answer. Set once, carried on the PR record forever, survives squash.
|
|
47
|
-
#
|
|
48
|
-
# 2. The DIFF against the base, but ONLY when it touches exactly one plan
|
|
49
|
-
# folder. Measured across this repository's 24 merged pull requests, this
|
|
50
|
-
# alone resolves 6 and is ambiguous or silent on 18, so it is a fallback and
|
|
51
|
-
# never a primary. Files directly under .plans/ (BACKLOG.md, README.md,
|
|
52
|
-
# INDEX.md) are ignored: they belong to no single plan.
|
|
53
|
-
#
|
|
54
|
-
# 3. Nothing else. There is no "closest match by topic", because a heuristic
|
|
55
|
-
# that is right most of the time produces an index nobody can trust and
|
|
56
|
-
# nobody can audit.
|
|
57
|
-
#
|
|
58
|
-
# XXX is deliberately NOT step 4. A PR that implements no plan is a legitimate
|
|
59
|
-
# and common thing - dependency bumps, CI work, hotfixes - and it is marked
|
|
60
|
-
# [XXX] so that "no plan" is visibly asserted rather than merely absent. But the
|
|
61
|
-
# author asserts it; this script will not, because emitting XXX on a failed
|
|
62
|
-
# lookup would launder "I could not tell" into "there is definitely none", which
|
|
63
|
-
# is the same lie as guessing a number, told in the other direction.
|
|
64
|
-
#
|
|
65
|
-
# © 2026 Konstantin Gredeskoul
|
|
66
|
-
|
|
67
|
-
set -euo pipefail
|
|
68
|
-
|
|
69
|
-
PLANS_DIR=".plans"
|
|
70
|
-
BRANCH=""
|
|
71
|
-
TITLE=""
|
|
72
|
-
NONE=0
|
|
73
|
-
BASE="${PLAN_NUMBER_BASE:-origin/main}"
|
|
74
|
-
|
|
75
|
-
die() { printf '%s\n' "$*" >&2; exit 1; }
|
|
76
|
-
|
|
77
|
-
usage() {
|
|
78
|
-
cat >&2 <<'USAGE'
|
|
79
|
-
Usage: plan-number [-D <plans-dir>] [-b <branch>] [--base <ref>] [--title <text>] [--none]
|
|
80
|
-
|
|
81
|
-
Prints the plan number for the branch (NNN, or NNN.MM when the plan was written
|
|
82
|
-
retroactively), or fails loudly.
|
|
83
|
-
|
|
84
|
-
With --title, prints "[NNN] <text>" ready to hand to `gh pr create -t`.
|
|
85
|
-
With --none, prints XXX: an assertion that this PR implements no plan.
|
|
86
|
-
USAGE
|
|
87
|
-
exit 2
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
while [[ $# -gt 0 ]]; do
|
|
91
|
-
case "$1" in
|
|
92
|
-
-D|--dir) PLANS_DIR="${2:?}"; shift 2 ;;
|
|
93
|
-
-b|--branch) BRANCH="${2:?}"; shift 2 ;;
|
|
94
|
-
--base) BASE="${2:?}"; shift 2 ;;
|
|
95
|
-
-t|--title) TITLE="${2:?}"; shift 2 ;;
|
|
96
|
-
-n|--none) NONE=1; shift ;;
|
|
97
|
-
-h|--help) usage ;;
|
|
98
|
-
*) die "plan-number: unknown argument: $1" ;;
|
|
99
|
-
esac
|
|
100
|
-
done
|
|
101
|
-
|
|
102
|
-
# The three-digit number, optionally followed by one retroactive digit.
|
|
103
|
-
NUM_RE='[0-9]{3}(\.[0-9]{2})?'
|
|
104
|
-
|
|
105
|
-
if [[ "$NONE" == "1" ]]; then
|
|
106
|
-
if [[ -n "$TITLE" ]]; then printf '[XXX] %s\n' "$TITLE"; else printf 'XXX\n'; fi
|
|
107
|
-
exit 0
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
[[ -d "$PLANS_DIR" ]] || die "plan-number: no $PLANS_DIR here. Run this from the repository root, or pass -D."
|
|
111
|
-
|
|
112
|
-
# Every plan number that actually exists on disk. A resolved number is checked
|
|
113
|
-
# against this, so a typo in a branch name fails here rather than in the index
|
|
114
|
-
# six weeks later.
|
|
115
|
-
existing="$(find "$PLANS_DIR" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; \
|
|
116
|
-
| grep -oE "^${NUM_RE}" | sort -u)"
|
|
117
|
-
[[ -n "$existing" ]] || die "plan-number: $PLANS_DIR holds no NNN[.MM]-<status>-<slug> folders."
|
|
118
|
-
|
|
119
|
-
exists() { printf '%s\n' "$existing" | grep -qx "$1"; }
|
|
120
|
-
|
|
121
|
-
emit() {
|
|
122
|
-
local n="$1"
|
|
123
|
-
exists "$n" || die "plan-number: resolved $n, but $PLANS_DIR has no folder $n-*.
|
|
124
|
-
Existing plans: $(printf '%s' "$existing" | tr '\n' ' ')"
|
|
125
|
-
if [[ -n "$TITLE" ]]; then printf '[%s] %s\n' "$n" "$TITLE"; else printf '%s\n' "$n"; fi
|
|
126
|
-
exit 0
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
[[ -n "$BRANCH" ]] || BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
|
130
|
-
|
|
131
|
-
# 1. The branch name. Matches 021 in kig/021-slug, 021-slug and feature/021_slug,
|
|
132
|
-
# and 018.01 in kig/018.01-slug. Longest match wins, so a retroactive branch
|
|
133
|
-
# does not silently resolve to its whole-number neighbour.
|
|
134
|
-
if [[ "$BRANCH" =~ (^|/|-|_)([0-9]{3}(\.[0-9]{2})?)($|[-_]) ]]; then
|
|
135
|
-
emit "${BASH_REMATCH[2]}"
|
|
136
|
-
fi
|
|
137
|
-
|
|
138
|
-
# 2. The diff, and only when it is unambiguous.
|
|
139
|
-
if git rev-parse --verify --quiet "$BASE" >/dev/null 2>&1; then
|
|
140
|
-
touched="$(git diff --name-only "$(git merge-base "$BASE" HEAD)"..HEAD -- "$PLANS_DIR" 2>/dev/null \
|
|
141
|
-
| awk -F/ 'NF>2 {print $2}' | grep -oE "^${NUM_RE}" | sort -u || true)"
|
|
142
|
-
count="$(printf '%s' "$touched" | grep -c . || true)"
|
|
143
|
-
|
|
144
|
-
if [[ "$count" == "1" ]]; then
|
|
145
|
-
emit "$touched"
|
|
146
|
-
elif [[ "${count:-0}" -gt 1 ]]; then
|
|
147
|
-
die "plan-number: this branch touches several plans and none is obviously primary:
|
|
148
|
-
$(printf '%s' "$touched" | tr '\n' ' ')
|
|
149
|
-
Name the branch after the one it implements (kig/NNN-slug), or pass the title yourself."
|
|
150
|
-
fi
|
|
151
|
-
fi
|
|
152
|
-
|
|
153
|
-
die "plan-number: cannot resolve a plan for branch '${BRANCH:-<detached>}'.
|
|
154
|
-
|
|
155
|
-
It carries no NNN in its name and its diff touches no single plan folder.
|
|
156
|
-
|
|
157
|
-
- If it implements a plan, rename the branch: git branch -m ${USER}/NNN-slug
|
|
158
|
-
- If it implements no plan, say so explicitly: plan-number --none --title '...'
|
|
159
|
-
gives you [XXX]. Infrastructure, dependency bumps and hotfixes are not
|
|
160
|
-
features, and [XXX] is the right answer for them - but it is your assertion
|
|
161
|
-
to make, not this script's guess.
|
|
162
|
-
|
|
163
|
-
Refusing rather than guessing: pull-requests.md is generated from these titles,
|
|
164
|
-
and a wrong number files the work under a plan that did not do it."
|
data/bin/setup
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
IFS=$'\n\t'
|
|
5
|
-
# set -v
|
|
6
|
-
|
|
7
|
-
command -v just>/dev/null || brew install just
|
|
8
|
-
command -v rbenv>/dev/null || brew install rbenv
|
|
9
|
-
|
|
10
|
-
ruby_version="$(cat .ruby-version | tr -d '\n')"
|
|
11
|
-
current_ruby_version="$(ruby -e 'puts RbConfig::CONFIG["RUBY_PROGRAM_VERSION"]')"
|
|
12
|
-
|
|
13
|
-
if [[ ${ruby_version} != ${current_ruby_version} ]]; then
|
|
14
|
-
bash -c "$(curl -fsSL https://bit.ly/ruby-install-0-2-1)" -- ${ruby_version}
|
|
15
|
-
else
|
|
16
|
-
echo -e "\e[0;33m[OK]\e[0m Ruby version ${ruby_version} is installed and current."
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
rbenv versions | grep -q ${ruby_version} || {
|
|
20
|
-
echo -e "\e[1;31mCan't find ruby version ${ruby_version} after an attempted install.\e[0m"
|
|
21
|
-
exit 1
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
bundle check>/dev/null && {
|
|
25
|
-
echo -e "\e[0;33m[OK]\e[0m Gemfile dependencies are already satisfied."
|
|
26
|
-
} || bundle install -j 12
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# Plan
|
|
2
|
-
|
|
3
|
-
Six units, U6 is the integration check.
|
|
4
|
-
|
|
5
|
-
## Order
|
|
6
|
-
|
|
7
|
-
U1, then U2 to U5 in parallel, then U6.
|
|
8
|
-
|
|
9
|
-
> [!NOTE]
|
|
10
|
-
>
|
|
11
|
-
> [2026-09-04 01:10:00 PM PDT] [ agent: palpatine-planner status: Started, round 1 ]
|
|
12
|
-
> [2026-09-04 01:14:30 PM PDT] [ agent: palpatine-planner status: Completed, round 1 ]
|
|
Binary file
|