Skip to content

Rebuild series ingest_locations on SyncFiles=false download (#302) - #303

Merged
stevevanhooser merged 2 commits into
mainfrom
claude/rebuild-ingest-locations-syncfiles-false
Sep 14, 2026
Merged

stevevanhooser merged 2 commits into
mainfrom
claude/rebuild-ingest-locations-syncfiles-false

Conversation

@stevevanhooser

Copy link
Copy Markdown
Contributor

Closes #302. Ports VH-Lab/NDI-matlab#988 -- the SyncFiles=false half of the file-series cloud round trip.

What changes

ndi.cloud.filehandler.updateFileInfoForRemoteFiles now teaches series to survive a SyncFiles=false cloud round trip. DID strips ingest_locations before storing a document, so one that comes back declares n_present members with no location for any of them -- exactly the shape DID's MembersNotLocatable guard (DID-matlab#185 / DID-python's _reject_series_without_ingest_locations) refuses on add_docs. The whole SyncFiles=false path was blocked on that guard for any document carrying a populated series.

The manifest bytes are not on disk on this path -- SyncFiles=false does not download them -- so the reconstruction has to fetch. It goes through the DID custom_file_handler contract (VH-Lab/DID-matlab#201, VH-Lab/DID-python#88), the same contract DID uses on the read side, so a caller with a handler already can pass it in and both sides use the same one. With no handler, fetch_cloud_file is called directly, matching what the read-side handler does when a manifest is asked for by uid.

Manifests fetched here do not land in the DID file cache; a subsequent member open trips DID#201's lazy fetch, which is what populates the cache. Cache eviction on a later session is answered by the same re-fetch path.

Code

  • src/ndi/cloud/filehandler.py:

    • updateFileInfoForRemoteFiles gains keyword-only custom_file_handler=None and client=None. After the file_info rewrite it checks _needs_series_reconstruction; when a series has n_present > 0 and empty ingest_locations, _reconstruct_series_from_cloud fetches each qualifying manifest to a scratch tempfile.TemporaryDirectory and calls reconstructSeriesIngestLocations before the temp dir is cleaned up.
    • _fetch_manifest dispatches through the handler when given (with seriesName="" in the context -- a non-empty seriesName marks a MEMBER fetch, where the handler treats ctx.uid as the file to retrieve and source_path as the manifest, the exact opposite of what we want here) or falls back to fetch_cloud_file.
    • _dispatch_custom_file_handler mirrors DID-python's arity-aware dispatch, so a two-argument handler still works.
    • A per-manifest fetch failure is logged; the entry is left un-reconstructed so DID's guard fires on add_docs with the document's own identity.
  • Bridge yaml: updateFileInfoForRemoteFiles's matlab_last_sync_hash bumped to aea0c72c (the NDI-matlab main head that carries #988). custom_file_handler and client recorded as keyword-only arguments.

Tests

tests/test_cloud_series_reconstruction_remote.py (new, seven tests, all offline):

  • TestFileInfoReshape -- every file_info location becomes ndicloud, ingest=0, delete_original=0, uid survives, location is ndic://<dataset>/<uid>. Covers both plain and series documents.
  • TestReconstructionSkipsWhenNotNeeded -- a doc whose ingest_locations is already populated skips reconstruction (no fetch, no handler call). Same for a doc without series_info.
  • TestReconstructionThroughAMockHandler -- end-to-end offline: a mock handler serves manifest bytes by uid; the test walks a doc through DID's database_add / database_search cycle so ingest_locations gets stripped (the exact shape a cloud round trip returns), reshapes via updateFileInfoForRemoteFiles(..., custom_file_handler=mock), and pins:
    • one entry per present member, ndic:// location, ingest=0.
    • exactly one manifest fetch total (a 28,000-member series must not provoke 28,000 fetches).
    • seriesName="" in the handler context on the manifest fetch.
    • a handler failure leaves ingest_locations empty so DID's guard fires on add.
    • a two-argument handler still works through the arity-aware dispatch.

Mirrors NDI-matlab's tests/+ndi/+unittest/+database/TestUpdateFileInfoForRemoteFilesShape.m (VH-Lab/NDI-matlab#987) shape-for-shape.

Verification

pytest tests/test_cloud_series_reconstruction.py \
       tests/test_cloud_series_reconstruction_remote.py \
       tests/test_cloud_download_ordering.py \
       tests/test_cloud_filehandler.py \
       tests/test_cloud_file_uid_enumeration.py

74 passed, 2 skipped. Bridge checks (test_matlab_bridge_hashes.py, test_matlab_bridge_completeness.py) all green with the local NDI-matlab checkout.

Unrelated GUI drift acknowledged

GEFManager's matlab_last_sync_hash bumped from 06f31a4e5 to 59c96e57 (VH-Lab/NDI-matlab#984 -- raise the open GEF Manager for a session instead of building a second one). MATLAB App Designer window uniqueness only, nothing to port on this side; hash bumped with a decision_log note so the drift check no longer trips on any PR opened after that commit landed.

Not in scope

A Python analog of NDI-matlab's FileSeriesRoundTripTest.testMembersSurviveADownloadFromTheCloudWithSyncFilesFalse -- the live-cloud round-trip test. Belongs in its own change, layered after this one; flagged in the closing note on #300 and to be filed as its own issue.

References

🤖 Generated with Claude Code

https://claude.ai/code/session_01AdGZ8fRPeY9JSpUHgXm9ao


Generated by Claude Code

updateFileInfoForRemoteFiles now teaches series to survive a
SyncFiles=false cloud round trip. DID strips ingest_locations before
storing a document, so one that comes back declares n_present members
with no location for any of them -- exactly the shape DID's
MembersNotLocatable guard (DID-matlab#185) refuses on add_docs. The
whole SyncFiles=false path was blocked on that guard for any document
carrying a populated series.

The manifest bytes are not on disk on this path -- SyncFiles=false does
not download them -- so the reconstruction has to fetch. It goes
through the DID custom_file_handler contract (VH-Lab/DID-matlab#201,
VH-Lab/DID-python#88), the same contract DID uses on the read side, so
a caller with a handler already can pass it in and both sides use the
same one. With no handler, fetch_cloud_file is called directly,
matching what the read-side handler does when a manifest is asked for
by uid. Manifests fetched here do NOT land in the DID file cache; a
subsequent member open trips DID#201's lazy fetch, which is what
populates the cache. Cache eviction on a later session is answered by
the same re-fetch path.

A per-manifest fetch failure leaves the entry un-reconstructed so
DID's guard fires on add_docs with the document's own identity -- the
right signal a partial download deserves.

Ported from VH-Lab/NDI-matlab#988. Offline coverage in
tests/test_cloud_series_reconstruction_remote.py exercises the same
mock-handler pattern NDI-matlab uses in
TestUpdateFileInfoForRemoteFilesShape (VH-Lab/NDI-matlab#987): walk a
doc through DID's store/read cycle so ingest_locations gets stripped,
reshape via updateFileInfoForRemoteFiles with a mock, and pin the
rebuilt shape.

Two-argument custom_file_handlers still work through the same
arity-aware dispatch DID-python uses on its side
(_dispatch_custom_file_handler mirrors DID's private helper of the
same shape).

Also acknowledges an unrelated GUI-side drift: VH-Lab/NDI-matlab#984
raises the open GEF Manager for a session instead of building a
second one -- App Designer window uniqueness only, nothing to port
this side. Hash bumped on the porting_deferred GEFManager entry.

Refs: VH-Lab/NDI-matlab#986, VH-Lab/NDI-matlab#987,
      VH-Lab/NDI-matlab#988, VH-Lab/DID-matlab#185,
      VH-Lab/DID-matlab#201, VH-Lab/DID-python#88.
Closes #302.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdGZ8fRPeY9JSpUHgXm9ao
Fixes the lint job on PR #303. No behavioural change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AdGZ8fRPeY9JSpUHgXm9ao
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rebuild series ingest_locations on SyncFiles=false download (companion to VH-Lab/NDI-matlab#988)

2 participants