Stage BIDS-inherited metadata for every job; common_paths for explicit extras - #385
Conversation
Replaces the hard-coded `dataset_description.json` in sparse-checkout, `datalad get`, and `datalad run -i` with a configurable `common_paths` list on each input dataset entry. Defaults to `["dataset_description.json"]` to preserve existing behaviour. An empty list disables all common-path inclusion. Closes PennLINC#374 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add common_paths to the section overview list and optional sections list, add a stub required_files section, and add a full common_paths section with examples and usage notes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add common paths to the submit script test inputs. No need to add to the zipped ones, they are skipped.
Fixup tests
`common_paths` (PR PennLINC#376) replaces the hard-coded `dataset_description.json` but still drops root-level inherited BIDS sidecars (e.g. a top-level `task-*_bold.json` carrying RepetitionTime/PhaseEncodingDirection) from the per-job sparse-checkout, so fmriprep fails on valid data with `KeyError: 'RepetitionTime'` / "Missing readout timing information". Make BIDS inheritance automatic and positional: each job now grabs every metadata blob at the tiers ABOVE its checkout -- the dataset root always, plus the subject tier (sub-XX/) for session-level jobs, whose sub-XX/ses-YY checkout would otherwise miss files sitting directly under sub-XX/. In valid BIDS no data files sit at those tiers, so every blob there is inheritable metadata. resolve_tier() uses `git ls-tree HEAD` -- non-recursive (anchored, never crosses into other subjects) and reading the committed tree (independent of sparse/checkout state) -- and resolves to literal paths because datalad/sparse-checkout cannot take a glob. The resolved paths feed all three consumers: `datalad get -n`, `git sparse-checkout set --stdin`, and `datalad run -i`. `common_paths` is kept as the explicit-extras escape hatch for a non-inherited file the grab doesn't reach (e.g. `sourcedata/.../nidm.ttl`); its default flips from `['dataset_description.json']` to `[]` since inheritance now covers `dataset_description.json` automatically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render-string assertions (matching this module's existing render+shellcheck style): root tier resolved for every job, subject tier only for session-level jobs, zipped inputs skipped, and an explicit common_paths entry reaching `datalad get` / sparse-checkout / `datalad run -i`. Plus an object-level test that common_paths defaults to []. These check the generated script text; runtime resolve_tier behavior is left to the e2e walkthrough (a faithful staging assertion is awkward until the splice-point hooks land -- see PR description / follow-up). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The fixup makes BIDS metadata inheritance automatic, so dataset_description.json and other upper-tier sidecars are staged without configuration, and common_paths now defaults to []. Add a "BIDS metadata inheritance (automatic)" section and recast common_paths as the explicit-extras escape hatch (e.g. a non-inherited sourcedata/.../nidm.ttl), replacing the old dataset_description.json-default and "keep the default + add" examples. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
CI failure is just codecov failing to upload, which happens on my other PR too. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #385 +/- ##
==========================================
+ Coverage 79.19% 79.21% +0.01%
==========================================
Files 17 17
Lines 2168 2170 +2
Branches 385 385
==========================================
+ Hits 1717 1719 +2
Misses 308 308
Partials 143 143 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@asmacdo please address this fail (let's hope for small fix)
|
|
Fixed in 3d11215 — the failing check was a test that needed updating after the #369 merge; CI is green now. I suggest holding the merge until I've re-validated on one of the datasets that originally hit the |
|
Oops was in review window and did not see this comment until after merging @asmacdo! LMK how the test goes, can revert main if needed. |
|
Verified on real data: ran |

Closes #374. Builds on @djarecka's
common_pathscommits (included here) #376.Problem
A per-subject (or per-session) sparse-checkout drops dataset-level BIDS metadata that sits above the subject in the directory hierarchy — e.g. a top-level
task-rest_bold.jsoncarryingRepetitionTime/PhaseEncodingDirection, orparticipants.tsv. Under the BIDS Inheritance Principle those files apply to the data below them, so dropping them makes BIDS Apps fail on otherwise-valid data (KeyError: 'RepetitionTime', "Missing readout timing information").@djarecka's
common_paths(#374) replaced the hard-codeddataset_description.jsonwith a configurable list, but a user still has to enumerate every inherited sidecar — which they can't, since the names vary per dataset.What changed
Automatic BIDS inheritance. For every non-zipped input dataset, each job now stages every metadata blob at the tiers above its checkout:
sub-XX/) — additionally for session-level jobs, whosesub-XX/ses-YYcheckout would otherwise miss files sitting directly undersub-XX/(subject-level jobs already check out the wholesub-XX/subtree).Resolution is positional and happens at job runtime:
resolve_tier()usesgit ls-tree HEAD <tier>, which is non-recursive (anchored to the tier, never crosses into other subjects) and reads the committed tree (independent of sparse/checkout state). It resolves to literal paths becausedatalad get/git sparse-checkoutcan't take a glob. The resolved paths feed all three consumers:datalad get -n,git sparse-checkout set --stdin, anddatalad run -i.common_pathsrecast as explicit-extras. Kept from @djarecka's work for a non-inherited file the grab doesn't reach (e.g. a sharedsourcedata/NIDM/nidm.ttl). Its default flips to[], since inheritance now coversdataset_description.jsonautomatically.Why positional, not a glob list
Inheritance is about position in the hierarchy, not file extension. In a valid BIDS dataset no data files sit at the root or directly under
sub-XX/(imaging data is always inside a datatype dir undersub-XX/[ses-YY/]), so every blob at those tiers is metadata by construction. Grabbing "all upper-tier blobs" is therefore BIDS-correct and future-proof, and — crucially — needs no user configuration to get right.Testing
common_pathsthreaded into all three consumers, and the[]default.common_paths, so with the new[]defaultdataset_description.jsonreachesfmriprep --anat-onlyonly via the grab — making a green walkthrough an implicit regression test for the inheritance staging.Follow-up
A faithful runtime staging test (positive + negative assertions, incl. the session subject-tier) is awkward today: the e2e walkthrough only checks job outcomes and runs subject-level. Tracked as #384 — to land as a pre-app-hook test once the splice-point hooks (#381) merge.
🤖 Generated with Claude Code