Session Resync Process and Data Version Migration in Agentsview: A Complete Guide

Agentsview detects stale SQLite databases using a built-in dataVersion constant and executes an atomic full-database rebuild via Engine.ResyncAll to migrate data forward without losing user-generated metadata or orphaned sessions.

Agentsview is an open-source session analytics platform that persists parsed agent data in a local SQLite database. When parser logic evolves—adding columns, altering field formats, or extracting new metadata—the existing database schema becomes incompatible. This guide explains the session resync process and data version migration in agentsview, detailing how the system detects version mismatches and safely rebuilds the database while preserving user insights, trashed items, and orphaned historical data.

How Data Version Migration Works

Agentsview tracks schema compatibility through a data version system defined in internal/db/db.go. The current constant is set to 41, and each increment represents a breaking change in parser output that requires existing data to be reprocessed.

// dataVersion tracks parser changes that require a full
// re-sync. Increment this when parsing logic changes in ways
// that affect stored data …
const dataVersion = 41

When the database opens via db.Open(), the probeDatabase function queries SQLite’s PRAGMA user_version and compares it against the built-in dataVersion constant. If the stored version is lower, needsDataResync sets db.dataStale = true and emits a log entry: data version outdated; full resync required. Applications can check this state explicitly by calling db.NeedsResync(), which returns the stale flag.

Once flagged, the migration path requires a full resync. The sync engine (internal/sync/engine.go) reads this flag and triggers Engine.ResyncAll, which constructs a fresh database with the updated schema, bulk-reimports all source files, and atomically swaps the old file for the new one.

The Session Resync Architecture

The ResyncAll method implements a copy-on-write migration strategy. Rather than modifying the existing database in place—which risks corruption during schema changes—the engine builds a temporary database file, populates it from source data, copies over user-managed metadata, and performs an atomic file rename. This approach ensures that the original database remains intact until the very last moment, allowing safe rollback if the process aborts.

Key safety mechanisms include abort-swap logic that cancels the migration if the sync discovers no files or fails on more sessions than it succeeds, and WAL file cleanup that removes SQLite write-ahead logs after the swap to prevent version conflicts.

Step-by-Step Breakdown of ResyncAll

The ResyncAll function in internal/sync/engine.go executes a 15-phase pipeline to ensure zero data loss during migration.

Phase 1: Preparation and Isolation

The process begins by creating a temporary database at origPath + "-resync" and removing any stale temporary files from previous attempts. The engine saves the in-memory skipCache—which tracks files to ignore during incremental syncs—and clears it so the new database starts with a clean state.

removeTempDB(tempPath)
savedSkipCache := e.skipCache
newDB, err := db.Open(tempPath)

Phase 2: Preservation of User Data

Before bulk loading begins, the engine copies user-managed data that cannot be reconstructed from source files:

  • Excluded and trashed sessions: CopyExcludedSessionsFrom and CopyTrashedDataFrom preserve permanently deleted or user-trashed items so they do not reappear after the rebuild.
  • Orphaned sessions: CopyOrphanedDataFromExcluding copies sessions whose source files have vanished, ensuring archived data survives the migration.
  • Insights and metadata: CopyInsightsFrom transfers user-generated notes, tags, and stars, while CopySessionMetadataFrom restores display names, soft-deletes, pins, and other UI state.

Phase 3: Bulk Data Loading

To maximize throughput, the engine drops FTS5 (Full-Text Search) tables temporarily to avoid per-row trigger costs during bulk insertion. It then redirects the sync engine to the temporary database and invokes syncAllLocked with the syncWriteBulk strategy, parsing all session files and inserting them in large batches.

newDB.DropFTS()
stats = e.syncAllLocked(ctx, newDB, syncWriteBulk)

Phase 4: Reconstruction and Validation

After bulk loading, the engine runs several backfill operations:

  • Sub-agent relinking: LinkSubagentSessions reconnects child sessions that may have been disjointed during the orphan copy phase.
  • Automated flag backfill: ForceBackfillIsAutomated recomputes is_automated flags for orphaned rows using the current classifier hash, ensuring stale automation markers are corrected.
  • FTS rebuild: If FTS was dropped earlier, RebuildFTS reconstructs and indexes the search tables.

