robot_lab-to 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +1 -0
  3. data/.github/workflows/deploy-github-pages.yml +52 -0
  4. data/.loki +21 -0
  5. data/.quality/reek_baseline.txt +17 -0
  6. data/.rubocop.yml +7 -0
  7. data/CHANGELOG.md +67 -0
  8. data/CLAUDE.md +74 -0
  9. data/COMMITS.md +27 -0
  10. data/README.md +352 -0
  11. data/Rakefile +132 -0
  12. data/bin/robot-to +14 -0
  13. data/docs/about/changelog.md +82 -0
  14. data/docs/assets/architecture.svg +110 -0
  15. data/docs/assets/images/robot_lab-to.jpg +0 -0
  16. data/docs/assets/iteration-loop.svg +97 -0
  17. data/docs/concepts/evals.md +254 -0
  18. data/docs/concepts/iteration-loop.md +128 -0
  19. data/docs/concepts/notes.md +91 -0
  20. data/docs/concepts/run-state.md +93 -0
  21. data/docs/concepts/stop-conditions.md +111 -0
  22. data/docs/concepts/verification.md +75 -0
  23. data/docs/configuration/cli.md +111 -0
  24. data/docs/configuration/index.md +93 -0
  25. data/docs/configuration/settings.md +270 -0
  26. data/docs/getting-started/anatomy-of-a-run.md +111 -0
  27. data/docs/getting-started/installation.md +78 -0
  28. data/docs/getting-started/quick-start.md +105 -0
  29. data/docs/index.md +96 -0
  30. data/docs/local-models/guardrails.md +117 -0
  31. data/docs/local-models/index.md +88 -0
  32. data/docs/local-models/ollama.md +122 -0
  33. data/docs/local-models/tools.md +92 -0
  34. data/docs/reference/architecture.md +139 -0
  35. data/examples/01_basic_usage/.gitignore +9 -0
  36. data/examples/01_basic_usage/README.md +173 -0
  37. data/examples/01_basic_usage/basic_usage.rb +386 -0
  38. data/examples/02_advanced_usage/.gitignore +8 -0
  39. data/examples/02_advanced_usage/README.md +117 -0
  40. data/examples/02_advanced_usage/advanced_usage.rb +308 -0
  41. data/examples/02_advanced_usage/quality_gate.rb +99 -0
  42. data/examples/03_scored/.gitignore +2 -0
  43. data/examples/03_scored/README.md +117 -0
  44. data/examples/03_scored/scored_run.rb +325 -0
  45. data/examples/04_prose/.gitignore +2 -0
  46. data/examples/04_prose/README.md +43 -0
  47. data/examples/04_prose/prompts_dir/grade_message.md +14 -0
  48. data/examples/04_prose/prompts_dir/judge.md +11 -0
  49. data/examples/04_prose/prompts_dir/outline_criteria.md +9 -0
  50. data/examples/04_prose/prompts_dir/outline_objective.md +11 -0
  51. data/examples/04_prose/prompts_dir/section_criteria.md +7 -0
  52. data/examples/04_prose/prompts_dir/sections_objective.md +14 -0
  53. data/examples/04_prose/prose_run.rb +302 -0
  54. data/lib/robot_lab/to/atomic_file.rb +69 -0
  55. data/lib/robot_lab/to/backoff.rb +46 -0
  56. data/lib/robot_lab/to/cli.rb +176 -0
  57. data/lib/robot_lab/to/commit_manager.rb +135 -0
  58. data/lib/robot_lab/to/config/defaults.yml +27 -0
  59. data/lib/robot_lab/to/config.rb +86 -0
  60. data/lib/robot_lab/to/decision.rb +40 -0
  61. data/lib/robot_lab/to/decision_manager.rb +193 -0
  62. data/lib/robot_lab/to/errors.rb +30 -0
  63. data/lib/robot_lab/to/evals/base.rb +23 -0
  64. data/lib/robot_lab/to/evals/code.rb +77 -0
  65. data/lib/robot_lab/to/evals/context.rb +17 -0
  66. data/lib/robot_lab/to/evals/factory.rb +84 -0
  67. data/lib/robot_lab/to/evals/null.rb +17 -0
  68. data/lib/robot_lab/to/evals/prose.rb +110 -0
  69. data/lib/robot_lab/to/evals/score.rb +22 -0
  70. data/lib/robot_lab/to/exit_summary.rb +75 -0
  71. data/lib/robot_lab/to/guards/checkpoint.rb +84 -0
  72. data/lib/robot_lab/to/guards/grader_lock.rb +62 -0
  73. data/lib/robot_lab/to/guards/path_resolution.rb +61 -0
  74. data/lib/robot_lab/to/guards/quality_monitor.rb +113 -0
  75. data/lib/robot_lab/to/guards/read_before_edit.rb +83 -0
  76. data/lib/robot_lab/to/guards/run_store.rb +31 -0
  77. data/lib/robot_lab/to/guards/write_guard.rb +71 -0
  78. data/lib/robot_lab/to/guards.rb +54 -0
  79. data/lib/robot_lab/to/iteration_result.rb +29 -0
  80. data/lib/robot_lab/to/jsonl_logger.rb +59 -0
  81. data/lib/robot_lab/to/notes_manager.rb +121 -0
  82. data/lib/robot_lab/to/orchestrator.rb +642 -0
  83. data/lib/robot_lab/to/prompt_builder.rb +208 -0
  84. data/lib/robot_lab/to/run.rb +109 -0
  85. data/lib/robot_lab/to/stop_conditions.rb +71 -0
  86. data/lib/robot_lab/to/tools/bash.rb +75 -0
  87. data/lib/robot_lab/to/tools/edit.rb +52 -0
  88. data/lib/robot_lab/to/tools/file_tool.rb +32 -0
  89. data/lib/robot_lab/to/tools/read.rb +57 -0
  90. data/lib/robot_lab/to/tools/request_decision.rb +66 -0
  91. data/lib/robot_lab/to/tools/submit_result.rb +50 -0
  92. data/lib/robot_lab/to/tools/write.rb +32 -0
  93. data/lib/robot_lab/to/verifier.rb +63 -0
  94. data/lib/robot_lab/to/version.rb +7 -0
  95. data/lib/robot_lab/to.rb +81 -0
  96. data/mkdocs.yml +145 -0
  97. metadata +175 -0
