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.
- outerloop_science-0.6.0/.claude-plugin/marketplace.json +14 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/CHANGELOG.md +40 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/CITATION.cff +2 -2
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/CONTRIBUTING.md +13 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/PKG-INFO +30 -6
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/README.md +29 -5
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/RELEASING.md +7 -4
- outerloop_science-0.6.0/docs/assets/loop-dark.svg +86 -0
- outerloop_science-0.6.0/docs/assets/loop.svg +86 -0
- outerloop_science-0.6.0/docs/design/trajectory.md +67 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/getting-started.md +33 -3
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/index.md +3 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/install.md +5 -5
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/cli.md +9 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/environment.md +22 -4
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/session-capture.md +5 -3
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/storage.md +1 -1
- outerloop_science-0.6.0/plugins/outerloop/.claude-plugin/plugin.json +10 -0
- outerloop_science-0.6.0/plugins/outerloop/.codex-plugin/plugin.json +19 -0
- outerloop_science-0.6.0/plugins/outerloop/skills/contract/SKILL.md +41 -0
- outerloop_science-0.6.0/plugins/outerloop/skills/operate/SKILL.md +52 -0
- outerloop_science-0.6.0/plugins/outerloop/skills/setup/SKILL.md +82 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/pyproject.toml +1 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/tick_deploy.sh +2 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/__init__.py +1 -1
- outerloop_science-0.6.0/src/outerloop/agent_text.py +343 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/attempt.py +212 -28
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/brief.py +3 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/cli.py +73 -0
- outerloop_science-0.6.0/src/outerloop/containment/__init__.py +135 -0
- outerloop_science-0.6.0/src/outerloop/containment/macos.sb +18 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/dispatch.py +9 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/github.py +22 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/guidance_pack.py +3 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/harness.py +88 -12
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/hermes_capture.py +20 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/image.py +2 -2
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/inbox.py +30 -13
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/init.py +9 -5
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/launchlog.py +24 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/ledger_events.py +20 -10
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/measure.py +54 -0
- outerloop_science-0.6.0/src/outerloop/number_check.py +86 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/orchestrator.py +235 -14
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/posting.py +24 -4
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/progress.py +9 -2
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review.py +10 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review_agent.py +61 -6
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review_post_cli.py +26 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review_summarize_cli.py +26 -3
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/role_runner.py +7 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/session_evidence.py +36 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/status.py +11 -2
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/steward.py +13 -10
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/syscall.py +13 -14
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/syscall_cli.py +10 -7
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/tick.py +12 -5
- outerloop_science-0.6.0/src/outerloop/tool_calls.py +296 -0
- outerloop_science-0.6.0/src/outerloop/trajectory.py +332 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/verifier.py +14 -3
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/conftest.py +20 -0
- outerloop_science-0.6.0/tests/test_agent_plugin.py +31 -0
- outerloop_science-0.6.0/tests/test_agent_text.py +177 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_app_permissions.py +27 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_attempt.py +412 -535
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_attempt_review.py +90 -104
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_author_overrides.py +5 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_brief.py +3 -2
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_climbboard.py +47 -80
- outerloop_science-0.6.0/tests/test_containment.py +180 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_contract_switches.py +16 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_endpoints.py +51 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_github.py +65 -106
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_harness.py +154 -178
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_inbox.py +50 -89
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_init.py +194 -302
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_launchlog.py +8 -3
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_local_compute.py +88 -164
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_measure.py +21 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_messages.py +8 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_orchestrator.py +225 -127
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_posting.py +56 -2
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_progress.py +9 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_agent.py +103 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_summarize.py +24 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_session_evidence.py +78 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_start.py +168 -199
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_status.py +36 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_syscall.py +65 -86
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_tick.py +368 -597
- outerloop_science-0.6.0/tests/test_tool_calls.py +281 -0
- outerloop_science-0.6.0/tests/test_trajectory.py +222 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_verifier.py +16 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_version.py +1 -1
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/.gitignore +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/.pre-commit-config.yaml +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/.python-version +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/CLAUDE.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/LICENSE +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/NOTICE +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/SECURITY.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/containers/README.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/containers/agent-py312.def +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/icon-dark.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/icon-light.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/icon.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/outerloop-apple-touch-icon.png +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/outerloop-favicon.png +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/assets/outerloop-favicon.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/community.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/concurrency.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/contract.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/accelerators.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/agent-environment.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/agent-protocols.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/agent-substrate.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/architecture.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/base-reintegration.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/compute-cloud.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/consolidation.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/dispatcher.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/eval-cache.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/external.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/forge.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/github-app-auth.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/headline.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/judge-placement.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/lifecycle.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/meta.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/model-gateway.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/multi-cluster.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/onboarding.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/orchestrator-verify.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/personal-token.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/public-surface.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/research-lines.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/research-loop-buildout.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/research-loop.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/resident-tick.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/review-placement.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/reviewer-infra.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/role-cli.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/roles.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/scaling.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/design/session-watcher.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/endpoints.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/how-it-works.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/javascripts/figures.js +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/messages.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/operate.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/contract.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/inbox.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reference/syscalls.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/reviewer.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/roadmap.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/stylesheets/figures.css +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/stylesheets/site.css +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/docs/validation/author-syscalls.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/examples/review.yml +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/mkdocs.yml +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/bi/circle-half.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/bi/moon.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/bi/search.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/bi/sun.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/.icons/lab/github.svg +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/main.html +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/overrides/partials/logo.html +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/README.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/docs/build.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/docs/hooks.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/install_bridge.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/install_claude.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/install_codex.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/install_hermes.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/requeue_moved_successors.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/setup_branch_protection.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/sweep_git_locks.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/tick_chain.sbatch +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/scripts/tick_resident.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/__main__.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/appauth.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/appmanifest.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/author_overrides.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/bridge_install.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/bridge_runtime/pyproject.toml +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/bridge_runtime/uv.lock +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/climbboard.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/codex_bridge.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/codex_requirements.toml +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/compute.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/contract.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/contract_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/disk.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/endpoint_wait.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/endpoints.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/evalcache.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/forge.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/forge_local.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/gpu_lanes.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/harness_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/harness_pins.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/harnesses.toml +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/hermes_install.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/housekeeping.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/hypothesis.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/instance.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/intake.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/job_names.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/ledger_branch.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/ledger_migrate.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/limits.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/maintain.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/maintain_agent_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/maintain_post_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/markers.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/operator_limits.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/panel.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/paths.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/provenance.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/py.typed +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/rebind.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/review_agent_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/roles.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/rolespec.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/runstate.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/style.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/verify_agent.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/verify_agent_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/verify_post_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/src/outerloop/watcher.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fakes.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/README.md +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/author_route_legacy.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/author_route_missing_model.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/author_route_missing_route.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/base_moved_cc4e5d7.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/climb-status-legacy.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/default_contract_head.json +0 -0
- {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
- {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
- {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
- {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
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/stdout +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/completed/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/identity.json +0 -0
- {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
- {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
- {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
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/dispatched_pre_gpu_identity/inflight/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/hermes_resume_legacy.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/hermes_sample_20260924.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/launches-before-provenance.jsonl +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/panel_wake_9ca3d7c.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/pre_contract_switches.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/baselines/main@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/brief.txt +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/end-request.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/ended.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-provenance/command.txt +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-provenance/job.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/command.txt +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/exit-code +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/job.sh +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/stdout +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/eval-run/eval-candidate-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-f69684777d523fe6502ee4ed4d09f98410a56aec/submitted +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/generate.py.txt +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/identity.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/launches.jsonl +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/line.bundle +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/merged.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/open.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/pending/owner__repo.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/pending/owner__repo@agent-01.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/pr-states.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/queue.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/rebind.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/rc1_v021/sessionless.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/session_cost_legacy.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/session_index_legacy.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/fixtures/suite_measurement_legacy.json +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/helpers.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/ledger_fake.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_appauth.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_appmanifest.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_bot_aliases.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_bot_login.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_channel_dir.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_codex_bridge.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_codex_harness.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_codex_hooks.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_codex_host.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_compute.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_contract.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_contract_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_contract_names.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_default_claude_model.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_disk.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_dispatch.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_endpoint_wait.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_evalcache.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_forge.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_forge_local.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_forge_local_e2e.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_forge_local_records.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_gpu_lanes.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_hardening.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_harness_pins.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_hermes_author.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_hermes_harness.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_housekeeping.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_identity.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_image.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_import.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_install_harness.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_instance.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_intake.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_ledger_events.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_ledger_migrate.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_lifecycle_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_limits.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_maintain.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_markers.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_measure_and_decide.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_operator_end.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_operator_limits.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_operator_message.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_packaging.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_panel.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_paths.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_rebind.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_requeue_moved_successors.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_agent_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_hardening.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_review_policy.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_role_runner.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_rolespec.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_runstate.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_steward.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_sweep_git_locks.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_syscall_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_tick_chain_successors.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_tick_resident.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_tiers.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_verify_agent.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_verify_agent_cli.py +0 -0
- {outerloop_science-0.5.0 → outerloop_science-0.6.0}/tests/test_watcher.py +0 -0
- {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
|
|
@@ -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.
|
|
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
|
-
|
|
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.
|
|
64
|
-
|
|
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
|
|
132
|
-
|
|
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
|
-
|
|
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.
|
|
40
|
-
|
|
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
|
|
108
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
+

|
|
10
|
+

|
|
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
|
|
13
|
-
- For contained runs, Linux with [Apptainer](https://apptainer.org). macOS
|
|
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
|
|
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`
|
|
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
|
|
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
|
|