outerloop-science 0.2.0rc2__tar.gz → 0.2.0rc4__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 (189) hide show
  1. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/CHANGELOG.md +17 -0
  2. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/CITATION.cff +2 -2
  3. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/PKG-INFO +1 -1
  4. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/install.md +82 -8
  5. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/pyproject.toml +5 -0
  6. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/README.md +1 -1
  7. outerloop_science-0.2.0rc4/scripts/install_claude.sh +82 -0
  8. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/tick_chain.sbatch +3 -1
  9. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/tick_deploy.sh +23 -0
  10. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/tick_resident.sh +3 -1
  11. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/__init__.py +1 -1
  12. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/attempt.py +302 -113
  13. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/cli.py +114 -23
  14. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/compute.py +21 -0
  15. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/dispatch.py +5 -2
  16. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/github.py +33 -5
  17. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/harness.py +36 -9
  18. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/image.py +6 -1
  19. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/init.py +56 -16
  20. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/measure.py +46 -2
  21. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/orchestrator.py +3 -1
  22. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/runstate.py +122 -0
  23. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/steward.py +20 -2
  24. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/tick.py +154 -34
  25. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/conftest.py +15 -0
  26. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_app_permissions.py +2 -0
  27. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_attempt.py +380 -7
  28. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_attempt_review.py +93 -0
  29. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_dispatch.py +44 -0
  30. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_github.py +31 -0
  31. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_harness.py +44 -15
  32. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_hermes_harness.py +2 -1
  33. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_image.py +18 -0
  34. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_init.py +60 -8
  35. outerloop_science-0.2.0rc4/tests/test_install_harness.py +218 -0
  36. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_local_compute.py +47 -2
  37. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_measure.py +100 -0
  38. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_orchestrator.py +6 -2
  39. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_runstate.py +128 -0
  40. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_start.py +288 -40
  41. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_tick.py +300 -4
  42. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_tick_resident.py +10 -2
  43. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/.gitignore +0 -0
  44. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/.pre-commit-config.yaml +0 -0
  45. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/.python-version +0 -0
  46. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/CLAUDE.md +0 -0
  47. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/CONTRIBUTING.md +0 -0
  48. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/LICENSE +0 -0
  49. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/NOTICE +0 -0
  50. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/README.md +0 -0
  51. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/RELEASING.md +0 -0
  52. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/SECURITY.md +0 -0
  53. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/containers/README.md +0 -0
  54. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/containers/agent-py312.def +0 -0
  55. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/assets/icon-dark.svg +0 -0
  56. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/assets/icon-light.svg +0 -0
  57. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/assets/icon.svg +0 -0
  58. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/community.md +0 -0
  59. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/compute.md +0 -0
  60. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/contract.md +0 -0
  61. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/agent-protocols.md +0 -0
  62. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/agent-substrate.md +0 -0
  63. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/architecture.md +0 -0
  64. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/base-reintegration.md +0 -0
  65. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/consolidation.md +0 -0
  66. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/dispatcher.md +0 -0
  67. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/eval-cache.md +0 -0
  68. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/external.md +0 -0
  69. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/github-app-auth.md +0 -0
  70. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/headline.md +0 -0
  71. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/judge-placement.md +0 -0
  72. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/lifecycle.md +0 -0
  73. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/meta.md +0 -0
  74. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/onboarding.md +0 -0
  75. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/orchestrator-verify.md +0 -0
  76. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/public-surface.md +0 -0
  77. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/research-lines.md +0 -0
  78. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/research-loop-buildout.md +0 -0
  79. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/research-loop.md +0 -0
  80. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/resident-tick.md +0 -0
  81. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/review-placement.md +0 -0
  82. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/reviewer-infra.md +0 -0
  83. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/role-cli.md +0 -0
  84. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/roles.md +0 -0
  85. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/scaling.md +0 -0
  86. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/design/session-watcher.md +0 -0
  87. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/reviewer.md +0 -0
  88. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/roadmap.md +0 -0
  89. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/docs/validation/author-syscalls.md +0 -0
  90. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/examples/review.yml +0 -0
  91. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/install_codex.sh +0 -0
  92. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/install_hermes.sh +0 -0
  93. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/requeue_moved_successors.sh +0 -0
  94. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/setup_branch_protection.sh +0 -0
  95. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/scripts/sweep_git_locks.sh +0 -0
  96. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/__main__.py +0 -0
  97. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/appauth.py +0 -0
  98. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/appmanifest.py +0 -0
  99. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/brief.py +0 -0
  100. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/climbboard.py +0 -0
  101. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/contract.py +0 -0
  102. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/contract_cli.py +0 -0
  103. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/disk.py +0 -0
  104. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/evalcache.py +0 -0
  105. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/housekeeping.py +0 -0
  106. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/hypothesis.py +0 -0
  107. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/inbox.py +0 -0
  108. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/intake.py +0 -0
  109. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/launchlog.py +0 -0
  110. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/limits.py +0 -0
  111. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/maintain.py +0 -0
  112. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/maintain_agent_cli.py +0 -0
  113. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/maintain_post_cli.py +0 -0
  114. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/markers.py +0 -0
  115. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/panel.py +0 -0
  116. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/paths.py +0 -0
  117. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/posting.py +0 -0
  118. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/progress.py +0 -0
  119. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/py.typed +0 -0
  120. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/review.py +0 -0
  121. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/review_agent.py +0 -0
  122. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/review_agent_cli.py +0 -0
  123. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/review_post_cli.py +0 -0
  124. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/review_summarize_cli.py +0 -0
  125. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/role_runner.py +0 -0
  126. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/roles.py +0 -0
  127. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/rolespec.py +0 -0
  128. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/style.py +0 -0
  129. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/syscall.py +0 -0
  130. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/syscall_cli.py +0 -0
  131. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/verifier.py +0 -0
  132. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/verify_agent.py +0 -0
  133. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/verify_agent_cli.py +0 -0
  134. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/verify_post_cli.py +0 -0
  135. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/src/outerloop/watcher.py +0 -0
  136. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/fakes.py +0 -0
  137. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/helpers.py +0 -0
  138. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_appauth.py +0 -0
  139. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_appmanifest.py +0 -0
  140. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_bot_aliases.py +0 -0
  141. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_bot_login.py +0 -0
  142. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_brief.py +0 -0
  143. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_channel_dir.py +0 -0
  144. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_climbboard.py +0 -0
  145. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_codex_harness.py +0 -0
  146. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_compute.py +0 -0
  147. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_contract.py +0 -0
  148. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_contract_cli.py +0 -0
  149. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_contract_names.py +0 -0
  150. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_disk.py +0 -0
  151. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_evalcache.py +0 -0
  152. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_hardening.py +0 -0
  153. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_housekeeping.py +0 -0
  154. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_import.py +0 -0
  155. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_inbox.py +0 -0
  156. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_intake.py +0 -0
  157. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_launchlog.py +0 -0
  158. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_lifecycle_cli.py +0 -0
  159. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_limits.py +0 -0
  160. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_maintain.py +0 -0
  161. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_markers.py +0 -0
  162. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_measure_and_decide.py +0 -0
  163. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_messages.py +0 -0
  164. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_packaging.py +0 -0
  165. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_panel.py +0 -0
  166. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_paths.py +0 -0
  167. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_posting.py +0 -0
  168. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_progress.py +0 -0
  169. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_requeue_moved_successors.py +0 -0
  170. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_review.py +0 -0
  171. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_review_agent.py +0 -0
  172. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_review_agent_cli.py +0 -0
  173. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_review_hardening.py +0 -0
  174. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_review_policy.py +0 -0
  175. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_review_summarize.py +0 -0
  176. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_role_runner.py +0 -0
  177. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_rolespec.py +0 -0
  178. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_steward.py +0 -0
  179. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_sweep_git_locks.py +0 -0
  180. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_syscall.py +0 -0
  181. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_syscall_cli.py +0 -0
  182. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_tick_chain_successors.py +0 -0
  183. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_tiers.py +0 -0
  184. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_verifier.py +0 -0
  185. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_verify_agent.py +0 -0
  186. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_verify_agent_cli.py +0 -0
  187. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_version.py +0 -0
  188. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/tests/test_watcher.py +0 -0
  189. {outerloop_science-0.2.0rc2 → outerloop_science-0.2.0rc4}/uv.lock +0 -0
