Conversation
…ding them When a stage had several credible approaches, the worker picked one and the supervisor accepted or corrected it; the alternatives were never recorded, so the choice between designs was made silently by one agent. A node can now opt in with `variations: N`, from 2 to 3, off by default. Its supervisor may then propose that many alternatives alongside accepting the work, each with a name and a rationale. They are recorded on the review, shown on the dashboard's Checkpoint and carried in `envctl run show --json`. Proposals come with an acceptance, never instead of one, so the attempt state machine is untouched and an opted-in stage runs exactly as before. Recording them is safe for two reasons that are now tested: WorkDigest clears the review, so an approval stays bound to the same work, and an unset `variations` restores a configuration's digest exactly. The existing supervisor contract has six callers, including the connection probe, so it is unchanged; opted-in nodes get a node-aware schema instead. It refuses a lone variation, which is not a choice; variations on a rejection, where the correction is the path forward; repeated names, which a person could not tell apart; and anything from a node that did not opt in. This does not build the variations. Running each as its own approach through QA and choosing one is most of the issue and needs the checkpoint model to hold competing results for the same stages. The design proposes sibling revisions for that, answers the issue's open question about where variations branch, and orders the remaining work into steps that each ship behind the opt-in. The dashboard labels proposals as considered and not built, so nothing implies a comparison that does not exist. Refs #10 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The first slice let a supervisor record alternatives but built none of them, so "which approach is better?" was still one agent's judgement. An accepted stage's proposals now become variations, in the mutation that accepts it so they branch exactly once. The accepted work continues as "as proposed"; each alternative is a sibling revision restored from before that stage, with the alternative given to its worker as steering, running that stage and everything after it through QA in its own worktree and VM. Siblings supersede nothing and leave the current revision alone. None reaches its approved change: ReadyNodes holds those back while a revision is undecided, and publication refuses too, since publishing work nobody chose cannot be undone. `envctl run variations` and `v` in the dashboard show them side by side: rationale, stage summaries, the size of each change from retained evidence, the checks each ran and their results, and token usage. `envctl run choose` and `C` continue one to approval and make it current; the rest become not-chosen, releasing their VMs and keeping their checkpoints, and are never published. Building it turned up three bugs that would each have broken this. limits.vms defaults to 2, but two proposals make three revisions and a finished variation kept its VM while waiting, so the last sibling could never start; a finished variation now parks while a sibling is queued, VMCount stops counting it, and choosing it provisions a VM again. Run.Message wrote to the current revision whatever was addressed, so steering one variation would have landed on another. And eight current-revision checks in the engine and daemon would have left every sibling queued; they now admit undecided variations, the daemon only for message, ask and choose, because a rewind addressed to a variation would otherwise rewind the current revision out from under the comparison. The composer's stale-selection check compared the viewed revision with the current one, which always differ while viewing a variation, so it refused every message sent there. It now compares against the viewed revision, which keeps its protection: a test proves a rewind that lands while typing is still refused. Closes #10 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
sam-bretz
force-pushed
the
issue-10-supervisor-variations
branch
from
September 16, 2026 17:42
4a9a75c to
569afd1
Compare
sam-bretz
added this pull request to stack #46
September 17, 2026 02:59
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #10.
Stacked on #44. This PR is based on
issue-24-chat-decision-log, because #44 removes the Checkpoint tab and this PR's dashboard display lives in the Chat panel that replaces it. Review and merge #44 first, then retarget this PR tomain. Its own changes are the top two commits.What this does
When a stage has several credible approaches, envctl now builds each one and lets you compare them before anything is published, so the choice no longer rests on one agent's judgement.
variations: N(2 or 3, off by default). Its supervisor may propose that many alternatives while accepting the work, each with a name and a rationale.ReadyNodesholds back the approved-change stage while a revision is undecided, and publication refuses too, because publishing work nobody chose can't be undone.envctl run variationsorvin the dashboard shows, for each variation: its rationale, stage summaries, the size of its change (from retained evidence), the checks it ran and their results, and its token usage.--jsongives the structured form. The run summary shows a waiting comparison on every tab.envctl run choose --variation <name>orCcontinues one to approval and makes it current. The others become not chosen: their VMs are released, their checkpoints stay reviewable, and they're never published.Three bugs building it turned up
Each of these would have broken the feature on its own.
1. Deadlock with the default settings.
limits.vmsdefaults to 2, but even two proposals make three revisions, and a finished variation kept its VM while waiting to be chosen. So the last sibling could never start. Now:VMCountdoesn't count a parked variation (it staysactive, so it isn't mistaken for a retired one);The end-to-end test reproduces the deadlock exactly when the
VMCountfix is removed ("never reached: every variation finishes"), and checks on every tick that the run never holds more VMs than its limit.2. Steering landed on the wrong variation.
Run.Messagewrote to the current revision whatever revision was addressed, so a message meant for one variation would silently go to another. It now writes to the addressed revision.3. Eight current-revision checks. The engine and daemon assumed only the current revision can run, which would have left every sibling queued forever. They now also accept undecided variations. The daemon allows this only for
message,askandchoose. I confirmed by mutation that allowingrewindtoo would let a rewind addressed to a variation rewind the current revision out from under the comparison.Two fixes to shared code
TestAMessageIsNotSentToWhateverTheSelectionBecameWhileTypingand confirmed it fails when the guard is removed. A rewind that lands while you're typing is still refused.Run.Usagenow sums a newRevision.Usage, so a run's total and each variation's usage can't disagree.Acceptance criteria
envctl ui(v) and on the command line (run variations --json). Variant data is also inrun show --jsonlimits.run_tokens; the configured maximum bounds how many start, andlimits.vmsbounds how many run at onceTwo deliberate choices:
variations. The supervisor picks which alternatives, never where to branch.Testing
gofmt -l .prints nothing,go vet ./...is clean, and the fullgo test ./...passes after the rebase. Coverage by layer:run show.Not verified against real VMs. Re-provisioning a parked variation and restoring its source from checkpoints goes through the fixture backend. It's the same path rewinds use, but one real comparison before merge is worth running.
🤖 Generated with Claude Code