Expand description
One-shot SQLite → file-registry migration for named instances.
Runs at most once per <registry_root>/.migrated_from_sqlite marker;
idempotent and read-only on every SQLite it touches.
P0.3 re-roots the registry to the GLOBAL ~/.agentmux/shared/agents/ registry/, so this scan is generalized from “the current channel’s
per-version DBs” to every channel and every dev branch on the machine:
<home>/channels/<ch>/versions/<v>/data/db/objects.db (installed/portable)
<home>/dev/<branch>[/<sub>]/data/db/objects.db (dev)Workspace anchoring. Agent workspaces live GLOBALLY at
<home>/agents/<name> (verified on disk: real working_directory values
are ~/.agentmux/agents/<name>, e.g. …/agents/mazs-0527n), independent of
channel/version. row_to_record therefore strips each row’s
working_directory against the global <home>/agents root FIRST, then
falls back to this row’s own source-channel agents dir (channels/<ch>/agents,
or <instance_dir>/agents for dev) for any legacy row that genuinely lived
in-channel — each source DB still carries that per-source dir. The two
subtrees are disjoint, so the fallback never mis-maps a global workspace.
The chosen base is stored absolute in the record, so a reader in ANY channel
round-trips source_agents_base.join(working_dir) back to the real
workspace. See docs/specs/SPEC_CROSS_CHANNEL_AGENT_PERSISTENCE_2026-06-13.md §11.5.
Structs§
- Migrate
Stats - Outcome stats — surfaced in the marker file + the srv log.
- RowSnapshot 🔒
- Source
Backfill Stats - Outcome of
backfill_source_bases_once. - Sqlite
Source 🔒 - A per-(channel,version) / per-dev-branch SQLite source, paired with this
source’s own agents dir — the per-channel fallback anchor
row_to_recorduses when a row’s (normally global)working_directoryisn’t under the primary<home>/agentsroot.
Constants§
- MARKER 🔒
- Marker filename. Lives in the registry root so the registry’s existence implies the migration question has been asked at least once.
- MIGRATION_
VERSION 🔒 - Bumped when the migration’s mapping logic changes in a way that must re-run
on registries an older build already finalized. v2 fixes the workspace
anchor: agent workspaces live globally at
<home>/agents/<name>, but v1 strippedworking_directoryagainst the per-channelchannels/<ch>/agentsdir, so every global workspace came back “unmappable” (row_to_recordreturnedNone) and “My Agents” stayed empty in every channel. A legacy marker (nomigration_version:line) reads as 0 and re-runs exactly once;exists_anywhere()keeps the re-run from duplicating already-written records. - SOURCE_
BACKFILL_ 🔒MARKER - Marker for the one-shot
source_agents_basebackfill. Separate fromMARKERso it runs exactly once even on registries the main migration already finalized before schema v3 existed.
Functions§
- backfill_
source_ bases_ once - One-shot backfill of
source_agents_baseonto registry records written before schema v3 — i.e. by P0.3b’s global migration (#1389) or any pre-P0.4 live mirror. - collect_
dev_ 🔒sources - Locate dev instance dirs (those with
data/db/objects.db) and anchor each on its siblingagents/dir. The dev layout is at most two levels underdev/:dev/<branch>/data/...(older single-level layout) ordev/<branch>/<sub-hash>/data/.... We check exactly those depths and never descend into an instance’s own subdirs — so an agent workspace (<instance>/agents/<slug>/) that holds a nested AgentMuxobjects.dbis never mistaken for a source (reagent P2), and a dev branch whose slug happens to equal an internal dir name likedata/agentsis still scanned (no name-based skip-list — codex P2). - empty_
to_ 🔒none - enumerate_
sources 🔒 - Enumerate every per-(channel,version) and per-dev-branch
objects.dbunderhome, pairing each with its own agents dir (the per-source fallback anchor; the primary anchor inrow_to_recordis the global<home>/agents). - is_
missing_ 🔒column_ or_ table - True iff the error is SQLite reporting “this column/table doesn’t exist in this DB’s schema.” Distinguishes a pre-v8 DB (skip silently — those agents weren’t named, so wouldn’t appear in the dropdown anyway) from corruption (caller logs + continues + defers the marker).
- marker_
migration_ 🔒version - Read the
migration_version:line from an existing marker. Returns 0 when the marker is absent, unreadable, or predates versioning (a legacy stats-only marker has no such line) — so a logic bump, or any pre-versioning marker, re-runs the migration exactly once. - migrate_
from_ sqlite_ once - Scan every channel + dev
objects.dbunderhomeand populate the shared registry. Skipped if the marker file exists. Never overwrites an existing registry record (idempotency + respect for newer-written data). The SQLite files are opened read-only — never modified. - push_
if_ 🔒instance - Push
diras a source iff it holdsdata/db/objects.db. Returns whether it did (so the caller can treat an instance dir as a leaf). - read_
dir_ 🔒tracking read_dirthat distinguishes “absent” (fine — nothing to scan) from “present but unreadable” (setsincompleteso the marker defers). ReturnsNonein both error cases; the iterator otherwise.- read_
named_ 🔒rows - row_
to_ 🔒record - write_
marker 🔒