Phase 5: Atomic Swap and Finalization

The engine performs safety checks to ensure the sync succeeded (more successes than failures, and at least some files processed). It then closes connections to the original database and executes an atomic rename:

os.Rename(tempPath, origPath)

This overwrites the old database file with the new one. The engine removes WAL files to prevent SQLite version mismatches, reopens the handle pointing to the new file, and persists the saved skipCache into the fresh database. Finally, it notifies any listeners via an optional Emitter that the full resync has completed.

Practical Implementation Examples

Detecting a Required Resync

Use the NeedsResync method to check database compatibility before running operations:

db, err := db.Open("path/to/agentsview.db")
if err != nil {
    log.Fatalf("open db: %v", err)
}
if db.NeedsResync() {
    fmt.Println("Database schema out-of-date – full resync required.")
}

Source: db.NeedsResync implementation in internal/db/db.go (lines 38-44).

Triggering a Programmatic Resync

Execute a blocking resync with progress callbacks:

engine := sync.NewEngine(db, sync.EngineConfig{
    AgentDirs: map[parser.AgentType][]string{ /* … */ },
    Machine:   "my-host",
})

stats := engine.ResyncAll(context.Background(), func(p sync.Progress) {
    log.Printf("Resync progress: %d/%d files", p.Done, p.Total)
})

log.Printf("Resync complete – %d synced, %d failed", stats.Synced, stats.Failed)

Source: Engine.ResyncAll signature in internal/sync/engine.go (lines 66-78).

Incrementing the Data Version

When modifying parsers in internal/parser/types.go or altering output formats, increment the constant in internal/db/db.go:

const dataVersion = 42 // Added session_name field extraction

Recompiling the binary causes existing databases to set dataStale = true on next open, automatically queueing a resync.

Summary

  • Version Detection: Agentsview stores a dataVersion constant (currently 41) in internal/db/db.go and compares it against SQLite’s PRAGMA user_version via probeDatabase to detect stale schemas.
  • Atomic Migration: The ResyncAll method in internal/sync/engine.go builds a temporary database, bulk-loads source data, and performs an atomic file swap to minimize downtime and risk.
  • Data Preservation: The process explicitly copies excluded sessions, trashed data, orphaned rows, user insights, and session metadata to prevent data loss during rebuilds.
  • Performance Optimization: FTS tables are dropped during bulk insertion and rebuilt afterward to avoid trigger overhead.
  • Safety Mechanisms: Abort-swap logic cancels the migration if sync failures exceed successes, ensuring the original database remains untouched if the process fails.

Frequently Asked Questions

What triggers a data version migration in Agentsview?

A migration triggers when the dataVersion constant in internal/db/db.go is incremented by developers to reflect breaking parser changes. When users open an existing database with a newer binary, probeDatabase detects that the SQLite PRAGMA user_version is lower than the constant, setting db.dataStale = true and requiring a full resync to rebuild the schema.

Is user data lost during the session resync process?

No. The ResyncAll pipeline explicitly preserves user-managed data through dedicated copy operations: CopyExcludedSessionsFrom, CopyTrashedDataFrom, CopyOrphanedDataFromExcluding, CopyInsightsFrom, and CopySessionMetadataFrom ensure that trashed items, orphaned historical sessions, notes, tags, and UI state survive the atomic swap intact.

How does Agentsview handle resync failures?

The engine implements abort-swap safety checks in internal/sync/engine.go (lines 1620-1640). If the sync process discovers no files, is manually aborted, or fails on more sessions than it successfully processes, the temporary database is discarded and the original file remains unchanged, preventing data corruption or loss.

Can I manually trigger a session resync?

Yes. While the sync engine automatically checks db.NeedsResync() during initialization, you can force a resync programmatically by calling engine.ResyncAll(ctx, progressCallback), or via the CLI using agentsview pg resync (if implemented in your build). This rebuilds the database immediately rather than waiting for the next automatic sync cycle.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →