Files
kenn-io--agentsview/internal/sync/antigravity_cli_integration_test.go
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

726 lines
26 KiB
Go

package sync_test
import (
"context"
"database/sql"
"fmt"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.kenn.io/agentsview/internal/db"
"go.kenn.io/agentsview/internal/parser"
"go.kenn.io/agentsview/internal/sync"
)
const antigravityCLITestSchema = `
CREATE TABLE steps (
idx integer,
step_type integer NOT NULL DEFAULT 0,
step_payload blob,
PRIMARY KEY (idx))
`
// agyCLIUnknownSchemaAnomaly is the anomaly a run produces when it writes n
// antigravity-cli sessions from the minimal test schema above. That schema is
// not the recognized agy baseline, so its fingerprint maps to an "agy-schema:"
// source_version and the decode-confidence rule flags it as low confidence.
func agyCLIUnknownSchemaAnomaly(n int) sync.AnomalyStats {
return sync.AnomalyStats{
UnknownSchemaSessionsByAgent: map[string]int{"antigravity-cli": n},
UnknownSchemaSessionsTotal: n,
}
}
func TestSyncEngineAntigravityCLI_HappyPath(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "33333333-4444-5555-6666-777777777777"
// Create subdirectories
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
// Write history.jsonl to map the project
historyLine := `{"conversationId": "` + uuid + `", "workspace": "/home/user/my-cli-project", "timestamp": 1716244800000, "display": "Initial Prompt"}` + "\n"
require.NoError(t, os.WriteFile(filepath.Join(env.antigravityCLIDir, "history.jsonl"), []byte(historyLine), 0o644))
// Write .pb file
pbPath := filepath.Join(convDir, uuid+".pb")
require.NoError(t, os.WriteFile(pbPath, []byte("dummy-pb"), 0o644))
// Write .trajectory.json
trajectoryJSON := `{
"trajectoryId": "` + uuid + `",
"steps": [
{
"type": "CORTEX_STEP_TYPE_USER_INPUT",
"status": "STATUS_COMPLETED",
"metadata": {
"createdAt": "2026-05-20T22:40:00Z"
},
"userInput": {
"userResponse": "Check workspace status"
}
},
{
"type": "CORTEX_STEP_TYPE_PLANNER_RESPONSE",
"status": "STATUS_COMPLETED",
"metadata": {
"createdAt": "2026-05-20T22:41:00Z"
},
"plannerResponse": {
"thinking": "I should list files",
"response": "listing files now",
"toolCalls": [
{
"id": "tc-123",
"name": "run_command",
"argumentsJson": "{\"CommandLine\":\"ls\"}"
}
]
}
},
{
"type": "CORTEX_STEP_TYPE_RUN_COMMAND",
"status": "STATUS_COMPLETED",
"metadata": {
"createdAt": "2026-05-20T22:42:00Z",
"executionId": "tc-123"
},
"runCommand": {
"commandLine": "ls",
"combinedOutput": "\"fileA.go\""
}
}
]
}`
trajPath := filepath.Join(convDir, uuid+".trajectory.json")
require.NoError(t, os.WriteFile(trajPath, []byte(trajectoryJSON), 0o644))
// First Sync: should ingest 1 session
stats := runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 1,
Synced: 1,
Skipped: 0,
})
assert.Equal(t, 1, stats.Synced)
// Verify database ingestion
assertSessionProject(t, env.db, "antigravity-cli:"+uuid, "/home/user/my-cli-project")
// Expected messages:
// 1. User: "Check workspace status"
// 2. Assistant: "listing files now" (with tool calls and thoughts)
// (Note: synthetic empty-content User message with tool results is paired and filtered out by the engine)
assertSessionMessageCount(t, env.db, "antigravity-cli:"+uuid, 2)
msgs := fetchMessages(t, env.db, "antigravity-cli:"+uuid)
require.Len(t, msgs, 2)
assert.Equal(t, "user", msgs[0].Role)
assert.Equal(t, "Check workspace status", msgs[0].Content)
assert.Equal(t, "assistant", msgs[1].Role)
assert.Equal(t, "listing files now", msgs[1].Content)
}
func TestSyncEngineAntigravityCLI_SidecarUpdates(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
syncUUID := "44444444-5555-6666-7777-888888888888"
sinceUUID := "77777777-8888-9999-0000-111111111111"
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
history := `{"conversationId": "` + syncUUID + `", "workspace": "/home/user/workspace-abc", "timestamp": 1716244800000, "display": "History Prompt"}` + "\n" +
`{"conversationId": "` + sinceUUID + `", "workspace": "/home/user/workspace-since", "timestamp": 1716244800000, "display": "History Prompt Since"}` + "\n"
require.NoError(t, os.WriteFile(
filepath.Join(env.antigravityCLIDir, "history.jsonl"),
[]byte(history), 0o644,
))
syncPBPath := filepath.Join(convDir, syncUUID+".pb")
require.NoError(t, os.WriteFile(syncPBPath, []byte("dummy-pb"), 0o644))
sincePBPath := filepath.Join(convDir, sinceUUID+".pb")
require.NoError(t, os.WriteFile(sincePBPath, []byte("dummy-pb"), 0o644))
oldTime := time.Now().Add(-48 * time.Hour)
require.NoError(t, os.Chtimes(sincePBPath, oldTime, oldTime))
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 2,
Synced: 2,
Skipped: 0,
})
assertSessionMessageCount(t, env.db, "antigravity-cli:"+syncUUID, 1)
msgs := fetchMessages(t, env.db, "antigravity-cli:"+syncUUID)
require.Len(t, msgs, 1)
assert.Equal(t, "History Prompt", msgs[0].Content)
assertSessionMessageCount(t, env.db, "antigravity-cli:"+sinceUUID, 1)
msgs = fetchMessages(t, env.db, "antigravity-cli:"+sinceUUID)
require.Len(t, msgs, 1)
assert.Equal(t, "History Prompt Since", msgs[0].Content)
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 2,
Synced: 0,
Skipped: 2,
})
time.Sleep(10 * time.Millisecond)
trajPath := filepath.Join(convDir, syncUUID+".trajectory.json")
trajectoryJSON := antigravityCLISingleUserTrajectory(
syncUUID, "New Prompt from Trajectory",
)
require.NoError(t, os.WriteFile(trajPath, []byte(trajectoryJSON), 0o644))
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 2,
Synced: 1,
Skipped: 1,
})
assertSessionMessageCount(t, env.db, "antigravity-cli:"+syncUUID, 1)
msgs = fetchMessages(t, env.db, "antigravity-cli:"+syncUUID)
require.Len(t, msgs, 1)
assert.Equal(t, "New Prompt from Trajectory", msgs[0].Content)
time.Sleep(10 * time.Millisecond)
cutoff := time.Now()
time.Sleep(10 * time.Millisecond)
sinceTrajPath := filepath.Join(convDir, sinceUUID+".trajectory.json")
sinceTrajectoryJSON := antigravityCLISingleUserTrajectory(
sinceUUID, "Prompt from SyncAllSince Trajectory",
)
require.NoError(t, os.WriteFile(
sinceTrajPath, []byte(sinceTrajectoryJSON), 0o644,
))
stats := env.engine.SyncAllSince(context.Background(), cutoff, nil)
require.Equal(t, 1, stats.Synced, "synced = %d, want 1", stats.Synced)
msgs = fetchMessages(t, env.db, "antigravity-cli:"+sinceUUID)
require.Len(t, msgs, 1)
assert.Equal(t, "Prompt from SyncAllSince Trajectory", msgs[0].Content)
}
func TestSyncEngineAntigravityCLI_SyncAllSinceReSyncsDBWalUpdate(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "22222222-3333-4444-5555-777777777777"
sessionID := "antigravity-cli:" + uuid
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
historyLine := `{"conversationId": "` + uuid + `", "workspace": "/home/user/workspace-db-wal", "timestamp": 1716244800000, "display": "History Prompt"}` + "\n"
require.NoError(t, os.WriteFile(filepath.Join(env.antigravityCLIDir, "history.jsonl"), []byte(historyLine), 0o644))
dbPath := filepath.Join(convDir, uuid+".db")
createAntigravityCLIDisplayStepDB(t, dbPath, "Initial database prompt text")
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 1,
Synced: 1,
Skipped: 0,
Anomalies: agyCLIUnknownSchemaAnomaly(1),
})
baseInfo, err := os.Stat(dbPath)
require.NoError(t, err)
baseMtime := baseInfo.ModTime()
time.Sleep(10 * time.Millisecond)
writer := openAntigravityCLITestWALDB(t, dbPath)
defer writer.Close()
insertAntigravityCLIStep(t, writer, 1, 17, "Assistant response from WAL text")
walInfo, err := os.Stat(dbPath + "-wal")
require.NoError(t, err, "expected WAL sidecar after uncheckpointed write")
require.Greater(t, walInfo.ModTime().UnixNano(), baseMtime.UnixNano())
require.NoError(t, os.Chtimes(dbPath, baseMtime, baseMtime))
baseAfter, err := os.Stat(dbPath)
require.NoError(t, err)
require.Equal(t, baseInfo.Size(), baseAfter.Size(), "base DB size changed")
require.Equal(t, baseMtime.UnixNano(), baseAfter.ModTime().UnixNano(),
"base DB mtime should not reveal WAL-only update")
stats := env.engine.SyncAllSince(context.Background(), baseMtime.Add(time.Nanosecond), nil)
assert.Equal(t, 1, stats.TotalSessions)
assert.Equal(t, 1, stats.Synced)
assert.Equal(t, 0, stats.Skipped)
assertSessionMessageCount(t, env.db, sessionID, 2)
msgs := fetchMessages(t, env.db, sessionID)
require.Len(t, msgs, 2)
assert.Equal(t, "Assistant response from WAL text", msgs[0].Content)
assert.Equal(t, "History Prompt", msgs[1].Content)
}
func TestSyncEngineAntigravityCLI_MalformedSidecarFallback(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "55555555-6666-7777-8888-999999999999"
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
historyLine := `{"conversationId": "` + uuid + `", "workspace": "/home/user/workspace-xyz", "timestamp": 1716244800000, "display": "History Prompt"}` + "\n"
require.NoError(t, os.WriteFile(filepath.Join(env.antigravityCLIDir, "history.jsonl"), []byte(historyLine), 0o644))
pbPath := filepath.Join(convDir, uuid+".pb")
require.NoError(t, os.WriteFile(pbPath, []byte("dummy-pb"), 0o644))
// Malformed sidecar
trajPath := filepath.Join(convDir, uuid+".trajectory.json")
require.NoError(t, os.WriteFile(trajPath, []byte("invalid-json{"), 0o644))
// Ingest: Should fall back to reading history.jsonl safely
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 1,
Synced: 1,
Skipped: 0,
})
assertSessionMessageCount(t, env.db, "antigravity-cli:"+uuid, 1)
msgs := fetchMessages(t, env.db, "antigravity-cli:"+uuid)
require.Len(t, msgs, 1)
assert.Equal(t, "History Prompt", msgs[0].Content)
}
func TestSyncEngineAntigravityCLI_DBFallbackRetries(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
malformedUUID := "77777777-8888-9999-aaaa-bbbbbbbbbbbb"
filteredUUID := "88888888-9999-aaaa-bbbb-cccccccccccc"
malformedSessionID := "antigravity-cli:" + malformedUUID
filteredSessionID := "antigravity-cli:" + filteredUUID
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
history := `{"conversationId": "` + malformedUUID + `", "workspace": "/home/user/workspace-db", "timestamp": 1716244800000, "display": "History Prompt"}` + "\n" +
`{"conversationId": "` + filteredUUID + `", "workspace": "/home/user/workspace-db-filtered", "timestamp": 1716244800000, "display": "Filtered History Prompt"}` + "\n"
require.NoError(t, os.WriteFile(
filepath.Join(env.antigravityCLIDir, "history.jsonl"),
[]byte(history), 0o644,
))
malformedDBPath := filepath.Join(convDir, malformedUUID+".db")
require.NoError(t, os.WriteFile(malformedDBPath, []byte("not a sqlite database"), 0o644))
filteredDBPath := filepath.Join(convDir, filteredUUID+".db")
createAntigravityCLIUndisplayableStepDB(t, filteredDBPath)
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 2,
Synced: 2,
Skipped: 0,
Anomalies: agyCLIUnknownSchemaAnomaly(1),
})
assertSessionMessageCount(t, env.db, malformedSessionID, 1)
msgs := fetchMessages(t, env.db, malformedSessionID)
require.Len(t, msgs, 1)
assert.Equal(t, "History Prompt", msgs[0].Content)
assert.Less(t, env.db.GetSessionDataVersion(malformedSessionID), db.CurrentDataVersion(),
"degraded DB fallback should stay stale so unchanged syncs retry")
assertSessionMessageCount(t, env.db, filteredSessionID, 1)
msgs = fetchMessages(t, env.db, filteredSessionID)
require.Len(t, msgs, 1)
assert.Equal(t, "Filtered History Prompt", msgs[0].Content)
assert.Less(t, env.db.GetSessionDataVersion(filteredSessionID), db.CurrentDataVersion(),
"DB fallback after dropping all raw steps should stay stale so unchanged syncs retry")
require.NoError(t, env.db.SetSessionDataVersion(malformedSessionID, db.CurrentDataVersion()))
require.NoError(t, env.db.SetSessionDataVersion(filteredSessionID, db.CurrentDataVersion()))
require.NoError(t, env.db.ResetAllMtimes(), "force fallback rewrite")
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 2,
Synced: 2,
Skipped: 0,
Anomalies: agyCLIUnknownSchemaAnomaly(1),
})
assert.Less(t, env.db.GetSessionDataVersion(malformedSessionID), db.CurrentDataVersion(),
"DB decode fallback should demote previously current rows")
assert.Less(t, env.db.GetSessionDataVersion(filteredSessionID), db.CurrentDataVersion(),
"DB fallback after dropping all raw steps should demote previously current rows")
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 2,
Synced: 2,
Skipped: 0,
Anomalies: agyCLIUnknownSchemaAnomaly(1),
})
assert.Less(t, env.db.GetSessionDataVersion(malformedSessionID), db.CurrentDataVersion(),
"unchanged DB decode fallback should keep retrying")
assert.Less(t, env.db.GetSessionDataVersion(filteredSessionID), db.CurrentDataVersion(),
"unchanged DB fallback after dropping all raw steps should keep retrying")
}
func TestSyncEngineAntigravityCLI_NeedsRetryReplacesCurrentMessages(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
sessionID := "antigravity-cli:" + uuid
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
historyLine := `{"conversationId": "` + uuid + `", "workspace": "/home/user/workspace-db-retry", "timestamp": 1716244800000, "display": "History Prompt"}` + "\n"
require.NoError(t, os.WriteFile(filepath.Join(env.antigravityCLIDir, "history.jsonl"), []byte(historyLine), 0o644))
dbPath := filepath.Join(convDir, uuid+".db")
createAntigravityCLIDisplayStepDB(t, dbPath, "Initial database prompt text")
conn := openAntigravityCLITestDB(t, dbPath)
insertAntigravityCLIStep(t, conn, 1, 17, "Initial assistant response text")
require.NoError(t, conn.Close())
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 1,
Synced: 1,
Skipped: 0,
Anomalies: agyCLIUnknownSchemaAnomaly(1),
})
assertSessionMessageCount(t, env.db, sessionID, 2)
assert.Equal(t, db.CurrentDataVersion(), env.db.GetSessionDataVersion(sessionID))
require.NoError(t, os.WriteFile(dbPath, []byte("not a sqlite database"), 0o644))
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 1,
Synced: 1,
Skipped: 0,
})
assertSessionMessageCount(t, env.db, sessionID, 1)
msgs := fetchMessages(t, env.db, sessionID)
require.Len(t, msgs, 1)
assert.Equal(t, "History Prompt", msgs[0].Content)
assert.Less(t, env.db.GetSessionDataVersion(sessionID), db.CurrentDataVersion(),
"retry fallback should be written before the row is demoted")
}
func TestSyncSingleSessionAntigravityCLI_DBDecodeFallbackRetries(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "99999999-aaaa-bbbb-cccc-dddddddddddd"
sessionID := "antigravity-cli:" + uuid
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
historyLine := `{"conversationId": "` + uuid + `", "workspace": "/home/user/workspace-db-single", "timestamp": 1716244800000, "display": "History Prompt"}` + "\n"
require.NoError(t, os.WriteFile(filepath.Join(env.antigravityCLIDir, "history.jsonl"), []byte(historyLine), 0o644))
dbPath := filepath.Join(convDir, uuid+".db")
require.NoError(t, os.WriteFile(dbPath, []byte("not a sqlite database"), 0o644))
// An explicit single-session sync (the file-watcher path) of a .db
// whose decode fails must store the fallback at a stale data_version
// so a later sync retries the high-resolution source.
require.NoError(t, env.engine.SyncSingleSession(sessionID))
assertSessionMessageCount(t, env.db, sessionID, 1)
msgs := fetchMessages(t, env.db, sessionID)
require.Len(t, msgs, 1)
assert.Equal(t, "History Prompt", msgs[0].Content)
assert.Less(t, env.db.GetSessionDataVersion(sessionID), db.CurrentDataVersion(),
"single-session DB fallback should stay stale so later syncs retry")
// A previously current row must be demoted when an explicit re-sync
// hits the same decode failure, otherwise the high-resolution DB is
// never retried once it has been stamped current.
require.NoError(t, env.db.SetSessionDataVersion(sessionID, db.CurrentDataVersion()))
require.NoError(t, env.db.ResetAllMtimes(), "force fallback rewrite")
require.NoError(t, env.engine.SyncSingleSession(sessionID))
assert.Less(t, env.db.GetSessionDataVersion(sessionID), db.CurrentDataVersion(),
"single-session DB decode fallback should demote previously current rows")
}
// writeAntigravityCLIInferredProjectFixture writes a .db session whose
// history.jsonl row omits conversationId, so the workspace can only reach
// the session row through the GH #579 prompt/timestamp inference fallback.
// The fixture step carries no timestamp, so the parser falls back to the
// .db mtime; pinning it with Chtimes keeps the 60s window deterministic.
func writeAntigravityCLIInferredProjectFixture(
t *testing.T, env *testEnv, uuid, display, workspace string,
dbMtime, rowTime time.Time,
) {
t.Helper()
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
historyLine := fmt.Sprintf(
`{"workspace": %q, "timestamp": %d, "display": %q}`+"\n",
workspace, rowTime.UnixMilli(), display,
)
require.NoError(t, os.WriteFile(
filepath.Join(env.antigravityCLIDir, "history.jsonl"),
[]byte(historyLine), 0o644,
))
dbPath := filepath.Join(convDir, uuid+".db")
createAntigravityCLIDisplayStepDB(t, dbPath, "Fix the flux capacitor BUG")
require.NoError(t, os.Chtimes(dbPath, dbMtime, dbMtime))
}
func TestSyncEngineAntigravityCLI_InferredProjectWithoutConversationID(t *testing.T) {
base := time.UnixMilli(1716244800000)
// Display differs from the stored prompt in case and extra
// leading/trailing/internal whitespace; only the normalized
// (lowercased, whitespace-collapsed) forms match.
const display = " fix THE Flux Capacitor Bug "
const workspace = "/home/user/inferred-project"
tests := []struct {
name string
rowTime time.Time
wantProject string
}{
{
name: "normalized match within window infers project",
rowTime: base.Add(10 * time.Second),
wantProject: workspace,
},
{
name: "match outside 60s window leaves project empty",
rowTime: base.Add(2 * time.Minute),
wantProject: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "ab12cd34-1111-2222-3333-444455556666"
sessionID := "antigravity-cli:" + uuid
writeAntigravityCLIInferredProjectFixture(
t, env, uuid, display, workspace, base, tt.rowTime,
)
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 1,
Synced: 1,
Skipped: 0,
Anomalies: agyCLIUnknownSchemaAnomaly(1),
})
assertSessionProject(t, env.db, sessionID, tt.wantProject)
assertSessionMessageCount(t, env.db, sessionID, 1)
msgs := fetchMessages(t, env.db, sessionID)
require.Len(t, msgs, 1)
assert.Equal(t, "Fix the flux capacitor BUG", msgs[0].Content)
})
}
}
func TestSyncSingleSessionAntigravityCLI_InferredProjectWithoutConversationID(t *testing.T) {
base := time.UnixMilli(1716244800000)
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "cd34ef56-7777-8888-9999-aaaabbbbcccc"
sessionID := "antigravity-cli:" + uuid
writeAntigravityCLIInferredProjectFixture(
t, env, uuid, " fix THE Flux Capacitor Bug ",
"/home/user/inferred-project-single",
base, base.Add(10*time.Second),
)
// The file-watcher path must persist the inferred project too.
require.NoError(t, env.engine.SyncSingleSession(sessionID))
assertSessionProject(t, env.db, sessionID, "/home/user/inferred-project-single")
assertSessionMessageCount(t, env.db, sessionID, 1)
}
func TestSyncPathsAntigravityCLIHistoryOnlyUpdateRefreshesProject(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "de45fa67-8888-9999-aaaa-bbbbccccdddd"
sessionID := "antigravity-cli:" + uuid
early := time.UnixMilli(1716244800000)
late := early.Add(time.Minute)
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
dbPath := filepath.Join(convDir, uuid+".db")
createAntigravityCLIDisplayStepDB(t, dbPath, "History arrives later")
require.NoError(t, os.Chtimes(dbPath, early, early))
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 1,
Synced: 1,
Skipped: 0,
Anomalies: agyCLIUnknownSchemaAnomaly(1),
})
assertSessionProject(t, env.db, sessionID, "")
historyPath := filepath.Join(env.antigravityCLIDir, "history.jsonl")
historyLine := fmt.Sprintf(
`{"conversationId": %q, "workspace": "/home/user/history-arrived", "timestamp": %d, "display": "History arrives later"}`+"\n",
uuid, late.UnixMilli(),
)
require.NoError(t, os.WriteFile(historyPath, []byte(historyLine), 0o644))
require.NoError(t, os.Chtimes(historyPath, late, late))
env.engine.SyncPaths([]string{historyPath})
assertSessionProject(t, env.db, sessionID, "/home/user/history-arrived")
assertSessionMessageCount(t, env.db, sessionID, 1)
}
func TestSyncPathsAntigravityCLIHistoryRetagClearsRemovedProject(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
removedID := "ee45fa67-8888-9999-aaaa-bbbbccccdddd"
retaggedID := "ff45fa67-8888-9999-aaaa-bbbbccccdddd"
removedSessionID := "antigravity-cli:" + removedID
retaggedSessionID := "antigravity-cli:" + retaggedID
base := time.UnixMilli(1716244800000)
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
for id, prompt := range map[string]string{
removedID: "Original history prompt",
retaggedID: "Retagged history prompt",
} {
dbPath := filepath.Join(convDir, id+".db")
createAntigravityCLIDisplayStepDB(t, dbPath, prompt)
require.NoError(t, os.Chtimes(dbPath, base, base))
}
historyPath := filepath.Join(env.antigravityCLIDir, "history.jsonl")
initialHistory := fmt.Sprintf(
`{"conversationId": %q, "workspace": "/home/user/removed", "timestamp": %d, "display": "Original history prompt"}`+"\n",
removedID, base.UnixMilli(),
) + fmt.Sprintf(
`{"conversationId": %q, "workspace": "/home/user/retagged", "timestamp": %d, "display": "Retagged history prompt"}`+"\n",
retaggedID, base.UnixMilli(),
)
require.NoError(t, os.WriteFile(historyPath, []byte(initialHistory), 0o644))
require.NoError(t, os.Chtimes(historyPath, base, base))
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 2,
Synced: 2,
Skipped: 0,
Anomalies: agyCLIUnknownSchemaAnomaly(2),
})
assertSessionProject(t, env.db, removedSessionID, "/home/user/removed")
assertSessionProject(t, env.db, retaggedSessionID, "/home/user/retagged")
updated := base.Add(time.Minute)
retaggedHistory := fmt.Sprintf(
`{"conversationId": %q, "workspace": "/home/user/retagged-now", "timestamp": %d, "display": "Retagged history prompt"}`+"\n",
retaggedID, updated.UnixMilli(),
)
require.NoError(t, os.WriteFile(historyPath, []byte(retaggedHistory), 0o644))
require.NoError(t, os.Chtimes(historyPath, updated, updated))
env.engine.SyncPaths([]string{historyPath})
assertSessionProject(t, env.db, removedSessionID, "")
assertSessionProject(t, env.db, retaggedSessionID, "/home/user/retagged-now")
assertSessionMessageCount(t, env.db, removedSessionID, 1)
assertSessionMessageCount(t, env.db, retaggedSessionID, 1)
}
func TestSyncEngineAntigravityCLI_MissingPbOrphanSidecar(t *testing.T) {
env := setupSingleAgentTestEnv(t, parser.AgentAntigravityCLI)
uuid := "66666666-7777-8888-9999-000000000000"
convDir := filepath.Join(env.antigravityCLIDir, "conversations")
require.NoError(t, os.MkdirAll(convDir, 0o755))
// Write ONLY .trajectory.json, no .pb
trajPath := filepath.Join(convDir, uuid+".trajectory.json")
require.NoError(t, os.WriteFile(trajPath, []byte("{}"), 0o644))
// Sync: should discover no sessions
runSyncAndAssert(t, env.engine, sync.SyncStats{
TotalSessions: 0,
Synced: 0,
Skipped: 0,
})
}
func createAntigravityCLIUndisplayableStepDB(t *testing.T, path string) {
t.Helper()
copyAntigravityCLITestSchemaTemplate(t, path)
conn := openAntigravityCLITestDB(t, path)
defer conn.Close()
insertAntigravityCLIStep(t, conn, 0, 14, "MODEL_PLACEHOLDER_0")
}
func createAntigravityCLIDisplayStepDB(t *testing.T, path, prompt string) {
t.Helper()
copyAntigravityCLITestSchemaTemplate(t, path)
conn := openAntigravityCLITestDB(t, path)
defer conn.Close()
insertAntigravityCLIStep(t, conn, 0, 14, prompt)
}
func antigravityCLISingleUserTrajectory(uuid, prompt string) string {
return fmt.Sprintf(`{
"trajectoryId": %q,
"steps": [
{
"type": "CORTEX_STEP_TYPE_USER_INPUT",
"status": "STATUS_COMPLETED",
"metadata": {
"createdAt": "2026-05-20T22:45:00Z"
},
"userInput": {
"userResponse": %q
}
}
]
}`, uuid, prompt)
}
func copyAntigravityCLITestSchemaTemplate(t *testing.T, path string) {
t.Helper()
copySQLiteSchemaTemplate(
t, path, "antigravity cli", &antigravityCLISchemaOnce,
&antigravityCLISchemaBytes, &antigravityCLISchemaErr,
antigravityCLITestSchema,
)
}
func openAntigravityCLITestDB(t *testing.T, path string) *sql.DB {
t.Helper()
conn, err := sql.Open("sqlite3", path)
require.NoError(t, err, "open antigravity cli test db")
return conn
}
func openAntigravityCLITestWALDB(t *testing.T, path string) *sql.DB {
t.Helper()
conn := openAntigravityCLITestDB(t, path)
_, err := conn.Exec(`PRAGMA journal_mode=WAL`)
require.NoError(t, err, "enable WAL mode")
_, err = conn.Exec(`PRAGMA wal_autocheckpoint=0`)
require.NoError(t, err, "disable WAL autocheckpoint")
return conn
}
func insertAntigravityCLIStep(
t *testing.T, conn *sql.DB, idx, stepType int, content string,
) {
t.Helper()
payload := antigravityCLIStringPayload(content)
_, err := conn.Exec(
`INSERT INTO steps (idx, step_type, step_payload) VALUES (?, ?, ?)`,
idx, stepType, payload,
)
require.NoError(t, err, "insert antigravity cli step")
}
func antigravityCLIStringPayload(s string) []byte {
content := []byte(s)
return append([]byte{0x8a, 0x01, byte(len(content))}, content...)
}