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
data/Rakefile ADDED
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb', 'test/**/test_*.rb'].exclude('**/*_helper.rb')
10
+ t.verbose = true
11
+ t.ruby_opts << '-rtest_helper'
12
+ end
13
+
14
+ task default: :test
15
+
16
+ desc 'Run tests with verbose output'
17
+ task :test_verbose do
18
+ ENV['TESTOPTS'] = '--verbose'
19
+ Rake::Task[:test].invoke
20
+ end
21
+
22
+ desc 'Run a single test file'
23
+ task :test_file, [:file] do |_t, args|
24
+ ruby "test/#{args[:file]}"
25
+ end
26
+
27
+ desc 'Check code complexity with Flog (warn >=20, fail >=50)'
28
+ task :flog_check do
29
+ require 'flog'
30
+
31
+ method_warn = 20.0
32
+ method_fail = 50.0
33
+
34
+ flogger = Flog.new(all: true)
35
+ flogger.flog(*Dir.glob('lib/**/*.rb'))
36
+
37
+ warnings = []
38
+ failures = []
39
+
40
+ flogger.each_by_score do |method, score|
41
+ next if method.end_with?('#none')
42
+
43
+ if score > method_fail
44
+ failures << "#{format('%.1f', score)}: #{method}"
45
+ elsif score > method_warn
46
+ warnings << "#{format('%.1f', score)}: #{method}"
47
+ end
48
+ end
49
+
50
+ unless warnings.empty?
51
+ puts "\nFlog warnings (#{method_warn}–#{method_fail}) — target for future refactoring:"
52
+ warnings.each { |v| puts " #{v}" }
53
+ end
54
+
55
+ if failures.empty?
56
+ puts "\nFlog: no methods exceed the failure threshold (>=#{method_fail})"
57
+ else
58
+ puts "\nFlog failures (>=#{method_fail}) — must be refactored:"
59
+ failures.each { |v| puts " #{v}" }
60
+ abort "\nFlog quality gate failed: #{failures.size} method(s) exceed #{method_fail}"
61
+ end
62
+ end
63
+
64
+ desc 'Check for structural code duplication with Flay (mass >= 50)'
65
+ task :flay_check do
66
+ require 'flay'
67
+
68
+ mass_threshold = 50
69
+
70
+ flay = Flay.new({ mass: mass_threshold, diff: false, verbose: false, summary: false, timeout: 60 })
71
+ flay.process(*Dir.glob('lib/**/*.rb'))
72
+ flay.analyze
73
+
74
+ if flay.hashes.empty?
75
+ puts "\nFlay: no structural duplication detected (mass >= #{mass_threshold})"
76
+ else
77
+ puts "\nFlay found structural duplication (mass >= #{mass_threshold}):"
78
+ flay.report
79
+ abort "\nFlay quality gate failed: #{flay.hashes.length} pattern(s) detected"
80
+ end
81
+ end
82
+
83
+ desc 'Run all quality checks: tests (with coverage), RuboCop, Flog, and Flay'
84
+ task :quality do
85
+ gates = [
86
+ ['Tests + Coverage', 'bundle exec rake test'],
87
+ ['RuboCop', 'bundle exec rubocop'],
88
+ ['Flog Complexity', 'bundle exec rake flog_check'],
89
+ ['Flay Duplication', 'bundle exec rake flay_check']
90
+ ]
91
+
92
+ results = gates.map do |label, command|
93
+ puts "\n#{'=' * 60}"
94
+ puts "Quality Gate: #{label}"
95
+ puts '=' * 60
96
+ [label, system(command) ? :pass : :fail]
97
+ end
98
+
99
+ green = ->(s) { "\e[32m#{s}\e[0m" }
100
+ red = ->(s) { "\e[31m#{s}\e[0m" }
101
+ width = results.map { |label, _| label.length }.max
102
+
103
+ puts "\n#{'=' * 60}"
104
+ puts 'Quality Gate Summary'
105
+ puts '=' * 60
106
+ results.each do |label, status|
107
+ badge = status == :pass ? green.call('PASS') : red.call('FAIL')
108
+ puts " [#{badge}] #{label.ljust(width)}"
109
+ end
110
+ puts '-' * 60
111
+
112
+ passed = results.count { |_, s| s == :pass }
113
+ failed = results.count { |_, s| s == :fail }
114
+ tally = "#{passed} passed, #{failed} failed"
115
+ puts " #{failed.zero? ? green.call(tally) : red.call(tally)}"
116
+ puts '=' * 60
117
+
118
+ abort "\n#{red.call('Quality gate failed.')}" unless failed.zero?
119
+ puts "\n#{green.call('All quality gates passed.')}"
120
+ end
121
+
122
+ namespace :docs do
123
+ desc 'Build MkDocs documentation'
124
+ task :build do
125
+ sh 'mkdocs build'
126
+ end
127
+
128
+ desc 'Serve MkDocs documentation locally on http://localhost:8000'
129
+ task :serve do
130
+ sh 'mkdocs serve'
131
+ end
132
+ end
data/bin/robot-to ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Activate bundler if running from the gem's own dev environment.
5
+ if File.exist?(File.expand_path("../Gemfile", __dir__))
6
+ require "bundler/setup"
7
+ end
8
+
9
+ require "robot_lab"
10
+ require "robot_lab/to"
11
+
12
+ Signal.trap("INT") { exit 130 }
13
+
14
+ RobotLab::To::CLI.run(ARGV)
@@ -0,0 +1,82 @@
1
+ # Changelog
2
+
3
+ All notable changes to `robot_lab-to` are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/), and the project follows
5
+ [Conventional Commits](https://www.conventionalcommits.org/) (see `COMMITS.md`).
6
+
7
+ ## Unreleased
8
+
9
+ ### Added
10
+
11
+ - **Evals: orchestrator-owned scoring.** An `Eval` scores each iteration's
12
+ working tree and returns a `Score(gate_ok, improved, met_target, value, detail,
13
+ output)` — the deciding authority for commit/rollback and, for measurable
14
+ objectives, for when the run is done — instead of the robot's self-reported
15
+ `should_fully_stop`. See [Evals](../concepts/evals.md).
16
+ - `Evals::Code` — measured/deterministic: the existing `--verify-command`
17
+ floor plus an optional `--measure CMD` (a command printing a number,
18
+ higher is better) and `--target FLOAT`. Fully backward compatible: with no
19
+ `--measure`, behaves exactly like the original verify-only gate.
20
+ - `Evals::Prose` — judged/pairwise: an LLM judge (`--judge-model`, defaults
21
+ to `--model`) compares the working tree against the last committed
22
+ version and rules it `better`/`worse`/`same`; an optional `--floor CMD`
23
+ gates mechanizable checks before the judge is called.
24
+ - `Evals::Null` — the default when nothing is configured; preserves the
25
+ original "commit any reported success" behavior exactly.
26
+ - `--eval NAME`, `--stop-on-plateau N`, `--[no-]require-improvement`, and
27
+ Ruby-API extension points (`RobotLab::To.register_eval`, an instance
28
+ responding to `#score`, or a bare proc, all passable as `eval:`).
29
+ - `require_improvement` (default **true**) rolls back a gate-passing
30
+ iteration that doesn't beat the parent's score, keeping the branch
31
+ strictly descending.
32
+ - `--stop-on-plateau N` — a new stop condition: abort after `N` iterations
33
+ with no committed improvement. The primary terminator for `Evals::Prose`,
34
+ which never sets a measurable target.
35
+ - `PromptBuilder` **Score Feedback** section — for scored runs, tells each
36
+ iteration's robot the best score committed so far, the target, and
37
+ whether recent attempts plateaued, so each attempt is a hypothesis
38
+ against the target instead of a blind guess.
39
+ - **`GraderLock` guard — grader lockdown.** Refuses `write`/`edit`/`bash` against
40
+ an eval's own grading criteria (`Evals::Prose#protected_paths`'s `--spec`, plus
41
+ any `--protect-path GLOB`), so the robot cannot "win" by rewriting the
42
+ criteria it is scored against. Unlike the small-model guardrails, it is always
43
+ installed when protected paths exist — independent of `--local-guards`. See
44
+ [Evals: Guarding the grader](../concepts/evals.md#guarding-the-grader-graderlock).
45
+ - **`write_guard` config (Ruby-only, default `true`).** Lets a `--local-guards`
46
+ run opt out of `Guards::WriteGuard` (`write_guard: false`) so the robot can
47
+ freely overwrite a file it is iteratively rewriting whole, e.g. a prose draft.
48
+ See [Guardrails](../local-models/guardrails.md#write-guard).
49
+ - **Local-model support.** Drive a local Ollama model end-to-end with
50
+ `--local-guards` and `--no-stream`. See [Local Models](../local-models/index.md).
51
+ - **Built-in workspace tools** (`read`, `write`, `edit`, `bash`) attached when
52
+ `--local-guards` is set. See [Built-in Tools](../local-models/tools.md).
53
+ - **Small-model guardrails** — `write-guard`, `read-before-edit`, `checkpoint`,
54
+ and `quality-monitor` hooks. See [Guardrails](../local-models/guardrails.md).
55
+ - **`stream` setting** (`--no-stream`) — disable response streaming, required for
56
+ Ollama tool calls; tokens are then accounted from each iteration's result.
57
+ - This documentation site.
58
+
59
+ ### Fixed
60
+
61
+ - Tools were passed to `RobotLab.build` via `tools:` (a name allowlist filter)
62
+ instead of `local_tools:` (the instance list), so the `submit_iteration_result`
63
+ tool was never attached to the robot. Now passed via `local_tools:`, with a
64
+ regression test asserting the built robot exposes the tool.
65
+
66
+ ## 0.1.0
67
+
68
+ Initial release.
69
+
70
+ ### Added
71
+
72
+ - Autonomous overnight loop (`Orchestrator`): fresh robot per iteration, commit on
73
+ success, `git reset --hard` on failure.
74
+ - `SubmitResult` tool and `IterationResult` value object.
75
+ - Cross-iteration memory via `notes.md` (`NotesManager`).
76
+ - Stop conditions: `--max-iterations`, `--max-tokens`,
77
+ `--max-consecutive-failures`, and natural-language `--stop-when`.
78
+ - Independent verification gate (`--verify-command`, `Verifier`).
79
+ - Interruptible exponential `Backoff` for transient-error retries.
80
+ - JSONL event log (`JsonlLogger`) and post-run `ExitSummary`.
81
+ - `robot-to` CLI and `RobotLab::To.run` programmatic entry point.
82
+ - `Config` cascade (`myway_config`): defaults → user file → env → CLI.
@@ -0,0 +1,110 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 640" font-family="Helvetica, Arial, sans-serif" role="img" aria-label="robot-to architecture">
2
+ <defs>
3
+ <marker id="a" markerWidth="9" markerHeight="9" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
4
+ <path d="M0,0 L7,3 L0,6 Z" fill="#94a3b8"/>
5
+ </marker>
6
+ </defs>
7
+ <style>
8
+ .t { fill:#ffffff; font-size:13px; font-weight:600; }
9
+ .s { fill:#e2e8f0; font-size:10px; }
10
+ .hd { fill:#cbd5e1; font-size:12px; font-weight:700; letter-spacing:.5px; }
11
+ .lg { fill:#cbd5e1; font-size:11px; }
12
+ </style>
13
+
14
+ <!-- CLI / entry -->
15
+ <rect x="360" y="16" width="180" height="46" rx="10" fill="#0ea5e9"/>
16
+ <text x="450" y="38" text-anchor="middle" class="t">robot-to CLI / To.run</text>
17
+ <text x="450" y="54" text-anchor="middle" class="s">CLI · Config (myway_config)</text>
18
+ <line x1="450" y1="62" x2="450" y2="86" stroke="#94a3b8" stroke-width="2" marker-end="url(#a)"/>
19
+
20
+ <!-- Orchestrator -->
21
+ <rect x="330" y="88" width="240" height="52" rx="10" fill="#475569"/>
22
+ <text x="450" y="110" text-anchor="middle" class="t">Orchestrator</text>
23
+ <text x="450" y="127" text-anchor="middle" class="s">main loop · setup → iterate → finalize</text>
24
+
25
+ <!-- collaborators row -->
26
+ <text x="60" y="172" class="hd">PER-RUN COLLABORATORS</text>
27
+ <g>
28
+ <rect x="40" y="184" width="150" height="46" rx="9" fill="#6366f1"/>
29
+ <text x="115" y="205" text-anchor="middle" class="t">PromptBuilder</text>
30
+ <text x="115" y="221" text-anchor="middle" class="s">per-iteration system prompt</text>
31
+
32
+ <rect x="200" y="184" width="150" height="46" rx="9" fill="#f59e0b"/>
33
+ <text x="275" y="205" text-anchor="middle" class="t">CommitManager</text>
34
+ <text x="275" y="221" text-anchor="middle" class="s">git via Open3 (no shell)</text>
35
+
36
+ <rect x="360" y="184" width="150" height="46" rx="9" fill="#a855f7"/>
37
+ <text x="435" y="205" text-anchor="middle" class="t">NotesManager</text>
38
+ <text x="435" y="221" text-anchor="middle" class="s">notes.md memory</text>
39
+
40
+ <rect x="520" y="184" width="150" height="46" rx="9" fill="#64748b"/>
41
+ <text x="595" y="205" text-anchor="middle" class="t">StopConditions</text>
42
+ <text x="595" y="221" text-anchor="middle" class="s">iters · tokens · failures</text>
43
+
44
+ <rect x="680" y="184" width="160" height="46" rx="9" fill="#64748b"/>
45
+ <text x="760" y="205" text-anchor="middle" class="t">Verifier</text>
46
+ <text x="760" y="221" text-anchor="middle" class="s">independent test gate</text>
47
+ </g>
48
+ <g>
49
+ <rect x="120" y="244" width="150" height="46" rx="9" fill="#64748b"/>
50
+ <text x="195" y="265" text-anchor="middle" class="t">Backoff</text>
51
+ <text x="195" y="281" text-anchor="middle" class="s">interruptible retry</text>
52
+
53
+ <rect x="280" y="244" width="150" height="46" rx="9" fill="#64748b"/>
54
+ <text x="355" y="265" text-anchor="middle" class="t">JsonlLogger</text>
55
+ <text x="355" y="281" text-anchor="middle" class="s">run.log events</text>
56
+
57
+ <rect x="440" y="244" width="150" height="46" rx="9" fill="#64748b"/>
58
+ <text x="515" y="265" text-anchor="middle" class="t">ExitSummary</text>
59
+ <text x="515" y="281" text-anchor="middle" class="s">post-run report</text>
60
+
61
+ <rect x="600" y="244" width="150" height="46" rx="9" fill="#a855f7"/>
62
+ <text x="675" y="265" text-anchor="middle" class="t">Run</text>
63
+ <text x="675" y="281" text-anchor="middle" class="s">run state object</text>
64
+ </g>
65
+ <line x1="450" y1="140" x2="450" y2="180" stroke="#94a3b8" stroke-width="2" marker-end="url(#a)"/>
66
+
67
+ <!-- robot -->
68
+ <text x="60" y="330" class="hd">PER-ITERATION ROBOT (RobotLab)</text>
69
+ <rect x="330" y="342" width="240" height="50" rx="10" fill="#3b82f6"/>
70
+ <text x="450" y="364" text-anchor="middle" class="t">RobotLab::Robot</text>
71
+ <text x="450" y="380" text-anchor="middle" class="s">RubyLLM agent · persistent chat</text>
72
+ <line x1="450" y1="290" x2="450" y2="340" stroke="#94a3b8" stroke-width="2" marker-end="url(#a)"/>
73
+
74
+ <!-- tools -->
75
+ <rect x="120" y="420" width="150" height="46" rx="9" fill="#16a34a"/>
76
+ <text x="195" y="441" text-anchor="middle" class="t">SubmitResult</text>
77
+ <text x="195" y="457" text-anchor="middle" class="s">reports iteration result</text>
78
+
79
+ <rect x="290" y="420" width="320" height="46" rx="9" fill="#16a34a"/>
80
+ <text x="450" y="441" text-anchor="middle" class="t">File tools — read · write · edit · bash</text>
81
+ <text x="450" y="457" text-anchor="middle" class="s">attached only when local_guards is on</text>
82
+ <line x1="420" y1="392" x2="240" y2="418" stroke="#94a3b8" stroke-width="2" marker-end="url(#a)"/>
83
+ <line x1="460" y1="392" x2="460" y2="418" stroke="#94a3b8" stroke-width="2" marker-end="url(#a)"/>
84
+
85
+ <!-- guards -->
86
+ <text x="630" y="412" class="hd">GUARDRAILS (Hooks)</text>
87
+ <rect x="630" y="420" width="250" height="92" rx="10" fill="#dc2626"/>
88
+ <text x="755" y="440" text-anchor="middle" class="t">RobotLab::Hook guards</text>
89
+ <text x="755" y="460" text-anchor="middle" class="s">write_guard · read_before_edit</text>
90
+ <text x="755" y="476" text-anchor="middle" class="s">checkpoint · quality_monitor</text>
91
+ <text x="755" y="496" text-anchor="middle" class="s">intercept tool calls for small models</text>
92
+ <line x1="610" y1="450" x2="628" y2="455" stroke="#94a3b8" stroke-width="2" marker-end="url(#a)"/>
93
+
94
+ <!-- run state output -->
95
+ <rect x="290" y="540" width="320" height="60" rx="10" fill="#334155"/>
96
+ <text x="450" y="562" text-anchor="middle" class="t">.robot_lab_to/runs/&lt;run_id&gt;/</text>
97
+ <text x="450" y="580" text-anchor="middle" class="s">notes.md · run.log (JSONL) · checkpoints/</text>
98
+ <text x="450" y="595" text-anchor="middle" class="s">+ git branch robot-to/&lt;slug&gt;</text>
99
+ <line x1="450" y1="466" x2="450" y2="538" stroke="#94a3b8" stroke-width="2" marker-end="url(#a)"/>
100
+
101
+ <!-- legend -->
102
+ <g transform="translate(40,548)">
103
+ <rect x="0" y="0" width="14" height="11" rx="3" fill="#475569"/><text x="20" y="9" class="lg">orchestrator / control</text>
104
+ <rect x="0" y="18" width="14" height="11" rx="3" fill="#3b82f6"/><text x="20" y="27" class="lg">robot / LLM</text>
105
+ <rect x="0" y="36" width="14" height="11" rx="3" fill="#16a34a"/><text x="20" y="45" class="lg">tools</text>
106
+ <rect x="140" y="0" width="14" height="11" rx="3" fill="#dc2626"/><text x="160" y="9" class="lg">guardrails</text>
107
+ <rect x="140" y="18" width="14" height="11" rx="3" fill="#a855f7"/><text x="160" y="27" class="lg">memory / state</text>
108
+ <rect x="140" y="36" width="14" height="11" rx="3" fill="#f59e0b"/><text x="160" y="45" class="lg">git</text>
109
+ </g>
110
+ </svg>
Binary file
@@ -0,0 +1,97 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 880 720" font-family="Helvetica, Arial, sans-serif" role="img" aria-label="The robot-to iteration loop">
2
+ <!-- transparent background: no background rect -->
3
+ <defs>
4
+ <marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
5
+ <path d="M0,0 L8,3 L0,6 Z" fill="#94a3b8"/>
6
+ </marker>
7
+ <marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
8
+ <path d="M0,0 L8,3 L0,6 Z" fill="#22c55e"/>
9
+ </marker>
10
+ <marker id="arrowRed" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
11
+ <path d="M0,0 L8,3 L0,6 Z" fill="#ef4444"/>
12
+ </marker>
13
+ </defs>
14
+
15
+ <style>
16
+ .label { fill: #ffffff; font-size: 14px; font-weight: 600; }
17
+ .sub { fill: #e2e8f0; font-size: 11px; }
18
+ .edge { fill: #cbd5e1; font-size: 12px; font-weight: 600; }
19
+ .legend{ fill: #cbd5e1; font-size: 12px; }
20
+ </style>
21
+
22
+ <!-- setup -->
23
+ <rect x="300" y="20" width="280" height="54" rx="10" fill="#475569"/>
24
+ <text x="440" y="42" text-anchor="middle" class="label">setup_run</text>
25
+ <text x="440" y="60" text-anchor="middle" class="sub">create branch · init notes.md · base commit</text>
26
+ <line x1="440" y1="74" x2="440" y2="100" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrow)"/>
27
+
28
+ <!-- build robot -->
29
+ <rect x="300" y="102" width="280" height="54" rx="10" fill="#3b82f6"/>
30
+ <text x="440" y="124" text-anchor="middle" class="label">build fresh Robot</text>
31
+ <text x="440" y="142" text-anchor="middle" class="sub">system prompt + notes + tools (per iteration)</text>
32
+ <line x1="440" y1="156" x2="440" y2="182" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrow)"/>
33
+
34
+ <!-- run -->
35
+ <rect x="300" y="184" width="280" height="54" rx="10" fill="#3b82f6"/>
36
+ <text x="440" y="206" text-anchor="middle" class="label">robot.run — do work via tools</text>
37
+ <text x="440" y="224" text-anchor="middle" class="sub">read/write/edit/bash · then submit_result</text>
38
+ <line x1="440" y1="238" x2="440" y2="264" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrow)"/>
39
+
40
+ <!-- submitted? -->
41
+ <polygon points="440,266 560,310 440,354 320,310" fill="#64748b"/>
42
+ <text x="440" y="306" text-anchor="middle" class="label">submitted?</text>
43
+ <text x="440" y="324" text-anchor="middle" class="sub">called submit_result</text>
44
+ <!-- no -> nudge -->
45
+ <line x1="560" y1="310" x2="650" y2="310" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrow)"/>
46
+ <text x="600" y="300" text-anchor="middle" class="edge">no</text>
47
+ <rect x="650" y="284" width="190" height="52" rx="10" fill="#475569"/>
48
+ <text x="745" y="306" text-anchor="middle" class="label">nudge robot</text>
49
+ <text x="745" y="323" text-anchor="middle" class="sub">re-ask up to max_submit_nudges</text>
50
+ <path d="M745,284 C745,240 620,250 560,290" fill="none" stroke="#94a3b8" stroke-width="2" stroke-dasharray="4 3" marker-end="url(#arrow)"/>
51
+
52
+ <!-- success? -->
53
+ <line x1="440" y1="354" x2="440" y2="384" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrow)"/>
54
+ <text x="455" y="372" class="edge">yes</text>
55
+ <polygon points="440,386 560,430 440,474 320,430" fill="#64748b"/>
56
+ <text x="440" y="426" text-anchor="middle" class="label">success?</text>
57
+ <text x="440" y="444" text-anchor="middle" class="sub">+ verify_command</text>
58
+
59
+ <!-- success path: verify ok -> commit -->
60
+ <line x1="440" y1="474" x2="440" y2="504" stroke="#22c55e" stroke-width="2" marker-end="url(#arrowGreen)"/>
61
+ <text x="455" y="492" class="edge">pass</text>
62
+ <rect x="300" y="506" width="280" height="54" rx="10" fill="#22c55e"/>
63
+ <text x="440" y="528" text-anchor="middle" class="label">git add -A &amp; commit</text>
64
+ <text x="440" y="546" text-anchor="middle" class="sub">one focused change committed</text>
65
+
66
+ <!-- failure path: reset -->
67
+ <line x1="320" y1="430" x2="150" y2="430" stroke="#ef4444" stroke-width="2" marker-end="url(#arrowRed)"/>
68
+ <text x="235" y="420" text-anchor="middle" class="edge">fail / no submit</text>
69
+ <rect x="20" y="404" width="130" height="52" rx="10" fill="#ef4444"/>
70
+ <text x="85" y="426" text-anchor="middle" class="label">git reset</text>
71
+ <text x="85" y="443" text-anchor="middle" class="sub">--hard + clean</text>
72
+
73
+ <!-- notes update (purple) collects both paths -->
74
+ <line x1="440" y1="560" x2="440" y2="588" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrow)"/>
75
+ <path d="M85,456 C85,600 250,600 298,600" fill="none" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrow)"/>
76
+ <rect x="300" y="590" width="280" height="50" rx="10" fill="#a855f7"/>
77
+ <text x="440" y="612" text-anchor="middle" class="label">append to notes.md</text>
78
+ <text x="440" y="629" text-anchor="middle" class="sub">cross-iteration memory</text>
79
+
80
+ <!-- stop conditions -->
81
+ <line x1="440" y1="640" x2="440" y2="664" stroke="#94a3b8" stroke-width="2" marker-end="url(#arrow)"/>
82
+ <rect x="300" y="666" width="280" height="44" rx="10" fill="#475569"/>
83
+ <text x="440" y="693" text-anchor="middle" class="label">stop conditions met? — else loop ↻</text>
84
+
85
+ <!-- loop back arrow -->
86
+ <path d="M300,688 C120,688 120,129 298,129" fill="none" stroke="#94a3b8" stroke-width="2" stroke-dasharray="6 4" marker-end="url(#arrow)"/>
87
+ <text x="95" y="410" text-anchor="middle" class="edge" transform="rotate(-90 95 410)">next iteration</text>
88
+
89
+ <!-- legend -->
90
+ <g transform="translate(620,560)">
91
+ <rect x="0" y="0" width="16" height="12" rx="3" fill="#3b82f6"/><text x="22" y="10" class="legend">robot / LLM</text>
92
+ <rect x="0" y="22" width="16" height="12" rx="3" fill="#22c55e"/><text x="22" y="32" class="legend">commit (success)</text>
93
+ <rect x="0" y="44" width="16" height="12" rx="3" fill="#ef4444"/><text x="22" y="54" class="legend">rollback (failure)</text>
94
+ <rect x="0" y="66" width="16" height="12" rx="3" fill="#a855f7"/><text x="22" y="76" class="legend">notes / memory</text>
95
+ <rect x="0" y="88" width="16" height="12" rx="3" fill="#475569"/><text x="22" y="98" class="legend">orchestrator control</text>
96
+ </g>
97
+ </svg>