Files
Agent-System-Core/task_snapshot_test.go
T
weichen 06f2723161 Add persistent work snapshots and checkpoint contracts
Why: preserve scoped work findings and truthful activity across reloads.

Evidence: task JSON loss and validation/state regressions RED to GREEN; complete Core suite.

Impact: optional card/document wire fields and bounded candidate validation; old Body remains supported.

Refs: fast-slow U2/U3 Task1, R03 R05 R09 R17
2026-10-13 01:04:56 +00:00

100 lines
4.0 KiB
Go

package core
import (
"encoding/json"
"strings"
"testing"
)
// Loss of the optional task object makes refresh discard stage findings.
func TestTaskSnapshotCardRoundTrip(t *testing.T) {
raw := []byte(`{"id":"card1","projectId":"p1","conversationId":"c1","kind":"progress","status":"active","task":{"schemaVersion":1,"taskId":"t1","attemptId":"a1","cardId":"card1","revision":4,"executionState":"running","focus":"核对材料","findings":[{"id":"f1","text":"日期不同","kind":"material_record","status":"active","revision":1}]}}`)
var card ActivityCard
if err := json.Unmarshal(raw, &card); err != nil {
t.Fatal(err)
}
encoded, err := json.Marshal(card)
if err != nil {
t.Fatal(err)
}
var restored map[string]json.RawMessage
if err := json.Unmarshal(encoded, &restored); err != nil {
t.Fatal(err)
}
if _, ok := restored["task"]; !ok {
t.Fatal("task findings were lost in card persistence")
}
var task map[string]json.RawMessage
if err := json.Unmarshal(restored["task"], &task); err != nil {
t.Fatal(err)
}
if string(task["revision"]) != "4" || string(task["focus"]) != `"核对材料"` {
t.Fatal("revision/focus did not survive")
}
}
func validTaskSnapshot() TaskSnapshot {
return TaskSnapshot{SchemaVersion: 1, TaskID: "t1", AttemptID: "a1", CardID: "card1", ProjectID: "p1", ConversationID: "c1", OwnerInstanceID: "i1", Revision: 1, ExecutionState: "queued", OutcomeCompleteness: "none", DeliveryState: "pending", StreamScope: "conversation:c1", Title: "整理材料"}
}
func TestTaskSnapshotRejectsUnsafeAndUnboundState(t *testing.T) {
for _, mutate := range []func(*TaskSnapshot){
func(s *TaskSnapshot) { s.TaskID = "" }, func(s *TaskSnapshot) { s.AttemptID = "" },
func(s *TaskSnapshot) { s.Revision = 9007199254740992 }, func(s *TaskSnapshot) { s.SourceSeq = -1 },
func(s *TaskSnapshot) { s.ExecutionState = "imagined" }, func(s *TaskSnapshot) { s.StreamScope = "conversation:other" },
func(s *TaskSnapshot) { s.ExecutionState = "running" },
} {
s := validTaskSnapshot()
mutate(&s)
if s.Validate() == nil {
t.Errorf("invalid snapshot accepted: %+v", s)
}
}
if err := validTaskSnapshot().Validate(); err != nil {
t.Fatal(err)
}
}
func TestWorkCheckpointRejectsVerifiedAndEmptyEvidence(t *testing.T) {
c := WorkCheckpointCandidate{CheckpointID: "cp1", PhaseID: "crosscheck", Findings: []WorkFindingCandidate{{FindingKey: "date", Text: "日期不同", Kind: "verified", Sources: []TaskSourceQuote{{SourceID: "src1", Quote: "六月"}}}}}
if err := ValidateWorkCheckpoint(c); err == nil || !strings.Contains(err.Error(), "kind") {
t.Fatal("model granted verified status", err)
}
c.Findings[0].Kind = "material_record"
c.Findings[0].Sources = nil
if ValidateWorkCheckpoint(c) == nil {
t.Fatal("source-free fact accepted")
}
c.Findings[0].Sources = []TaskSourceQuote{{SourceID: "src1", Quote: "六月"}}
if err := ValidateWorkCheckpoint(c); err != nil {
t.Fatal(err)
}
c.Findings[0].Text = strings.Repeat("x", 17000)
if ValidateWorkCheckpoint(c) == nil {
t.Fatal("unbounded candidate accepted")
}
}
func TestTaskSnapshotBodyPreservesLimitsAndHonestStates(t *testing.T) {
s := validTaskSnapshot()
s.Remaining = "图片内容未纳入"
s.ExecutionState = "failed"
s.Conclusion = "处理失败,已有发现可查看"
s.Findings = []TaskFinding{{ID: "f1", Text: "金额缺失", Kind: "material_record", Status: "active", Revision: 1}}
raw, _ := json.Marshal(s.SummaryBody())
for _, want := range []string{"执行失败", "图片内容未纳入", "金额缺失", "处理失败"} {
if !strings.Contains(string(raw), want) {
t.Errorf("lost critical summary %s", want)
}
}
}
func TestTaskSnapshotUnstartedAndFinishedDoNotClaimReading(t *testing.T) {
for _, state := range []string{"queued", "completed", "interrupted"} {
s := validTaskSnapshot()
s.ExecutionState = state
s.Conclusion = "处理已结束"
b, _ := json.Marshal(s.SummaryBody())
if strings.Contains(string(b), "正在读取") {
t.Errorf("%s falsely claims active reading", state)
}
}
}