outerloop-science 0.4.0__tar.gz → 0.5.0__tar.gz

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 (328) hide show
  1. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/.pre-commit-config.yaml +1 -1
  2. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/CHANGELOG.md +33 -0
  3. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/CITATION.cff +2 -2
  4. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/PKG-INFO +1 -1
  5. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/accelerators.md +3 -2
  6. outerloop_science-0.5.0/docs/design/forge.md +318 -0
  7. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/lifecycle.md +4 -2
  8. outerloop_science-0.5.0/docs/design/personal-token.md +268 -0
  9. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/roles.md +8 -9
  10. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/endpoints.md +9 -0
  11. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/install.md +73 -3
  12. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/operate.md +4 -1
  13. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/reference/cli.md +33 -2
  14. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/reference/environment.md +32 -2
  15. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/reviewer.md +4 -1
  16. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/roadmap.md +10 -0
  17. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/examples/review.yml +2 -0
  18. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/tick_deploy.sh +1 -0
  19. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/__init__.py +1 -1
  20. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/attempt.py +217 -81
  21. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/author_overrides.py +11 -7
  22. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/cli.py +203 -0
  23. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/climbboard.py +135 -2
  24. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/compute.py +68 -23
  25. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/dispatch.py +12 -2
  26. outerloop_science-0.5.0/src/outerloop/forge.py +253 -0
  27. outerloop_science-0.5.0/src/outerloop/forge_local.py +1594 -0
  28. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/github.py +111 -107
  29. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/inbox.py +32 -13
  30. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/init.py +277 -9
  31. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/intake.py +3 -4
  32. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/ledger_branch.py +17 -8
  33. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/ledger_events.py +6 -5
  34. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/ledger_migrate.py +12 -9
  35. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/maintain.py +15 -7
  36. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/maintain_post_cli.py +1 -1
  37. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/markers.py +20 -0
  38. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/operator_limits.py +30 -3
  39. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/orchestrator.py +3 -0
  40. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/progress.py +18 -5
  41. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/review.py +5 -11
  42. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/session_evidence.py +70 -8
  43. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/steward.py +30 -20
  44. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/syscall_cli.py +47 -9
  45. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/tick.py +162 -71
  46. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/verifier.py +7 -6
  47. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/README.md +5 -0
  48. outerloop_science-0.5.0/tests/fixtures/climb-status-legacy.json +24 -0
  49. outerloop_science-0.5.0/tests/fixtures/session_index_legacy.json +19 -0
  50. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/ledger_fake.py +18 -0
  51. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_app_permissions.py +6 -0
  52. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_attempt.py +109 -21
  53. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_attempt_review.py +2 -2
  54. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_author_overrides.py +7 -1
  55. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_climbboard.py +151 -0
  56. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_codex_bridge.py +56 -0
  57. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_default_claude_model.py +11 -3
  58. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_dispatch.py +43 -0
  59. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_endpoints.py +7 -1
  60. outerloop_science-0.5.0/tests/test_forge.py +56 -0
  61. outerloop_science-0.5.0/tests/test_forge_local.py +249 -0
  62. outerloop_science-0.5.0/tests/test_forge_local_e2e.py +220 -0
  63. outerloop_science-0.5.0/tests/test_forge_local_records.py +610 -0
  64. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_github.py +22 -52
  65. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_hardening.py +0 -6
  66. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_hermes_author.py +7 -1
  67. outerloop_science-0.5.0/tests/test_identity.py +80 -0
  68. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_init.py +314 -1
  69. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_install_harness.py +10 -2
  70. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_ledger_migrate.py +25 -4
  71. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_local_compute.py +64 -2
  72. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_maintain.py +3 -0
  73. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_operator_end.py +9 -1
  74. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_operator_limits.py +30 -0
  75. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_session_evidence.py +97 -9
  76. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_start.py +2 -1
  77. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_steward.py +1 -1
  78. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_syscall_cli.py +50 -0
  79. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_tick.py +235 -2
  80. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_version.py +1 -1
  81. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/uv.lock +122 -122
  82. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/.gitignore +0 -0
  83. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/.python-version +0 -0
  84. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/CLAUDE.md +0 -0
  85. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/CONTRIBUTING.md +0 -0
  86. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/LICENSE +0 -0
  87. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/NOTICE +0 -0
  88. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/README.md +0 -0
  89. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/RELEASING.md +0 -0
  90. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/SECURITY.md +0 -0
  91. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/containers/README.md +0 -0
  92. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/containers/agent-py312.def +0 -0
  93. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/assets/icon-dark.svg +0 -0
  94. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/assets/icon-light.svg +0 -0
  95. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/assets/icon.svg +0 -0
  96. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/assets/outerloop-apple-touch-icon.png +0 -0
  97. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/assets/outerloop-favicon.png +0 -0
  98. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/assets/outerloop-favicon.svg +0 -0
  99. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/community.md +0 -0
  100. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/concurrency.md +0 -0
  101. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/contract.md +0 -0
  102. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/agent-environment.md +0 -0
  103. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/agent-protocols.md +0 -0
  104. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/agent-substrate.md +0 -0
  105. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/architecture.md +0 -0
  106. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/base-reintegration.md +0 -0
  107. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/compute-cloud.md +0 -0
  108. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/consolidation.md +0 -0
  109. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/dispatcher.md +0 -0
  110. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/eval-cache.md +0 -0
  111. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/external.md +0 -0
  112. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/github-app-auth.md +0 -0
  113. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/headline.md +0 -0
  114. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/judge-placement.md +0 -0
  115. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/meta.md +0 -0
  116. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/model-gateway.md +0 -0
  117. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/multi-cluster.md +0 -0
  118. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/onboarding.md +0 -0
  119. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/orchestrator-verify.md +0 -0
  120. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/public-surface.md +0 -0
  121. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/research-lines.md +0 -0
  122. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/research-loop-buildout.md +0 -0
  123. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/research-loop.md +0 -0
  124. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/resident-tick.md +0 -0
  125. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/review-placement.md +0 -0
  126. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/reviewer-infra.md +0 -0
  127. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/role-cli.md +0 -0
  128. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/scaling.md +0 -0
  129. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/design/session-watcher.md +0 -0
  130. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/getting-started.md +0 -0
  131. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/how-it-works.md +0 -0
  132. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/index.md +0 -0
  133. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/javascripts/figures.js +0 -0
  134. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/messages.md +0 -0
  135. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/reference/contract.md +0 -0
  136. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/reference/inbox.md +0 -0
  137. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/reference/syscalls.md +0 -0
  138. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/session-capture.md +0 -0
  139. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/storage.md +0 -0
  140. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/stylesheets/figures.css +0 -0
  141. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/stylesheets/site.css +0 -0
  142. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/docs/validation/author-syscalls.md +0 -0
  143. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/mkdocs.yml +0 -0
  144. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/overrides/.icons/bi/circle-half.svg +0 -0
  145. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/overrides/.icons/bi/moon.svg +0 -0
  146. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/overrides/.icons/bi/search.svg +0 -0
  147. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/overrides/.icons/bi/sun.svg +0 -0
  148. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/overrides/.icons/lab/github.svg +0 -0
  149. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/overrides/main.html +0 -0
  150. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/overrides/partials/logo.html +0 -0
  151. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/pyproject.toml +0 -0
  152. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/README.md +0 -0
  153. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/docs/build.sh +0 -0
  154. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/docs/hooks.py +0 -0
  155. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/install_bridge.sh +0 -0
  156. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/install_claude.sh +0 -0
  157. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/install_codex.sh +0 -0
  158. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/install_hermes.sh +0 -0
  159. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/requeue_moved_successors.sh +0 -0
  160. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/setup_branch_protection.sh +0 -0
  161. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/sweep_git_locks.sh +0 -0
  162. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/tick_chain.sbatch +0 -0
  163. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/scripts/tick_resident.sh +0 -0
  164. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/__main__.py +0 -0
  165. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/appauth.py +0 -0
  166. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/appmanifest.py +0 -0
  167. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/bridge_install.py +0 -0
  168. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/bridge_runtime/pyproject.toml +0 -0
  169. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/bridge_runtime/uv.lock +0 -0
  170. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/brief.py +0 -0
  171. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/codex_bridge.py +0 -0
  172. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/codex_requirements.toml +0 -0
  173. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/contract.py +0 -0
  174. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/contract_cli.py +0 -0
  175. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/disk.py +0 -0
  176. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/endpoint_wait.py +0 -0
  177. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/endpoints.py +0 -0
  178. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/evalcache.py +0 -0
  179. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/gpu_lanes.py +0 -0
  180. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/guidance_pack.py +0 -0
  181. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/harness.py +0 -0
  182. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/harness_cli.py +0 -0
  183. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/harness_pins.py +0 -0
  184. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/harnesses.toml +0 -0
  185. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/hermes_capture.py +0 -0
  186. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/hermes_install.py +0 -0
  187. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/housekeeping.py +0 -0
  188. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/hypothesis.py +0 -0
  189. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/image.py +0 -0
  190. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/instance.py +0 -0
  191. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/job_names.py +0 -0
  192. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/launchlog.py +0 -0
  193. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/limits.py +0 -0
  194. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/maintain_agent_cli.py +0 -0
  195. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/measure.py +0 -0
  196. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/panel.py +0 -0
  197. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/paths.py +0 -0
  198. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/posting.py +0 -0
  199. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/provenance.py +0 -0
  200. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/py.typed +0 -0
  201. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/rebind.py +0 -0
  202. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/review_agent.py +0 -0
  203. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/review_agent_cli.py +0 -0
  204. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/review_post_cli.py +0 -0
  205. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/review_summarize_cli.py +0 -0
  206. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/role_runner.py +0 -0
  207. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/roles.py +0 -0
  208. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/rolespec.py +0 -0
  209. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/runstate.py +0 -0
  210. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/status.py +0 -0
  211. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/style.py +0 -0
  212. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/syscall.py +0 -0
  213. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/verify_agent.py +0 -0
  214. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/verify_agent_cli.py +0 -0
  215. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/verify_post_cli.py +0 -0
  216. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/src/outerloop/watcher.py +0 -0
  217. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/conftest.py +0 -0
  218. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fakes.py +0 -0
  219. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/author_route_legacy.json +0 -0
  220. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/author_route_missing_model.json +0 -0
  221. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/author_route_missing_route.json +0 -0
  222. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/base_moved_cc4e5d7.json +0 -0
  223. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/default_contract_head.json +0 -0
  224. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/command.txt +0 -0
  225. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/exit-code +0 -0
  226. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/job.sh +0 -0
  227. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/provenance.json +0 -0
  228. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/stdout +0 -0
  229. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
  230. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/identity.json +0 -0
  231. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/command.txt +0 -0
  232. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/job.sh +0 -0
  233. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/provenance.json +0 -0
  234. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
  235. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/hermes_resume_legacy.json +0 -0
  236. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/hermes_sample_20260924.json +0 -0
  237. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/launches-before-provenance.jsonl +0 -0
  238. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/panel_wake_9ca3d7c.json +0 -0
  239. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/pre_contract_switches.json +0 -0
  240. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/baselines/main@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json +0 -0
  241. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/brief.txt +0 -0
  242. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/end-request.json +0 -0
  243. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/ended.json +0 -0
  244. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/eval-provenance/command.txt +0 -0
  245. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/eval-provenance/job.sh +0 -0
  246. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/command.txt +0 -0
  247. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/exit-code +0 -0
  248. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/job.sh +0 -0
  249. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/stdout +0 -0
  250. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
  251. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/generate.py.txt +0 -0
  252. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/identity.json +0 -0
  253. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/launches.jsonl +0 -0
  254. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/line.bundle +0 -0
  255. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/merged.json +0 -0
  256. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/open.json +0 -0
  257. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/pending/owner__repo.json +0 -0
  258. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/pending/owner__repo@agent-01.json +0 -0
  259. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/pr-states.json +0 -0
  260. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/queue.json +0 -0
  261. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/rebind.json +0 -0
  262. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/rc1_v021/sessionless.json +0 -0
  263. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/session_cost_legacy.json +0 -0
  264. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/fixtures/suite_measurement_legacy.json +0 -0
  265. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/helpers.py +0 -0
  266. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_appauth.py +0 -0
  267. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_appmanifest.py +0 -0
  268. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_bot_aliases.py +0 -0
  269. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_bot_login.py +0 -0
  270. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_brief.py +0 -0
  271. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_channel_dir.py +0 -0
  272. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_codex_harness.py +0 -0
  273. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_codex_hooks.py +0 -0
  274. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_codex_host.py +0 -0
  275. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_compute.py +0 -0
  276. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_contract.py +0 -0
  277. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_contract_cli.py +0 -0
  278. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_contract_names.py +0 -0
  279. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_contract_switches.py +0 -0
  280. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_disk.py +0 -0
  281. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_endpoint_wait.py +0 -0
  282. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_evalcache.py +0 -0
  283. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_gpu_lanes.py +0 -0
  284. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_harness.py +0 -0
  285. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_harness_pins.py +0 -0
  286. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_hermes_harness.py +0 -0
  287. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_housekeeping.py +0 -0
  288. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_image.py +0 -0
  289. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_import.py +0 -0
  290. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_inbox.py +0 -0
  291. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_instance.py +0 -0
  292. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_intake.py +0 -0
  293. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_launchlog.py +0 -0
  294. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_ledger_events.py +0 -0
  295. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_lifecycle_cli.py +0 -0
  296. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_limits.py +0 -0
  297. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_markers.py +0 -0
  298. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_measure.py +0 -0
  299. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_measure_and_decide.py +0 -0
  300. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_messages.py +0 -0
  301. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_operator_message.py +0 -0
  302. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_orchestrator.py +0 -0
  303. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_packaging.py +0 -0
  304. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_panel.py +0 -0
  305. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_paths.py +0 -0
  306. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_posting.py +0 -0
  307. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_progress.py +0 -0
  308. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_rebind.py +0 -0
  309. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_requeue_moved_successors.py +0 -0
  310. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_review.py +0 -0
  311. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_review_agent.py +0 -0
  312. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_review_agent_cli.py +0 -0
  313. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_review_hardening.py +0 -0
  314. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_review_policy.py +0 -0
  315. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_review_summarize.py +0 -0
  316. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_role_runner.py +0 -0
  317. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_rolespec.py +0 -0
  318. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_runstate.py +0 -0
  319. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_status.py +0 -0
  320. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_sweep_git_locks.py +0 -0
  321. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_syscall.py +0 -0
  322. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_tick_chain_successors.py +0 -0
  323. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_tick_resident.py +0 -0
  324. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_tiers.py +0 -0
  325. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_verifier.py +0 -0
  326. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_verify_agent.py +0 -0
  327. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_verify_agent_cli.py +0 -0
  328. {outerloop_science-0.4.0 → outerloop_science-0.5.0}/tests/test_watcher.py +0 -0
