iop/packages/go/agenttask/manager_test.go
toki 3c48879c44 feat(agent-runtime): 독립 Agent CLI 런타임 기반을 구현한다
독립 호스트에서 안전한 작업 실행과 복구를 제공하기 위해 런타임 설정, 정책, 상태 저장소, 워크스페이스 격리 및 AgentTask 오케스트레이션을 확장한다.
2026-07-29 13:12:21 +09:00

1513 lines
56 KiB
Go

package agenttask
import (
"context"
"errors"
"sync"
"testing"
"time"
)
func TestNoUnselectedStart(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("ready", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 2)
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
if harness.invoker.callCount() != 0 {
t.Fatalf("unselected ready project invoked provider %d times", harness.invoker.callCount())
}
project := harness.store.snapshot().Projects["project"]
if project.Status != ProjectStatusObserved || project.Intent != nil {
t.Fatalf("unselected project = %#v", project)
}
}
func TestManualStartFullProgression(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetDisjoint))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 2)
harness.start("project", "workspace", nil)
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
state := harness.store.snapshot()
project := state.Projects["project"]
work := project.Works["work"]
if project.Status != ProjectStatusCompleted || work.State != WorkStateCompleted {
t.Fatalf("project/work = %s/%s, want completed/completed", project.Status, work.State)
}
if harness.invoker.callCount() != 1 || harness.reviewer.callCount() != 1 ||
harness.integrator.callCount() != 1 {
t.Fatalf(
"calls invoke=%d review=%d integrate=%d",
harness.invoker.callCount(), harness.reviewer.callCount(), harness.integrator.callCount(),
)
}
}
func TestInterruptedResumeDefaultsOn(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusRunning
state.Projects["project"] = project
})
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
if harness.store.snapshot().Projects["project"].Status != ProjectStatusCompleted {
t.Fatalf("default interrupted resume did not complete")
}
if harness.invoker.callCount() != 1 {
t.Fatalf("default resume invocations = %d, want 1", harness.invoker.callCount())
}
}
func TestInterruptedResumeOverrideFalseStops(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
disabled := false
harness.start("project", "workspace", &disabled)
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusRunning
state.Projects["project"] = project
})
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
if harness.store.snapshot().Projects["project"].Status != ProjectStatusStopped {
t.Fatalf("override-off interrupted project was not stopped")
}
if harness.invoker.callCount() != 0 {
t.Fatalf("override-off resume invoked provider %d times", harness.invoker.callCount())
}
}
func TestProjectIsolationKeepsIndependentProjectRunning(t *testing.T) {
snapshots := map[ProjectID]ProjectWorkflowSnapshot{
"broken": testSnapshot("broken", "workspace-broken", testUnit("broken-work", WriteSetUnknown)),
"healthy": testSnapshot("healthy", "workspace-healthy", testUnit("healthy-work", WriteSetUnknown)),
}
harness := newHarness(t, snapshots, 2)
harness.workflow.errors["broken"] = errors.New("fixture workflow parse failure")
harness.start("broken", "workspace-broken", nil)
harness.start("healthy", "workspace-healthy", nil)
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
state := harness.store.snapshot()
if state.Projects["broken"].Status != ProjectStatusBlocked {
t.Fatalf("broken project status = %s", state.Projects["broken"].Status)
}
if state.Projects["healthy"].Status != ProjectStatusCompleted {
t.Fatalf("healthy project status = %s", state.Projects["healthy"].Status)
}
if harness.invoker.callCount() != 1 {
t.Fatalf("provider calls = %d, want healthy project only", harness.invoker.callCount())
}
}
func TestManualStartDuplicateManagerLeasePreventsConcurrentInvocation(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusRunning
project.Lease = &LeaseRecord{
OwnerID: "other-manager", Token: "live",
ExpiresAt: fixedClock{now: harness.manager.clock.Now()}.Now().Add(harness.manager.config.LeaseDuration),
}
state.Projects["project"] = project
})
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
if harness.invoker.callCount() != 0 {
t.Fatalf("live duplicate manager lease invoked provider")
}
}
func TestManualStartStopCancelsInvocationAndReleasesCapacity(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.invoker.delays["work"] = time.Second
harness.invoker.locators["work"] = []LocatorRecord{{
Kind: LocatorProcess, Opaque: "pid:77:start:100", Revision: "process-r1",
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
}}
harness.start("project", "workspace", nil)
done := make(chan error, 1)
go func() {
done <- harness.manager.Reconcile(context.Background())
}()
deadline := time.Now().Add(time.Second)
for harness.invoker.activeCount() == 0 && time.Now().Before(deadline) {
time.Sleep(time.Millisecond)
}
if harness.invoker.activeCount() == 0 {
t.Fatal("provider invocation did not start")
}
if err := harness.manager.StopProject(context.Background(), "project"); err != nil {
t.Fatalf("StopProject: %v", err)
}
select {
case err := <-done:
if !errors.Is(err, context.Canceled) {
t.Fatalf("Reconcile error = %v, want project cancellation", err)
}
case <-time.After(time.Second):
t.Fatal("Reconcile did not stop cancelled invocation")
}
state := harness.store.snapshot()
if state.Projects["project"].Status != ProjectStatusStopped {
t.Fatalf("project status = %s, want stopped", state.Projects["project"].Status)
}
if harness.manager.scheduler.Active("provider\x00profile") != 0 {
t.Fatal("provider scheduler capacity leaked after stop")
}
work := state.Projects["project"].Works["work"]
if _, ok := work.Locators[LocatorProcess]; !ok {
t.Fatal("cancel lost the durable process locator")
}
if _, ok := state.WorkspaceLeases["workspace"]; ok {
t.Fatal("cancel retained the workspace invocation lease")
}
}
func TestStopProjectPersistsUntilExplicitRestart(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
if err := harness.manager.StopProject(context.Background(), "project"); err != nil {
t.Fatalf("StopProject: %v", err)
}
for i := 0; i < 5; i++ {
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile %d: %v", i, err)
}
}
if harness.invoker.callCount() != 0 {
t.Fatalf("stopped project was executed %d times, want 0", harness.invoker.callCount())
}
state := harness.store.snapshot()
if state.Projects["project"].Status != ProjectStatusStopped {
t.Fatalf("project status = %s, want stopped", state.Projects["project"].Status)
}
req := StartRequest{
CommandID: "start-2-project",
ProjectID: "project",
WorkspaceID: "workspace",
MilestoneID: "milestone",
WorkflowRevision: "workflow-r1",
ConfigRevision: "config-r1",
GrantRevision: "grant-r1",
}
if err := harness.manager.StartProject(context.Background(), req); err != nil {
t.Fatalf("StartProject restart: %v", err)
}
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile after restart: %v", err)
}
state = harness.store.snapshot()
if state.Projects["project"].Status != ProjectStatusCompleted {
t.Fatalf("restarted project status = %s, want completed", state.Projects["project"].Status)
}
if harness.invoker.callCount() != 1 {
t.Fatalf("restarted project provider calls = %d, want 1", harness.invoker.callCount())
}
}
func TestStoppedWorkResumesFromDurableStage(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusRunning
work := project.Works["work"]
work.Unit = testUnit("work", WriteSetUnknown)
work.State = WorkStateReviewing
work.ResumeStage = WorkStateReviewing
work.Attempt = 1
work.AttemptID = attemptID("work", 1)
work.Submission = &Submission{
ProjectID: "project",
WorkUnitID: "work",
AttemptID: attemptID("work", 1),
ArtifactID: "art-1",
Ready: true,
}
project.Works["work"] = work
state.Projects["project"] = project
})
if err := harness.manager.StopProject(context.Background(), "project"); err != nil {
t.Fatalf("StopProject: %v", err)
}
state := harness.store.snapshot()
work := state.Projects["project"].Works["work"]
if work.State != WorkStateStopped || work.ResumeStage != WorkStateReviewing {
t.Fatalf("stopped work state/resumeStage = %s/%s, want stopped/reviewing", work.State, work.ResumeStage)
}
req := StartRequest{
CommandID: "start-2-project",
ProjectID: "project",
WorkspaceID: "workspace",
MilestoneID: "milestone",
WorkflowRevision: "workflow-r1",
ConfigRevision: "config-r1",
GrantRevision: "grant-r1",
}
if err := harness.manager.StartProject(context.Background(), req); err != nil {
t.Fatalf("StartProject: %v", err)
}
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
state = harness.store.snapshot()
work = state.Projects["project"].Works["work"]
if work.ResumeStage != "" {
t.Fatalf("recovered work still has resumeStage = %s", work.ResumeStage)
}
if work.State != WorkStateCompleted {
t.Fatalf("recovered work state = %s, want completed", work.State)
}
}
func TestAutoResumeOverrideFalseRequiresExplicitRestart(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
disabled := false
harness.start("project", "workspace", &disabled)
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusRunning
state.Projects["project"] = project
})
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
state := harness.store.snapshot()
if state.Projects["project"].Status != ProjectStatusStopped {
t.Fatalf("project status = %s, want stopped", state.Projects["project"].Status)
}
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile 2: %v", err)
}
if harness.invoker.callCount() != 0 {
t.Fatalf("provider calls = %d, want 0", harness.invoker.callCount())
}
req := StartRequest{
CommandID: "start-2-project",
ProjectID: "project",
WorkspaceID: "workspace",
MilestoneID: "milestone",
WorkflowRevision: "workflow-r1",
ConfigRevision: "config-r1",
GrantRevision: "grant-r1",
}
if err := harness.manager.StartProject(context.Background(), req); err != nil {
t.Fatalf("StartProject: %v", err)
}
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile 3: %v", err)
}
if harness.store.snapshot().Projects["project"].Status != ProjectStatusCompleted {
t.Fatalf("explicitly restarted project did not complete")
}
}
func TestDeviceSingletonLeaseBlocksDuplicateOwnerUntilExpiry(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
harness.store.edit(func(state *ManagerState) {
state.DeviceLease = &LeaseRecord{
OwnerID: "other-daemon", Token: "device-token",
ExpiresAt: harness.manager.clock.Now().Add(time.Minute),
}
})
err := harness.manager.Reconcile(context.Background())
if !errors.Is(err, ErrDeviceLeaseHeld) {
t.Fatalf("Reconcile error = %v, want ErrDeviceLeaseHeld", err)
}
if harness.invoker.callCount() != 0 {
t.Fatalf("duplicate device owner invoked provider %d times", harness.invoker.callCount())
}
harness.store.edit(func(state *ManagerState) {
state.DeviceLease.ExpiresAt = harness.manager.clock.Now().Add(-time.Second)
})
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile after expiry: %v", err)
}
if harness.invoker.callCount() != 1 {
t.Fatalf("expired device lease provider calls = %d, want 1", harness.invoker.callCount())
}
}
func TestWorkspaceLeaseBlocksDuplicateInvocationUntilExpiry(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
harness.store.edit(func(state *ManagerState) {
state.WorkspaceLeases["workspace"] = LeaseRecord{
OwnerID: "other-manager", Token: "workspace-token",
ExpiresAt: harness.manager.clock.Now().Add(time.Minute),
}
})
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
project := harness.store.snapshot().Projects["project"]
if project.Status != ProjectStatusBlocked || project.Blocker == nil ||
project.Blocker.Code != BlockerDuplicateWorkspaceCall {
t.Fatalf("project = %#v, want duplicate workspace blocker", project)
}
if harness.invoker.callCount() != 0 {
t.Fatalf("duplicate workspace owner invoked provider %d times", harness.invoker.callCount())
}
harness.store.edit(func(state *ManagerState) {
lease := state.WorkspaceLeases["workspace"]
lease.ExpiresAt = harness.manager.clock.Now().Add(-time.Second)
state.WorkspaceLeases["workspace"] = lease
})
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile after expiry: %v", err)
}
if harness.store.snapshot().Projects["project"].Status != ProjectStatusCompleted {
t.Fatal("project did not recover after the foreign workspace lease expired")
}
}
func TestWorkspaceLeaseConflictDoesNotFenceIndependentProject(t *testing.T) {
snapshotA := testSnapshot("a-blocked", "workspace-a", testUnit("work-a", WriteSetUnknown))
snapshotB := testSnapshot("b-independent", "workspace-b", testUnit("work-b", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{
"a-blocked": snapshotA,
"b-independent": snapshotB,
}, 1)
harness.start("a-blocked", "workspace-a", nil)
harness.start("b-independent", "workspace-b", nil)
harness.store.edit(func(state *ManagerState) {
state.WorkspaceLeases["workspace-a"] = LeaseRecord{
OwnerID: "other-manager", Token: "foreign-token-a",
ExpiresAt: harness.manager.clock.Now().Add(time.Minute),
}
})
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
state := harness.store.snapshot()
projectA := state.Projects["a-blocked"]
if projectA.Status != ProjectStatusBlocked || projectA.Blocker == nil ||
projectA.Blocker.Code != BlockerDuplicateWorkspaceCall {
t.Fatalf("projectA = %#v, want duplicate workspace blocker", projectA)
}
projectB := state.Projects["b-independent"]
if projectB.Status != ProjectStatusCompleted {
t.Fatalf("projectB = %#v, want completed status", projectB)
}
if harness.invoker.callCount() != 1 {
t.Fatalf("invoker calls = %d, want 1 for independent project", harness.invoker.callCount())
}
foreignLease, ok := state.WorkspaceLeases["workspace-a"]
if !ok || foreignLease.Token != "foreign-token-a" {
t.Fatalf("foreign workspace lease was modified or deleted: %#v", foreignLease)
}
if state.DeviceLease != nil || state.Projects["a-blocked"].Lease != nil || state.Projects["b-independent"].Lease != nil {
t.Fatalf("owned device/project claims remained: %#v", state)
}
if _, ok := state.WorkspaceLeases["workspace-b"]; ok {
t.Fatalf("owned workspace claim for b remained: %#v", state)
}
}
func TestRestartRetainsLiveChildWithoutDuplicateInvocation(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
seedDispatchingCheckpoint(harness, LocatorRecord{
Kind: LocatorProcess, Opaque: "pid:42:start:9001", Revision: "process-r1",
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
})
harness.recovery.observations["work"] = RecoveryObservation{
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
Execution: RecoveryExecutionLive,
}
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile live child: %v", err)
}
work := harness.store.snapshot().Projects["project"].Works["work"]
if work.State != WorkStateDispatching {
t.Fatalf("live child work state = %s, want dispatching", work.State)
}
if harness.invoker.callCount() != 0 {
t.Fatalf("live child was duplicated %d times", harness.invoker.callCount())
}
submission := Submission{
ProjectID: "project", WorkUnitID: "work", AttemptID: attemptID("work", 1),
ArtifactID: "artifact-recovered", Ready: true,
Locators: []LocatorRecord{{
Kind: LocatorProcess, Opaque: "pid:42:start:9001", Revision: "process-r1",
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
}},
}
harness.recovery.observations["work"] = RecoveryObservation{
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
Execution: RecoveryExecutionSubmitted, Submission: &submission,
}
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile submitted child: %v", err)
}
if harness.invoker.callCount() != 0 {
t.Fatalf("recovered submission reinvoked provider %d times", harness.invoker.callCount())
}
if harness.store.snapshot().Projects["project"].Works["work"].State != WorkStateCompleted {
t.Fatal("recovered submission did not finish review and integration")
}
}
func TestRestartRecoveredSubmissionRequiresWorkflowEvidence(t *testing.T) {
sessionLocator := LocatorRecord{
Kind: LocatorSession, Opaque: "native-session", Revision: "session-r1",
ProjectID: "project", WorkspaceID: "workspace", WorkUnitID: "work", AttemptID: attemptID("work", 1),
}
processLocator := LocatorRecord{
Kind: LocatorProcess, Opaque: "pid:42:start:9001", Revision: "process-r1",
ProjectID: "project", WorkspaceID: "workspace", WorkUnitID: "work", AttemptID: attemptID("work", 1),
}
for _, test := range []struct {
name string
submission Submission
isPi bool
observeFunc func(WorkflowEvidenceRequest, int) (ArtifactEvidence, error)
wantState WorkState
wantBlocker BlockerCode
wantReviews int
wantObserves int
wantRepairs int
}{
{
name: "complete evidence recovers and reviews",
submission: Submission{
ProjectID: "project", WorkUnitID: "work", AttemptID: attemptID("work", 1),
ArtifactID: "artifact-recovered", Ready: true,
Locators: []LocatorRecord{processLocator},
},
wantState: WorkStateCompleted,
wantReviews: 1,
wantObserves: 1,
wantRepairs: 0,
},
{
name: "incomplete submission blocks without evidence check or review",
submission: Submission{
ProjectID: "project", WorkUnitID: "work", AttemptID: attemptID("work", 1),
ArtifactID: "artifact-recovered", Ready: false,
Locators: []LocatorRecord{processLocator},
},
wantState: WorkStateBlocked,
wantBlocker: BlockerSubmissionIncomplete,
wantReviews: 0,
wantObserves: 0,
wantRepairs: 0,
},
{
name: "placeholder from non-Pi provider blocks without repair or review",
submission: Submission{
ProjectID: "project", WorkUnitID: "work", AttemptID: attemptID("work", 1),
ArtifactID: "artifact-recovered", Ready: true,
Locators: []LocatorRecord{processLocator},
},
observeFunc: func(request WorkflowEvidenceRequest, _ int) (ArtifactEvidence, error) {
return ArtifactEvidence{
Active: true, Identity: artifactIdentity(request.Project, request.Work, request.Submission),
Completeness: ArtifactPlaceholder,
}, nil
},
wantState: WorkStateBlocked,
wantBlocker: BlockerEvidenceRepairDenied,
wantReviews: 0,
wantObserves: 1,
wantRepairs: 0,
},
{
name: "inactive or identity mismatched evidence blocks review",
submission: Submission{
ProjectID: "project", WorkUnitID: "work", AttemptID: attemptID("work", 1),
ArtifactID: "artifact-recovered", Ready: true,
Locators: []LocatorRecord{processLocator},
},
observeFunc: func(request WorkflowEvidenceRequest, _ int) (ArtifactEvidence, error) {
identity := artifactIdentity(request.Project, request.Work, request.Submission)
identity.AttemptID = "stale-attempt"
return ArtifactEvidence{Active: true, Identity: identity, Completeness: ArtifactComplete}, nil
},
wantState: WorkStateBlocked,
wantBlocker: BlockerArtifactMismatch,
wantReviews: 0,
wantObserves: 1,
wantRepairs: 0,
},
{
name: "Pi repair rematches and reviews successfully",
isPi: true,
submission: Submission{
ProjectID: "project", WorkUnitID: "work", AttemptID: attemptID("work", 1),
ArtifactID: "artifact-recovered", Ready: true,
Locators: []LocatorRecord{sessionLocator},
},
observeFunc: func(request WorkflowEvidenceRequest, call int) (ArtifactEvidence, error) {
identity := artifactIdentity(request.Project, request.Work, request.Submission)
if call == 0 {
return ArtifactEvidence{
Active: true, Identity: identity, Completeness: ArtifactPlaceholder,
RepairIntent: &EvidenceRepairIntent{
Identity: identity, DispatchOrdinal: request.Work.DispatchOrdinal,
NativeLocator: request.Work.Locators[LocatorSession],
},
}, nil
}
return ArtifactEvidence{Active: true, Identity: identity, Completeness: ArtifactComplete}, nil
},
wantState: WorkStateCompleted,
wantReviews: 1,
wantObserves: 2,
wantRepairs: 1,
},
{
name: "Pi repair without fresh match blocks review",
isPi: true,
submission: Submission{
ProjectID: "project", WorkUnitID: "work", AttemptID: attemptID("work", 1),
ArtifactID: "artifact-recovered", Ready: true,
Locators: []LocatorRecord{sessionLocator},
},
observeFunc: func(request WorkflowEvidenceRequest, _ int) (ArtifactEvidence, error) {
identity := artifactIdentity(request.Project, request.Work, request.Submission)
return ArtifactEvidence{
Active: true, Identity: identity, Completeness: ArtifactPlaceholder,
RepairIntent: &EvidenceRepairIntent{
Identity: identity, DispatchOrdinal: request.Work.DispatchOrdinal,
NativeLocator: request.Work.Locators[LocatorSession],
},
}, nil
},
wantState: WorkStateBlocked,
wantBlocker: BlockerSubmissionIncomplete,
wantReviews: 0,
wantObserves: 2,
wantRepairs: 1,
},
} {
t.Run(test.name, func(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
locator := processLocator
if test.isPi {
locator = sessionLocator
}
seedDispatchingCheckpoint(harness, locator)
if test.isPi {
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
work := project.Works["work"]
work.Target.ProviderID = "pi"
project.Works["work"] = work
state.Projects["project"] = project
})
}
if test.observeFunc != nil {
harness.evidence.observeFunc = test.observeFunc
}
harness.recovery.observations["work"] = RecoveryObservation{
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
Execution: RecoveryExecutionSubmitted, Submission: &test.submission,
}
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
work := harness.store.snapshot().Projects["project"].Works["work"]
if work.State != test.wantState {
t.Fatalf("work state = %s, want %s", work.State, test.wantState)
}
if test.wantBlocker != "" {
if work.Blocker == nil || work.Blocker.Code != test.wantBlocker {
t.Fatalf("work blocker = %#v, want %q", work.Blocker, test.wantBlocker)
}
} else if work.Blocker != nil {
t.Fatalf("unexpected work blocker = %#v", work.Blocker)
}
if harness.reviewer.callCount() != test.wantReviews {
t.Fatalf("reviewer calls = %d, want %d", harness.reviewer.callCount(), test.wantReviews)
}
observes, repairs := harness.evidence.counts()
if observes != test.wantObserves {
t.Fatalf("observe calls = %d, want %d", observes, test.wantObserves)
}
if repairs != test.wantRepairs {
t.Fatalf("repair calls = %d, want %d", repairs, test.wantRepairs)
}
})
}
}
func TestCorruptLocatorIdentityStopsBeforeRecoveryOrProvider(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
seedDispatchingCheckpoint(harness, LocatorRecord{
Kind: LocatorProcess, Opaque: "pid:42:start:9001", Revision: "process-r1",
ProjectID: "other-project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
})
err := harness.manager.Reconcile(context.Background())
if !errors.Is(err, ErrCheckpointInvalid) {
t.Fatalf("Reconcile error = %v, want ErrCheckpointInvalid", err)
}
if harness.recovery.callCount() != 0 || harness.invoker.callCount() != 0 {
t.Fatalf(
"corrupt checkpoint calls recovery/provider = %d/%d, want 0/0",
harness.recovery.callCount(), harness.invoker.callCount(),
)
}
}
func TestPartialCompletionArchiveBecomesTerminalBlocker(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusRunning
work := WorkRecord{
Unit: testUnit("work", WriteSetUnknown), State: WorkStateCompleted,
Attempt: 1, AttemptID: attemptID("work", 1),
DispatchOrdinal: 1,
Locators: map[LocatorKind]LocatorRecord{
LocatorCompletion: {
Kind: LocatorCompletion, Opaque: "archive:partial", Revision: "archive-r1",
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
},
},
}
project.Works["work"] = work
state.Projects["project"] = project
})
harness.recovery.observations["work"] = RecoveryObservation{
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
Completion: RecoveryCompletionPartial,
}
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
project := harness.store.snapshot().Projects["project"]
work := project.Works["work"]
if project.Status != ProjectStatusBlocked || work.State != WorkStateBlocked ||
work.Blocker == nil || work.Blocker.Code != BlockerPartialCompletion {
t.Fatalf("partial completion project/work = %#v/%#v", project, work)
}
if harness.invoker.callCount() != 0 {
t.Fatalf("partial completion invoked provider %d times", harness.invoker.callCount())
}
}
func TestFailureBudgetPersistsAndStopsReviewRework(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.manager.config.MaxReworkAttempts = 5
harness.manager.config.MaxFailureAttempts = 2
harness.reviewer.sequences["work"] = []ReviewVerdict{
ReviewVerdictWarn, ReviewVerdictWarn, ReviewVerdictPass,
}
harness.start("project", "workspace", nil)
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
work := harness.store.snapshot().Projects["project"].Works["work"]
budget := work.FailureBudgets[FailureStageReview]
if work.State != WorkStateBlocked || work.Blocker == nil ||
work.Blocker.Code != BlockerFailureBudgetExhausted {
t.Fatalf("work = %#v, want exhausted failure budget blocker", work)
}
if budget.Consecutive != 2 || budget.Limit != 2 ||
budget.LastCode != BlockerReviewFailed {
t.Fatalf("review budget = %#v", budget)
}
if harness.invoker.callCount() != 2 || harness.reviewer.callCount() != 2 ||
harness.integrator.callCount() != 0 {
t.Fatalf(
"calls invoke/review/integrate = %d/%d/%d",
harness.invoker.callCount(), harness.reviewer.callCount(), harness.integrator.callCount(),
)
}
}
func TestSuccessfulRunPersistsOpaqueLocatorKinds(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
attempt := attemptID("work", 1)
harness.invoker.locators["work"] = []LocatorRecord{
{
Kind: LocatorProcess, Opaque: "pid:42:start:9001", Revision: "process-r1",
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attempt,
},
{
Kind: LocatorSession, Opaque: "provider-session-7", Revision: "session-r1",
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attempt,
},
}
harness.start("project", "workspace", nil)
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
work := harness.store.snapshot().Projects["project"].Works["work"]
for _, kind := range []LocatorKind{
LocatorProcess, LocatorSession, LocatorOverlay, LocatorChangeSet,
} {
if _, ok := work.Locators[kind]; !ok {
t.Errorf("missing persisted %s locator", kind)
}
}
}
func seedDispatchingCheckpoint(harness *managerHarness, locator LocatorRecord) {
harness.t.Helper()
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusRunning
unit := testUnit("work", WriteSetUnknown)
work := WorkRecord{
Unit: unit, State: WorkStateDispatching,
Attempt: 1, AttemptID: attemptID("work", 1),
DispatchOrdinal: 1,
Target: &ExecutionTarget{
ProviderID: "provider", ModelID: "model", ProfileID: "profile",
ProfileRevision: "profile-r1", ConfigRevision: "config-r1", Capacity: 1,
},
Isolation: &IsolationIdentity{
ID: "isolation-1", Revision: "isolation-r1",
Mode: unit.IsolationMode, PinnedBaseRevision: "base-r1",
TaskRoot: "/tmp/task-work",
},
Locators: map[LocatorKind]LocatorRecord{
LocatorOverlay: {
Kind: LocatorOverlay, Opaque: "isolation-1", Revision: "isolation-r1",
ProjectID: "project", WorkspaceID: "workspace",
WorkUnitID: "work", AttemptID: attemptID("work", 1),
},
locator.Kind: locator,
},
}
project.Works["work"] = work
state.Projects["project"] = project
})
}
func TestClaimProjectCASConflictReturnsCommittedDecision(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
store := harness.store
wrappedStore := &casConflictStore{
store: store,
onConflict: func() {
store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusStopped
state.Projects["project"] = project
})
},
}
harness.manager.store = wrappedStore
claim, err := harness.manager.claimProject(context.Background(), "project")
if err != nil {
t.Fatalf("claimProject: %v", err)
}
if claim != nil {
t.Fatalf("claimProject returned a claim for stopped project after CAS conflict")
}
}
func TestClaimIntegrationCASConflictReturnsCommittedDecision(t *testing.T) {
harness := newHarness(t, nil, 1)
store := harness.store
wrappedStore := &casConflictStore{
store: store,
onConflict: func() {
store.edit(func(state *ManagerState) {
state.IntegrationLeases["workspace"] = LeaseRecord{
OwnerID: "other-owner",
Token: "foreign",
ExpiresAt: time.Now().Add(time.Hour),
}
})
},
}
harness.manager.store = wrappedStore
claim, err := harness.manager.claimIntegration(context.Background(), "workspace")
if err != nil {
t.Fatalf("claimIntegration: %v", err)
}
if claim != nil {
t.Fatalf("claimIntegration returned a claim when foreign lease committed during CAS conflict")
}
}
func TestWorkflowActivationCASConflictUsesCommittedState(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
store := harness.store
wrappedStore := &casConflictStore{
store: store,
onConflict: func() {
store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusStopped
state.Projects["project"] = project
})
},
}
harness.manager.store = wrappedStore
active, err := harness.manager.observeWorkflows(context.Background())
if err != nil {
t.Fatalf("observeWorkflows: %v", err)
}
if len(active) != 0 {
t.Fatalf("observeWorkflows returned active projects = %v, want empty after CAS conflict to stopped", active)
}
}
func TestWorkflowActivationCASConflictUsesCommittedEventDecision(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
harness.store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusRunning
state.Projects["project"] = project
})
store := harness.store
wrappedStore := &casConflictStore{
store: store,
onConflict: func() {
store.edit(func(state *ManagerState) {
project := state.Projects["project"]
project.Status = ProjectStatusStarted
state.Projects["project"] = project
})
},
}
harness.manager.store = wrappedStore
active, err := harness.manager.observeWorkflows(context.Background())
if err != nil {
t.Fatalf("observeWorkflows: %v", err)
}
if len(active) != 1 || active[0] != "project" {
t.Fatalf("observeWorkflows active = %v, want [project]", active)
}
eventsEmitted := harness.events.snapshot()
var autoResumeCount int
var observedEvent *Event
for _, e := range eventsEmitted {
if e.Type == EventAutoResume {
autoResumeCount++
}
if e.Type == EventObserved {
eCopy := e
observedEvent = &eCopy
}
}
if autoResumeCount != 0 {
t.Fatalf("auto-resume event count = %d, want 0 on explicit-start winner", autoResumeCount)
}
if observedEvent == nil || observedEvent.CommandID == "" || observedEvent.WorkflowRevision == "" {
t.Fatalf("observed event missing committed identity: %#v", observedEvent)
}
}
type casConflictStore struct {
store *memoryStore
mu sync.Mutex
injected bool
armed <-chan struct{}
onConflict func()
}
func (s *casConflictStore) Load(ctx context.Context) (ManagerState, StateRevision, error) {
return s.store.Load(ctx)
}
func (s *casConflictStore) CompareAndSwap(ctx context.Context, expected StateRevision, next ManagerState) (StateRevision, error) {
s.mu.Lock()
if s.armed != nil {
select {
case <-s.armed:
default:
s.mu.Unlock()
return s.store.CompareAndSwap(ctx, expected, next)
}
}
if !s.injected {
s.injected = true
if s.onConflict != nil {
s.onConflict()
}
s.mu.Unlock()
return "", ErrRevisionConflict
}
s.mu.Unlock()
return s.store.CompareAndSwap(ctx, expected, next)
}
// TestDeviceLeaseRenewsDuringLongInvocation verifies that the device lease
// remains held while a provider invocation is blocked, and that the guarded
// context stays alive across multiple renewal intervals.
func TestDeviceLeaseRenewsDuringLongInvocation(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 2)
ticks, clock := harness.manualRenewals()
harness.start("project", "workspace", nil)
// Block the invoker so the manager stays inside invocation.Wait.
releaseInvocation := make(chan struct{})
harness.invoker.blockAllInvocations(releaseInvocation)
reconcileDone := make(chan error, 1)
go func() {
reconcileDone <- harness.manager.Reconcile(context.Background())
}()
// Give the manager time to enter the blocked invocation.
deadline := time.Now().Add(time.Second)
for harness.invoker.activeCount() == 0 && time.Now().Before(deadline) {
time.Sleep(time.Millisecond)
}
if harness.invoker.activeCount() == 0 {
t.Fatal("invocation did not enter blocked state")
}
// The device lease must still be held with a future expiry.
state := harness.store.snapshot()
if state.DeviceLease == nil {
t.Fatal("device lease was not claimed")
}
if state.DeviceLease.OwnerID != harness.manager.config.OwnerID {
t.Fatalf("device lease owner = %q, want %q", state.DeviceLease.OwnerID, harness.manager.config.OwnerID)
}
originalExpiry := state.DeviceLease.ExpiresAt
if !originalExpiry.After(harness.manager.clock.Now()) {
t.Fatalf("device lease expiry %v is not after now %v", originalExpiry, harness.manager.clock.Now())
}
clock.Advance(time.Minute)
ticks <- clock.Now()
waitFor(t, "device lease renewal", func() bool {
return harness.store.snapshot().DeviceLease.ExpiresAt.After(originalExpiry)
})
// Release the invocation and verify it completes.
close(releaseInvocation)
err := <-reconcileDone
if err != nil {
t.Fatalf("Reconcile returned error after renewal: %v", err)
}
if harness.invoker.callCount() != 1 {
t.Fatalf("invoker calls = %d, want 1", harness.invoker.callCount())
}
}
// TestLeaseLossCancelsInvocationBeforeCommit verifies that replacing the
// device lease token while a provider invocation is blocked cancels the
// guarded context and prevents the late result from being committed.
func TestLeaseLossCancelsInvocationBeforeCommit(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 2)
ticks, clock := harness.manualRenewals()
harness.start("project", "workspace", nil)
releaseInvocation := make(chan struct{})
harness.invoker.blockAllInvocations(releaseInvocation)
reconcileDone := make(chan error, 1)
go func() {
reconcileDone <- harness.manager.Reconcile(context.Background())
}()
deadline := time.Now().Add(time.Second)
for harness.invoker.activeCount() == 0 && time.Now().Before(deadline) {
time.Sleep(time.Millisecond)
}
if harness.invoker.activeCount() == 0 {
t.Fatal("invocation did not enter blocked state")
}
// Capture the original state, then replace the token with a foreign one.
_ = harness.store.snapshot()
harness.store.edit(func(state *ManagerState) {
state.DeviceLease = &LeaseRecord{
OwnerID: "rival-manager",
Token: "rival-token",
ExpiresAt: harness.manager.clock.Now().Add(time.Minute),
}
})
ticks <- clock.Now()
// Wait for Reconcile to return (it should fail with lease loss or context cancel).
err := <-reconcileDone
if err == nil {
t.Fatal("Reconcile returned nil after device lease token was replaced")
}
// The work must not have been committed to a terminal state.
state := harness.store.snapshot()
work := state.Projects["project"].Works["work"]
if work.State == WorkStateCompleted || work.State == WorkStateSubmitted {
t.Fatalf("work state = %s, want non-terminal after lease loss", work.State)
}
// The foreign token must still be in the store.
if state.DeviceLease == nil || state.DeviceLease.Token != "rival-token" {
t.Fatalf("device lease token = %v, want rival-token", state.DeviceLease)
}
// The original manager's invoker cancellation count must be > 0.
if harness.invoker.cancelCount() == 0 {
t.Fatal("invocation was not cancelled after lease loss")
}
// Release the blocked invocation to clean up.
close(releaseInvocation)
}
// TestWorkspaceProjectLeaseRenewsDuringLongReview verifies that workspace and
// project leases remain held while review is blocked, and that the guarded
// context stays alive.
func TestWorkspaceProjectLeaseRenewsDuringLongReview(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 2)
ticks, clock := harness.manualRenewals()
harness.start("project", "workspace", nil)
// Block the reviewer so the manager stays inside reviewSubmission.
releaseReview := make(chan struct{})
harness.reviewer.blockAllReviews(releaseReview)
reconcileDone := make(chan error, 1)
go func() {
reconcileDone <- harness.manager.Reconcile(context.Background())
}()
// Wait for the manager to enter the review stage.
deadline := time.Now().Add(2 * time.Second)
for {
state := harness.store.snapshot()
work := state.Projects["project"].Works["work"]
if work.State == WorkStateReviewing {
break
}
if time.Now().After(deadline) {
t.Fatalf("work did not enter reviewing state, state=%s", work.State)
}
time.Sleep(time.Millisecond)
}
// Workspace and project leases must be held.
state := harness.store.snapshot()
project := state.Projects["project"]
if project.Lease == nil || project.Lease.OwnerID != harness.manager.config.OwnerID {
t.Fatalf("project lease = %v, want held by manager", project.Lease)
}
if _, ok := state.WorkspaceLeases["workspace"]; !ok {
t.Fatal("workspace lease was not claimed")
}
projectExpiry := project.Lease.ExpiresAt
workspaceExpiry := state.WorkspaceLeases["workspace"].ExpiresAt
clock.Advance(time.Minute)
ticks <- clock.Now()
waitFor(t, "project and workspace lease renewal", func() bool {
state := harness.store.snapshot()
return state.Projects["project"].Lease.ExpiresAt.After(projectExpiry) &&
state.WorkspaceLeases["workspace"].ExpiresAt.After(workspaceExpiry)
})
// Release the review and verify the work completes.
close(releaseReview)
err := <-reconcileDone
if err != nil {
t.Fatalf("Reconcile returned error after review renewal: %v", err)
}
finalState := harness.store.snapshot()
work := finalState.Projects["project"].Works["work"]
if work.State != WorkStateCompleted {
t.Fatalf("work state = %s, want completed", work.State)
}
}
// TestIntegrationLeaseRenewsAndFencesLateResult verifies that the integration
// lease remains held through the external integration call, that lease loss
// rejects the late result, and that the successor token is preserved.
func TestIntegrationLeaseRenewsAndFencesLateResult(t *testing.T) {
snapshot := testSnapshot(
"project", "workspace",
testUnit("work", WriteSetDisjoint),
)
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 2)
harness.start("project", "workspace", nil)
// Let the work complete dispatch and review, then block integration.
releaseIntegration := make(chan struct{})
harness.integrator.blockAllIntegrations(releaseIntegration)
// Run reconcile in the background.
reconcileDone := make(chan error, 1)
go func() {
reconcileDone <- harness.manager.Reconcile(context.Background())
}()
// Wait for one snapshot that proves both the external integration phase
// and the manager-owned integration lease are present.
deadline := time.Now().Add(2 * time.Second)
for {
state := harness.store.snapshot()
work := state.Projects["project"].Works["work"]
lease, claimed := state.IntegrationLeases["workspace"]
if work.State == WorkStateIntegrating && claimed &&
lease.OwnerID == harness.manager.config.OwnerID {
break
}
if time.Now().After(deadline) {
t.Fatalf(
"work did not enter manager-owned integration, state=%s lease=%#v",
work.State,
lease,
)
}
time.Sleep(time.Millisecond)
}
// Capture the original integration lease token.
originalState := harness.store.snapshot()
origIntegToken, ok := originalState.IntegrationLeases["workspace"]
if !ok {
t.Fatalf("integration lease was not claimed; state=%s integrationLeases=%v",
originalState.Projects["project"].Works["work"].State, originalState.IntegrationLeases)
}
// Replace the integration lease token (simulating ownership loss).
harness.store.edit(func(state *ManagerState) {
state.IntegrationLeases["workspace"] = LeaseRecord{
OwnerID: "rival-manager",
Token: "rival-integration-token",
ExpiresAt: harness.manager.clock.Now().Add(time.Minute),
}
})
// Release the integrator and verify the late result is rejected.
close(releaseIntegration)
err := <-reconcileDone
if err == nil {
t.Fatal("Reconcile returned nil after integration lease token was replaced")
}
// The work must not have been committed to completed.
finalState := harness.store.snapshot()
work := finalState.Projects["project"].Works["work"]
if work.State == WorkStateCompleted {
t.Fatalf("work state = %s, want non-completed after integration lease loss", work.State)
}
// The successor token must still be in the store.
finalIntegLease, ok := finalState.IntegrationLeases["workspace"]
if !ok {
t.Fatal("integration lease disappeared after loss")
}
if finalIntegLease.Token != "rival-integration-token" {
t.Fatalf("integration lease token = %q, want rival-integration-token", finalIntegLease.Token)
}
// The original token must not match the current lease.
if origIntegToken.Token == finalIntegLease.Token {
t.Fatal("original integration token was not replaced")
}
}
func TestLeaseSupervisorRenewsAllTrackedScopesAndDeniesTakeover(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetDisjoint))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
ticks, clock := harness.manualRenewals()
harness.start("project", "workspace", nil)
release := make(chan struct{})
harness.integrator.blockAllIntegrations(release)
done := make(chan error, 1)
go func() { done <- harness.manager.Reconcile(context.Background()) }()
waitFor(t, "manager-owned integration lease", func() bool {
state := harness.store.snapshot()
work := state.Projects["project"].Works["work"]
_, integration := state.IntegrationLeases["workspace"]
return work.State == WorkStateIntegrating && state.DeviceLease != nil &&
state.Projects["project"].Lease != nil && integration &&
state.WorkspaceLeases["workspace"].Token != ""
})
before := harness.store.snapshot()
deviceExpiry := before.DeviceLease.ExpiresAt
projectExpiry := before.Projects["project"].Lease.ExpiresAt
workspaceExpiry := before.WorkspaceLeases["workspace"].ExpiresAt
integrationExpiry := before.IntegrationLeases["workspace"].ExpiresAt
clock.Advance(time.Minute)
ticks <- clock.Now()
waitFor(t, "supervisor renewal", func() bool {
state := harness.store.snapshot()
return state.DeviceLease.ExpiresAt.After(deviceExpiry) &&
state.Projects["project"].Lease.ExpiresAt.After(projectExpiry) &&
state.WorkspaceLeases["workspace"].ExpiresAt.After(workspaceExpiry) &&
state.IntegrationLeases["workspace"].ExpiresAt.After(integrationExpiry)
})
second, err := NewManager(
ManagerConfig{OwnerID: harness.manager.config.OwnerID, LeaseDuration: time.Minute}, clock,
harness.store, harness.workflow, fakeSelector{capacity: 1}, harness.isolation,
harness.invoker, harness.recovery, harness.evidence, harness.reviewer, harness.integrator, harness.events,
)
if err != nil {
t.Fatalf("NewManager(second): %v", err)
}
if _, err := second.claimDevice(context.Background()); !errors.Is(err, ErrDeviceLeaseHeld) {
t.Fatalf("second manager device claim error = %v, want ErrDeviceLeaseHeld", err)
}
close(release)
if err := <-done; err != nil {
t.Fatalf("Reconcile: %v", err)
}
}
func TestLeaseLossImmediatelyFencesProviderAndReviewResults(t *testing.T) {
for _, stage := range []string{"provider", "review"} {
t.Run(stage, func(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
release := make(chan struct{})
if stage == "provider" {
harness.invoker.blockAllInvocations(release)
} else {
harness.reviewer.blockAllReviews(release)
}
done := make(chan error, 1)
go func() { done <- harness.manager.Reconcile(context.Background()) }()
waitFor(t, stage+" external call", func() bool {
if stage == "provider" {
return harness.invoker.activeCount() > 0
}
return harness.store.snapshot().Projects["project"].Works["work"].State == WorkStateReviewing
})
harness.store.edit(func(state *ManagerState) {
state.DeviceLease = &LeaseRecord{
OwnerID: "successor", Token: "successor-device", ExpiresAt: time.Now().Add(time.Minute),
}
})
close(release)
if err := <-done; err == nil {
t.Fatal("Reconcile returned nil after immediate ownership loss")
}
state := harness.store.snapshot()
work := state.Projects["project"].Works["work"]
if stage == "provider" && (work.State == WorkStateSubmitted || work.State == WorkStateCompleted) {
t.Fatalf("provider result committed after lease loss: %s", work.State)
}
if stage == "review" && (work.State == WorkStatePendingIntegration || work.State == WorkStateCompleted) {
t.Fatalf("review result committed after lease loss: %s", work.State)
}
if state.DeviceLease == nil || state.DeviceLease.Token != "successor-device" {
t.Fatalf("device successor was not retained: %#v", state.DeviceLease)
}
})
}
}
func TestLeaseLossImmediatelyFencesIntegrationResult(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetDisjoint))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
release := make(chan struct{})
harness.integrator.blockAllIntegrations(release)
done := make(chan error, 1)
go func() { done <- harness.manager.Reconcile(context.Background()) }()
waitFor(t, "integration", func() bool {
state := harness.store.snapshot()
_, claimed := state.IntegrationLeases["workspace"]
return state.Projects["project"].Works["work"].State == WorkStateIntegrating && claimed
})
harness.store.edit(func(state *ManagerState) {
state.IntegrationLeases["workspace"] = LeaseRecord{
OwnerID: "successor", Token: "successor-integration", ExpiresAt: time.Now().Add(time.Minute),
}
})
close(release)
if err := <-done; err == nil {
t.Fatal("Reconcile returned nil after immediate integration lease loss")
}
state := harness.store.snapshot()
if work := state.Projects["project"].Works["work"]; work.State == WorkStateCompleted {
t.Fatal("integration result committed after lease loss")
}
if lease := state.IntegrationLeases["workspace"]; lease.Token != "successor-integration" {
t.Fatalf("integration successor token = %q", lease.Token)
}
}
func TestLeaseFenceRevalidatesAfterCASConflict(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetUnknown))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
release := make(chan struct{})
harness.invoker.blockAllInvocations(release)
arm := make(chan struct{})
harness.manager.store = &casConflictStore{
store: harness.store,
armed: arm,
onConflict: func() {
harness.store.edit(func(state *ManagerState) {
state.DeviceLease = &LeaseRecord{
OwnerID: "successor", Token: "successor-after-conflict", ExpiresAt: time.Now().Add(time.Minute),
}
})
},
}
done := make(chan error, 1)
go func() { done <- harness.manager.Reconcile(context.Background()) }()
waitFor(t, "provider invocation", func() bool { return harness.invoker.activeCount() > 0 })
close(arm)
close(release)
if err := <-done; err == nil {
t.Fatal("Reconcile returned nil after CAS-conflict ownership loss")
}
state := harness.store.snapshot()
if work := state.Projects["project"].Works["work"]; work.State == WorkStateSubmitted {
t.Fatal("provider result committed after conflict retry observed a successor")
}
if state.DeviceLease == nil || state.DeviceLease.Token != "successor-after-conflict" {
t.Fatalf("successor device lease was not retained: %#v", state.DeviceLease)
}
}
func TestLeaseCleanupReleasesOwnedClaimsAndPreservesSuccessors(t *testing.T) {
t.Run("normal", func(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetDisjoint))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
if err := harness.manager.Reconcile(context.Background()); err != nil {
t.Fatalf("Reconcile: %v", err)
}
state := harness.store.snapshot()
if state.DeviceLease != nil || state.Projects["project"].Lease != nil {
t.Fatalf("owned device/project claims remained: %#v %#v", state.DeviceLease, state.Projects["project"].Lease)
}
if _, ok := state.WorkspaceLeases["workspace"]; ok {
t.Fatal("owned workspace claim remained")
}
if _, ok := state.IntegrationLeases["workspace"]; ok {
t.Fatal("owned integration claim remained")
}
})
t.Run("successors", func(t *testing.T) {
snapshot := testSnapshot("project", "workspace", testUnit("work", WriteSetDisjoint))
harness := newHarness(t, map[ProjectID]ProjectWorkflowSnapshot{"project": snapshot}, 1)
harness.start("project", "workspace", nil)
release := make(chan struct{})
harness.integrator.blockAllIntegrations(release)
done := make(chan error, 1)
go func() { done <- harness.manager.Reconcile(context.Background()) }()
waitFor(t, "integration claim", func() bool {
state := harness.store.snapshot()
_, claimed := state.IntegrationLeases["workspace"]
return state.Projects["project"].Works["work"].State == WorkStateIntegrating && claimed
})
harness.store.edit(func(state *ManagerState) {
expires := time.Now().Add(time.Minute)
project := state.Projects["project"]
project.Lease = &LeaseRecord{OwnerID: "successor", Token: "successor-project", ExpiresAt: expires}
state.Projects["project"] = project
state.WorkspaceLeases["workspace"] = LeaseRecord{OwnerID: "successor", Token: "successor-workspace", ExpiresAt: expires}
state.IntegrationLeases["workspace"] = LeaseRecord{OwnerID: "successor", Token: "successor-integration", ExpiresAt: expires}
})
close(release)
if err := <-done; err == nil {
t.Fatal("Reconcile returned nil after successor replacement")
}
state := harness.store.snapshot()
if state.DeviceLease != nil {
t.Fatalf("owned device claim was not released: %#v", state.DeviceLease)
}
if state.Projects["project"].Lease.Token != "successor-project" ||
state.WorkspaceLeases["workspace"].Token != "successor-workspace" ||
state.IntegrationLeases["workspace"].Token != "successor-integration" {
t.Fatalf("successor claims were not retained: %#v", state)
}
})
}