@@ -20,12 +20,29 @@ launch arrays in parallel. The board shows running GPU jobs. `OUTERLOOP_LOCAL_GP
20
20
  overrides GPU detection; `0` disables allocation. Submissions still wait for all
21
21
  tasks to finish.
22
22
 
23
+ ### Added
24
+
25
+ - `outerloop init` installs a missing author CLI and records its path; `--no-install-harness` opts out. Claude has a pinned, SHA256-verified installer.
26
+
27
+ - `OUTERLOOP_TICK_HOST=login` (or `--tick-host login`) runs a niced foreground tick loop against Slurm, with one tick lease per state root.
28
+ - `OUTERLOOP_QOS` sets the QOS for every submitted Slurm job.
29
+ - `OUTERLOOP_APPTAINER_BIN` selects the container executable on compute nodes.
30
+ - `<root>/HOLD_LAUNCHES` holds fresh kernel launches while existing runs, wake delivery, and the tick chain continue; remove the file to resume.
31
+
23
32
  ### Removed
24
33
 
25
34
  - The pre-rename names are gone. The `AUTORESEARCH_*` environment names are no longer read by the package, the `.env` reader, the chain or the deploy step. `~/.config/autoresearch/`, `.autoresearch.yaml`, the `.autoresearch` channel directory, the `~/.autoresearch` local root default and `~/autoresearch-images` are no longer looked for. The `autoresearch-resident` and `autoresearch-tick` job names are no longer recognized. The `harness_key` file, the `*_HARNESS_KEY_FILE` setting and the `climb_job_id` record field are gone. Before upgrading, rename the `.env` keys, the config directory and the image directory. Cancel a resident or chain still queued under an old job name (`scancel --name autoresearch-resident`, or `scancel --name autoresearch-tick`) before running `outerloop start`: the guard against two loops on one root knows only the current name. Old `autoresearch:` comment markers are still recognized.
26
35
 
27
36
  ### Fixed
28
37
 
38
+ - The measurer waits up to 90 seconds for the files of a finished evaluation job. A filesystem delay no longer ends a measured improvement as a negative result.
39
+ - `outerloop start` refuses to run when the configured author's CLI is missing: it checks the recorded path, else PATH, else `~/.local/bin`, and names the install command and `outerloop init --force`.
40
+
41
+ - Ending line snapshots push once, with the kernel's GitHub auth. Without auth nothing is pushed and the log says so. A refused push is retried only when the remote line moved.
42
+ - PRs under `merge: auto` say why self-merge is waiting, and the publish records and logs the bless decision.
43
+
44
+ - A timed-out local job could leave its GPUs reserved until the next submit; they are now released once its process group is gone.
45
+
29
46
  - The tick log says why a blessed PR is not merged yet (a message waits for the author, the run sleeps on jobs, the base moved, and so on), one line per sweep.
30
47
  - Runs retain their hypothesis through review. The status strip and sibling view carry it with the PR link, and the brief asks authors to check the view, refreshed at every wake, before choosing a direction and before submitting.
31
48
 
@@ -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.2.0rc2
17
- date-released: "2026-09-14"
16
+ version: 0.2.0rc4
17
+ date-released: "2026-09-15"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: outerloop-science
3
- Version: 0.2.0rc2
3
+ Version: 0.2.0rc4
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
@@ -295,6 +295,35 @@ says so — rerun `outerloop init --force` online, or set the login in the
295
295
 
296
296
  ### 2c. Run the loop
297
297
 
298
+ When the configured author's CLI is missing, `outerloop init` installs it from
299
+ a pinned release and records its path. An existing executable is kept.
300
+ Use `--no-install-harness` to skip this step. To install by hand from the
301
+ outerloop checkout, run `bash scripts/install_claude.sh [target_path]` or
302
+ `bash scripts/install_codex.sh [target_path]`, then rerun `outerloop init --force`.
303
+ The default target is `$OUTERLOOP_<BACKEND>_BIN`, else `~/.local/bin/<backend>`.
304
+ Claude 2.1.272 is pinned for Linux x64 (glibc/musl) and ARM64; other platforms
305
+ are refused. Installation needs `curl`, `sha256sum`, and a writable target
306
+ directory. Hermes remains a review backend, provisioned with
307
+ `bash scripts/install_hermes.sh [target_dir]`.
308
+
309
+ **Host prerequisites for model backends.** From the outerloop checkout:
310
+
311
+ - Claude, as author or reviewer: the pinned Claude Code CLI; install with
312
+ `bash scripts/install_claude.sh`.
313
+ - Codex, as author or reviewer: the pinned Codex CLI; install with
314
+ `bash scripts/install_codex.sh`.
315
+ - Hermes, as reviewer only (not an author backend): the pinned hermes-agent
316
+ source checkout, run through `uv`; install with `bash scripts/install_hermes.sh`.
317
+
318
+ `init` records the absolute Claude
319
+ or Codex path found on PATH (or in `~/.local/bin`) as `OUTERLOOP_<BACKEND>_BIN`.
320
+ `start` checks only the configured author's CLI: the recorded path takes
321
+ precedence, otherwise it searches PATH, then `~/.local/bin`. A missing or non-executable CLI stops
322
+ launch before any job runs. After installing or moving it, run
323
+ `outerloop init --force` to record its path again. `--dry-run` prints the launch
324
+ command without checking the CLI. For Hermes, set `REVIEW_HERMES_REPO` to the installed checkout (the installer
325
+ defaults to `~/hermes-agent`); contained review sessions bind its source read-only.
326
+
298
327
  The quickest path is the guided setup:
299
328
 
300
329
  ```bash
@@ -321,7 +350,7 @@ that can reach GitHub and your LLM provider works.
321
350
  you start it again. On a headless machine start it under `nohup` or in a
322
351
  tmux window, or as a user service.
323
352
 
324
- **Updates.** A Slurm deployment runs from a checkout, and
353
+ **Updates.** A resident Slurm deployment runs from a checkout, and
325
354
  `OUTERLOOP_AUTO_UPDATE` in `.env` says whether the deploy step moves it. `off`,
326
355
  the default, leaves the checkout alone: you upgrade when you choose, and the
327
356
  loop keeps running the code you validated. `release` moves it to the newest
@@ -334,6 +363,44 @@ the installed package, which has no such policy: run `outerloop upgrade` to move
334
363
  it to the newest release (add `--pre` to track pre-releases), then start it
335
364
  again. That is `pip install --upgrade outerloop-science` under one verb.
336
365
 
366
+ #### Login-node loop
367
+
368
+ Use this when no Slurm partition can start a resident tick within your cadence
369
+ and the site permits a long-lived process on the login node. In
370
+ `~/.config/outerloop/.env`, set:
371
+
372
+ ```bash
373
+ OUTERLOOP_TICK_HOST=login
374
+ OUTERLOOP_QOS=priority
375
+ OUTERLOOP_APPTAINER_BIN=/cm/local/apps/apptainer/current/bin/apptainer
376
+ ```
377
+
378
+ Run `outerloop start` from your checkout with the usual shared `OUTERLOOP_ROOT`.
379
+ `--tick-host login` overrides the environment, which overrides `.env`.
380
+ `resident` is the default on Slurm; local compute still runs locally.
381
+ The login loop requires `sbatch` on PATH, runs in the foreground at nice +10,
382
+ and submits compute to Slurm. QOS applies to all submitted jobs; the Apptainer
383
+ path is used on compute nodes. The tick only checks the image file exists and
384
+ does not run Apptainer on the login node. Empty QOS uses Slurm's default;
385
+ an empty binary setting uses `apptainer`.
386
+
387
+ Only one loop may own a state root. Every foreground loop, local or login,
388
+ holds the root's `TICK` lease (holder `host:pid`, heartbeat and the holder's
389
+ cadence written each tick); a file lock refuses a second loop while the owner
390
+ runs. `outerloop start` refuses any loop while the lease is held, and a login
391
+ loop while a resident chain is queued or running; a resident submission that
392
+ finds the lease taken meanwhile withdraws its job, and a login loop that
393
+ finds a resident queued after taking the lease stops. After a crash the next
394
+ start on the same host takes the lease at once, and a start from another
395
+ host once the heartbeat is three of the holder's cadences old (file locks may
396
+ not reach across nodes); a loop whose record was overwritten from another
397
+ node stops at its next heartbeat. Ctrl-C or SIGTERM releases the lease. Use
398
+ tmux or a user service if the process should survive your terminal.
399
+
400
+ The login loop does **not auto-update**, even with `OUTERLOOP_AUTO_UPDATE=main`.
401
+ To restart or upgrade: stop the process, run `git pull`, run `uv sync`, then
402
+ run `outerloop start` again. Settings from `.env` are exported only at launch.
403
+
337
404
  ### Upgrading