@@ -14,7 +14,7 @@ repos:
14
14
  - id: check-toml
15
15
 
16
16
  - repo: https://github.com/astral-sh/ruff-pre-commit
17
- rev: v0.16.6
17
+ rev: v0.17.1
18
18
  hooks:
19
19
  - id: ruff-check
20
20
  args: [--fix]
@@ -6,6 +6,39 @@ Versions follow [SemVer](https://semver.org).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.5.0] - 2026-10-23
10
+
11
+ ### Upgrading
12
+
13
+ No operator action is needed before upgrading; details in each entry.
14
+
15
+ What changes for runs in flight: kernel comments, issues and pull requests
16
+ start carrying `<!-- outerloop:kernel -->`; the first tick republishes
17
+ `climb/status.json` in the new shape; a resumed Claude session captured
18
+ before the upgrade still overstates its cost.
19
+
20
+ Before rolling back: remove `slots` keys from `limits.toml`, and let local
21
+ jobs on GPU UUID ids (`GPU-...`) finish.
22
+
23
+ ### Changes
24
+
25
+ - Docs for personal-token mode: the install guide's personal-mode section (setup, how kernel posts are told apart, what GitHub no longer enforces, token reach, switching modes), the safety defaults in `operate.md`, `OUTERLOOP_IDENTITY` in the environment reference, `init --personal` in the CLI reference, and `personal: true` in the reviewer setup.
26
+ - Personal-token mode is selectable: `outerloop init --personal` takes the token `gh auth token` prints and writes it 0600 to the default PAT file (or uses a token file given with `--pat-file`), records its login as `OUTERLOOP_BOT_LOGIN`, and writes `OUTERLOOP_IDENTITY=personal`; `start` and the deploy step carry the setting into every tick. The kernel then posts as the person, so its own posts are told apart by the `outerloop:` marker rather than by login. The setup prints the trade-offs: no notifications for the kernel's activity, the `gh` token's repository-wide reach, and the reviewer workflow's `personal: true`. Upgrading: switching a personal deployment back to the App means releasing or closing claimed issues first — their claim comments were posted as the person, and the App's login check no longer counts them.
27
+ - One rule, `forge.is_kernel_post`, now decides whether a comment, issue or pull request is the kernel's, wherever the kernel asks. In personal mode (`OUTERLOOP_IDENTITY=personal`) the login is the person's, so a post is the kernel's only when its body also carries an `outerloop:` marker. Alias logins keep the login check alone. Kernel comments, issues and pull requests now carry `<!-- outerloop:kernel -->` unless they already carry a marker. When the kernel arms auto-merge it records the PR in the run's stage (`auto_merge_armed`) and clears the record once it disarms or sees auto-merge off; under the auto dial it withdraws only an arm that record claims, so a human's later arm is left alone. Upgrading: kernel posts now carry a marker, which an older kernel ignores. A stage without an `auto_merge_armed` record (every run armed before the upgrade) keeps the old `enabled_by.login` rule outside personal mode, so a PR the kernel armed before the upgrade is still withdrawn under the auto dial; rolling back ignores the field.
28
+ - The `siblings` view now shows each sibling's research instead of its last chat line: the run's stated hypothesis, its three most recent launches with `--why`, state and result line, the candidate score from the kernel's own gate measurement (never the agent's claim), and its PR, plus the target's five most recent endings with outcome and report title. `climb/status.json` gains `best_result` and `launches` per live run and a top-level `ended` list. Upgrading: no action needed; a snapshot written before this change reads as before (no launches, score or endings) and the first tick republishes it once in the new shape; rolling back is safe because an older reader ignores the added fields.
29
+ - Session records and the session index now carry the Claude Code version that ran, read from the binary's resolved path (the native installer's `versions/<x.y.z>` and the kernel's managed `harnesses/claude/<x.y.z>/` installs; the pin is not used, since an operator can point the kernel at any binary), so a resumed Claude session prices its cumulative resume totals once at the newest leg when it ran on Claude Code 2.1.277 or later. Codex is unchanged, and a Claude entry without a usable version keeps summing its legs (#515). Upgrading: no action needed; index entries written before this change carry no version, so a resumed Claude session captured before the upgrade still overstates its cost until those entries age out.
30
+ - `outerloop init` warns when the Claude or Codex CLI it finds is older than the pinned version and names `outerloop harness upgrade`, and says when the pre-PR panel stays off for lack of an image. The install guide covers seeding a private repository into the local forge.
31
+ - `outerloop pr merge` on the local forge authors the merge commit with the operator's git identity from system and global config (a repo-local identity in the directory it runs from is ignored); automatic merges keep the kernel's. Candidate snapshot commits are named `candidate snapshot: <run id>` instead of `dispatch snapshot`.
32
+ - Add an internal `Forge` seam behind the kernel's code-host operations; `OUTERLOOP_FORGE` accepts only `github` for now (no behavior change).
33
+ - Add the local forge backend's transport, refs and files over bare repositories (clone, branch and file reads and compare-and-swap writes); `OUTERLOOP_FORGE=local` is not selectable until pull requests, threads and issues exist.
34
+ - Add the local forge backend's records on `refs/outerloop/records`: pull requests, comment threads and the kernel's issues, with merge-commit transactions and recovery of merges made outside the kernel; user-visible inbox and self-merge text takes the forge's name.
35
+ - The local forge is selectable: `OUTERLOOP_FORGE=local` builds it from the state root, entry points need no GitHub credential or bot login, `outerloop init --forge local` bootstraps the bare repository, and `outerloop pr` lists, shows, comments on, merges and closes its pull requests.
36
+ - Local compute's GPU pool follows the loop's own `CUDA_VISIBLE_DEVICES`: when it is set at startup, those ids are the pool (jobs still get a subset), so two deployments on one host can be given disjoint GPUs. `OUTERLOOP_LOCAL_GPUS` caps the list, keeping the first ids; empty or `-1` means no GPUs. Jobs never see GPUs outside the loop's view: a job without an allocation inherits the loop's `CUDA_VISIBLE_DEVICES`. Fix: on local compute, the evaluated command now sees its job's GPU mask; the eval script's scrubbed environment (`env -i`, or apptainer `--cleanenv`) used to drop it, so evaluated code saw every GPU on the host. Slurm jobs are unchanged.
37
+ - Upgrading: an existing pool file (`gpus.json`) reads correctly, since holder keys stay the GPU index strings. Before rolling back to an older kernel, let jobs on non-integer ids (`GPU-...` UUIDs) finish: the older kernel cannot sort those holder keys.
38
+ - `limits.toml` accepts a per-target `slots` list (`[targets."owner/repo"]`, `slots = ["agent-04"]`): self-initiated runs take the first free listed agent identity, in order, instead of the lowest free `agent-01..agent-<width>`, so an operator can keep a slot's research line and author binding. Slots never add capacity (the attempt width still caps concurrency), running runs in unlisted slots are untouched, and `outerloop limits` shows the list. Without `slots`, behaviour is unchanged.
39
+ - Upgrading: existing `limits.toml` files need no change. Remove any `slots` key before rolling back to an older kernel, which treats it as unknown and fails the file closed.
40
+ - Refresh the toolchain within the existing bounds: pydantic 2.14.1, pydantic-core 2.50.1, ruff 0.17.1 and pre-commit 4.7.0; the ruff-pre-commit hook matches the locked Ruff. No source formatting or lint changes were needed.
41
+
9
42
  ## [0.4.0] - 2026-10-20
