outerloop end: an operator can end one run at the next tick - #449
Conversation
The command records a request in the run directory; the tick ends the run through the same path as a PR merge or close, as the new operator ending. It works for parked runs whose endpoint is gone and for runs without a PR.
There was a problem hiding this comment.
Round 1 — reviewed head 94b50c89 — reviewer summarizer:hermes/gpt-5.6-terra over coverage+credentials+deployment+general+lifecycle+prose.
terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.
Verdict: 2 blocking, 0 advisory.
1 finding attached to the lines below.
An in-flight session can publish after an operator ending. [coverage+credentials+general+lifecycle] publish reads the run record only once before its GitHub work. The tick can save an operator ending after that check (the terminal record is not written until finish_run), while the already-running publish can still save its journal, push, and create or update a PR; its later stale record write is rejected but cannot undo those external effects. (src/outerloop/attempt.py:3751; high confidence)
Blocking: an operator ending does not fence an already-running publish, so it can still create/update a PR or push; all of coverage, credentials, general, and lifecycle identify this race. Also, ending an issue-backed run without a GitHub client can strand its issue claim. Rejected: none; the overlapping publish-race opinions were merged because they make the same finding with complementary timing evidence.
A live session finishes its leg with its publish refused, a queued wake exits without a leg, an issue-backed run waits for GitHub without being woken, and a requested run whose session dies ends as operator.
There was a problem hiding this comment.
Round 1 — reviewed head e3880614 — reviewer hermes/gpt-5.6-terra.
terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.
Verdict: 2 blocking, 0 advisory.
1 finding attached to the lines below.
Dead requested runs get an aborted report. After recording OPERATOR for a requested run, this branch writes an absent report with the hard-coded aborted outcome and kill note, so the report disagrees with the saved operator ending and request note. (src/outerloop/tick.py:1608; high confidence)
Dead operator-requested sessions miss operator cleanup and write the wrong report.
There was a problem hiding this comment.
Round 2 — reviewed head bebb4b8d — reviewer hermes/gpt-5.6-terra.
terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.
Verdict: 1 blocking, 0 advisory.
1 finding attached to the lines below.
A dead requested issue run can be ended without GitHub, leaving its claim unreleased.
There was a problem hiding this comment.
Round 3 — reviewed head d66bbae2 — reviewer hermes/gpt-5.6-terra.
terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.
Verdict: 1 blocking, 0 advisory.
Operator requests lose to a non-improving live session. When a requested live session returns any non-improved outcome without a PR, _finish_attempt calls finish_run with the outcome ending, so the later sweep sees an ended record and never records the requested operator ending or its note. (src/outerloop/attempt.py:3635; high confidence)
A live session can complete with its own ending after an operator request, so the requested operator ending is not recorded.
…ing operator request
There was a problem hiding this comment.
Round 4 — reviewed head f3d8c8ec — reviewer hermes/gpt-5.6-terra.
terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.
Verdict: nothing blocking — 1 advisory note.
1 finding attached to the lines below.
Malformed JSON request notes can be recorded as text instead of an empty note.
# Conflicts: # CHANGELOG.md # src/outerloop/attempt.py # tests/test_attempt.py
There was a problem hiding this comment.
Round 5 — reviewed head 3012d66c — reviewer hermes/gpt-5.6-terra.
terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.
Verdict: 2 blocking, 0 advisory.
2 findings attached to the lines below.
End requests are not synchronized with in-flight publication or queued wake startup.
…hat a publish already under way completes
There was a problem hiding this comment.
Round 6 — reviewed head 678951e2 — reviewer hermes/gpt-5.6-terra.
terra
Advisory findings from outerloop — the code owner decides. Reply to disagree; the outerloop:no-review label opts this PR out.
Verdict: 1 blocking, 0 advisory.
1 finding attached to the lines below.
An operator request can end a fresh active session before its leg finishes.
…ase and finishes its leg
An operator had no way to end a single run. A run ends when its author ends it, its PR is merged or closed, its meter runs out, or the kernel cannot continue. A run without a PR that is stuck, for example one bound to a model server that has been retired and waiting for its address, could only wait.
outerloop end <run-id> [--root <root>] [--note <text>]:end-request.jsonin the run directory (requested time and note) and says what will happen. Unknown ids and ended runs are refused; a repeated request keeps the first one.close_if_done→finish_run): report, line notebook seal, live launches cancelled. A session in flight finishes its leg and its publish is refused. It works for parked runs whose endpoint is unavailable and for runs with no PR or issue.operator, with the note as the ending note. On an issue-claimed run without a PR, the ending posts the claim-release marker, as any other ending without a PR does; with a PR, the claim stays with the PR.OUTERLOOP_AUTHOR_OVERRIDES.Compatibility: state read across versions gains
end-request.jsonin a run directory and the ending valueoperatorin run records. Records and run directories without them are unchanged (legacy fixture:test_legacy_record_without_request_is_unchanged); no backfill. An older kernel ignores requests. An older kernel loads anoperator-ended record, but its save validation does not know the value, so rolling back while operator-ended runs still have unshed workspaces can log an error when it sheds them; keep the updated kernel once the command is used. TheUpgrading:line is in the CHANGELOG.Gate: pytest (2781 passed), ruff check, ruff format --check, mypy.