Skip to content

fix(registry): recognize migrated SQLite workspaces — closes #35#89

Merged
Wolfvin merged 1 commit into
mainfrom
fix/35-registry-exists-sqlite
Jun 29, 2026
Merged

fix(registry): recognize migrated SQLite workspaces — closes #35#89
Wolfvin merged 1 commit into
mainfrom
fix/35-registry-exists-sqlite

Conversation

@Wolfvin

@Wolfvin Wolfvin commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Fix

Closes #35

Problem

_registry_exists(workspace) in scripts/codelens.py only checked for .codelens/backend.json or .codelens/frontend.json. A workspace that had run migrate (JSON → SQLite) and then deleted the JSON files was always treated as having no registry — triggering init + scan on every subsequent command and discarding the migrated SQLite data.

This was compounded by a second bug: cmd_migrate early-returned whenever codelens.db existed (even if empty), because scan now creates that file via store_scan_result (issue #31). So migrate silently no-op'd and never actually copied JSON → SQLite.

What changed

Three coupled changes, all gated on a new db_is_populated(db_path) helper in scripts/persistent_registry.py:

  1. codelens._registry_exists — path 1 (JSON check) is unchanged; new path 2 returns True when codelens.db exists AND the symbols table has ≥1 row. Empty/corrupt dbs still return False so auto-setup runs (constraint: db kosong/corrupt tidak salah dianggap registry valid).

  2. commands.migrate.cmd_migrate — only skips re-migration when the db is actually populated (was: os.path.exists). Without this, the empty db shell created by store_scan_result blocked the real JSON→SQLite migration.

  3. registry.load_frontend_registry / load_backend_registry — added SQLite fallback. When JSON is missing, load from the analysis_cache rows written by PersistentRegistry.store_{frontend,backend}_registry during migrate. Makes the migrated workspace actually usable (padahal data lengkap sudah ada di codelens.db).

The helper is shared between (1) and (2) — no dead code, no duplication.

Backward compatibility

  • JSON-only workspaces (no migrate) still work — path 1 returns True before any SQLite check, so pre-migration behavior is unchanged.
  • Empty/corrupt SQLite dbs are NOT treated as valid — auto-setup still runs, so users aren't stuck with a broken registry.
  • migrate still skips when the db is genuinely populated (the old "already exists" behavior, just with a stricter condition).

Files changed

File Change
scripts/codelens.py _registry_exists: add SQLite path 2 (populated db check)
scripts/persistent_registry.py Add db_is_populated(db_path) helper
scripts/commands/migrate.py cmd_migrate: use db_is_populated for skip check
scripts/registry.py load_{frontend,backend}_registry: SQLite fallback when JSON missing
tests/test_cli.py 5 new regression tests
tests/test_persistent_registry.py Update test_migrate_already_exists to populate db first

Manual repro (issue #35)

WS=/tmp/demo
mkdir -p $WS/src
# (create sample Rust + HTML files)
python3 scripts/codelens.py init $WS
python3 scripts/codelens.py scan $WS
python3 scripts/codelens.py migrate $WS
rm $WS/.codelens/backend.json $WS/.codelens/frontend.json
python3 scripts/codelens.py query "main" $WS

Before fix: query printed [CodeLens] No registry found. Auto-running init + scan... and re-scanned from scratch.

After fix:

STEP 2: scan
  sqlite_persisted: true
  --- DB state after scan ---
  symbols: 0          ← scan only writes to analysis_cache, not symbols
  analysis_cache: 1

STEP 3: migrate
  "message": "Migration from JSON to SQLite completed successfully"
  "migration": { "frontend_classes": 4, "frontend_ids": 2, "backend_nodes": 3 }
  --- DB state after migrate ---
  symbols: 9          ← migrate actually ran (no early-return on empty db)
  refs: 19
  files: 2

STEP 4: delete JSON files
  removed 'backend.json'
  removed 'frontend.json'

STEP 5: query "main"
  {
    "status": "ok",
    "found": true,                  ← reads from SQLite via fallback
    "type": "function",
    "domain": "backend",
    "node": { "fn": "main", "file": "src/main.rs", "line": 1, ... }
  }

RESULT: PASS — 'Auto-running init + scan...' does NOT appear

Test run

$ PYTHONPATH=scripts python3 -m pytest tests/test_cli.py tests/test_persistent_registry.py tests/test_persistent_registry_extra.py tests/test_command_registry.py tests/test_version_consistency.py -v

============================= test session starts ==============================
collected 83 items

tests/test_cli.py .......................                                [ 27%]
tests/test_persistent_registry.py .............................          [ 62%]
tests/test_persistent_registry_extra.py ..........................       [ 93%]
tests/test_command_registry.py ..                                        [ 96%]
tests/test_version_consistency.py ...                                    [100%]

============================== 83 passed in 1.83s ==============================

New regression tests in tests/test_cli.py::TestRegistryExistsSqlite:

  • test_registry_exists_after_migrate_with_json_deleted — the exact issue [BUG-07] _registry_exists() ignores SQLite — spurious auto-setup after migrate #35 repro: migrate → delete JSON → _registry_exists() returns True
  • test_registry_exists_false_for_empty_db — empty db (0 symbols) returns False (constraint: db kosong tidak valid)
  • test_registry_exists_false_for_corrupt_db — corrupt db file returns False (constraint: db corrupt tidak valid)
  • test_registry_exists_true_for_json_only_workspace — JSON-only workspace (no migrate) still returns True (backward compat)
  • test_query_uses_sqlite_fallback_after_json_deleted — end-to-end: query returns real data from SQLite after JSON deletion

Note on pre-existing test failures

The full suite (python3 -m pytest tests/) has pre-existing failures in test_architecture.py, test_graph_model.py, test_compact_format.py, test_hybrid_type_resolver.py, and test_integration.py::TestAllCommandsJSON::test_no_args_command_json[scan] (OOM/rc=137). These failures exist on main without my changes (verified via git stash + re-run) and are unrelated to issue #35. All tests touching the changed code pass.

## Problem

_registry_exists(workspace) in scripts/codelens.py only checked for
.codelens/backend.json or .codelens/frontend.json. A workspace that
had run migrate (JSON to SQLite) and then deleted the JSON files was
always treated as having no registry — triggering init + scan on every
subsequent command and discarding the migrated SQLite data.

This was compounded by a second bug: cmd_migrate early-returned
whenever codelens.db existed (even if empty), because scan now
creates that file via store_scan_result (issue #31). So migrate
silently no-op'd and never actually copied JSON into SQLite.

## Fix

Three coupled changes, all gated on a new db_is_populated(db_path)
helper in scripts/persistent_registry.py:

1. codelens._registry_exists — path 1 (JSON check) is unchanged; new
   path 2 returns True when codelens.db exists AND the symbols table
   has at least 1 row. Empty/corrupt dbs still return False so
   auto-setup runs (constraint: db kosong/corrupt tidak salah dianggap
   registry valid).

2. commands.migrate.cmd_migrate — only skips re-migration when the db
   is actually populated (was: os.path.exists). Without this, the
   empty db shell created by store_scan_result blocked the real
   JSON-to-SQLite migration.

3. registry.load_frontend_registry / load_backend_registry — added
   SQLite fallback. When JSON is missing, load from the analysis_cache
   rows written by PersistentRegistry.store_frontend_registry /
   store_backend_registry during migrate. Makes the migrated workspace
   actually usable (padahal data lengkap sudah ada di codelens.db).

The helper is shared between (1) and (2) — no dead code, no duplication.

## Backward compatibility

- JSON-only workspaces (no migrate) still work — path 1 returns True
  before any SQLite check, so pre-migration behavior is unchanged.
- Empty/corrupt SQLite dbs are NOT treated as valid — auto-setup still
  runs, so users aren't stuck with a broken registry.
- migrate still skips when the db is genuinely populated (the old
  'already exists' behavior, just with a stricter condition).

## Files changed

- scripts/codelens.py              — _registry_exists: add SQLite path
- scripts/persistent_registry.py   — add db_is_populated() helper
- scripts/commands/migrate.py      — use db_is_populated for skip check
- scripts/registry.py              — SQLite fallback in load_*_registry
- tests/test_cli.py                — 5 new regression tests
- tests/test_persistent_registry.py — update test_migrate_already_exists

Closes #35
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Wolfvin Wolfvin merged commit 7334186 into main Jun 29, 2026
2 of 8 checks passed
@Wolfvin Wolfvin deleted the fix/35-registry-exists-sqlite branch June 29, 2026 17:06
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG-07] _registry_exists() ignores SQLite — spurious auto-setup after migrate

1 participant