10
43
 
11
44
  ### Upgrading
@@ -13,5 +13,5 @@ authors:
13
13
  repository-code: "https://github.com/outerloop-science/outerloop"
14
14
  url: "https://outerloop.science"
15
15
  license: Apache-2.0
16
- version: 0.4.0
17
- date-released: "2026-10-20"
16
+ version: 0.5.0
17
+ date-released: "2026-10-23"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: outerloop-science
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Autonomous research agents that improve the benchmark you point them at, one verified pull request at a time
5
5
  Project-URL: Homepage, https://outerloop.science
6
6
  Project-URL: Repository, https://github.com/outerloop-science/outerloop
@@ -35,8 +35,9 @@ placed and capped before any TPU contract is accepted.
35
35
  - **Operator limits.** `limits.toml` `max_gpus`, counted from one
36
36
  `squeue --me --json` snapshot by parsing `gres/gpu` TRES, attributing jobs to
37
37
  this fleet by run id.
38
- - **Local backend.** `nvidia-smi` detection (or `OUTERLOOP_LOCAL_GPUS`),
39
- `CUDA_VISIBLE_DEVICES` pinning.
38
+ - **Local backend.** `nvidia-smi` detection (or `OUTERLOOP_LOCAL_GPUS`), the
39
+ loop's own `CUDA_VISIBLE_DEVICES` when it is set, `CUDA_VISIBLE_DEVICES`
40
+ pinning.
40
41
  - **Surfaces.** Briefs, wake lines, refusals, `budget.json`
