LTSMQS6HFFA7XNEEM5LNPPHJ37DXFULAHAWDRNG43N4HF4W3K55QC description: Auto-classify segments in one filter based on verified classifications from another filter. Use when one ML filter has been reviewed and you want to propagate those classifications to another filter on overlapping segments.
description: Propagate verified classifications between ML filters using `skraak calls propagate`. Common case is many folders at once.
# Deduplicate Call SpecificationPropagate verified classifications from one filter to another for overlapping segments. This is useful when you have reviewed classifications from one ML model and want to apply those learnings to another model's segments.## When to Use- You have reviewed/classified segments for one filter (e.g., `opensoundscape-kiwi-1.2`)- You want to auto-classify overlapping segments in another filter (e.g., `opensoundscape-kiwi-1.5`)- Both filters have segments on the same audio files## Prerequisites- Folder with `.data` files containing segments from multiple filters- One filter already reviewed (certainty >= 80 for confirmed calls, certainty = 0 for confirmed noise)- `./skraak` binary built and available## Workflow### Step 1: Identify candidate segmentsRun summarise and use jq to find overlapping segments where:- Source filter has verified classification (certainty >= 90 or = 0)- Target filter has unverified classification (certainty = 70)- Segments overlap in time- Species/calltype differ between filters```bash./skraak calls summarise --folder "<folder_path>" > /tmp/summary.jsoncat /tmp/summary.json | jq '.segments |group_by(.file) |map({file: .[0].file,segments: [.[] | {start: .start_time, end: .end_time, labels: .labels}]}) |map({file: .file,auto_classify: [.segments[] as $s1 |.segments[] as $s2 |select(($s1.labels[0].filter == "<source_filter>") and($s2.labels[0].filter == "<target_filter>") and# Time overlap($s1.start | floor) <= ($s2.end | ceil) and($s2.start | floor) <= ($s1.end | ceil) and# Source is verified(($s1.labels[0].certainty >= 90) or ($s1.labels[0].certainty == 0)) and# Target is unverified($s2.labels[0].certainty == 70) and# They differ(($s2.labels[0].species != $s1.labels[0].species) or ($s2.labels[0].calltype != $s1.labels[0].calltype))) |{file: .file,source_start: $s1.start,source_end: $s1.end,source_species: $s1.labels[0].species,source_calltype: $s1.labels[0].calltype,source_certainty: $s1.labels[0].certainty,target_start: $s2.start,target_end: $s2.end,target_species: $s2.labels[0].species,target_calltype: $s2.labels[0].calltype,action: (if $s1.labels[0].certainty == 0 then "Noise" else $s1.labels[0].species + "+" + ($s1.labels[0].calltype // "N/A") end)}]}) |map(select(.auto_classify | length > 0)) |[.[] | .auto_classify[]]'```### Step 2: Present summary to userGroup results by action type:| Action | Count | Description ||--------|-------|-------------|| GSK+Male from "Don't Know" | N | Target had "Don't Know", source verified GSK Male || GSK+Female from "Don't Know" | N | Target had "Don't Know", source verified GSK Female || GSK+Duet from "Don't Know" | N | Target had "Don't Know", source verified GSK Duet || Calltype correction | N | Both had GSK but different calltypes || Noise from "Don't Know" | N | Source confirmed as noise (certainty=0) || Weka from "Don't Know" | N | Source identified as Weka || Other species | N | Other verified species |Ask user to confirm before applying changes.
Use `skraak calls modify` to update target filter segments:
Use the `skraak calls propagate` CLI tool. It does all the work — overlap detection, certainty gating, conflict handling, JSON report. Read its `--help` output for full semantics.
# For noise./skraak calls modify --file "<folder>/<file>.wav.data" --reviewer Claude \--filter <target_filter> --segment <start>-<end> \--species Noise --certainty 90# For species without calltype (Weka, etc.)./skraak calls modify --file "<folder>/<file>.wav.data" --reviewer Claude \--filter <target_filter> --segment <start>-<end> \--species <Species> --certainty 90
### Step 4: Verify results```bash./skraak calls summarise --folder "<folder_path>" --brief
skraak calls propagate --folder <path> --from <filter> --to <filter> --species <species>skraak calls propagate --file <path> --from <filter> --to <filter> --species <species>
- **Always use certainty=90** for auto-classified segments (distinguishes from ML=70, human=100)- **Reviewer**: always "Claude" (indicates LLM-assisted classification)- **Only modify** segments where target has certainty=70 (unverified)- **Never modify** segments where target already has certainty >= 80- **Handle noise carefully**: source certainty=0 means confirmed noise, propagate as Noise species
```bashFOLDERS=("/media/david/Pomona-4/Pomona/A05/2026-04-06"# ...)TO=opensoundscape-kiwi-1.5
## Segment MatchingSegments overlap when:```floor(source.start) <= ceil(target.end) AND floor(target.start) <= ceil(source.end)
for F in "${FOLDERS[@]}"; dotag=$(echo "$F" | awk -F/ '{print $(NF-2)"_"$(NF-1)}')./skraak calls propagate --folder "$F" \--from opensoundscape-kiwi-1.2 --to "$TO" --species Kiwi \> "/tmp/propagate_logs/${tag}_${TO}.json"done
The `--segment` parameter uses integer seconds, so floor/ceil handles the matching:- Source: 362.5-390 matches target: 360-390 via `--segment 362-390`- Source: 297.5-322.5 matches target: 295-323 via `--segment 297-323`
- Tag must disambiguate colliding site codes (e.g. G05 in Pomona-4 and TwentyFourSeven) — use at least two path components.- Invoke the tool **once** per folder. Don't re-run for display; it's idempotent and will report zero.
Based on verified opensoundscape-kiwi-1.2 classifications:### GSK from "Don't Know" (14 segments)| File | Segment | Action ||------|---------|--------|| ridgeend_20260303_204507 | 297-323 | → GSK+Female || ridgeend_20260303_211507 | 292-320 | → GSK+Female |...
Tally totals and surface any conflicts for manual review:
### Calltype Corrections (4 segments)| File | Segment | Current | Correct ||------|---------|---------|---------|| ridgeend_20260304_043007 | 360-388 | GSK+Duet | → GSK+Male |...
```bashjq -s 'map({p:.propagated,fc:.files_changed,c:.skipped_conflict}) |reduce .[] as $x ({p:0,fc:0,c:0};{p:(.p+$x.p), fc:(.fc+$x.fc), c:(.c+$x.c)})' \/tmp/propagate_logs/*_${TO}.json
## Notes- This is a one-way propagation: source filter → target filter- Run `skraak-check-call-classification` skill first to verify the source filter- The target filter may have unique segments not in source - these remain unverified- Species that sound similar (e.g., GSK and LSK) may need human review - flag these for user attention
Conflicts = target overlaps ≥2 verified sources with different calltypes; user resolves in the TUI.