Skip to content

Symmetry Stage 3: MATLAB cannot read Python-generated artifacts — .ndi is missing the object-type marker #73

Closed

Description

@stevevanhooser

What is wrong

The cross-language symmetry job runs three gated stages. Stage 3 — MATLAB readArtifacts, reading artifacts Python generated — fails: 23 passed, 11 failed, and every single failure is SourceType=pythonArtifacts. The paired SourceType=matlabArtifacts variant of each one passes.

MATLAB reading its own artifacts is fine. MATLAB reading Python's is not.

Why this has never been seen

Stage 3 is gated behind Stage 2, and Stage 2 has been failing since June. On main (c7fd864) the job records:

Step Result
Stage 1: MATLAB makeArtifacts success
Stage 2: Python symmetry tests (make + read) failure
Stage 3: MATLAB readArtifacts skipped

Stage 2's failure was the daqSystemNames 8-vs-9 mismatch — #71. With that fixed in #72, Stage 2 passes and Stage 3 ran for the first time, exposing this. These failures are revealed, not caused, by that PR.

This is the same shape as #71 itself: a real failure invisible because it was unreachable.

Eight of the failures are one message

Field filesInDotNDI has different lengths in summary1 (5) and summary2 (3)

Identical, verbatim, in all eight of:

  • session.blankSessionKjnielsenlab/testBlankSessionKjnielsenlab
  • session.blankSessionMarderlab/testBlankSessionMarderlab
  • session.blankSessionRayolab/testBlankSessionRayolab
  • session.blankSessionVhlab/testBlankSessionVhlab
  • session.buildSession/testBuildSessionArtifacts
  • session.ingestionAxonNDR/testIngestionAxonNDRArtifacts
  • session.ingestionIntan/testIngestionIntanArtifacts
  • session.ingestionIntanNDR/testIngestionIntanNDRArtifacts

So this is one root cause repeated eight times, not eight problems. Note the field: filesInDotNDI, not daqSystemNames — the DAQ-system counts now agree, which is what #71 asked for.

The ninth failure names the likely cause

ndi.session.dir.directorytype returned 'session' for the pythonArtifacts dataset
artifact directory; expected 'dataset'. A dataset that has never been opened
since markers were introduced reports 'unknown'.

(session.objectTypeMarker/testDatasetDirectoryType)

MATLAB records what a directory holds in a marker file inside .ndi. From ndi.session.dir.objecttypemarkerfilename():

fname = 'ndi_object_type.txt';

NDI-python never writes it. grep -rn "ndi_object_type\|object_type" --include=*.py src/ returns nothing.

What Python actually writes

summary filesInDotNDI : ['did-sqlite.sqlite', 'reference.txt', 'unique_reference.txt']
on disk .ndi          : ['did-sqlite.sqlite', 'reference.txt', 'unique_reference.txt']

Three entries, and the summary agrees with the disk — so Python is self-consistent. MATLAB expects five.

Hypothesis, not verified: the two missing entries are ndi_object_type.txt (confirmed absent) and DID's files/ directory, which after #70 is created lazily. That accounts for 3 vs 5. Writing the marker would fix the ninth failure outright and plausibly the eight filesInDotNDI ones with it — but the second half of the arithmetic is a guess, and someone with MATLAB should confirm by listing a MATLAB-generated .ndi before anyone writes code against it.

The rest

Two errored (not verification failures), both dataset-level, cause not yet established:

  • dataset.buildDataset/testBuildDatasetArtifacts(pythonArtifacts)
  • dataset.downloadIngested/testDownloadIngestedArtifacts(pythonArtifacts)

Four skipped, because the artifacts simply are not there — Python's makeArtifacts never produces them:

Test Diagnostic: pythonArtifacts pathSafeName artifact missing. Skipping.
Test Diagnostic: pythonArtifacts whatVaries artifact missing. Skipping.
  • fun.pathSafeName/testMatlabPythonSymmetry, fun.pathSafeName/testInputsAgree
  • fun.whatVaries/testMatlabPythonSymmetry, fun.whatVaries/testKnownDivergencesAreStillReal

These are separate from the marker problem: two artifact generators are missing on the Python side. They are skips today, so they do not fail the job, but they mean four symmetry checks are silently not running.

Suggested order

  1. Write ndi_object_type.txt from ndi_session_dir / ndi_dataset, matching MATLAB's updateObjectTypeMarker / setObjectTypeMarker. Cheapest, and it is the one failure with a named cause.
  2. Re-run and re-count. If filesInDotNDI still disagrees, list a MATLAB-generated .ndi and diff it against Python's rather than guessing at the fifth entry.
  3. Root-cause the two dataset errors, which have no diagnostic yet and may be unrelated to the file listing.
  4. Add the missing pathSafeName and whatVaries artifact generators, so those four checks stop silently skipping.

The acceptance criterion is Stage 3 passing, which would make the symmetry job green end to end for the first time since June.