338
405
 
339
406
  1. Run `outerloop upgrade` (add `--pre` for pre-releases).
@@ -397,13 +464,20 @@ lesser of 10 minutes and half the cadence), and `OUTERLOOP_MAX_JOB_MINUTES` caps
397
464
  the walltime the tick requests for the climb and author-sleep wake jobs it sizes
398
465
  (clamped under a code ceiling).
399
466
 
400
- **Two operator switches.** `touch <root>/PAUSE` pauses the loop; `touch
401
- <root>/DISARM_WAKE` stops parked runs from being woken, by the sweep and by a
402
- wake job a park already queued, until the file is removed. Both are read at
403
- every tick and by every wake, no restart: the sentinel is the switch.
404
- (`OUTERLOOP_DISPATCH_WAKE=0` in the environment that starts the loop or the
405
- chain does the same; it is not read from `.env`.) Wakes are on by default: a
406
- loop that cannot wake strands every parked run without a word.
467
+ **Three operator switches.** `touch <root>/PAUSE` stops tick work except the
468
+ heartbeat; jobs already running continue. The resident chain drains on it (it
469
+ cancels its successor and exits), so after `rm <root>/PAUSE` run `outerloop
470
+ start` there; the per-tick chain and the local loop resume on their own at the
471
+ next tick.
472
+ `touch <root>/DISARM_WAKE` stops wake delivery by the sweep and by queued wake
473
+ jobs until `rm <root>/DISARM_WAKE`, while fresh launches, ending records, and
474
+ the chain keep running (the starting environment's `OUTERLOOP_DISPATCH_WAKE=0`
475
+ also disarms wakes; it is not read from `.env`).
476
+ `touch <root>/HOLD_LAUNCHES` stops fresh intake, self-initiated, and steward runs
477
+ until `rm <root>/HOLD_LAUNCHES`, with no chain restart, while the sweep, wake and
478
+ message delivery, GitHub polling, self-merge sweep, board, and ending records
479
+ continue; existing runs keep spending, including their panels, author sessions,
480
+ and the authors' own `launch` submissions.
407
481
 