41
42
  (`gpu_hours_remaining`), `outerloop status`, `outerloop limits` and the board
42
43
  carry GPU-named fields.
@@ -0,0 +1,318 @@
1
+ # A forge interface with a local backend
2
+
3
+ Status: proposed for the release after 0.4. Tracking issue: #488. A
4
+ personal-token mode for GitHub follows on top of it (end of this note).
5
+
6
+ ## Problem
7
+
8
+ The kernel reaches GitHub in three roles that share one client today:
9
+
10
+ - **Code transport**: clone, fetch and push of line branches, candidate
11
+ branches and the base, through `Workspace` with a credential scoped to
12
+ `https://github.com/`.
13
+ - **Review surface**: pull requests (open, draft, body edits, ready for
14
+ review, auto-merge, merge, close), thread comments, reviews, check runs.
15
+ - **Intake and record**: issues as work orders, claim markers, the
16
+ research-log branch (ledger, reports, board) written through the git-data
17
+ API, and pointer comments on the research-log issue.
18
+
19
+ All of it goes through `GitHubClient` (`github.py`), which about a dozen
20
+ modules call. Two costs follow. A measured result depends on GitHub's
21
+ availability and rate limits; outages have already aborted runs. And every
22
+ deployment needs a hosted repository and GitHub access through an App or a
23
+ machine-user token, which some labs cannot or will not set up. Adopters
24
+ report the App setup as the main friction of getting started.
25
+
26
+ Large studies feel all of this at once. A study of hundreds of runs needs a
27
+ hosted repository per cell, shares one API rate limit, and loses runs to any
28
+ outage. Locally a cell is a directory that a study script creates and
29
+ deletes.
30
+
31
+ ## Scope of the first version
32
+
33
+ The first version serves unattended deployments and studies, where the
34
+ auto merge dial is the norm: the kernel merges a head only after its own
35
+ measurement and the judge panel's approval, and people review the record
36
+ afterwards. It keeps what that needs and what the kernel writes on its own:
37
+
38
+ - code, line and candidate branches and the research-log branch (ledger,
39
+ reports, board) on a bare repository;
40
+ - pull-request records with real merge evidence;
41
+ - comment threads as an append-only log, so agent and kernel posts have a
42
+ place to land and a person's reply reaches the author's inbox;
43
+ - issue records that the kernel creates, comments on and closes (the
44
+ research-log issue and contract alarms need them);
45
+ - the identity seam, trivial locally;
46
+ - `outerloop pr list`, `show N`, `comment N`, `merge N` and `close N`.
47
+
48
+ It leaves out human work orders: issue intake, the steward lane and claim
49
+ markers. Work comes from the kernel's self-initiated lanes, and
50
+ `outerloop message` steers a live run. The manual dial works, but nothing
51
+ notifies a person that a pull request waits; `outerloop status` and
52
+ `outerloop pr list` show it.
53
+
54
+ Every record is kept in git (below), so a deployment's or a study's full
55
+ history of pull requests, threads and outcomes can be read back, mirrored
56
+ or replayed later.
57
+
58
+ ## Design
59
+
60
+ ### One interface, two backends
61
+
62
+ A `Forge` Protocol, selected the way `Compute` is (`compute.py`):
63
+ `OUTERLOOP_FORGE=github` (the default) or `local`. `GitHubForge` wraps the
64
+ existing client and changes no behavior. `LocalForge` implements the same
65
+ verbs over a bare git repository and JSON records on the deployment's
66
+ shared filesystem. `OUTERLOOP_FORGE` joins the keys `start` exports and the
67
+ deploy step carries into every tick.
68
+
69
+ One factory builds the forge at every entry point (the tick, attempt and
70
+ steward jobs, and the CLI) and supplies three things callers take from the
71
+ client or the environment today:
72
+
73
+ - **Workspaces.** Clone URL and credential come from the forge. This
74
+ replaces `target_clone_url` (`attempt.py`) everywhere it is used: run
75
+ wakes, publish, `finish_run`, the tick's mid-leg sync and the steward.
76
+ - **Kernel identity.** `bot_login` stays load-bearing: commits take their
77
+ author from it, and intake and the steward refuse to run without it. On
78
+ GitHub it is the App or bot login, as today; locally the factory supplies
79
+ `outerloop`.
80
+ - **Secrets and dry run.** `forge.secrets()` feeds redaction, replacing the
81
+ direct `github.auth.token()` read in the tick; `forge.dry_run` replaces the
82
+ direct attribute read in the ledger.
83
+
84
+ Startup gates become backend-aware. Servicing needs a home, a target and an
85
+ image on every backend; the credential and bot-login gate applies to
86
+ `github` only.
87
+
88
+ The kernel's verbs, grouped by role:
89
+
90
+ | Role | Verbs |
91
+ |---|---|
92
+ | Transport | clone URL and credential for a `Workspace` |
93
+ | Refs and files | `default_branch`, `branch_head`, `branch_sha`, `head_contains`, `commit_tree`, `get_tree`, `get_file`, `get_file_content`, `list_directory`, `create_ref`, `ensure_branch`, `put_file`, `put_files` |
94
+ | Pull requests | `create_pull`, `get_pull_request`, `find_open_pull_for_head`, `update_candidate_row`, `append_pull_body`, `mark_ready_for_review`, `arm_auto_merge_when_review_required`, `disable_auto_merge`, `allowed_merge_methods`, `merge_pull`, `close_issue` |
95
+ | Threads | `list_comments`, `comment`, `list_pr_reviews`, `list_pr_review_comments` |
96
+ | Issues | `list_open_issues`, `create_issue` (with labels) |
97
+ | Checks | `list_check_runs` |
98
+ | Identity | `is_kernel_post`, `qualifies` (below) |
99
+ | References | `ref_for(kind, id)`: a stable reference and an optional link |
100
+
101
+ `get_pull_request_diff`, `create_pr_review`, `update_issue` and
102
+ `job_log_tail` serve only the GitHub Actions posting CLIs (or are reached
103
+ only through a GitHub check run), so they stay on the GitHub client and out
104
+ of the Protocol. Four client methods have no caller (`create_pr`,
105
+ `get_issue`, `get_pull_request_files`, `upsert_comment`) and are deleted.
106
+
107
+ ### Shapes and errors
108
+
109
+ Return values keep the shapes callers read today, so the ledger observer,
110
+ bless and merge checks, intake and the steward read local records unchanged:
111
+
112
+ - **Pull request**: `number`, `state`, `merged`, `merged_at`,
113
+ `merge_commit_sha`, `draft`, `mergeable_state`, `user.login`, `html_url`,
114
+ `labels`, and nested `head.sha`, `head.ref`, `base.ref`. `head.sha` and the
115
+ base tip are read from the live refs on every call, so a follow-up push is
116
+ seen at once.
117
+ - **Issue**: `number`, `title`, `body`, `user.login`, `author_association`,
118
+ `labels[].name`, `created_at`.
119
+ - **Comment**: an increasing integer `id`, `user.login`, `body`,
120
+ `author_association`, and an ISO `created_at` (the steward releases stale
121
+ claims by age).
122
+ - **Reads at a ref** follow the real client: a branch or a sha; `get_file`
123
+ raises on a missing file and `get_file_content` returns `None`; regular
124
+ files only; `get_tree` reports `truncated` false; `branch_head` returns
125
+ `""` for a missing branch and `None` on failure; `put_file` and `put_files`
126
+ return what the client returns.
127
+ - **Errors**: a missing branch, file or record raises the client's error
128
+ with `status` 404. Several callers read a 404 as "absent" (the ledger, the
129
+ board, `close_if_done`, base-moved checks); any other error reads as an
130
+ outage and stalls them.
131
+
132
+ Contract reads use `default_branch` instead of the literal `main` the tick
133
+ passes in two places today, so a local repository with another default
134
+ branch still loads its contract. User-visible text that names GitHub (the
135
+ inbox's member label, its conflict line, the self-merge status comment)
136
+ takes the forge's name instead.
137
+
138
+ ### The local backend
139
+
140
+ - **Bootstrap.** `outerloop init` creates the bare repository at
141
+ `$OUTERLOOP_ROOT/forge/<owner>/<repo>.git` with `git clone --bare` from an
142
+ existing repository (a path or URL), or `git init --bare` with a first
143
+ commit of a given directory. Its default branch is recorded.
144
+ - **Records in git.** `pulls/<n>.json`, `issues/<n>.json` and
145
+ `threads/<n>/<id>.json` are files on a dedicated ref in the bare
146
+ repository (`refs/outerloop/records`), written with the same
147
+ compare-and-swap commit the ledger uses on the research-log branch. Every
148
+ change is a commit, so nothing is overwritten, any past state can be read
149
+ back, a crash leaves either the old commit or the new one, and
150
+ `git clone --mirror` backs up code and records together. A conflicting
151
+ writer retries on the new tip. Issues and pull requests share one number
152
+ sequence, as on GitHub, and `close_issue` closes either kind. Thread
153
+ entries are append-only with increasing integer ids. Closing is always
154
+ written explicitly: `close_if_done` reads `state == "closed"` to end a
155
+ rejected run.
156
+ - **Transport.** A path URL with no credential (`Workspace` already accepts
157
+ `auth=None`). Filesystem permissions are the access control. The bare
158
+ repository sits outside every path a session can see. Every clone a
159
+ session can see uses `--no-local` with no alternates, so it shares no
160
+ object files with the bare repository; today only the single-branch clone
161
+ passes `--no-local`, so the default `Workspace.clone` and the channel
162
+ bootstrap clone change too. A contained session cannot reach the state
163
+ root; an uncontained one can, which the install guide's uncontained
164
+ trade-offs will say.
165
+ - **Line snapshots.** `_push_line_snapshot` returns early when a workspace
166
+ has no credential. It changes to ask whether the forge has a transport,
167
+ and the forge reaches it through `finish_run` as well as the run legs, so
168
+ local snapshots are pushed on every path.
169
+ - **Pull requests.** Opening one pushes the head branch first, as on GitHub.
170
+ `mergeable_state` is computed when read and cached by the head and base
171
+ shas: `clean` when the head contains the base tip, `behind` when it does
172
+ not but a trial merge succeeds, and `dirty` only when the trial merge
173
+ conflicts, which is what the inbox treats as a conflict.
174
+ - **References.** `html_url` holds the local reference and still ends in
175
+ the pull-request number, which the kernel parses from it.
176
+ - **Merging.** `allowed_merge_methods` returns `["MERGE"]`. `merge_pull`
177
+ builds a merge commit from the expected head sha and moves the base in one
178
+ git ref transaction (`git update-ref --stdin`): verify the head branch
179
+ still points at the expected sha, then update the base from its old tip.
180
+ Either both checks pass and the base moves, or nothing changes. The record
181
+ is written after the ref moves.
182
+ - **Merges made outside the kernel.** A person may merge with plain git and
183
+ push, and a process may die between moving the base and writing the
184
+ record. Either way the next read of an open record whose head the base
185
+ now contains records the merge: `merge_commit_sha` is the commit on the
186
+ base whose second parent is the head, or the head itself after a
187
+ fast-forward. The ledger needs that commit and compares its tree with the
188
+ measured one, so it is recorded once and kept even if the branch later
189
+ moves or is deleted. Squash and rebase merges leave no such commit and
190
+ are not supported. (`head_contains(repo, base, head)` asks whether the
191
+ head contains the base; this check is the reverse call.)
192
+ - **Auto-merge.** Nothing is armed locally:
193
+ `arm_auto_merge_when_review_required` returns `False` and
194
+ `disable_auto_merge` reports disarmed. Under the auto dial the kernel
195
+ merges a blessed head through `merge_pull` with the same bless rules as on
196
+ GitHub. There are no required checks, so the stale-base guard is the
197
+ head-contains-tip test.
198
+ - **Checks and reviews.** `list_check_runs`, `list_pr_reviews` and
199
+ `list_pr_review_comments` return nothing. The inbox reads them to pass
200
+ CI results and Actions review rounds to the author; locally there are
201
+ none.
202
+
203
+ ### People: review and merge from the CLI (open question 1)
204
+
205
+ A small `outerloop pr` group: `list`, `show N` (the record and its thread),
206
+ `comment N`, `merge N` and `close N`. These commands write the same records
207
+ the kernel reads, so a person's comment reaches the author's inbox and the
208
+ agent's and kernel's posts are readable in the thread, as on GitHub. Diffs
209
+ come from `git diff` against the bare repository. No web view in the first
210
+ version.
211
+
212
+ Human work orders come later: `outerloop issue new [--label L]` and
213
+ `outerloop issue comment N`, with issue intake, the steward lane and claim
214
+ markers. The labels are the kernel's own: `outerloop:steward` routes an
215
+ issue to the steward lane and `outerloop:task` vouches for a work order.
216
+
217
+ ### Ledger, board, research log (open question 2)
218
+
219
+ The ledger, reports and board go through refs-and-files verbs and work
220
+ unchanged. Research-log publication also creates and comments on the
221
+ research-log issue; locally those become records like any issue. Links are
222
+ hard-coded to `github.com` today in the tick (the research-log pointer and
223
+ the rolling issue body), `progress.py`, `ledger_branch.py` and
224
+ `maintain.py`; they move to `ref_for`, so they point at the forge in use.
225
+
226
+ ### Identity, and who merges (open question 3)
227
+
228
+ `is_own_login(author, bot_login)` becomes `forge.is_kernel_post(item)` at
229
+ the call sites that judge an issue, comment or post:
230
+
231
+ - **GitHub with an App or bot account**: the login check, aliases included,
232
+ as today.
233
+ - **Local**: records the kernel writes carry the author `outerloop`; the CLI
234
+ writes the operator's user name.
235
+ - **GitHub with a personal token** (follow-up): the kernel and the person
236
+ share one login, so a post is the kernel's when it comes from that login
237
+ and carries an `outerloop:` marker. Every kernel post gets a marker for
238
+ this; most already have one.
239
+
240
+ Two call sites judge a pull request's author rather than a post (the
241
+ reviewer's and the verifier's skip rules). They run in GitHub Actions only,
242
+ and read `user.login` from the pull request.
243
+
244
+ One ownership check has neither: `_observe_auto_pr` disarms auto-merge only
245
+ when the kernel armed it, read from `auto_merge.enabled_by.login`. With a
246
+ shared login that cannot tell the kernel from the person, so the follow-up
247
+ records the arming itself (pull request number and head) in the run's stage
248
+ when the kernel arms, and compares against that.
249
+
250
+ `author_association` checks become `forge.qualifies(item)`: author standing
251
+ only. Locally every person-written record qualifies, since only people with
252
+ write access to the forge directory can write one. Each lane keeps its own
253
+ rules on top: intake also accepts its vouching label, the steward lane
254
+ requires author standing, and the verifier still trusts kernel replies and
255
+ marked verifier posts.
256
+
257
+ Who merges is unchanged on every backend. Under the manual dial the kernel
258
+ never calls `merge_pull`; on GitHub it only arms auto-merge behind a
259
+ required human review, and locally it arms nothing. Under the auto dial the
260
+ kernel merges blessed solver heads, and steward pull requests stay a
261
+ person's to merge. Reviews are comments only.
262
+
263
+ Locally none of this is enforced by permission. The kernel and the people
264
+ share the filesystem, so anyone who can write the forge directory can write
265
+ a record with the author `outerloop`, including a claim marker that GitHub's
266
+ bot identity makes unforgeable. That is the trust the operator already gives
267
+ everyone with write access to the state root.
268
+
269
+ ### Migration (open question 4)
270
+
271
+ One backend per deployment, chosen at `outerloop init`. Moving between
272
+ backends is not supported in the first version. Branches can be pushed to
273
+ another remote by hand. Records stay recoverable because they live in git:
274
+ a mirror keeps them, and a later tool can replay them into another forge.
275
+
276
+ ## What stays GitHub-only
277
+
278
+ The advisory review and verifier workflows run in GitHub Actions on
279
+ `pull_request_target`, and the maintenance digest posts issues there. On
280
+ the local backend they do not run. The kernel's own review panel and gate
281
+ run inside the kernel and are unaffected.
282
+
283
+ ## Build order
284
+
285
+ Each step is one PR with its tests:
286
+
287
+ 1. The `Forge` Protocol, `GitHubForge` around the existing client, the
288
+ factory at every entry point (workspaces, kernel identity, secrets, dry
289
+ run), `OUTERLOOP_FORGE` in the deploy and start key lists, callers typed
290
+ to `Forge`, contract reads at `default_branch`, the four dead methods
291
+ removed. No behavior change.
292
+ 2. `LocalForge` bootstrap, refs, files and transport, with `--no-local`
293
+ session clones, transport-gated snapshots and the 404 error contract; the
294
+ ledger, research log and board on a bare repository; links through
295
+ `ref_for`; `outerloop init` offers the local backend.
296
+ 3. Records on `refs/outerloop/records`: pull requests, threads and the
297
+ kernel's issues; the merge transaction and the recording of merges made
298
+ outside the kernel; the identity seam (`is_kernel_post`, `qualifies`) on
299
+ both backends; the forge name in user-visible text; the `outerloop pr`
300
+ commands; docs.
301
+
302
+ Later, when someone needs them: human work orders (the `outerloop issue`
303
+ commands, intake, the steward lane and claims on the local backend).
304
+
305
+ Then the personal-token mode: `init` offers "use my GitHub login" and writes
306
+ `gh auth token` to the PAT file, the marker rule from step 3 applies,
307
+ auto-merge arming is recorded in the run's stage, and PR bodies and commit
308
+ trailers name Outerloop as the author. Its docs state the trade-offs: the
309
+ person cannot approve their own agents' pull requests, so a required-review
310
+ rule needs a second reviewer; the kernel's restraint about merging holds by
311
+ its code, not by permission; and a fine-grained token limited to the target
312
+ repository is safer than the `gh` login token.
313
+
314
+ ## Not in the first version
315
+
316
+ A web view, hosted alternatives such as Gitea or GitLab (later backends of
317
+ the same interface), migration between backends, CI checks, inline reviews
318
+ and job logs on the local backend, and the Actions-based workflows on it.
@@ -119,8 +119,10 @@ The author's session never sees a raw comment body outside a fence, never
119
119
  sees the seed, and never sees a message the kernel did not write into the
120
120
  inbox. Advisory findings are delivered like blocking ones; the author decides
121
121
  what to do with them. The sibling view is refreshed at every wake, not only
122
- at start, and `siblings` shows each live run's hypothesis and PR link, including
123
- runs in review. The hypothesis survives publication for the rest of the run.
122
+ at start, and `siblings` shows each live run's stated hypothesis, its recent
123
+ launches with their reasons and result lines, its best measured score and its
124
+ PR link, including runs in review, plus the target's recent endings. The
125
+ hypothesis survives publication for the rest of the run.
124
126
  When to check the view — before choosing a direction and again before a
125
127
  submit, taking an unused direction unless its angle is distinct — is guidance,
126
128
  not a kernel step: the brief states the refresh, the pack carries the advice.