@@ -0,0 +1,75 @@
1
+ # Verification Gate
2
+
3
+ A robot reporting `success: true` is a *claim*, not a fact. The verification gate
4
+ makes that claim count only when an independent command — one you choose — agrees.
5
+
6
+ This is separation of duties applied to a single agent: the **robot does the
7
+ work**, but the **orchestrator runs the deciding command**. The robot cannot
8
+ self-certify a passing verdict.
9
+
10
+ ## Enabling it
11
+
12
+ ```bash
13
+ robot-to "Fix the failing parser tests" \
14
+ --verify-command "bundle exec rake test" \
15
+ --verify-timeout 600
16
+ ```
17
+
18
+ When `--verify-command` is set, every iteration the robot marks successful is
19
+ gated:
20
+
21
+ ```
22
+ robot says success ──► run verify-command ──► exit 0 ? ──► COMMIT
23
+
24
+ │ non-zero / timeout
25
+
26
+ ROLLBACK (recorded as [VERIFY FAILED])
27
+ ```
28
+
29
+ If verification fails, the working tree is reset with `git reset --hard`, the
30
+ iteration counts as a failure (incrementing the consecutive-failure counter), and
31
+ the verification output is written to `notes.md` so the next robot can see *why*
32
+ it failed.
33
+
34
+ ## How it runs
35
+
36
+ The `Verifier`:
37
+
38
+ - Runs the command in the project working directory via `Open3.popen2e`.
39
+ - Launches it in its own **process group** so a timeout can kill the whole
40
+ process tree — test runners that spawn children won't survive a timeout.
41
+ - Enforces `--verify-timeout` seconds (default **600**). On timeout the process
42
+ group is terminated and the gate fails.
43
+ - Captures combined stdout+stderr, clamped to ~4 000 characters for the notes.
44
+
45
+ A non-zero exit **or** a timeout fails the gate.
46
+
47
+ ## Choosing a verify command
48
+
49
+ The command should be the real signal of correctness for your objective:
50
+
51
+ | Objective | Good `--verify-command` |
52
+ |-----------|-------------------------|
53
+ | Fix failing tests | `bundle exec rake test` |
54
+ | Keep the build green | `npm run build` |
55
+ | Don't regress lint | `bundle exec rubocop` |
56
+ | Full quality bar | `bundle exec rake` (tests + lint + coverage) |
57
+
58
+ Tips:
59
+
60
+ - Make it **fast enough to run every iteration** — it runs on each claimed
61
+ success.
62
+ - Make it **deterministic** — flaky commands cause spurious rollbacks.
63
+ - Combine checks with `&&` if you want several gates: `--verify-command "bundle
64
+ exec rake test && bundle exec rubocop"`.
65
+
66
+ ## Without a verify command
67
+
68
+ If you don't set `--verify-command`, the robot's self-reported `success` is taken
69
+ at face value and committed. This is fine for low-risk objectives (docs,
70
+ comments, scaffolding) but for anything that must keep working, a verification
71
+ gate is strongly recommended.
72
+
73
+ ---
74
+
75
+ Next: [Evals: Scoring Iterations](evals.md).
@@ -0,0 +1,111 @@
1
+ # CLI Reference
2
+
3
+ The `robot-to` executable runs an autonomous loop toward an objective.
4
+
5
+ ## Synopsis
6
+
7
+ ```
8
+ robot-to [objective] [options]
9
+ ```
10
+
11
+ The **objective** is a single argument (quote it). If omitted, `robot-to` reads
12
+ the objective from standard input — handy for long objectives:
13
+
14
+ ```bash
15
+ robot-to "Add request logging middleware and tests"
16
+
17
+ # or via stdin
18
+ echo "Add request logging middleware and tests" | robot-to
19
+ ```
20
+
21
+ ## Options
22
+
23
+ | Flag | Argument | Default | Description |
24
+ |------|----------|---------|-------------|
25
+ | `--provider` | `NAME` | `openai` | LLM provider (`anthropic`, `openai`, …). |
26
+ | `--model` | `MODEL` | `gpt-5.5` | Model identifier for the provider. |
27
+ | `--max-iterations` | `N` | *unlimited* | Stop after `N` iterations. |
28
+ | `--max-tokens` | `N` | *unlimited* | Stop after `N` total tokens. |
29
+ | `--stop-when` | `CONDITION` | *unset* | Natural-language stop condition the robot evaluates. |
30
+ | `--max-consecutive-failures` | `N` | `3` | Abort after `N` consecutive failures. |
31
+ | `--verify-command` | `CMD` | *unset* | Command that must pass before a success is committed. |
32
+ | `--verify-timeout` | `SECONDS` | `600` | Timeout for `--verify-command`. |
33
+ | `--max-verify-repairs` | `N` | `2` | Repair-in-place attempts when the eval's gate fails before rolling back. |
34
+ | `--eval` | `code`\|`null`\|`prose` | *code if verify/measure/target set, else null* | Eval strategy — see [Evals](../concepts/evals.md). |
35
+ | `--measure` | `CMD` | *unset* | Command printing a numeric score (higher = better); drives `Evals::Code`. |
36
+ | `--target` | `FLOAT` | *unset* | Stop once the measured score reaches this. |
37
+ | `--spec` | `PATH` | *unset* | Spec/outline artifact `Evals::Prose`'s judge measures against. |
38
+ | `--floor` | `CMD` | *unset* | Mechanizable floor check for prose (links, outline coverage, AI-tells). |
39
+ | `--judge-model` | `MODEL` | *`--model`* | Model for the pairwise prose judge. |
40
+ | `--stop-on-plateau` | `N` | *unset — off* | Stop after `N` iterations with no committed improvement. |
41
+ | `--[no-]require-improvement` | — | on | Roll back gate-passing iterations that don't beat the parent (default on). |
42
+ | `--protect-path` | `GLOB` (repeatable) | *none* | Lock a grader file from robot edits (always-on `GraderLock`). |
43
+ | `--commit-format` | `default`\|`conventional` | `default` | Commit message format. |
44
+ | `--run-dir` | `PATH` | `.robot_lab_to` | Directory for run state. |
45
+ | `--local-guards` | — | off | Add built-in file tools + small-model guardrails. |
46
+ | `--no-stream` | — | streaming on | Disable streaming (required for local Ollama tool calls). |
47
+ | `--debug` | — | off | Keep verbose provider logging enabled. |
48
+ | `--version` | — | — | Print version and exit. |
49
+ | `-h`, `--help` | — | — | Show help and exit. |
50
+
51
+ See the [Settings Reference](settings.md) for the full meaning of each option and
52
+ its config-file / environment-variable equivalents.
53
+
54
+ ## Examples
55
+
56
+ A bounded run with a verification gate:
57
+
58
+ ```bash
59
+ robot-to "Fix the failing parser tests" \
60
+ --provider anthropic \
61
+ --model claude-sonnet-4-6 \
62
+ --max-iterations 15 \
63
+ --verify-command "bundle exec rake test"
64
+ ```
65
+
66
+ An overnight migration with a natural-language stop condition:
67
+
68
+ ```bash
69
+ robot-to "Migrate from Minitest to RSpec" \
70
+ --max-iterations 60 \
71
+ --max-tokens 3000000 \
72
+ --verify-command "bundle exec rake" \
73
+ --stop-when "every test file is RSpec and the suite passes" \
74
+ --commit-format conventional
75
+ ```
76
+
77
+ A scored run that descends toward a measured target (see [Evals](../concepts/evals.md)):
78
+
79
+ ```bash
80
+ robot-to "Raise parser coverage to 90%" \
81
+ --verify "bundle exec rake test" \
82
+ --measure "bundle exec rake coverage" \
83
+ --target 90 \
84
+ --protect-path test/coverage_helper.rb
85
+ ```
86
+
87
+ A judged prose run that ends on plateau instead of a target:
88
+
89
+ ```bash
90
+ robot-to "Write an opinionated guide to the Viable Systems Model" \
91
+ --eval prose --spec outline.md --floor "rake docs:lint" \
92
+ --stop-on-plateau 3
93
+ ```
94
+
95
+ A fully local run on Ollama (see [Local Models](../local-models/index.md)):
96
+
97
+ ```bash
98
+ robot-to "Add a greet(name) method in greeter.rb" \
99
+ --provider openai \
100
+ --model gpt-oss:20b \
101
+ --local-guards \
102
+ --no-stream \
103
+ --max-iterations 5
104
+ ```
105
+
106
+ ## Exit behavior
107
+
108
+ `robot-to` runs to completion or until a stop condition trips, then prints an
109
+ [exit summary](../getting-started/anatomy-of-a-run.md#the-exit-summary). Press
110
+ **Ctrl-C** for a graceful stop — the in-flight iteration is rolled back, the loop
111
+ exits, and the summary still prints.
@@ -0,0 +1,93 @@
1
+ # Configuration
2
+
3
+ `robot_lab-to` is configured through a layered cascade built on
4
+ [`myway_config`](https://github.com/MadBomber/myway_config). Lower layers provide
5
+ defaults; higher layers override them.
6
+
7
+ ## The cascade
8
+
9
+ From lowest to highest precedence:
10
+
11
+ ```
12
+ 1. Bundled defaults lib/robot_lab/to/config/defaults.yml
13
+ 2. User config file ~/.config/robot_lab/to.yml
14
+ 3. Environment variables ROBOT_LAB_TO_*
15
+ 4. CLI flags / To.run args (highest — always wins)
16
+ ```
17
+
18
+ A value set at a higher layer overrides the same value from any lower layer.
19
+
20
+ ## 1. Bundled defaults
21
+
22
+ Shipped with the gem. You never edit these, but they define the baseline:
23
+
24
+ ```yaml
25
+ defaults:
26
+ provider: openai
27
+ model: gpt-5.5
28
+ max_tool_rounds: 100
29
+ max_consecutive_failures: 3
30
+ max_retries: 2
31
+ max_submit_nudges: 1
32
+ verify_timeout: 600
33
+ commit_format: default
34
+ run_dir: .robot_lab_to
35
+ local_guards: false
36
+ stream: true
37
+ debug: false
38
+ ```
39
+
40
+ ## 2. User config file
41
+
42
+ Create `~/.config/robot_lab/to.yml` to set your own defaults — for example, to
43
+ always use a local model:
44
+
45
+ ```yaml
46
+ provider: openai # routes to Ollama's OpenAI-compatible endpoint
47
+ model: gpt-oss:20b
48
+ local_guards: true
49
+ stream: false
50
+ max_consecutive_failures: 4
51
+ ```
52
+
53
+ These apply to every `robot-to` run unless overridden by an env var or CLI flag.
54
+
55
+ ## 3. Environment variables
56
+
57
+ Every setting can be set via an environment variable prefixed with
58
+ `ROBOT_LAB_TO_`. Nested keys use a double underscore (rare here, since the config
59
+ is flat):
60
+
61
+ ```bash
62
+ export ROBOT_LAB_TO_MODEL="gpt-oss:20b"
63
+ export ROBOT_LAB_TO_LOCAL_GUARDS=true
64
+ export ROBOT_LAB_TO_MAX_CONSECUTIVE_FAILURES=4
65
+ ```
66
+
67
+ Provider **API keys** are *not* `robot_lab-to` settings — they are read by the
68
+ underlying provider (e.g. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`).
69
+
70
+ ## 4. CLI flags / `To.run` arguments
71
+
72
+ The highest-precedence layer. Anything passed on the command line (or as a keyword
73
+ to `RobotLab::To.run`) wins over all other layers:
74
+
75
+ ```bash
76
+ robot-to "..." --model claude-sonnet-4-6 --max-iterations 20
77
+ ```
78
+
79
+ ```ruby
80
+ RobotLab::To.run("...", model: "claude-sonnet-4-6", max_iterations: 20)
81
+ ```
82
+
83
+ Some options are **CLI-only** (no YAML default) because they are run-specific:
84
+ `--max-iterations`, `--max-tokens`, `--stop-when`, `--verify-command`, and the
85
+ whole [Evals](../concepts/evals.md) family (`--eval`, `--measure`, `--target`,
86
+ `--spec`, `--floor`, `--judge-model`, `--stop-on-plateau`,
87
+ `--[no-]require-improvement`, `--protect-path`). When unset they mean "no limit
88
+ / not configured".
89
+
90
+ ---
91
+
92
+ - See the [Settings Reference](settings.md) for every option in detail.
93
+ - See the [CLI Reference](cli.md) for every flag.
@@ -0,0 +1,270 @@
1
+ # Settings Reference
2
+
3
+ Every `robot_lab-to` setting, its default, and what it controls. Each setting can
4
+ be supplied via the user config file, an `ROBOT_LAB_TO_*` environment variable, a
5
+ CLI flag, or a `RobotLab::To.run` keyword argument (see
6
+ [the cascade](index.md#the-cascade)).
7
+
8
+ ## Model & provider
9
+
10
+ ### `provider`
11
+
12
+ - **Default:** `openai`
13
+ - **CLI:** `--provider NAME`
14
+
15
+ The LLM provider, passed through to RobotLab / RubyLLM. Use `anthropic`,
16
+ `openai`, etc. For **local Ollama models**, set this to `openai` and point
17
+ RubyLLM at Ollama's OpenAI-compatible endpoint — see [Ollama Setup](../local-models/ollama.md).
18
+
19
+ ### `model`
20
+
21
+ - **Default:** `gpt-5.5`
22
+ - **CLI:** `--model MODEL`
23
+
24
+ The model identifier for the chosen provider (e.g. `claude-sonnet-4-6`,
25
+ `gpt-5.5`, `gpt-oss:20b`).
26
+
27
+ ### `stream`
28
+
29
+ - **Default:** `true`
30
+ - **CLI:** `--no-stream` to disable
31
+
32
+ Whether to stream the model response. Streaming enables per-chunk token
33
+ accounting and mid-iteration token-budget enforcement. **Local Ollama models must
34
+ run with `--no-stream`** — Ollama suppresses tool calls when streaming. With
35
+ streaming off, tokens are accounted from each iteration's result instead. See
36
+ [Streaming and tool calls](../local-models/ollama.md#streaming-and-tool-calls).
37
+
38
+ ## Loop control
39
+
40
+ ### `max_iterations`
41
+
42
+ - **Default:** *(unset — no limit)*
43
+ - **CLI:** `--max-iterations N`
44
+
45
+ Stop after `N` iterations. CLI-only; strongly recommended for unattended runs.
46
+
47
+ ### `max_tokens`
48
+
49
+ - **Default:** *(unset — no limit)*
50
+ - **CLI:** `--max-tokens N`
51
+
52
+ Stop once cumulative input+output tokens reach `N`. CLI-only.
53
+
54
+ ### `max_consecutive_failures`
55
+
56
+ - **Default:** `3`
57
+ - **CLI:** `--max-consecutive-failures N`
58
+
59
+ Abort after `N` consecutive failed iterations. Resets to zero on each successful,
60
+ committed iteration. The primary safety net against a run going nowhere.
61
+
62
+ ### `max_verify_repairs`
63
+
64
+ - **Default:** `2`
65
+ - **CLI:** `--max-verify-repairs N`
66
+
67
+ When the robot claims success but `verify_command` fails, the orchestrator hands
68
+ the failure output back to the **same** robot — its chat and working tree intact —
69
+ and lets it fix the code, re-verifying after each attempt, up to `N` times before
70
+ rolling the iteration back. Nothing commits until the gate is green, so the git
71
+ history stays clean; a near-miss just isn't discarded. Set to `0` to roll back
72
+ immediately on the first verify failure. See [Verification Gate](../concepts/verification.md).
73
+
74
+ ### `stop_when`
75
+
76
+ - **Default:** *(unset)*
77
+ - **CLI:** `--stop-when "<condition>"`
78
+
79
+ A natural-language condition the robot evaluates after each successful iteration.
80
+ When the robot judges it met, the run stops. CLI-only. See
81
+ [Stop Conditions](../concepts/stop-conditions.md).
82
+
83
+ ### `max_submit_nudges`
84
+
85
+ - **Default:** `1`
86
+
87
+ How many times to re-prompt a robot that finished without calling
88
+ `submit_iteration_result`. Recovers the "thinks but doesn't act" case before
89
+ counting the iteration as a failure.
90
+
91
+ ### `max_retries`
92
+
93
+ - **Default:** `2`
94
+
95
+ How many consecutive *errors* (exceptions, e.g. transient API failures) to retry
96
+ before aborting. Retries use an interruptible exponential backoff
97
+ (`60 × 2ⁿ` seconds).
98
+
99
+ ### `max_tool_rounds`
100
+
101
+ - **Default:** `100`
102
+
103
+ Passed to the robot — the maximum number of tool-call rounds within a single
104
+ iteration before RobotLab's circuit breaker stops it.
105
+
106
+ ## Verification
107
+
108
+ ### `verify_command`
109
+
110
+ - **Default:** *(unset — no verification)*
111
+ - **CLI:** `--verify-command "CMD"`
112
+
113
+ A command that must exit `0` before a robot's claimed success is committed.
114
+ CLI-only. See [Verification Gate](../concepts/verification.md).
115
+
116
+ ### `verify_timeout`
117
+
118
+ - **Default:** `600` (seconds)
119
+ - **CLI:** `--verify-timeout SECONDS`
120
+
121
+ Maximum time `verify_command` may run before it is killed (whole process group)
122
+ and the gate fails.
123
+
124
+ ## Evals (scoring)
125
+
126
+ CLI-only (no YAML default) unless noted. See [Evals](../concepts/evals.md) for
127
+ the full behavioral model.
128
+
129
+ ### `eval`
130
+
131
+ - **Default:** *(unset — `Evals::Code` if `verify_command`/`eval_measure`/
132
+ `eval_target` is set, else the unscored `Evals::Null`)*
133
+ - **CLI:** `--eval code|null|prose`
134
+
135
+ Selects the scoring strategy. Via the Ruby API, `eval:` also accepts an instance
136
+ responding to `#score`, a proc, or a `Symbol` registered with
137
+ `RobotLab::To.register_eval` — the CLI flag itself only resolves the three named
138
+ strings.
139
+
140
+ ### `eval_measure`
141
+
142
+ - **Default:** *(unset)*
143
+ - **CLI:** `--measure CMD`
144
+
145
+ A command that prints a number to stdout (higher = better) — the descent signal
146
+ for `Evals::Code`. Setting this alone (without `verify_command`) still selects
147
+ `Evals::Code`, not `Evals::Null`.
148
+
149
+ ### `eval_target`
150
+
151
+ - **Default:** *(unset)*
152
+ - **CLI:** `--target FLOAT`
153
+
154
+ Stop the run once `eval_measure`'s value reaches this (`Evals::Code`).
155
+
156
+ ### `eval_spec`
157
+
158
+ - **Default:** *(unset)*
159
+ - **CLI:** `--spec PATH`
160
+
161
+ The spec/outline artifact `Evals::Prose`'s judge measures the draft against.
162
+ Automatically added to `Evals::Prose#protected_paths` — locked from robot edits
163
+ by `GraderLock`.
164
+
165
+ ### `eval_floor`
166
+
167
+ - **Default:** *(unset)*
168
+ - **CLI:** `--floor CMD`
169
+
170
+ An optional mechanizable check (e.g. link validation, outline coverage) that
171
+ must pass before `Evals::Prose` calls the judge at all. A failing floor is
172
+ scored `:worse` without spending a judge call.
173
+
174
+ ### `eval_judge_model`
175
+
176
+ - **Default:** *(unset — falls back to `model`)*
177
+ - **CLI:** `--judge-model MODEL`
178
+
179
+ The model used for `Evals::Prose`'s pairwise judge. Defaults to the doer's own
180
+ `model`; set this to use a different (often cheaper, or deliberately more
181
+ skeptical) model for the judge role.
182
+
183
+ ### `stop_on_plateau`
184
+
185
+ - **Default:** *(unset — off)*
186
+ - **CLI:** `--stop-on-plateau N`
187
+
188
+ Stop after `N` consecutive iterations with no committed improvement. The
189
+ primary terminator for `Evals::Prose`, which never sets a measurable target. See
190
+ [Stop Conditions](../concepts/stop-conditions.md#plateau-no-improvement).
191
+
192
+ ### `require_improvement`
193
+
194
+ - **Default:** `true` (CLI/Ruby-only hardcoded default; not in `defaults.yml`)
195
+ - **CLI:** `--[no-]require-improvement`
196
+
197
+ When true, a gate-passing iteration that doesn't beat the parent commit's score
198
+ is rolled back rather than committed. `--no-require-improvement` restores
199
+ "commit any gate-passing success."
200
+
201
+ ### `protect_paths`
202
+
203
+ - **Default:** `[]`
204
+ - **CLI:** `--protect-path GLOB` (repeatable)
205
+
206
+ Extra grader files (globs) locked from robot writes/edits/bash, on top of
207
+ whatever the configured eval's `#protected_paths` already locks. Always
208
+ enforced via `Guards::GraderLock` when the resolved set is non-empty —
209
+ independent of `local_guards`.
210
+
211
+ ## Local models
212
+
213
+ ### `local_guards`
214
+
215
+ - **Default:** `false`
216
+ - **CLI:** `--local-guards`
217
+
218
+ Attach the built-in file tools (`read`, `write`, `edit`, `bash`) and the
219
+ small-model guardrail hooks. Designed for local models that need a more
220
+ constrained, forgiving tool surface. See [Local Models](../local-models/index.md).
221
+
222
+ ### `write_guard`
223
+
224
+ - **Default:** `true` (Ruby-only — no CLI flag)
225
+ - **Ruby:** `RobotLab::To.run(..., write_guard: false)`
226
+
227
+ When `local_guards` is on, `write_guard: false` drops `Guards::WriteGuard` from
228
+ the installed set (`Guards.install(..., except: [Guards::WriteGuard])`), letting
229
+ the robot freely overwrite a file it is iteratively refining — WriteGuard
230
+ otherwise refuses `write` on a file that already exists and redirects to `edit`,
231
+ which is unhelpful for a robot rewriting a whole prose draft each pass. See
232
+ [Guardrails](../local-models/guardrails.md).
233
+
234
+ ## Git & output
235
+
236
+ ### `commit_format`
237
+
238
+ - **Default:** `default`
239
+ - **CLI:** `--commit-format default|conventional`
240
+
241
+ How commit messages are formatted:
242
+
243
+ === "default"
244
+
245
+ `robot-to <iteration>: <summary>`
246
+
247
+ === "conventional"
248
+
249
+ `<type>(<scope>): <summary>` — `type`/`scope` come from the robot's result
250
+ (defaulting to `chore`). Follows [Conventional Commits](https://www.conventionalcommits.org/).
251
+
252
+ ### `run_dir`
253
+
254
+ - **Default:** `.robot_lab_to`
255
+ - **CLI:** `--run-dir PATH`
256
+
257
+ Where run state (`runs/<run_id>/`) is written. Added to `.git/info/exclude`
258
+ automatically.
259
+
260
+ ### `debug`
261
+
262
+ - **Default:** `false`
263
+ - **CLI:** `--debug`
264
+
265
+ Keep verbose RubyLLM/RobotLab provider logging enabled. The JSONL event log is
266
+ written regardless.
267
+
268
+ ---
269
+
270
+ See the [CLI Reference](cli.md) for the command-line form of every option.
@@ -0,0 +1,111 @@
1
+ # Anatomy of a Run
2
+
3
+ A single `robot-to` invocation produces a git branch, a run directory, and a
4
+ post-run summary. This page walks through each artifact.
5
+
6
+ ## The branch
7
+
8
+ On startup the orchestrator:
9
+
10
+ 1. Verifies `HEAD` exists (at least one commit).
11
+ 2. Creates a branch named `robot-to/<slug>-<timestamp>` from the current `HEAD`.
12
+ 3. Records the `base_commit` SHA so the exit summary can diff the whole run.
13
+
14
+ Every successful iteration is committed onto this branch. Your original branch is
15
+ untouched.
16
+
17
+ ```
18
+ * 6a7137c robot-to 2: add docstring and confirm behavior ← iteration 2
19
+ * 5810ae0 robot-to 1: create greeter.rb with greet method ← iteration 1
20
+ * 362b8d9 initial ← base_commit
21
+ ```
22
+
23
+ ### Commit message format
24
+
25
+ Controlled by `--commit-format`:
26
+
27
+ === "default"
28
+
29
+ ```
30
+ robot-to 1: <result summary>
31
+ ```
32
+
33
+ === "conventional"
34
+
35
+ ```
36
+ feat(scope): <result summary>
37
+ ```
38
+
39
+ The `type` and `scope` come from the robot's submitted result (defaulting to
40
+ `chore`). See [Commit conventions](../configuration/settings.md#commit_format).
41
+
42
+ ## The run directory
43
+
44
+ All run state lives under `.robot_lab_to/runs/<run_id>/` (the directory is added
45
+ to `.git/info/exclude` automatically):
46
+
47
+ ```
48
+ .robot_lab_to/
49
+ └── runs/
50
+ └── 20260623-144554-3e5c27/
51
+ ├── notes.md # cross-iteration memory (human-readable)
52
+ ├── run.log # JSONL event log (machine-readable)
53
+ └── checkpoints/ # pre-edit file snapshots (local_guards only)
54
+ ```
55
+
56
+ - **`notes.md`** — written by the orchestrator after each iteration; read by the
57
+ robot at the start of the next. See [Cross-Iteration Memory](../concepts/notes.md).
58
+ - **`run.log`** — one JSON object per line, one per lifecycle event. See
59
+ [Run State & Event Log](../concepts/run-state.md).
60
+ - **`checkpoints/`** — first-write-wins file snapshots taken before a Write/Edit,
61
+ created only when `--local-guards` is active. See [Guardrails](../local-models/guardrails.md).
62
+
63
+ ## One iteration, step by step
64
+
65
+ ```
66
+ iteration:start → fresh robot built with prompt + notes
67
+ agent:run:start → robot.run() begins
68
+ (robot uses tools, calls submit_iteration_result)
69
+ agent:run:end
70
+ agent:nudge? → if no result submitted, re-ask (max_submit_nudges)
71
+ eval → the configured eval scores the working tree (if reported success)
72
+ commit:success → git add -A && commit (gate ok + improved)
73
+ — or —
74
+ iteration:no_improvement → git reset --hard (gate ok, not improved)
75
+ iteration:gate_failure → git reset --hard (after eval:repair) (gate failed)
76
+ iteration:failure → git reset --hard (robot reported failure / no submit)
77
+ ```
78
+
79
+ A committed iteration resets the consecutive-failure counter and the plateau
80
+ counter; a failure or gate failure increments consecutive-failures, and any
81
+ non-committing outcome increments the plateau counter (may trip
82
+ `--max-consecutive-failures` or `--stop-on-plateau`). See
83
+ [Evals](../concepts/evals.md) for the full scoring model — by default (no
84
+ `--verify-command`/`--eval`), every reported success commits, matching this
85
+ page's simpler mental model.
86
+
87
+ ## The exit summary
88
+
89
+ When the loop ends — whether it completed, hit a stop condition, or aborted —
90
+ `robot-to` prints a summary to stdout:
91
+
92
+ ```
93
+ robot-to complete — claude-sonnet-4-6 — 4m 12s
94
+ Branch: robot-to/migrate-to-rspec-20260623-144554
95
+
96
+ Iterations: 8 (6 committed, 2 rolled back)
97
+ Changes: +312 / -47 across 14 files
98
+ Tokens: 1,204,883 in / 38,902 out
99
+
100
+ Run state: .robot_lab_to/runs/20260623-144554-3e5c27/
101
+ Next steps:
102
+ git log robot-to/migrate-to-rspec-20260623-144554
103
+ git diff 362b8d9..HEAD
104
+ ```
105
+
106
+ If the run was stopped early, the header shows the reason (e.g. `max iterations
107
+ reached (8)`, `4 consecutive failures`, or `stop condition met: ...`).
108
+
109
+ ---
110
+
111
+ Next: the full lifecycle in [The Iteration Loop](../concepts/iteration-loop.md).