Why: Compiled workflow critics could resend a prior prompt output after document duty or publication changed across separate model guards. Evidence: RED cross-guard revocation and retry tests; go test ./... -count=1 and targeted go test -race passed locally before commit. Impact: Bind materialized prompt bytes to the engine step attempt and responded model receipt; recheck current project sources and epochs before judging, failing closed for unproven or ambiguous output. Refs: RDM two-trees stage5 W6; stage6 X1 public extension request; W6 independent review P1.
76 lines
3.0 KiB
Go
76 lines
3.0 KiB
Go
package store
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"agent-system-backend/pkg/extension"
|
|
core "agent-system-core"
|
|
)
|
|
|
|
func TestWorkflowStepSourceRequiresRespondedManifestAndExactRunStep(t *testing.T) {
|
|
st, err := OpenSQLite(filepath.Join(t.TempDir(), "step.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer st.Close()
|
|
p, err := st.CreateProject("alice", "P")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
c, err := st.CreateConversation("alice", string(p.ID), "C", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := core.Run{Owner: "alice", ConversationID: c.ID, ProjectID: p.ID, Kind: core.RunConversation, Status: core.RunPending}
|
|
if err := st.CreateRun(&r); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
started := time.Now().Add(-time.Second)
|
|
m := extension.ProjectContextManifest{ProjectID: p.ID, AttemptID: "attempt-1", WorkflowStepAttemptID: "engine-run/draft/1", Delivery: "attempted", InputSHA256: "input", AuthorizationEpoch: "duty-1", PublicationEpoch: "pub-1", Watermark: "w1"}
|
|
if err := st.AppendContextManifest("alice", string(r.ID), m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := st.BindWorkflowStepSource("alice", string(r.ID), "draft", "engine-run/draft/1", "prompt", started, []byte("old body")); err == nil {
|
|
t.Fatal("unanswered model attempt bound output")
|
|
}
|
|
unrelated := m
|
|
unrelated.AttemptID = "other-model-attempt"
|
|
unrelated.WorkflowStepAttemptID = "engine-run/other/1"
|
|
if err := st.AppendContextManifest("alice", string(r.ID), unrelated); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
unrelated.Delivery = "responded"
|
|
if err := st.AppendContextManifest("alice", string(r.ID), unrelated); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := st.BindWorkflowStepSource("alice", string(r.ID), "draft", "engine-run/draft/1", "prompt", started, []byte("old body")); err == nil {
|
|
t.Fatal("another step's responded attempt bound draft output")
|
|
}
|
|
m.Delivery = "responded"
|
|
if err := st.AppendContextManifest("alice", string(r.ID), m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bound, err := st.BindWorkflowStepSource("alice", string(r.ID), "draft", "engine-run/draft/1", "prompt", started, []byte("old body"))
|
|
if err != nil || bound.AttemptID != "attempt-1" || bound.StepAttemptID != "engine-run/draft/1" || bound.OutputSHA256 != sha256.Sum256([]byte("old body")) {
|
|
t.Fatalf("source binding missed actual output: %+v err=%v", bound, err)
|
|
}
|
|
if _, err := st.WorkflowStepSource("alice", string(r.ID), "other"); err == nil {
|
|
t.Fatal("another step reused output binding")
|
|
}
|
|
if _, err := st.WorkflowStepSource("bob", string(r.ID), "draft"); err == nil {
|
|
t.Fatal("another owner read output binding")
|
|
}
|
|
if _, err := st.BindWorkflowStepSource("alice", string(r.ID), "tool", "engine-run/tool/1", "tool", started, []byte("tool body")); err == nil {
|
|
t.Fatal("tool output bound as model-sourced material")
|
|
}
|
|
if err := st.UnbindWorkflowStepSource("alice", string(r.ID), "draft"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := st.WorkflowStepSource("alice", string(r.ID), "draft"); err == nil {
|
|
t.Fatal("retry retained an earlier source binding")
|
|
}
|
|
}
|