v3.0.0-rc.1 Cannot Detect or Migrate v2 Estates

Bug Report: v3.0.0-rc.1 Cannot Detect or Migrate v2 Estates

Summary

synthesist v3.0.0-rc.1 fails to detect v2 Automerge-based estates, reporting them as "fresh" instead of "2.x". This prevents all v2-to-v3 migration workflows from functioning. The issue occurs regardless of estate structure (compacted vs. with incremental changes) or directory layout (isolated vs. in-repo).

Environment

  • v3 binary: v3.0.0-rc.1 (darwin-arm64)
  • v2 binary tested: v2.5.2 (darwin-arm64, for comparison)
  • Platform: macOS (Apple Silicon)
  • Estate size: ~9530 claims, 16 trees, 19 sessions (production-scale)

Expected Behavior

When synthesist v3.0.0-rc.1 encounters a v2 estate (containing genesis.amc, snapshot.amc, and config.toml with schema_version = "0.1"), it should:

  1. synthesist migrate status → report "current_version": "2.x" with pending migrations
  2. synthesist migrate v2-to-v3 --dry-run → plan the migration without error
  3. synthesist migrate run → execute the v2.x-to-v3.0.0-pre.1 migration
  4. synthesist status → read v2 claims (at least in read-only mode, if not full migration)

Actual Behavior

v3 reports the v2 estate as "fresh" (v3 native) in all scenarios:

$ synthesist migrate status
{"current_version":"fresh","migrated_at":null,"pending":[],"status":"store is at latest"}

$ synthesist migrate v2-to-v3 --dry-run
error: no applicable migration: store is fresh -- no migration needed. 
Run `synthesist migrate list` to see available migrations, or `synthesist migrate status` 
to check the current schema version.

$ synthesist status
{"claim_counts":{},"ready_tasks":[],"sessions":[],"total_claims":0,"trees":[]}

Impact: All v2-to-v3 migration paths are blocked. Users cannot migrate production estates.

Reproduction

We tested three different scenarios. All exhibit the same failure:

Scenario 1: Compacted v2 Estate (Isolated Directory)

# Copy production v2 estate (compacted, no changes/ directory)
mkdir -p ~/tmp/synthesist-v3-test
cp -r /path/to/v2/estate/claims ~/tmp/synthesist-v3-test/

# Verify v2.5.2 can read it
~/.local/share/mise/installs/http-synthesist/2.5.2/synthesist \
  --data-dir ~/tmp/synthesist-v3-test status
# → {"total_claims":9530,"trees":[...],"sessions":[...]}  ✓ Works

# Attempt v3 migration
synthesist --data-dir ~/tmp/synthesist-v3-test migrate status
# → {"current_version":"fresh",...}  ✗ Broken

Estate structure:

claims/
├── config.toml          # schema_version = "0.1"
├── genesis.amc          # 122 bytes
└── snapshot.amc         # 2.4 MB (compacted)

Scenario 2: v2 Estate with Incremental Changes

# Reconstruct pre-compaction v2 estate from git history
# (estate with 150 incremental .amc files in claims/changes/)
mkdir -p ~/tmp/v2-pre-compact/claims/changes
# ... extract genesis.amc, snapshot.amc, config.toml, and changes/*.amc from git ...

# Verify structure
ls ~/tmp/v2-pre-compact/claims/
# config.toml  genesis.amc  snapshot.amc  changes/

ls ~/tmp/v2-pre-compact/claims/changes/ | wc -l
# 150

# Attempt v3 migration
synthesist --data-dir ~/tmp/v2-pre-compact migrate v2-to-v3 --dry-run
# → error: no applicable migration: store is fresh

Estate structure:

claims/
├── changes/
│   ├── 06c03c5a...aaeb0.amc  # 7.9 MB
│   ├── 095db27c...c0ec7f.amc # 8.7 MB
│   └── ... (148 more)
├── config.toml
├── genesis.amc
└── snapshot.amc

Scenario 3: In-Repo Worktree

# Create git worktree with v3 binary, pointing at v2 estate
cd /path/to/repo
git worktree add .worktrees/v3-test -b v3-test

cd .worktrees/v3-test
synthesist migrate status
# → {"current_version":"fresh",...}  ✗ Broken

# Verify v2.5.2 still works in same location
~/.local/share/mise/installs/http-synthesist/2.5.2/synthesist status
# → {"total_claims":9530,...}  ✓ Works

Additional Evidence

Export/Import Path Also Fails

# Export from v2
~/.local/share/mise/installs/http-synthesist/2.5.2/synthesist \
  --data-dir ~/tmp/synthesist-v3-test export > v2-export.json

ls -lh v2-export.json
# -rw-r--r-- 14M v2-export.json

# Import into fresh v3 estate
mkdir -p ~/tmp/v3-import && cd ~/tmp/v3-import
synthesist init
synthesist import ../synthesist-v3-test/v2-export.json
# → {"imported":0,"skipped":9530}

synthesist status
# → {"total_claims":0,...}

v3 skips all 9530 claims from the v2 export without error, resulting in an empty estate.

Version Detection Always Returns "fresh"

Even with all v2 artifacts present, migrate status never reports "current_version": "2.x":

# Fresh v3 init (expected)
synthesist init && synthesist migrate status
# → "current_version":"fresh"  ✓ Correct

# v2 compacted estate (unexpected)
synthesist --data-dir ~/tmp/synthesist-v3-test migrate status
# → "current_version":"fresh"  ✗ Wrong (should be "2.x")

# v2 estate with changes/ (unexpected)
synthesist --data-dir ~/tmp/v2-pre-compact migrate status
# → "current_version":"fresh"  ✗ Wrong (should be "2.x")

Analysis

v3's version detection logic does not appear to check for v2 indicators:

  1. Does not check config.toml: The file contains schema_version = "0.1" (v2 format), but v3 ignores it
  2. Does not detect .amc files: Presence of genesis.amc, snapshot.amc, or changes/*.amc is not recognized as v2
  3. Defaults to "fresh": When no v3-specific markers (e.g., claims/_schema.json, claims/*/log.jsonl) are found, v3 assumes a fresh estate rather than checking for v2 files

Expected Detection Logic

v3 should detect v2 estates by checking for:

  • claims/config.toml with schema_version = "0.1" (v2 marker), OR
  • Presence of claims/genesis.amc + claims/snapshot.amc (Automerge files), OR
  • claims/changes/*.amc (incremental change files)

If any of these are found, migrate status should return "current_version": "2.x" and make the v2.x-to-v3.0.0-pre.1 migration available.

Registered Migration

v3 knows about the v2-to-v3 migration:

$ synthesist migrate list
{
  "migrations": [{
    "description": "Translate v2 Automerge .amc claims to v3 per-asserter JSON-LD logs",
    "from_version": "2.x",
    "name": "v2.x-to-v3.0.0-pre.1",
    "to_version": "3.0.0-pre.1"
  }]
}

But it never triggers because version detection fails to identify v2 estates as "2.x".

Impact

Severity: Blocker for v3 adoption

  • Users with production v2 estates cannot migrate to v3
  • The RC documentation describes v2-to-v3 migration as a supported path, but it is non-functional
  • No workaround exists (export/import path also fails)
  • All v3 features are inaccessible to existing v2 users until this is fixed

Update synthesist migrate status and the migration runner's version detection to:

  1. Check for claims/config.toml and parse schema_version field
  2. If schema_version = "0.1", report "current_version": "2.x"
  3. If .amc files exist but no config.toml, infer v2 format
  4. Only default to "fresh" if no v2 or v3 markers are found

This would unblock the registered v2.x-to-v3.0.0-pre.1 migration and allow the migrate run / migrate v2-to-v3 commands to execute.

Test Case for Validation

Once fixed, the following should succeed:

# Given: a v2 estate with config.toml (schema_version = "0.1"), genesis.amc, snapshot.amc
synthesist migrate status
# → "current_version": "2.x", pending: ["v2.x-to-v3.0.0-pre.1"]

synthesist migrate v2-to-v3 --dry-run
# → plan output (no error)

synthesist migrate v2-to-v3
# → successful migration, estate converted to v3 format

synthesist migrate status
# → "current_version": "3.0.0-pre.1" or later

Reporter Context

We're testing v3.0.0-rc.1 for production adoption in a zeel-dev monorepo with 9530 claims accumulated over 2+ years. We performed isolated testing (Phase 1) in temporary directories and worktrees to avoid affecting the production estate. Happy to provide additional logs, estate samples, or test any patches.

Tested binaries:

  • v2.5.2: ~/.local/share/mise/installs/http-synthesist/2.5.2/synthesist (checksum: blake3:b1ac479e95...)
  • v3.0.0-rc.1: mise-installed from GitLab package generic/synthesist/v3.0.0-rc.1/synthesist-darwin-arm64 (checksum: blake3:21c1d7a22e...)