Skip to main content

projectatlas_db/
worktree_registry.rs

1//! Durable `ProjectAtlas` worktree registrations owned by one control atlas.
2
3use crate::{
4    AtlasStore, DbError, DbResult, WorktreeUsageSnapshot, WorktreeUsageSyncState, telemetry,
5};
6use projectatlas_core::{
7    CanonicalProjectRoot, MAX_GIT_WORKTREE_REGISTRATIONS, graph::ProjectInstanceId,
8};
9use rusqlite::{Connection, OptionalExtension, Row, params};
10use std::fmt;
11use std::path::Path;
12
13/// Reserved alias for the selected control checkout.
14pub const MAIN_WORKTREE_ALIAS: &str = "main";
15/// Maximum bytes admitted for one worktree alias.
16pub const MAX_WORKTREE_ALIAS_BYTES: usize = 64;
17/// Maximum normalized bytes stored for one worktree identity path.
18const MAX_WORKTREE_REGISTRATION_PATH_BYTES: usize = 128 * 1_024;
19/// Lowercase hexadecimal bytes in one opaque Git administrative identity.
20const GIT_ADMINISTRATIVE_IDENTITY_BYTES: usize = 64;
21
22/// Validated short selector for one registered non-control worktree.
23#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
24pub struct WorktreeAlias(String);
25
26impl WorktreeAlias {
27    /// Validate one caller-supplied worktree alias.
28    ///
29    /// # Errors
30    ///
31    /// Returns an error for the reserved `main` alias, excessive length, or
32    /// characters outside lowercase ASCII letters, digits, `.`, `_`, and `-`.
33    pub fn parse(value: &str) -> DbResult<Self> {
34        if value.is_empty() {
35            return invalid_alias(value, "alias is empty");
36        }
37        if value.len() > MAX_WORKTREE_ALIAS_BYTES {
38            return invalid_alias(value, "alias exceeds 64 UTF-8 bytes");
39        }
40        if value == MAIN_WORKTREE_ALIAS {
41            return invalid_alias(value, "main is reserved for the control atlas");
42        }
43        let mut bytes = value.bytes();
44        let first = bytes.next().ok_or_else(|| DbError::InvalidWorktreeAlias {
45            alias: value.to_string(),
46            reason: "alias is empty",
47        })?;
48        if !first.is_ascii_lowercase() && !first.is_ascii_digit() {
49            return invalid_alias(value, "alias must start with a lowercase letter or digit");
50        }
51        if !bytes.all(|byte| {
52            byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'.' | b'_' | b'-')
53        }) {
54            return invalid_alias(value, "alias contains unsupported characters");
55        }
56        Ok(Self(value.to_string()))
57    }
58
59    /// Borrow the normalized serialized alias.
60    #[must_use]
61    pub fn as_str(&self) -> &str {
62        &self.0
63    }
64}
65
66impl fmt::Display for WorktreeAlias {
67    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
68        formatter.write_str(self.as_str())
69    }
70}
71
72/// Durable registration lifecycle state.
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74pub enum WorktreeRegistrationState {
75    /// The alias may resolve source operations.
76    Active,
77    /// The alias no longer resolves, while historical aggregate state remains.
78    Retired,
79}
80
81impl WorktreeRegistrationState {
82    /// Return the stable `SQLite` representation.
83    #[must_use]
84    pub const fn as_str(self) -> &'static str {
85        match self {
86            Self::Active => "active",
87            Self::Retired => "retired",
88        }
89    }
90
91    /// Parse a stable `SQLite` representation.
92    fn parse(value: &str) -> DbResult<Self> {
93        match value {
94            "active" => Ok(Self::Active),
95            "retired" => Ok(Self::Retired),
96            _ => Err(DbError::WorktreeRegistrationRow {
97                reason: "unknown registration state",
98            }),
99        }
100    }
101}
102
103/// One active or retired `ProjectAtlas` worktree registration.
104#[derive(Clone, Debug, Eq, PartialEq)]
105pub struct WorktreeRegistration {
106    /// Stable control-database row identity.
107    pub registration_id: i64,
108    /// Human/agent-facing short selector.
109    pub alias: WorktreeAlias,
110    /// Whether the selector remains active.
111    pub state: WorktreeRegistrationState,
112    /// UTF-8 compatibility projection of the Git common directory. The native
113    /// identity field beside it is authoritative for routing and persistence.
114    pub git_common_directory: String,
115    /// Lossless native identity authority for the Git common directory.
116    pub git_common_directory_identity: CanonicalProjectRoot,
117    /// UTF-8 compatibility projection of the administrative directory. The
118    /// native identity field beside it is authoritative.
119    pub git_administrative_directory: String,
120    /// Lossless native identity authority for the administrative directory.
121    pub git_administrative_directory_identity: CanonicalProjectRoot,
122    /// Opaque identity for the current administrative-directory lifecycle.
123    pub git_administrative_identity: String,
124    /// UTF-8 compatibility projection of the last source root. The native
125    /// identity field beside it is authoritative.
126    pub last_root: String,
127    /// Lossless native identity authority for the source root.
128    pub last_root_identity: CanonicalProjectRoot,
129    /// Exact worktree atlas identity after initialization.
130    pub project_instance_id: Option<ProjectInstanceId>,
131    /// Last local aggregate revision accepted by the control atlas.
132    pub accepted_telemetry_revision: u64,
133    /// Creation time as Unix epoch seconds.
134    pub created_at_epoch: u64,
135    /// Retirement time as Unix epoch seconds.
136    pub retired_at_epoch: Option<u64>,
137}
138
139/// Transaction-owned capability for one exact active worktree registration.
140///
141/// Construction is restricted to [`AtlasStore::with_active_worktree_registration`]
142/// so lifecycle-sensitive callers can validate external state and publish one
143/// bind or retirement under the same control-catalog writer exclusion.
144pub struct ActiveWorktreeRegistrationGuard<'transaction> {
145    /// Connection currently owned by the outer validated write transaction.
146    connection: &'transaction Connection,
147    /// Exact active row reloaded after writer exclusion was acquired.
148    registration: WorktreeRegistration,
149}
150
151impl ActiveWorktreeRegistrationGuard<'_> {
152    /// Borrow the exact active row reloaded by the transaction.
153    #[must_use]
154    pub const fn registration(&self) -> &WorktreeRegistration {
155        &self.registration
156    }
157
158    /// Bind the exact initialized project inside this transaction.
159    ///
160    /// # Errors
161    ///
162    /// Returns an error for invalid paths, conflicting project identity,
163    /// malformed persisted state, or any `SQLite` failure.
164    pub fn bind_project(
165        &mut self,
166        root: &Path,
167        project_instance_id: ProjectInstanceId,
168    ) -> DbResult<WorktreeRegistration> {
169        let root = worktree_identity("root", root)?;
170        let bound = bind_registration_project(
171            self.connection,
172            &self.registration,
173            &root,
174            project_instance_id,
175        )?;
176        self.registration = bound.clone();
177        Ok(bound)
178    }
179
180    /// Bind this project and accept its initial usage snapshot atomically.
181    ///
182    /// # Errors
183    ///
184    /// Returns an error for invalid paths, conflicting project or telemetry
185    /// identity, malformed or excessive snapshot state, or any `SQLite` failure.
186    pub fn bind_project_with_usage_snapshot(
187        &mut self,
188        root: &Path,
189        project_instance_id: ProjectInstanceId,
190        snapshot: &WorktreeUsageSnapshot,
191    ) -> DbResult<(WorktreeRegistration, WorktreeUsageSyncState)> {
192        let root = worktree_identity("root", root)?;
193        let bound = bind_registration_project(
194            self.connection,
195            &self.registration,
196            &root,
197            project_instance_id,
198        )?;
199        let synchronized = telemetry::synchronize_worktree_usage_snapshot(
200            self.connection,
201            bound.registration_id,
202            snapshot,
203        )?;
204        let bound = load_by_id(self.connection, bound.registration_id)?;
205        self.registration = bound.clone();
206        Ok((bound, synchronized))
207    }
208
209    /// Accept one usage snapshot for this already-bound active registration.
210    ///
211    /// # Errors
212    ///
213    /// Returns an error for a missing or mismatched project identity, malformed
214    /// or excessive snapshot state, or any `SQLite` failure.
215    pub fn synchronize_usage_snapshot(
216        &mut self,
217        snapshot: &WorktreeUsageSnapshot,
218    ) -> DbResult<WorktreeUsageSyncState> {
219        let synchronized = telemetry::synchronize_worktree_usage_snapshot(
220            self.connection,
221            self.registration.registration_id,
222            snapshot,
223        )?;
224        self.registration = load_by_id(self.connection, self.registration.registration_id)?;
225        Ok(synchronized)
226    }
227
228    /// Retire this active registration without importing another local snapshot.
229    ///
230    /// # Errors
231    ///
232    /// Returns an error for an invalid time, malformed persisted state, or any
233    /// `SQLite` failure.
234    pub fn retire(&mut self, retired_at_epoch: u64) -> DbResult<WorktreeRegistration> {
235        let retired = retire_registration(
236            self.connection,
237            &self.registration,
238            epoch_to_sqlite(retired_at_epoch)?,
239        )?;
240        self.registration = retired.clone();
241        Ok(retired)
242    }
243
244    /// Bind, synchronize one writer-excluded local snapshot, and retire atomically.
245    ///
246    /// # Errors
247    ///
248    /// Returns an error for invalid paths or times, a mismatched project or
249    /// telemetry identity, malformed persisted state, or any `SQLite` failure.
250    pub fn retire_with_usage_snapshot(
251        &mut self,
252        root: &Path,
253        project_instance_id: ProjectInstanceId,
254        snapshot: &WorktreeUsageSnapshot,
255        retired_at_epoch: u64,
256    ) -> DbResult<(WorktreeRegistration, WorktreeUsageSyncState)> {
257        let retired_at_epoch = epoch_to_sqlite(retired_at_epoch)?;
258        let (bound, synchronized) =
259            self.bind_project_with_usage_snapshot(root, project_instance_id, snapshot)?;
260        let retired = retire_registration(self.connection, &bound, retired_at_epoch)?;
261        self.registration = retired.clone();
262        Ok((retired, synchronized))
263    }
264}
265
266/// Raw row retained until all typed conversions succeed.
267struct PersistedWorktreeRegistration {
268    /// Stable row identity.
269    registration_id: i64,
270    /// Persisted alias text.
271    alias: String,
272    /// Persisted lifecycle state.
273    state: String,
274    /// Persisted normalized common-directory path.
275    git_common_directory: String,
276    /// Persisted lossless common-directory codec bytes.
277    git_common_directory_identity: Vec<u8>,
278    /// Persisted normalized administrative-directory path.
279    git_administrative_directory: String,
280    /// Persisted lossless administrative-directory codec bytes.
281    git_administrative_directory_identity: Vec<u8>,
282    /// Persisted opaque administrative-directory lifecycle identity.
283    git_administrative_identity: String,
284    /// Persisted last structurally validated root.
285    last_root: String,
286    /// Persisted lossless source-root codec bytes.
287    last_root_identity: Vec<u8>,
288    /// Optional exact initialized atlas identity bytes.
289    project_instance_id: Option<Vec<u8>>,
290    /// Last accepted local aggregate revision.
291    accepted_telemetry_revision: i64,
292    /// Creation epoch seconds.
293    created_at_epoch: i64,
294    /// Optional retirement epoch seconds.
295    retired_at_epoch: Option<i64>,
296}
297
298impl AtlasStore {
299    /// Register one structurally validated non-control Git worktree.
300    ///
301    /// A matching retired row is reactivated only when its administrative and
302    /// project identities still agree. Historical rows for a replaced atlas
303    /// remain retired so their aggregate telemetry cannot be relabelled.
304    ///
305    /// # Errors
306    ///
307    /// Returns an error for invalid paths/times, active alias or identity
308    /// conflicts, malformed persisted state, or any transactional `SQLite`
309    /// failure.
310    #[allow(clippy::too_many_arguments)]
311    pub fn register_worktree(
312        &self,
313        alias: &WorktreeAlias,
314        git_common_directory: &Path,
315        git_administrative_directory: &Path,
316        git_administrative_identity: &str,
317        root: &Path,
318        project_instance_id: Option<ProjectInstanceId>,
319        created_at_epoch: u64,
320    ) -> DbResult<WorktreeRegistration> {
321        let git_common_directory_identity =
322            worktree_identity("git_common_directory", git_common_directory)?;
323        let git_administrative_directory_identity =
324            worktree_identity("git_administrative_directory", git_administrative_directory)?;
325        let git_administrative_identity =
326            validated_administrative_identity(git_administrative_identity)?;
327        let root_identity = worktree_identity("root", root)?;
328        let git_common_directory = native_path_projection(&git_common_directory_identity)?;
329        let git_administrative_directory =
330            native_path_projection(&git_administrative_directory_identity)?;
331        let root = native_path_projection(&root_identity)?;
332        let created_at_epoch = epoch_to_sqlite(created_at_epoch)?;
333        let project_bytes = project_instance_id.map(ProjectInstanceId::as_bytes);
334        let common_identity_bytes = git_common_directory_identity.encode()?;
335        let administrative_identity_bytes = git_administrative_directory_identity.encode()?;
336        let root_identity_bytes = root_identity.encode()?;
337
338        self.with_validated_write(|transaction| {
339            if let Some(existing) = load_active_by_alias(transaction, alias.as_str())? {
340                if existing.git_administrative_directory_identity
341                    != git_administrative_directory_identity
342                {
343                    return Err(DbError::WorktreeRegistrationConflict {
344                        field: "alias",
345                        value: alias.to_string(),
346                    });
347                }
348                if existing.git_administrative_identity != git_administrative_identity {
349                    return Err(DbError::WorktreeRegistrationConflict {
350                        field: "git_administrative_identity",
351                        value: git_administrative_identity,
352                    });
353                }
354                if identities_conflict(existing.project_instance_id, project_instance_id) {
355                    return Err(DbError::WorktreeRegistrationConflict {
356                        field: "project_instance_id",
357                        value: project_instance_id
358                            .map_or_else(String::new, |value| value.to_string()),
359                    });
360                }
361                if let Some(project_bytes) = project_bytes.as_ref()
362                    && project_identity_exists_for_other(
363                        transaction,
364                        Some(existing.registration_id),
365                        project_bytes.as_slice(),
366                    )?
367                {
368                    return Err(DbError::WorktreeRegistrationConflict {
369                        field: "project_instance_id",
370                        value: project_instance_id
371                            .map_or_else(String::new, |value| value.to_string()),
372                    });
373                }
374                if native_root_identity_exists_for_other(
375                    transaction,
376                    Some(existing.registration_id),
377                    root_identity_bytes.as_slice(),
378                )? {
379                    return Err(DbError::WorktreeRegistrationConflict {
380                        field: "root",
381                        value: root,
382                    });
383                }
384                transaction.execute(
385                    "UPDATE worktree_registrations
386                 SET git_common_directory = ?1, git_common_directory_identity = ?2,
387                     last_root = ?3, last_root_identity = ?4,
388                     project_instance_id = COALESCE(project_instance_id, ?5)
389                 WHERE registration_id = ?6",
390                    params![
391                        git_common_directory,
392                        common_identity_bytes.as_slice(),
393                        root,
394                        root_identity_bytes.as_slice(),
395                        project_bytes.as_ref().map(<[u8; 16]>::as_slice),
396                        existing.registration_id,
397                    ],
398                )?;
399                return load_by_id(transaction, existing.registration_id);
400            }
401
402            let retired_id = load_matching_retired_id(
403                transaction,
404                &administrative_identity_bytes,
405                &git_administrative_identity,
406                project_bytes.as_ref(),
407            )?;
408            if active_git_identity_exists(
409                transaction,
410                &administrative_identity_bytes,
411                &git_administrative_identity,
412            )? {
413                return Err(DbError::WorktreeRegistrationConflict {
414                    field: "git_or_project_identity",
415                    value: git_administrative_directory,
416                });
417            }
418            if let Some(project_bytes) = project_bytes.as_ref()
419                && project_identity_exists_for_other(
420                    transaction,
421                    retired_id,
422                    project_bytes.as_slice(),
423                )?
424            {
425                return Err(DbError::WorktreeRegistrationConflict {
426                    field: "project_instance_id",
427                    value: project_instance_id.map_or_else(String::new, |value| value.to_string()),
428                });
429            }
430            if native_root_identity_exists_for_other(
431                transaction,
432                retired_id,
433                root_identity_bytes.as_slice(),
434            )? {
435                return Err(DbError::WorktreeRegistrationConflict {
436                    field: "root",
437                    value: root,
438                });
439            }
440            let registration_id = if let Some(registration_id) = retired_id {
441                transaction.execute(
442                    "UPDATE worktree_registrations
443                 SET alias = ?1, state = 'active', git_common_directory = ?2,
444                     git_common_directory_identity = ?3,
445                     git_administrative_directory = ?4,
446                     git_administrative_directory_identity = ?5,
447                     git_administrative_identity = ?6, last_root = ?7,
448                     last_root_identity = ?8, project_instance_id = ?9,
449                     retired_at_epoch = NULL
450                 WHERE registration_id = ?10",
451                    params![
452                        alias.as_str(),
453                        git_common_directory,
454                        common_identity_bytes.as_slice(),
455                        git_administrative_directory,
456                        administrative_identity_bytes.as_slice(),
457                        git_administrative_identity,
458                        root,
459                        root_identity_bytes.as_slice(),
460                        project_bytes.as_ref().map(<[u8; 16]>::as_slice),
461                        registration_id,
462                    ],
463                )?;
464                registration_id
465            } else {
466                let registration_count = transaction.query_row(
467                    "SELECT COUNT(*) FROM worktree_registrations",
468                    [],
469                    |row| row.get::<_, i64>(0),
470                )?;
471                if usize::try_from(registration_count).map_err(|_source| {
472                    DbError::WorktreeRegistrationRow {
473                        reason: "negative registration count",
474                    }
475                })? >= MAX_GIT_WORKTREE_REGISTRATIONS
476                {
477                    return Err(DbError::WorktreeRegistrationCapacity {
478                        limit: MAX_GIT_WORKTREE_REGISTRATIONS,
479                    });
480                }
481                transaction.execute(
482                    "INSERT INTO worktree_registrations(
483                    alias, state, git_common_directory, git_common_directory_identity,
484                    git_administrative_directory, git_administrative_directory_identity,
485                    git_administrative_identity, last_root, last_root_identity,
486                    project_instance_id, created_at_epoch
487                 ) VALUES(?1, 'active', ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
488                    params![
489                        alias.as_str(),
490                        git_common_directory,
491                        common_identity_bytes.as_slice(),
492                        git_administrative_directory,
493                        administrative_identity_bytes.as_slice(),
494                        git_administrative_identity,
495                        root,
496                        root_identity_bytes.as_slice(),
497                        project_bytes.as_ref().map(<[u8; 16]>::as_slice),
498                        created_at_epoch,
499                    ],
500                )?;
501                transaction.last_insert_rowid()
502            };
503            load_by_id(transaction, registration_id)
504        })
505    }
506
507    /// Register one initialized worktree and import its captured usage atomically.
508    ///
509    /// # Errors
510    ///
511    /// Returns any registration or snapshot synchronization error without making
512    /// the registration visible.
513    #[allow(clippy::too_many_arguments)]
514    pub fn register_worktree_with_usage_snapshot(
515        &self,
516        alias: &WorktreeAlias,
517        git_common_directory: &Path,
518        git_administrative_directory: &Path,
519        git_administrative_identity: &str,
520        root: &Path,
521        project_instance_id: ProjectInstanceId,
522        snapshot: &WorktreeUsageSnapshot,
523        created_at_epoch: u64,
524    ) -> DbResult<(WorktreeRegistration, WorktreeUsageSyncState)> {
525        self.with_validated_write(|transaction| {
526            let registration = self.register_worktree(
527                alias,
528                git_common_directory,
529                git_administrative_directory,
530                git_administrative_identity,
531                root,
532                Some(project_instance_id),
533                created_at_epoch,
534            )?;
535            let synchronization = telemetry::synchronize_worktree_usage_snapshot(
536                transaction,
537                registration.registration_id,
538                snapshot,
539            )?;
540            Ok((
541                load_by_id(transaction, registration.registration_id)?,
542                synchronization,
543            ))
544        })
545    }
546
547    /// Refresh the canonical root of one captured active registration.
548    ///
549    /// # Errors
550    ///
551    /// Returns an error when the captured registration is no longer active,
552    /// the root is invalid, persisted state is malformed, or `SQLite` fails.
553    pub fn refresh_worktree_root(
554        &self,
555        registration: &WorktreeRegistration,
556        root: &Path,
557    ) -> DbResult<WorktreeRegistration> {
558        let root_identity = worktree_identity("root", root)?;
559        let root = native_path_projection(&root_identity)?;
560        let root_identity_bytes = root_identity.encode()?;
561        self.with_validated_write(|transaction| {
562            let updated = transaction.execute(
563                "UPDATE worktree_registrations
564                 SET last_root = ?1, last_root_identity = ?2
565                 WHERE registration_id = ?3 AND alias = ?4 AND state = 'active'",
566                params![
567                    root,
568                    root_identity_bytes.as_slice(),
569                    registration.registration_id,
570                    registration.alias.as_str()
571                ],
572            )?;
573            if updated != 1 {
574                return Err(DbError::WorktreeRegistrationNotFound {
575                    alias: registration.alias.to_string(),
576                });
577            }
578            load_by_id(transaction, registration.registration_id)
579        })
580    }
581
582    /// Return one active registration by alias.
583    ///
584    /// # Errors
585    ///
586    /// Returns an error when the alias is absent, a persisted row is malformed,
587    /// or `SQLite` cannot complete the read.
588    pub fn worktree_registration(&self, alias: &WorktreeAlias) -> DbResult<WorktreeRegistration> {
589        load_active_by_alias(&self.connection, alias.as_str())?.ok_or_else(|| {
590            DbError::WorktreeRegistrationNotFound {
591                alias: alias.to_string(),
592            }
593        })
594    }
595
596    /// List active registrations and optionally retained retired history.
597    ///
598    /// # Errors
599    ///
600    /// Returns an error for malformed persisted rows or `SQLite` read failures.
601    pub fn worktree_registrations(
602        &self,
603        include_retired: bool,
604    ) -> DbResult<Vec<WorktreeRegistration>> {
605        let mut statement = self.connection.prepare(
606            "SELECT registration_id, alias, state, git_common_directory,
607                    git_common_directory_identity,
608                    git_administrative_directory, git_administrative_directory_identity,
609                    git_administrative_identity, last_root, last_root_identity,
610                    project_instance_id,
611                    accepted_telemetry_revision, created_at_epoch, retired_at_epoch
612             FROM worktree_registrations
613             WHERE state = 'active' OR ?1
614             ORDER BY CASE state WHEN 'active' THEN 0 ELSE 1 END, alias, registration_id
615             LIMIT ?2",
616        )?;
617        let limit = i64::try_from(MAX_GIT_WORKTREE_REGISTRATIONS + 1).map_err(|_source| {
618            DbError::WorktreeRegistrationRow {
619                reason: "registration bound exceeds SQLite integer range",
620            }
621        })?;
622        let rows = statement.query_map(params![include_retired, limit], persisted_registration)?;
623        let registrations = rows
624            .map(|row| row.map_err(DbError::from).and_then(try_registration))
625            .collect::<DbResult<Vec<_>>>()?;
626        if registrations.len() > MAX_GIT_WORKTREE_REGISTRATIONS {
627            return Err(DbError::WorktreeRegistrationCapacity {
628                limit: MAX_GIT_WORKTREE_REGISTRATIONS,
629            });
630        }
631        Ok(registrations)
632    }
633
634    /// Run one short operation when active catalog identities still match.
635    ///
636    /// # Errors
637    ///
638    /// Returns `None` when the active row count, order, aliases, or project
639    /// bindings changed. Returns an error for malformed persisted rows, a
640    /// changed database binding, an operation failure, or any `SQLite`
641    /// transaction failure.
642    pub fn with_matching_active_worktree_catalog<T>(
643        &self,
644        expected: &[WorktreeRegistration],
645        operation: impl FnOnce() -> DbResult<T>,
646    ) -> DbResult<Option<T>> {
647        self.with_validated_write(|_| {
648            let current = self.worktree_registrations(false)?;
649            if current.len() != expected.len()
650                || current.iter().zip(expected).any(|(current, expected)| {
651                    current.registration_id != expected.registration_id
652                        || current.alias != expected.alias
653                        || current.project_instance_id != expected.project_instance_id
654                })
655            {
656                return Ok(None);
657            }
658            operation().map(Some)
659        })
660    }
661
662    /// Run one short operation while an exact active registration owns control-writer exclusion.
663    ///
664    /// Callers that coordinate another local atlas must acquire this scope first,
665    /// then open or lock the local atlas, and finally publish through this guard
666    /// before returning.
667    ///
668    /// # Errors
669    ///
670    /// Returns an error when the captured registration is no longer active under
671    /// the same alias, the control database binding changed, the callback fails,
672    /// or `SQLite` cannot commit or roll back the transaction.
673    pub fn with_active_worktree_registration<T>(
674        &self,
675        registration_id: i64,
676        alias: &WorktreeAlias,
677        operation: impl FnOnce(&mut ActiveWorktreeRegistrationGuard<'_>) -> DbResult<T>,
678    ) -> DbResult<T> {
679        self.with_validated_write(|transaction| {
680            let registration = load_by_id(transaction, registration_id)?;
681            if registration.state != WorktreeRegistrationState::Active
682                || registration.alias != *alias
683            {
684                return Err(DbError::WorktreeRegistrationNotFound {
685                    alias: alias.to_string(),
686                });
687            }
688            operation(&mut ActiveWorktreeRegistrationGuard {
689                connection: transaction,
690                registration,
691            })
692        })
693    }
694
695    /// Run one external reset operation while an exact registration remains unbound.
696    ///
697    /// The callback must not mutate the control catalog. Its nested result keeps
698    /// caller-owned filesystem errors typed while the outer result owns `SQLite`
699    /// validation and transaction failures.
700    ///
701    /// # Errors
702    ///
703    /// Returns an error when the captured registration is no longer active under
704    /// the same alias, became bound, the control binding changed, or `SQLite`
705    /// cannot complete the writer-exclusion transaction.
706    pub fn with_unbound_worktree_registration<T, E>(
707        &self,
708        registration_id: i64,
709        alias: &WorktreeAlias,
710        operation: impl FnOnce(&WorktreeRegistration) -> Result<T, E>,
711    ) -> DbResult<Result<T, E>> {
712        self.with_active_worktree_registration(registration_id, alias, |guard| {
713            if guard.registration().project_instance_id.is_some() {
714                return Err(DbError::WorktreeRegistrationConflict {
715                    field: "project_instance_id",
716                    value: guard
717                        .registration()
718                        .project_instance_id
719                        .map_or_else(String::new, |value| value.to_string()),
720                });
721            }
722            Ok(operation(guard.registration()))
723        })
724    }
725
726    /// Export this exact atlas's bounded local aggregate snapshot.
727    ///
728    /// # Errors
729    ///
730    /// Returns an error when the selected database binding is invalid, stored
731    /// aggregate state is corrupt, a bound is exceeded, or `SQLite` cannot read
732    /// one complete snapshot.
733    pub fn export_worktree_usage_snapshot(&self) -> DbResult<WorktreeUsageSnapshot> {
734        telemetry::export_worktree_usage_snapshot(&self.connection)
735    }
736
737    /// Hold local writer exclusion while a caller consumes one exact usage snapshot.
738    ///
739    /// The callback must remain short: its lifetime is the final-synchronization
740    /// boundary that prevents a local usage commit from landing between export
741    /// and control-atlas retirement.
742    ///
743    /// # Errors
744    ///
745    /// Returns an error when the exact database binding changed, writer exclusion
746    /// cannot be acquired, snapshot export fails, or the callback fails.
747    pub fn with_exclusive_worktree_usage_snapshot<T>(
748        &self,
749        operation: impl FnOnce(&WorktreeUsageSnapshot) -> DbResult<T>,
750    ) -> DbResult<T> {
751        let binding = self.captured_project_binding()?;
752        self.with_telemetry_connection(|connection| {
753            crate::with_validated_native_write_transaction(
754                connection,
755                Some(&binding.project_root_identity),
756                Some(binding.project_instance_id),
757                |transaction| {
758                    let snapshot = telemetry::export_worktree_usage_snapshot(transaction)?;
759                    operation(&snapshot)
760                },
761            )
762        })
763    }
764
765    /// Accept a strictly newer local aggregate snapshot for one active alias.
766    ///
767    /// # Errors
768    ///
769    /// Returns an error for an absent alias, mismatched project identity,
770    /// malformed or excessive snapshot state, changed control binding, or any
771    /// transactional `SQLite` failure. The last accepted snapshot remains intact
772    /// on every error.
773    pub fn synchronize_worktree_usage(
774        &self,
775        alias: &WorktreeAlias,
776        snapshot: &WorktreeUsageSnapshot,
777    ) -> DbResult<WorktreeUsageSyncState> {
778        let registration = self.worktree_registration(alias)?;
779        self.with_validated_write(|transaction| {
780            telemetry::synchronize_worktree_usage_snapshot(
781                transaction,
782                registration.registration_id,
783                snapshot,
784            )
785        })
786    }
787
788    /// Bind an initialized worktree identity to one captured active registration.
789    ///
790    /// # Errors
791    ///
792    /// Returns an error when the captured registration is no longer active under
793    /// the same alias, its project identity conflicts, the path is invalid, stored
794    /// state is malformed, or `SQLite` fails.
795    pub fn bind_worktree_project(
796        &self,
797        registration_id: i64,
798        alias: &WorktreeAlias,
799        root: &Path,
800        project_instance_id: ProjectInstanceId,
801    ) -> DbResult<WorktreeRegistration> {
802        self.with_active_worktree_registration(registration_id, alias, |guard| {
803            guard.bind_project(root, project_instance_id)
804        })
805    }
806
807    /// Retire one active alias without deleting its aggregate history.
808    ///
809    /// The caller owns required final telemetry synchronization before invoking
810    /// this storage transition.
811    ///
812    /// # Errors
813    ///
814    /// Returns an error when the captured registration is no longer active under
815    /// the same alias, the time or stored state is invalid, or `SQLite` fails.
816    pub fn retire_worktree(
817        &self,
818        registration_id: i64,
819        alias: &WorktreeAlias,
820        retired_at_epoch: u64,
821    ) -> DbResult<WorktreeRegistration> {
822        let retired_at_epoch = epoch_to_sqlite(retired_at_epoch)?;
823        self.with_validated_write(|transaction| {
824            let existing = load_by_id(transaction, registration_id)?;
825            if existing.state != WorktreeRegistrationState::Active || existing.alias != *alias {
826                return Err(DbError::WorktreeRegistrationNotFound {
827                    alias: alias.to_string(),
828                });
829            }
830            retire_registration(transaction, &existing, retired_at_epoch)
831        })
832    }
833
834    /// Bind, synchronize one writer-excluded local snapshot, and retire its alias atomically.
835    ///
836    /// # Errors
837    ///
838    /// Returns an error for an absent alias, mismatched snapshot identity, invalid
839    /// time or aggregate state, changed control binding, or transactional `SQLite`
840    /// failure. Binding, synchronization, and retirement roll back together.
841    pub fn retire_worktree_with_usage_snapshot(
842        &self,
843        registration_id: i64,
844        alias: &WorktreeAlias,
845        root: &Path,
846        project_instance_id: ProjectInstanceId,
847        snapshot: &WorktreeUsageSnapshot,
848        retired_at_epoch: u64,
849    ) -> DbResult<(WorktreeRegistration, WorktreeUsageSyncState)> {
850        self.with_active_worktree_registration(registration_id, alias, |guard| {
851            guard.retire_with_usage_snapshot(root, project_instance_id, snapshot, retired_at_epoch)
852        })
853    }
854}
855
856/// Bind one already-loaded active registration inside its caller-owned transaction.
857fn bind_registration_project(
858    connection: &Connection,
859    registration: &WorktreeRegistration,
860    root: &CanonicalProjectRoot,
861    project_instance_id: ProjectInstanceId,
862) -> DbResult<WorktreeRegistration> {
863    if identities_conflict(registration.project_instance_id, Some(project_instance_id)) {
864        return Err(DbError::WorktreeRegistrationConflict {
865            field: "project_instance_id",
866            value: project_instance_id.to_string(),
867        });
868    }
869    let project_bytes = project_instance_id.as_bytes();
870    if project_identity_exists_for_other(
871        connection,
872        Some(registration.registration_id),
873        project_bytes.as_slice(),
874    )? {
875        return Err(DbError::WorktreeRegistrationConflict {
876            field: "project_instance_id",
877            value: project_instance_id.to_string(),
878        });
879    }
880    let root_display = native_path_projection(root)?;
881    let root_identity = root.encode()?;
882    let updated = connection.execute(
883        "UPDATE worktree_registrations
884         SET last_root = ?1, last_root_identity = ?2, project_instance_id = ?3
885         WHERE registration_id = ?4 AND alias = ?5 AND state = 'active'",
886        params![
887            root_display,
888            root_identity.as_slice(),
889            project_bytes.as_slice(),
890            registration.registration_id,
891            registration.alias.as_str()
892        ],
893    )?;
894    if updated != 1 {
895        return Err(DbError::WorktreeRegistrationNotFound {
896            alias: registration.alias.to_string(),
897        });
898    }
899    load_by_id(connection, registration.registration_id)
900}
901
902/// Retire one already-loaded active registration inside its caller-owned transaction.
903fn retire_registration(
904    connection: &Connection,
905    registration: &WorktreeRegistration,
906    retired_at_epoch: i64,
907) -> DbResult<WorktreeRegistration> {
908    if retired_at_epoch < epoch_to_sqlite(registration.created_at_epoch)? {
909        return Err(DbError::WorktreeRegistrationRow {
910            reason: "retirement time precedes creation time",
911        });
912    }
913    let updated = connection.execute(
914        "UPDATE worktree_registrations
915         SET state = 'retired', retired_at_epoch = ?1
916         WHERE registration_id = ?2 AND alias = ?3 AND state = 'active'",
917        params![
918            retired_at_epoch,
919            registration.registration_id,
920            registration.alias.as_str()
921        ],
922    )?;
923    if updated != 1 {
924        return Err(DbError::WorktreeRegistrationNotFound {
925            alias: registration.alias.to_string(),
926        });
927    }
928    load_by_id(connection, registration.registration_id)
929}
930
931/// Build one typed public alias-validation error.
932fn invalid_alias<T>(value: &str, reason: &'static str) -> DbResult<T> {
933    Err(DbError::InvalidWorktreeAlias {
934        alias: value.to_string(),
935        reason,
936    })
937}
938
939/// Admit one caller-validated absolute path through the shared native identity.
940fn worktree_identity(field: &'static str, path: &Path) -> DbResult<CanonicalProjectRoot> {
941    let byte_len = path.as_os_str().as_encoded_bytes().len();
942    if !path.is_absolute() || byte_len > MAX_WORKTREE_REGISTRATION_PATH_BYTES {
943        return Err(DbError::InvalidWorktreeRegistrationPath {
944            field,
945            path: path.to_string_lossy().into_owned(),
946        });
947    }
948    // Existing worktree paths are canonicalized against the filesystem. The
949    // persisted-path fallback keeps registration/recovery usable for a
950    // retired or moved worktree whose old directory is absent, while still
951    // rejecting an existing regular file at an active boundary.
952    if path.exists() {
953        CanonicalProjectRoot::from_path(path).map_err(DbError::from)
954    } else {
955        CanonicalProjectRoot::from_persisted_path(path.to_path_buf()).map_err(DbError::from)
956    }
957}
958
959/// Return the UTF-8 projection retained for compatibility metadata.
960fn native_path_projection(identity: &CanonicalProjectRoot) -> DbResult<String> {
961    identity.display_string().or_else(|_| {
962        let encoded = identity.encode()?;
963        Ok(format!(
964            "native-path-unavailable:{}",
965            blake3::hash(&encoded).to_hex()
966        ))
967    })
968}
969
970/// Validate one filesystem-derived opaque administrative identity.
971fn validated_administrative_identity(value: &str) -> DbResult<String> {
972    if value.len() != GIT_ADMINISTRATIVE_IDENTITY_BYTES
973        || !value
974            .bytes()
975            .all(|byte| byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase())
976    {
977        return Err(DbError::WorktreeRegistrationRow {
978            reason: "invalid Git administrative identity",
979        });
980    }
981    Ok(value.to_string())
982}
983
984/// Narrow one public epoch into the exact `SQLite` integer domain.
985fn epoch_to_sqlite(value: u64) -> DbResult<i64> {
986    i64::try_from(value).map_err(|_source| DbError::WorktreeRegistrationRow {
987        reason: "epoch exceeds SQLite integer range",
988    })
989}
990
991/// Detect only incompatible initialized project identities.
992fn identities_conflict(
993    existing: Option<ProjectInstanceId>,
994    requested: Option<ProjectInstanceId>,
995) -> bool {
996    matches!((existing, requested), (Some(left), Some(right)) if left != right)
997}
998
999/// Read one raw registration row before typed validation.
1000fn persisted_registration(row: &Row<'_>) -> rusqlite::Result<PersistedWorktreeRegistration> {
1001    Ok(PersistedWorktreeRegistration {
1002        registration_id: row.get(0)?,
1003        alias: row.get(1)?,
1004        state: row.get(2)?,
1005        git_common_directory: row.get(3)?,
1006        git_common_directory_identity: row.get(4)?,
1007        git_administrative_directory: row.get(5)?,
1008        git_administrative_directory_identity: row.get(6)?,
1009        git_administrative_identity: row.get(7)?,
1010        last_root: row.get(8)?,
1011        last_root_identity: row.get(9)?,
1012        project_instance_id: row.get(10)?,
1013        accepted_telemetry_revision: row.get(11)?,
1014        created_at_epoch: row.get(12)?,
1015        retired_at_epoch: row.get(13)?,
1016    })
1017}
1018
1019/// Validate and convert one persisted registration row.
1020fn try_registration(row: PersistedWorktreeRegistration) -> DbResult<WorktreeRegistration> {
1021    let git_common_directory_identity =
1022        CanonicalProjectRoot::decode(&row.git_common_directory_identity)?;
1023    let git_administrative_directory_identity =
1024        CanonicalProjectRoot::decode(&row.git_administrative_directory_identity)?;
1025    let last_root_identity = CanonicalProjectRoot::decode(&row.last_root_identity)?;
1026    let project_instance_id = row
1027        .project_instance_id
1028        .map(|value| {
1029            let bytes: [u8; 16] =
1030                value
1031                    .try_into()
1032                    .map_err(|value: Vec<u8>| DbError::InvalidBlobLength {
1033                        field: "worktree_registrations.project_instance_id",
1034                        expected: 16,
1035                        found: value.len(),
1036                    })?;
1037            ProjectInstanceId::from_bytes(bytes).map_err(DbError::from)
1038        })
1039        .transpose()?;
1040    let accepted_telemetry_revision =
1041        u64::try_from(row.accepted_telemetry_revision).map_err(|_source| {
1042            DbError::WorktreeRegistrationRow {
1043                reason: "negative accepted telemetry revision",
1044            }
1045        })?;
1046    let created_at_epoch = u64::try_from(row.created_at_epoch).map_err(|_source| {
1047        DbError::WorktreeRegistrationRow {
1048            reason: "negative creation time",
1049        }
1050    })?;
1051    let retired_at_epoch = row
1052        .retired_at_epoch
1053        .map(|value| {
1054            u64::try_from(value).map_err(|_source| DbError::WorktreeRegistrationRow {
1055                reason: "negative retirement time",
1056            })
1057        })
1058        .transpose()?;
1059    Ok(WorktreeRegistration {
1060        registration_id: row.registration_id,
1061        alias: WorktreeAlias::parse(&row.alias)?,
1062        state: WorktreeRegistrationState::parse(&row.state)?,
1063        git_common_directory: row.git_common_directory,
1064        git_common_directory_identity,
1065        git_administrative_directory: row.git_administrative_directory,
1066        git_administrative_directory_identity,
1067        git_administrative_identity: validated_administrative_identity(
1068            &row.git_administrative_identity,
1069        )?,
1070        last_root: row.last_root,
1071        last_root_identity,
1072        project_instance_id,
1073        accepted_telemetry_revision,
1074        created_at_epoch,
1075        retired_at_epoch,
1076    })
1077}
1078
1079/// Common typed registration projection shared by bounded lookups.
1080const REGISTRATION_SELECT: &str = "SELECT registration_id, alias, state, git_common_directory,
1081            git_common_directory_identity, git_administrative_directory,
1082            git_administrative_directory_identity, git_administrative_identity,
1083            last_root, last_root_identity, project_instance_id,
1084            accepted_telemetry_revision, created_at_epoch, retired_at_epoch
1085     FROM worktree_registrations";
1086
1087/// Load one active alias through its required partial unique index.
1088fn load_active_by_alias(
1089    connection: &Connection,
1090    alias: &str,
1091) -> DbResult<Option<WorktreeRegistration>> {
1092    let sql = format!(
1093        "{REGISTRATION_SELECT} INDEXED BY idx_worktree_registrations_active_alias
1094         WHERE state = 'active' AND alias = ?1"
1095    );
1096    let row = connection
1097        .query_row(&sql, [alias], persisted_registration)
1098        .optional()?;
1099    row.map(try_registration).transpose()
1100}
1101
1102/// Load one active or retired row by stable primary key.
1103fn load_by_id(connection: &Connection, registration_id: i64) -> DbResult<WorktreeRegistration> {
1104    let sql = format!("{REGISTRATION_SELECT} WHERE registration_id = ?1");
1105    let row = connection.query_row(&sql, [registration_id], persisted_registration)?;
1106    try_registration(row)
1107}
1108
1109/// Check active Git identity conflicts through owned indexes.
1110fn active_git_identity_exists(
1111    connection: &Connection,
1112    administrative_directory: &[u8],
1113    administrative_identity: &str,
1114) -> DbResult<bool> {
1115    let administrative_identity_exists = connection.query_row(
1116        "SELECT EXISTS(
1117            SELECT 1 FROM worktree_registrations
1118                 INDEXED BY idx_worktree_registrations_active_native_administrative_directory
1119            WHERE state = 'active' AND git_administrative_directory_identity = ?1
1120         )",
1121        [administrative_directory],
1122        |row| row.get::<_, bool>(0),
1123    )?;
1124    if administrative_identity_exists {
1125        return Ok(true);
1126    }
1127    let lifecycle_identity_exists = connection.query_row(
1128        "SELECT EXISTS(
1129            SELECT 1 FROM worktree_registrations
1130                 INDEXED BY idx_worktree_registrations_active_administrative_identity
1131            WHERE state = 'active' AND git_administrative_identity = ?1
1132         )",
1133        [administrative_identity],
1134        |row| row.get::<_, bool>(0),
1135    )?;
1136    if lifecycle_identity_exists {
1137        return Ok(true);
1138    }
1139    Ok(false)
1140}
1141
1142/// Check whether another active or retired row owns one initialized project identity.
1143fn project_identity_exists_for_other(
1144    connection: &Connection,
1145    registration_id: Option<i64>,
1146    project_instance_id: &[u8],
1147) -> DbResult<bool> {
1148    let found = connection.query_row(
1149        "SELECT EXISTS(
1150            SELECT 1 FROM worktree_registrations
1151            WHERE registration_id IS NOT ?1 AND project_instance_id = ?2
1152         )",
1153        params![registration_id, project_instance_id],
1154        |row| row.get::<_, bool>(0),
1155    )?;
1156    Ok(found)
1157}
1158
1159/// Check whether another active registration owns the native source root.
1160fn native_root_identity_exists_for_other(
1161    connection: &Connection,
1162    registration_id: Option<i64>,
1163    root_identity: &[u8],
1164) -> DbResult<bool> {
1165    connection
1166        .query_row(
1167            "SELECT EXISTS(
1168                SELECT 1 FROM worktree_registrations
1169                INDEXED BY idx_worktree_registrations_active_native_root
1170                WHERE state = 'active'
1171                  AND registration_id IS NOT ?1
1172                  AND last_root_identity = ?2
1173             )",
1174            params![registration_id, root_identity],
1175            |row| row.get::<_, bool>(0),
1176        )
1177        .map_err(DbError::from)
1178}
1179
1180/// Find the newest retired history row with the exact same stable identities.
1181fn load_matching_retired_id(
1182    connection: &Connection,
1183    administrative_directory: &[u8],
1184    administrative_identity: &str,
1185    project_instance_id: Option<&[u8; 16]>,
1186) -> DbResult<Option<i64>> {
1187    connection
1188        .query_row(
1189            "SELECT registration_id
1190             FROM worktree_registrations
1191             WHERE state = 'retired'
1192               AND git_administrative_directory_identity = ?1
1193               AND git_administrative_identity = ?2
1194               AND project_instance_id IS ?3
1195             ORDER BY registration_id DESC
1196             LIMIT 1",
1197            params![
1198                administrative_directory,
1199                administrative_identity,
1200                project_instance_id.map(<[u8; 16]>::as_slice),
1201            ],
1202            |row| row.get(0),
1203        )
1204        .optional()
1205        .map_err(DbError::from)
1206}
1207
1208#[cfg(test)]
1209mod tests {
1210    use super::*;
1211    use std::error::Error;
1212    use std::fmt::Debug;
1213    use std::fs;
1214    use std::io;
1215    #[cfg(unix)]
1216    use std::os::unix::ffi::OsStringExt;
1217    use std::time::Duration;
1218
1219    /// Return a test error instead of panicking inside a fallible test.
1220    fn require(condition: bool, message: &str) -> Result<(), Box<dyn Error>> {
1221        if condition {
1222            Ok(())
1223        } else {
1224            Err(io::Error::other(message).into())
1225        }
1226    }
1227
1228    /// Return whether an independent writer reached `SQLite`'s held writer lock.
1229    fn sqlite_writer_busy(error: &DbError) -> bool {
1230        matches!(
1231            error,
1232            DbError::Sqlite(rusqlite::Error::SqliteFailure(code, _))
1233                if matches!(
1234                    code.code,
1235                    rusqlite::ErrorCode::DatabaseBusy | rusqlite::ErrorCode::DatabaseLocked
1236                )
1237        )
1238    }
1239
1240    /// Compare test values without panicking inside a fallible test.
1241    fn require_eq<T>(actual: &T, expected: &T, label: &str) -> Result<(), Box<dyn Error>>
1242    where
1243        T: Debug + PartialEq,
1244    {
1245        if actual == expected {
1246            Ok(())
1247        } else {
1248            Err(io::Error::other(format!(
1249                "{label} mismatch: expected {expected:?}, found {actual:?}"
1250            ))
1251            .into())
1252        }
1253    }
1254
1255    fn identity(byte: u8) -> Result<ProjectInstanceId, Box<dyn Error>> {
1256        Ok(ProjectInstanceId::from_bytes([byte; 16])?)
1257    }
1258
1259    fn administrative_identity(byte: u8) -> String {
1260        format!("{byte:02x}").repeat(32)
1261    }
1262
1263    #[test]
1264    fn aliases_reject_reserved_or_ambiguous_shapes() -> Result<(), Box<dyn Error>> {
1265        for invalid in ["", "main", "Issue-430", "issue 430", "-issue", "ä"] {
1266            if WorktreeAlias::parse(invalid).is_ok() {
1267                return Err(format!("invalid alias was accepted: {invalid:?}").into());
1268            }
1269        }
1270        let alias = WorktreeAlias::parse("issue-430.fix")?;
1271        require_eq(&alias.as_str(), &"issue-430.fix", "valid alias")?;
1272        Ok(())
1273    }
1274
1275    #[cfg(unix)]
1276    #[test]
1277    fn registry_round_trips_non_utf8_identity_paths_without_lossy_keys()
1278    -> Result<(), Box<dyn Error>> {
1279        let temp = tempfile::tempdir()?;
1280        let control = temp.path().join("control");
1281        let common = temp.path().join("common.git");
1282        let administrative = common.join("worktrees").join("linked");
1283        let root = temp.path().join("linked");
1284        let invalid_common = temp
1285            .path()
1286            .join(std::ffi::OsString::from_vec(b"common-\xff".to_vec()));
1287        let invalid_administrative = temp
1288            .path()
1289            .join(std::ffi::OsString::from_vec(b"admin-\xff".to_vec()));
1290        let invalid_root = temp
1291            .path()
1292            .join(std::ffi::OsString::from_vec(b"root-\xff".to_vec()));
1293        let invalid_administrative_two = temp
1294            .path()
1295            .join(std::ffi::OsString::from_vec(b"admin-\xfe".to_vec()));
1296        fs::create_dir_all(&control)?;
1297        for path in [
1298            &common,
1299            &administrative,
1300            &root,
1301            &invalid_common,
1302            &invalid_administrative,
1303            &invalid_root,
1304            &invalid_administrative_two,
1305        ] {
1306            fs::create_dir_all(path)?;
1307        }
1308        let store = AtlasStore::open_for_project(&control.join("projectatlas.db"), &control)?;
1309        // Keep the temporary paths in owned variables while registration uses
1310        // them; this also makes each active root identity distinct.
1311        let second_root = temp.path().join("second");
1312        let third_administrative = common.join("worktrees/third");
1313        fs::create_dir_all(&second_root)?;
1314        fs::create_dir_all(&third_administrative)?;
1315        let mut rows = Vec::new();
1316        for (alias, common_path, administrative_path, root_path, identity) in [
1317            (
1318                "nonutf-common",
1319                invalid_common.as_path(),
1320                administrative.as_path(),
1321                root.as_path(),
1322                1,
1323            ),
1324            (
1325                "nonutf-admin",
1326                common.as_path(),
1327                invalid_administrative.as_path(),
1328                second_root.as_path(),
1329                2,
1330            ),
1331            (
1332                "nonutf-root",
1333                common.as_path(),
1334                third_administrative.as_path(),
1335                invalid_root.as_path(),
1336                3,
1337            ),
1338        ] {
1339            rows.push(store.register_worktree(
1340                &WorktreeAlias::parse(alias)?,
1341                common_path,
1342                administrative_path,
1343                &administrative_identity(identity),
1344                root_path,
1345                None,
1346                1,
1347            )?);
1348        }
1349        for row in &rows {
1350            require(
1351                row.git_common_directory_identity.encode()?.len() >= 3
1352                    && row.git_administrative_directory_identity.encode()?.len() >= 3
1353                    && row.last_root_identity.encode()?.len() >= 3,
1354                "native identity codec bytes were not persisted",
1355            )?;
1356            require(
1357                row.git_common_directory.contains("native-path-unavailable")
1358                    || row
1359                        .git_administrative_directory
1360                        .contains("native-path-unavailable")
1361                    || row.last_root.contains("native-path-unavailable"),
1362                "non-UTF-8 display projection was not typed as unavailable",
1363            )?;
1364        }
1365        require(
1366            matches!(
1367                store.register_worktree(
1368                    &WorktreeAlias::parse("duplicate-native-admin")?,
1369                    &common,
1370                    &invalid_administrative,
1371                    &administrative_identity(4),
1372                    &temp.path().join("duplicate-root"),
1373                    None,
1374                    1,
1375                ),
1376                Err(DbError::WorktreeRegistrationConflict { .. })
1377            ),
1378            "duplicate native administrative identity was accepted",
1379        )?;
1380        fs::create_dir_all(temp.path().join("distinct-root"))?;
1381        store.register_worktree(
1382            &WorktreeAlias::parse("distinct-native-admin")?,
1383            &common,
1384            &invalid_administrative_two,
1385            &administrative_identity(5),
1386            &temp.path().join("distinct-root"),
1387            None,
1388            1,
1389        )?;
1390        let reopened = AtlasStore::open_for_project(&control.join("projectatlas.db"), &control)?;
1391        require_eq(
1392            &reopened.worktree_registrations(false)?.len(),
1393            &4,
1394            "native rows",
1395        )?;
1396        require(
1397            reopened
1398                .worktree_registrations(false)?
1399                .iter()
1400                .any(|row| row.last_root_identity.display_string().is_err()),
1401            "native non-UTF-8 root did not remain lossless after reopen",
1402        )
1403    }
1404
1405    #[test]
1406    fn registry_reuses_matching_history_and_keeps_retired_alias_history()
1407    -> Result<(), Box<dyn Error>> {
1408        let temp = tempfile::tempdir()?;
1409        let control = temp.path().join("control");
1410        let common = temp.path().join("common.git");
1411        let first_admin = common.join("worktrees/first");
1412        let second_admin = common.join("worktrees/second");
1413        let first_root = temp.path().join("first");
1414        let second_root = temp.path().join("second");
1415        for path in [
1416            &control,
1417            &first_admin,
1418            &second_admin,
1419            &first_root,
1420            &second_root,
1421        ] {
1422            fs::create_dir_all(path)?;
1423        }
1424        let database = control.join("projectatlas.db");
1425        let store = AtlasStore::open_for_project(&database, &control)?;
1426        let alias = WorktreeAlias::parse("issue-430")?;
1427        let first = store.register_worktree(
1428            &alias,
1429            &common,
1430            &first_admin,
1431            &administrative_identity(1),
1432            &first_root,
1433            Some(identity(1)?),
1434            10,
1435        )?;
1436        let idempotent = store.register_worktree(
1437            &alias,
1438            &common,
1439            &first_admin,
1440            &administrative_identity(1),
1441            &first_root,
1442            Some(identity(1)?),
1443            11,
1444        )?;
1445        require_eq(
1446            &first.registration_id,
1447            &idempotent.registration_id,
1448            "idempotent registration identity",
1449        )?;
1450        require(
1451            matches!(
1452                store.register_worktree(
1453                    &alias,
1454                    &common,
1455                    &second_admin,
1456                    &administrative_identity(2),
1457                    &second_root,
1458                    Some(identity(2)?),
1459                    12,
1460                ),
1461                Err(DbError::WorktreeRegistrationConflict { field: "alias", .. })
1462            ),
1463            "active alias conflict was not rejected",
1464        )?;
1465
1466        let retired = store.retire_worktree(first.registration_id, &alias, 20)?;
1467        require_eq(
1468            &retired.state,
1469            &WorktreeRegistrationState::Retired,
1470            "retired state",
1471        )?;
1472        require(
1473            matches!(
1474                store.refresh_worktree_root(&first, &second_root),
1475                Err(DbError::WorktreeRegistrationNotFound { .. })
1476            ),
1477            "stale root refresh reactivated a retired registration",
1478        )?;
1479        require_eq(
1480            &store
1481                .worktree_registrations(true)?
1482                .into_iter()
1483                .find(|registration| registration.registration_id == first.registration_id)
1484                .ok_or_else(|| io::Error::other("retired registration history is missing"))?
1485                .state,
1486            &WorktreeRegistrationState::Retired,
1487            "state after stale root refresh",
1488        )?;
1489        let replacement = store.register_worktree(
1490            &alias,
1491            &common,
1492            &first_admin,
1493            &administrative_identity(2),
1494            &second_root,
1495            None,
1496            21,
1497        )?;
1498        require(
1499            replacement.registration_id != first.registration_id,
1500            "replacement reused unrelated retired history",
1501        )?;
1502        require(
1503            matches!(
1504                store.bind_worktree_project(
1505                    first.registration_id,
1506                    &alias,
1507                    &first_root,
1508                    identity(1)?
1509                ),
1510                Err(DbError::WorktreeRegistrationNotFound { .. })
1511            ),
1512            "stale bind targeted a replacement registration after alias reuse",
1513        )?;
1514        require(
1515            matches!(
1516                store.retire_worktree(first.registration_id, &alias, 22),
1517                Err(DbError::WorktreeRegistrationNotFound { .. })
1518            ),
1519            "stale retirement targeted a replacement registration after alias reuse",
1520        )?;
1521        let replacement = store.worktree_registration(&alias)?;
1522        require(
1523            replacement.project_instance_id.is_none()
1524                && replacement.last_root
1525                    == native_path_projection(&worktree_identity("root", &second_root)?)?,
1526            "stale bind changed the replacement registration",
1527        )?;
1528        let all = store.worktree_registrations(true)?;
1529        require_eq(&all.len(), &2, "retained registration count")?;
1530        require_eq(
1531            &all.first()
1532                .ok_or_else(|| io::Error::other("active registration is missing"))?
1533                .state,
1534            &WorktreeRegistrationState::Active,
1535            "active row ordering",
1536        )?;
1537        require_eq(
1538            &all.get(1)
1539                .ok_or_else(|| io::Error::other("retired registration is missing"))?
1540                .state,
1541            &WorktreeRegistrationState::Retired,
1542            "retired row ordering",
1543        )?;
1544        require(
1545            store
1546                .worktree_registrations(false)?
1547                .iter()
1548                .all(|row| row.state == WorktreeRegistrationState::Active),
1549            "active-only list returned retired history",
1550        )?;
1551        Ok(())
1552    }
1553
1554    #[test]
1555    fn active_registration_guard_rolls_back_nested_binding_and_rechecks_unbound_state()
1556    -> Result<(), Box<dyn Error>> {
1557        let temp = tempfile::tempdir()?;
1558        let control_root = temp.path().join("control");
1559        let common = temp.path().join("common.git");
1560        let admin = common.join("worktrees/guarded");
1561        let root = temp.path().join("guarded");
1562        for path in [&control_root, &admin, &root] {
1563            fs::create_dir_all(path)?;
1564        }
1565        let store =
1566            AtlasStore::open_for_project(&control_root.join("projectatlas.db"), &control_root)?;
1567        let alias = WorktreeAlias::parse("guarded")?;
1568        let project = identity(1)?;
1569        let registration = store.register_worktree(
1570            &alias,
1571            &common,
1572            &admin,
1573            &administrative_identity(1),
1574            &root,
1575            None,
1576            1,
1577        )?;
1578
1579        let rejected = store.with_active_worktree_registration(
1580            registration.registration_id,
1581            &alias,
1582            |guard| {
1583                guard.bind_project(&root, project)?;
1584                Err::<(), _>(DbError::WorktreeRegistrationConflict {
1585                    field: "test_operation",
1586                    value: "rollback".to_string(),
1587                })
1588            },
1589        );
1590        require(
1591            matches!(
1592                rejected,
1593                Err(DbError::WorktreeRegistrationConflict {
1594                    field: "test_operation",
1595                    ..
1596                })
1597            ),
1598            "guarded callback failure was not returned",
1599        )?;
1600        require(
1601            store
1602                .worktree_registration(&alias)?
1603                .project_instance_id
1604                .is_none(),
1605            "failed guarded callback committed its nested binding",
1606        )?;
1607
1608        store.with_active_worktree_registration(registration.registration_id, &alias, |guard| {
1609            guard.bind_project(&root, project).map(|_| ())
1610        })?;
1611        let reset_callback_ran = std::cell::Cell::new(false);
1612        require(
1613            matches!(
1614                store.with_unbound_worktree_registration(
1615                    registration.registration_id,
1616                    &alias,
1617                    |_registration| {
1618                        reset_callback_ran.set(true);
1619                        Ok::<(), io::Error>(())
1620                    }
1621                ),
1622                Err(DbError::WorktreeRegistrationConflict {
1623                    field: "project_instance_id",
1624                    ..
1625                })
1626            ) && !reset_callback_ran.get(),
1627            "bound registration entered the guarded reset callback",
1628        )
1629    }
1630
1631    #[test]
1632    fn active_registration_guard_serializes_bind_and_reset_across_connections()
1633    -> Result<(), Box<dyn Error>> {
1634        let temp = tempfile::tempdir()?;
1635        let control_root = temp.path().join("control");
1636        let common = temp.path().join("common.git");
1637        let first_root = temp.path().join("first");
1638        let second_root = temp.path().join("second");
1639        for path in [&control_root, &first_root, &second_root] {
1640            fs::create_dir_all(path)?;
1641        }
1642        let database = control_root.join("projectatlas.db");
1643        let store = AtlasStore::open_for_project(&database, &control_root)?;
1644        let contender = AtlasStore::open_for_project(&database, &control_root)?;
1645        contender.connection.busy_timeout(Duration::ZERO)?;
1646        let bind_wins = WorktreeAlias::parse("bind-wins")?;
1647        let bind_registration = store.register_worktree(
1648            &bind_wins,
1649            &common,
1650            &common.join("worktrees/bind-wins"),
1651            &administrative_identity(1),
1652            &first_root,
1653            None,
1654            1,
1655        )?;
1656        let bind_project = identity(1)?;
1657        store.with_active_worktree_registration(
1658            bind_registration.registration_id,
1659            &bind_wins,
1660            |guard| {
1661                let blocked = contender.with_unbound_worktree_registration(
1662                    bind_registration.registration_id,
1663                    &bind_wins,
1664                    |_registration| Ok::<(), io::Error>(()),
1665                );
1666                if !blocked.as_ref().is_err_and(sqlite_writer_busy) {
1667                    return Err(DbError::WorktreeRegistrationRow {
1668                        reason: "reset contender did not reach the held SQLite writer lock",
1669                    });
1670                }
1671                guard.bind_project(&first_root, bind_project).map(|_| ())
1672            },
1673        )?;
1674        let reset_callback_ran = std::cell::Cell::new(false);
1675        require(
1676            matches!(
1677                contender.with_unbound_worktree_registration(
1678                    bind_registration.registration_id,
1679                    &bind_wins,
1680                    |_registration| {
1681                        reset_callback_ran.set(true);
1682                        Ok::<(), io::Error>(())
1683                    },
1684                ),
1685                Err(DbError::WorktreeRegistrationConflict {
1686                    field: "project_instance_id",
1687                    ..
1688                })
1689            ) && !reset_callback_ran.get(),
1690            "reset did not reload the binding committed by the winning writer",
1691        )?;
1692
1693        let reset_wins = WorktreeAlias::parse("reset-wins")?;
1694        let reset_registration = store.register_worktree(
1695            &reset_wins,
1696            &common,
1697            &common.join("worktrees/reset-wins"),
1698            &administrative_identity(2),
1699            &second_root,
1700            None,
1701            2,
1702        )?;
1703        let reset_project = identity(2)?;
1704        let target_database = second_root.join("projectatlas.db");
1705        fs::write(&target_database, b"captured atlas")?;
1706        store.with_unbound_worktree_registration(
1707            reset_registration.registration_id,
1708            &reset_wins,
1709            |_registration| {
1710                let blocked = contender.with_active_worktree_registration(
1711                    reset_registration.registration_id,
1712                    &reset_wins,
1713                    |guard| guard.bind_project(&second_root, reset_project).map(|_| ()),
1714                );
1715                if !blocked.as_ref().is_err_and(sqlite_writer_busy) {
1716                    return Err(io::Error::other(
1717                        "bind contender did not reach the held SQLite writer lock",
1718                    ));
1719                }
1720                fs::remove_file(&target_database)
1721            },
1722        )??;
1723        let late_bind_result = contender.with_active_worktree_registration(
1724            reset_registration.registration_id,
1725            &reset_wins,
1726            |guard| {
1727                if !target_database.is_file() {
1728                    return Ok(Err("target atlas is missing"));
1729                }
1730                guard.bind_project(&second_root, reset_project)?;
1731                Ok(Ok(()))
1732            },
1733        )?;
1734        require(
1735            matches!(late_bind_result, Err("target atlas is missing")),
1736            "late bind did not recheck the reset target after writer exclusion",
1737        )?;
1738        require(
1739            store
1740                .worktree_registration(&reset_wins)?
1741                .project_instance_id
1742                .is_none()
1743                && !target_database.exists(),
1744            "reset-wins interleaving bound or recreated the deleted target atlas",
1745        )
1746    }
1747
1748    #[test]
1749    fn failed_final_sync_rolls_back_project_binding_and_retirement() -> Result<(), Box<dyn Error>> {
1750        let temp = tempfile::tempdir()?;
1751        let control_root = temp.path().join("control");
1752        let common = temp.path().join("common.git");
1753        let admin = common.join("worktrees/issue-430");
1754        let original_root = temp.path().join("original");
1755        let moved_root = temp.path().join("moved");
1756        let other_root = temp.path().join("other");
1757        for path in [
1758            &control_root,
1759            &admin,
1760            &original_root,
1761            &moved_root,
1762            &other_root,
1763        ] {
1764            fs::create_dir_all(path)?;
1765        }
1766
1767        let control =
1768            AtlasStore::open_for_project(&control_root.join("projectatlas.db"), &control_root)?;
1769        let other = AtlasStore::open_for_project(&other_root.join("projectatlas.db"), &other_root)?;
1770        let mismatched_snapshot = other.export_worktree_usage_snapshot()?;
1771        let target_project = if mismatched_snapshot.project_instance_id() == identity(7)? {
1772            identity(8)?
1773        } else {
1774            identity(7)?
1775        };
1776        let alias = WorktreeAlias::parse("issue-430")?;
1777        let before = control.register_worktree(
1778            &alias,
1779            &common,
1780            &admin,
1781            &administrative_identity(1),
1782            &original_root,
1783            None,
1784            10,
1785        )?;
1786
1787        require(
1788            matches!(
1789                control.retire_worktree_with_usage_snapshot(
1790                    before.registration_id,
1791                    &alias,
1792                    &moved_root,
1793                    target_project,
1794                    &mismatched_snapshot,
1795                    20,
1796                ),
1797                Err(DbError::WorktreeTelemetryProjectMismatch { .. })
1798            ),
1799            "mismatched final snapshot was not rejected",
1800        )?;
1801        require_eq(
1802            &control.worktree_registration(&alias)?,
1803            &before,
1804            "active registration after failed final synchronization",
1805        )?;
1806        Ok(())
1807    }
1808
1809    #[test]
1810    fn registration_and_initial_usage_snapshot_commit_atomically() -> Result<(), Box<dyn Error>> {
1811        let temp = tempfile::tempdir()?;
1812        let control_root = temp.path().join("control");
1813        let local_root = temp.path().join("local");
1814        let common = temp.path().join("common.git");
1815        let admin = common.join("worktrees/local");
1816        for path in [&control_root, &local_root, &admin] {
1817            fs::create_dir_all(path)?;
1818        }
1819        let control =
1820            AtlasStore::open_for_project(&control_root.join("projectatlas.db"), &control_root)?;
1821        let local = AtlasStore::open_for_project(&local_root.join("projectatlas.db"), &local_root)?;
1822        local.record_usage(&projectatlas_core::telemetry::usage_from_estimates(
1823            "atomic-registration",
1824            "atlas_overview",
1825            None,
1826            None,
1827            100,
1828            20,
1829        ))?;
1830        let snapshot = local.export_worktree_usage_snapshot()?;
1831        let project = snapshot.project_instance_id();
1832        let mismatched_project = if project == identity(7)? {
1833            identity(8)?
1834        } else {
1835            identity(7)?
1836        };
1837        let alias = WorktreeAlias::parse("local")?;
1838
1839        require(
1840            matches!(
1841                control.register_worktree_with_usage_snapshot(
1842                    &alias,
1843                    &common,
1844                    &admin,
1845                    &administrative_identity(1),
1846                    &local_root,
1847                    mismatched_project,
1848                    &snapshot,
1849                    1,
1850                ),
1851                Err(DbError::WorktreeTelemetryProjectMismatch { .. })
1852            ),
1853            "failed initial snapshot synchronization made a registration visible",
1854        )?;
1855        require(
1856            matches!(
1857                control.worktree_registration(&alias),
1858                Err(DbError::WorktreeRegistrationNotFound { .. })
1859            ) && control.repository_token_overview()?.calls == 0,
1860            "failed initial snapshot synchronization changed registration or aggregate state",
1861        )?;
1862
1863        let (registration, synchronization) = control.register_worktree_with_usage_snapshot(
1864            &alias,
1865            &common,
1866            &admin,
1867            &administrative_identity(1),
1868            &local_root,
1869            project,
1870            &snapshot,
1871            1,
1872        )?;
1873        require(
1874            registration.project_instance_id == Some(project)
1875                && registration.accepted_telemetry_revision == snapshot.revision()
1876                && synchronization == WorktreeUsageSyncState::Synchronized
1877                && control.repository_token_overview()?.calls == 1,
1878            "successful initial registration exposed incomplete aggregate state",
1879        )
1880    }
1881
1882    #[test]
1883    fn deferred_binding_and_initial_usage_snapshot_commit_atomically() -> Result<(), Box<dyn Error>>
1884    {
1885        let temp = tempfile::tempdir()?;
1886        let control_root = temp.path().join("control");
1887        let local_root = temp.path().join("local");
1888        let common = temp.path().join("common.git");
1889        let admin = common.join("worktrees/local");
1890        for path in [&control_root, &local_root, &admin] {
1891            fs::create_dir_all(path)?;
1892        }
1893        let control =
1894            AtlasStore::open_for_project(&control_root.join("projectatlas.db"), &control_root)?;
1895        let local = AtlasStore::open_for_project(&local_root.join("projectatlas.db"), &local_root)?;
1896        local.record_usage(&projectatlas_core::telemetry::usage_from_estimates(
1897            "deferred-binding",
1898            "atlas_overview",
1899            None,
1900            None,
1901            100,
1902            20,
1903        ))?;
1904        let snapshot = local.export_worktree_usage_snapshot()?;
1905        let project = snapshot.project_instance_id();
1906        let mismatched_project = if project == identity(7)? {
1907            identity(8)?
1908        } else {
1909            identity(7)?
1910        };
1911        let alias = WorktreeAlias::parse("local")?;
1912        let registration = control.register_worktree(
1913            &alias,
1914            &common,
1915            &admin,
1916            &administrative_identity(1),
1917            &local_root,
1918            None,
1919            1,
1920        )?;
1921
1922        let rejected = control.with_active_worktree_registration(
1923            registration.registration_id,
1924            &alias,
1925            |guard| {
1926                guard.bind_project_with_usage_snapshot(&local_root, mismatched_project, &snapshot)
1927            },
1928        );
1929        require(
1930            matches!(
1931                rejected,
1932                Err(DbError::WorktreeTelemetryProjectMismatch { .. })
1933            ) && control
1934                .worktree_registration(&alias)?
1935                .project_instance_id
1936                .is_none()
1937                && control.repository_token_overview()?.calls == 0,
1938            "failed deferred synchronization committed a project binding or aggregate",
1939        )?;
1940
1941        let (bound, synchronization) = control.with_active_worktree_registration(
1942            registration.registration_id,
1943            &alias,
1944            |guard| guard.bind_project_with_usage_snapshot(&local_root, project, &snapshot),
1945        )?;
1946        require(
1947            bound.project_instance_id == Some(project)
1948                && bound.accepted_telemetry_revision == snapshot.revision()
1949                && synchronization == WorktreeUsageSyncState::Synchronized
1950                && control.repository_token_overview()?.calls == 1,
1951            "successful deferred binding exposed incomplete aggregate state",
1952        )
1953    }
1954
1955    #[test]
1956    fn retired_project_identity_cannot_bind_another_registration() -> Result<(), Box<dyn Error>> {
1957        let temp = tempfile::tempdir()?;
1958        let control = temp.path().join("control");
1959        let common = temp.path().join("common.git");
1960        fs::create_dir_all(&control)?;
1961        let store = AtlasStore::open_for_project(&control.join("projectatlas.db"), &control)?;
1962        let original = WorktreeAlias::parse("original")?;
1963        let original_registration = store.register_worktree(
1964            &original,
1965            &common,
1966            &common.join("worktrees/original"),
1967            &administrative_identity(1),
1968            &temp.path().join("original"),
1969            Some(identity(1)?),
1970            1,
1971        )?;
1972        store.retire_worktree(original_registration.registration_id, &original, 2)?;
1973
1974        let unbound = WorktreeAlias::parse("unbound")?;
1975        let unbound_registration = store.register_worktree(
1976            &unbound,
1977            &common,
1978            &common.join("worktrees/unbound"),
1979            &administrative_identity(2),
1980            &temp.path().join("unbound"),
1981            None,
1982            3,
1983        )?;
1984        for result in [
1985            store
1986                .bind_worktree_project(
1987                    unbound_registration.registration_id,
1988                    &unbound,
1989                    &temp.path().join("unbound"),
1990                    identity(1)?,
1991                )
1992                .map(|_| ()),
1993            store
1994                .register_worktree(
1995                    &unbound,
1996                    &common,
1997                    &common.join("worktrees/unbound"),
1998                    &administrative_identity(2),
1999                    &temp.path().join("unbound"),
2000                    Some(identity(1)?),
2001                    4,
2002                )
2003                .map(|_| ()),
2004            store
2005                .register_worktree(
2006                    &WorktreeAlias::parse("direct")?,
2007                    &common,
2008                    &common.join("worktrees/direct"),
2009                    &administrative_identity(3),
2010                    &temp.path().join("direct"),
2011                    Some(identity(1)?),
2012                    4,
2013                )
2014                .map(|_| ()),
2015        ] {
2016            require(
2017                matches!(
2018                    result,
2019                    Err(DbError::WorktreeRegistrationConflict {
2020                        field: "project_instance_id",
2021                        ..
2022                    })
2023                ),
2024                "retired project identity was rebound to another registration",
2025            )?;
2026        }
2027        require(
2028            store
2029                .worktree_registration(&unbound)?
2030                .project_instance_id
2031                .is_none(),
2032            "failed retired-identity binding changed the active registration",
2033        )
2034    }
2035
2036    #[test]
2037    fn registry_capacity_rejects_new_history_without_changing_existing_rows()
2038    -> Result<(), Box<dyn Error>> {
2039        let temp = tempfile::tempdir()?;
2040        let control = temp.path().join("control");
2041        fs::create_dir_all(&control)?;
2042        let store = AtlasStore::open_for_project(&control.join("projectatlas.db"), &control)?;
2043        store.connection.execute_batch(
2044            "WITH RECURSIVE registrations(value) AS (
2045                 SELECT 0
2046                 UNION ALL
2047                 SELECT value + 1 FROM registrations WHERE value + 1 < 1024
2048             )
2049             INSERT INTO worktree_registrations(
2050                 alias, state, git_common_directory, git_administrative_directory,
2051                 git_administrative_identity, last_root, created_at_epoch, retired_at_epoch
2052             )
2053             SELECT printf('retired-%04d', value), 'retired', '/common',
2054                    printf('/common/worktrees/%04d', value),
2055                    printf('%064x', value + 1), printf('/worktrees/%04d', value), 0, 0
2056             FROM registrations;",
2057        )?;
2058        let alias = WorktreeAlias::parse("overflow")?;
2059        require(
2060            matches!(
2061                store.register_worktree(
2062                    &alias,
2063                    &temp.path().join("common"),
2064                    &temp.path().join("common/worktrees/overflow"),
2065                    &administrative_identity(3),
2066                    &temp.path().join("overflow"),
2067                    None,
2068                    1,
2069                ),
2070                Err(DbError::WorktreeRegistrationCapacity {
2071                    limit: MAX_GIT_WORKTREE_REGISTRATIONS
2072                })
2073            ),
2074            "registration capacity did not reject new history",
2075        )?;
2076        require_eq(
2077            &store.worktree_registrations(true)?.len(),
2078            &MAX_GIT_WORKTREE_REGISTRATIONS,
2079            "catalog after capacity rejection",
2080        )?;
2081        Ok(())
2082    }
2083
2084    #[test]
2085    fn schema_twentytwo_worktree_identity_migration_backfills_and_retries_atomically()
2086    -> Result<(), Box<dyn Error>> {
2087        let temp = tempfile::tempdir()?;
2088        let base = temp.path().canonicalize()?;
2089        let control = base.join("control");
2090        let common = base.join("common.git");
2091        let administrative = common.join("worktrees/legacy");
2092        let root = base.join("legacy");
2093        for path in [&control, &common, &administrative, &root] {
2094            fs::create_dir_all(path)?;
2095        }
2096        let database = control.join("projectatlas.db");
2097        let store = AtlasStore::open_for_project(&database, &control)?;
2098        let common_display = projectatlas_core::normalize_native_path_display(&common);
2099        let administrative_display =
2100            projectatlas_core::normalize_native_path_display(&administrative);
2101        let root_display = projectatlas_core::normalize_native_path_display(&root);
2102        store.connection.execute(
2103            "INSERT INTO worktree_registrations(
2104                alias, state, git_common_directory, git_administrative_directory,
2105                git_administrative_identity, last_root, created_at_epoch
2106             ) VALUES('legacy', 'active', ?1, ?2, ?3, ?4, 1)",
2107            params![
2108                common_display,
2109                administrative_display,
2110                administrative_identity(9),
2111                root_display,
2112            ],
2113        )?;
2114        crate::schema::drop_worktree_native_identity_schema(&store.connection)?;
2115        store.connection.execute(
2116            "UPDATE metadata SET value = '22' WHERE key = 'schema_version'",
2117            [],
2118        )?;
2119        drop(store);
2120
2121        let migrated = AtlasStore::open_for_project(&database, &control)?;
2122        let registration = migrated.worktree_registration(&WorktreeAlias::parse("legacy")?)?;
2123        require_eq(
2124            &registration.git_common_directory_identity,
2125            &CanonicalProjectRoot::from_path(&common)?,
2126            "migrated common identity",
2127        )?;
2128        require_eq(
2129            &registration.git_administrative_directory_identity,
2130            &CanonicalProjectRoot::from_path(&administrative)?,
2131            "migrated administrative identity",
2132        )?;
2133        require_eq(
2134            &registration.last_root_identity,
2135            &CanonicalProjectRoot::from_path(&root)?,
2136            "migrated root identity",
2137        )?;
2138        drop(migrated);
2139
2140        let failed_database = control.join("failed-projectatlas.db");
2141        let failed = AtlasStore::open_for_project(&failed_database, &control)?;
2142        failed.connection.execute(
2143            "INSERT INTO worktree_registrations(
2144                alias, state, git_common_directory, git_administrative_directory,
2145                git_administrative_identity, last_root, created_at_epoch
2146             ) VALUES('legacy', 'active', ?1, ?2, ?3, ?4, 1)",
2147            params![
2148                common_display,
2149                administrative_display,
2150                administrative_identity(10),
2151                root_display,
2152            ],
2153        )?;
2154        crate::schema::drop_worktree_native_identity_schema(&failed.connection)?;
2155        failed.connection.execute_batch(
2156            "UPDATE metadata SET value = '22' WHERE key = 'schema_version';
2157             UPDATE worktree_registrations SET last_root = 'relative';",
2158        )?;
2159        let failed_database_path = failed_database;
2160        drop(failed);
2161        let migration_result = AtlasStore::open_for_project(&failed_database_path, &control);
2162        require(
2163            matches!(&migration_result, Err(DbError::ProjectRootIdentity(_))),
2164            &format!(
2165                "injected native identity migration failure was not returned: {:?}",
2166                migration_result.as_ref().err().map(ToString::to_string)
2167            ),
2168        )?;
2169        let inspect = Connection::open(&failed_database_path)?;
2170        let marker = inspect.query_row(
2171            "SELECT value FROM metadata WHERE key = 'schema_version'",
2172            [],
2173            |row| row.get::<_, String>(0),
2174        )?;
2175        require_eq(&marker, &"22".to_string(), "failed migration marker")?;
2176        require_eq(
2177            &inspect.query_row(
2178                "SELECT COUNT(*) FROM pragma_table_info('worktree_registrations')
2179                 WHERE name = 'last_root_identity'",
2180                [],
2181                |row| row.get::<_, i64>(0),
2182            )?,
2183            &0,
2184            "failed migration native columns",
2185        )?;
2186        inspect.execute(
2187            "UPDATE worktree_registrations SET last_root = ?1",
2188            [root_display.as_str()],
2189        )?;
2190        let retried = AtlasStore::open_for_project(&failed_database_path, &control)?;
2191        require_eq(
2192            &retried.worktree_registrations(false)?.len(),
2193            &1,
2194            "retried migration registration",
2195        )?;
2196        Ok(())
2197    }
2198
2199    #[test]
2200    fn schema_twentytwo_retired_history_survives_path_reuse_as_files() -> Result<(), Box<dyn Error>>
2201    {
2202        let temp = tempfile::tempdir()?;
2203        let base = CanonicalProjectRoot::from_path(temp.path())?.into_path();
2204        let control = base.join("control");
2205        let common = base.join("common.git");
2206        let administrative = common.join("worktrees/legacy");
2207        let root = base.join("legacy");
2208        for path in [&control, &common, &administrative, &root] {
2209            fs::create_dir_all(path)?;
2210        }
2211        for field in [
2212            "git_common_directory",
2213            "git_administrative_directory",
2214            "last_root",
2215        ] {
2216            let reused = base.join(field);
2217            fs::create_dir(&reused)?;
2218            let historical = CanonicalProjectRoot::from_path(&reused)?;
2219            fs::remove_dir(&reused)?;
2220            fs::write(&reused, "unrelated replacement file")?;
2221            let paths = [
2222                if field == "git_common_directory" {
2223                    &reused
2224                } else {
2225                    &common
2226                },
2227                if field == "git_administrative_directory" {
2228                    &reused
2229                } else {
2230                    &administrative
2231                },
2232                if field == "last_root" { &reused } else { &root },
2233            ];
2234            let database = control.join(format!("reused-{field}.db"));
2235            let store = AtlasStore::open_for_project(&database, &control)?;
2236            store.connection.execute(
2237                "INSERT INTO worktree_registrations(
2238                    alias, state, git_common_directory, git_administrative_directory,
2239                    git_administrative_identity, last_root, created_at_epoch
2240                 ) VALUES('legacy', 'active', ?1, ?2, ?3, ?4, 1)",
2241                params![
2242                    projectatlas_core::normalize_native_path_display(paths[0]),
2243                    projectatlas_core::normalize_native_path_display(paths[1]),
2244                    administrative_identity(21),
2245                    projectatlas_core::normalize_native_path_display(paths[2]),
2246                ],
2247            )?;
2248            crate::schema::drop_worktree_native_identity_schema(&store.connection)?;
2249            store.connection.execute(
2250                "UPDATE metadata SET value = '22' WHERE key = 'schema_version'",
2251                [],
2252            )?;
2253            drop(store);
2254            require(
2255                matches!(
2256                    AtlasStore::open_for_project(&database, &control),
2257                    Err(DbError::ProjectRootIdentity(_))
2258                ),
2259                "active registration admitted a replacement file",
2260            )?;
2261            let inspect = Connection::open(&database)?;
2262            require_eq(
2263                &inspect.query_row(
2264                    "SELECT value FROM metadata WHERE key = 'schema_version'",
2265                    [],
2266                    |row| row.get::<_, String>(0),
2267                )?,
2268                &"22".to_string(),
2269                "active path failure rolled back migration",
2270            )?;
2271            inspect.execute_batch(
2272                "UPDATE worktree_registrations SET state = 'retired', retired_at_epoch = 2;",
2273            )?;
2274            drop(inspect);
2275            let migrated = AtlasStore::open_for_project(&database, &control)?;
2276            let rows = migrated.worktree_registrations(true)?;
2277            require_eq(&rows.len(), &1, "retained historical row")?;
2278            let row = &rows[0];
2279            let recovered = match field {
2280                "git_common_directory" => &row.git_common_directory_identity,
2281                "git_administrative_directory" => &row.git_administrative_directory_identity,
2282                _ => &row.last_root_identity,
2283            };
2284            require_eq(
2285                recovered,
2286                &historical,
2287                "historical native identity after path reuse",
2288            )?;
2289            require_eq(
2290                &row.state,
2291                &WorktreeRegistrationState::Retired,
2292                "retained retirement state",
2293            )?;
2294            require_eq(
2295                &fs::read_to_string(&reused)?,
2296                &"unrelated replacement file".to_string(),
2297                "replacement file untouched",
2298            )?;
2299            drop(migrated);
2300            let reopened = AtlasStore::open_for_project(&database, &control)?;
2301            require_eq(
2302                &reopened.worktree_registrations(true)?,
2303                &rows,
2304                "retired migration survives reopen",
2305            )?;
2306        }
2307        Ok(())
2308    }
2309
2310    #[cfg(any(unix, windows))]
2311    #[test]
2312    fn schema_twentytwo_history_does_not_follow_replacement_directory_links()
2313    -> Result<(), Box<dyn Error>> {
2314        let temp = tempfile::tempdir()?;
2315        let base = temp.path().canonicalize()?;
2316        let target = base.join("unrelated");
2317        fs::create_dir(&target)?;
2318        fs::write(target.join("sentinel"), "unrelated contents")?;
2319        for replaced_field in 0..3 {
2320            let fixture = base.join(format!("field-{replaced_field}"));
2321            fs::create_dir(&fixture)?;
2322            let paths = [
2323                fixture.join("common"),
2324                fixture.join("admin"),
2325                fixture.join("root"),
2326            ];
2327            for path in &paths {
2328                fs::create_dir(path)?;
2329            }
2330            let expected = paths
2331                .iter()
2332                .map(|path| CanonicalProjectRoot::from_path(path))
2333                .collect::<Result<Vec<_>, _>>()?;
2334            fs::rename(&paths[replaced_field], fixture.join("moved"))?;
2335            #[cfg(unix)]
2336            std::os::unix::fs::symlink(&target, &paths[replaced_field])?;
2337            #[cfg(windows)]
2338            {
2339                let output = std::process::Command::new("cmd")
2340                    .args(["/D", "/C", "mklink", "/J"])
2341                    .arg(&paths[replaced_field])
2342                    .arg(&target)
2343                    .output()?;
2344                require(
2345                    output.status.success(),
2346                    &format!("junction fixture failed: {output:?}"),
2347                )?;
2348            }
2349            for state in ["active", "retired"] {
2350                let database = fixture.join(format!("{state}.db"));
2351                let store = AtlasStore::open_for_project(&database, &fixture)?;
2352                crate::schema::drop_worktree_native_identity_schema(&store.connection)?;
2353                store.connection.execute(
2354                    "INSERT INTO worktree_registrations(
2355                        alias, state, git_common_directory, git_administrative_directory,
2356                        git_administrative_identity, last_root, created_at_epoch, retired_at_epoch
2357                     ) VALUES('legacy', ?1, ?2, ?3, ?4, ?5, 1, ?6)",
2358                    params![
2359                        state,
2360                        projectatlas_core::normalize_native_path_display(&paths[0]),
2361                        projectatlas_core::normalize_native_path_display(&paths[1]),
2362                        administrative_identity(22),
2363                        projectatlas_core::normalize_native_path_display(&paths[2]),
2364                        (state == "retired").then_some(2),
2365                    ],
2366                )?;
2367                if replaced_field == 2 {
2368                    let other_admin = fixture.join("other-admin");
2369                    fs::create_dir_all(&other_admin)?;
2370                    store.connection.execute(
2371                        "INSERT INTO worktree_registrations(
2372                            alias, state, git_common_directory, git_administrative_directory,
2373                            git_administrative_identity, last_root, created_at_epoch
2374                         ) VALUES('other', 'active', ?1, ?2, ?3, ?4, 1)",
2375                        params![
2376                            projectatlas_core::normalize_native_path_display(&paths[0]),
2377                            projectatlas_core::normalize_native_path_display(&other_admin),
2378                            administrative_identity(23),
2379                            projectatlas_core::normalize_native_path_display(&target),
2380                        ],
2381                    )?;
2382                }
2383                store.connection.execute(
2384                    "UPDATE metadata SET value = '22' WHERE key = 'schema_version'",
2385                    [],
2386                )?;
2387                drop(store);
2388                let migrated = AtlasStore::open_for_project(&database, &fixture)?;
2389                let rows = migrated.worktree_registrations(true)?;
2390                require_eq(
2391                    &rows.len(),
2392                    &(1 + usize::from(replaced_field == 2)),
2393                    "historical registrations preserved without a false collision",
2394                )?;
2395                let alias = WorktreeAlias::parse("legacy")?;
2396                let row = rows
2397                    .iter()
2398                    .find(|row| row.alias == alias)
2399                    .ok_or_else(|| io::Error::other("legacy registration missing"))?;
2400                for (actual, expected) in [
2401                    &row.git_common_directory_identity,
2402                    &row.git_administrative_directory_identity,
2403                    &row.last_root_identity,
2404                ]
2405                .into_iter()
2406                .zip(&expected)
2407                {
2408                    require_eq(
2409                        actual,
2410                        expected,
2411                        "migration must not adopt a replacement target",
2412                    )?;
2413                }
2414                drop(migrated);
2415                let reopened = AtlasStore::open_for_project(&database, &fixture)?;
2416                require_eq(
2417                    &reopened.worktree_registrations(true)?,
2418                    &rows,
2419                    "stable migrated history",
2420                )?;
2421            }
2422        }
2423        require_eq(
2424            &fs::read_to_string(target.join("sentinel"))?,
2425            &"unrelated contents".to_string(),
2426            "replacement target untouched",
2427        )?;
2428        Ok(())
2429    }
2430
2431    #[cfg(windows)]
2432    #[test]
2433    fn schema_twentytwo_worktree_identity_migration_rejects_unprovable_verbatim_paths()
2434    -> Result<(), Box<dyn Error>> {
2435        let temp = tempfile::tempdir()?;
2436        let base = temp.path().canonicalize()?;
2437        let control = base.join("control");
2438        let common = base.join("common.git");
2439        let administrative = common.join("worktrees/legacy");
2440        let root = base.join("legacy");
2441        for path in [&control, &common, &administrative, &root] {
2442            fs::create_dir_all(path)?;
2443        }
2444        let common_display = projectatlas_core::normalize_native_path_display(&common);
2445        let administrative_display =
2446            projectatlas_core::normalize_native_path_display(&administrative);
2447        let root_display = projectatlas_core::normalize_native_path_display(&root);
2448        let missing_display =
2449            projectatlas_core::normalize_native_path_display(base.join("a".repeat(240)));
2450        let suffix_units = r"\.projectatlas\projectatlas.db".encode_utf16().count();
2451        let prefix = projectatlas_core::normalize_native_path_display(&base);
2452        let suffix_threshold_display = format!(
2453            "{prefix}/{}",
2454            "b".repeat(260 - suffix_units - prefix.encode_utf16().count() - 1)
2455        );
2456        let ambiguous_common_display = format!("{common_display}.");
2457        let ambiguous_administrative_display = format!("{administrative_display}.");
2458        let ambiguous_root_display = format!("{root_display}.");
2459
2460        for (case, field, rejected_display) in [
2461            ("missing-common", "git_common_directory", &missing_display),
2462            (
2463                "missing-administrative",
2464                "git_administrative_directory",
2465                &missing_display,
2466            ),
2467            ("missing-root", "last_root", &missing_display),
2468            (
2469                "suffix-common",
2470                "git_common_directory",
2471                &suffix_threshold_display,
2472            ),
2473            (
2474                "suffix-administrative",
2475                "git_administrative_directory",
2476                &suffix_threshold_display,
2477            ),
2478            ("suffix-root", "last_root", &suffix_threshold_display),
2479            (
2480                "ambiguous-common",
2481                "git_common_directory",
2482                &ambiguous_common_display,
2483            ),
2484            (
2485                "ambiguous-administrative",
2486                "git_administrative_directory",
2487                &ambiguous_administrative_display,
2488            ),
2489            ("ambiguous-root", "last_root", &ambiguous_root_display),
2490        ] {
2491            let database = control.join(format!("unprovable-{case}.db"));
2492            let store = AtlasStore::open_for_project(&database, &control)?;
2493            let common_value = if field == "git_common_directory" {
2494                rejected_display
2495            } else {
2496                &common_display
2497            };
2498            let administrative_value = if field == "git_administrative_directory" {
2499                rejected_display
2500            } else {
2501                &administrative_display
2502            };
2503            let root_value = if field == "last_root" {
2504                rejected_display
2505            } else {
2506                &root_display
2507            };
2508            store.connection.execute(
2509                "INSERT INTO worktree_registrations(
2510                    alias, state, git_common_directory, git_administrative_directory,
2511                    git_administrative_identity, last_root, created_at_epoch
2512                 ) VALUES('legacy', 'active', ?1, ?2, ?3, ?4, 1)",
2513                params![
2514                    common_value,
2515                    administrative_value,
2516                    administrative_identity(20),
2517                    root_value,
2518                ],
2519            )?;
2520            crate::schema::drop_worktree_native_identity_schema(&store.connection)?;
2521            store.connection.execute(
2522                "UPDATE metadata SET value = '22' WHERE key = 'schema_version'",
2523                [],
2524            )?;
2525            drop(store);
2526
2527            let migration_result = AtlasStore::open_for_project(&database, &control);
2528            require(
2529                matches!(
2530                    &migration_result,
2531                    Err(DbError::WorktreeRegistrationMigrationIdentityUnavailable {
2532                        field: failed_field,
2533                        registration_id: 1,
2534                    }) if failed_field == &field
2535                ),
2536                &format!(
2537                    "unprovable {field} migration returned the wrong result: {:?}",
2538                    migration_result.as_ref().err().map(ToString::to_string)
2539                ),
2540            )?;
2541
2542            let inspect = Connection::open(&database)?;
2543            let marker = inspect.query_row(
2544                "SELECT value FROM metadata WHERE key = 'schema_version'",
2545                [],
2546                |row| row.get::<_, String>(0),
2547            )?;
2548            require_eq(&marker, &"22".to_string(), "failed migration marker")?;
2549            require_eq(
2550                &inspect.query_row(
2551                    "SELECT COUNT(*) FROM pragma_table_info('worktree_registrations')
2552                     WHERE name IN (
2553                         'git_common_directory_identity',
2554                         'git_administrative_directory_identity',
2555                         'last_root_identity'
2556                     )",
2557                    [],
2558                    |row| row.get::<_, i64>(0),
2559                )?,
2560                &0,
2561                "failed migration native columns",
2562            )?;
2563            require_eq(
2564                &inspect.query_row(
2565                    "SELECT COUNT(*) FROM sqlite_schema
2566                     WHERE type = 'index' AND name IN (
2567                         'idx_worktree_registrations_active_native_administrative_directory',
2568                         'idx_worktree_registrations_active_native_root'
2569                     )",
2570                    [],
2571                    |row| row.get::<_, i64>(0),
2572                )?,
2573                &0,
2574                "failed migration native indexes",
2575            )?;
2576            let legacy_paths = inspect.query_row(
2577                "SELECT git_common_directory, git_administrative_directory, last_root
2578                 FROM worktree_registrations WHERE registration_id = 1",
2579                [],
2580                |row| {
2581                    Ok((
2582                        row.get::<_, String>(0)?,
2583                        row.get::<_, String>(1)?,
2584                        row.get::<_, String>(2)?,
2585                    ))
2586                },
2587            )?;
2588            require(
2589                [&legacy_paths.0, &legacy_paths.1, &legacy_paths.2]
2590                    .into_iter()
2591                    .any(|path| path == rejected_display),
2592                "failed migration changed the unprovable legacy path",
2593            )?;
2594            let repair = match field {
2595                "git_common_directory" => common_display.as_str(),
2596                "git_administrative_directory" => administrative_display.as_str(),
2597                _ => root_display.as_str(),
2598            };
2599            inspect.execute(
2600                &format!("UPDATE worktree_registrations SET {field} = ?1"),
2601                [repair],
2602            )?;
2603            drop(inspect);
2604
2605            let retried = AtlasStore::open_for_project(&database, &control)?;
2606            let registration = retried.worktree_registration(&WorktreeAlias::parse("legacy")?)?;
2607            require_eq(
2608                &registration.git_common_directory_identity,
2609                &CanonicalProjectRoot::from_path(&common)?,
2610                "repaired common identity",
2611            )?;
2612            require_eq(
2613                &registration.git_administrative_directory_identity,
2614                &CanonicalProjectRoot::from_path(&administrative)?,
2615                "repaired administrative identity",
2616            )?;
2617            require_eq(
2618                &registration.last_root_identity,
2619                &CanonicalProjectRoot::from_path(&root)?,
2620                "repaired root identity",
2621            )?;
2622        }
2623        Ok(())
2624    }
2625
2626    #[test]
2627    fn schema_twentytwo_worktree_identity_migration_rejects_legacy_collisions_atomically()
2628    -> Result<(), Box<dyn Error>> {
2629        let temp = tempfile::tempdir()?;
2630        let control = temp.path().join("control");
2631        let common = temp.path().join("common.git");
2632        let administrative_a = common.join("worktrees/legacy-a");
2633        let administrative_b = common.join("worktrees/legacy-b");
2634        let administrative_distinct = common.join("worktrees/distinct");
2635        let collision_root = temp.path().join("legacy-\u{fffd}-root");
2636        let repaired_root = temp.path().join("repaired-root");
2637        let unaffected_root = temp.path().join("unaffected-root");
2638        for path in [
2639            &control,
2640            &common,
2641            &administrative_a,
2642            &administrative_b,
2643            &administrative_distinct,
2644            &collision_root,
2645            &repaired_root,
2646            &unaffected_root,
2647        ] {
2648            fs::create_dir_all(path)?;
2649        }
2650
2651        let database = control.join("projectatlas.db");
2652        let store = AtlasStore::open_for_project(&database, &control)?;
2653        let common_display = projectatlas_core::normalize_native_path_display(&common);
2654        let collision_display = projectatlas_core::normalize_native_path_display(&collision_root);
2655        let repaired_display = projectatlas_core::normalize_native_path_display(&repaired_root);
2656        let unaffected_display = projectatlas_core::normalize_native_path_display(&unaffected_root);
2657        crate::schema::drop_worktree_native_identity_schema(&store.connection)?;
2658        store.connection.execute(
2659            "UPDATE metadata SET value = '22' WHERE key = 'schema_version'",
2660            [],
2661        )?;
2662        for (alias, administrative, root, identity) in [
2663            (
2664                "legacy-a",
2665                &administrative_a,
2666                &collision_display,
2667                administrative_identity(11),
2668            ),
2669            (
2670                "legacy-b",
2671                &administrative_b,
2672                &collision_display,
2673                administrative_identity(12),
2674            ),
2675            (
2676                "distinct",
2677                &administrative_distinct,
2678                &unaffected_display,
2679                administrative_identity(13),
2680            ),
2681        ] {
2682            let administrative_display =
2683                projectatlas_core::normalize_native_path_display(administrative);
2684            store.connection.execute(
2685                "INSERT INTO worktree_registrations(
2686                    alias, state, git_common_directory, git_administrative_directory,
2687                    git_administrative_identity, last_root, created_at_epoch
2688                 ) VALUES(?1, 'active', ?2, ?3, ?4, ?5, 1)",
2689                params![
2690                    alias,
2691                    common_display,
2692                    administrative_display,
2693                    identity,
2694                    root,
2695                ],
2696            )?;
2697        }
2698        drop(store);
2699
2700        let migration_result = AtlasStore::open_for_project(&database, &control);
2701        require(
2702            matches!(
2703                &migration_result,
2704                Err(DbError::WorktreeRegistrationMigrationConflict {
2705                    field: "last_root_identity",
2706                    first_registration_id: 1,
2707                    second_registration_id: 2,
2708                })
2709            ),
2710            &format!(
2711                "legacy native identity collision was not rejected deterministically: {:?}",
2712                migration_result.as_ref().err().map(ToString::to_string)
2713            ),
2714        )?;
2715
2716        let inspect = Connection::open(&database)?;
2717        let marker = inspect.query_row(
2718            "SELECT value FROM metadata WHERE key = 'schema_version'",
2719            [],
2720            |row| row.get::<_, String>(0),
2721        )?;
2722        require_eq(&marker, &"22".to_string(), "collision migration marker")?;
2723        require_eq(
2724            &inspect.query_row(
2725                "SELECT COUNT(*) FROM pragma_table_info('worktree_registrations')
2726                 WHERE name IN (
2727                     'git_common_directory_identity',
2728                     'git_administrative_directory_identity',
2729                     'last_root_identity'
2730                 )",
2731                [],
2732                |row| row.get::<_, i64>(0),
2733            )?,
2734            &0,
2735            "collision migration native columns",
2736        )?;
2737        require_eq(
2738            &inspect.query_row(
2739                "SELECT COUNT(*) FROM sqlite_schema
2740                 WHERE type = 'index' AND name IN (
2741                     'idx_worktree_registrations_active_native_administrative_directory',
2742                     'idx_worktree_registrations_active_native_root'
2743                 )",
2744                [],
2745                |row| row.get::<_, i64>(0),
2746            )?,
2747            &0,
2748            "collision migration native indexes",
2749        )?;
2750        require_eq(
2751            &inspect.query_row(
2752                "SELECT COUNT(*) FROM worktree_registrations
2753                 WHERE state = 'active'",
2754                [],
2755                |row| row.get::<_, i64>(0),
2756            )?,
2757            &3,
2758            "collision migration registered rows",
2759        )?;
2760        let legacy_rows = inspect
2761            .prepare(
2762                "SELECT alias, last_root FROM worktree_registrations
2763                 ORDER BY registration_id",
2764            )?
2765            .query_map([], |row| {
2766                Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
2767            })?
2768            .collect::<Result<Vec<_>, _>>()?;
2769        require_eq(
2770            &legacy_rows,
2771            &vec![
2772                ("legacy-a".to_string(), collision_display.clone()),
2773                ("legacy-b".to_string(), collision_display.clone()),
2774                ("distinct".to_string(), unaffected_display.clone()),
2775            ],
2776            "collision migration preserved legacy rows",
2777        )?;
2778        inspect.execute(
2779            "UPDATE worktree_registrations SET last_root = ?1 WHERE alias = 'legacy-b'",
2780            [repaired_display.as_str()],
2781        )?;
2782        drop(inspect);
2783
2784        let retried = AtlasStore::open_for_project(&database, &control)?;
2785        let legacy_a = retried.worktree_registration(&WorktreeAlias::parse("legacy-a")?)?;
2786        let legacy_b = retried.worktree_registration(&WorktreeAlias::parse("legacy-b")?)?;
2787        let distinct = retried.worktree_registration(&WorktreeAlias::parse("distinct")?)?;
2788        require_eq(
2789            &legacy_a.last_root,
2790            &collision_display,
2791            "collision migration first row",
2792        )?;
2793        require_eq(
2794            &legacy_b.last_root,
2795            &repaired_display,
2796            "collision migration repaired row",
2797        )?;
2798        require_eq(
2799            &distinct.last_root,
2800            &unaffected_display,
2801            "collision migration unaffected row",
2802        )?;
2803        require(
2804            legacy_a.last_root_identity != legacy_b.last_root_identity
2805                && legacy_b.last_root_identity != distinct.last_root_identity
2806                && legacy_a.last_root_identity != distinct.last_root_identity,
2807            "collision migration did not preserve distinct native identities",
2808        )?;
2809        require_eq(
2810            &retried.worktree_registrations(false)?.len(),
2811            &3,
2812            "collision migration retry registration count",
2813        )?;
2814        require_eq(
2815            &retried.connection.query_row(
2816                "SELECT COUNT(*) FROM sqlite_schema
2817                 WHERE type = 'index' AND name IN (
2818                     'idx_worktree_registrations_active_native_administrative_directory',
2819                     'idx_worktree_registrations_active_native_root'
2820                 )",
2821                [],
2822                |row| row.get::<_, i64>(0),
2823            )?,
2824            &2,
2825            "collision migration native indexes after retry",
2826        )?;
2827        for (identity, index, column) in [
2828            (
2829                legacy_a.git_administrative_directory_identity,
2830                "idx_worktree_registrations_active_native_administrative_directory",
2831                "git_administrative_directory_identity",
2832            ),
2833            (
2834                legacy_a.last_root_identity,
2835                "idx_worktree_registrations_active_native_root",
2836                "last_root_identity",
2837            ),
2838        ] {
2839            let query = format!(
2840                "EXPLAIN QUERY PLAN
2841                 SELECT registration_id FROM worktree_registrations
2842                       INDEXED BY {index}
2843                 WHERE state = 'active' AND {column} = ?1"
2844            );
2845            let plan = retried
2846                .connection
2847                .prepare(&query)?
2848                .query_map(params![identity.encode()?], |row| row.get::<_, String>(3))?
2849                .collect::<Result<Vec<_>, _>>()?;
2850            require(
2851                plan.iter().any(|detail| detail.contains(index)),
2852                &format!("retry query plan omitted {index}: {plan:?}"),
2853            )?;
2854        }
2855        Ok(())
2856    }
2857
2858    #[test]
2859    fn hot_registry_and_aggregate_lookups_use_owning_indexes() -> Result<(), Box<dyn Error>> {
2860        let connection = Connection::open_in_memory()?;
2861        crate::schema::initialize(&connection, None)?;
2862        for (sql, index) in [
2863            (
2864                "EXPLAIN QUERY PLAN
2865                 SELECT registration_id FROM worktree_registrations
2866                       INDEXED BY idx_worktree_registrations_active_alias
2867                 WHERE state = 'active' AND alias = 'issue-430'",
2868                "idx_worktree_registrations_active_alias",
2869            ),
2870            (
2871                "EXPLAIN QUERY PLAN
2872                 SELECT registration_id FROM worktree_registrations
2873                       INDEXED BY idx_worktree_registrations_active_administrative_directory
2874                 WHERE state = 'active' AND git_administrative_directory = 'admin'",
2875                "idx_worktree_registrations_active_administrative_directory",
2876            ),
2877            (
2878                "EXPLAIN QUERY PLAN
2879                 SELECT registration_id FROM worktree_registrations
2880                       INDEXED BY idx_worktree_registrations_active_administrative_identity
2881                 WHERE state = 'active' AND git_administrative_identity = 'identity'",
2882                "idx_worktree_registrations_active_administrative_identity",
2883            ),
2884            (
2885                "EXPLAIN QUERY PLAN
2886                 SELECT registration_id FROM worktree_registrations
2887                       INDEXED BY idx_worktree_registrations_active_native_administrative_directory
2888                 WHERE state = 'active' AND git_administrative_directory_identity = zeroblob(3)",
2889                "idx_worktree_registrations_active_native_administrative_directory",
2890            ),
2891            (
2892                "EXPLAIN QUERY PLAN
2893                 SELECT registration_id FROM worktree_registrations
2894                       INDEXED BY idx_worktree_registrations_active_native_root
2895                 WHERE state = 'active' AND last_root_identity = zeroblob(3)",
2896                "idx_worktree_registrations_active_native_root",
2897            ),
2898            (
2899                "EXPLAIN QUERY PLAN
2900                 SELECT registration_id FROM worktree_registrations
2901                       INDEXED BY idx_worktree_registrations_active_project
2902                 WHERE state = 'active' AND project_instance_id = zeroblob(16)",
2903                "idx_worktree_registrations_active_project",
2904            ),
2905            (
2906                "EXPLAIN QUERY PLAN
2907                 SELECT registration_id FROM worktree_usage_aggregates
2908                       INDEXED BY idx_worktree_usage_aggregates_day_registration
2909                 WHERE day_epoch = -1 AND source_kind = 'synchronized'",
2910                "idx_worktree_usage_aggregates_day_registration",
2911            ),
2912        ] {
2913            let plan = connection
2914                .prepare(sql)?
2915                .query_map([], |row| row.get::<_, String>(3))?
2916                .collect::<Result<Vec<_>, _>>()?;
2917            require(
2918                plan.iter().any(|detail| detail.contains(index)),
2919                &format!("query plan omitted {index}: {plan:?}"),
2920            )?;
2921        }
2922        Ok(())
2923    }
2924}