outerloop-science 0.5.0__tar.gz → 0.6.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 (348) hide show
  1. outerloop_science-0.6.0/.claude-plugin/marketplace.json +14 -0
  2. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/CHANGELOG.md +40 -0
  3. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/CITATION.cff +2 -2
  4. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/CONTRIBUTING.md +13 -0
  5. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/PKG-INFO +30 -6
  6. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/README.md +29 -5
  7. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/RELEASING.md +7 -4
  8. outerloop_science-0.6.0/docs/assets/loop-dark.svg +86 -0
  9. outerloop_science-0.6.0/docs/assets/loop.svg +86 -0
  10. outerloop_science-0.6.0/docs/design/trajectory.md +67 -0
  11. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/getting-started.md +33 -3
  12. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/index.md +3 -0
  13. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/install.md +5 -5
  14. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/cli.md +9 -0
  15. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/environment.md +22 -4
  16. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/session-capture.md +5 -3
  17. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/storage.md +1 -1
  18. outerloop_science-0.6.0/plugins/outerloop/.claude-plugin/plugin.json +10 -0
  19. outerloop_science-0.6.0/plugins/outerloop/.codex-plugin/plugin.json +19 -0
  20. outerloop_science-0.6.0/plugins/outerloop/skills/contract/SKILL.md +41 -0
  21. outerloop_science-0.6.0/plugins/outerloop/skills/operate/SKILL.md +52 -0
  22. outerloop_science-0.6.0/plugins/outerloop/skills/setup/SKILL.md +82 -0
  23. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/pyproject.toml +1 -0
  24. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/tick_deploy.sh +2 -1
  25. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/__init__.py +1 -1
  26. outerloop_science-0.6.0/src/outerloop/agent_text.py +343 -0
  27. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/attempt.py +212 -28
  28. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/brief.py +3 -1
  29. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/cli.py +73 -0
  30. outerloop_science-0.6.0/src/outerloop/containment/__init__.py +135 -0
  31. outerloop_science-0.6.0/src/outerloop/containment/macos.sb +18 -0
  32. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/dispatch.py +9 -1
  33. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/github.py +22 -1
  34. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/guidance_pack.py +3 -1
  35. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/harness.py +88 -12
  36. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/hermes_capture.py +20 -1
  37. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/image.py +2 -2
  38. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/inbox.py +30 -13
  39. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/init.py +9 -5
  40. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/launchlog.py +24 -0
  41. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/ledger_events.py +20 -10
  42. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/measure.py +54 -0
  43. outerloop_science-0.6.0/src/outerloop/number_check.py +86 -0
  44. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/orchestrator.py +235 -14
  45. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/posting.py +24 -4
  46. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/progress.py +9 -2
  47. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review.py +10 -1
  48. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review_agent.py +61 -6
  49. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review_post_cli.py +26 -0
  50. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review_summarize_cli.py +26 -3
  51. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/role_runner.py +7 -1
  52. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/session_evidence.py +36 -1
  53. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/status.py +11 -2
  54. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/steward.py +13 -10
  55. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/syscall.py +13 -14
  56. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/syscall_cli.py +10 -7
  57. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/tick.py +12 -5
  58. outerloop_science-0.6.0/src/outerloop/tool_calls.py +296 -0
  59. outerloop_science-0.6.0/src/outerloop/trajectory.py +332 -0
  60. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/verifier.py +14 -3
  61. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/conftest.py +20 -0
  62. outerloop_science-0.6.0/tests/test_agent_plugin.py +31 -0
  63. outerloop_science-0.6.0/tests/test_agent_text.py +177 -0
  64. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_app_permissions.py +27 -0
  65. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_attempt.py +412 -535
  66. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_attempt_review.py +90 -104
  67. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_author_overrides.py +5 -0
  68. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_brief.py +3 -2
  69. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_climbboard.py +47 -80
  70. outerloop_science-0.6.0/tests/test_containment.py +180 -0
  71. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_contract_switches.py +16 -0
  72. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_endpoints.py +51 -0
  73. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_github.py +65 -106
  74. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_harness.py +154 -178
  75. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_inbox.py +50 -89
  76. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_init.py +194 -302
  77. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_launchlog.py +8 -3
  78. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_local_compute.py +88 -164
  79. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_measure.py +21 -0
  80. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_messages.py +8 -0
  81. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_orchestrator.py +225 -127
  82. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_posting.py +56 -2
  83. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_progress.py +9 -0
  84. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_agent.py +103 -1
  85. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_summarize.py +24 -1
  86. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_session_evidence.py +78 -0
  87. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_start.py +168 -199
  88. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_status.py +36 -0
  89. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_syscall.py +65 -86
  90. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_tick.py +368 -597
  91. outerloop_science-0.6.0/tests/test_tool_calls.py +281 -0
  92. outerloop_science-0.6.0/tests/test_trajectory.py +222 -0
  93. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_verifier.py +16 -0
  94. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_version.py +1 -1
  95. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/.gitignore +0 -0
  96. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/.pre-commit-config.yaml +0 -0
  97. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/.python-version +0 -0
  98. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/CLAUDE.md +0 -0
  99. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/LICENSE +0 -0
  100. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/NOTICE +0 -0
  101. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/SECURITY.md +0 -0
  102. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/containers/README.md +0 -0
  103. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/containers/agent-py312.def +0 -0
  104. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/icon-dark.svg +0 -0
  105. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/icon-light.svg +0 -0
  106. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/icon.svg +0 -0
  107. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/outerloop-apple-touch-icon.png +0 -0
  108. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/outerloop-favicon.png +0 -0
  109. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/outerloop-favicon.svg +0 -0
  110. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/community.md +0 -0
  111. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/concurrency.md +0 -0
  112. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/contract.md +0 -0
  113. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/accelerators.md +0 -0
  114. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/agent-environment.md +0 -0
  115. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/agent-protocols.md +0 -0
  116. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/agent-substrate.md +0 -0
  117. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/architecture.md +0 -0
  118. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/base-reintegration.md +0 -0
  119. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/compute-cloud.md +0 -0
  120. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/consolidation.md +0 -0
  121. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/dispatcher.md +0 -0
  122. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/eval-cache.md +0 -0
  123. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/external.md +0 -0
  124. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/forge.md +0 -0
  125. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/github-app-auth.md +0 -0
  126. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/headline.md +0 -0
  127. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/judge-placement.md +0 -0
  128. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/lifecycle.md +0 -0
  129. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/meta.md +0 -0
  130. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/model-gateway.md +0 -0
  131. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/multi-cluster.md +0 -0
  132. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/onboarding.md +0 -0
  133. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/orchestrator-verify.md +0 -0
  134. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/personal-token.md +0 -0
  135. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/public-surface.md +0 -0
  136. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/research-lines.md +0 -0
  137. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/research-loop-buildout.md +0 -0
  138. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/research-loop.md +0 -0
  139. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/resident-tick.md +0 -0
  140. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/review-placement.md +0 -0
  141. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/reviewer-infra.md +0 -0
  142. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/role-cli.md +0 -0
  143. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/roles.md +0 -0
  144. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/scaling.md +0 -0
  145. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/session-watcher.md +0 -0
  146. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/endpoints.md +0 -0
  147. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/how-it-works.md +0 -0
  148. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/javascripts/figures.js +0 -0
  149. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/messages.md +0 -0
  150. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/operate.md +0 -0
  151. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/contract.md +0 -0
  152. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/inbox.md +0 -0
  153. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/syscalls.md +0 -0
  154. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reviewer.md +0 -0
  155. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/roadmap.md +0 -0
  156. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/stylesheets/figures.css +0 -0
  157. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/stylesheets/site.css +0 -0
  158. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/validation/author-syscalls.md +0 -0
  159. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/examples/review.yml +0 -0
  160. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/mkdocs.yml +0 -0
  161. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/bi/circle-half.svg +0 -0
  162. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/bi/moon.svg +0 -0
  163. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/bi/search.svg +0 -0
  164. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/bi/sun.svg +0 -0
  165. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/lab/github.svg +0 -0
  166. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/main.html +0 -0
  167. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/partials/logo.html +0 -0
  168. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/README.md +0 -0
  169. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/docs/build.sh +0 -0
  170. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/docs/hooks.py +0 -0
  171. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/install_bridge.sh +0 -0
  172. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/install_claude.sh +0 -0
  173. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/install_codex.sh +0 -0
  174. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/install_hermes.sh +0 -0
  175. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/requeue_moved_successors.sh +0 -0
  176. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/setup_branch_protection.sh +0 -0
  177. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/sweep_git_locks.sh +0 -0
  178. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/tick_chain.sbatch +0 -0
  179. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/tick_resident.sh +0 -0
  180. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/__main__.py +0 -0
  181. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/appauth.py +0 -0
  182. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/appmanifest.py +0 -0
  183. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/author_overrides.py +0 -0
  184. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/bridge_install.py +0 -0
  185. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/bridge_runtime/pyproject.toml +0 -0
  186. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/bridge_runtime/uv.lock +0 -0
  187. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/climbboard.py +0 -0
  188. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/codex_bridge.py +0 -0
  189. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/codex_requirements.toml +0 -0
  190. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/compute.py +0 -0
  191. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/contract.py +0 -0
  192. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/contract_cli.py +0 -0
  193. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/disk.py +0 -0
  194. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/endpoint_wait.py +0 -0
  195. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/endpoints.py +0 -0
  196. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/evalcache.py +0 -0
  197. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/forge.py +0 -0
  198. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/forge_local.py +0 -0
  199. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/gpu_lanes.py +0 -0
  200. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/harness_cli.py +0 -0
  201. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/harness_pins.py +0 -0
  202. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/harnesses.toml +0 -0
  203. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/hermes_install.py +0 -0
  204. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/housekeeping.py +0 -0
  205. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/hypothesis.py +0 -0
  206. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/instance.py +0 -0
  207. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/intake.py +0 -0
  208. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/job_names.py +0 -0
  209. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/ledger_branch.py +0 -0
  210. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/ledger_migrate.py +0 -0
  211. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/limits.py +0 -0
  212. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/maintain.py +0 -0
  213. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/maintain_agent_cli.py +0 -0
  214. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/maintain_post_cli.py +0 -0
  215. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/markers.py +0 -0
  216. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/operator_limits.py +0 -0
  217. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/panel.py +0 -0
  218. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/paths.py +0 -0
  219. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/provenance.py +0 -0
  220. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/py.typed +0 -0
  221. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/rebind.py +0 -0
  222. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review_agent_cli.py +0 -0
  223. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/roles.py +0 -0
  224. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/rolespec.py +0 -0
  225. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/runstate.py +0 -0
  226. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/style.py +0 -0
  227. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/verify_agent.py +0 -0
  228. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/verify_agent_cli.py +0 -0
  229. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/verify_post_cli.py +0 -0
  230. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/watcher.py +0 -0
  231. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fakes.py +0 -0
  232. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/README.md +0 -0
  233. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/author_route_legacy.json +0 -0
  234. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/author_route_missing_model.json +0 -0
  235. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/author_route_missing_route.json +0 -0
  236. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/base_moved_cc4e5d7.json +0 -0
  237. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/climb-status-legacy.json +0 -0
  238. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/default_contract_head.json +0 -0
  239. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/command.txt +0 -0
  240. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/exit-code +0 -0
  241. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/job.sh +0 -0
  242. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/provenance.json +0 -0
  243. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/stdout +0 -0
  244. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
  245. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/identity.json +0 -0
  246. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/command.txt +0 -0
  247. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/job.sh +0 -0
  248. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/provenance.json +0 -0
  249. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
  250. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/hermes_resume_legacy.json +0 -0
  251. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/hermes_sample_20260924.json +0 -0
  252. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/launches-before-provenance.jsonl +0 -0
  253. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/panel_wake_9ca3d7c.json +0 -0
  254. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/pre_contract_switches.json +0 -0
  255. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/baselines/main@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json +0 -0
  256. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/brief.txt +0 -0
  257. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/end-request.json +0 -0
  258. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/ended.json +0 -0
  259. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-provenance/command.txt +0 -0
  260. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-provenance/job.sh +0 -0
  261. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/command.txt +0 -0
  262. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/exit-code +0 -0
  263. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/job.sh +0 -0
  264. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/stdout +0 -0
  265. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
  266. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/generate.py.txt +0 -0
  267. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/identity.json +0 -0
  268. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/launches.jsonl +0 -0
  269. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/line.bundle +0 -0
  270. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/merged.json +0 -0
  271. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/open.json +0 -0
  272. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/pending/owner__repo.json +0 -0
  273. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/pending/owner__repo@agent-01.json +0 -0
  274. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/pr-states.json +0 -0
  275. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/queue.json +0 -0
  276. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/rebind.json +0 -0
  277. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/sessionless.json +0 -0
  278. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/session_cost_legacy.json +0 -0
  279. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/session_index_legacy.json +0 -0
  280. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/suite_measurement_legacy.json +0 -0
  281. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/helpers.py +0 -0
  282. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/ledger_fake.py +0 -0
  283. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_appauth.py +0 -0
  284. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_appmanifest.py +0 -0
  285. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_bot_aliases.py +0 -0
  286. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_bot_login.py +0 -0
  287. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_channel_dir.py +0 -0
  288. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_codex_bridge.py +0 -0
  289. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_codex_harness.py +0 -0
  290. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_codex_hooks.py +0 -0
  291. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_codex_host.py +0 -0
  292. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_compute.py +0 -0
  293. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_contract.py +0 -0
  294. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_contract_cli.py +0 -0
  295. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_contract_names.py +0 -0
  296. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_default_claude_model.py +0 -0
  297. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_disk.py +0 -0
  298. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_dispatch.py +0 -0
  299. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_endpoint_wait.py +0 -0
  300. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_evalcache.py +0 -0
  301. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_forge.py +0 -0
  302. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_forge_local.py +0 -0
  303. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_forge_local_e2e.py +0 -0
  304. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_forge_local_records.py +0 -0
  305. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_gpu_lanes.py +0 -0
  306. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_hardening.py +0 -0
  307. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_harness_pins.py +0 -0
  308. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_hermes_author.py +0 -0
  309. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_hermes_harness.py +0 -0
  310. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_housekeeping.py +0 -0
  311. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_identity.py +0 -0
  312. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_image.py +0 -0
  313. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_import.py +0 -0
  314. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_install_harness.py +0 -0
  315. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_instance.py +0 -0
  316. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_intake.py +0 -0
  317. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_ledger_events.py +0 -0
  318. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_ledger_migrate.py +0 -0
  319. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_lifecycle_cli.py +0 -0
  320. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_limits.py +0 -0
  321. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_maintain.py +0 -0
  322. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_markers.py +0 -0
  323. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_measure_and_decide.py +0 -0
  324. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_operator_end.py +0 -0
  325. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_operator_limits.py +0 -0
  326. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_operator_message.py +0 -0
  327. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_packaging.py +0 -0
  328. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_panel.py +0 -0
  329. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_paths.py +0 -0
  330. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_rebind.py +0 -0
  331. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_requeue_moved_successors.py +0 -0
  332. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review.py +0 -0
  333. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_agent_cli.py +0 -0
  334. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_hardening.py +0 -0
  335. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_policy.py +0 -0
  336. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_role_runner.py +0 -0
  337. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_rolespec.py +0 -0
  338. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_runstate.py +0 -0
  339. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_steward.py +0 -0
  340. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_sweep_git_locks.py +0 -0
  341. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_syscall_cli.py +0 -0
  342. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_tick_chain_successors.py +0 -0
  343. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_tick_resident.py +0 -0
  344. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_tiers.py +0 -0
  345. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_verify_agent.py +0 -0
  346. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_verify_agent_cli.py +0 -0
  347. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_watcher.py +0 -0
  348. {outerloop_science-0.5.0 → outerloop_science-0.6.0}/uv.lock +0 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "outerloop",
