Skip to content

feat: add a read-only supervisor that observes and decides (P3) - #9

Open
TKontu wants to merge 2 commits into
autonomous-reconcile-loopfrom
auto/p3-supervisor-skeleton
Open

TKontu wants to merge 2 commits into
autonomous-reconcile-loopfrom
auto/p3-supervisor-skeleton

Conversation

@TKontu

@TKontu TKontu commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

This is phase P3 of the autonomous Reconcile-loop plan. It is stacked on #8. After #7 and #8 merge, retarget this PR to autonomous-reconcile-loop.

scripts/supervise.py is the deterministic loop manager, in a read-only form. Each tick it observes the ledger, Git, PR and check state, and session processes. A pure decide() then returns exactly one action. This version performs no effects: it never launches agents, writes the ledger, pushes, comments or merges.

python3 scripts/supervise.py next            # one decision as JSON; writes nothing
python3 scripts/supervise.py status          # round, assignments, sessions, halts, decision
python3 scripts/supervise.py run --dry-run   # log decisions; poll while waiting

run without --dry-run exits 2. Exit codes:

  • 0: an action is available, waiting, or a routine park
  • 10: attention needed
  • 11: unknown observation
  • 2: configuration error
  • 3: the lock is held by another instance

New modules in scripts/supervisor/:

  • config.py reads .agent/supervisor.toml from REMOTE/BRANCH with git show, never from the working tree.
    • An ignored supervisor.local.toml may override only [runner] and [budgets].
    • Unknown keys and wrong types fail.
    • L2 requires auto_merge and a non-empty required_checks. L3 also requires auto_reconcile and allow_reconcile_merge. A missing grant exits 2 and names it.
    • --autonomy can only lower the level.
  • decide.py is pure and implements the plan's state machine. Live sessions are contained even while a halt is uncleared (kill past deadline, mark lost, ack).
    • The guard order is a deliberate deviation from the plan: the plan parks on a halt before session handling, which would leave a session past its deadline running.
    • Candidates are aggregated as Merge > LaunchReviewer > LaunchExecutor > Cleanup, within max_parallel and the round budget.
    • A hold on one assignment does not block another.
  • policy.py provides path globs, the required-checks state on an exact SHA, and the L2 merge gate. The newest run wins. Missing, skipped, neutral and cancelled checks fail. Pending checks hold after ci_timeout_minutes.
    • The gate also holds binary changes, because --numstat gives them no line count to check against max_changed_lines. The plan doesn't list this.
  • gh.py is a read-only wrapper. Any failure is Unknown, never an empty success.
  • observe.py builds the snapshot:
    • It reads the open round through round.py status, so the read takes the ledger lock.
    • It gets the remote head via ls-remote and does not fetch. If the remote head object is missing locally, the decision is Fetch.
    • A session process counts as alive only if its pid's command line contains the session UUID. Pid and exit files live under .agent-runs/supervisor/sessions/; P4's shim will write them.
  • .agent/supervisor.example.toml is conservative: L1, no merge grants. The Claude output field names in it are still unconfirmed until P4 records a real envelope.
  • SUPERVISION.md gains the preview, configuration, decision table and merge gate sections.

Validation:

  • RED: the policy, config and decide tests failed before each module existed. The CLI tests were written alongside supervise.py, so they did not have a separate failing run. They then caught one real bug: a trailing newline in git diff --name-only became an empty path, which the gate held as "outside-owned".
  • GREEN: python3 -m unittest discover -s tests passed 91 tests: the existing ledger tests plus 50 new ones. These include table-driven decide cases and CLI tests against a real bare origin and clone, with tests/fakes/gh on PATH. The CLI tests assert that only read-only gh calls are made and that the ledger and checkout are unchanged.
  • git diff --check passed, and the staged diff was reviewed for secrets.
  • Smoke test on this repository: next exits 2 because there is no committed supervisor.toml.

Not covered: the shadow run on a real project (rollout step 1) has not been done, and nothing is verified against live GitHub. All gh behavior in the tests comes from the fake.

🤖 Generated with Claude Code

Tuomo Vendelin and others added 2 commits October 10, 2026 22:34
…ction

Phase P3 of the autonomous Reconcile-loop plan. scripts/supervise.py observes the
ledger, Git, PR/check state and session processes, and a pure decide() picks exactly
one next action. It performs no effects: `next` prints the decision, `status`
explains it, and `run --dry-run` logs decisions under a single-instance lock.

- config: committed policy is read from the remote default branch; an ignored local
  file may override runner/budget keys only; autonomy needs committed grants.
- policy: path globs, required checks on an exact SHA, and the L2 merge gate.
- gh: read-only wrapper where any failure is Unknown, never success.
- Table-driven decide tests, plus CLI tests against real Git and a fake gh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TKontu
TKontu changed the base branch from auto/p2-sessions-halts to autonomous-reconcile-loop October 10, 2026 20:38
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