1f5bf7e66e
The brief told the agent to ask for a phase change and never carried the asking. The agent asked in prose, no code represented the request, and the session idled until its lease expired. That is what failed run 3. F21. The agent asks with .orchestra/phase-request.json, and seals research.json or plan.json where the phase it is leaving produces one. At a verified turn boundary the worker checks the phase belief, the transition and the artifact, then calls the coordinator with its lease epoch and a derived operation id. AdvanceWorkPhase is unchanged, so a request cannot reach a move the operator surface could not also make. Redelivery is idempotent. F22. A session now records the phase it was launched to run. One that no longer matches its task rotates with reason phase_changed, whether this worker asked for the change or an operator made it. F20. CLIAdapter.prompt sent handoff and rotation prompts without confirming them, which is the failure F20 exists to catch. Fixed at the shared call site. F23 needed no change. Issue comments already become decisions with no submission, through Reconciler.Reconcile at PreLease and at every turn boundary. The earlier finding searched internal/operations alone and was wrong. Tests now cover the boundary it turns on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011xsXyr5J1RACo71YeKG3Pu
43 lines
1.7 KiB
Go
43 lines
1.7 KiB
Go
package human
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// The two concepts must stay separate. Pull-request feedback is a response to
|
|
// a submission, so anything written at or before the submission was already
|
|
// visible when it was made and cannot be a response to it. Steering written
|
|
// earlier is issue input, reconciled through the task's own source, and it
|
|
// stays valid in every phase.
|
|
func TestPullRequestFeedbackIgnoresAnythingNotAfterTheSubmission(t *testing.T) {
|
|
submitted := time.Date(2026, 8, 27, 12, 0, 0, 0, time.UTC)
|
|
state := PullRequestState{
|
|
Comments: []Input{
|
|
{Provider: "gitea", ExternalID: "1", Author: "kami", At: submitted.Add(-time.Hour), Body: "before"},
|
|
{Provider: "gitea", ExternalID: "2", Author: "kami", At: submitted, Body: "at"},
|
|
{Provider: "gitea", ExternalID: "3", Author: "kami", At: submitted.Add(time.Hour), Body: "after"},
|
|
},
|
|
Reviews: []ReviewObservation{
|
|
{Actor: "kami", State: "changes_requested", At: submitted.Add(-time.Minute), Body: "early review"},
|
|
},
|
|
}
|
|
got := state.FeedbackAfter("gitea", submitted, Trust{})
|
|
if len(got) != 1 || got[0].Body != "after" {
|
|
t.Fatalf("feedback = %+v", got)
|
|
}
|
|
}
|
|
|
|
// An untrusted actor's words never move a task, whenever they arrive.
|
|
func TestPullRequestFeedbackAppliesTrust(t *testing.T) {
|
|
submitted := time.Date(2026, 8, 27, 12, 0, 0, 0, time.UTC)
|
|
state := PullRequestState{Comments: []Input{
|
|
{Provider: "gitea", ExternalID: "1", Author: "bot", At: submitted.Add(time.Hour), Body: "merged by automation"},
|
|
{Provider: "gitea", ExternalID: "2", Author: "kami", At: submitted.Add(time.Hour), Body: "change this"},
|
|
}}
|
|
got := state.FeedbackAfter("gitea", submitted, Trust{Ignored: []string{"bot"}})
|
|
if len(got) != 1 || got[0].Author != "kami" {
|
|
t.Fatalf("feedback = %+v", got)
|
|
}
|
|
}
|