diff --git a/Manifest.toml b/Manifest.toml
index 3fca9e2898fa0e9d39b146f4580da4456151347d..4758c942b153ce03879d40868de40321bd467397 100644
--- a/Manifest.toml
+++ b/Manifest.toml
@@ -858,10 +858,8 @@ uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688"
 version = "0.1.1"
 
 [[deps.PlanarLarvae]]
-deps = ["DelimitedFiles", "JSON3", "MAT", "Meshes", "OrderedCollections", "StaticArrays", "StructTypes"]
-git-tree-sha1 = "f45e4befaca93ceeab1830cce9239394ac01c45d"
-repo-rev = "main"
-repo-url = "https://gitlab.pasteur.fr/nyx/planarlarvae.jl"
+deps = ["DelimitedFiles", "JSON3", "MAT", "Meshes", "OrderedCollections", "SHA", "StaticArrays", "StructTypes"]
+path = "../PlanarLarvae"
 uuid = "c2615984-ef14-4d40-b148-916c85b43307"
 version = "0.4.0"
 
diff --git a/Project.toml b/Project.toml
index 59a41ed3280242b36d233f9eda0dc56364519de5..d3f21fb460d4a1ac99e49e5f583a5c75706235de 100644
--- a/Project.toml
+++ b/Project.toml
@@ -16,7 +16,6 @@ Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
 ObservationPolicies = "6317928a-6b1a-42e8-b853-b8e2fc3e9ca3"
 OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
 PlanarLarvae = "c2615984-ef14-4d40-b148-916c85b43307"
-SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"
 StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
 Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
 TidyObservables = "c8131bbd-73a8-4254-a42d-d5d4c5febb31"
diff --git a/src/LarvaTagger.jl b/src/LarvaTagger.jl
index 55f8351e5b1a1d35a75ee43f9825a59aef88770f..f17f04602f2ea0754eef56b2b7631eb75847cee5 100644
--- a/src/LarvaTagger.jl
+++ b/src/LarvaTagger.jl
@@ -1,7 +1,7 @@
 module LarvaTagger
 
 using PlanarLarvae, PlanarLarvae.Chore, PlanarLarvae.Trxmat, PlanarLarvae.Datasets,
-      PlanarLarvae.FIMTrack
+      PlanarLarvae.FIMTrack, PlanarLarvae.Loaders
 using ObservationPolicies
 using TidyObservables
 
@@ -16,26 +16,8 @@ using Observables
 using Meshes
 using Logging
 import Dates
-using SHA
 using OrderedCollections
 
-# move this into PlanarLarvae.jl
-
-function guessfileformat(path)
-    f = open(path, "r")
-    try
-        s = String(read(f, 19))
-        (s == "MATLAB 7.3 MAT-file") && return :trx
-        isnothing(match(r"^[0-9]{8}_[0-9}{6}\s\[0-9]*$", s)) || return :chore
-        isnothing(match(r"^\{\s*\"", s)) || return :json
-        isnothing(match(r"^,larva\([0-9]+", s)) || return :fimtrack
-    finally
-        close(f)
-    end
-end
-
-#
-
 export larvaviewer, larvaeditor
 
 include("misc.jl")
diff --git a/src/controllers.jl b/src/controllers.jl
index e405db2839c5ee048a67bc4f1492a27bac56bce3..489616d2f222b6ab10a66db05d8bc006917285c7 100644
--- a/src/controllers.jl
+++ b/src/controllers.jl
@@ -62,13 +62,13 @@ struct FilterEvents
     allow_multiple_tags::Observable{Bool}
 end
 
-FilterEvents() = FilterEvents(Observable{LarvaID}(0), Observable(false))
+FilterEvents() = FilterEvents(Observable{LarvaID}(NoLarva), Observable(false))
 
 function filterevents(controller)
     events = get!(gethub(controller), :filterevents, FilterEvents())
     if isempty(Observables.listeners(events.discard_larva_edits))
         on(events.discard_larva_edits) do id
-            @assert 0 < id
+            @assert id != NoLarva
             larva = getlarva(controller, id)
             empty!(larva.usertags[])
         end
diff --git a/src/files.jl b/src/files.jl
index 91f000f2c4e02146986b1a26cb81118889f109c5..1b0239e984064ca0501bb444e36fd6e75e5822bd 100644
--- a/src/files.jl
+++ b/src/files.jl
@@ -274,8 +274,7 @@ function savetofile(controller, file; datafile=nothing)
             end
         end
         datafilepath = isfile(datafile) ? datafile : joinpath(cwd(controller), datafile)
-        get!(dataset.attributes, :dependencies,
-             Dict(:filename=>basename(datafile), :sha1=>checksum(datafilepath)))
+        push_dependency!(dataset, datafilepath)
         PlanarLarvae.Datasets.to_json_file(filepath, dataset)
     end
 end
@@ -329,20 +328,7 @@ function read_json_labels(path)
     end
     #
     datadir = dirname(path)
-    deps = run.attributes[:dependencies]
-    if deps isa Vector
-        deps = Dict{Symbol, String}[Dict(Symbol(key)=>val
-                                         for (key, val) in pairs(dep))
-                                    for dep in deps]
-    else
-        deps = [Dict{Symbol, String}(Symbol(key)=>val for (key, val) in deps)]
-    end
-    run.attributes[:dependencies] = deps
-    datafile = nothing
-    for dep in deps
-        datafile = joinpath(datadir, dep[:filename])
-        checksum(datafile) == dep[:sha1] || throw("checksums do not equal")
-    end
+    datafile = get_dependencies(run, path)[1]
     datafmt = guessfileformat(datafile)
     data′= nothing
     if datafmt === :trx
diff --git a/src/wgl.jl b/src/wgl.jl
index a46ee1a18c3a719b060409f2c7491483ad5b1ee6..991194ef64789464a6ef95fe889ae16db493d125 100644
--- a/src/wgl.jl
+++ b/src/wgl.jl
@@ -451,7 +451,7 @@ end
 function larvafilter(larvae, controller::ControllerHub; kwargs...)
     entries = [larvainfo(controller, id) for id in keys(larvae)]
     on(async_latest(getactivelarva(controller))) do id
-        if 0 < id
+        if id != NoLarva
             for entry in entries
                 if id == entry.id
                     entry.reviewed[] = true