Why: Validate W6 against account-audit migration v31 on the current authoritative base. Evidence: Focused store/runmgr/migrator and full Go tests passed in the isolated probe after resolving migration collisions. Impact: Renumbers unpublished W6 migrations to v32-v36; this probe commit is not a shipping branch or live migration. Refs: feature/rdm-two-trees-latest-templates W6 integration probe
277 lines
10 KiB
Go
277 lines
10 KiB
Go
package store
|
|
|
|
import (
|
|
"bytes"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"agent-system-backend/pkg/extension"
|
|
)
|
|
|
|
func TestContextManifestTerminalRequiresMatchingAttempt(t *testing.T) {
|
|
st, err := OpenSQLite(filepath.Join(t.TempDir(), "receipt.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer st.Close()
|
|
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "attempt-1", InputSHA256: "input-hash", Delivery: "responded"}
|
|
if err := st.AppendContextManifest("alice", "run-1", m); err == nil {
|
|
t.Fatal("terminal receipt without attempted send")
|
|
}
|
|
m.Delivery = "attempted"
|
|
if err := st.AppendContextManifest("alice", "run-1", m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.AppendContextManifest("alice", "run-1", m); err == nil {
|
|
t.Fatal("duplicate attempted receipt")
|
|
}
|
|
m.Delivery = "responded"
|
|
if err := st.AppendContextManifest("alice", "run-2", m); err == nil {
|
|
t.Fatal("attempt reused across target")
|
|
}
|
|
if err := st.AppendContextManifest("alice", "run-1", m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m.Delivery = "unknown"
|
|
if err := st.AppendContextManifest("alice", "run-1", m); err == nil {
|
|
t.Fatal("one attempt acquired two terminal states")
|
|
}
|
|
}
|
|
|
|
func TestContextManifestDoesNotPersistWorkflowOutputInResolveScope(t *testing.T) {
|
|
st, err := OpenSQLite(filepath.Join(t.TempDir(), "scope-privacy.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer st.Close()
|
|
const restricted = "OLD_RESTRICTED_BODY"
|
|
m := extension.ProjectContextManifest{ProjectID: "project-1", AttemptID: "judge-attempt", InputSHA256: "input-hash", Delivery: "attempted"}
|
|
scope := ContextManifestScope{Bound: true}
|
|
if err := st.AppendContextManifestWithScope("alice", "run-1", m, scope); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m.Delivery = "responded"
|
|
if err := st.AppendContextManifestWithScope("alice", "run-1", m, scope); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rows, err := st.db.Query(st.q(`SELECT delivery,manifest_json FROM context_manifests WHERE attempt_id=? ORDER BY created_at,id`), m.AttemptID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer rows.Close()
|
|
seen := 0
|
|
for rows.Next() {
|
|
var delivery, manifest string
|
|
if err := rows.Scan(&delivery, &manifest); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
seen++
|
|
if strings.Contains(manifest, restricted) {
|
|
t.Errorf("%s trusted receipt persisted workflow output body", delivery)
|
|
}
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if seen != 2 {
|
|
t.Fatalf("expected attempted and responded receipts, got %d", seen)
|
|
}
|
|
var rawColumns int
|
|
if err := st.db.QueryRow(`SELECT COUNT(*) FROM pragma_table_info('context_manifests') WHERE name IN ('resolve_goal','resolve_target_id')`).Scan(&rawColumns); err != nil || rawColumns != 0 {
|
|
t.Fatalf("fresh receipt schema retains raw scope columns: columns=%d err=%v", rawColumns, err)
|
|
}
|
|
}
|
|
|
|
func TestCandidateV35RawScopeColumnsAreScrubbedOnUpgrade(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "candidate-v35.db")
|
|
st, err := OpenSQLite(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "old-model", InputSHA256: "input", Delivery: "attempted"}
|
|
if err := st.AppendContextManifestWithScope("alice", "run-1", m, ContextManifestScope{Bound: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, query := range []string{
|
|
`DELETE FROM schema_migrations WHERE version=36`,
|
|
`ALTER TABLE context_manifests ADD COLUMN resolve_goal TEXT NOT NULL DEFAULT ''`,
|
|
`ALTER TABLE context_manifests ADD COLUMN resolve_target_id TEXT NOT NULL DEFAULT ''`,
|
|
`UPDATE context_manifests SET resolve_goal='OLD_RESTRICTED_BODY',resolve_target_id='doc-private'`,
|
|
} {
|
|
if _, err := st.db.Exec(query); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := st.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
upgraded, err := OpenSQLite(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer upgraded.Close()
|
|
var columns int
|
|
if err := upgraded.db.QueryRow(`SELECT COUNT(*) FROM pragma_table_info('context_manifests') WHERE name IN ('resolve_goal','resolve_target_id')`).Scan(&columns); err != nil || columns != 0 {
|
|
t.Fatalf("candidate raw scope columns survived v36: columns=%d err=%v", columns, err)
|
|
}
|
|
var count int
|
|
if err := upgraded.db.QueryRow(`SELECT COUNT(*) FROM context_manifests WHERE attempt_id='old-model'`).Scan(&count); err != nil || count != 1 {
|
|
t.Fatalf("trusted manifest disappeared during scrub: count=%d err=%v", count, err)
|
|
}
|
|
for _, suffix := range []string{"", "-wal"} {
|
|
data, err := os.ReadFile(path + suffix)
|
|
if err == nil && bytes.Contains(data, []byte("OLD_RESTRICTED_BODY")) {
|
|
t.Fatalf("candidate raw scope bytes remain in SQLite %s after upgrade", suffix)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCandidateV35CommittedDropKeepsPendingScrubAcrossRestart(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "candidate-crash.db")
|
|
st, err := OpenSQLite(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "crash-model", InputSHA256: "input", Delivery: "attempted"}
|
|
if err := st.AppendContextManifestWithScope("alice", "run-1", m, ContextManifestScope{Bound: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, query := range []string{
|
|
`DELETE FROM schema_migrations WHERE version=36`,
|
|
`ALTER TABLE context_manifests ADD COLUMN resolve_goal TEXT NOT NULL DEFAULT ''`,
|
|
`ALTER TABLE context_manifests ADD COLUMN resolve_target_id TEXT NOT NULL DEFAULT ''`,
|
|
`UPDATE context_manifests SET resolve_goal='OLD_RESTRICTED_BODY',resolve_target_id='doc-private'`,
|
|
} {
|
|
if _, err := st.db.Exec(query); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
// Apply only the transactional DDL and ledger. A process crash at this
|
|
// boundary must not make the next open mistake the file for scrubbed.
|
|
if err := st.migratePass(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var pending int
|
|
if err := st.db.QueryRow(`SELECT pending FROM context_scope_scrub_state WHERE id=1`).Scan(&pending); err != nil || pending != 1 {
|
|
t.Fatalf("committed v36 lost physical scrub obligation: pending=%d err=%v", pending, err)
|
|
}
|
|
if err := st.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
restarted, err := OpenSQLite(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer restarted.Close()
|
|
if err := restarted.db.QueryRow(`SELECT pending FROM context_scope_scrub_state WHERE id=1`).Scan(&pending); err != nil || pending != 0 {
|
|
t.Fatalf("restart did not complete pending scrub: pending=%d err=%v", pending, err)
|
|
}
|
|
for _, suffix := range []string{"", "-wal"} {
|
|
data, err := os.ReadFile(path + suffix)
|
|
if err == nil && bytes.Contains(data, []byte("OLD_RESTRICTED_BODY")) {
|
|
t.Fatalf("restart left candidate raw bytes in SQLite %s", suffix)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEarlierV36WithoutMarkerIsConservativelyRescrubbed(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "old-v36.db")
|
|
st, err := OpenSQLite(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "old-v36-model", InputSHA256: "input", Delivery: "attempted"}
|
|
if err := st.AppendContextManifestWithScope("alice", "run-1", m, ContextManifestScope{Bound: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, query := range []string{
|
|
`ALTER TABLE context_manifests ADD COLUMN resolve_goal TEXT NOT NULL DEFAULT ''`,
|
|
`UPDATE context_manifests SET resolve_goal='OLD_RESTRICTED_BODY'`,
|
|
`UPDATE context_manifests SET resolve_goal=''`,
|
|
`ALTER TABLE context_manifests DROP COLUMN resolve_goal`,
|
|
`DROP TABLE context_scope_scrub_state`,
|
|
} {
|
|
if _, err := st.db.Exec(query); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := st.Close(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
restarted, err := OpenSQLite(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer restarted.Close()
|
|
var pending int
|
|
if err := restarted.db.QueryRow(`SELECT pending FROM context_scope_scrub_state WHERE id=1`).Scan(&pending); err != nil || pending != 0 {
|
|
t.Fatalf("earlier v36 without marker was not re-scrubbed: pending=%d err=%v", pending, err)
|
|
}
|
|
for _, suffix := range []string{"", "-wal"} {
|
|
data, err := os.ReadFile(path + suffix)
|
|
if err == nil && bytes.Contains(data, []byte("OLD_RESTRICTED_BODY")) {
|
|
t.Fatalf("old v36 raw bytes survived re-scrub in SQLite %s", suffix)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCandidateV35PostgresRawScopeScrubAndFreshSchema(t *testing.T) {
|
|
st := openTestStore(t)
|
|
if st.Dialect() != "postgres" {
|
|
t.Skip("isolated PostgreSQL DSN not configured")
|
|
}
|
|
checkColumns := func(want int) {
|
|
t.Helper()
|
|
var count int
|
|
if err := st.db.QueryRow(`SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=current_schema() AND table_name='context_manifests' AND column_name IN ('resolve_goal','resolve_target_id')`).Scan(&count); err != nil || count != want {
|
|
t.Fatalf("raw scope columns=%d want=%d err=%v", count, want, err)
|
|
}
|
|
}
|
|
checkColumns(0)
|
|
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "candidate-model", InputSHA256: "input", Delivery: "attempted"}
|
|
if err := st.AppendContextManifestWithScope("alice", "run-1", m, ContextManifestScope{Bound: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, query := range []string{
|
|
`DELETE FROM schema_migrations WHERE version=36`,
|
|
`ALTER TABLE context_manifests ADD COLUMN resolve_goal TEXT NOT NULL DEFAULT ''`,
|
|
`ALTER TABLE context_manifests ADD COLUMN resolve_target_id TEXT NOT NULL DEFAULT ''`,
|
|
`UPDATE context_manifests SET resolve_goal='OLD_RESTRICTED_BODY',resolve_target_id='doc-private'`,
|
|
} {
|
|
if _, err := st.db.Exec(query); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
checkColumns(2)
|
|
if err := st.migrate(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
checkColumns(0)
|
|
var count int
|
|
if err := st.db.QueryRow(`SELECT COUNT(*) FROM context_manifests WHERE attempt_id='candidate-model'`).Scan(&count); err != nil || count != 1 {
|
|
t.Fatalf("trusted manifest lost during PG scrub: count=%d err=%v", count, err)
|
|
}
|
|
}
|
|
|
|
func TestContextManifestTerminalCannotChangeTrustedResolveScope(t *testing.T) {
|
|
st, err := OpenSQLite(filepath.Join(t.TempDir(), "scope.db"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer st.Close()
|
|
m := extension.ProjectContextManifest{ProjectID: "p1", AttemptID: "model-1", WorkflowStepAttemptID: "engine/draft/1", InputSHA256: "input-hash", Delivery: "attempted"}
|
|
scope := ContextManifestScope{Bound: true}
|
|
if err := st.AppendContextManifestWithScope("alice", "run-1", m, scope); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m.Delivery = "responded"
|
|
if err := st.AppendContextManifest("alice", "run-1", m); err == nil {
|
|
t.Fatal("terminal receipt dropped its trusted Resolve scope")
|
|
}
|
|
if err := st.AppendContextManifestWithScope("alice", "run-1", m, scope); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|