TSOJUMHVLPASHBAVCTUK6WSGZOSBDZIC47FYILGQ2QAU7Z4BUZMAC 43TMU2JOAE2HIWKUUPSK5LP7KLGLBVZIZHTFA43ZAXDOHU4XWZ5QC VU3KBTQ6AFJV36WVQ4A7BM7Q3MLJQX4DBCGMIZJXNPMEKLIBGHZAC 3DVPQOKB6BX63XSBIYYCPWBL2RBG3LXZS3XPQBANJP2FWVRAOVZQC KZKLAINJJWZ64T5MUZT34LJVQIKBTKZ6EJGD7C7TTSSDGCHEDPMAC AVQ66WO4R4KVXAVP4YPEF65CPHJJY55H7ZOVPZ2BHFMGEBTWRUQQC LQLC7S3ADBR4O2JYVUSQJD65U3HG4ADOQBGB4F7KQCXUMNKMNEKAC package impimport ("database/sql""os""strings""testing""skraak/db""skraak/utils")// setupImportTestDB creates a DB with the full schema + test data for import validation.//// dataset (structured): ds_imptest00001// dataset (unstructured): ds_imptest00002// location in ds1: loc_imptest0001// location (inactive): loc_imptest0002 (in ds1)// cluster in loc1: cl_imptest00001// cluster (inactive): cl_imptest00002 (in loc1)// species: Kiwi (sp_kiwi000000), Roroa (sp_roroa00000)// calltypes: Kiwi/song (ct_kiwi000001), Kiwi/duet (ct_kiwi000002)// filter: kiwi.txt (fi_kiwi0000001), tomtit.txt (fi_tomtit000001)func setupImportTestDB(t *testing.T) *sql.DB {t.Helper()database := db.SetupTestDB(t)db.InsertTestDatasetWithType(t, database, "ds_imptest00001", "Imp Structured", "structured")db.InsertTestDatasetWithType(t, database, "ds_imptest00002", "Imp Unstructured", "unstructured")db.InsertTestLocation(t, database, "loc_imptest0001", "ds_imptest00001", "Loc Active")db.InsertTestLocation(t, database, "loc_imptest0002", "ds_imptest00001", "Loc Inactive")mustExecImport(t, database, "UPDATE location SET active = false WHERE id = 'loc_imptest0002'")db.InsertTestCluster(t, database, "cl_imptest00001", "ds_imptest00001", "loc_imptest0001", "Cl Active")db.InsertTestCluster(t, database, "cl_imptest00002", "ds_imptest00001", "loc_imptest0001", "Cl Inactive")mustExecImport(t, database, "UPDATE cluster SET active = false WHERE id = 'cl_imptest00002'")db.InsertTestSpecies(t, database, "sp_kiwi000000", "Kiwi")db.InsertTestSpecies(t, database, "sp_roroa00000", "Roroa")db.InsertTestCallType(t, database, "ct_kiwi000001", "sp_kiwi000000", "song")db.InsertTestCallType(t, database, "ct_kiwi000002", "sp_kiwi000000", "duet")db.InsertTestFilter(t, database, "fi_kiwi0000001", "kiwi.txt")db.InsertTestFilter(t, database, "fi_tomtit000001", "tomtit.txt")return database}func mustExecImport(t *testing.T, database *sql.DB, query string, args ...any) {t.Helper()if _, err := database.Exec(query, args...); err != nil {t.Fatalf("exec: %v", err)}}// --- validateSegmentHierarchy ---func TestValidateSegmentHierarchy(t *testing.T) {database := setupImportTestDB(t)defer database.Close()t.Run("valid hierarchy", func(t *testing.T) {err := validateSegmentHierarchy(database, "ds_imptest00001", "loc_imptest0001", "cl_imptest00001")if err != nil {t.Errorf("unexpected error: %v", err)}})t.Run("unstructured dataset rejected", func(t *testing.T) {err := validateSegmentHierarchy(database, "ds_imptest00002", "loc_imptest0001", "cl_imptest00001")if err == nil || !strings.Contains(err.Error(), "structured") {t.Errorf("expected structured-only error, got: %v", err)}})t.Run("inactive location rejected", func(t *testing.T) {err := validateSegmentHierarchy(database, "ds_imptest00001", "loc_imptest0002", "cl_imptest00001")if err == nil {t.Error("expected error for inactive location")}})t.Run("inactive cluster rejected", func(t *testing.T) {err := validateSegmentHierarchy(database, "ds_imptest00001", "loc_imptest0001", "cl_imptest00002")if err == nil {t.Error("expected error for inactive cluster")}})}// --- validateFiltersExist ---func TestValidateFiltersExist(t *testing.T) {database := setupImportTestDB(t)defer database.Close()t.Run("all filters exist", func(t *testing.T) {filterMap, err := validateFiltersExist(database, map[string]bool{"kiwi.txt": true, "tomtit.txt": true})if err != nil {t.Fatalf("unexpected error: %v", err)}if len(filterMap) != 2 {t.Errorf("expected 2 filters, got %d", len(filterMap))}if filterMap["kiwi.txt"] != "fi_kiwi0000001" {t.Errorf("kiwi.txt ID = %q, want fi_kiwi0000001", filterMap["kiwi.txt"])}})t.Run("missing filter reported", func(t *testing.T) {_, err := validateFiltersExist(database, map[string]bool{"phantom.txt": true})if err == nil || !strings.Contains(err.Error(), "not found") {t.Errorf("expected not-found error, got: %v", err)}})t.Run("empty filter set returns empty map", func(t *testing.T) {filterMap, err := validateFiltersExist(database, map[string]bool{})if err != nil {t.Fatalf("unexpected error: %v", err)}if len(filterMap) != 0 {t.Errorf("expected empty map, got %d entries", len(filterMap))}})}// --- loadSpeciesCalltypeIDs ---func TestLoadSpeciesCalltypeIDs(t *testing.T) {database := setupImportTestDB(t)defer database.Close()mapping := utils.MappingFile{"K-M": {Species: "Kiwi"},"GSK": {Species: "Roroa", Calltypes: map[string]string{"brrr": "brrr"}},}t.Run("loads species IDs", func(t *testing.T) {speciesMap, _, err := loadSpeciesCalltypeIDs(database, mapping,map[string]bool{"K-M": true, "GSK": true},map[string]map[string]bool{"K-M": {"song": true}, "GSK": {"brrr": true}},)if err != nil {t.Fatalf("unexpected error: %v", err)}if speciesMap["Kiwi"] != "sp_kiwi000000" {t.Errorf("Kiwi ID = %q, want sp_kiwi000000", speciesMap["Kiwi"])}if speciesMap["Roroa"] != "sp_roroa00000" {t.Errorf("Roroa ID = %q, want sp_roroa00000", speciesMap["Roroa"])}})t.Run("loads calltype IDs", func(t *testing.T) {_, calltypeMap, err := loadSpeciesCalltypeIDs(database, mapping,map[string]bool{"K-M": true},map[string]map[string]bool{"K-M": {"song": true, "duet": true}},)if err != nil {t.Fatalf("unexpected error: %v", err)}if calltypeMap["Kiwi"]["song"] != "ct_kiwi000001" {t.Errorf("Kiwi/song ID = %q, want ct_kiwi000001", calltypeMap["Kiwi"]["song"])}if calltypeMap["Kiwi"]["duet"] != "ct_kiwi000002" {t.Errorf("Kiwi/duet ID = %q, want ct_kiwi000002", calltypeMap["Kiwi"]["duet"])}})t.Run("missing calltype returns empty ID", func(t *testing.T) {_, calltypeMap, err := loadSpeciesCalltypeIDs(database, mapping,map[string]bool{"K-M": true},map[string]map[string]bool{"K-M": {"phantom": true}},)if err != nil {t.Fatalf("unexpected error: %v", err)}if calltypeMap["Kiwi"]["phantom"] != "" {t.Errorf("missing calltype should have empty ID, got %q", calltypeMap["Kiwi"]["phantom"])}})}// --- validateAndMapFiles ---func TestValidateAndMapFiles_NoScannedFiles(t *testing.T) {database := setupImportTestDB(t)defer database.Close()fileMap, errors := validateAndMapFiles(database, nil, "cl_imptest00001", "ds_imptest00001")if len(fileMap) != 0 {t.Errorf("expected empty map, got %d entries", len(fileMap))}if len(errors) != 0 {t.Errorf("expected no errors, got %d", len(errors))}}func TestValidateAndMapFiles_FileFoundByHash(t *testing.T) {database := setupImportTestDB(t)defer database.Close()// Create a temp WAV file so hash can be computedtmpDir := t.TempDir()wavPath := tmpDir + "/test.wav"if err := createFakeWAV(wavPath); err != nil {t.Fatalf("create WAV: %v", err)}// Compute the actual hash of the file we createdhash, err := utils.ComputeXXH64(wavPath)if err != nil {t.Fatalf("compute hash: %v", err)}// Insert a file with the actual hashdb.InsertTestFileForCluster(t, database, "fi_test00000001", "cl_imptest00001", "loc_imptest0001", "test.wav", hash, 60.0)// Link file to datasetmustExecImport(t, database, "INSERT INTO file_dataset (file_id, dataset_id, created_at, last_modified) VALUES (?, ?, now(), now())", "fi_test00000001", "ds_imptest00001")scanned := []scannedDataFile{{DataPath: tmpDir + "/test.data",WavPath: wavPath,Duration: 60.0,Segments: []*utils.Segment{{StartTime: 1.0, EndTime: 2.0, Labels: []*utils.Label{}}},}}fileMap, errors := validateAndMapFiles(database, scanned, "cl_imptest00001", "ds_imptest00001")// Should find the file by hashif len(fileMap) != 1 {t.Errorf("expected 1 file mapped, got %d; errors: %v", len(fileMap), errors)}}func TestValidateAndMapFiles_HashNotFound(t *testing.T) {database := setupImportTestDB(t)defer database.Close()// No file in DB — any hash lookup failstmpDir := t.TempDir()wavPath := tmpDir + "/test.wav"if err := createFakeWAV(wavPath); err != nil {t.Fatalf("create WAV: %v", err)}scanned := []scannedDataFile{{DataPath: tmpDir + "/test.data",WavPath: wavPath,Duration: 60.0,Segments: []*utils.Segment{{StartTime: 1.0, EndTime: 2.0, Labels: []*utils.Label{}}},}}fileMap, errors := validateAndMapFiles(database, scanned, "cl_imptest00001", "ds_imptest00001")if len(fileMap) != 0 {t.Errorf("expected 0 files mapped, got %d", len(fileMap))}if len(errors) == 0 {t.Error("expected hash-not-found error")}}// createFakeWAV creates a minimal WAV file (44-byte header + 1 sample)func createFakeWAV(path string) error {// Minimal valid WAV: 44-byte header + 4 bytes of dataheader := make([]byte, 44)copy(header[0:4], "RIFF")copy(header[4:8], "\x24\x00\x00\x00") // file size - 8copy(header[8:12], "WAVE")copy(header[12:16], "fmt ")copy(header[16:20], "\x10\x00\x00\x00") // chunk sizecopy(header[20:22], "\x01\x00") // PCMcopy(header[22:24], "\x01\x00") // monocopy(header[24:28], "\x80\x3E\x00\x00") // 16000 sample ratecopy(header[28:32], "\x00\x7D\x00\x00") // byte ratecopy(header[32:34], "\x02\x00") // block aligncopy(header[34:36], "\x10\x00") // bits per samplecopy(header[36:40], "data")copy(header[40:44], "\x00\x00\x00\x00") // data sizereturn os.WriteFile(path, header, 0644)}
func validateSegmentHierarchy(dbConn *sql.DB, datasetID, locationID, clusterID string) error {// Validate dataset exists and is structuredif err := db.ValidateDatasetTypeForImport(dbConn, datasetID); err != nil {
func validateSegmentHierarchy(q db.Querier, datasetID, locationID, clusterID string) error {if err := db.ValidateDatasetTypeForImport(q, datasetID); err != nil {
package callsimport ("testing""skraak/utils")// --- Navigation: NextSegment / PrevSegment ---func TestNextSegment(t *testing.T) {df := &utils.DataFile{FilePath: "/test/f1.data",Segments: []*utils.Segment{{StartTime: 0, EndTime: 10},{StartTime: 10, EndTime: 20},},}df2 := &utils.DataFile{FilePath: "/test/f2.data",Segments: []*utils.Segment{{StartTime: 0, EndTime: 10},},}state := NewClassifyState(ClassifyConfig{Certainty: -1}, []*utils.DataFile{df, df2})// Start at file 0, segment 0if state.FileIdx != 0 || state.SegmentIdx != 0 {t.Fatalf("initial: file=%d seg=%d", state.FileIdx, state.SegmentIdx)}// Advance within same fileif !state.NextSegment() {t.Error("next within file should succeed")}if state.FileIdx != 0 || state.SegmentIdx != 1 {t.Errorf("after next: file=%d seg=%d", state.FileIdx, state.SegmentIdx)}// Advance to next fileif !state.NextSegment() {t.Error("next to next file should succeed")}if state.FileIdx != 1 || state.SegmentIdx != 0 {t.Errorf("after next file: file=%d seg=%d", state.FileIdx, state.SegmentIdx)}// At endif state.NextSegment() {t.Error("should fail at end")}}func TestPrevSegment(t *testing.T) {df := &utils.DataFile{FilePath: "/test/f1.data",Segments: []*utils.Segment{{StartTime: 0, EndTime: 10},},}df2 := &utils.DataFile{FilePath: "/test/f2.data",Segments: []*utils.Segment{{StartTime: 0, EndTime: 10},{StartTime: 10, EndTime: 20},},}state := NewClassifyState(ClassifyConfig{Certainty: -1}, []*utils.DataFile{df, df2})state.FileIdx = 1state.SegmentIdx = 1// Prev within same fileif !state.PrevSegment() {t.Error("prev within file should succeed")}if state.FileIdx != 1 || state.SegmentIdx != 0 {t.Errorf("after prev: file=%d seg=%d", state.FileIdx, state.SegmentIdx)}// Prev to previous fileif !state.PrevSegment() {t.Error("prev to previous file should succeed")}if state.FileIdx != 0 || state.SegmentIdx != 0 {t.Errorf("after prev file: file=%d seg=%d", state.FileIdx, state.SegmentIdx)}// At startif state.PrevSegment() {t.Error("should fail at start")}}// --- buildClassifyState goto logic ---func TestBuildClassifyState_GotoFound(t *testing.T) {df1 := &utils.DataFile{FilePath: "/test/alpha.data", Segments: []*utils.Segment{{}}}df2 := &utils.DataFile{FilePath: "/test/beta.data", Segments: []*utils.Segment{{}}}segs := [][]*utils.Segment{df1.Segments, df2.Segments}state, err := buildClassifyState(ClassifyConfig{Goto: "beta.data"}, []*utils.DataFile{df1, df2}, segs, 0)if err != nil {t.Fatalf("unexpected error: %v", err)}if state.FileIdx != 1 {t.Errorf("FileIdx=%d, want 1", state.FileIdx)}}func TestBuildClassifyState_GotoNotFound(t *testing.T) {df1 := &utils.DataFile{FilePath: "/test/alpha.data", Segments: []*utils.Segment{{}}}segs := [][]*utils.Segment{df1.Segments}_, err := buildClassifyState(ClassifyConfig{Goto: "missing.data"}, []*utils.DataFile{df1}, segs, 0)if err == nil {t.Error("expected error for missing goto file")}}func TestBuildClassifyState_NoGoto(t *testing.T) {df1 := &utils.DataFile{FilePath: "/test/alpha.data", Segments: []*utils.Segment{{}}}segs := [][]*utils.Segment{df1.Segments}state, err := buildClassifyState(ClassifyConfig{}, []*utils.DataFile{df1}, segs, 0)if err != nil {t.Fatalf("unexpected error: %v", err)}if state.FileIdx != 0 {t.Errorf("FileIdx=%d, want 0", state.FileIdx)}}// --- FormatLabels ---func TestFormatLabels(t *testing.T) {labels := []*utils.Label{{Species: "Kiwi", CallType: "Duet", Certainty: 90, Filter: "model-1.0"},{Species: "Tomtit", Certainty: 70, Filter: "model-2.0"},}t.Run("no filter", func(t *testing.T) {got := FormatLabels(labels, "")if got == "" {t.Error("expected non-empty output")}// Both labels should appearif !contains(got, "Kiwi/Duet") {t.Errorf("expected Kiwi/Duet in %q", got)}if !contains(got, "Tomtit") {t.Errorf("expected Tomtit in %q", got)}})t.Run("with filter", func(t *testing.T) {got := FormatLabels(labels, "model-1.0")if !contains(got, "Kiwi") {t.Errorf("expected Kiwi in %q", got)}if contains(got, "Tomtit") {t.Errorf("Tomtit should be filtered out in %q", got)}})t.Run("with comment", func(t *testing.T) {labels := []*utils.Label{{Species: "Kiwi", Certainty: 100, Filter: "f1", Comment: "nice call"},}got := FormatLabels(labels, "")if !contains(got, `"nice call"`) {t.Errorf("expected comment in %q", got)}})}// --- Bookmark navigation ---func TestBookmarkNavigation(t *testing.T) {df := &utils.DataFile{FilePath: "/test/f1.data",Meta: &utils.DataMeta{},Segments: []*utils.Segment{{Labels: []*utils.Label{{Species: "Kiwi", Filter: "f", Bookmark: true}}},{Labels: []*utils.Label{{Species: "Tomtit", Filter: "f"}}},{Labels: []*utils.Label{{Species: "Roroa", Filter: "f", Bookmark: true}}},},}state := NewClassifyState(ClassifyConfig{Filter: "f", Certainty: -1}, []*utils.DataFile{df})// Start at segment 0 (has bookmark)if !state.HasBookmark() {t.Error("segment 0 should have bookmark")}// NextBookmark should go to segment 2if !state.NextBookmark() {t.Error("NextBookmark should find bookmark at segment 2")}if state.SegmentIdx != 2 {t.Errorf("after NextBookmark: seg=%d, want 2", state.SegmentIdx)}// PrevBookmark should go back to segment 0if !state.PrevBookmark() {t.Error("PrevBookmark should find bookmark at segment 0")}if state.SegmentIdx != 0 {t.Errorf("after PrevBookmark: seg=%d, want 0", state.SegmentIdx)}}func TestToggleBookmark(t *testing.T) {df := &utils.DataFile{FilePath: "/test/f1.data",Meta: &utils.DataMeta{},Segments: []*utils.Segment{{Labels: []*utils.Label{{Species: "Kiwi", Filter: "f"}}},},}state := NewClassifyState(ClassifyConfig{Filter: "f", Reviewer: "Test", Certainty: -1}, []*utils.DataFile{df})if state.HasBookmark() {t.Error("should start without bookmark")}state.ToggleBookmark()if !state.HasBookmark() {t.Error("should have bookmark after toggle")}state.ToggleBookmark()if state.HasBookmark() {t.Error("bookmark should be removed after second toggle")}}// --- ConfirmLabel ---func TestConfirmLabel(t *testing.T) {df := &utils.DataFile{FilePath: "/test/f1.data",Meta: &utils.DataMeta{},Segments: []*utils.Segment{{Labels: []*utils.Label{{Species: "Kiwi", Filter: "f", Certainty: 70}}},},}state := NewClassifyState(ClassifyConfig{Filter: "f", Reviewer: "Test", Certainty: -1}, []*utils.DataFile{df})// Confirm should upgrade 70 → 100if !state.ConfirmLabel() {t.Error("ConfirmLabel should return true for certainty < 100")}if df.Segments[0].Labels[0].Certainty != 100 {t.Errorf("certainty=%d, want 100", df.Segments[0].Labels[0].Certainty)}// Confirm again on 100 should be no-opif state.ConfirmLabel() {t.Error("ConfirmLabel should return false when already at 100")}}
write a PLAN.md, then begin with 1. we will take stock after that.
write a PLAN.md, then begin with 1. we will take stock after that.Go line count:18737 totalTest line count:11982 totalgo test -cover ./...ok skraak 2.548s coverage: 0.0% of statementsok skraak/cmd 0.005s coverage: 0.9% of statementsok skraak/db 1.269s coverage: 76.4% of statementsok skraak/tools 1.663s coverage: 58.4% of statementsok skraak/tools/calls 0.172s coverage: 60.7% of statementsok skraak/tools/import 0.007s coverage: 3.7% of statementsskraak/tui coverage: 0.0% of statementsok skraak/utils 0.280s coverage: 78.8% of statementsgoreportcard-cli2026/05/13 15:49:22 disabling ineffassign on large repo...Grade .......... A+ 100.0%Files ................ 147Issues ................. 0go_vet .............. 100%gofmt ............... 100%ineffassign ......... 100%gocyclo ............. 100%license ............. 100%misspell ............ 100%Go line count:18864 totalTest line count:13489 total
// InsertTestFilter inserts a filter row.func InsertTestFilter(t *testing.T, database *sql.DB, id, name string) {t.Helper()_, err := database.Exec("INSERT INTO filter (id, name, active) VALUES (?, ?, true)",id, name,)if err != nil {t.Fatalf("failed to insert filter: %v", err)}}// InsertTestFileForCluster inserts a file row linked to a cluster.func InsertTestFileForCluster(t *testing.T, database *sql.DB, id, clusterID, locationID, fileName, hash string, duration float64) {t.Helper()_, err := database.Exec(`INSERT INTO file (id, file_name, xxh64_hash, location_id, cluster_id, timestamp_local, duration, sample_rate, active)VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, ?, 48000, true)`,id, fileName, hash, locationID, clusterID, duration,)if err != nil {t.Fatalf("failed to insert file: %v", err)}}
- `db/testdb.go`: Extracted `SetupTestDB`, `InsertTestDataset`, `InsertTestDatasetWithType`, `InsertTestLocation`, `InsertTestCluster`, `InsertTestFile`, `InsertTestSpecies`, `InsertTestCallType`, `InsertTestPattern` from `invariants_test.go`. Available across `db/` test files.
- `db/testdb.go`: Extracted `SetupTestDB`, `InsertTestDataset`, `InsertTestDatasetWithType`, `InsertTestLocation`, `InsertTestCluster`, `InsertTestFile`, `InsertTestFileForCluster`, `InsertTestSpecies`, `InsertTestCallType`, `InsertTestPattern`, `InsertTestFilter` from `invariants_test.go`. Available across `db/` test files.
- `db/validation_test.go`: All 11 validation functions tested against in-memory DuckDB with full schema: `GetDatasetType`, `ValidateDatasetTypeForImport`, `ValidateDatasetTypeUnstructured`, `ValidateLocationBelongsToDataset`, `DatasetExistsAndActive`, `LocationBelongsToDataset`, `ClusterExistsAndActive`, `PatternExistsAndActive`, `LocationExistsAndActive`, `ValidateDatasetTypeForExport`, `ClusterBelongsToLocation`. Each tested for valid, inactive, nonexistent, and mismatched cases.
- `db/validation_test.go`: All 11 validation functions tested against in-memory DuckDB with full schema.- Extended `db.Querier` interface to include `Query` method (both `*sql.DB` and `*LoggedTx` already implement it).
- `utils/mapping_db_test.go`: `collectMappedLabels`, `collectUnmappedCalltypes`, `validateMappedSpecies`, `validateMappedCalltypes`, `ValidateMappingAgainstDB` tested against in-memory DuckDB with species/call_type tables. Covers valid mapping, missing species, missing DB species, inactive species, sentinel exclusion, missing calltypes.
- `utils/mapping_db_test.go`: `collectMappedLabels`, `collectUnmappedCalltypes`, `validateMappedSpecies`, `validateMappedCalltypes`, `ValidateMappingAgainstDB` tested against in-memory DuckDB.
- `tools/calls/calls_clip_test.go`: `filterSegments`, `validateClipInput`, `accumulateFileResult` — pure logic tests.
- `tools/calls/calls_clip_test.go`: `filterSegments`, `validateClipInput`, `accumulateFileResult`.
- `tools/export_test.go`: `orderByFKDependency`, `buildOwnedViaQuery`, `buildCountOwnedViaQuery`, `checkOutputFile`, `datasetTables` manifest completeness and relation validation.
- `tools/export_test.go`: `orderByFKDependency`, `buildOwnedViaQuery`, `buildCountOwnedViaQuery`, `checkOutputFile`, `datasetTables` manifest completeness.### 3a: Import Querier refactor (3.7% → 15.1%)- Refactored `tools/import/import_segments.go`: `validateSegmentHierarchy`, `validateFiltersExist`, `loadSpeciesCalltypeIDs`, `validateAndMapFiles` now accept `db.Querier` instead of `*sql.DB`.- Refactored `tools/import/bulk_file_import.go`: `bulkValidateLocationsBelongToDataset` now accepts `db.Querier`.- `tools/import/import_segments_validation_test.go`: Tests for all 4 Querier-based validation functions against in-memory DuckDB.### 3b: Classify navigation tests (58.4% → 65.1%)- `tools/calls/calls_classify_nav_test.go`: `NextSegment`, `PrevSegment`, `buildClassifyState` (goto), `FormatLabels`, `NextBookmark`/`PrevBookmark`, `ToggleBookmark`, `ConfirmLabel`.