408
482
  **Local mode without an image.** On a machine with no Apptainer image,
409
483
  `OUTERLOOP_COMPUTE=local` still runs. Sessions run under the harness's own
@@ -59,6 +59,11 @@ path = "src/outerloop/__init__.py"
59
59
  [tool.hatch.build.targets.wheel]
60
60
  packages = ["src/outerloop"]
61
61
 
62
+ [tool.hatch.build.targets.wheel.force-include]
63
+ "scripts/install_claude.sh" = "outerloop/_installers/install_claude.sh"
64
+ "scripts/install_codex.sh" = "outerloop/_installers/install_codex.sh"
65
+ "scripts/install_hermes.sh" = "outerloop/_installers/install_hermes.sh"
66
+
62
67
  [tool.ruff]
63
68
  line-length = 100
64
69
  target-version = "py312"
@@ -12,4 +12,4 @@ runs the local loop elsewhere, reading placement from `~/.config/outerloop/.env`
12
12
  | `tick_resident.sh` | The resident loop: deploy → one tick under a timeout → sleep to the slot, one `afterany:self` successor, pause exits clean, shim changes resubmit the successor. |
13
13
  | `requeue_moved_successors.sh` | Cancel pending same-name jobs that are no longer on the requested partition. The tick chain runs this before adding successors. |