3
+ "owner": {
4
+ "name": "Outerloop"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "outerloop",
9
+ "source": "./plugins/outerloop",
10
+ "category": "Productivity"
11
+ }
12
+ ],
13
+ "description": "Set up and operate Outerloop from your coding agent."
14
+ }
@@ -6,6 +6,46 @@ Versions follow [SemVer](https://semver.org).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.6.0] - 2026-10-26
10
+
11
+ ### Upgrading
12
+
13
+ No operator action is needed before upgrading; details in each entry.
14
+
15
+ What changes for runs in flight: runs started before the upgrade have no
16
+ trajectory until their next recorded event, and their earlier session events
17
+ count as author sessions. The first run to end creates the attestation key
18
+ `trajectory_attest.key`, owner-only, beside the operator settings file. On
19
+ macOS without an image, sessions, evaluations and panel lenses now run under
20
+ the Seatbelt profile, which allows outbound connections only to TCP port 443,
21
+ DNS and local ports.
22
+
23
+ Before rolling back: if you switched the author to a Claude subscription
24
+ token, switch back to an API key, since older kernels send the token as
25
+ `ANTHROPIC_API_KEY`. Older kernels ignore `trajectory.jsonl`, the attestation
26
+ key and `OUTERLOOP_AUTHOR_EFFORT`, and pending ledger records are unchanged.
27
+
28
+ ### Changes
29
+
30
+ - `OUTERLOOP_AUTHOR_EFFORT` sets the author's effort level (`low`, `medium`, `high`, `xhigh` or `max`) on every backend: Claude Code's `--effort` (the pinned 2.1.285 has the flag), Codex's `model_reasoning_effort` as a kernel-owned `-c` setting, and Hermes's `agent.reasoning_effort`. For Hermes the setting is written to the seeded `config.yaml`, and the capture wrapper also hands it to the agent, because `run_agent.main` builds its agent without a reasoning config and would otherwise ignore it. Unset keeps each harness's default. Panel judges and the steward ignore it, so a cheaper author does not change how its work is reviewed. Start, the tick and each wake refuse an unknown level. The setting is read when each session starts and is not stored in run records.
31
+ - Run endings append one `attest` trajectory event: an HMAC-SHA256 over the run id and chain head with a deployment key created owner-only beside the operator settings file, and `outerloop trajectory --verify` reports `attested` or `attestation mismatch`. Older kernels ignore the event; no ledger or PR-body change.
32
+ - The PR body gains a report-only Number check that lists values in the author's report matching no `measure` event or Experiments result when rounded to the report's decimals; nothing is blocked and no trajectory event is added.
33
+ - Session end records each tool call from the retained Claude or Codex log as a `tool.call` trajectory event, once per call even when a session resumes. `outerloop trajectory` and a new Process section in the PR body show counts by tool. Older kernels ignore these events.
34
+ - `outerloop status` shows a run's priced session cost as a lower bound when some legs are unpriced (a wake killed mid-turn leaves no usage event), for example `session-cost>=$4.2000 (1 unpriced)`, instead of hiding the priced part behind `unknown`; the status row and JSON gain the priced-leg count (#516).
35
+ - The pre-PR panel accepts a composite change — a PR that carries several changes — when the report gives each component's isolated measurement and the combination's under the same protocol and the combination is what the kernel measured. A bundle without those isolated measurements stays blocked (#489).
36
+ - Claude sessions can use a subscription token from `claude setup-token` (`sk-ant-oat...`). The harness sends it as `CLAUDE_CODE_OAUTH_TOKEN`, not `ANTHROPIC_API_KEY`, and `init` accepts it. A panel judge on a token runs `claude --safe-mode`, because `--bare` cannot read OAuth tokens. Both modes ignore the checkout's CLAUDE.md, hooks, plugins and MCP servers. The pinned Claude Code release (2.1.285) has `--safe-mode`.
37
+ - A session now hears once when a live sibling states a new or changed hypothesis (#360). The note names the sibling, quotes its hypothesis, and points to `siblings` for its launches and score. It is sent only when `channels.siblings` is on. Upgrading: the note uses the existing `note` kind, so an older kernel shows it as ordinary inbox text.
38
+ - A skills-only plugin for Claude Code and Codex sets up Outerloop, writes and validates the contract, and operates runs from the agent (#562). Install it with `/plugin marketplace add outerloop-science/outerloop` or `codex plugin marketplace add outerloop-science/outerloop`. Its setup skill offers a Claude subscription token from `claude setup-token` as the author credential.
39
+ - `outerloop trajectory <run-id>` now ends with the run's refusal count and its author models, for release canary comparisons; the agent-text inventory includes operator notes (#487, part 3). Session events record `role` (`author` or `judge`). Upgrading: session events written before this kernel have no `role` and count as author sessions.
40
+ - On macOS without an image, sessions, evaluations and panel lenses run under a packaged Seatbelt profile (`sandbox-exec`, #292): they write only the workspace and a scratch directory, read only system and toolchain paths, and connect out only to TCP port 443, DNS and local ports. Linux and Apptainer runs are unchanged.
41
+ - Kernel events now append to a per-run `trajectory.jsonl` with sequence numbers and a SHA-256 chain. `outerloop trajectory <run-id> [--verify] [--json]` reads it without changing run state. Operational files and their formats are unchanged; older kernels ignore the new file. Upgrading: runs started before this kernel have no trajectory and read as "no trajectory" until their next recorded event. No backfill is attempted.
42
+ - `measure` trajectory events carry provenance: the ledger's ruler digest (the same value a pending submission computes), the container image digest when an image is configured, and refs to the evaluation result files the kernel read. Self-reported measures record the ruler alone. Pending ledger records now ignore fields this kernel does not know, so a later release can add one without breaking this release's ledger read. Upgrading: no action needed; older kernels ignore the trajectory, and pending records are unchanged.
43
+ - Add a printable agent-text inventory and protocol wording lint, with clearer refusal recovery guidance (#487, parts 1–2).
44
+ - `BENCHMARKS.md` shows `—` instead of "provenance unknown" in the main-commit column of a baseline row that nothing has improved on yet, as on a freshly seeded repository.
45
+ - The README and the getting-started guide link to the quickstart repository. It runs two CPU benchmarks with the local forge and local compute, and needs no GitHub account or cluster (#563). The docs and the startup warning no longer say an uncontained session runs in a sandbox: on Linux without an image, it gets a scrubbed environment and its own home, but can read any file the account can.
46
+ - The advisory review covers exactly the head its triggering event named (`REVIEW_HEAD_SHA`, #462). If the PR has moved off that head, nothing is reviewed or posted. Otherwise the diff is read between fixed commits, every envelope records the reviewed `head_sha`, and the review is attached to that commit. When GitHub refuses its inline comments, the fallback is a body-only review on the same commit, never an unpinned comment (#569). The summarizer skips clean when its lenses read different heads. For a fork PR (`REVIEW_CHECKOUT_BASE=1`) the brief says the tree is the base branch and the change is only in the diff. Upgrading: envelopes without `head_sha` (from an older reviewer ref) still post; callers that do not pass REVIEW_HEAD_SHA behave as before.
47
+ - When check runs cannot be read on a token deployment, the warning now says the token cannot read them (fine-grained tokens have no checks permission) instead of asking for an App permission.
48
+
9
49
  ## [0.5.0] - 2026-10-23
10
50
 
11
51
  ### 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.5.0
17
- date-released: "2026-10-23"
16
+ version: 0.6.0
17
+ date-released: "2026-10-26"
@@ -61,6 +61,19 @@ uv run pytest
61
61
  `uv lock`; CI checks the lock.
62
62
  - Docs are plain prose. Short sentences, no metaphors, no internal jargon.
63
63
 
64
+ New agent-facing text must be reachable from `outerloop.agent_text.inventory()`.
65
+ Keep text with its renderer and add a representative rendering to the inventory;
66
+ review it with `python -m outerloop.agent_text`. The fast lint in
67
+ `tests/test_agent_text.py` checks protocol terms, syscall names, and refusal recovery
68
+ guidance. Keep refusals inline: the inventory reads explicit AST constructs listed in
69
+ `REFUSAL_CONSTRUCTS`, rendering f-string expressions as `{}`. Input constraints
70
+ use category `validation` and need no redundant next step. The syscall CLI mixes
71
+ constraints and state refusals in `ToolError`; `CLI_REFUSALS` identifies the latter
72
+ by function and call ordinal, so update it when adding or reordering those calls. Delivery reasons composed into a
73
+ refusal use category `reason`; the enclosing refusal carries recovery guidance.
74
+ Kernel refusals require a next step; any exception needs a specific reason beside
75
+ the lint rule.
76
+
64
77
  ## Tests
65
78
 
66
79
  | Tier | Marker | Runs |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: outerloop-science
3
- Version: 0.5.0
3
+ Version: 0.6.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
@@ -41,6 +41,11 @@ change, runs the experiment on your cluster, and opens a pull request only
41
41
  when your benchmark actually improves. Every attempt ends in a short
42
42
  written report, including the attempts that failed.
43
43
 
44
+ <picture>
45
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/outerloop-science/outerloop/main/docs/assets/loop-dark.svg">
46
+ <img alt="One attempt: an agent proposes a change, runs the experiment on your GPUs, the kernel re-measures it against the base, judges review the claim, and a pull request opens with a written report. Without a real gain the attempt ends in a report and the next hypothesis starts." src="https://raw.githubusercontent.com/outerloop-science/outerloop/main/docs/assets/loop.svg" width="880">
47
+ </picture>
48
+
44
49
  You run it yourself, with your own keys, your own compute and your own
45
50
  repositories, and nothing reports back to us. It is built and used every
46
51
  day by the [Agentic Learning AI Lab](https://agenticlearning.ai) at NYU,
@@ -48,7 +53,24 @@ where it co-develops our research codebases.
48
53
 
49
54
  ## Get started
50
55
 
51
- Three commands and one file. You need a repo with a benchmark command, an
56
+ To see a run first, the [quickstart](https://github.com/outerloop-science/quickstart)
57
+ runs two CPU benchmarks on your own machine, with no GitHub account and no
58
+ cluster. You need Python 3.12 or later, [uv](https://docs.astral.sh/uv/), Git
59
+ 2.38 or later and an Anthropic API key:
60
+
61
+ ```bash
62
+ pip install outerloop-science
63
+ outerloop init --forge local --compute local --no-image --target me/quickstart \
64
+ --forge-source https://github.com/outerloop-science/quickstart
65
+ outerloop start
66
+ ```
67
+
68
+ On macOS, sessions and evaluations run under a Seatbelt profile: they write their
69
+ workspace and a scratch directory, read system and toolchain paths, and cannot read
70
+ the rest of your home. On Linux without a container they can read any file your
71
+ account can. Either way, use a spend-capped API key.
72
+
73
+ For your own repository: three commands and one file. You need a repo with a benchmark command, an
52
74
  API key for the model that will write the code, and a Slurm cluster or one
53
75
  machine with a GPU.
54
76
 
@@ -60,8 +82,10 @@ outerloop init # where the loop runs, which repo, which model and its key, y
60
82
  The wizard asks for a GitHub identity for the agents to open pull requests
61
83
  as. Pick `app` and it walks you through creating a GitHub App under your
62
84
  account and installing it on the repo. Pick `pat` if you already have a
63
- token, and it writes the config and the key files either way. Then add one
64
- file, `.outerloop.yaml`, to the repo you want improved:
85
+ token, and it writes the config and the key files either way. To try it
86
+ without GitHub, `outerloop init --forge local` keeps the repository and its
87
+ pull requests in a git repository on the same machine. Then add one file,
88
+ `.outerloop.yaml`, to the repo you want improved:
65
89
 
66
90
  ```yaml
67
91
  benchmarks:
@@ -128,8 +152,8 @@ build. See [the reviewer guide](https://docs.outerloop.science/reviewer/).
128
152
  output are rendered to the agent as fenced data, not instructions, which
129
153
  lowers the risk of injected text being followed without guaranteeing it.
130
154
  A contained session gets no GitHub credential in its environment and
131
- cannot read the operator's key files; an uncontained local session runs
132
- as your user and can read any file you can, key files included.
155
+ cannot read the operator's key files. Without a container, a macOS session
156
+ runs under Seatbelt; a Linux session can read any file you can, key files included.
133
157
  - **Budgets in code.** Launches, GPU-hours, and runs per week are enforced
134
158
  by the kernel, not left to the agent.
135
159
  - **No model lock-in.** Claude Code, Codex, and hermes-agent are wired
@@ -17,6 +17,11 @@ change, runs the experiment on your cluster, and opens a pull request only
17
17
  when your benchmark actually improves. Every attempt ends in a short
18
18
  written report, including the attempts that failed.
19
19
 
20
+ <picture>
21
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/outerloop-science/outerloop/main/docs/assets/loop-dark.svg">
22
+ <img alt="One attempt: an agent proposes a change, runs the experiment on your GPUs, the kernel re-measures it against the base, judges review the claim, and a pull request opens with a written report. Without a real gain the attempt ends in a report and the next hypothesis starts." src="https://raw.githubusercontent.com/outerloop-science/outerloop/main/docs/assets/loop.svg" width="880">
23
+ </picture>
24
+
20
25
  You run it yourself, with your own keys, your own compute and your own
21
26
  repositories, and nothing reports back to us. It is built and used every
22
27
  day by the [Agentic Learning AI Lab](https://agenticlearning.ai) at NYU,
@@ -24,7 +29,24 @@ where it co-develops our research codebases.
24
29
 
25
30
  ## Get started
26
31
 
27
- Three commands and one file. You need a repo with a benchmark command, an
32
+ To see a run first, the [quickstart](https://github.com/outerloop-science/quickstart)
33
+ runs two CPU benchmarks on your own machine, with no GitHub account and no
34
+ cluster. You need Python 3.12 or later, [uv](https://docs.astral.sh/uv/), Git
35
+ 2.38 or later and an Anthropic API key:
36
+
37
+ ```bash
38
+ pip install outerloop-science
39
+ outerloop init --forge local --compute local --no-image --target me/quickstart \
40
+ --forge-source https://github.com/outerloop-science/quickstart
41
+ outerloop start
42
+ ```
43
+
44
+ On macOS, sessions and evaluations run under a Seatbelt profile: they write their
45
+ workspace and a scratch directory, read system and toolchain paths, and cannot read
46
+ the rest of your home. On Linux without a container they can read any file your
47
+ account can. Either way, use a spend-capped API key.
48
+
49
+ For your own repository: three commands and one file. You need a repo with a benchmark command, an
28
50
  API key for the model that will write the code, and a Slurm cluster or one
29
51
  machine with a GPU.
30
52
 
@@ -36,8 +58,10 @@ outerloop init # where the loop runs, which repo, which model and its key, y
36
58
  The wizard asks for a GitHub identity for the agents to open pull requests
37
59
  as. Pick `app` and it walks you through creating a GitHub App under your
38
60
  account and installing it on the repo. Pick `pat` if you already have a
39
- token, and it writes the config and the key files either way. Then add one
40
- file, `.outerloop.yaml`, to the repo you want improved:
61
+ token, and it writes the config and the key files either way. To try it
62
+ without GitHub, `outerloop init --forge local` keeps the repository and its
63
+ pull requests in a git repository on the same machine. Then add one file,
64
+ `.outerloop.yaml`, to the repo you want improved:
41
65
 
42
66
  ```yaml
43
67
  benchmarks:
@@ -104,8 +128,8 @@ build. See [the reviewer guide](https://docs.outerloop.science/reviewer/).
104
128
  output are rendered to the agent as fenced data, not instructions, which
105
129
  lowers the risk of injected text being followed without guaranteeing it.
106
130
  A contained session gets no GitHub credential in its environment and
107
- cannot read the operator's key files; an uncontained local session runs
108
- as your user and can read any file you can, key files included.
131
+ cannot read the operator's key files. Without a container, a macOS session
132
+ runs under Seatbelt; a Linux session can read any file you can, key files included.
109
133
  - **Budgets in code.** Launches, GPU-hours, and runs per week are enforced
110
134
  by the kernel, not left to the agent.
111
135
  - **No model lock-in.** Claude Code, Codex, and hermes-agent are wired
@@ -43,21 +43,24 @@ wording does not reach a parked run unless its deduplication key changes.
43
43
  run with an open PR, a run whose PR merged, and an ended run. A dev or rc pre-release still bumps the version (PyPI never
44
44
  accepts a version twice, so the next one is `.dev1`, `rc2`, ...) but leaves
45
45
  `[Unreleased]` in place until the final release.
46
- 3. `git tag vX.Y.Z && git push origin vX.Y.Z`. The `release` workflow builds
46
+ 3. Pre-release canary runs report `refusals` per run by author model from
47
+ `outerloop trajectory <run-id>`. If refusals per run rise against the previous
48
+ release, explain why in the release PR before shipping.
49
+ 4. `git tag vX.Y.Z && git push origin vX.Y.Z`. The `release` workflow builds
47
50
  and publishes `outerloop-science` to PyPI through Trusted Publishing; the
48
51
  one-time PyPI setup is described at the top of
49
52
  `.github/workflows/release.yml`.
50
- 4. Once the `release` workflow run for the tag is green (`gh run watch` on
53
+ 5. Once the `release` workflow run for the tag is green (`gh run watch` on
51
54
  it), `pip install outerloop-science==X.Y.Z` in a fresh venv, then
52
55
  `outerloop --help`. This comes before the GitHub release: publishing it is
53
56
  what Discord announces, so nothing is announced that did not install.
54
- 5. `gh release create vX.Y.Z --generate-notes`, with `--prerelease` for a dev
57
+ 6. `gh release create vX.Y.Z --generate-notes`, with `--prerelease` for a dev
55
58
  or rc tag.
56
59
  For a final release, also move the `v0` tag that external callers of the
57
60
  reusable workflows track: `git tag -f v0 vX.Y.Z && git push -f origin v0`.
58
61
  Dev and rc tags never move it. This tag alias is the one force-push the
59
62
  repository allows; branches are never force-pushed.
60
- 6. Announce. Discord's `#announcements` gets the release from the GitHub
63
+ 7. Announce. Discord's `#announcements` gets the release from the GitHub
61
64
  webhook on its own (docs/community.md). For a final release, also write
62
65
  the post for X (`@outerloop_sci`) and Bluesky (`@outerloop.science`): one
63
66
  or two sentences on what changed for the reader, the install line, the
@@ -0,0 +1,86 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 880 210" width="880" height="210" role="img" aria-label="One Outerloop attempt: an agent proposes a change, runs the experiment on your GPUs, the kernel re-measures it against the base, judges review the claim, and a pull request opens with a written report. Without a real gain the attempt ends in a report and the next hypothesis.">
2
+ <style>
3
+ svg { --fg: #e4e7eb; --muted: #9aa5b1; --line: #3e4c59; --card: #161b22; --accent: #6ea2ff; --good: #4cc38a; }
4
+ text { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; fill: var(--fg); }
5
+ .title { font-size: 15px; font-weight: 600; }
6
+ .sub { font-size: 12px; fill: var(--muted); }
7
+ .node { fill: var(--card); stroke: var(--line); stroke-width: 1.5; }
8
+ .edge { stroke: var(--line); stroke-width: 1.5; fill: none; }
9
+ .back { stroke: var(--line); stroke-width: 1.5; fill: none; stroke-dasharray: 5 5; }
10
+ .head { fill: var(--line); }
11
+ .dot { fill: var(--accent); }
12
+ .score { font-size: 12px; font-variant-numeric: tabular-nums; fill: var(--fg); }
13
+ .ok { fill: var(--good); font-weight: 600; }
14
+ .pr { fill: var(--accent); font-weight: 600; font-size: 12px; }
15
+
16
+ .n1 { animation: lit1 12s infinite; } .n2 { animation: lit2 12s infinite; }
17
+ .n3 { animation: lit3 12s infinite; } .n4 { animation: lit4 12s infinite; }
18
+ .n5 { animation: lit5 12s infinite; }
19
+
20
+ @keyframes lit1 { 0%, 11% { stroke: var(--accent); stroke-width: 2.5; } 13%, 100% { stroke: var(--line); stroke-width: 1.5; } }
21
+ @keyframes lit2 { 0%, 15% { stroke: var(--line); stroke-width: 1.5; } 16%, 31% { stroke: var(--accent); stroke-width: 2.5; } 33%, 100% { stroke: var(--line); stroke-width: 1.5; } }
22
+ @keyframes lit3 { 0%, 35% { stroke: var(--line); stroke-width: 1.5; } 36%, 53% { stroke: var(--accent); stroke-width: 2.5; } 55%, 100% { stroke: var(--line); stroke-width: 1.5; } }
23
+ @keyframes lit4 { 0%, 57% { stroke: var(--line); stroke-width: 1.5; } 58%, 71% { stroke: var(--accent); stroke-width: 2.5; } 73%, 100% { stroke: var(--line); stroke-width: 1.5; } }
24
+ @keyframes lit5 { 0%, 75% { stroke: var(--line); stroke-width: 1.5; } 76%, 96% { stroke: var(--accent); stroke-width: 2.5; } 98%, 100% { stroke: var(--line); stroke-width: 1.5; } }
25
+
26
+ @media (prefers-reduced-motion: reduce) {
27
+ .n1, .n2, .n3, .n4, .n5 { animation: none; }
28
+ }
29
+ </style>
30
+
31
+ <!-- edges between the five steps -->
32
+ <g>
33
+ <line class="edge" x1="172" y1="68" x2="186" y2="68"/><polygon class="head" points="192,68 184,64 184,72"/>
34
+ <line class="edge" x1="344" y1="68" x2="358" y2="68"/><polygon class="head" points="364,68 356,64 356,72"/>
35
+ <line class="edge" x1="516" y1="68" x2="530" y2="68"/><polygon class="head" points="536,68 528,64 528,72"/>
36
+ <line class="edge" x1="688" y1="68" x2="702" y2="68"/><polygon class="head" points="708,68 700,64 700,72"/>
37
+ </g>
38
+
39
+ <!-- the steps -->
40
+ <g>
41
+ <rect class="node n1" x="20" y="36" width="152" height="64" rx="10"/>
42
+ <text class="title" x="96" y="63" text-anchor="middle">Propose</text>
43
+ <text class="sub" x="96" y="83" text-anchor="middle">agent edits the solver</text>
44
+
45
+ <rect class="node n2" x="192" y="36" width="152" height="64" rx="10"/>
46
+ <text class="title" x="268" y="63" text-anchor="middle">Experiment</text>
47
+ <text class="sub" x="268" y="83" text-anchor="middle">jobs on your GPUs</text>
48
+
49
+ <rect class="node n3" x="364" y="36" width="152" height="64" rx="10"/>
50
+ <text class="title" x="440" y="63" text-anchor="middle">Measure</text>
51
+ <text class="sub" x="440" y="83" text-anchor="middle">Outerloop re-measures</text>
52
+
53
+ <rect class="node n4" x="536" y="36" width="152" height="64" rx="10"/>
54
+ <text class="title" x="612" y="63" text-anchor="middle">Review</text>
55
+ <text class="sub" x="612" y="83" text-anchor="middle">judges read the claim</text>
56
+
57
+ <rect class="node n5" x="708" y="36" width="152" height="64" rx="10"/>
58
+ <text class="title" x="784" y="63" text-anchor="middle">Pull request</text>
59
+ <text class="sub" x="784" y="83" text-anchor="middle">with a written report</text>
60
+ </g>
61
+
62
+ <!-- the attempt moving through -->
63
+ <circle class="dot" cx="96" cy="22" r="6">
64
+ <animateTransform attributeName="transform" type="translate" dur="12s" repeatCount="indefinite"
65
+ values="0 0; 0 0; 172 0; 172 0; 344 0; 344 0; 516 0; 516 0; 688 0; 688 0"
66
+ keyTimes="0; 0.10; 0.16; 0.30; 0.36; 0.52; 0.58; 0.70; 0.76; 1"/>
67
+ </circle>
68
+
69
+ <!-- what the measure step reports -->
70
+ <g class="readout" opacity="0">
71
+ <animate attributeName="opacity" dur="12s" repeatCount="indefinite"
72
+ values="0; 0; 1; 1; 0" keyTimes="0; 0.39; 0.42; 0.94; 1"/>
73
+ <text class="score" x="440" y="124" text-anchor="middle">base 9,472 steps → candidate 7,808</text>
74
+ <text class="score ok" x="440" y="142" text-anchor="middle">more than run-to-run noise</text>
75
+ </g>
76
+ <g class="badge" opacity="0">
77
+ <animate attributeName="opacity" dur="12s" repeatCount="indefinite"
78
+ values="0; 0; 1; 1; 0" keyTimes="0; 0.78; 0.81; 0.94; 1"/>
79
+ <text class="pr" x="784" y="124" text-anchor="middle">opened for a human to merge</text>
80
+ </g>
81
+
82
+ <!-- no gain: report and try again -->
83
+ <path class="back" d="M 440 154 L 440 176 L 96 176 L 96 108"/>
84
+ <polygon class="head" points="96,102 92,110 100,110"/>
85
+ <text class="sub" x="268" y="196" text-anchor="middle">no real gain: the report says why, and the next hypothesis starts</text>
86
+ </svg>
@@ -0,0 +1,86 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 880 210" width="880" height="210" role="img" aria-label="One Outerloop attempt: an agent proposes a change, runs the experiment on your GPUs, the kernel re-measures it against the base, judges review the claim, and a pull request opens with a written report. Without a real gain the attempt ends in a report and the next hypothesis.">
2
+ <style>
3
+ svg { --fg: #1f2933; --muted: #616e7c; --line: #c4cdd5; --card: #ffffff; --accent: #3c81f7; --good: #1f8a4c; }
4
+ text { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; fill: var(--fg); }
5
+ .title { font-size: 15px; font-weight: 600; }
6
+ .sub { font-size: 12px; fill: var(--muted); }
7
+ .node { fill: var(--card); stroke: var(--line); stroke-width: 1.5; }
8
+ .edge { stroke: var(--line); stroke-width: 1.5; fill: none; }
9
+ .back { stroke: var(--line); stroke-width: 1.5; fill: none; stroke-dasharray: 5 5; }
10
+ .head { fill: var(--line); }
11
+ .dot { fill: var(--accent); }
12
+ .score { font-size: 12px; font-variant-numeric: tabular-nums; fill: var(--fg); }
13
+ .ok { fill: var(--good); font-weight: 600; }
14
+ .pr { fill: var(--accent); font-weight: 600; font-size: 12px; }
15
+
16
+ .n1 { animation: lit1 12s infinite; } .n2 { animation: lit2 12s infinite; }
17
+ .n3 { animation: lit3 12s infinite; } .n4 { animation: lit4 12s infinite; }
18
+ .n5 { animation: lit5 12s infinite; }
19
+
20
+ @keyframes lit1 { 0%, 11% { stroke: var(--accent); stroke-width: 2.5; } 13%, 100% { stroke: var(--line); stroke-width: 1.5; } }
21
+ @keyframes lit2 { 0%, 15% { stroke: var(--line); stroke-width: 1.5; } 16%, 31% { stroke: var(--accent); stroke-width: 2.5; } 33%, 100% { stroke: var(--line); stroke-width: 1.5; } }
22
+ @keyframes lit3 { 0%, 35% { stroke: var(--line); stroke-width: 1.5; } 36%, 53% { stroke: var(--accent); stroke-width: 2.5; } 55%, 100% { stroke: var(--line); stroke-width: 1.5; } }
23
+ @keyframes lit4 { 0%, 57% { stroke: var(--line); stroke-width: 1.5; } 58%, 71% { stroke: var(--accent); stroke-width: 2.5; } 73%, 100% { stroke: var(--line); stroke-width: 1.5; } }
24
+ @keyframes lit5 { 0%, 75% { stroke: var(--line); stroke-width: 1.5; } 76%, 96% { stroke: var(--accent); stroke-width: 2.5; } 98%, 100% { stroke: var(--line); stroke-width: 1.5; } }
25
+
26
+ @media (prefers-reduced-motion: reduce) {
27
+ .n1, .n2, .n3, .n4, .n5 { animation: none; }
28
+ }
29
+ </style>
30
+
31
+ <!-- edges between the five steps -->
32
+ <g>
33
+ <line class="edge" x1="172" y1="68" x2="186" y2="68"/><polygon class="head" points="192,68 184,64 184,72"/>
34
+ <line class="edge" x1="344" y1="68" x2="358" y2="68"/><polygon class="head" points="364,68 356,64 356,72"/>
35
+ <line class="edge" x1="516" y1="68" x2="530" y2="68"/><polygon class="head" points="536,68 528,64 528,72"/>
36
+ <line class="edge" x1="688" y1="68" x2="702" y2="68"/><polygon class="head" points="708,68 700,64 700,72"/>
37
+ </g>
38
+
39
+ <!-- the steps -->
40
+ <g>
41
+ <rect class="node n1" x="20" y="36" width="152" height="64" rx="10"/>
42
+ <text class="title" x="96" y="63" text-anchor="middle">Propose</text>
43
+ <text class="sub" x="96" y="83" text-anchor="middle">agent edits the solver</text>
44
+
45
+ <rect class="node n2" x="192" y="36" width="152" height="64" rx="10"/>
46
+ <text class="title" x="268" y="63" text-anchor="middle">Experiment</text>
47
+ <text class="sub" x="268" y="83" text-anchor="middle">jobs on your GPUs</text>
48
+
49
+ <rect class="node n3" x="364" y="36" width="152" height="64" rx="10"/>
50
+ <text class="title" x="440" y="63" text-anchor="middle">Measure</text>
51
+ <text class="sub" x="440" y="83" text-anchor="middle">Outerloop re-measures</text>
52
+
53
+ <rect class="node n4" x="536" y="36" width="152" height="64" rx="10"/>
54
+ <text class="title" x="612" y="63" text-anchor="middle">Review</text>
55
+ <text class="sub" x="612" y="83" text-anchor="middle">judges read the claim</text>
56
+
57
+ <rect class="node n5" x="708" y="36" width="152" height="64" rx="10"/>
58
+ <text class="title" x="784" y="63" text-anchor="middle">Pull request</text>
59
+ <text class="sub" x="784" y="83" text-anchor="middle">with a written report</text>
60
+ </g>
61
+
62
+ <!-- the attempt moving through -->
63
+ <circle class="dot" cx="96" cy="22" r="6">
64
+ <animateTransform attributeName="transform" type="translate" dur="12s" repeatCount="indefinite"
65
+ values="0 0; 0 0; 172 0; 172 0; 344 0; 344 0; 516 0; 516 0; 688 0; 688 0"
66
+ keyTimes="0; 0.10; 0.16; 0.30; 0.36; 0.52; 0.58; 0.70; 0.76; 1"/>
67
+ </circle>
68
+
69
+ <!-- what the measure step reports -->
70
+ <g class="readout" opacity="0">
71
+ <animate attributeName="opacity" dur="12s" repeatCount="indefinite"
72
+ values="0; 0; 1; 1; 0" keyTimes="0; 0.39; 0.42; 0.94; 1"/>
73
+ <text class="score" x="440" y="124" text-anchor="middle">base 9,472 steps → candidate 7,808</text>
74
+ <text class="score ok" x="440" y="142" text-anchor="middle">more than run-to-run noise</text>
75
+ </g>
76
+ <g class="badge" opacity="0">
77
+ <animate attributeName="opacity" dur="12s" repeatCount="indefinite"
78
+ values="0; 0; 1; 1; 0" keyTimes="0; 0.78; 0.81; 0.94; 1"/>
79
+ <text class="pr" x="784" y="124" text-anchor="middle">opened for a human to merge</text>
80
+ </g>
81
+
82
+ <!-- no gain: report and try again -->
83
+ <path class="back" d="M 440 154 L 440 176 L 96 176 L 96 108"/>
84
+ <polygon class="head" points="96,102 92,110 100,110"/>
85
+ <text class="sub" x="268" y="196" text-anchor="middle">no real gain: the report says why, and the next hypothesis starts</text>
86
+ </svg>
@@ -0,0 +1,67 @@
1
+ # Run trajectory
2
+
3
+ The kernel alone writes `runs/<run-id>/trajectory.jsonl`, outside the session's
4
+ workspace and writable sandbox mounts. Operational state, launch ledgers,
5
+ inbox/outbox and session records continue unchanged. The trajectory is a
6
+ best-effort timeline, not a replacement for those recovery records.
7
+
8
+ Each line is a JSON object with sorted keys: `seq` (starting at zero), `ts`
9
+ (Unix seconds), `type`, `actor`, `data`, `refs`, and `prev`. `prev` is SHA-256
10
+ of the previous event's exact bytes excluding its newline; the first event
11
+ uses SHA-256 of the UTF-8 run id. Actors use `kernel`, `author:<agent-id>`,
12
+ `judge:<name>`, or `human:<login>`; these kernel actions use `kernel`.
13
+ Refs contain run-relative `path` and `sha256` fields. Native session logs are
14
+ hashed once at session end, and the report is hashed at ending.
15
+
16
+ Events cover session start/end, syscall acceptance/refusal, launch submission
17
+ and results, gate results, acknowledged inbox delivery, wakes, PR opening
18
+ (including reconciliation of an already opened PR), and endings. Inbox events
19
+ contain sequence ranges, kinds and origins, never message bodies. Session start
20
+ may have an empty id until the backend returns one. Exceptions produce a session
21
+ end outcome when the host can catch them; process death can leave an unmatched
22
+ start. Measurement credit describes the gate decision, not publication or merge.
23
+
24
+ A sibling `trajectory.lock` serializes appenders with exclusive `flock`. A torn
25
+ last line is preserved and separated by a newline; the chain continues from
26
+ the last parseable event. Verification reports malformed lines even after
27
+ recovery. A complete JSON event missing only its newline is retained and its
28
+ newline repaired on the next append. No old bytes are rewritten. Writes stop
29
+ at 4 MB and warn once per file per kernel process; I/O failures warn without
30
+ failing a tick. Events are not transactional with the operational files, so a
31
+ crash or write failure can leave missing events or retries.
32
+
33
+ `outerloop trajectory <run-id> --verify` checks sequence gaps and byte hashes;
34
+ `--json` emits events (and verification problems when requested). Missing legacy
35
+ files read as an empty history and verification says "no trajectory". No history
36
+ is reconstructed from older operational files. Older kernels ignore this file.
37
+ The chain detects internal edits and missing lines, but without a trusted
38
+ external anchor cannot detect a rewritten chain or removal of its tail.
39
+ Verification checks the event chain, not the contents of referenced artifacts.
40
+
41
+ `measure` events carry provenance. They record the ledger's ruler digest (the
42
+ measurement signature plus the non-solver tree entries, the value a pending
43
+ submission also computes), the container image digest when one is configured,
44
+ and refs to the evaluation result files the kernel read under the run
45
+ directory. Self-reported measures record the ruler alone: they have no
46
+ evaluation image or result files. A pending submission does not yet name the
47
+ trajectory head: kernels before this one reject unknown pending fields, so the
48
+ pending reader now ignores them, and a later release can add the field without
49
+ breaking this one. Session end also appends one `tool.call` event per tool call
50
+ found in the retained, redacted native log: Claude project transcripts and Codex
51
+ rollout JSONL are parsed host-side, bounded to 500 calls per session, and Hermes
52
+ or unknown formats yield nothing. Each event carries the tool name, repo-relative
53
+ files, a bounded shell command and, when the log has them, the exit code and
54
+ outcome — never file contents or tool output. `outerloop trajectory` counts them
55
+ by tool and the PR body summarizes the top tools. The PR body also runs a
56
+ report-only number check: numbers on report lines that mention the metric,
57
+ baseline or candidate are compared with every `measure` event's values and the
58
+ Experiments result cells, after rounding to the report's decimals, and the
59
+ unmatched ones are listed under "## Number check". Nothing is blocked and no
60
+ event is written for it. At the end of a run the kernel appends one `attest`
61
+ event carrying an HMAC-SHA256 over the run id and the chain head. Its 32-byte
62
+ key is created owner-only beside the operator settings file
63
+ (`trajectory_attest.key`). `--verify` reports `attested` when the last attest
64
+ event matches that key and `attestation mismatch` otherwise, which shows the
65
+ deployment's own chain was not altered after ending but is not a public proof:
66
+ anyone holding the key can recompute it, and a missing event or key proves
67
+ nothing.
@@ -1,16 +1,46 @@
1
1
  # Getting started
2
2
 
3
- This page walks you through one Outerloop run end to end: install the package, declare a contract in your repository, run guided setup, and watch the loop make its first climb. You need one machine, a GitHub repository you administer, and Anthropic API keys for the author and the review judges.
3
+ This page walks you through one Outerloop run end to end: install the package, declare a contract in your repository, run guided setup, and watch the loop make its first climb. You need one machine, a GitHub repository you administer, and separate Anthropic credentials for the author and the review judges: API keys, or subscription tokens from `claude setup-token`.
4
4
 
5
5
  The tutorial uses the simplest supported setup: everything runs on your machine. [Install](install.md) covers Slurm clusters, upgrades and the other deployment shapes.
6
6
 
7
+ To watch a run before you write a contract, the [quickstart repository](https://github.com/outerloop-science/quickstart) has two CPU benchmarks and runs without GitHub or a cluster; its README has the three commands.
8
+
9
+ ## Use from your coding agent
10
+
11
+ Install the skills-only Outerloop plugin in Claude Code:
12
+
13
+ ```text
14
+ /plugin marketplace add outerloop-science/outerloop
15
+ /plugin install outerloop@outerloop
16
+ ```
17
+
18
+ Or in Codex:
19
+
20
+ ```bash
21
+ codex plugin marketplace add outerloop-science/outerloop
22
+ codex plugin add outerloop@outerloop
23
+ ```
24
+
25
+ Both use the repository's `.claude-plugin/marketplace.json` and the same skills;
26
+ Codex also supports this marketplace layout. Open a new agent session in your
27
+ target repository and say "use outerloop on this repo". The setup skill installs
28
+ `outerloop-science`, helps validate a contract, and offers local forge with local
29
+ or Slurm compute. On a laptop, the plugin runs CPU benchmarks locally without
30
+ needing GitHub or a cluster. For GPU benchmarks, SSH to the cluster's login node
31
+ and run your coding agent there; the resident loop submits all work to Slurm.
32
+ Model access is still needed;
33
+ enter secrets only in the CLI's private prompts. Setup does not start the loop.
34
+ Ask separately to start, inspect, message or stop a run. The plugin drives the
35
+ CLI; budgets, measurement and forbidden paths remain enforced by the kernel.
36
+
7
37
  ## Before you start
8
38
 
9
39
  - Python 3.12 or later on a Linux machine, or any machine you can leave a terminal open on.
10
40
  - `uv` on PATH. Install it with `curl -LsSf https://astral.sh/uv/install.sh | sh` and check `uv --version`. Every evaluation runs through `uv run`, and `outerloop start` refuses to launch without it.
11
41
  - A GitHub repository you administer, so you can install a GitHub App on it and merge pull requests.
12
42
  - A benchmark command in that repository that prints a JSON metric, such as `{"success_rate": 0.42}`.
13
- - An Anthropic API key for the author model, and a second key for the review judges. Spend-capped keys are recommended.
43
+ - An Anthropic credential for the author model and a separate one for the review judges: an API key, or a subscription token from `claude setup-token`. Spend-capped keys are recommended.
14
44
 
15
45
  The loop opens pull requests, comments and pushes branches as a GitHub identity that is not yours. On the way you create that identity with `outerloop init`.
16
46
 
@@ -89,7 +119,7 @@ The wizard asks for the deployment settings and writes `~/.config/outerloop/.env
89
119
  - Enter for the author backend, keeping `claude`.
90
120
  - Enter for the author model to take the backend's default, or name one.
91
121
  - The model ID your key serves for the Claude model.
92
- - Your Anthropic key when it asks for the author's key. The prompt hides it.
122
+ - Your Anthropic API key or `claude setup-token` token when it asks for the author's key. The prompt hides it.
93
123
  - Enter for `app` at the auth prompt.
94
124
 
95
125
  On Linux with [Apptainer](https://apptainer.org) installed, init also offers to download the agent image, about 200 MB, so sessions run contained. Accept unless you have a reason not to. Without an image, on macOS for example, runs stay uncontained and the verification panel is off, and a pull request opened without a panel says so.
@@ -6,6 +6,9 @@ request when the kernel's own measurement shows the benchmark improved. Every
6
6
  attempt ends in a written report, including the ones that failed. You run
7
7
  Outerloop yourself, with your own keys, compute and repositories.
8
8
 
9
+ ![One attempt: an agent proposes a change, runs the experiment on your GPUs, the kernel re-measures it against the base, judges review the claim, and a pull request opens with a written report.](assets/loop.svg#only-light)
10
+ ![One attempt: an agent proposes a change, runs the experiment on your GPUs, the kernel re-measures it against the base, judges review the claim, and a pull request opens with a written report.](assets/loop-dark.svg#only-dark)
11
+
9
12
  - [Getting started](getting-started.md) takes one repository through a first
10
13
  run on a single machine.
11
14
  - [Guides](install.md) cover installing Outerloop, operating runs, writing a
@@ -9,8 +9,8 @@ Outerloop is self-hosted. You run it, with your keys, on your compute, against y
9
9
  - Python 3.12 or later.
10
10
  - `uv` on PATH, or in the directory its installer uses. Evaluations and launches run through `uv run`, and `outerloop start` stops with a message when it cannot find it.
11
11
  - A GitHub repository you administer.
12
- - An API key for the author's model, and separate keys for the judges.
13
- - For contained runs, Linux with [Apptainer](https://apptainer.org). macOS has no Apptainer, so runs there stay uncontained.
12
+ - An API key for the author's model, or for Claude a subscription token from `claude setup-token`, and separate credentials for the judges.
13
+ - For contained runs, Linux with [Apptainer](https://apptainer.org). macOS uses Seatbelt through `/usr/bin/sandbox-exec` without a VM.
14
14
 
15
15
  The orchestrator is CPU-only and makes outbound connections only. Anywhere that can reach GitHub and your model provider works.
16
16
 
@@ -46,7 +46,7 @@ With `OUTERLOOP_COMPUTE=local`, or with no `sbatch` on PATH, `outerloop start` r
46
46
 
47
47
  Local compute has no lanes, so GPU benchmarks need no GPU partition: evaluations and the author's launches run on the machine's own GPUs, shared first come first served. Launch arrays run their tasks in parallel as GPUs become available, and `OUTERLOOP_LOCAL_GPUS` overrides automatic detection through `nvidia-smi` (`0` disables GPU allocation). The pool also follows the loop's own `CUDA_VISIBLE_DEVICES`: start each deployment with its own list (for example `0,1` and `2,3`) to give it its own GPUs. The author's session itself starts with `CUDA_VISIBLE_DEVICES` empty, so an experiment that needs a GPU goes through the author's `launch` syscall, where it is recorded and metered against `gpu_hours_per_run`. Uncontained, that is a default the session could reset; containment is what keeps the GPU out of the session, which is one more reason to run with the image.
48
48
 
49
- Without a container image, sessions run under the harness's own sandbox and evaluations run bare on your machine with your keys, and the loop says so once at start. The verification panel is off in this mode unless you set `OUTERLOOP_PANEL_UNCONTAINED=1`, because an uncontained judge holds a shell next to its own key file. A pull request opened without a panel says so. A Codex or Hermes author needs the image in every mode. For priorities, walltime accounting, or several machines, use Slurm.
49
+ Without a container image, sessions and evaluations run as your account on your machine, and the loop says so once at start. On macOS a Seatbelt profile (`/usr/bin/sandbox-exec`) confines them: they write only the workspace and a scratch directory, read only system and toolchain paths, and connect out only to TCP port 443, DNS and local ports (a local model endpoint). If `sandbox-exec` is missing, the loop warns and runs them uncontained. The profile does not open the keychain, but a subscription token from `claude setup-token` works as a Claude credential because it travels as an environment variable. It shares your plan's usage limits with your own Claude use and cannot be spend-capped. A judge on a token runs with `--safe-mode` instead of `--bare`. On Linux they get a scrubbed environment and their own home but can read any file your account can, keys included. Use a spend-capped key. The verification panel is off in this mode unless you set `OUTERLOOP_PANEL_UNCONTAINED=1`, because an uncontained judge holds a shell next to its own key file. A pull request opened without a panel says so. A Codex or Hermes author needs the image in every mode. For priorities, walltime accounting, or several machines, use Slurm.
50
50
 
51
51
  ### One machine as its own Slurm cluster
52
52
 
@@ -208,13 +208,13 @@ GPU jobs request at least 8 cores and 64 GB of host memory per GPU. On nodes wit
208
208
 
209
209
  ## The agent image
210
210
 
211
- On a cluster, evals and launches run inside the Apptainer image at `OUTERLOOP_IMAGE` (default `~/outerloop-images/agent-py312.sif`), in a jail that binds only the checked-out tree. An eval that needs data must fetch it into the tree. The published image lives at [huggingface.co/outerloop-science/agent-image](https://huggingface.co/outerloop-science/agent-image), built from `containers/agent-py312.def`. On Linux with Apptainer installed, `outerloop init` downloads it (about 200 MB), verifies its published checksum and records it. `--image` points at your own, and `--no-image` keeps runs uncontained even when an image is on disk by writing `OUTERLOOP_IMAGE=`.
211
+ On a cluster, evals and launches run inside the Apptainer image at `OUTERLOOP_IMAGE` (default `~/outerloop-images/agent-py312.sif`), in a jail that binds only the checked-out tree. An eval that needs data must fetch it into the tree. The published image lives at [huggingface.co/outerloop-science/agent-image](https://huggingface.co/outerloop-science/agent-image), built from `containers/agent-py312.def`. On Linux with Apptainer installed, `outerloop init` downloads it (about 200 MB), verifies its published checksum and records it. `--image` points at your own, and `--no-image` skips Apptainer even when an image is on disk (macOS still uses Seatbelt when available) by writing `OUTERLOOP_IMAGE=`.
212
212
 
213
213
  Only the local loop falls back to running uncontained on its own. On Slurm the tick does not service a target without an image, and its log names the setting below, so a lost image never silently drops containment. Codex and Hermes authors need the image in every mode.
214
214
 
215
215
  ### Running a cluster without a container
216
216
 
217
- A Slurm cluster with no container runtime sets `OUTERLOOP_UNCONTAINED=1` in `~/.config/outerloop/.env`. Sessions and evaluations then run as local mode does without an image, but as Slurm jobs on your account. Sessions run under the harness's own sandbox. Evaluations run bare, in a throwaway tree with an allowlisted environment. The image is ignored even when one is on disk.
217
+ A Slurm cluster with no container runtime sets `OUTERLOOP_UNCONTAINED=1` in `~/.config/outerloop/.env`. Sessions and evaluations then run as local mode does without an image, but as Slurm jobs on your account. Sessions get a scrubbed environment and their own home directory but can read any file the account can. Evaluations run bare, in a throwaway tree with an allowlisted environment. The image is ignored even when one is on disk.
218
218
 
219
219
  The local-mode rules carry over. The panel is off unless you set `OUTERLOOP_PANEL_UNCONTAINED=1`, and `start` refuses a Codex or Hermes author. A session can read any file your account can: the bot's credential, the model key files, the evaluator's tree and the kernel's state. Key secrecy and measurement integrity both weaken: the benchmark command can write its own eval directory, and on a multi-node benchmark any node can write every node's result files, so a measurement is only as trustworthy as the author whose code ran. Use it where the site already confines processes, or where that exposure is acceptable to you.
220
220