Verification of the claims above

  • Stage table: job steps on main run 33338462744 vs Port the vhprairieview image DAQ system, and stop swallowing load failures #72 run 33340014507.
  • Failure list and diagnostics: Stage 3 log of the latter.
  • Marker filename: src/ndi/+ndi/+session/dir.m in NDI-matlab, objecttypemarkerfilename().
  • Marker absent from Python, and the three-entry .ndi listing: run locally against this branch.
  • No MATLAB in the environment where this was written, so nothing MATLAB-side was executed; the MATLAB claims are read from source and from CI logs.

Related

Activity

  1. stevevanhooser commented on Aug 31, 2026

    @stevevanhooser
    ContributorAuthor

    Resolved — Stage 3 is green

    The symmetry job passes end to end on #74, all five checks: run 33343247075. Stage 3 has never passed before, because it had never run before.

    Two changes did it:

    The buildDataset finding

    This issue listed two errored tests with "cause not yet established":

    • dataset.buildDataset/testBuildDatasetArtifacts(pythonArtifacts)
    • dataset.downloadIngested/testDownloadIngestedArtifacts(pythonArtifacts)

    Both were fixed by the object-type marker alone, before the files/ change landed. They are not a separate defect.

    The evidence is the intermediate run, 33343247075 attempt 1 — marker present, files/ still missing because did had not yet been updated. That run went from 23 passed, 11 failed to 26 passed, 8 failed, and the two dataset tests dropped out of the failure summary entirely while the eight filesInDotNDI failures remained, their message shifting from (5) and (3) to (5) and (4).

    So the marker cleared exactly three: objectTypeMarker, plus these two.

    On the mechanism, being careful about what is actually established: what is observed is that both went from Errored to passing when the marker was added, and nothing else changed in that run. The plausible reading is that they open the artifact directory as a dataset and MATLAB could not identify a Python-written directory as one — the same root cause objectTypeMarker reports directly, which is why one fix cleared all three. I did not read those tests' source to confirm that path, so treat the mechanism as inference and the before/after as fact.

    That also explains why the marker was worth more than its one named test: directorytype returning 'session' for a dataset directory is not a cosmetic mismatch, it makes MATLAB mis-handle the directory.

    Status against this issue's suggested order

    1. Write ndi_object_type.txt done (#74)
    2. Re-run and re-count; find the fifth .ndi entry done — it is files/, confirmed against a real MATLAB .ndi, fixed in DID-python#57
    3. Root-cause the two dataset errors done — same root cause as item 1, no separate work
    4. Add the missing pathSafeName / whatVaries artifact generators still open

    What is left

    Only item 4. Four tests remain Filtered by assumption, unchanged throughout:

    Test Diagnostic: pythonArtifacts pathSafeName artifact missing. Skipping.
    Test Diagnostic: pythonArtifacts whatVaries artifact missing. Skipping.
    
    • fun.pathSafeName/testMatlabPythonSymmetry, fun.pathSafeName/testInputsAgree
    • fun.whatVaries/testMatlabPythonSymmetry, fun.whatVaries/testKnownDivergencesAreStillReal

    These are skips, not failures, so the job is green with them outstanding — which is precisely the hazard this issue was filed about in the first place. Four symmetry checks are silently not running because Python's makeArtifacts never produces those two artifacts. Worth keeping this issue open for that, or splitting it out so a green job does not bury it.


    Generated by Claude Code

  2. stevevanhooser commented on Aug 31, 2026

    @stevevanhooser
    ContributorAuthor

    Done. All four items in the suggested order, and the acceptance criterion is met: Stage 3 passes, 38 passed / 0 failed, on main (2dfa3ff) against NDI-matlab main (3b865ef).

    item resolution
    1. Write ndi_object_type.txt #74
    2. Re-count filesInDotNDI the fifth entry was DID's files/, created by VH-Lab/DID-python#57 — not NDI's to create
    3. Root-cause the two dataset errors fixed by item 1; see below
    4. Add the pathSafeName / whatVaries generators #75

    Three corrections to what this issue assumed, worth recording:

    The files/ fix did not belong here. A commit on #74's branch briefly created .ndi/files from ndi_database.__init__. That was the wrong layer: DID-MATLAB's sqlitedb constructor creates it and NDI-matlab never touches .ndi/files except to read it. Reverted and fixed upstream in DID-python#57 instead.

    Item 3 needed no separate work. This issue said the two errored dataset tests (buildDataset, downloadIngested) had "cause not yet established" and might be unrelated. They were not — writing the marker cleared them along with objectTypeMarker, so item 1 fixed three of the failures, not one.

    Item 4 was larger than described. The issue framed it as two missing generators. The functions they exercise were absent from this repository too — ndi.fun had no pathSafeName/elementDirectoryName, and ndi.fun.stimulus had no whatVaries/whatIsConstant. #75 ports all four, then the battery, then the generators.

    First contact between the two hand-written batteries then needed reconciliation, which the schema had predicted: two reader bugs (MATLAB's jsonencode collapses a one-element int list to a bare number) and one measurement that refuted a documented prediction — cellValuedConstantParameter never diverged. The last real divergence, allNaNParameter, was fixed in #76 / VH-Lab/NDI-matlab#902, so both allow-lists are now empty.

    Follow-ups filed rather than left implicit: #77 (a missing artifact still turns a check into a silent pass — this issue's own failure mode, one layer down) and #78 (unported MATLAB functionality).


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions