Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
354 commits
Select commit Hold shift + click to select a range
286d261
Add TestSignedUrlSetJobFixture: pin async job vs. getFileDetails disa…
claude Sep 27, 2026
aab35a1
TestSignedUrlSetJobFixture: build a fresh fixture instead of hardcodi…
claude Sep 27, 2026
427fbd5
TestSignedUrl*: probe toolchain with which() not exist(...,'file')
claude Sep 27, 2026
1223661
TestSignedUrlSetJobFixture: narrate + fetch real fileCount from resul…
claude Sep 27, 2026
27661a3
signedUrlDiskCache: persistent signed-URL disk cache for reopen
claude Sep 27, 2026
988ec74
signedUrlDiskCache: swap fragile Java I/O for MATLAB builtins
claude Sep 27, 2026
5541a00
AuthTest: index organizations(1).id so a multi-org account still passes
claude Sep 27, 2026
0288006
batchSignedUrlLookup: exponential backoff for partial-map retry
claude Sep 28, 2026
2039130
didsqlite: single-file DID fetches bypass batch signed-URL scope
claude Sep 28, 2026
fe90588
TestSignedUrlSetJobScaling: add N=50k / 100k rows for the >100k regime
claude Sep 28, 2026
b0dcce1
TestSignedUrlSetJobScaling: pass 'chunks' to fromOMEZarr so N is real
claude Sep 28, 2026
e3d5f51
TestSignedUrlSetJobScaling: scale waitForAllBulkUploads timeout with N
claude Sep 28, 2026
1d3c1c2
TestSignedUrlSetJobScaling: treat bulk-extract timeout as an observation
claude Sep 28, 2026
d5e43d3
TestPublishWithDocsAndFiles: poll getFileDetails until uploaded=true
claude Sep 30, 2026
04a8889
haley import_V2: start the decision log
claude Sep 30, 2026
19e8564
haley import_V2 decisions: one session per day, a study-level groupin…
claude Sep 30, 2026
fa1cf27
haley import_V2 decisions: E. coli one epoch per image; study entity …
claude Sep 30, 2026
31bf7c4
haley import_V2 decisions: study entity approved and built (did-schem…
claude Sep 30, 2026
152527f
haley import_V2 decisions: studies from the paper, ROR lookups, datas…
claude Sep 30, 2026
07de430
haley import_V2 decisions: Mutants/Sensory settled from the data; ins…
claude Sep 30, 2026
64ad07f
haley import_V2 decisions: labs/departments are organizations; CC-BY-…
claude Sep 30, 2026
cad68d3
haley import_V2 decisions: roles, awarded_to, required dataset fields…
claude Sep 30, 2026
f695784
haley import_V2: stages 0 and 2 (discover, dataset metadata)
claude Sep 30, 2026
d1e5aad
datasetMetadata: spec keys starting with _ are annotations (jsondecod…
claude Sep 30, 2026
36e32a7
test-import-v2: did-schema main (PR #78 merged); decision log: study/…
claude Sep 30, 2026
3a4f821
haley import_V2: record the revised stage order (nested: dataset / pe…
claude Sep 30, 2026
0a88781
ndi.setup.V2.createSession: create a session backed by a V2 (did2) da…
claude Sep 30, 2026
0ec844e
createSession: write unique_reference.txt / reference.txt before opening
claude Sep 30, 2026
96a582f
haley import_V2 decisions: session directories, description, E. coli …
claude Sep 30, 2026
d4b2033
haley import_V2 decisions: session.name decided and built (did-schema…
claude Sep 30, 2026
b27ca99
haley import_V2: stage 3, sessions
claude Sep 30, 2026
28dd9a7
import_V2: preflight check for did2.build and the V2 schema, with the…
claude Sep 30, 2026
214c5cd
preflight: check the schema cache's actual path and reset a stale sin…
claude Sep 30, 2026
78c5970
haley sessionList: report suspicious day-index entries (shared direct…
claude Sep 30, 2026
5d90db0
TestHaleySessions: verifyFalse instead of verifyTrue(... == false)
claude Sep 30, 2026
8bd69ef
sessionList: loop over unused video days by index
claude Sep 30, 2026
1990d4c
ndi.vintage: read documents built to the current V2 schema
claude Oct 1, 2026
275fb4b
import_V2 decision log: stage 4 decisions 27-31, file location (32) open
claude Oct 1, 2026
5e81272
import_V2 decision log: subjects before acquisition; stage 4 decision…
claude Oct 1, 2026
0df68f7
haley import_V2: subjects stage (listing), short folder prefixes
claude Oct 1, 2026
7b27adc
subjectList: patch positions from the first video that has them
claude Oct 1, 2026
b29afb7
subjectList: an E. coli patch of LB alone (OD600 0) is a patch, not a…
claude Oct 1, 2026
ec9f502
haley import_V2: acquisition listing (stage 5), before anything is wr…
claude Oct 1, 2026
b23dd20
import_V2: read lawn-clip lengths by default; cameras are camera1 / c…
claude Oct 1, 2026
617827d
test-import-v2: pin MATLAB R2026a (latest = R2026b breaks MatBox's ad…
claude Oct 1, 2026
659555b
haley import_V2: paper names (assay / acclimation plates), subject na…
claude Oct 1, 2026
077e861
haley import_V2: write path for one session ('Write', 'Sessions')
claude Oct 1, 2026
103591f
haley sessionDocuments: datum_type (uint8 video, TIFF header); fixtur…
claude Oct 1, 2026
87ad3da
import_V2 write: list subjects against every session; stop on a schem…
claude Oct 1, 2026
8b9fd77
haley write: a video's datum_type from VideoReader's VideoFormat (not…
claude Oct 1, 2026
707d381
TestHaleyWrite: three acclimation plates in the fixture; body fields …
claude Oct 1, 2026
9062324
TestHaleyWrite: daqsystem_load's name is an exact match; load all
claude Oct 1, 2026
abcdad6
haley import: NDI finds recordings through their documents, not hard …
claude Oct 1, 2026
bfefd93
TestHaleyWrite: camera 2 has three epochs in the fixture (plate 14 is…
claude Oct 1, 2026
263857d
e2e workflow: pin MATLAB R2026a (R2026b breaks MatBox's add-on install)
claude Oct 1, 2026
39d3255
haley import: studies minted in stage 3 (#50); stage 6 relations (#52)
claude Oct 1, 2026
cda17a0
decisions #52: RO:0001018 is 'contained in', not 'located in'
claude Oct 1, 2026
1890965
haley import: Food Deprivation Plates (#53); worm windows acclimation…
claude Oct 1, 2026
49c9ec7
subjectList: an empty plate list made [P.starved] a double
claude Oct 1, 2026
12a2099
haley import: transfer time from the spec's transfer_protocol (lawn c…
claude Oct 2, 2026
5390db9
haley relations: pick times approximate; a window's duration approxim…
claude Oct 2, 2026
91d2e8d
Haley relations: each window records its end, with its own precision
claude Oct 2, 2026
f626c6b
Haley relations: state an exact end as approximate=false
claude Oct 2, 2026
a9aa60e
Haley transfer protocol: Matching day 1 and 2022-09-02 08:48 are worm…
claude Oct 2, 2026
1c06633
Haley transfer time: an exception may name the lawn clip
claude Oct 2, 2026
5b1dbac
Haley relations: each time states its bound (CHANGE 7)
claude Oct 2, 2026
14828f6
Haley decisions: stage 8 (#54), and two corrections to #53
claude Oct 2, 2026
8e6631d
Haley decisions #54: ampicillin, OP50 vs OP50-GFP, transfer per move
claude Oct 2, 2026
7dc755e
Haley import stage 7: subject types, worm cohorts, assertions (#55)
claude Oct 2, 2026
6edb95e
schemaHasField: read a schema whose fields jsondecode returns as a cell
claude Oct 2, 2026
f3b0d37
Haley decisions #56: stage 8 details from the Benchling protocols
claude Oct 2, 2026
4432d27
Haley decisions: keep #55 before #56
claude Oct 2, 2026
dff023a
Haley decisions #56: S-Complete for Matching, worms at 20 C, plate 90…
claude Oct 2, 2026
40dc3ba
Haley decisions #56: E. coli agar is the plain NGM recipe (carbenicil…
claude Oct 2, 2026
d2b6c82
Haley decisions #56: recipes at the dataset level, written out from W…
claude Oct 2, 2026
98bdd5e
Haley stage 8, part A: chemicals and recipes, dataset-level (#56)
claude Oct 3, 2026
7af0cd7
Haley spec: give the NaCl product its own key
claude Oct 3, 2026
42687bd
Haley decision #57: what OD600Real and CFU measure; two CFU corrections
claude Oct 3, 2026
0c563af
Read schema fields through schemaHasField everywhere
claude Oct 3, 2026
fcc1913
Haley stage 8 part B: the seeding suspensions (decision #57)
claude Oct 3, 2026
c0909db
Haley stage 8 part C: the manipulations (decision #58)
claude Oct 3, 2026
fe9913c
Haley spec: list the 10 estimated timeRoomTemp (Concentration 1 Feb 2…
claude Oct 3, 2026
461db94
subjectList: declare the options argument on the function line
claude Oct 3, 2026
b589e85
Haley stage 8: pour variable by peptone; E. coli plates out just befo…
claude Oct 3, 2026
a00768c
Haley stage 8: variable / value / method per the review (decision #58)
claude Oct 3, 2026
5219482
Haley seeding suspensions: no formulation type
claude Oct 3, 2026
d0956e0
Haley stage 9: temperature and humidity observations (decision #59)
claude Oct 3, 2026
b47d637
Haley stage 9: close colValue; count the temperature probe
claude Oct 3, 2026
5c9e8a8
lightsheet pyramid: default tile budget 8 MB -> 32 MB
claude Oct 4, 2026
99416ab
did2sqlite: open ingested files from the database's file store
claude Oct 4, 2026
011c97d
Haley stage 10 part A: each behaviour video's geometry (decision #60)
claude Oct 4, 2026
378a1ec
Haley stage 10 part B: the worm tracks (decision #61)
claude Oct 4, 2026
00917e9
Haley stage 10 part C: the E. coli patch profiles (decision #62)
claude Oct 5, 2026
5eab60f
Haley stage 11: the encounters (decision #63)
claude Oct 5, 2026
d41a7c0
Haley import: raw E. coli images as recordings, normalised TIFFs as c…
claude Oct 5, 2026
b5ed2e8
Haley import stage 12: the border amplitude fits and their estimates …
claude Oct 5, 2026
af37a7e
Haley import stage 13: check and write the dataset as one V2 database…
claude Oct 5, 2026
7aa7db5
Haley import decisions: close #6 and #7, settled by #61 and #63
claude Oct 5, 2026
ef6d1b1
createDataset: read a V2 edge's document_id (v1's field is value)
claude Oct 5, 2026
cc1e4b5
createDataset: mark the folder a dataset before opening it
claude Oct 5, 2026
040df90
Haley import: elapsed times across daylight saving; EcoliProfilesFile…
claude Oct 5, 2026
9002e30
V2 writes: batched with progress, no repeat validation, occupied fold…
claude Oct 5, 2026
68f518d
TestHaleyWrite: the occupied-folder test uses a session the fixture has
claude Oct 6, 2026
4395295
Haley import decisions: #49 reviewed, still provisional pending the o…
claude Oct 6, 2026
1e8fc58
ProgressBarWindow: the bar fills its row on R2026a
claude Oct 6, 2026
d367f62
ProgressBarWindow: the bar sits 5 px inside its row, on the window's …
claude Oct 6, 2026
ac416b8
Progress bar tests: the bar's axes sit in a panel the grid lays out
claude Oct 6, 2026
175b08a
ProgressBarWindow: the bar's panel turns AutoResizeChildren off
claude Oct 6, 2026
a197e87
Merge claude/progressbar-fill (#1020): the progress bar fills its row…
claude Oct 6, 2026
d9adc13
Haley import: verifyDataset, the checks to run on a written dataset
claude Oct 6, 2026
a8bf9a3
Haley import: termInventory, every plain-text and ontology term a dat…
claude Oct 6, 2026
0c18fae
testDataset: print the term inventory CSV to the log
claude Oct 6, 2026
d87044e
termInventory: a plain-text term is stored as {name} alone
claude Oct 6, 2026
6f47750
Haley import: a progress bar and a line per session while the session…
claude Oct 6, 2026
b4a45f8
Haley import: a grid-template plate's patches take their own OD600 (d…
claude Oct 6, 2026
a8cf3b1
Haley import: a dose with no known formulation is reported, not built
claude Oct 6, 2026
f0909de
Haley import: with 'Write', the run keeps its record in a run folder
claude Oct 6, 2026
bf41e76
verifyDataset: a progress bar and lines for the slow checks
claude Oct 6, 2026
954a10b
Haley import: a recording's time reference is OWL-Time intervalDuring…
claude Oct 6, 2026
d8f8d98
Design: the NDI V2 object layer (subjects, statements, entities)
claude Oct 6, 2026
9ac9798
V2 object layer: ndi.entity, ndi.subject as an entity, statements, nd…
claude Oct 6, 2026
d25cd30
tools: v2_object_layer_tryout, the object layer on a real V2 dataset,…
claude Oct 6, 2026
2479a11
V2 object layer: walk decoded lists as cells; shape a body by its sta…
claude Oct 6, 2026
bfe92b3
V2 object layer: numeric parameters and conditions as numbers; read t…
claude Oct 6, 2026
4b08919
tools: v2_query_plan, the dataset vs session search compared (SQL, pl…
claude Oct 6, 2026
1017518
tools: v2_subject_listing_profile, where the time goes listing every …
claude Oct 6, 2026
586a949
V2 reads: decode each document once, and skip the report-only census
claude Oct 6, 2026
38e1cd3
V2 reads: a document already at the read target skips the conversion …
claude Oct 6, 2026
78fe081
tools: v2_import_profile, where the time goes importing one Haley ses…
claude Oct 6, 2026
4df896e
V2 objects: name, kind, document_id (ndi.entity) and type (ndi.subjec…
claude Oct 6, 2026
6066dcb
ndi.subject: statements/assertions 'Inherited' follow member_of and d…
claude Oct 6, 2026
df5da45
ndi.entity: parents/children take a relation path and an array of ent…
claude Oct 7, 2026
9160f8a
ndi.entity walk: call on an array in function form; [c{:}].method() i…
claude Oct 7, 2026
97c0aff
ndi.entity.relations takes an array of entities: one search per direc…
claude Oct 7, 2026
92ae918
TestObjectLayer: acclimation plate 0001 holds more than cohort 0012; …
claude Oct 7, 2026
d0154ba
ndi.entity descendants/ancestors: with 'Type' or 'Kind' the search pi…
claude Oct 7, 2026
b9a1aff
did2sqlite: pass a depends_on whose target is a query (and or-branche…
claude Oct 7, 2026
3250f17
TestObjectLayer: test the nested-target conversion, which runs on any…
claude Oct 7, 2026
fa70698
ndi.subject.find: 'Strain' and 'Asserted' find subjects by what is as…
claude Oct 7, 2026
4d5b105
ndi.subject.find(ds, 'type', 'organism', 'species', ..., 'strain', ..…
claude Oct 7, 2026
bf0dc97
ndi.subject.find: six statement keys, values compared by kind, wildca…
claude Oct 7, 2026
1b2d8cc
findStatements: a value that narrows the search to nothing is searche…
claude Oct 7, 2026
538bdd5
ndi.subject.find: relations (step 1) and 'explain'
claude Oct 7, 2026
b73c80c
Rename the V2 finders to search: ndi.subject.search, ndi.statement.se…
claude Oct 7, 2026
2296586
explain: the 'through a group' note goes with the subject's role, not…
claude Oct 7, 2026
8fbc23e
V2 object layer: clearer search messages, 'strict', statements of man…
claude Oct 7, 2026
3c1b8bb
subject.m: remove a stray end pair and a trailing end (8fbc23eb did n…
claude Oct 7, 2026
eb451d9
Time filters: 'at', 'during', 'before', 'after', 'duration' on statem…
claude Oct 7, 2026
4f1a1df
Time cache: an empty containers.Map is isempty, so the cache never st…
claude Oct 7, 2026
222cdbd
summary: term nodes beside names; times shown in the zone they were r…
claude Oct 7, 2026
a843a01
summary: the column list no longer shares a name with the subject-nam…
claude Oct 7, 2026
d3c5217
summary: a value of several numbers inline shows '(array)'
claude Oct 7, 2026
b884178
Haley import: strain WormBase nodes and biological sex (decision #68)
claude Oct 7, 2026
c3d6491
Haley spec: WormBase ids for FK103, MT13113, MT9455
claude Oct 7, 2026
5572f7d
V2 object layer: assertions() returns objects; observations/manipulat…
claude Oct 8, 2026
328a837
V2 object layer: 'context' -- a container's interactions reach what i…
claude Oct 8, 2026
23f053d
V2 object layer: 'during' takes statements; object-layer fixture gain…
claude Oct 8, 2026
aeac916
TestObjectLayer: plate 12 has two videos and one is timed with its re…
claude Oct 8, 2026
7e35bb9
V2 object layer: read V_eta's new statement names, and the old ones
claude Oct 8, 2026
9da6dd8
TestObjectLayer: '>21.615' is plates 12 and 14 (14 reads 21.64); none…
claude Oct 8, 2026
f3c36b0
TestHaleyWrite: edges named entity_id read subject_id from a document…
claude Oct 8, 2026
05067dd
V2 import and readers work on one entity class and composed statements
claude Oct 8, 2026
07ac828
V2 tests and preflight read both the per-leaf and the one-entity schema
claude Oct 8, 2026
3a19c28
Read the session, epoch and software blocks of either schema; tests f…
claude Oct 8, 2026
b34b646
Object layer: a composed statement's value kind, entity-block queries…
claude Oct 8, 2026
5c7c397
statement.value reads value.data_type; temporary diagnostic for compo…
claude Oct 8, 2026
cf3a755
TestObjectLayer: strain as an instance_of relation when the schema ha…
claude Oct 8, 2026
ee933b5
Dataset census counts the stored class; testDataset looks up by eithe…
claude Oct 8, 2026
fb5e84c
A session opened from its dataset resolves a strain through the dataset
claude Oct 8, 2026
d4e5006
Haley import: OP50's NCBITaxon id, L4 stage at the pick, N2 backgroun…
claude Oct 8, 2026
b92b508
Haley import: patch centre/radius/circularity, recording keys, the gr…
claude Oct 8, 2026
3c7bb14
Decision log #69 and #70: the old import's remaining columns
claude Oct 8, 2026
244f065
Haley import: foragingMini experiment 24's acclimation room temperatu…
claude Oct 8, 2026
35e122f
Move the V2 search and statements from ndi.subject to ndi.entity; rea…
claude Oct 8, 2026
ba9ac79
TestObjectLayer: the summary column for 'family name' is familyName
claude Oct 8, 2026
7e243a9
ndi.entity: follow instance_of; a type's assertions hold of its insta…
claude Oct 8, 2026
20b83d5
ndi.entity: listing subjects leaves out devices and materials; record…
claude Oct 8, 2026
414aad3
ndi.entity: no default type on a bare subject search
claude Oct 8, 2026
e7671e9
isa subject is the biological subjects; 'subject' is not an ndi.entit…
claude Oct 8, 2026
58718b4
Leave ndi.subject as on V2; every entity is read as an ndi.entity
claude Oct 8, 2026
0694389
TestObjectLayer: drop the unpaired-argument check ndi.entity.search c…
claude Oct 8, 2026
3f594f7
arguments blocks: search takes one cell of conditions; statements tak…
claude Oct 8, 2026
1c474cf
ndi.entity.search 'explain': name what is searched for
claude Oct 8, 2026
71141e4
Synonym search: a value that matches no term by name matches a term i…
claude Oct 8, 2026
d4ad0ad
explain: list the types instead of saying 'subjects'
claude Oct 8, 2026
65fe6b5
Synonyms for wildcard patterns too; explain tests search by species
claude Oct 8, 2026
179fd77
Linked and ingested sessions in a V2 dataset
claude Oct 9, 2026
5dad669
V2 object layer doc: linked and ingested sessions
claude Oct 9, 2026
72e33c6
Haley import: each session written once, linked from the dataset (dec…
claude Oct 9, 2026
8489f88
verifyDataset: one progress bar per database checked
claude Oct 9, 2026
75f937a
Add ndi.cloud.api.feedback commands and ndi.unittest.cloud.feedbackTest
claude Oct 11, 2026
846c4bd
Merge pull request #1023 from VH-Lab/claude/feedback-api-test
stevevanhooser Oct 11, 2026
4a571dc
Fix cloud login: decode JWT payload with matlab.net.base64decode
claude Oct 11, 2026
1dd3029
Merge pull request #1024 from VH-Lab/claude/fix-decodejwt-base64
stevevanhooser Oct 11, 2026
37afa16
Merge origin/main into claude/lightsheet-zarr-ndi-viewer-djp5vk
claude Oct 11, 2026
2f9b0ea
lightsheet: launch the napari viewer with debugging on and its log kept
claude Oct 12, 2026
04ce18f
lightsheet: fromSPIM -- pyramid straight from SmartSPIM All_Channels
claude Oct 12, 2026
668beb1
TestFromSPIM: the fixture's slices are 0-based; preallocate fromSPIM'…
claude Oct 12, 2026
2b81bfe
Add file CRC-32 support for NDI Cloud: lookupFiles, checksummed uploa…
claude Oct 12, 2026
8fa6ea5
Send the signed x-amz-checksum-crc32 header from putFiles (NDI Cloud …
claude Oct 12, 2026
199724e
Fail, not skip, live checksum tests on a server without #162
claude Oct 12, 2026
f7955ea
Fail, not skip, the signed URL set job test when the worker is missing
claude Oct 12, 2026
1426fcd
Merge pull request #1025 from VH-Lab/claude/file-crc32-lookup
stevevanhooser Oct 13, 2026
efff004
Merge remote-tracking branch 'origin/main' into claude/lightsheet-zar…
claude Oct 13, 2026
fb10169
LightsheetZarrManager: View dialog previews the exact logged launch c…
claude Oct 13, 2026
b8e4b54
TestLaunchViewer: match the manager's logged launch and preview
claude Oct 13, 2026
eb58328
Add ndi.cloud.api.files.waitForFileUpload; use it in FilesTest
claude Oct 13, 2026
9c21eb6
uploadSingleFile: wait for NDI Cloud to record the upload
claude Oct 13, 2026
c982a18
FileUploadWaitMockTest: make helpers local functions
claude Oct 13, 2026
44e707a
Merge pull request #1027 from VH-Lab/claude/wait-for-file-upload
stevevanhooser Oct 13, 2026
d94973c
Merge origin/main into claude/lightsheet-zarr-ndi-viewer-djp5vk
claude Oct 13, 2026
d292984
Merge pull request #979 from VH-Lab/claude/lightsheet-zarr-ndi-viewer…
stevevanhooser Oct 14, 2026
aa96851
Merge remote-tracking branch 'origin/main' into claude/progressbar-fill
claude Oct 14, 2026
695e8d8
Merge pull request #1020 from VH-Lab/claude/progressbar-fill
stevevanhooser Oct 14, 2026
ce4dae2
Merge NDI-matlab main into claude/v2-object-layer
claude Oct 14, 2026
7aad37c
Haley import: a run that writes the dataset ingests every session (de…
claude Oct 14, 2026
6b7967e
TestDid2SqliteHierarchy: the AND test uses a class ndi.query does not…
claude Oct 14, 2026
72e55bc
Haley import: experiment 14 of foragingConcentration is single-patch …
claude Oct 14, 2026
7b8efb3
Haley import: exclusion reasons, no day flag, unfilmed plates (#73)
claude Oct 14, 2026
b166d00
Haley import: Mini plates 55 and 189 room temperature is an estimate …
claude Oct 14, 2026
f5800eb
Haley import: a seeding time known only to a span (#75)
claude Oct 14, 2026
4e890a0
Haley import: compare patch densities as a set, not in order (#76)
claude Oct 14, 2026
5a07064
Haley import: sensory experiment 2 room temperature date (#77)
claude Oct 14, 2026
1bf6798
Haley import: extract the E. coli profile curves into a small file
claude Oct 14, 2026
34bea7a
Haley import: record the E. coli profile extract (#78)
claude Oct 14, 2026
eceb60e
Haley import: say whether missing E. coli images were analysed (#79)
claude Oct 14, 2026
d80c29d
Haley import: place partly detected E. coli images on the grid (#80)
claude Oct 14, 2026
22b0aee
Haley import: learn the E. coli patch layout from the acetate templat…
claude Oct 14, 2026
3399e91
Haley import: place partly detected E. coli images by the acetate tem…
claude Oct 14, 2026
1afa028
Haley import: decision #80 verified by eye
claude Oct 14, 2026
60fe012
Haley import: name a missing failed-start video as such (#81)
claude Oct 14, 2026
34280c1
Haley import: record an unmeasured suspension for a growth day (#82)
claude Oct 14, 2026
ad8ec5f
Haley import: note a plate's camera change; say when no plate is miss…
claude Oct 14, 2026
206a4a5
Fix two CI failures: reasons as text statements; keep V2 connections …
claude Oct 14, 2026
3750a1d
Haley import: a tag's reason is its notes when statements have notes …
claude Oct 14, 2026
7331012
Haley import: report a reason the schema cannot hold (#73)
claude Oct 14, 2026
44d069d
TestHaleyWrite: read the exclusion test's documents under current names
claude Oct 14, 2026
fb46ffe
Haley import: a data body's format is an EDAM file-format term (#84)
claude Oct 14, 2026
d8a1f70
Lightsheet readers read a migrated (V_eta) pyramid as well as v1
claude Oct 15, 2026
b26435e
Haley import: software names its developer, the dataset its host (#85)
claude Oct 15, 2026
1b26ddf
Lightsheet vintage tests: declare a series by its manifest; no self i…
claude Oct 15, 2026
fe74f8e
Lightsheet keys: read origin/spacing as did2.build.key writes them, {…
claude Oct 15, 2026
7baa073
Haley import #17: a strain is distributed_as its stock product
claude Oct 15, 2026
f503afa
datasetMetadata: no hosted_by loop over an empty host list
claude Oct 15, 2026
a4af79c
LightsheetZarrManager: read a v1 subject's name exactly as before
claude Oct 15, 2026
9b46cef
didsqlite: plain `isa subject` on a v1 database
claude Oct 15, 2026
5c79009
Lightsheet: find migrated pyramids by method; label from notes
claude Oct 15, 2026
3b93439
lightsheetKeys: read a regular, unit-less axis as voxel size 1, trans…
claude Oct 15, 2026
b6ea5a9
Haley encounters: skip a worm only when its own track is on an unreco…
claude Oct 15, 2026
e0ac92c
Series ingest reconstruction: number members in the document's own base
claude Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/test-eta-migrate-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,10 @@ jobs:
- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v2
with:
release: latest
# PINNED: `latest` moved to R2026b (2026-10-01), whose
# matlab.addons.install no longer accepts the agreeToLicense argument
# MatBox's installFexPackage passes. Unpin when MatBox is updated.
release: R2026a
cache: true
products: |
Statistics_and_Machine_Learning_Toolbox
Expand Down
253 changes: 253 additions & 0 deletions .github/workflows/test-import-v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
name: V2 import (Haley) -- fast

# Stage-by-stage tests of ndi.setup.conv.haley.import_V2 and the generic
# ndi.setup.V2 makers it uses. They build V2 (V_eta) documents through
# DID-matlab's did2.build, so this job needs DID-matlab V2 and a did-schema
# that carries the classes the import uses.
#
# DID_SCHEMA_REF: main. The `study` entity, `instance_of`, `awarded_to` and
# `directed_relation.roles` the import uses reached main with did-schema PR #78
# (merged 2026-09-30).
#
# The tests FILTER themselves when the schema or did2.build is missing; this
# job fails if any were filtered, so a green run always means they ran.

on:
push:
branches: ['claude/**']
paths:
- 'src/ndi/+ndi/+setup/+V2/**'
- 'src/ndi/+ndi/+setup/+conv/+haley/import_V2*'
- 'src/ndi/+ndi/+setup/+conv/+haley/**'
- 'src/ndi/+ndi/+setup/+conv/+haley/sessionList.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/subjectList.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/idPrefix.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/recordingList.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/sessionDocuments.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/relationDocuments.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/transferTime.m'
- 'src/ndi/+ndi/+file/+navigator/bodies.m'
- 'src/ndi/+ndi/+database/+fun/externalFileLocation.m'
- 'src/ndi/+ndi/+database/+implementations/+database/did2sqlite.m'
- 'src/ndi/+ndi/+database/+internal/applyReadNormalization.m'
- 'tests/+ndi/+unittest/+setup/+V2/**'
- 'src/ndi/+ndi/+vintage/**'
- 'src/ndi/+ndi/+v2/**'
- 'src/ndi/+ndi/entity.m'
- 'src/ndi/+ndi/dataset.m'
- 'src/ndi/+ndi/+dataset/dir.m'
- 'src/ndi/+ndi/session.m'
- 'src/ndi/+ndi/subject.m'
- 'src/ndi/+ndi/statement.m'
- 'src/ndi/+ndi/assertion.m'
- 'src/ndi/+ndi/interaction.m'
- 'src/ndi/+ndi/observation.m'
- 'src/ndi/+ndi/manipulation.m'
- 'src/ndi/+ndi/calculation.m'
- 'src/ndi/+ndi/data_type.m'
- 'tests/+ndi/+unittest/+vintage/**'
- 'src/ndi/+ndi/+fun/+doc/+lightsheet/**'
- 'src/ndi/+ndi/+gui/+app/LightsheetZarrManager.m'
- 'src/ndi/+ndi/+database/+fun/extract_docs_files.m'
- 'src/ndi/+ndi/+database/+internal/list_binary_files.m'
- 'tests/+ndi/+unittest/+fun/+doc/+lightsheet/**'
- 'tests/+ndi/+unittest/+gui/+app/TestLightsheetZarrManager.m'
- 'src/ndi/+ndi/+database/+implementations/+database/didsqlite.m'
- 'tests/+ndi/+unittest/+database/TestV1SubjectSearch.m'
- 'src/ndi/+ndi/+cloud/+sync/+internal/reconstructSeriesIngestLocations.m'
- 'src/ndi/+ndi/+cloud/+sync/+internal/updateFileInfoForLocalFiles.m'
- 'src/ndi/+ndi/+cloud/+sync/+internal/updateFileInfoForRemoteFiles.m'
- 'tests/+ndi/+unittest/ReconstructSeriesIngestLocationsTest.m'
- '.github/workflows/test-import-v2.yml'
pull_request:
branches: [main, V2]
paths:
- 'src/ndi/+ndi/+setup/+V2/**'
- 'src/ndi/+ndi/+setup/+conv/+haley/import_V2*'
- 'src/ndi/+ndi/+setup/+conv/+haley/**'
- 'src/ndi/+ndi/+setup/+conv/+haley/sessionList.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/subjectList.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/idPrefix.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/recordingList.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/sessionDocuments.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/relationDocuments.m'
- 'src/ndi/+ndi/+setup/+conv/+haley/transferTime.m'
- 'src/ndi/+ndi/+file/+navigator/bodies.m'
- 'src/ndi/+ndi/+database/+fun/externalFileLocation.m'
- 'src/ndi/+ndi/+database/+implementations/+database/did2sqlite.m'
- 'src/ndi/+ndi/+database/+internal/applyReadNormalization.m'
- 'tests/+ndi/+unittest/+setup/+V2/**'
- 'src/ndi/+ndi/+vintage/**'
- 'src/ndi/+ndi/+v2/**'
- 'src/ndi/+ndi/entity.m'
- 'src/ndi/+ndi/dataset.m'
- 'src/ndi/+ndi/+dataset/dir.m'
- 'src/ndi/+ndi/session.m'
- 'src/ndi/+ndi/subject.m'
- 'src/ndi/+ndi/statement.m'
- 'src/ndi/+ndi/assertion.m'
- 'src/ndi/+ndi/interaction.m'
- 'src/ndi/+ndi/observation.m'
- 'src/ndi/+ndi/manipulation.m'
- 'src/ndi/+ndi/calculation.m'
- 'src/ndi/+ndi/data_type.m'
- 'tests/+ndi/+unittest/+vintage/**'
- 'src/ndi/+ndi/+fun/+doc/+lightsheet/**'
- 'src/ndi/+ndi/+gui/+app/LightsheetZarrManager.m'
- 'src/ndi/+ndi/+database/+fun/extract_docs_files.m'
- 'src/ndi/+ndi/+database/+internal/list_binary_files.m'
- 'tests/+ndi/+unittest/+fun/+doc/+lightsheet/**'
- 'tests/+ndi/+unittest/+gui/+app/TestLightsheetZarrManager.m'
- 'src/ndi/+ndi/+database/+implementations/+database/didsqlite.m'
- 'tests/+ndi/+unittest/+database/TestV1SubjectSearch.m'
- 'src/ndi/+ndi/+cloud/+sync/+internal/reconstructSeriesIngestLocations.m'
- 'src/ndi/+ndi/+cloud/+sync/+internal/updateFileInfoForLocalFiles.m'
- 'src/ndi/+ndi/+cloud/+sync/+internal/updateFileInfoForRemoteFiles.m'
- 'tests/+ndi/+unittest/ReconstructSeriesIngestLocationsTest.m'
- '.github/workflows/test-import-v2.yml'
workflow_dispatch:

concurrency:
group: test-import-v2-${{ github.ref }}
cancel-in-progress: true

jobs:
import-v2:
name: V2 import stages (${{ matrix.schema }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# current: did-schema main with DID-matlab V2 (from requirements.txt).
# next: the one-entity-class / composition schema and the did2.build that
# reads it, both still on review branches (did-schema PR #88, DID-matlab
# PR #218). The import and the readers build and read both shapes, so both
# legs must pass. Drop `next` once both merge.
include:
- schema: current
did_schema_ref: main
did_matlab_ref: ''
- schema: next
did_schema_ref: claude/ecstatic-pascal-md91fw
did_matlab_ref: claude/ecstatic-pascal-md91fw
env:
DID_SCHEMA_REF: ${{ matrix.did_schema_ref }}
steps:
- name: Check out NDI-matlab
uses: actions/checkout@v4

- name: Check out did-schema
uses: actions/checkout@v4
with:
repository: Waltham-Data-Science/did-schema
ref: ${{ env.DID_SCHEMA_REF }}
path: did-schema

- name: Assemble the V2 schema set
run: |
mkdir -p "${GITHUB_WORKSPACE}/eta-schemas"
cp did-schema/schemas/V_eta/stable/*.json "${GITHUB_WORKSPACE}/eta-schemas/"
cp did-schema/schemas/V_eta/draft/*.json "${GITHUB_WORKSPACE}/eta-schemas/"
cp did-schema/schemas/V_eta/deprecated/*.json "${GITHUB_WORKSPACE}/eta-schemas/"
echo "DENOMINATOR: $(ls "${GITHUB_WORKSPACE}/eta-schemas" | wc -l) schema file(s) from did-schema@$(git -C did-schema rev-parse --short HEAD)"
echo "DID_SCHEMA_PATH=${GITHUB_WORKSPACE}/eta-schemas" >> "$GITHUB_ENV"

- name: Check out DID-matlab (next leg only)
if: ${{ matrix.did_matlab_ref != '' }}
uses: actions/checkout@v4
with:
repository: VH-Lab/DID-matlab
ref: ${{ matrix.did_matlab_ref }}
path: did-matlab-next

- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v2
with:
# PINNED: `latest` moved to R2026b (2026-10-01), whose
# matlab.addons.install no longer accepts the agreeToLicense argument
# MatBox's installFexPackage passes, so dependency install fails before
# any test runs. Unpin when MatBox is updated.
release: R2026a
cache: true

- name: Install MatBox
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: ehennestad/matbox-actions/install-matbox@v1

- name: Install repo dependencies
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: matlab-actions/run-command@v2
with:
command: |
addpath(genpath("src"));
addpath(genpath("tools"));
matbox.installRequirements(nditools.projectdir());
matbox.installRequirements(fullfile(pwd, "tests"));

- name: Run the V2 import tests
uses: matlab-actions/run-command@v2
with:
command: |
addpath(genpath("src"));
addpath(genpath("tests"));
if isfolder("did-matlab-next/src")
% ahead of the installed DID-matlab V2 on the path
addpath(genpath(fullfile(pwd, "did-matlab-next", "src")));
disp("DID-matlab from did-matlab-next (" + string(which("did2.build.document")) + ")");
end
import matlab.unittest.TestSuite;
suite = [TestSuite.fromPackage("ndi.unittest.setup.V2", "IncludingSubpackages", true), ...
TestSuite.fromClass(?ndi.unittest.vintage.TestVintageMap), ...
TestSuite.fromClass(?ndi.unittest.vintage.TestLightsheetVintage), ...
TestSuite.fromClass(?ndi.unittest.vintage.TestLightsheetMigratedSession)];
% The lightsheet readers over the REAL migrator's output need
% DID-matlab's lightsheet migrators, which only the next leg's
% DID-matlab has. Selected there, not filtered here.
if isfolder("did-matlab-next/src")
suite = [suite, TestSuite.fromClass(?ndi.unittest.vintage.TestLightsheetMigrated)];
end
results = run(suite);
disp(table(results));
nRun = numel(results);
nFailed = sum([results.Failed]);
nIncomplete = sum([results.Incomplete]);
fprintf("VERDICT: %d run, %d failed, %d filtered\n", nRun, nFailed, nIncomplete);
assert(nRun > 0, "no V2 import tests were selected");
assert(nFailed == 0, sprintf("%d V2 import test(s) failed", nFailed));
assert(nIncomplete == 0, sprintf("%d V2 import test(s) were FILTERED -- did2.build or the schema is missing", nIncomplete));

- name: Run the v1 lightsheet reader tests (v1 must read exactly as before)
# The lightsheet readers now route through ndi.vintage so they also
# read a MIGRATED pyramid. These are the existing v1 tests (legacy
# session, documents makePyramid writes), otherwise run only by
# run-tests.yml on main/Vnext; here so a branch that touches the
# readers proves v1 unchanged. A filtered test (a missing optional
# dependency) is reported, not counted as a pass, and not a failure.
uses: matlab-actions/run-command@v2
with:
command: |
addpath(genpath("src"));
addpath(genpath("tests"));
if isfolder("did-matlab-next/src")
addpath(genpath(fullfile(pwd, "did-matlab-next", "src")));
end
% as run-tests.yml runs them: no V2 schema set
setenv("DID_SCHEMA_PATH", "");
import matlab.unittest.TestSuite;
suite = [TestSuite.fromClass(?ndi.unittest.fun.doc.lightsheet.TestLevelTable), ...
TestSuite.fromClass(?ndi.unittest.fun.doc.lightsheet.TestChooseLevel), ...
TestSuite.fromClass(?ndi.unittest.fun.doc.lightsheet.TestMakePyramid), ...
TestSuite.fromClass(?ndi.unittest.fun.doc.lightsheet.TestFromSPIM), ...
TestSuite.fromClass(?ndi.unittest.gui.app.TestLightsheetZarrManager), ...
TestSuite.fromClass(?ndi.unittest.database.TestV1SubjectSearch), ...
TestSuite.fromClass(?ndi.unittest.ReconstructSeriesIngestLocationsTest)];
results = run(suite);
disp(table(results));
nRun = numel(results);
nFailed = sum([results.Failed]);
nIncomplete = sum([results.Incomplete]);
fprintf("VERDICT (v1 lightsheet): %d run, %d failed, %d filtered\n", nRun, nFailed, nIncomplete);
assert(nRun > 0, "no v1 lightsheet tests were selected");
assert(nFailed == 0, sprintf("%d v1 lightsheet test(s) failed", nFailed));
65 changes: 65 additions & 0 deletions find_napari_debug_log.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env zsh
# find_napari_debug_log.sh - locate and print the napariViewLightsheet debug
# log written by ndi.test.cloud.lightsheet_blob_cloud_roundtrip.
#
# The demo defaults DebugLogFile to fullfile(tempdir, 'NapariLightSheetDebugging.txt')
# which on macOS resolves under $TMPDIR (typically /var/folders/xx/yyy/T/),
# not /tmp. This script looks in the usual spots, picks the newest match,
# and cats it.

set -u
setopt NULL_GLOB EXTENDED_GLOB

name="NapariLightSheetDebugging.txt"

# Places to check, ordered by likelihood on macOS.
locations=(
"${TMPDIR:-}/$name" # matches MATLAB's tempdir
"/tmp/$name" # a user who set DebugLogFile explicitly
"$HOME/$name" # or dropped it in their home
"/private/tmp/$name" # macOS's real /tmp target
)

# De-duplicate, drop empties, and keep only paths that actually exist.
typeset -a found
for p in "${locations[@]}"; do
[[ -n "$p" && -f "$p" ]] && found+=("$p")
done

# Wider net if the shortlist misses: recurse under /var/folders and $TMPDIR
# (macOS parks per-user tempdirs there). Prune common noise; keep the
# recent ones.
if (( ${#found} == 0 )); then
for root in "${TMPDIR:-}" /var/folders /private/var/folders /tmp; do
[[ -n "$root" && -d "$root" ]] || continue
while IFS= read -r hit; do
[[ -n "$hit" ]] && found+=("$hit")
done < <(find "$root" -maxdepth 6 -type f -name "$name" 2>/dev/null)
done
fi

if (( ${#found} == 0 )); then
print -u2 "No $name found. If your demo ran with a custom DebugLogFile,"
print -u2 "point me at it: $0 /full/path/to/logfile"
print -u2 "Or check the value of tempdir in MATLAB (>> tempdir)."
exit 1
fi

# Pick the newest match if multiple.
newest="$found[1]"
newest_mtime=$(stat -f %m "$newest" 2>/dev/null || stat -c %Y "$newest" 2>/dev/null)
for f in "$found[@]"; do
m=$(stat -f %m "$f" 2>/dev/null || stat -c %Y "$f" 2>/dev/null)
if [[ -n "$m" && "$m" -gt "$newest_mtime" ]]; then
newest="$f"
newest_mtime="$m"
fi
done

print -u2 "=== Reading: $newest ==="
if (( ${#found} > 1 )); then
print -u2 "(also found: ${(j:, :)found[2,-1]})"
fi
print -u2 ""

cat -- "$newest"
57 changes: 57 additions & 0 deletions lightsheet_test_blob_ingest_and_view.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
%LIGHTSHEET_TEST_BLOB_INGEST_AND_VIEW - end-to-end smoke test on the synthetic blob
%
% Builds a small OME-Zarr fixture on disk (a couple of MB), ingests it
% into a fresh NDI session with the blosc-zstd fast path, and launches
% the napari viewer on the resulting pyramid. Use this to sanity-check
% the ingest -> document -> viewer pipeline without touching the real
% SmartSPIM data. Runs in under a minute on a laptop.
%
% Edit CONFIG below if you want to keep the fixture / session between
% runs, change the blob size, or point at a different napari launcher.
%
% See also: ndi.test.lightsheet.makeBlobFixture,
% ndi.test.lightsheet.ingestBlobFixture,
% ndi.fun.doc.lightsheet.fromOMEZarr,
% ndi.fun.doc.lightsheet.view

% -------------------- CONFIG -----------------------------------------
% Leave scratch dirs empty to land under tempname (fresh every run).
sessionDir = ''; % e.g. '~/ndi-sessions/blob-demo'
zarrDir = ''; % e.g. '~/ndi-sessions/blob-demo/zarr'
subjectName = 'blob@vhlab';

% Blob size / shape. 128^3 uint16 across 2 channels ~= 16 MB raw; small
% enough for a fast smoke test, big enough that the pyramid has real
% levels to look at.
shape = [128 128 128];
numChannels = 2;
chunkShape = [32 32 32];
numLevels = 3;

% Ingest options. blosc-zstd exercises the encodeChunk fast path; keep
% clevel modest for a smoke test.
codec = 'blosc-zstd';
clevel = 5;

% Napari launcher. Same wrapper the real-data script uses.
launcher = '/usr/local/bin/napariViewLightsheet';
% ---------------------------------------------------------------------

fprintf('Building blob fixture and ingesting ...\n');
[S, P, info] = ndi.test.lightsheet.ingestBlobFixture( ...
'sessionDir', sessionDir, ...
'zarrDir', zarrDir, ...
'subject', subjectName, ...
'Shape', shape, ...
'NumChannels', numChannels, ...
'ChunkShape', chunkShape, ...
'NumLevels', numLevels, ...
'codec', codec, ...
'clevel', clevel);

fprintf('Session: %s\n', char(S.path()));
fprintf('Zarr: %s\n', info.zarrPath);
fprintf('Pyramid: %s\n', P.id());

fprintf('Launching napari viewer ...\n');
ndi.fun.doc.lightsheet.view(S, P.id(), 'launcher', launcher);
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ https://github.com/Waltham-Data-Science/NDI-compress-matlabp
https://github.com/Waltham-Data-Science/ndi-ontology-matlab
https://github.com/VH-Lab/pyraview
https://github.com/bastibe/Violinplot-Matlab
fex://134212-openminds-metadata-models-for-matlab/0.11.0
https://github.com/openMetadataInitiative/openMINDS_MATLAB@v0.12.0
Loading
Loading