14
14
  | `sweep_git_locks.sh` | Remove stale `.git/*.lock` files from a checkout (older than N minutes, no live git process); the chain runs it before each deploy. |
15
- | `install_codex.sh`, `install_hermes.sh` | Pin and install the non-claude author/judge backends on the tick host. |
15
+ | `install_claude.sh`, `install_codex.sh`, `install_hermes.sh` | Pin and install author/judge backends on the host. |
@@ -0,0 +1,82 @@
1
+ #!/bin/bash
2
+ # Idempotent install of the harness-verified claude author binary.
3
+ #
4
+ # claude is a HOST prerequisite for the claude author backend (like uv, apptainer,
5
+ # and the .sif image) — deliberately NOT baked into the image, so it updates by
6
+ # swapping one host binary. Safe to run repeatedly: the fast path is a local
7
+ # `claude --version`, so it only touches the network on a version mismatch or a
8
+ # missing binary. Run at host setup, or best-effort from the tick chain.
9
+ #
10
+ # Usage: install_claude.sh [target_path]
11
+ # target_path where to place the binary
12
+ # (default: $OUTERLOOP_CLAUDE_BIN, else ~/.local/bin/claude)
13
+ set -euo pipefail
14
+
15
+ # Pinned release and per-platform SHA256. To bump WANT, hash the new binaries
16
+ # and update the pins together; downloaded bytes are verified before execution.
17
+ WANT="2.1.272"
18
+ TARGET="${1:-${OUTERLOOP_CLAUDE_BIN:-$HOME/.local/bin/claude}}"
19
+
20
+ have="$("$TARGET" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
21
+ if [ "$have" = "$WANT" ]; then
22
+ echo "install_claude: claude $WANT already at $TARGET"
23
+ exit 0
24
+ fi
25
+
26
+ platform="$(uname -s)-$(uname -m)"
27
+ case "$platform" in
28
+ Linux-x86_64)
29
+ platform="linux-x64"
30
+ WANT_SHA256="d81396a668eb76fbddb49a2a5841f1b5d7af96b4c1f6500ced92f2c988f5bcd4"
31
+ if compgen -G '/lib/libc.musl-*' > /dev/null; then
32
+ platform="linux-x64-musl"
33
+ WANT_SHA256="e30bb3ac07c4f1c3f63b47312e9a73ba6875256b7768c4cdb784d2b179417489"
34
+ fi
35
+ ;;
36
+ Linux-aarch64|Linux-arm64)
37
+ platform="linux-arm64"
38
+ WANT_SHA256="214a90efdd16ee0ea81132ffecced588dba394d178cc494f285ba04b5288c8de"
39
+ ;;
40
+ *)
41
+ echo "install_claude: unsupported platform $platform (no sha256 pin)" >&2
42
+ exit 1
43
+ ;;
44
+ esac
45
+ for tool in curl sha256sum; do
46
+ command -v "$tool" >/dev/null || {
47
+ echo "install_claude: required tool $tool is missing" >&2
48
+ exit 1
49
+ }
50
+ done
51
+ echo "install_claude: installing claude $WANT -> $TARGET (have '${have:-none}')"
52
+ url="https://downloads.claude.ai/claude-code-releases/$WANT/$platform/claude"
53
+ tmp="$(mktemp -d)"
54
+ staged="$TARGET.tmp.$$"
55
+ # clean BOTH the work dir and any staged binary on every exit, so a failed
56
+ # download/mv never leaves a stale temp next to the target
57
+ trap 'rm -rf "$tmp" "$staged"' EXIT
58
+ # bounded so a stalled download can't eat the tick's own walltime: this runs
59
+ # INSIDE the ~15-min tick job, so cap the fetch well under it (single attempt —
60
+ # a transient failure is retried by the next tick, since the install is idempotent)
61
+ curl -fsSL --connect-timeout 15 --max-time 120 --retry 0 "$url" -o "$tmp/claude"
62
+ # INTEGRITY GATE: verify the binary against the pin BEFORE installing or
63
+ # executing anything from it.
64
+ got_sha=$(sha256sum "$tmp/claude" | cut -d' ' -f1)
65
+ if [ "$got_sha" != "$WANT_SHA256" ]; then
66
+ echo "install_claude: sha256 mismatch (got $got_sha, want $WANT_SHA256) — refusing" >&2
67
+ exit 1
68
+ fi
69
+ # the target dir must exist BEFORE staging into it (the staged temp lives beside
70
+ # TARGET so the final mv is atomic on the same filesystem)
71
+ mkdir -p "$(dirname "$TARGET")"
72
+ install -m 0755 "$tmp/claude" "$staged"
73
+ # secondary sanity (integrity is already the sha256 gate above, so this runs
74
+ # VERIFIED bytes): the staged binary reports the pinned version before the mv
75
+ got="$("$staged" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true)"
76
+ if [ "$got" != "$WANT" ]; then
77
+ echo "install_claude: downloaded claude reports '$got', wanted '$WANT' — not installing" >&2
78
+ exit 1
79
+ fi
80
+ # atomic replace on the same filesystem: never leave a half-written binary
81
+ mv -f "$staged" "$TARGET"
82
+ echo "install_claude: installed claude $got at $TARGET"
@@ -52,6 +52,8 @@ done
52
52
  # partition may be a comma-list like "a,b"), pass them through.
