openlanes 3.0.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 +7 -0
- data/agents/hansolo-reviewer.md +59 -0
- data/agents/lando-broker.md +70 -0
- data/agents/leah-researcher.md +60 -0
- data/agents/luke-backend.md +73 -0
- data/agents/palpatine-planner.md +61 -0
- data/agents/r2d2-mechanic.md +57 -0
- data/agents/rey-frontend.md +68 -0
- data/agents/yoda-writer.md +67 -0
- data/bin/factory-e2e +157 -0
- data/bin/plan-number +164 -0
- data/bin/setup +26 -0
- data/evals/cases/hansolo-reviewer/review-medium/fail/plan/plan.md +3 -0
- data/evals/cases/hansolo-reviewer/review-medium/fail/plan/pull-requests.md +12 -0
- data/evals/cases/hansolo-reviewer/review-medium/fail/plan/spec.md +5 -0
- data/evals/cases/hansolo-reviewer/review-medium/fail/repo/lib/cart.rb +9 -0
- data/evals/cases/hansolo-reviewer/review-medium/fail/repo/spec/cart_spec.rb +7 -0
- data/evals/cases/hansolo-reviewer/review-medium/fail/run.json +1 -0
- data/evals/cases/hansolo-reviewer/review-medium/pass/plan/plan.md +3 -0
- data/evals/cases/hansolo-reviewer/review-medium/pass/plan/pull-requests.md +15 -0
- data/evals/cases/hansolo-reviewer/review-medium/pass/plan/spec.md +5 -0
- data/evals/cases/hansolo-reviewer/review-medium/pass/repo/lib/cart.rb +9 -0
- data/evals/cases/hansolo-reviewer/review-medium/pass/repo/spec/cart_spec.rb +7 -0
- data/evals/cases/hansolo-reviewer/review-medium/pass/run.json +1 -0
- data/evals/cases/hansolo-reviewer/review-medium.yml +60 -0
- data/evals/cases/lando-broker/unblock-fast/fail/plan/blocked.md +9 -0
- data/evals/cases/lando-broker/unblock-fast/fail/plan/spec.md +16 -0
- data/evals/cases/lando-broker/unblock-fast/fail/run.json +1 -0
- data/evals/cases/lando-broker/unblock-fast/pass/plan/spec.md +18 -0
- data/evals/cases/lando-broker/unblock-fast/pass/run.json +1 -0
- data/evals/cases/lando-broker/unblock-fast.yml +44 -0
- data/evals/cases/leah-researcher/research-deep/fail/plan/spec.md +26 -0
- data/evals/cases/leah-researcher/research-deep/fail/run.json +1 -0
- data/evals/cases/leah-researcher/research-deep/pass/plan/spec.md +39 -0
- data/evals/cases/leah-researcher/research-deep/pass/run.json +1 -0
- data/evals/cases/leah-researcher/research-deep.yml +51 -0
- data/evals/cases/leah-researcher/research-fast/fail/plan/spec.md +27 -0
- data/evals/cases/leah-researcher/research-fast/fail/run.json +1 -0
- data/evals/cases/leah-researcher/research-fast/pass/plan/spec.md +23 -0
- data/evals/cases/leah-researcher/research-fast/pass/run.json +1 -0
- data/evals/cases/leah-researcher/research-fast.yml +55 -0
- data/evals/cases/luke-backend/build-medium/fail/plan/plan-backend.md +7 -0
- data/evals/cases/luke-backend/build-medium/fail/plan/plan-frontend.md +1 -0
- data/evals/cases/luke-backend/build-medium/fail/plan/plan.md +3 -0
- data/evals/cases/luke-backend/build-medium/fail/plan/pull-requests.md +8 -0
- data/evals/cases/luke-backend/build-medium/fail/plan/spec.md +8 -0
- data/evals/cases/luke-backend/build-medium/fail/repo/Gemfile +2 -0
- data/evals/cases/luke-backend/build-medium/fail/repo/lib/cart.rb +11 -0
- data/evals/cases/luke-backend/build-medium/fail/repo/spec/cart_spec.rb +7 -0
- data/evals/cases/luke-backend/build-medium/fail/run.json +1 -0
- data/evals/cases/luke-backend/build-medium/pass/plan/plan-backend.md +7 -0
- data/evals/cases/luke-backend/build-medium/pass/plan/plan-frontend.md +1 -0
- data/evals/cases/luke-backend/build-medium/pass/plan/plan.md +3 -0
- data/evals/cases/luke-backend/build-medium/pass/plan/pull-requests.md +8 -0
- data/evals/cases/luke-backend/build-medium/pass/plan/spec.md +8 -0
- data/evals/cases/luke-backend/build-medium/pass/repo/Gemfile +2 -0
- data/evals/cases/luke-backend/build-medium/pass/repo/lib/cart.rb +11 -0
- data/evals/cases/luke-backend/build-medium/pass/repo/spec/cart_spec.rb +11 -0
- data/evals/cases/luke-backend/build-medium/pass/run.json +1 -0
- data/evals/cases/luke-backend/build-medium.yml +68 -0
- data/evals/cases/palpatine-planner/plan-deep/fail/plan/plan-backend.md +6 -0
- data/evals/cases/palpatine-planner/plan-deep/fail/plan/plan.md +8 -0
- data/evals/cases/palpatine-planner/plan-deep/fail/plan/spec.md +14 -0
- data/evals/cases/palpatine-planner/plan-deep/fail/run.json +1 -0
- data/evals/cases/palpatine-planner/plan-deep/pass/plan/plan-backend.md +32 -0
- data/evals/cases/palpatine-planner/plan-deep/pass/plan/plan-frontend.md +1 -0
- data/evals/cases/palpatine-planner/plan-deep/pass/plan/plan.md +28 -0
- data/evals/cases/palpatine-planner/plan-deep/pass/plan/spec.md +14 -0
- data/evals/cases/palpatine-planner/plan-deep/pass/run.json +1 -0
- data/evals/cases/palpatine-planner/plan-deep.yml +50 -0
- data/evals/cases/palpatine-planner/plan-fast/fail/plan/plan-backend.md +48 -0
- data/evals/cases/palpatine-planner/plan-fast/fail/plan/plan-frontend.md +1 -0
- data/evals/cases/palpatine-planner/plan-fast/fail/plan/plan.md +12 -0
- data/evals/cases/palpatine-planner/plan-fast/fail/plan/spec.md +12 -0
- data/evals/cases/palpatine-planner/plan-fast/fail/run.json +1 -0
- data/evals/cases/palpatine-planner/plan-fast/pass/plan/plan-backend.md +8 -0
- data/evals/cases/palpatine-planner/plan-fast/pass/plan/plan-frontend.md +1 -0
- data/evals/cases/palpatine-planner/plan-fast/pass/plan/plan.md +12 -0
- data/evals/cases/palpatine-planner/plan-fast/pass/plan/spec.md +12 -0
- data/evals/cases/palpatine-planner/plan-fast/pass/run.json +1 -0
- data/evals/cases/palpatine-planner/plan-fast.yml +47 -0
- data/evals/cases/r2d2-mechanic/task-fast/fail/plan/plan.md +7 -0
- data/evals/cases/r2d2-mechanic/task-fast/fail/plan/pull-requests.md +6 -0
- data/evals/cases/r2d2-mechanic/task-fast/fail/plan/spec.md +6 -0
- data/evals/cases/r2d2-mechanic/task-fast/fail/repo/README.md +3 -0
- data/evals/cases/r2d2-mechanic/task-fast/fail/repo/lib/greeter/formatter.rb +12 -0
- data/evals/cases/r2d2-mechanic/task-fast/fail/repo/lib/greeter.rb +15 -0
- data/evals/cases/r2d2-mechanic/task-fast/fail/repo/spec/greeter_spec.rb +7 -0
- data/evals/cases/r2d2-mechanic/task-fast/fail/run.json +1 -0
- data/evals/cases/r2d2-mechanic/task-fast/pass/plan/plan.md +7 -0
- data/evals/cases/r2d2-mechanic/task-fast/pass/plan/pull-requests.md +6 -0
- data/evals/cases/r2d2-mechanic/task-fast/pass/plan/spec.md +6 -0
- data/evals/cases/r2d2-mechanic/task-fast/pass/repo/README.md +3 -0
- data/evals/cases/r2d2-mechanic/task-fast/pass/repo/lib/greeter.rb +3 -0
- data/evals/cases/r2d2-mechanic/task-fast/pass/repo/spec/greeter_spec.rb +7 -0
- data/evals/cases/r2d2-mechanic/task-fast/pass/run.json +1 -0
- data/evals/cases/r2d2-mechanic/task-fast.yml +59 -0
- data/evals/cases/r2d2-mechanic/task-medium/fail/plan/plan.md +7 -0
- data/evals/cases/r2d2-mechanic/task-medium/fail/plan/pull-requests.md +6 -0
- data/evals/cases/r2d2-mechanic/task-medium/fail/plan/spec.md +6 -0
- data/evals/cases/r2d2-mechanic/task-medium/fail/repo/Gemfile +2 -0
- data/evals/cases/r2d2-mechanic/task-medium/fail/repo/lib/greeter.rb +6 -0
- data/evals/cases/r2d2-mechanic/task-medium/fail/repo/spec/greeter_spec.rb +11 -0
- data/evals/cases/r2d2-mechanic/task-medium/fail/run.json +1 -0
- data/evals/cases/r2d2-mechanic/task-medium/pass/plan/plan.md +7 -0
- data/evals/cases/r2d2-mechanic/task-medium/pass/plan/pull-requests.md +6 -0
- data/evals/cases/r2d2-mechanic/task-medium/pass/plan/spec.md +6 -0
- data/evals/cases/r2d2-mechanic/task-medium/pass/repo/Gemfile +2 -0
- data/evals/cases/r2d2-mechanic/task-medium/pass/repo/lib/greeter.rb +6 -0
- data/evals/cases/r2d2-mechanic/task-medium/pass/repo/spec/greeter_spec.rb +11 -0
- data/evals/cases/r2d2-mechanic/task-medium/pass/run.json +1 -0
- data/evals/cases/r2d2-mechanic/task-medium.yml +58 -0
- data/evals/cases/rey-frontend/ui-medium/fail/plan/plan-frontend.md +7 -0
- data/evals/cases/rey-frontend/ui-medium/fail/plan/plan.md +4 -0
- data/evals/cases/rey-frontend/ui-medium/fail/plan/pull-requests.md +8 -0
- data/evals/cases/rey-frontend/ui-medium/fail/plan/spec.md +5 -0
- data/evals/cases/rey-frontend/ui-medium/fail/repo/lib/cart.rb +5 -0
- data/evals/cases/rey-frontend/ui-medium/fail/repo/web/cart_badge.js +4 -0
- data/evals/cases/rey-frontend/ui-medium/fail/repo/web/cart_badge.test.js +3 -0
- data/evals/cases/rey-frontend/ui-medium/fail/run.json +1 -0
- data/evals/cases/rey-frontend/ui-medium/pass/plan/plan-frontend.md +7 -0
- data/evals/cases/rey-frontend/ui-medium/pass/plan/plan.md +4 -0
- data/evals/cases/rey-frontend/ui-medium/pass/plan/pull-requests.md +8 -0
- data/evals/cases/rey-frontend/ui-medium/pass/plan/spec.md +5 -0
- data/evals/cases/rey-frontend/ui-medium/pass/repo/lib/cart.rb +3 -0
- data/evals/cases/rey-frontend/ui-medium/pass/repo/web/cart_badge.js +4 -0
- data/evals/cases/rey-frontend/ui-medium/pass/repo/web/cart_badge.test.js +3 -0
- data/evals/cases/rey-frontend/ui-medium/pass/run.json +1 -0
- data/evals/cases/rey-frontend/ui-medium.yml +55 -0
- data/evals/cases/yoda-writer/spec-medium/fail/plan/plan.md +5 -0
- data/evals/cases/yoda-writer/spec-medium/fail/plan/spec.md +45 -0
- data/evals/cases/yoda-writer/spec-medium/fail/run.json +1 -0
- data/evals/cases/yoda-writer/spec-medium/pass/plan/plan.md +0 -0
- data/evals/cases/yoda-writer/spec-medium/pass/plan/spec.md +63 -0
- data/evals/cases/yoda-writer/spec-medium/pass/run.json +1 -0
- data/evals/cases/yoda-writer/spec-medium.yml +40 -0
- data/exe/depot +1 -0
- data/exe/lanes +1 -0
- data/exe/openlanes +32 -0
- data/lib/openlanes/adapters/base.rb +69 -0
- data/lib/openlanes/adapters/claude.rb +124 -0
- data/lib/openlanes/adapters/codex.rb +33 -0
- data/lib/openlanes/adapters/model.rb +37 -0
- data/lib/openlanes/adapters/pi.rb +22 -0
- data/lib/openlanes/adapters/stream.rb +196 -0
- data/lib/openlanes/adapters.rb +92 -0
- data/lib/openlanes/agents/agent.rb +103 -0
- data/lib/openlanes/agents/profile.rb +57 -0
- data/lib/openlanes/agents/registry.rb +151 -0
- data/lib/openlanes/agents/roster.rb +103 -0
- data/lib/openlanes/agents/routing.rb +88 -0
- data/lib/openlanes/agents.rb +20 -0
- data/lib/openlanes/cli/agents/subcommands/describe.rb +66 -0
- data/lib/openlanes/cli/agents/subcommands/list.rb +20 -0
- data/lib/openlanes/cli/base.rb +263 -0
- data/lib/openlanes/cli/create/create.rb +354 -0
- data/lib/openlanes/cli/docs/docs.rb +40 -0
- data/lib/openlanes/cli/eval/eval.rb +110 -0
- data/lib/openlanes/cli/index/index.rb +40 -0
- data/lib/openlanes/cli/init/init.rb +145 -0
- data/lib/openlanes/cli/linear/linear.rb +36 -0
- data/lib/openlanes/cli/linear/subcommands/import.rb +174 -0
- data/lib/openlanes/cli/linear/subcommands/projects.rb +55 -0
- data/lib/openlanes/cli/list_plans/list_plans.rb +21 -0
- data/lib/openlanes/cli/mail/mail.rb +31 -0
- data/lib/openlanes/cli/mail/subcommands/read.rb +43 -0
- data/lib/openlanes/cli/mail/subcommands/send.rb +43 -0
- data/lib/openlanes/cli/resync/subcommands/dirs.rb +48 -0
- data/lib/openlanes/cli/resync/subcommands/prs.rb +111 -0
- data/lib/openlanes/cli/run/run.rb +544 -0
- data/lib/openlanes/cli/skills/subcommands/install.rb +92 -0
- data/lib/openlanes/cli/start/start.rb +110 -0
- data/lib/openlanes/cli/state/state.rb +26 -0
- data/lib/openlanes/cli/state/subcommands/show.rb +30 -0
- data/lib/openlanes/cli/state/subcommands/sign.rb +48 -0
- data/lib/openlanes/cli/states/states.rb +15 -0
- data/lib/openlanes/cli/unblock/unblock.rb +235 -0
- data/lib/openlanes/cli/version/version.rb +13 -0
- data/lib/openlanes/cli/web/web.rb +100 -0
- data/lib/openlanes/cli/worktree/worktree.rb +85 -0
- data/lib/openlanes/cli.rb +162 -0
- data/lib/openlanes/engine/board.rb +104 -0
- data/lib/openlanes/engine/dispatcher.rb +1297 -0
- data/lib/openlanes/engine/progress_log.rb +125 -0
- data/lib/openlanes/engine/runner.rb +224 -0
- data/lib/openlanes/engine/state_file.rb +190 -0
- data/lib/openlanes/engine/tally.rb +177 -0
- data/lib/openlanes/engine.rb +19 -0
- data/lib/openlanes/evals/case.rb +191 -0
- data/lib/openlanes/evals/changes.rb +67 -0
- data/lib/openlanes/evals/check.rb +224 -0
- data/lib/openlanes/evals/evidence.rb +71 -0
- data/lib/openlanes/evals/live.rb +140 -0
- data/lib/openlanes/evals/recording.rb +38 -0
- data/lib/openlanes/evals/run.rb +34 -0
- data/lib/openlanes/evals/score.rb +24 -0
- data/lib/openlanes/evals/scorer.rb +42 -0
- data/lib/openlanes/evals/verdict.rb +18 -0
- data/lib/openlanes/evals.rb +56 -0
- data/lib/openlanes/execution/checks.rb +73 -0
- data/lib/openlanes/execution/child.rb +228 -0
- data/lib/openlanes/execution/clock.rb +121 -0
- data/lib/openlanes/execution/control.rb +194 -0
- data/lib/openlanes/execution/executor.rb +849 -0
- data/lib/openlanes/execution/prompt.rb +455 -0
- data/lib/openlanes/execution/transcript.rb +531 -0
- data/lib/openlanes/execution.rb +19 -0
- data/lib/openlanes/launcher.rb +69 -0
- data/lib/openlanes/lifecycle/adoption.rb +197 -0
- data/lib/openlanes/lifecycle/brief.rb +288 -0
- data/lib/openlanes/lifecycle/completion.rb +58 -0
- data/lib/openlanes/lifecycle/creator.rb +126 -0
- data/lib/openlanes/lifecycle/lanes.rb +74 -0
- data/lib/openlanes/lifecycle/resync.rb +428 -0
- data/lib/openlanes/lifecycle/unblocker.rb +236 -0
- data/lib/openlanes/lifecycle.rb +28 -0
- data/lib/openlanes/linear/api.rb +224 -0
- data/lib/openlanes/linear/attribution.rb +185 -0
- data/lib/openlanes/linear/fuzzy.rb +68 -0
- data/lib/openlanes/linear/import.rb +314 -0
- data/lib/openlanes/linear/issue.rb +187 -0
- data/lib/openlanes/linear/mapping.rb +116 -0
- data/lib/openlanes/linear/push.rb +197 -0
- data/lib/openlanes/linear/survey.rb +174 -0
- data/lib/openlanes/linear/unit.rb +274 -0
- data/lib/openlanes/linear.rb +41 -0
- data/lib/openlanes/live/claims.rb +71 -0
- data/lib/openlanes/live/command.rb +122 -0
- data/lib/openlanes/live/commands.rb +505 -0
- data/lib/openlanes/live/keys.rb +93 -0
- data/lib/openlanes/live/memory_store.rb +265 -0
- data/lib/openlanes/live/meter.rb +51 -0
- data/lib/openlanes/live/node.rb +88 -0
- data/lib/openlanes/live/publisher.rb +179 -0
- data/lib/openlanes/live/repo.rb +60 -0
- data/lib/openlanes/live/run_record.rb +133 -0
- data/lib/openlanes/live/run_state.rb +101 -0
- data/lib/openlanes/live/session.rb +107 -0
- data/lib/openlanes/live/snapshot.rb +322 -0
- data/lib/openlanes/live/stations.rb +67 -0
- data/lib/openlanes/live/store.rb +277 -0
- data/lib/openlanes/live.rb +109 -0
- data/lib/openlanes/plans/dev_work.rb +56 -0
- data/lib/openlanes/plans/feature.rb +131 -0
- data/lib/openlanes/plans/index.rb +208 -0
- data/lib/openlanes/plans/ledger.rb +255 -0
- data/lib/openlanes/plans/mailbox.rb +164 -0
- data/lib/openlanes/plans/ordinal.rb +92 -0
- data/lib/openlanes/plans/plan_state.rb +291 -0
- data/lib/openlanes/plans/pull_request.rb +29 -0
- data/lib/openlanes/plans/pull_requests.rb +229 -0
- data/lib/openlanes/plans/spec.rb +213 -0
- data/lib/openlanes/plans/state_machine.rb +433 -0
- data/lib/openlanes/plans/status.rb +365 -0
- data/lib/openlanes/plans/subject.rb +133 -0
- data/lib/openlanes/plans/tree.rb +79 -0
- data/lib/openlanes/plans.rb +39 -0
- data/lib/openlanes/presentation/diagram.rb +149 -0
- data/lib/openlanes/presentation/documentation.rb +438 -0
- data/lib/openlanes/presentation/reporter.rb +181 -0
- data/lib/openlanes/presentation/viewer.rb +62 -0
- data/lib/openlanes/presentation.rb +23 -0
- data/lib/openlanes/skills/installer.rb +68 -0
- data/lib/openlanes/skills/skill.rb +60 -0
- data/lib/openlanes/skills.rb +25 -0
- data/lib/openlanes/support/config.rb +217 -0
- data/lib/openlanes/support/frontmatter.rb +38 -0
- data/lib/openlanes/support/markdown.rb +56 -0
- data/lib/openlanes/support/project.rb +79 -0
- data/lib/openlanes/support/ui.rb +671 -0
- data/lib/openlanes/vcs/github.rb +171 -0
- data/lib/openlanes/vcs/publisher.rb +221 -0
- data/lib/openlanes/vcs/worktree.rb +248 -0
- data/lib/openlanes/vcs.rb +19 -0
- data/lib/openlanes/version.rb +10 -0
- data/lib/openlanes/web/app.rb +176 -0
- data/lib/openlanes/web/context.rb +66 -0
- data/lib/openlanes/web/demo.rb +169 -0
- data/lib/openlanes/web/demo_board.rb +286 -0
- data/lib/openlanes/web/events.rb +104 -0
- data/lib/openlanes/web/launcher.rb +143 -0
- data/lib/openlanes/web/server.rb +77 -0
- data/lib/openlanes/web/stores.rb +36 -0
- data/lib/openlanes/web.rb +92 -0
- data/lib/openlanes.rb +151 -0
- data/openlanes.gemspec +87 -0
- data/schemas/plan-state.schema.json +101 -0
- data/skills/openlanes/SKILL.md +48 -0
- data/skills/openlanes-documents/SKILL.md +61 -0
- data/skills/openlanes-documents/references/blocked-template.md +49 -0
- data/skills/openlanes-documents/references/closing-templates.md +59 -0
- data/skills/openlanes-documents/references/plan-template.md +61 -0
- data/skills/openlanes-documents/references/retroactive-spec.md +43 -0
- data/skills/openlanes-documents/references/spec-template.md +45 -0
- data/skills/openlanes-run/SKILL.md +90 -0
- data/skills/openlanes-setup/SKILL.md +64 -0
- data/skills/openlanes-spec/SKILL.md +90 -0
- data/skills/openlanes-status/SKILL.md +32 -0
- data/skills/openlanes-sync/SKILL.md +22 -0
- data/web/dist/assets/apl-B4CMkyY2.js +2 -0
- data/web/dist/assets/asciiarmor-Df11BRmG.js +2 -0
- data/web/dist/assets/asn1-EdZsLKOL.js +2 -0
- data/web/dist/assets/asterisk-B-8jnY81.js +2 -0
- data/web/dist/assets/barlow-condensed-latin-600-normal-BFJEwTuo.woff +0 -0
- data/web/dist/assets/barlow-condensed-latin-600-normal-DepVgxBB.woff2 +0 -0
- data/web/dist/assets/barlow-condensed-latin-ext-600-normal-18ESti3H.woff2 +0 -0
- data/web/dist/assets/barlow-condensed-latin-ext-600-normal-Clv9cIcR.woff +0 -0
- data/web/dist/assets/barlow-condensed-vietnamese-600-normal-A5AYRdjN.woff2 +0 -0
- data/web/dist/assets/barlow-condensed-vietnamese-600-normal-CNlPk46_.woff +0 -0
- data/web/dist/assets/brainfuck-C4LP7Hcl.js +2 -0
- data/web/dist/assets/clike-B9uivgTg.js +2 -0
- data/web/dist/assets/clojure-BMjYHr_A.js +2 -0
- data/web/dist/assets/cmake-BQqOBYOt.js +2 -0
- data/web/dist/assets/cobol-CWcv1MsR.js +2 -0
- data/web/dist/assets/coffeescript-S37ZYGWr.js +2 -0
- data/web/dist/assets/commonlisp-DBKNyK5s.js +2 -0
- data/web/dist/assets/crystal-SjHAIU92.js +2 -0
- data/web/dist/assets/css-BnMrqG3P.js +2 -0
- data/web/dist/assets/cypher-C_CwsFkJ.js +2 -0
- data/web/dist/assets/d-pRatUO7H.js +2 -0
- data/web/dist/assets/diff-DbItnlRl.js +2 -0
- data/web/dist/assets/dockerfile-BKs6k2Af.js +2 -0
- data/web/dist/assets/dtd-DF_7sFjM.js +2 -0
- data/web/dist/assets/dylan-DwRh75JA.js +2 -0
- data/web/dist/assets/ebnf-CDyGwa7X.js +2 -0
- data/web/dist/assets/ecl-Cabwm37j.js +2 -0
- data/web/dist/assets/eiffel-CnydiIhH.js +2 -0
- data/web/dist/assets/elm-vLlmbW-K.js +2 -0
- data/web/dist/assets/erlang-BNw1qcRV.js +2 -0
- data/web/dist/assets/factor-kuTfRLto.js +2 -0
- data/web/dist/assets/favicon-C8pAuqqr.png +0 -0
- data/web/dist/assets/fcl-Kvtd6kyn.js +2 -0
- data/web/dist/assets/forth-Ffai-XNe.js +2 -0
- data/web/dist/assets/fortran-DYz_wnZ1.js +2 -0
- data/web/dist/assets/gas-Bneqetm1.js +2 -0
- data/web/dist/assets/gherkin-heZmZLOM.js +2 -0
- data/web/dist/assets/groovy-D9Dt4D0W.js +2 -0
- data/web/dist/assets/haskell-Cw1EW3IL.js +2 -0
- data/web/dist/assets/haxe-H-WmDvRZ.js +2 -0
- data/web/dist/assets/http-DBlCnlav.js +2 -0
- data/web/dist/assets/idl-BEugSyMb.js +2 -0
- data/web/dist/assets/index-2xPC-eHt.js +2 -0
- data/web/dist/assets/index-8dNhRmiO.js +8 -0
- data/web/dist/assets/index-B7-O9ujQ.js +2 -0
- data/web/dist/assets/index-BdR34cXC.js +2 -0
- data/web/dist/assets/index-Bp7W-ZTq.js +2 -0
- data/web/dist/assets/index-Byyxbh_z.js +4 -0
- data/web/dist/assets/index-C0VmkrO-.js +2 -0
- data/web/dist/assets/index-CI74IkWP.js +198 -0
- data/web/dist/assets/index-CYxcbpRM.js +3 -0
- data/web/dist/assets/index-D0rmJN3h.js +2 -0
- data/web/dist/assets/index-DB4QwuYn.js +2 -0
- data/web/dist/assets/index-DDI7zoAS.js +2 -0
- data/web/dist/assets/index-DGLUuEXi.js +2 -0
- data/web/dist/assets/index-OR-odsl9.js +2 -0
- data/web/dist/assets/index-P3vSkiU-.js +2 -0
- data/web/dist/assets/index-a4EHR-u4.css +1 -0
- data/web/dist/assets/index-mRV4XhJZ.js +2 -0
- data/web/dist/assets/index-popk9wbW.js +2 -0
- data/web/dist/assets/index-uM88Njdj.js +2 -0
- data/web/dist/assets/javascript-iXu5QeM3.js +2 -0
- data/web/dist/assets/julia-DuME0IfC.js +2 -0
- data/web/dist/assets/livescript-BwQOo05w.js +2 -0
- data/web/dist/assets/logo-on-dark-DI9gCdN4.png +0 -0
- data/web/dist/assets/logo-on-light-BciwqxcJ.png +0 -0
- data/web/dist/assets/lua-VAEuO923.js +2 -0
- data/web/dist/assets/mathematica-DTrFuWx2.js +2 -0
- data/web/dist/assets/mbox-CNhZ1qSd.js +2 -0
- data/web/dist/assets/mirc-CjQqDB4T.js +2 -0
- data/web/dist/assets/mllike-CXdrOF99.js +2 -0
- data/web/dist/assets/modelica-Dc1JOy9r.js +2 -0
- data/web/dist/assets/mscgen-BA5vi2Kp.js +2 -0
- data/web/dist/assets/mumps-BT43cFF4.js +2 -0
- data/web/dist/assets/nginx-DdIZxoE0.js +2 -0
- data/web/dist/assets/nsis-LY0VT_u5.js +2 -0
- data/web/dist/assets/ntriples-BfvgReVJ.js +2 -0
- data/web/dist/assets/octave-Ck1zUtKM.js +2 -0
- data/web/dist/assets/overpass-cyrillic-400-normal-CZsFKZCk.woff +0 -0
- data/web/dist/assets/overpass-cyrillic-400-normal-tXDxWGRy.woff2 +0 -0
- data/web/dist/assets/overpass-cyrillic-600-normal-B-hc8bdn.woff2 +0 -0
- data/web/dist/assets/overpass-cyrillic-600-normal-CXB2Mrig.woff +0 -0
- data/web/dist/assets/overpass-cyrillic-800-normal-CP6RWG8X.woff2 +0 -0
- data/web/dist/assets/overpass-cyrillic-800-normal-PJW4A9xW.woff +0 -0
- data/web/dist/assets/overpass-cyrillic-ext-400-normal-AKUz2ZI5.woff2 +0 -0
- data/web/dist/assets/overpass-cyrillic-ext-400-normal-C2ZjKngx.woff +0 -0
- data/web/dist/assets/overpass-cyrillic-ext-600-normal-CW4Fknsm.woff2 +0 -0
- data/web/dist/assets/overpass-cyrillic-ext-600-normal-Zx-mtRSD.woff +0 -0
- data/web/dist/assets/overpass-cyrillic-ext-800-normal-CIth-cA7.woff2 +0 -0
- data/web/dist/assets/overpass-cyrillic-ext-800-normal-hx3cL3ZN.woff +0 -0
- data/web/dist/assets/overpass-latin-400-normal-BpeLJ0bs.woff2 +0 -0
- data/web/dist/assets/overpass-latin-400-normal-c_C8zulu.woff +0 -0
- data/web/dist/assets/overpass-latin-600-normal-25RhTNCi.woff2 +0 -0
- data/web/dist/assets/overpass-latin-600-normal-DhYQrzaY.woff +0 -0
- data/web/dist/assets/overpass-latin-800-normal-Bzx_XP5l.woff2 +0 -0
- data/web/dist/assets/overpass-latin-800-normal-D6-q4f0l.woff +0 -0
- data/web/dist/assets/overpass-latin-ext-400-normal-BIkXRwl6.woff2 +0 -0
- data/web/dist/assets/overpass-latin-ext-400-normal-DUq_h-_z.woff +0 -0
- data/web/dist/assets/overpass-latin-ext-600-normal-Cyj4Nio0.woff2 +0 -0
- data/web/dist/assets/overpass-latin-ext-600-normal-DQKKMGzp.woff +0 -0
- data/web/dist/assets/overpass-latin-ext-800-normal-D6WKWGNs.woff +0 -0
- data/web/dist/assets/overpass-latin-ext-800-normal-DZ60AB77.woff2 +0 -0
- data/web/dist/assets/overpass-mono-cyrillic-400-normal-B144qALP.woff2 +0 -0
- data/web/dist/assets/overpass-mono-cyrillic-400-normal-XK9_jwT8.woff +0 -0
- data/web/dist/assets/overpass-mono-cyrillic-ext-400-normal-Dgejvo4A.woff2 +0 -0
- data/web/dist/assets/overpass-mono-cyrillic-ext-400-normal-DgxwtUZy.woff +0 -0
- data/web/dist/assets/overpass-mono-latin-400-normal-DqAOLzCO.woff +0 -0
- data/web/dist/assets/overpass-mono-latin-400-normal-VINZG6Js.woff2 +0 -0
- data/web/dist/assets/overpass-mono-latin-ext-400-normal-5YIw-E8g.woff +0 -0
- data/web/dist/assets/overpass-mono-latin-ext-400-normal-DBdJzBs_.woff2 +0 -0
- data/web/dist/assets/overpass-mono-vietnamese-400-normal-DrLp_cHj.woff +0 -0
- data/web/dist/assets/overpass-mono-vietnamese-400-normal-jmOeL0rj.woff2 +0 -0
- data/web/dist/assets/overpass-vietnamese-400-normal-BKRCEiXy.woff2 +0 -0
- data/web/dist/assets/overpass-vietnamese-400-normal-CCWAWVG7.woff +0 -0
- data/web/dist/assets/overpass-vietnamese-600-normal-CjS-4vQf.woff2 +0 -0
- data/web/dist/assets/overpass-vietnamese-600-normal-DlMMlPfM.woff +0 -0
- data/web/dist/assets/overpass-vietnamese-800-normal-BB8K9-Vo.woff2 +0 -0
- data/web/dist/assets/overpass-vietnamese-800-normal-DZUygvj_.woff +0 -0
- data/web/dist/assets/oz-BzwKVEFT.js +2 -0
- data/web/dist/assets/pascal--L3eBynH.js +2 -0
- data/web/dist/assets/perl-CdXCOZ3F.js +2 -0
- data/web/dist/assets/pig-CevX1Tat.js +2 -0
- data/web/dist/assets/powershell-CFHJl5sT.js +2 -0
- data/web/dist/assets/properties-C78fOPTZ.js +2 -0
- data/web/dist/assets/protobuf-ChK-085T.js +2 -0
- data/web/dist/assets/pug-DeIclll2.js +2 -0
- data/web/dist/assets/puppet-DMA9R1ak.js +2 -0
- data/web/dist/assets/python-BuPzkPfP.js +2 -0
- data/web/dist/assets/q-pXgVlZs6.js +2 -0
- data/web/dist/assets/r-B6wPVr8A.js +2 -0
- data/web/dist/assets/rpm-CTu-6PCP.js +2 -0
- data/web/dist/assets/ruby-BK5aLJev.js +2 -0
- data/web/dist/assets/sas-B4kiWyti.js +2 -0
- data/web/dist/assets/scheme-C41bIUwD.js +2 -0
- data/web/dist/assets/shell-CjFT_Tl9.js +2 -0
- data/web/dist/assets/sieve-C3Gn_uJK.js +2 -0
- data/web/dist/assets/simple-mode-GW_nhZxv.js +2 -0
- data/web/dist/assets/smalltalk-CnHTOXQT.js +2 -0
- data/web/dist/assets/solr-DehyRSwq.js +2 -0
- data/web/dist/assets/sparql-DkYu6x3z.js +2 -0
- data/web/dist/assets/spreadsheet-BCZA_wO0.js +2 -0
- data/web/dist/assets/sql-D0XecflT.js +2 -0
- data/web/dist/assets/stex-C3f8Ysf7.js +2 -0
- data/web/dist/assets/stylus-B533Al4x.js +2 -0
- data/web/dist/assets/swift-BzpIVaGY.js +2 -0
- data/web/dist/assets/tcl-DVfN8rqt.js +2 -0
- data/web/dist/assets/textile-CnDTJFAw.js +2 -0
- data/web/dist/assets/tiddlywiki-DO-Gjzrf.js +2 -0
- data/web/dist/assets/tiki-DGYXhP31.js +2 -0
- data/web/dist/assets/toml-Bm5Em-hy.js +2 -0
- data/web/dist/assets/troff-wAsdV37c.js +2 -0
- data/web/dist/assets/ttcn-CfJYG6tj.js +2 -0
- data/web/dist/assets/ttcn-cfg-B9xdYoR4.js +2 -0
- data/web/dist/assets/turtle-B1tBg_DP.js +2 -0
- data/web/dist/assets/vb-CmGdzxic.js +2 -0
- data/web/dist/assets/vbscript-BuJXcnF6.js +2 -0
- data/web/dist/assets/velocity-D8B20fx6.js +2 -0
- data/web/dist/assets/verilog-C6RDOZhf.js +2 -0
- data/web/dist/assets/vhdl-lSbBsy5d.js +2 -0
- data/web/dist/assets/webidl-ZXfAyPTL.js +2 -0
- data/web/dist/assets/xquery-DzFWVndE.js +2 -0
- data/web/dist/assets/yacas-BJ4BC0dw.js +2 -0
- data/web/dist/assets/z80-Hz9HOZM7.js +2 -0
- data/web/dist/index.html +14 -0
- data/web/dist/vite.svg +1 -0
- data/web/src/api/fixtures/board.json +512 -0
- metadata +907 -0
data/bin/factory-e2e
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
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
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Pull Requests
|
|
2
|
+
|
|
3
|
+
| Pull Request Number | Pull Request Name | Status |
|
|
4
|
+
| ------------------: | :---------------- | -----: |
|
|
5
|
+
| 12 | [[001.00] Quantity floor](https://github.com/example/shop/pull/12) | Open 🟡 |
|
|
6
|
+
|
|
7
|
+
Looks good. 👍🏼 to deploy
|
|
8
|
+
|
|
9
|
+
> [!NOTE]
|
|
10
|
+
>
|
|
11
|
+
> [2026-09-04 05:00:00 PM PDT] [ agent: hansolo-reviewer status: Started, round 1 ]
|
|
12
|
+
> [2026-09-04 05:01:02 PM PDT] [ agent: hansolo-reviewer status: Completed, round 1 (approved) ]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"seconds": 62, "tokens": 18000, "state": "in_review"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Pull Requests
|
|
2
|
+
|
|
3
|
+
| Pull Request Number | Pull Request Name | Status |
|
|
4
|
+
| ------------------: | :---------------- | -----: |
|
|
5
|
+
| 12 | [[001.00] Quantity floor](https://github.com/example/shop/pull/12) | Open 🟡 |
|
|
6
|
+
|
|
7
|
+
## Findings
|
|
8
|
+
|
|
9
|
+
1. `lib/cart.rb:5` rejects only negatives, so `add("A1", 0)` returns 0 instead of raising. The spec says zero included. Failing scenario: `expect { cart.add("A1", 0) }.to raise_error(Cart::InvalidQuantity)`.
|
|
10
|
+
2. `spec/cart_spec.rb` has no example for zero, which is how the defect got past CI.
|
|
11
|
+
|
|
12
|
+
> [!NOTE]
|
|
13
|
+
>
|
|
14
|
+
> [2026-09-04 05:00:00 PM PDT] [ agent: hansolo-reviewer status: Started, round 1 ]
|
|
15
|
+
> [2026-09-04 05:03:10 PM PDT] [ agent: hansolo-reviewer status: Completed, round 1 (rejected 1/2) ]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"seconds": 190, "tokens": 61000, "state": "in_review"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
id: review-medium
|
|
2
|
+
agent: hansolo-reviewer
|
|
3
|
+
depth: medium
|
|
4
|
+
description: >
|
|
5
|
+
A pull request with a seeded defect: the spec asks to reject quantities
|
|
6
|
+
below 1, the code rejects only negatives, so zero slips through and no
|
|
7
|
+
test covers it. hansolo must find it, list it, and sign rejected; an
|
|
8
|
+
approval means the defect was missed. Reviewers write no code.
|
|
9
|
+
fixture:
|
|
10
|
+
state: ready_for_review
|
|
11
|
+
slug: quantity-floor
|
|
12
|
+
files:
|
|
13
|
+
spec.md: |
|
|
14
|
+
# Quantity Floor
|
|
15
|
+
|
|
16
|
+
## Goal
|
|
17
|
+
|
|
18
|
+
`Cart#add` rejects any quantity below 1, zero included.
|
|
19
|
+
plan.md: |
|
|
20
|
+
# Plan
|
|
21
|
+
|
|
22
|
+
1. U1 Reject quantities below 1 in `lib/cart.rb`, with a spec.
|
|
23
|
+
pull-requests.md: |
|
|
24
|
+
# Pull Requests
|
|
25
|
+
|
|
26
|
+
| Pull Request Number | Pull Request Name | Status |
|
|
27
|
+
| ------------------: | :---------------- | -----: |
|
|
28
|
+
| 12 | [[001.00] Quantity floor](https://github.com/example/shop/pull/12) | Open 🟡 |
|
|
29
|
+
repo:
|
|
30
|
+
lib/cart.rb: |
|
|
31
|
+
class Cart
|
|
32
|
+
class InvalidQuantity < ArgumentError; end
|
|
33
|
+
|
|
34
|
+
def add(sku, quantity)
|
|
35
|
+
raise InvalidQuantity if quantity.negative?
|
|
36
|
+
|
|
37
|
+
quantity
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
spec/cart_spec.rb: |
|
|
41
|
+
require_relative "../lib/cart"
|
|
42
|
+
|
|
43
|
+
RSpec.describe Cart do
|
|
44
|
+
it "rejects a negative quantity" do
|
|
45
|
+
expect { described_class.new.add("A1", -1) }.to raise_error(Cart::InvalidQuantity)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
expect:
|
|
49
|
+
signed: rejected
|
|
50
|
+
files_absent: [rewrite.md]
|
|
51
|
+
sections:
|
|
52
|
+
pull-requests.md: [Findings]
|
|
53
|
+
contains:
|
|
54
|
+
pull-requests.md: [lib/cart.rb, "/[Zz]ero|\\b0\\b/"]
|
|
55
|
+
untouched: ["**/*"]
|
|
56
|
+
depth:
|
|
57
|
+
medium:
|
|
58
|
+
count: { file: pull-requests.md, section: Findings, pattern: '^[ \t]*(?:[-*]|\d+\.)[ \t]', min: 1, max: 6 }
|
|
59
|
+
max_seconds: 300
|
|
60
|
+
max_tokens: 150000
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Cart Retention
|
|
2
|
+
|
|
3
|
+
Delete abandoned carts automatically.
|
|
4
|
+
|
|
5
|
+
## Research
|
|
6
|
+
|
|
7
|
+
1. Carts are never deleted today (`lib/cart_store.rb:9`).
|
|
8
|
+
|
|
9
|
+
## Constraints
|
|
10
|
+
|
|
11
|
+
- Deletion runs nightly.
|
|
12
|
+
|
|
13
|
+
> [!NOTE]
|
|
14
|
+
>
|
|
15
|
+
> [2026-09-04 06:00:00 PM PDT] [ agent: lando-broker status: Started, round 1 ]
|
|
16
|
+
> [2026-09-04 06:00:19 PM PDT] [ agent: lando-broker status: Completed, round 1 ]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"seconds": 19, "tokens": 6100, "state": "product_blocked"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Cart Retention
|
|
2
|
+
|
|
3
|
+
Delete abandoned carts automatically.
|
|
4
|
+
|
|
5
|
+
## Research
|
|
6
|
+
|
|
7
|
+
1. Carts are never deleted today (`lib/cart_store.rb:9`).
|
|
8
|
+
|
|
9
|
+
## Constraints
|
|
10
|
+
|
|
11
|
+
- Deletion runs nightly.
|
|
12
|
+
- An abandoned cart is kept for ninety days, counted from the last change to the cart, then deleted.
|
|
13
|
+
Decided 2026-09-01 by the product owner: retention is ninety days from the last change.
|
|
14
|
+
|
|
15
|
+
> [!NOTE]
|
|
16
|
+
>
|
|
17
|
+
> [2026-09-04 06:00:00 PM PDT] [ agent: lando-broker status: Started, round 1 ]
|
|
18
|
+
> [2026-09-04 06:00:41 PM PDT] [ agent: lando-broker status: Completed, round 1 ]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"seconds": 41, "tokens": 9800, "state": "researched"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
id: unblock-fast
|
|
2
|
+
agent: lando-broker
|
|
3
|
+
depth: fast
|
|
4
|
+
description: >
|
|
5
|
+
One answered product question. lando must fold the answer into spec.md as
|
|
6
|
+
settled fact with a provenance line, delete blocked.md because nothing is
|
|
7
|
+
left open, and not create plan.md. The folder then resyncs out of 🅱️.
|
|
8
|
+
fixture:
|
|
9
|
+
state: product_blocked
|
|
10
|
+
slug: cart-retention
|
|
11
|
+
files:
|
|
12
|
+
spec.md: |
|
|
13
|
+
# Cart Retention
|
|
14
|
+
|
|
15
|
+
Delete abandoned carts automatically.
|
|
16
|
+
|
|
17
|
+
## Research
|
|
18
|
+
|
|
19
|
+
1. Carts are never deleted today (`lib/cart_store.rb:9`).
|
|
20
|
+
|
|
21
|
+
## Constraints
|
|
22
|
+
|
|
23
|
+
- Deletion runs nightly.
|
|
24
|
+
blocked.md: |
|
|
25
|
+
# Blocked
|
|
26
|
+
|
|
27
|
+
## B1 Retention period
|
|
28
|
+
|
|
29
|
+
How long is an abandoned cart kept before it is deleted?
|
|
30
|
+
|
|
31
|
+
## A1
|
|
32
|
+
|
|
33
|
+
Ninety days, counted from the last change to the cart. Decided by the product owner on 2026-09-01.
|
|
34
|
+
expect:
|
|
35
|
+
state: researched
|
|
36
|
+
signed: true
|
|
37
|
+
files_absent: [blocked.md, plan.md]
|
|
38
|
+
contains:
|
|
39
|
+
spec.md: ["/[Nn]inety days|90 days/", "Decided "]
|
|
40
|
+
depth:
|
|
41
|
+
fast:
|
|
42
|
+
count: { file: spec.md, pattern: "Decided ", min: 1, max: 1 }
|
|
43
|
+
max_seconds: 120
|
|
44
|
+
max_tokens: 40000
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Filing Deadlines
|
|
2
|
+
|
|
3
|
+
Show each small business its next estimated-tax filing deadline, moved for weekends and holidays.
|
|
4
|
+
|
|
5
|
+
## What research needs to settle
|
|
6
|
+
|
|
7
|
+
1. Which deadlines apply, and where do they come from?
|
|
8
|
+
2. How is a deadline moved when it falls on a weekend or holiday?
|
|
9
|
+
3. Does the codebase already know about holidays?
|
|
10
|
+
|
|
11
|
+
## Research
|
|
12
|
+
|
|
13
|
+
Four quarterly deadlines apply (https://tax.example.gov/estimated-payments). Weekend dates move to Monday. `lib/calendar.rb` has a holiday list.
|
|
14
|
+
|
|
15
|
+
### Contradictions
|
|
16
|
+
|
|
17
|
+
None found.
|
|
18
|
+
|
|
19
|
+
### Could not settle
|
|
20
|
+
|
|
21
|
+
Nothing.
|
|
22
|
+
|
|
23
|
+
> [!NOTE]
|
|
24
|
+
>
|
|
25
|
+
> [2026-09-04 11:29:20 AM PDT] [ agent: leah-researcher status: Started, round 1 ]
|
|
26
|
+
> [2026-09-04 11:30:12 AM PDT] [ agent: leah-researcher status: Completed, round 1 ]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"seconds": 52, "tokens": 12400, "state": "researched"}
|