Adopt an already-published PR instead of publishing again
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m12s
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m12s
Fixes counterexample 5 from specs/orchestrator.qnt. A crash after the PR was opened but before PR_OPEN was recorded left the agent in EXECUTING; the restart question's 'continue' and 'start' then published again, Gitea refused a second PR, and there was no way back to PR_OPEN. On the live run the executor model happened to notice the existing work; recovery must not depend on that. - Boot (A3 step 4): in EXECUTING, if Gitea shows the milestone's PR open, adopt it, watch it from the start, and resume in PR_OPEN with a notice. Gitea unreachable or the PR closed: the restart question, as before. - MilestonePublisher.publish is idempotent: after pushing, an open PR for the branch is adopted (and so updated) rather than a second one opened. This also covers a PR opened whose response was lost. - The model adopts too, gains the lost-response outcome, and now counts PRs opened per milestone (one_pr_per_milestone was a flag the fixed model could never set). A mutant without adoption fails the new scenario; the properties held over 200,000 traces each. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
1 parent
6fdec6922b
commit
8bd1ac0fa2
6 files changed
+165
-24
No files matched your search
@@ -1,7 +1,8 @@
|
||||
// Counterexamples found in orchestrator.qnt, written out as short traces.
|
||||
// Each ends by expecting the property it breaks to be false; once the design
|
||||
// is fixed, the expectation is flipped and the trace kept as a regression.
|
||||
// Fixed: 1-4 (replies bind to the revision up when they arrived).
|
||||
// Fixed: 1-4 (replies bind to the revision up when they arrived), 5 (boot
|
||||
// and publishing adopt an open PR).
|
||||
//
|
||||
// Run: quint test specs/orchestrator_scenarios.qnt (runs every *Test)
|
||||
module orchestrator_scenarios {
|
||||
@@ -77,22 +78,32 @@ module orchestrator_scenarios {
|
||||
.expect(s.st == "AWAIT_MILESTONE_SIGNOFF" and feedback_only_on_what_was_seen)
|
||||
|
||||
// 5. A crash after the PR is opened but before PR_OPEN is recorded.
|
||||
// Boot finds the intent's PR and marks it done, but the state still
|
||||
// says EXECUTING, so it asks the restart question. 'continue' returns
|
||||
// to the approved plan, and starting it publishes again: Gitea refuses
|
||||
// a second PR for the branch, and there is no path back to PR_OPEN.
|
||||
// Boot found the intent's PR and marked it done, but the state still
|
||||
// said EXECUTING, so it asked the restart question; 'continue' and
|
||||
// 'start' then published again, which Gitea refused, with no path back
|
||||
// to PR_OPEN. Fixed: boot sees the open PR and adopts it.
|
||||
run crashAfterOpeningThePrPublishesAgainTest =
|
||||
toMilestoneApproved
|
||||
.then(send("start")).then(runJob(true))
|
||||
.then(finishExec("completed"))
|
||||
.then(publishStep("ok")) // begin_intent
|
||||
.then(publishStep("ok")) // push, open the PR
|
||||
.then(crash(true)) // intent verified and committed; state EXECUTING
|
||||
.then(send("continue")).then(runJob(true))
|
||||
.then(crash(true)) // adopted on boot
|
||||
.expect(s.st == "PR_OPEN" and s.prOn and one_pr_per_milestone)
|
||||
|
||||
// 5b. Gitea opens the PR but the response is lost; the operator starts
|
||||
// the milestone again. Publishing adopts the open PR.
|
||||
run lostResponseThenRetryAdoptsThePrTest =
|
||||
toMilestoneApproved
|
||||
.then(send("start")).then(runJob(true))
|
||||
.then(finishExec("completed"))
|
||||
.then(publishStep("ok")).then(publishStep("lost"))
|
||||
.then(send("continue")).then(runJob(true)) // back to the approved plan
|
||||
.then(send("start")).then(runJob(true))
|
||||
.then(finishExec("completed"))
|
||||
.then(publishStep("ok")).then(publishStep("ok"))
|
||||
.expect(s.st == "AWAIT_INPUT" and not(one_pr_per_milestone))
|
||||
.then(publishStep("ok")).then(publishStep("ok"))
|
||||
.expect(s.st == "PR_OPEN" and one_pr_per_milestone)
|
||||
|
||||
// 6. A crash while the executor answers a review comment. The comment's
|
||||
// cursor was saved before the work started (at most once), and a
|
||||
|
||||
Reference in new issue
Block a user