53
53
  acct_arg=""
54
54
  [ -n "${OUTERLOOP_ACCOUNT:-}" ] && acct_arg="--account=${OUTERLOOP_ACCOUNT}"
55
+ qos_arg=""
56
+ [ -n "${OUTERLOOP_QOS:-}" ] && qos_arg="--qos=${OUTERLOOP_QOS}"
55
57
  part_arg=""
56
58
  [ -n "${OUTERLOOP_PARTITION:-}" ] && part_arg="--partition=${OUTERLOOP_PARTITION}"
57
59
 
@@ -104,7 +106,7 @@ while [ "$i" -le "$need" ]; do
104
106
  || date -r "$begin_epoch" +%Y-%m-%dT%H:%M:%S)
105
107
  for attempt in 1 2 3; do
106
108
  if sbatch --dependency=singleton --begin="$begin" \
107
- ${acct_arg:+"$acct_arg"} ${part_arg:+"$part_arg"} \
109
+ ${acct_arg:+"$acct_arg"} ${part_arg:+"$part_arg"} ${qos_arg:+"$qos_arg"} \
108
110
  "${OUTERLOOP_HOME:-}/scripts/tick_chain.sbatch"; then
109
111
  break
110
112
  fi
@@ -195,6 +195,7 @@ if [ -n "$ENV_TRUSTED" ]; then
195
195
  OUTERLOOP_TARGET \
