Skip to content

fix: unattended-run fixes from the #768 run - #107

Merged
abedegno merged 7 commits into
mainfrom
fix/unattended-run
Sep 26, 2026
Merged

abedegno merged 7 commits into
mainfrom
fix/unattended-run

Conversation

@abedegno

Copy link
Copy Markdown
Owner

What and why

Fixes from the #768 run, each one a live incident:

  • Park lookups had no PYTHONPATH. Three direct coordinator.cli calls (parked and park-notice in _park_back_half, parked in run_item's rc 4 branch) never imported from the wave's working directory, so they always read back empty. As a result, every back-half park concluded it had not been recorded and posted no notice, and every parked scorecard row read parked: with no reason. The calls now go through _coordinator. A guard test requires PYTHONPATH on every coordinator.cli call in batch/. The front-half seam harness used to answer the bare call itself, so it passed while production failed; it no longer does.
  • Label gap. A human ruling consumes the park, while the issue keeps bircher:running, so neither the park clause nor the labels found #768 after its approval. The journal sweep now selects every open run short of its PR (front half, shaping, planned). This subsumes the unresolved-dispute clause, which is removed.
  • Superseded pass. OwnershipLost escaped run_loop as a traceback. The runner read rc 1 as a failure and tried to record failed over a run that a newer pass owned. run_loop now returns Exit.SUPERSEDED (6). run_item writes a superseded row, keeps the queue file and records nothing.
  • Seat bound past the seam. Back-half reviewer dispatches counted against the front half's max_seats, so a long repair loop would have been refused its review with no park (#768 reached 42 of 50). The closed-loop spec removes count bounds past the seam (§2), and dispatch now checks the bound only outside BACK_HALF_DERIVING_STATES.
  • Empty waves. Each empty wave probed both providers, opened two dispatch-probe sessions and uploaded three bundles. main now generates the queue after the kernel preflight and exits on an empty queue before any provider work.
  • revise_bundle noise. It was asked on every back-half resume and always refused. It is now skipped there.

The "silent park" from the same run was not a defect. The rc 4 pass came before the approval and correctly re-parked on the standing gate. Its empty reason was the PYTHONPATH bug. The post-approval pass was working, and a hand-run loop superseded it (the case above).

Effect on the merge gate

None. No change to review, CI observation or merge authorization. The seat change affects only dispatch counting past planned. Back-half rounds were always meant to be bounded by the no-progress park, not by count.

Testing

  • bash batch/run-queue.sh --self-test passes (macOS; Linux via CI)
  • New behaviour I depend on has a test case: queue-generation tests for open front-half runs and for cancelled runs, the empty-wave test (the real runner on an empty queue with logging provider stubs), superseded exit tests at both the coordinator and the runner, the back-half seat test, and the PYTHONPATH guard
  • Guards shown red as well as green: removing the sweep clause reds 7 tests, adding cancelled reds its control, the old main order reds the empty-wave test, and every other new test failed before its fix

Whole suite: 2003 passed, 3 skipped. A cross-vendor (codex) review found nothing critical. It raised two pre-existing gaps, listed below.

Anything a reviewer should look at twice

…t-half runs

Three direct `coordinator.cli` calls (two in _park_back_half, one in
run_item's rc=4 branch) ran without PYTHONPATH, never imported from the
wave's cwd, and read back empty: every back-half park concluded it had not
been recorded and posted no notice, and every parked scorecard row lost its
reason. A guard test now requires PYTHONPATH on every coordinator.cli call
in batch/, and the front-half seam harness no longer answers the bare call.

The journal sweep now selects every open run short of its pull request
(front-half, shaping, planned). A human ruling consumes the park while the
issue keeps `bircher:running`, so neither clause found #768 after its
approval. This subsumes the unresolved-dispute clause, which is removed.
OwnershipLost escaped run_loop as a traceback, the runner read rc 1 as a
failure, and recorded a terminal `failed` over a run a newer pass still
owned (#768, a hand-run loop overlapping a wave). run_loop now returns
Exit.SUPERSEDED (6); run_item logs it, writes a `superseded` row, keeps
the queue file, and records nothing.
The kernel admits revise_bundle only in the front half and shaping, so on
every back-half resume it was asked and refused, one noise refusal per wave.
The wave timer fires every 30 minutes, and each empty wave probed both
providers with a model call, opened two dispatch-probe sessions and
uploaded three bundles before learning the queue held nothing. main now
generates the queue (after the kernel preflight, which the sweep and the
generator read) and exits on an empty one before any provider work.
seats_used counted every reviewer dispatch, so a back-half repair loop
would meet the front half's max_seats wall and be refused its review with
no park to say why (#768 reached 42 of 50). The closed-loop spec removes
every count-based bound past the seam (§2) and ends the back half on no
progress (§3); dispatch now checks the bound only outside the back half.
@abedegno
abedegno merged commit 8312f8d into main Sep 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant