DGGHHKY6RVX4GMFAMUURMOQAOR4PRW6GBAJXYBAOAAR6AEBW6QYQC GSLFNY4P6HJICX5WBZNWRSAQSWLWSTX3MFN4A35WSYOTSGYRXSDQC XVJ2RNWUSHVSQKNHU54XBIY5E7VDE6QUPTVIV52GRBAQAO7WOQOAC V2HX6HEB2OBNI4IMWD5XJN3RKAZYHAFJAJAPFP3BFYFZVZVEYN6AC JMDW37LVYJAWULK4RREBRC6OLNVMA5PO5TLN6JIZCTZELL7YF4MQC WVNOO43BIQWP2QT3GLK2CIIAFHD7NBCZOMIB7BZMLEJP6LSBBD5QC XU7FTYK3YAM5TADBGEJZ44UJ6LNJ7YVABSFEXKOBGUHXBFHVLR2AC T2WZBTVFHVWPKL6AKEWSEVQBR3HWWWUPUNUP2MULF4WXEAZP46KQC LBWQJEDHCNUNMEJWXILGBGYZUKQI7CDAMH2BD44HULM77SVH5UYQC BZ6KQRYDMP4PWYJRL62XXIUXLTBKEASIKSAJIQPZS6DKDSYKA76QC KZKLAINJJWZ64T5MUZT34LJVQIKBTKZ6EJGD7C7TTSSDGCHEDPMAC ZDZDASRTTRPJRIBAMNO3TB533XFELVYJQQGAMA3WOQYY35SVAQUQC E27ZWCDPESXDEHYZONCAKYL2U4K4ZLVXWX4453ICWSH4TGMQI4KQC QFPEKXL5OUKLT4WECMATSOHWYM24QPHKS6WZAAI5BAEQSAGAK6CQC 2P27XV3DGJCRA4SNJENCJYZLPR2XWZMTY7CGYYSJOY4UMDVVO25AC DD3LCTLZFDIPVXXSG7RDINZCQ7NGKVG3X52OFVGVZIISD5VPF35QC JAT3DXOLENZZGXE2NYFF3TVQAQIXMMNYO234ETKQGC2CRHJVZERQC I4CMOMXFJ3Y4AY5LPA7MDLWVHJ674IRFYLXCEXCC5ZARLCWSKCAAC }// handleFmtChunk processes the fmt chunk and updates info.func handleFmtChunk(file *os.File, chunkSize int64, info *wavChunkInfo) error {fmtData := make([]byte, chunkSize)if _, err := io.ReadFull(file, fmtData); err != nil {return fmt.Errorf("failed to read fmt chunk: %w", err)}if len(fmtData) >= 16 {info.channels = int(binary.LittleEndian.Uint16(fmtData[2:4]))info.sampleRate = int(binary.LittleEndian.Uint32(fmtData[4:8]))info.bitsPerSample = int(binary.LittleEndian.Uint16(fmtData[14:16]))}return nil
// skipUnknownChunk skips an unknown chunk and its padding.func skipUnknownChunk(file *os.File, chunkSize int64) error {if _, err := file.Seek(chunkSize, io.SeekCurrent); err != nil {return fmt.Errorf("failed to skip chunk: %w", err)}if chunkSize%2 != 0 {if _, err := file.Seek(1, io.SeekCurrent); err != nil {return fmt.Errorf("failed to skip padding: %w", err)}}return nil}
// detectDateFormat analyzes filenames to determine the date formatfunc detectDateFormat(filenames []string) (DateFormat, error) {// Extract all date parts from filenamesvar parts []datePartsvar has8Digit bool
// datePartsResult holds the result of collecting date parts from filenamestype datePartsResult struct {parts []datePartshas8Digit bool}
// Compare uniqueness of x1 (first 2 digits) vs x2 (last 2 digits)// Day values vary more than year values across recordingsuniqueX1 := countUnique(parts, func(p dateParts) int { return p.x1 })uniqueX2 := countUnique(parts, func(p dateParts) int { return p.x2 })
uniqueX1 := countUnique(result.parts, func(p dateParts) int { return p.x1 })uniqueX2 := countUnique(result.parts, func(p dateParts) int { return p.x2 })
}// shouldSkipHidden checks if a path should be skipped due to hidden file rules.func shouldSkipHidden(name, path, rootPath string, info os.FileInfo, skipHidden bool) (bool, error) {if !skipHidden || !strings.HasPrefix(name, ".") || path == rootPath {return false, nil}if info.IsDir() {return true, filepath.SkipDir}return true, nil
// shouldSkipDir checks if a directory should be skipped based on prefixes.func shouldSkipDir(name, path, rootPath string, skipPrefixes []string) bool {if path == rootPath {return false}for _, prefix := range skipPrefixes {if strings.HasPrefix(name, prefix) {return true}}return false}
if opts.SkipHidden && strings.HasPrefix(name, ".") && path != rootPath {if info.IsDir() {return filepath.SkipDir}return nil
if skip, skipErr := shouldSkipHidden(name, path, rootPath, info, opts.SkipHidden); skip {return skipErr
if info.IsDir() && path != rootPath {for _, prefix := range opts.SkipPrefixes {if strings.HasPrefix(name, prefix) {return filepath.SkipDir}
if info.IsDir() {if shouldSkipDir(name, path, rootPath, opts.SkipPrefixes) {return filepath.SkipDir
// Create temp folder with a WAV filetmpDir := t.TempDir()wavPath := filepath.Join(tmpDir, "test_recording.wav")hash := createTestWAV(t, wavPath)
// Import to unstructured datasetoutput, err := ImportUnstructured(ctx, ImportUnstructuredInput{DBPath: dbPath,DatasetID: "dstest000002", // unstructured datasetFolderPath: tmpDir,Recursive: new(true),})if err != nil {t.Fatalf("ImportUnstructured failed: %v", err)}
// Import to unstructured datasetoutput, err := ImportUnstructured(ctx, ImportUnstructuredInput{DBPath: dbPath,DatasetID: "dstest000002", // unstructured datasetFolderPath: tmpDir,Recursive: new(true),})if err != nil {t.Fatalf("ImportUnstructured failed: %v", err)}
// Verify outputif output.TotalFiles != 1 {t.Errorf("expected 1 total file, got %d", output.TotalFiles)}if output.ImportedFiles != 1 {t.Errorf("expected 1 imported file, got %d", output.ImportedFiles)}if output.SkippedFiles != 0 {t.Errorf("expected 0 skipped files, got %d", output.SkippedFiles)}if len(output.Errors) != 0 {t.Errorf("unexpected errors: %v", output.Errors)}
// Verify outputif output.TotalFiles != 1 {t.Errorf("expected 1 total file, got %d", output.TotalFiles)}if output.ImportedFiles != 1 {t.Errorf("expected 1 imported file, got %d", output.ImportedFiles)}if output.SkippedFiles != 0 {t.Errorf("expected 0 skipped files, got %d", output.SkippedFiles)}if len(output.Errors) != 0 {t.Errorf("unexpected errors: %v", output.Errors)}
// Verify file was inserted into databasedatabase, err := sql.Open("duckdb", dbPath)if err != nil {t.Fatalf("failed to open database for verification: %v", err)}defer database.Close()
// Verify file was inserted into databasedatabase, err := sql.Open("duckdb", dbPath)if err != nil {t.Fatalf("failed to open database for verification: %v", err)}defer database.Close()
var fileCount interr = database.QueryRow("SELECT COUNT(*) FROM file WHERE xxh64_hash = ? AND active = true", hash).Scan(&fileCount)if err != nil {t.Fatalf("failed to query file: %v", err)}if fileCount != 1 {t.Errorf("expected 1 file in database, got %d", fileCount)}
var fileCount interr = database.QueryRow("SELECT COUNT(*) FROM file WHERE xxh64_hash = ? AND active = true", hash).Scan(&fileCount)if err != nil {t.Fatalf("failed to query file: %v", err)}if fileCount != 1 {t.Errorf("expected 1 file in database, got %d", fileCount)}
// Verify file_dataset linkvar linkCount interr = database.QueryRow(`SELECT COUNT(*) FROM file_dataset fdJOIN file f ON fd.file_id = f.idWHERE f.xxh64_hash = ? AND fd.dataset_id = 'dstest000002'`, hash).Scan(&linkCount)if err != nil {t.Fatalf("failed to query file_dataset: %v", err)}if linkCount != 1 {t.Errorf("expected 1 file_dataset link, got %d", linkCount)}
// Verify file_dataset linkvar linkCount interr = database.QueryRow(`SELECT COUNT(*) FROM file_dataset fdJOIN file f ON fd.file_id = f.idWHERE f.xxh64_hash = ? AND fd.dataset_id = 'dstest000002'`, hash).Scan(&linkCount)if err != nil {t.Fatalf("failed to query file_dataset: %v", err)}if linkCount != 1 {t.Errorf("expected 1 file_dataset link, got %d", linkCount)}
// Verify location_id and cluster_id are NULL for unstructuredvar locID, clID sql.NullStringerr = database.QueryRow("SELECT location_id, cluster_id FROM file WHERE xxh64_hash = ?", hash).Scan(&locID, &clID)if err != nil {t.Fatalf("failed to query file: %v", err)}if locID.Valid {t.Errorf("expected NULL location_id for unstructured file, got %s", locID.String)}if clID.Valid {t.Errorf("expected NULL cluster_id for unstructured file, got %s", clID.String)}}
// Verify location_id and cluster_id are NULL for unstructuredvar locID, clID sql.NullStringerr = database.QueryRow("SELECT location_id, cluster_id FROM file WHERE xxh64_hash = ?", hash).Scan(&locID, &clID)if err != nil {t.Fatalf("failed to query file: %v", err)}if locID.Valid {t.Errorf("expected NULL location_id for unstructured file, got %s", locID.String)}if clID.Valid {t.Errorf("expected NULL cluster_id for unstructured file, got %s", clID.String)}})
func TestImportUnstructured_DuplicateHandling(t *testing.T) {ctx := context.Background()dbPath := setupFileBasedTestDB(t)
t.Run("duplicate handling - skip file with existing hash", func(t *testing.T) {dbPath := setupFileBasedTestDB(t)
// Create temp folder with a WAV filetmpDir := t.TempDir()wavPath := filepath.Join(tmpDir, "test_recording.wav")hash := createTestWAV(t, wavPath)
// Create temp folder with a WAV filetmpDir := t.TempDir()wavPath := filepath.Join(tmpDir, "test_recording.wav")hash := createTestWAV(t, wavPath)
// First import_, err := ImportUnstructured(ctx, ImportUnstructuredInput{DBPath: dbPath,DatasetID: "dstest000002",FolderPath: tmpDir,Recursive: new(true),})if err != nil {t.Fatalf("first import failed: %v", err)}
// First import_, err := ImportUnstructured(ctx, ImportUnstructuredInput{DBPath: dbPath,DatasetID: "dstest000002",FolderPath: tmpDir,Recursive: new(true),})if err != nil {t.Fatalf("first import failed: %v", err)}
// Second import of same file (should be skipped as duplicate)output, err := ImportUnstructured(ctx, ImportUnstructuredInput{DBPath: dbPath,DatasetID: "dstest000002",FolderPath: tmpDir,Recursive: new(true),})if err != nil {t.Fatalf("second import failed: %v", err)}
// Second import of same file (should be skipped as duplicate)output, err := ImportUnstructured(ctx, ImportUnstructuredInput{DBPath: dbPath,DatasetID: "dstest000002",FolderPath: tmpDir,Recursive: new(true),})if err != nil {t.Fatalf("second import failed: %v", err)}
// Verify outputif output.TotalFiles != 1 {t.Errorf("expected 1 total file, got %d", output.TotalFiles)}if output.ImportedFiles != 0 {t.Errorf("expected 0 imported files (duplicate), got %d", output.ImportedFiles)}if output.SkippedFiles != 1 {t.Errorf("expected 1 skipped file (duplicate), got %d", output.SkippedFiles)}
// Verify outputif output.TotalFiles != 1 {t.Errorf("expected 1 total file, got %d", output.TotalFiles)}if output.ImportedFiles != 0 {t.Errorf("expected 0 imported files (duplicate), got %d", output.ImportedFiles)}if output.SkippedFiles != 1 {t.Errorf("expected 1 skipped file (duplicate), got %d", output.SkippedFiles)}
// Verify only one file in database (not duplicated)database, err := sql.Open("duckdb", dbPath)if err != nil {t.Fatalf("failed to open database for verification: %v", err)}defer database.Close()
// Verify only one file in database (not duplicated)database, err := sql.Open("duckdb", dbPath)if err != nil {t.Fatalf("failed to open database for verification: %v", err)}defer database.Close()
var fileCount interr = database.QueryRow("SELECT COUNT(*) FROM file WHERE xxh64_hash = ? AND active = true", hash).Scan(&fileCount)if err != nil {t.Fatalf("failed to query file: %v", err)}if fileCount != 1 {t.Errorf("expected 1 file in database (not duplicated), got %d", fileCount)}}
var fileCount interr = database.QueryRow("SELECT COUNT(*) FROM file WHERE xxh64_hash = ? AND active = true", hash).Scan(&fileCount)if err != nil {t.Fatalf("failed to query file: %v", err)}if fileCount != 1 {t.Errorf("expected 1 file in database (not duplicated), got %d", fileCount)}})
func TestImportUnstructured_EdgeCases(t *testing.T) {ctx := context.Background()
t.Run("happy path - import single WAV file", func(t *testing.T) {dbPath := setupFileBasedTestDB(t)// Don't keep database open - ImportAudioFiles manages its own connections
// Create temp folder with a WAV filetmpDir := t.TempDir()wavPath := filepath.Join(tmpDir, "test_recording.wav")hash := createTestWAV(t, wavPath)
// Create temp folder with a WAV filetmpDir := t.TempDir()wavPath := filepath.Join(tmpDir, "test_recording.wav")hash := createTestWAV(t, wavPath)
// Importoutput, err := ImportAudioFiles(ctx, ImportAudioFilesInput{DBPath: dbPath,FolderPath: tmpDir,DatasetID: "dstest000001",LocationID: "loctest00001",ClusterID: "cltest000001",Recursive: new(true),})if err != nil {t.Fatalf("ImportAudioFiles failed: %v", err)}
// Importoutput, err := ImportAudioFiles(ctx, ImportAudioFilesInput{DBPath: dbPath,FolderPath: tmpDir,DatasetID: "dstest000001",LocationID: "loctest00001",ClusterID: "cltest000001",Recursive: new(true),})if err != nil {t.Fatalf("ImportAudioFiles failed: %v", err)}
// Verify outputif output.Summary.TotalFiles != 1 {t.Errorf("expected 1 total file, got %d", output.Summary.TotalFiles)}if output.Summary.ImportedFiles != 1 {t.Errorf("expected 1 imported file, got %d", output.Summary.ImportedFiles)}if output.Summary.SkippedFiles != 0 {t.Errorf("expected 0 skipped files, got %d", output.Summary.SkippedFiles)}if len(output.Errors) != 0 {t.Errorf("unexpected errors: %v", output.Errors)}
// Verify outputif output.Summary.TotalFiles != 1 {t.Errorf("expected 1 total file, got %d", output.Summary.TotalFiles)}if output.Summary.ImportedFiles != 1 {t.Errorf("expected 1 imported file, got %d", output.Summary.ImportedFiles)}if output.Summary.SkippedFiles != 0 {t.Errorf("expected 0 skipped files, got %d", output.Summary.SkippedFiles)}if len(output.Errors) != 0 {t.Errorf("unexpected errors: %v", output.Errors)}
// Verify file was inserted into database - open new connectiondatabase, err := sql.Open("duckdb", dbPath)if err != nil {t.Fatalf("failed to open database for verification: %v", err)}defer database.Close()
// Verify file was inserted into database - open new connectiondatabase, err := sql.Open("duckdb", dbPath)if err != nil {t.Fatalf("failed to open database for verification: %v", err)}defer database.Close()
var fileCount interr = database.QueryRow("SELECT COUNT(*) FROM file WHERE xxh64_hash = ? AND active = true", hash).Scan(&fileCount)if err != nil {t.Fatalf("failed to query file: %v", err)}if fileCount != 1 {t.Errorf("expected 1 file in database, got %d", fileCount)}
var fileCount interr = database.QueryRow("SELECT COUNT(*) FROM file WHERE xxh64_hash = ? AND active = true", hash).Scan(&fileCount)if err != nil {t.Fatalf("failed to query file: %v", err)}if fileCount != 1 {t.Errorf("expected 1 file in database, got %d", fileCount)}
// Verify file_dataset linkvar linkCount interr = database.QueryRow(`SELECT COUNT(*) FROM file_dataset fdJOIN file f ON fd.file_id = f.idWHERE f.xxh64_hash = ? AND fd.dataset_id = 'dstest000001'`, hash).Scan(&linkCount)if err != nil {t.Fatalf("failed to query file_dataset: %v", err)}if linkCount != 1 {t.Errorf("expected 1 file_dataset link, got %d", linkCount)}}
// Verify file_dataset linkvar linkCount interr = database.QueryRow(`SELECT COUNT(*) FROM file_dataset fdJOIN file f ON fd.file_id = f.idWHERE f.xxh64_hash = ? AND fd.dataset_id = 'dstest000001'`, hash).Scan(&linkCount)if err != nil {t.Fatalf("failed to query file_dataset: %v", err)}if linkCount != 1 {t.Errorf("expected 1 file_dataset link, got %d", linkCount)}})
func TestImportAudioFiles_DuplicateHandling(t *testing.T) {ctx := context.Background()dbPath := setupFileBasedTestDB(t)
t.Run("duplicate handling - skip file with existing hash", func(t *testing.T) {dbPath := setupFileBasedTestDB(t)// Don't keep database open - ImportAudioFiles manages its own connections
// Create temp folder with a WAV filetmpDir := t.TempDir()wavPath := filepath.Join(tmpDir, "test_recording.wav")hash := createTestWAV(t, wavPath)
// Create temp folder with a WAV filetmpDir := t.TempDir()wavPath := filepath.Join(tmpDir, "test_recording.wav")hash := createTestWAV(t, wavPath)
// First import_, err := ImportAudioFiles(ctx, ImportAudioFilesInput{DBPath: dbPath,FolderPath: tmpDir,DatasetID: "dstest000001",LocationID: "loctest00001",ClusterID: "cltest000001",Recursive: new(true),})if err != nil {t.Fatalf("first import failed: %v", err)}
// First import_, err := ImportAudioFiles(ctx, ImportAudioFilesInput{DBPath: dbPath,FolderPath: tmpDir,DatasetID: "dstest000001",LocationID: "loctest00001",ClusterID: "cltest000001",Recursive: new(true),})if err != nil {t.Fatalf("first import failed: %v", err)}
// Second import of same file (should be skipped as duplicate)output, err := ImportAudioFiles(ctx, ImportAudioFilesInput{DBPath: dbPath,FolderPath: tmpDir,DatasetID: "dstest000001",LocationID: "loctest00001",ClusterID: "cltest000001",Recursive: new(true),})if err != nil {t.Fatalf("second import failed: %v", err)}
// Second import of same file (should be skipped as duplicate)output, err := ImportAudioFiles(ctx, ImportAudioFilesInput{DBPath: dbPath,FolderPath: tmpDir,DatasetID: "dstest000001",LocationID: "loctest00001",ClusterID: "cltest000001",Recursive: new(true),})if err != nil {t.Fatalf("second import failed: %v", err)}
// Verify outputif output.Summary.TotalFiles != 1 {t.Errorf("expected 1 total file, got %d", output.Summary.TotalFiles)}if output.Summary.ImportedFiles != 0 {t.Errorf("expected 0 imported files (duplicate), got %d", output.Summary.ImportedFiles)}if output.Summary.SkippedFiles != 1 {t.Errorf("expected 1 skipped file (duplicate), got %d", output.Summary.SkippedFiles)}
// Verify outputif output.Summary.TotalFiles != 1 {t.Errorf("expected 1 total file, got %d", output.Summary.TotalFiles)}if output.Summary.ImportedFiles != 0 {t.Errorf("expected 0 imported files (duplicate), got %d", output.Summary.ImportedFiles)}if output.Summary.SkippedFiles != 1 {t.Errorf("expected 1 skipped file (duplicate), got %d", output.Summary.SkippedFiles)}
// Verify only one file in database (not duplicated) - open new connectiondatabase, err := sql.Open("duckdb", dbPath)if err != nil {t.Fatalf("failed to open database for verification: %v", err)}defer database.Close()
// Verify only one file in database (not duplicated) - open new connectiondatabase, err := sql.Open("duckdb", dbPath)if err != nil {t.Fatalf("failed to open database for verification: %v", err)}defer database.Close()
var fileCount interr = database.QueryRow("SELECT COUNT(*) FROM file WHERE xxh64_hash = ? AND active = true", hash).Scan(&fileCount)if err != nil {t.Fatalf("failed to query file: %v", err)}if fileCount != 1 {t.Errorf("expected 1 file in database (not duplicated), got %d", fileCount)}}
var fileCount interr = database.QueryRow("SELECT COUNT(*) FROM file WHERE xxh64_hash = ? AND active = true", hash).Scan(&fileCount)if err != nil {t.Fatalf("failed to query file: %v", err)}if fileCount != 1 {t.Errorf("expected 1 file in database (not duplicated), got %d", fileCount)}})
func TestImportAudioFiles_ErrorCases(t *testing.T) {ctx := context.Background()
// topologicalSort orders tables so dependencies come first (Kahn's algorithm).// Tables in cycles are appended at the end.func topologicalSort(tables map[string]bool, dependsOnMe map[string][]string) []string {
// countDependencies counts FK dependencies for each table.func countDependencies(tables map[string]bool, dependsOnMe map[string][]string) map[string]int {
}}return queue}// appendCyclicTables adds tables that are part of cycles to the result.func appendCyclicTables(result []string, tables map[string]bool) []string {if len(result) == len(tables) {return result}for table := range tables {if !slices.Contains(result, table) {result = append(result, table)
return result}// topologicalSort orders tables so dependencies come first (Kahn's algorithm).// Tables in cycles are appended at the end.func topologicalSort(tables map[string]bool, dependsOnMe map[string][]string) []string {fkCount := countDependencies(tables, dependsOnMe)queue := buildInitialQueue(tables, fkCount)
// Handle cycles: append remaining tablesif len(result) != len(tables) {for table := range tables {if !slices.Contains(result, table) {result = append(result, table)}}}return result
return appendCyclicTables(result, tables)
switch arg {case "--folder":f.folder = mustValue(args, &i, "--folder")case "--file":f.file = mustValue(args, &i, "--file")case "--filter":f.filter = mustValue(args, &i, "--filter")case "--species":f.species = mustValue(args, &i, "--species")case "--night":f.night = truei++case "--day":f.day = truei++case "--location":if f.location != "" {fmt.Fprintf(os.Stderr, "Error: --location can only be specified once\n")os.Exit(1)}f.location = mustValue(args, &i, "--location")case "--help", "-h":printPushCertaintyUsage()os.Exit(0)default:
if !parsePushCertaintyFlag(arg, args, &i, &f) {
}// parsePushCertaintyFlag handles a single flag. Returns false if unknown flag.func parsePushCertaintyFlag(arg string, args []string, i *int, f *pushCertaintyFlags) bool {switch arg {case "--folder":f.folder = mustValue(args, i, "--folder")case "--file":f.file = mustValue(args, i, "--file")case "--filter":f.filter = mustValue(args, i, "--filter")case "--species":f.species = mustValue(args, i, "--species")case "--night":f.night = true*i++case "--day":f.day = true*i++case "--location":if f.location != "" {fmt.Fprintf(os.Stderr, "Error: --location can only be specified once\n")os.Exit(1)}f.location = mustValue(args, i, "--location")case "--help", "-h":printPushCertaintyUsage()os.Exit(0)default:return false}return true
## [2026-05-19] tools/calls and tools/import refactoring complete — all 5 phases doneRefactored `tools/calls/` and `tools/import/` to improve code quality, test coverage, and interface consistency.### Phase 0: Tests for tools/import Critical Paths- Created `tools/import/test_helpers.go` with shared test utilities- Added tests for `ImportAudioFiles`, `ImportUnstructured`, `ImportSegments`, `writeIDsToDataFiles`, `BulkFileImport`- **Coverage increased from 20.7% to 71.9%**### Phase 1: Reduce Cyclomatic Complexity- Extracted `resolveLabelIDs`, `insertLabel`, `insertLabelMetadata` from `importSingleLabel`- Extracted `detectBirdaColumns`, `validateBirdaColumns` from `parseBirdaCSVHeader`- Extracted `validateAndParseShowImagesInput`, `resolveImageSize`, `resolveGraphicsProtocol`, `displaySegmentSpectrograms` from `CallsShowImages`- Fixed `scanWavFiles`, `CallsRemove`, `findMatchingLabels` complexity issues- **All functions now under complexity 10**### Phase 2: Split import_segments.go- Created `import_segments_prepare.go` (429 lines) — validation, scanning, ID lookup- Created `import_segments_db.go` (422 lines) — DB write, writeback- Reduced `import_segments.go` to 155 lines — orchestrator + types### Phase 3: Fix *sql.DB Usage- **3A (tools/):** Changed `validateClusterActive`, `validateClusterCyclicPattern`, `executeSQLQuery` to use `db.Querier`- **3A (tools/export.go):** Defined local `writer` interface; replaced `sql.Open` with `db.OpenWriteableDB`- **3A (db/schema.go):** Changed `GetFKOrder`, `buildFKDependencyGraph`, `collectAllTables` to `db.Querier`- **3B (tools/import/):** Changed all bulk functions to accept `Mutator` interface- Updated `ImportSegments`, `ImportFile` to use `WithReadDB`/`WithWriteTx` patterns- Raw `*sql.DB` now only appears in callback signatures and type assertions### Phase 4: Move Classify TUI-Adjacent Types to Dedicated Files- Created `classify_bindings.go` (123 lines) — `KeyBinding`, `BindingResult`, binding methods- Created `classify_format.go` (32 lines) — `FormatLabels`- Created `classify_io.go` (106 lines) — `LoadFilteredSegment`, `SaveClip`, `PlaySegmentAtSpeed`- Created `classify_bookmarks.go` (135 lines) — bookmark navigation methods- Created `classify_labels.go` (87 lines) — label manipulation methods- Added documentation for TUI-only fields in `ClassifyConfig`- **`calls_classify.go` reduced from 802 to 375 lines**### Phase 5: Consolidate Shared Types into from_common.go- Moved `ClusteredCall`, `DirCache`, `ParseFilterFromFilename`, `clusterStartTimes`, `clusterDetections`, `predFileSpeciesKey` to `calls_from_common.go`- **`calls_from_preds.go` reduced from 717 to 546 lines (24% reduction)**- **`calls_from_common.go` grew from 287 to 460 lines**