196
196
  OUTERLOOP_GITHUB_APP_FILE OUTERLOOP_BOT_LOGIN OUTERLOOP_BOT_ALIASES \
197
197
  OUTERLOOP_GPU_PARTITION OUTERLOOP_GPU_ACCOUNT \
198
+ OUTERLOOP_QOS OUTERLOOP_APPTAINER_BIN \
198
199
  OUTERLOOP_IMAGE \
199
200
  OUTERLOOP_PANEL OUTERLOOP_PANEL_KEY_FILE \
200
201
  OUTERLOOP_PANEL_CODEX_KEY_FILE \
@@ -212,6 +213,28 @@ if [ -n "$ENV_TRUSTED" ]; then
212
213
  done
213
214
  fi
214
215
 
216
+ # The configured author's CLI is a host prerequisite (pinned installers under
217
+ # scripts/); provision it when missing. Best-effort: a failure here must never
218
+ # break the chain — start's preflight and the climb report a missing CLI.
219
+ case "${OUTERLOOP_AUTHOR_BACKEND:-claude}" in
220
+ claude)
221
+ # a recorded path is the one the jobs will use, so it alone decides;
222
+ # otherwise an absolute PATH hit or the default install location counts
223
+ # (jobs ignore relative PATH entries, so the check does too)
224
+ if [ -n "${OUTERLOOP_CLAUDE_BIN:-}" ]; then
225
+ target="$OUTERLOOP_CLAUDE_BIN"; found=""
226
+ [ -x "$target" ] && found="$target"
227
+ else
228
+ target="$HOME/.local/bin/claude"
229
+ found="$(command -v claude 2>/dev/null || true)"
230
+ case "$found" in /*) ;; *) found="" ;; esac
231
+ [ -z "$found" ] && [ -x "$target" ] && found="$target"
232
+ fi
233
+ if [ -z "$found" ]; then
234
+ bash "$OUTERLOOP_HOME/scripts/install_claude.sh" "$target" || echo "deploy: claude install failed"
235
+ fi
236
+ ;;
237
+ esac
215
238
  # The codex author binary is a host prerequisite; install it (idempotent, fast
216
239
  # path is a local version check) when ANY codex role is deployed — the fleet
217
240
  # author, or a codex panel lens (a claude-author/codex-panel rollout still
@@ -45,13 +45,15 @@ submit_successor() {
45
45
  # them only when set.
46
46
  local acct_arg=""
47
47
  [ -n "${OUTERLOOP_ACCOUNT:-}" ] && acct_arg="--account=${OUTERLOOP_ACCOUNT}"
48
+ local qos_arg=""
49
+ [ -n "${OUTERLOOP_QOS:-}" ] && qos_arg="--qos=${OUTERLOOP_QOS}"
48
50
  local part_arg=""
49
51
  [ -n "${OUTERLOOP_PARTITION:-}" ] && part_arg="--partition=${OUTERLOOP_PARTITION}"
50
52
  local out="" attempt
51
53
  for attempt in 1 2 3; do
52
54
  if out=$(sbatch --parsable --dependency="$dep" --time="$resident_minutes" \
53
55
  --job-name="$RESIDENT_JOB_NAME" --export=ALL \
54
- ${acct_arg:+"$acct_arg"} ${part_arg:+"$part_arg"} \
56
+ ${acct_arg:+"$acct_arg"} ${part_arg:+"$part_arg"} ${qos_arg:+"$qos_arg"} \
55
57
  "$shim" 2>/dev/null); then
56
58
  printf '%s' "${out%%;*}"
57
59
  return 0
@@ -1,3 +1,3 @@
1
1
  """Outerloop: autonomous research agents that improve a benchmark on your own code."""
2
2
 
3
- __version__ = "0.2.0rc2"
3
+ __version__ = "0.2.0rc4"