package cardsvc import ( core "agent-system-core" "testing" ) func TestTaskProjectionPreservesUserReadingPreferences(t *testing.T) { st, svc, _ := openTest(t) c := core.ActivityCard{ID: "card-task", ProjectID: "p1", ConversationID: "c1", Title: "任务", Kind: core.CardKindProgress} c.Task = &core.TaskSnapshot{SchemaVersion: 1, TaskID: "t1", AttemptID: "a1", CardID: c.ID, ProjectID: "p1", ConversationID: "c1", OwnerInstanceID: "i1", StreamScope: "conversation:c1", Revision: 1, ExecutionState: "queued", OutcomeCompleteness: "none", DeliveryState: "pending", Title: "任务"} if _, e := svc.CreateTask("o", c, ""); e != nil { t.Fatal(e) } current, e := st.GetCard("o", c.ID) if e != nil { t.Fatal("task card not saved", e) } current.Pinned = true current.Status = core.CardDismissed if e = st.SaveCard("o", ¤t, ""); e != nil { t.Fatal(e) } next := *current.Task next.Revision = 2 next.ExecutionState = "completed" next.Conclusion = "有用成果" if e = svc.CommitTask("o", next, 1); e != nil { t.Fatal(e) } got, e := st.GetCard("o", c.ID) if e != nil || !got.Pinned || got.Status != core.CardDismissed || got.Task.ExecutionState != "completed" { t.Fatal("work update stole user state", got, e) } } func TestTaskProjectionUserPinHasAtomicEventWatermark(t *testing.T) { st, svc, _ := openTest(t) c := core.ActivityCard{ID: "card-pin", ProjectID: "p1", ConversationID: "c1", Title: "任务", Kind: core.CardKindProgress} c.Task = &core.TaskSnapshot{SchemaVersion: 1, TaskID: "t1", AttemptID: "a1", CardID: c.ID, ProjectID: "p1", ConversationID: "c1", OwnerInstanceID: "i1", StreamScope: "conversation:c1", Revision: 1, ExecutionState: "queued", OutcomeCompleteness: "none", DeliveryState: "pending", Title: "任务"} if _, e := svc.CreateTask("o", c, ""); e != nil { t.Fatal(e) } if _, e := svc.SetPinned("o", c.ID, true); e != nil { t.Fatal(e) } snapshot, e := st.CardsAtCursor("o", "c1") if e != nil || snapshot.ReplaySeq != 2 || len(snapshot.Cards) != 1 || !snapshot.Cards[0].Pinned { t.Fatal("pin not atomically replayable", snapshot, e) } } func TestTaskProjectionCannotCreateSecondExecutionOwner(t *testing.T) { _, svc, _ := openTest(t) c := core.ActivityCard{ID: "card-archive", ProjectID: "p1", ConversationID: "c1", Title: "任务", Kind: core.CardKindProgress} c.Task = &core.TaskSnapshot{SchemaVersion: 1, TaskID: "t1", AttemptID: "a1", CardID: c.ID, ProjectID: "p1", ConversationID: "c1", OwnerInstanceID: "i1", StreamScope: "conversation:c1", Revision: 1, ExecutionState: "queued", OutcomeCompleteness: "none", DeliveryState: "pending", Title: "任务"} if _, e := svc.CreateTask("o", c, ""); e != nil { t.Fatal(e) } if _, e := svc.ToProject("o", c.ID); e == nil { t.Fatal("task was copied with a second execution owner") } }