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