bids_metadata_trace

    Lossless BIDS JSON metadata inheritance and field provenance for raw MRI datasets.

    bids
    neuroimaging
    metadata
    provenance
    Download zip
    Author
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    9 days ago
    Downloads
    19

    #BIDS Metadata Trace

    A reusable MoonBit library and offline CLI for resolving raw MRI JSON metadata inheritance, with a complete assignment history for every top-level field. It helps data-tool authors avoid treating a missing adjacent JSON as missing metadata and lets reviewers identify the source of a parameter.

    This is a bounded implementation of the BIDS 1.11.2 inheritance principle, not a full BIDS validator or imaging library. It does not inspect NIfTI voxels, convert DICOM, certify datasets, or fetch remote data.

    #Run the real-data example

    Requires MoonBit stable v0.10.17. The CLI runs under Moonrun on the Wasm target and needs no C compiler. The module pins moonbitlang/async 0.22.5 for CLI filesystem access; the library uses core only. Native core compilation/testing separately requires a system C toolchain; on Windows this stable version requires MSVC Build Tools with C++ tools and the Windows SDK.

    moon update moon run cmd/main --target wasm -- resolve examples/ds000001/manifest.json sub-01/func/sub-01_task-balloonanalogrisktask_run-01_bold.nii.gz

    The included manifest contains real paths and metadata from OpenNeuro ds000001 (CC0). No image downloads or private infrastructure are needed. The report contains RepetitionTime: 2.0, with task-balloonanalogrisktask_bold.json as its source. See source and license details.

    To use your own dataset, discover a supported directory as described below, or supply all potentially applicable JSON sidecars within the supported scope and the data paths you want to query. Explicit manifests are caller-supplied snapshots: omitted sidecars cannot be inferred by their query APIs.

    { "files": ["sub-01/func/sub-01_task-rest_bold.nii.gz"], "sidecars": [ {"path": "task-rest_bold.json", "metadata": {"RepetitionTime": 2.0}}, {"path": "sub-01/func/sub-01_task-rest_bold.json", "metadata": {"EchoTime": 0.04}} ] }

    This second manifest is an authored illustration, not an OpenNeuro fixture. Unknown manifest control fields are errors; unknown metadata fields and values are preserved.

    #Library workflow

    Import cn-cheems/bids_metadata_trace as @trace in your moon.pkg. This module is not yet published to Mooncakes; use a local/path dependency or this repository when evaluating it.

    For a reproducible local package check, see archive and independent consumer verification. The check extracts the actual release ZIP and runs a separate module against its public API; it never publishes to Mooncakes.

    • DatasetIndex::from_manifest(files, sidecars) consumes relative data paths and SidecarInput::new(path, json_text) sources.
    • DatasetIndex::from_json(text) reads the strict manifest above.
    • index.resolve(path) returns effective metadata or a TraceError(Diagnostic).
    • resolved.get(key) returns a defensive JSON copy. resolved.trace(key) gives assignments in root-to-leaf order; the last assignment wins.
    • resolved.to_json() exports effective metadata; to_report_json() includes applied sidecars and field history.
    • index.to_manifest_json() exports the complete input snapshot for reproducible reloading.
    • index.audit(paths) retains every query occurrence in request order, including duplicates. audit_all() uses sorted indexed paths. report.entries() returns success or diagnostic per query; error_count() and to_json() support release checks.
    • index.select_scans(ScanQuery) chooses a cohort by supported filename entities. selection.paths() returns sorted matches; selection.audit() resolves them against the complete original snapshot, preserving ancestor source histories.
    • index.source_coverage() accounts for every sidecar within indexed scans: applicability, winning/fully-shadowed scan paths, per-scan winning/shadowed fields and unresolved evidence. Empty sources are retained; unmatched_paths() means no indexed target, not permission to delete a source.
    • before.compare(after) reports the sorted union of scan paths, with ImpactKind, typed FieldChangeKind, full before/after evidence, per-field histories and source-chain changes. changed_count() excludes indeterminate scans; always also inspect error_count().
    • impact.release_check(ReleasePolicy) produces a reusable release decision, sorted affected/unresolved paths and the complete impact evidence. AllChanges includes provenance; EffectiveValues checks exact values and scan inventory. Indeterminate always takes priority over detected changes.
    • index.plan_edit(sidecar_path, MetadataPatch::from_json(text)) proposes an edit to one existing source, retaining complete before/after snapshots and impact evidence. plan.updated_sidecar_json() includes untouched metadata; plan.after() is a reusable candidate index.
    • EditBatch::from_patches([(path, patch), ...]) builds coordinated edits for MoonBit callers; from_json(text) reads the batch format below. index.plan_edits(batch) validates every edit against the same baseline and returns one BatchEditPlan, complete source replacements and final scan impacts.
    • ReviewBundle::create(before, after, policy) captures complete snapshots and the complete release check. ReviewBundle::from_json(text) recomputes and verifies all expected evidence; bundle.report() exposes the verified decision.
    • base.merge(left, right) merges concurrent snapshot edits and checks inherited scan intent. Conflicts block the entire candidate, including cross-source edits that hide another branch's correction. report.candidate(), conflicts() and impact() support a reusable review workflow; see contract and offline CLI example.

    See the compiler-generated public interface and executable library examples.

    #Exact supported boundary

    • Single raw MRI dataset; bold in func, T1w in anat, dwi in dwi; .nii and .nii.gz paths plus .json sidecars.
    • Root, participant, optional session, and corresponding datatype directories. Filename participant/session entities must agree with directory scope.
    • Ordered entities: sub, ses, task, acq, ce, rec, dir, run, echo, part, chunk. Decimal index entities compare without leading zeroes; labels compare exactly and case-colliding labels are rejected.
    • UTF-8 JSON via the CLI; well-formed Unicode strings; at most 64 nested metadata containers and 2,097,152 UTF-16 code units per parsed source. The complete canonical snapshot is also bounded to 2,097,152 code units at construction, guaranteeing it can be reloaded. Manifest envelopes allow three additional containers. The CLI also rejects manifests larger than 8 MiB on disk.
    • Core library: Wasm, Wasm-GC, JavaScript and Native. CLI: Wasm under Moonrun only; CI runs on Linux, macOS and Windows. The filesystem CLI is not a generic browser/WASI embedding. These are tested targets, not claims about every host or embedding runtime.

    Other suffixes/entities, derivative/auxiliary trees, non-JSON inheritance, URLs, absolute paths, parent traversal and backslash dataset paths are rejected. This profile does not validate every MRI schema constraint (including modality-specific entity combinations), required acquisition fields, image headers, or dataset-wide BIDS compliance. An empty result means no applicable sidecar was supplied; it is not a validity certificate.

    #Correctness and diagnostics

    Applicable sidecars must be on the data file's ancestor chain, have the same suffix, and contain only matching filename entities. JSON assignments are applied from root downwards. Objects and arrays are whole field values, not recursively merged; absent keys leave inherited values intact, and null is retained rather than treated as deletion.

    Multiple applicable sidecars at the same directory level fail, even if one has more entities or both contain identical metadata. Duplicate JSON keys, including escaped aliases and nested objects, fail before resolution. Diagnostics provide a stable code, source path, message, optional UTF-16 offset and related paths.

    Every numeric token is explicitly retained, including -0, large integers, decimal precision and overflowing exponents. Do not use the approximate Double stored inside core Json::Number or Json::equal to compare metadata. Snapshot audit/diff/merge and exact predicates retain token-sensitive comparison; optional numeric project policy separately provides exact mathematical order without floating-point conversion. Report/manifest serializers preserve number tokens, sort object keys in ordinal UTF-16 lexicographic order, and retain array order. They normalize whitespace and string escaping, so they do not reproduce source bytes verbatim.

    #Verification

    Performance experiments are separate from correctness gates: see workloads, release timing and RSS methodology. CI smoke-checks execution and invalid inputs, without enforcing unstable timing thresholds.

    Path/source lookup now uses internal indexes while preserving the public API and deterministic reports. The documented paired Native measurements include full raw data, output-equivalence checks and construction/memory regressions; gains depend on source layout and are not cross-platform guarantees.

    For the complete reproducible gate and three user workflows, see acceptance. moon run scripts/acceptance.mbtx checks formatting/interfaces, strict compilation, explicit builds, actual tests, CLI behavior and archive-based consumers; success records the revision and actual target coverage in _build/acceptance.json. Publication remains deferred.

    moon fmt moon fmt scripts/verify.mbtx moon info --target all moon check --target all --deny-warn moon test --target all --deny-warn

    Tests cover inheritance, source histories, ambiguity, scope errors, numeric preservation, unknown nested values, manifest round trips, input permutations, defensive copies, malformed JSON, depth boundaries and recovery after errors. The real-data command above must also run successfully. CI checks formatting and generated-interface drift, executes all four core targets, and runs the Wasm CLI on three operating systems. moon run scripts/verify.mbtx checks the real report as well as test exit status. On a host with a portable compiler, set BIDS_TRACE_CC to its absolute executable path for that script.

    #Roadmap

    Multi-subject/session validation: ds006 fixture and reproduction steps exercise actual upstream paths and task metadata across two subjects/two sessions, with a clearly synthetic session override, scoped release impact and offline bundle replay. The suite also runs a deterministic 1,000-scan synthetic cohort regression; this is a correctness check, not a performance claim.

    Implemented: inheritance/provenance, recoverable batch audit, snapshot impact comparison, policy-based release checks, directory discovery with an explicit inventory report, single-source/coordinated multi-source edits, scan/source/combined inventory planning, proven redundant override pruning, replayable review bundles, source coverage, exact-value summaries and grouped consistency, CSV review export, metadata/acquisition expectation checks and filename/metadata selection. Local package adoption and a reproducible acceptance gate are implemented. Mooncakes publication and registry-installed consumer validation await owner instruction. Additional raw-data profiles remain future work backed by specification and fixtures; full BIDS validation is not implemented.

    #Batch review example

    moon run cmd/main --target wasm -- audit examples/ds000001/manifest.json moon run cmd/main --target wasm -- audit examples/ds000001/manifest.json absent sub-01/func/sub-01_task-balloonanalogrisktask_run-01_bold.nii.gz

    The first command reports all three real scans. The second deliberately requests an unknown path, then a real scan: stdout contains both outcomes and error_count: 1; process exit is 1. A query error does not stop the batch. An invalid manifest or IO failure rejects the entire batch and writes a diagnostic to stderr. Usage errors print usage to stderr and also exit 1. An empty query list in the library yields an empty report; it never implicitly audits everything.

    #Snapshot impact example

    moon run cmd/main --target wasm -- diff examples/ds000001/manifest.json examples/ds000001/changed-manifest.json

    The second manifest is a synthetic edit, changing the real fixture's root RepetitionTime from 2.0 to 3.0. It is not another real acquisition. All three real scan paths show value_changed; TaskName stays unchanged. The command exits 0 when all comparisons are determinate, even when changes exist. Exit 1 means input/IO failure or an indeterminate scan; a completed report retains all scan outcomes on stdout.

    Scan kinds are unchanged, changed, added, removed, and unresolved. A resolution error on either side takes priority over addition/removal and suppresses speculative field differences. Reports include both sides' diagnostics. Field kinds are added, removed, value_changed, and provenance_changed; the last means the effective value is identical but its assignment history differs. Empty sidecar edits can change sources_changed without field differences.

    Comparison sorts object keys, preserves array order, and compares numeric tokens exactly: 2, 2.0 and 2e0 are distinguishable. It is a lossless metadata edit audit, not numeric or scientific equivalence analysis. JSON report presence flags distinguish a missing field from present null; the library's before_json()/after_json() distinguish None from Some("null"). Unchanged entries remain in the report. Omitted sidecars, image contents and edits to nonapplicable sources are outside scan-impact inference.

    #Release checks in CI

    diff is an inspection command that succeeds for determinate changes. check turns the comparison into a policy decision and a failing process status, suitable for a dataset release job:

    moon run cmd/main --target wasm -- check baseline.json candidate.json --effective-only

    The default policy is AllChanges; omit --effective-only to also block provenance edits. Unsupported options fail explicitly. Both policies retain the entire impact report in the JSON impact field, including changes ignored by the selected policy.

    EventDefault / AllChanges--effective-only / EffectiveValues
    Effective field added, removed or value token changedFailFail
    Scan added or removed, even without metadataFailFail
    Assignment history or applied source chain changed onlyFailPass
    Resolution error in either snapshotIndeterminate / failIndeterminate / fail
    No relevant changes or errorsPassPass

    Executable examples, requiring no private data:

    moon run cmd/main --target wasm -- check examples/ds000001/manifest.json examples/ds000001/changed-manifest.json moon run cmd/main --target wasm -- check examples/ds000001/manifest.json examples/ds000001/provenance-manifest.json --effective-only moon run cmd/main --target wasm -- check examples/release-check/ambiguous-manifest.json examples/release-check/ambiguous-manifest.json

    The first exits 1 with decision: "changes_detected" and three affected paths. The second exits 0 with decision: "pass", while retaining run 01's synthetic provenance edit. Without --effective-only that edit fails. The third exits 1 with decision: "indeterminate", unresolved paths and diagnostics from both sides; it cannot pass by comparing the same invalid snapshot to itself.

    The CLI uses 0 for success/pass, 1 for failure. Current Moonrun normalizes nonzero WASI exit codes to 1, so scripts must use JSON decision to distinguish changes from indeterminate comparisons. Completed checks write reports to stdout; malformed input or IO failure writes a diagnostic to stderr and produces no completed report. Usage errors write usage to stderr. A Pass applies only to the selected policy and supplied snapshots; omitted sources remain the caller's responsibility and it does not certify BIDS compliance. Value checks retain the exact numeric-token semantics described above.

    #Discover a directory

    moon run cmd/main --target wasm -- discover examples/ds000001/tree moon run cmd/main --target wasm -- discover /path/to/dataset --manifest-only > snapshot.json moon run cmd/main --target wasm -- audit snapshot.json

    Default output includes a reusable manifest, auxiliary_paths and excluded_paths. --manifest-only explicitly selects just the query input; inspect the default report when reviewing completeness. DiscoveryReport::from_inventory(paths, sidecar_texts) exposes the same validated workflow for archive or virtual-filesystem adapters. classify_discovery_entry(path, directory=...) tells adapters which files need source text. Every expected sidecar must have exactly one text input; missing/extra sources and collisions fail.

    Discovery reads regular JSON sidecars and records .nii/.nii.gz paths without opening image bytes. It walks regular directories, includes hidden entries in its inventory and refuses symlinks/junctions and special files. It prunes and reports hidden paths and the top-level derivatives, sourcedata, code, stimuli trees. It records auxiliary TSV/TSV.GZ/BVAL/BVEC files, events/scans/sessions/participants JSON, and the documented root README/LICENSE/CHANGES and dataset_description/participants resources. Their contents do not contribute MRI metadata and are not exported in the manifest. Other files, unsupported MRI suffixes/entities, malformed JSON and read failures reject the entire discovery. .bidsignore is reported as a hidden exclusion and is not interpreted.

    Limits: 20,000 visited entries, 32 directory levels, the existing snapshot/JSON limits, and 8 MiB on disk per sidecar. Run against a quiescent directory: discovery is not an atomic filesystem transaction. complete_supported_inventory covers the selected raw-MRI profile and recorded exclusions, not the entire BIDS ecosystem. The included tree uses real CC0 metadata and path names; image entries are explicit text placeholders for a metadata-only demonstration. It does not claim image validity.

    #Preview a sidecar edit

    moon run cmd/main --target wasm -- plan examples/ds000001/manifest.json task-balloonanalogrisktask_bold.json examples/edit-plan/change-repetition-time.json moon run cmd/main --target wasm -- plan examples/ds000001/manifest.json task-balloonanalogrisktask_bold.json examples/edit-plan/change-repetition-time.json --manifest-only > candidate.json moon run cmd/main --target wasm -- check examples/ds000001/manifest.json candidate.json

    The authored patch is {"set":{"RepetitionTime":3.0},"remove":[]}. It proposes a synthetic change to the real fixture's root source, preserves TaskName, and reports all three affected scans. The default plan output contains the patch, full old/new sidecar metadata, both complete manifests and the full impact report. --manifest-only exports the candidate snapshot for subsequent audit/check workflows. Planning never writes source files or approves an edit.

    Both patch controls are required. set replaces whole top-level field values, including explicit null; remove deletes only explicitly named fields from the selected existing sidecar. A removed local override can expose an inherited parent value. Other fields, nested unknown values and numeric tokens are preserved. Unknown controls, repeated removal names, set/remove conflicts, absent removal fields, unknown sidecar paths and depth/size violations fail before producing a plan; the baseline remains usable. Creating/removing sidecars and recursive JSON patch operations are outside this capability.

    Exit 0 means the plan has determinate scan impacts, including expected changes. An indeterminate impact produces the completed plan on stdout and exit 1; input/IO errors produce only a diagnostic on stderr. Inspect impact diagnostics before using a candidate. The exported manifest has the existing reloadable snapshot bound; complete plan reports repeat provenance evidence and can be larger.

    #Preview coordinated sidecar edits

    A curator changing a root parameter may also need to remove a stale run-specific override. Batch planning accepts all operations together and produces one final candidate:

    moon run cmd/main --target wasm -- plan-batch examples/ds000001/provenance-manifest.json examples/edit-plan/coordinated-edits.json moon run cmd/main --target wasm -- plan-batch examples/ds000001/provenance-manifest.json examples/edit-plan/coordinated-edits.json --manifest-only > candidate.json moon run cmd/main --target wasm -- bundle examples/ds000001/provenance-manifest.json candidate.json > review.json moon run cmd/main --target wasm -- replay review.json

    The baseline combines real CC0 paths/metadata with the previously documented synthetic run-01 override. The authored batch sets root RepetitionTime to 3.0 and removes that field from run 01, leaving its empty sidecar in place. All three scans inherit the new root value; TaskName remains intact. This is a counterfactual review, not corrected acquisition data.

    {"edits":[ {"path":"task-rest_bold.json","set":{"RepetitionTime":3.0},"remove":[]}, {"path":"sub-01/func/sub-01_task-rest_run-01_bold.json","set":{},"remove":["RepetitionTime"]} ]}

    Every entry requires path, set and remove; only edits is allowed at the envelope. The whole-field semantics are identical to single-source planning. Duplicate sidecar paths, duplicate decoded keys, unknown controls, invalid operations, unknown sources and absent removal fields are errors. No operation is silently skipped. All removals refer to the baseline, repeated edits to one source are rejected, and input order cannot change a valid plan. Empty batches preserve the baseline and any existing resolution errors.

    The library/CLI never write source files. “All or nothing” means immutable snapshot planning, not a disk transaction. The final combined snapshot is rebuilt and bounded once, allowing a coordinated shrink/grow that would exceed the limit in an intermediate single edit. A failure returns no partial plan; an indeterminate final impact retains the complete plan with CLI exit 1. A determinate plan exits 0 even when changes exist. Default output contains the normalized batch, every edited source's old/new values, both full snapshots and one impact report. --manifest-only exports the final candidate for existing resolve/check/bundle workflows. updated_sidecar_json(path) returns None for sources not explicitly edited.

    Limits: 256 distinct existing sidecars, 2,097,152 UTF-16 units per parsed/canonical batch, 67 batch containers accommodating metadata64, and the existing per-sidecar/final-snapshot limits. CLI batch files also have an 8 MiB disk bound. This patch API does not create/delete sidecars; use the separate source inventory planner below. Nested patch operations, sequential same-source operations and disk application remain unsupported. Complete reports may exceed snapshot size because they retain repeated evidence.

    #Share and replay an offline review

    moon run cmd/main --target wasm -- bundle examples/ds000001/manifest.json examples/ds000001/changed-manifest.json > review.json moon run cmd/main --target wasm -- replay review.json

    The reviewer needs only the bundle and this tool. The version1 format has exactly six controls: format: "bids-metadata-trace/review-bundle", integer token version: 1, complete before/after manifests, policy (all_changes or effective_values) and the complete expected release report. Use bundle ... --effective-only to select the effective-value policy; the default retains all-change policy semantics. Library accessors return the immutable snapshots and recomputed report for further queries.

    Import rejects unknown controls, unsupported versions, duplicate decoded keys at any depth and mismatched expected evidence. It reloads both snapshots under the original profile, recomputes the entire report and compares canonical JSON exactly, including number tokens and policy-ignored provenance. Deleted evidence, stale inputs or changed policy with a stale report fail with a diagnostic. Coordinated inputs with a matching new report are valid: this is content replay, without signatures or proof of authorship, source authenticity or filesystem completeness.

    bundle and replay exit 0 for successful capture or verified reproduction, even when the retained release decision is changes_detected or indeterminate. Use check for a failing release gate. Invalid input, inconsistent evidence and IO failures exit 1 with a diagnostic on stderr and no completed stdout report. Bundle input/canonical output is limited to 16,777,216 UTF-16 code units and input to 80 containers; each embedded snapshot keeps its 2,097,152-unit and metadata 64-container limits. The CLI also rejects bundle files larger than 64 MiB on disk. These are input/serialization limits, not a strict memory bound for comparison, which retains complete per-scan evidence.

    #Review source coverage

    moon run cmd/main --target wasm -- sources examples/ds000001/provenance-manifest.json

    The real-path fixture's synthetic run-01 source wins RepetitionTime for that scan. The root source still wins TaskName there and both fields for runs 02/03. Coverage reports each field separately, so a partially shadowed source is not mislabeled as wholly unused. Every supplied source appears, including empty sources and sources without applicable indexed scans. Source and scan ordering is deterministic. Resolution failures retain diagnostics and applicable paths, with no guessed winner or shadowed assignment; a completed report exits 1 if any scan is unresolved, otherwise 0.

    This is a projection over the supplied snapshot's indexed scans. Unmatched sources may serve unindexed data; coverage neither inspects other files nor recommends automatic deletion. Empty applicable sources affect the applied chain even though they assign no fields. Correcting input and rerunning starts a fresh report. The library and command use the existing supported raw-MRI boundary.

    #Summarize a scan cohort

    moon run cmd/main --target wasm -- summarize examples/ds000001/provenance-manifest.json RepetitionTime TaskName MissingField

    AuditReport::summarize_fields(fields) groups exact canonical whole JSON values, retains every request occurrence with its query index, winning source and complete assignment trace, and lists resolved-but-absent fields separately from failed queries. Null is a value; 2 and 2.0 are separate groups. Objects/arrays and unknown metadata are supported without numerical conversion. Field order follows the explicit request; groups are ordered by canonical token, not numeric magnitude. At most 256 distinct fields may be requested; duplicate/oversized requests fail. An empty library projection is valid. The CLI requires at least one field and exits 1 when the completed report contains resolution errors, otherwise 0. This reviews consistency; it does not validate BIDS field units or establish scientific equivalence. Existing resolution and input limits apply. JSON evidence can be consumed through the report's defensive to_json_value() accessor.

    #Export a review table

    moon run cmd/main --target wasm -- table examples/ds000001/provenance-manifest.json RepetitionTime TaskName MissingField > review.csv

    AuditReport::to_review_csv(fields) exports every query occurrence in request order, including errors. Four fixed columns (query_index, data_path, status, diagnostic) precede four columns per explicit field: present, value, winning_source, trace; their headers include the JSON-encoded field name. All cells are quoted, quotes doubled, records end with CRLF, and CLI output is UTF-8 without a BOM. This is a review CSV, not a BIDS TSV or a complete snapshot replacement; only requested fields are projected.

    Dynamic path/value/source/trace/diagnostic cells start with json: followed by exact canonical JSON. After CSV decoding, remove exactly that prefix and parse the remainder as JSON (use a token-preserving parser for exact numbers). This preserves 2.0, -0, large integers, unknown nested data, commas, quotes and escaped newlines; formula-like strings remain JSON strings behind a text prefix. It does not claim all spreadsheet applications preserve text automatically. Presence true with json:null means explicit null; false with empty value/source/trace means absent from a resolved scan. An error row has its complete diagnostic and empty presence/value/source/trace cells, meaning unknown rather than absent. Full assignment traces retain shadowed values.

    The CLI requires one or more distinct fields, audits all indexed scans, and exits 1 if any exported row is unresolved; library callers can select scans through audit(paths). Empty library field selection exports the four base columns. Projection limits match cohort summaries (256 unique fields); complete CSV output is limited to 16,777,216 UTF-16 units including quoting and CRLF. Exceeding the limit fails before any completed CSV reaches stdout; reduce fields or split queries. No CSV import, disk edits or automatic spreadsheet formatting are implemented.

    #Plan source inventory changes

    moon run cmd/main --target wasm -- plan-sources examples/ds000001/manifest.json examples/edit-plan/rename-source.json moon run cmd/main --target wasm -- plan-sources examples/ds000001/manifest.json examples/edit-plan/rename-source.json --manifest-only > candidate.json moon run cmd/main --target wasm -- check examples/ds000001/manifest.json candidate.json --effective-only

    The authored counterfactual renames the real fixture's root source to bold.json with the same CC0 metadata. All three scans retain their exact values, while their source histories change. No disk source is renamed: the command only exports an immutable candidate and evidence. The final effective-value check passes; the default all-change gate detects the provenance change.

    SourceChanges::new(additions, removals) or from_json(text) accepts {"add":[{"path":"bold.json","metadata":{}}],"remove":["task-rest_bold.json"]}. Both controls are required. Unknown controls, duplicate decoded keys, malformed metadata, unsupported paths and duplicate/case-colliding operations fail. An addition must name a source absent from the baseline; removal must name an existing source. Adding and removing the same path is rejected: use metadata edits for replacement. index.plan_sources(changes) validates/rebuilds the final inventory once, with unchanged scan paths, then returns before(), after(), impact() and a complete JSON report. Removed empty/unmatched source data remain in the before snapshot even when scan impact is zero.

    At most 256 combined operations and 2,097,152 UTF-16 units of canonical change input; JSON envelope depth67 accommodates metadata64. Existing source/final snapshot limits apply. The CLI rejects change files above 8 MiB on disk. Atomic shrink/grow can succeed without constructing an oversized intermediate snapshot. Empty changes are an identity. Failures return no partial plan; resolution ambiguity retains both snapshots and diagnostics, with CLI exit1. Determinate changes exit0. Source disk application and scan creation/deletion remain unsupported; source creation/removal planning is now implemented separately from existing-source patch planning.

    #Check acquisition expectations

    moon run cmd/main --target wasm -- expect examples/ds000001/manifest.json examples/expectations/acquisition.json moon run cmd/main --target wasm -- expect examples/ds000001/changed-manifest.json examples/expectations/acquisition.json

    The authored project rule requires RepetitionTime and TaskName, and permits the exact values from the existing real CC0 fixture. All three real scans pass; the synthetic repetition-time edit fails with three unexpected_value checks. This gate checks acquisition expectations directly, including errors already present in a baseline; a before/after comparison alone can only describe changes. The command accepts optional scan paths after the expectation file; otherwise it audits every indexed scan. Duplicate queries keep their request positions. Pass exits0; Violations and Indeterminate exit1 with the complete report on stdout. Bad input/IO errors emit a diagnostic on stderr without a completed report.

    The base MetadataExpectations::from_json(text) format is {"required":["RepetitionTime"],"allowed":{"RepetitionTime":[2.0]}}, with optional numeric policy. Both base controls are mandatory. Required fields must be present after inheritance; explicit null counts as present. An allowed-only field may be absent; when present, its complete JSON value must match one of the exact canonical choices. Unknown metadata, arrays and objects are supported without coercion or nested merging. 2, 2.0, 2e0 and negative zero remain distinct for allowed choices. Whitespace, object key order and equivalent string escapes do not distinguish choices. Unknown controls, duplicate required fields/decoded keys/canonical choices, empty or non-array choice sets fail rather than being ignored.

    audit.check_expectations(expected) returns a report with decision(), failed field violation_count(), unresolved query error_count(), deterministic to_json() and defensive to_json_value(). Every resolved query retains its complete metadata, applied sources and field assignment history, including unselected fields. Checks include presence, actual value, allowed choices, winner and full trace. Failed queries retain diagnostics without speculative checks. Errors take priority over violations. An empty audit is Indeterminate with empty_audit:true and zero query errors; select at least one scan to obtain a decision. Empty rules impose no field restrictions on a nonempty resolved audit, while still retaining errors.

    Limits: 256 distinct fields across all controls, 1–256 choices per allowed field, 2,097,152 UTF-16 units per input/canonical expectation document and 67 JSON containers; CLI file size at most 8 MiB. Optional numeric rules add exact scalar intervals, array constraints and scalar field relations through NumericRules::from_json or with_numeric; see format, semantics and three workflows. Original required/allowed and exact-value semantics are preserved. This is project policy, not BIDS schema compliance, image/header validation, numeric tolerance or unit conversion. Complete repeated evidence can expand beyond input size.

    #Select and audit a cohort

    moon run cmd/main --target wasm -- select examples/ds000001/manifest.json examples/selection/later-runs.json moon run cmd/main --target wasm -- select examples/ds000001/manifest.json examples/selection/later-runs.json --audit

    The authored query selects real ds000001 runs 02 and 03 for participant01 and the balloon-analog-risk task. Run01 remains explicitly listed as excluded. --audit embeds both selection evidence and the complete selected audit, including the inherited root RepetitionTime:2.0. Source inputs are never filtered by the scan query: parent, local and unselected-source data remain in the original index. This avoids accidentally dropping inheritance when selecting a subset of scans.

    ScanQuery::from_json(text) accepts exactly {"suffixes":["bold"],"entities":{"sub":["01"],"run":["2","0003"]}}; both controls are mandatory. ScanQuery::new(suffixes, entity_choices) copies and normalizes typed inputs. Choose one or more of bold, T1w, dwi. Entity keys use filename names (sub, ses, task, acq, ce, rec, dir, run, echo, part, chunk). Different keys are ANDed, each key's values are ORed, and suffix choices are ORed. A missing filename entity cannot match a constrained key. Decimal indices normalize leading zeroes without machine-integer conversion; all label comparisons are exact, including case and leading zeroes. Unknown keys/controls, empty choice lists, malformed values, duplicate normalized choices and case-colliding labels are errors. Empty entities:{} means no entity constraints; empty suffix selection is invalid.

    index.select_scans(query) returns ScanSelection with defensive sorted paths(), deterministic to_json() inventory accounting, and audit(). The report includes normalized query, selected/excluded paths, total and selected counts. It is not an exported manifest or a disk operation. Selection uses filename entities only; chosen scans with resolution errors remain in the audit. The original index is unchanged. selection.audit() also composes with existing summarize_fields, to_review_csv and check_expectations; see the executable library example in USAGE.mbt.md.

    Default CLI selection exits0 after a valid query, including zero matches. --audit exits1 if any chosen scan is unresolved, while retaining the complete report; an empty audit exits0 because auditing alone does not grant approval. It never substitutes audit_all() for zero matches. Project expectation checks on an empty selected audit remain Indeterminate; inspect selected_count or use that gate before approving a cohort. Input/IO errors exit1 with a diagnostic on stderr and no partial stdout.

    Limits: at most 256 total suffix/entity choices, 2,097,152 UTF-16 units per parsed/canonical query, three JSON containers, and 8 MiB per CLI query file. Original index limits still apply. Metadata predicates, regular expressions, wildcards, ranges, extension filtering, negative filters and derivative profiles are unsupported. This capability selects indexed supported raw-MRI paths; it does not imply BIDS compliance or complete filesystem capture.

    #Filter by inherited metadata

    moon run cmd/main --target wasm -- filter examples/ds000001/manifest.json examples/selection/metadata-query.json

    The authored query matches all three real CC0 scans by inherited RepetitionTime:2.0, presence of TaskName and absence of an unrecorded field. Against changed-manifest.json it excludes all three, retaining their complete metadata/history as evidence. No image bytes or private infrastructure are needed.

    MetadataQuery::from_json(text) requires base controls {"present":[],"absent":[],"equals":{"RepetitionTime":[2.0]}} and accepts optional numeric policy. Constraints are ANDed; each equality array supplies OR choices. Equality requires presence, unlike an optional allowed-only expectation. Null is present, absence is distinct, and 2, 2.0, 2e0, 0 and -0 remain distinct for exact choices. Whole arrays/objects and unknown fields are supported. Contradictory absent/present-or-equal-or-numeric constraints, duplicate fields/choices/decoded keys, empty equality choices and unknown controls fail. Present plus equality/numeric for the same field is permitted. Empty predicates match all resolved entries.

    Use index.select_metadata(query) for all indexed scans or audit.filter_metadata(query) after filename selection/custom audit. selection.paths() contains only proven matches, preserving request order and duplicates. selection.audit() contains matches plus unresolved entries in that order: parse/ambiguity/unknown-path errors cannot silently disappear from downstream gates. The report classifies every input occurrence as matched, excluded or error, retaining full metadata/source history or diagnostics and original query indices. Counts sum to total input occurrences. An empty result never falls back to all scans or grants release approval. Accessors return defensive copies; the original index and unselected sources remain intact.

    The CLI accepts optional explicit paths, exits1 for unresolved entries and otherwise0, including zero matches. Input/IO errors produce no partial stdout. Limits: 256 distinct fields across controls, 1–256 exact choices per equality field, 2,097,152 UTF-16 units per parsed/canonical query, 67 containers and 8 MiB per CLI control file. Numeric policy adds exact ranges, array checks and scalar relations. Regex, tolerance, nested paths and unit conversion remain unsupported. This is a project-specific cohort review layer, not a full BIDS query engine.

    #Plan scan inventory changes

    moon run cmd/main --target wasm -- plan-scans examples/ds000001/manifest.json examples/edit-plan/scan-inventory.json moon run cmd/main --target wasm -- plan-scans examples/ds000001/manifest.json examples/edit-plan/scan-inventory.json --manifest-only > candidate.json moon run cmd/main --target wasm -- check examples/ds000001/manifest.json candidate.json --effective-only

    The authored example proposes excluding the real fixture's run01 from the indexed inventory, retaining runs02/03 and every original source. The plan reports removed-scan metadata, before/after source coverage and a release impact. The final check exits1 because a scan was removed. This is an explicit counterfactual inventory review, not an observed dataset revision or a recommendation to discard scans.

    ScanChanges::new(additions, removals) or from_json(text) accepts exactly {"add":["sub-01/func/sub-01_task-rest_run-02_bold.nii"],"remove":[]}. Both controls are required. Paths use the existing raw-MRI profile; duplicate/case-colliding paths, conflicting add/remove, unknown controls, malformed paths, existing additions and absent removals fail. The final inventory is checked against all retained sources, including entity label collisions and the complete snapshot size limit. An atomic shrink/grow is evaluated only at the final snapshot, not an oversized intermediate state.

    index.plan_scans(changes) exposes immutable before(), after(), impact(), before_coverage(), after_coverage() and a complete to_json() report. Added scans inherit existing applicable sources; removed scans retain their complete before evidence. No source is automatically removed, even if it becomes unmatched. Resolution errors in newly added or removed scans remain indeterminate in impact evidence; the CLI exits1 with its complete report. --manifest-only exports the candidate snapshot, including unresolved sources, and also exits1 when impact is indeterminate. Export success alone does not approve a release. Empty changes are an identity; an empty candidate is a valid inventory, not proof of data completeness.

    At most 256 combined operations, two JSON containers, 2,097,152 UTF-16 units per parsed/canonical changes document, and 8 MiB per CLI control file. The existing complete snapshot limit applies. No scan/image bytes are created, deleted or renamed; no disk edits, source edits, image validation or automatic rediscovery are performed. Use source/edit planning separately. The report and review-bundle workflow retain enough data to independently replay before/after checks.

    #Check scoped acquisition rules

    moon run cmd/main --target wasm -- acquisition examples/ds000001/manifest.json examples/expectations/scoped-acquisition.json moon run cmd/main --target wasm -- acquisition examples/ds000001/changed-manifest.json examples/expectations/scoped-acquisition.json

    The authored rule set checks all three real CC0 BOLD scans and additionally checks runs02/03. The unchanged snapshot passes; the synthetic RepetitionTime:3.0 snapshot fails with three field violations. Overlapping rules apply together, so the extra later-run rule never overrides the common acquisition requirement. These are project expectations, not BIDS or scientific validity certificates.

    AcquisitionRules::from_json(text) requires exactly {"rules":[{"id":"func","select":{"suffixes":["bold"],"entities":{}},"expect":{"required":["RepetitionTime"],"allowed":{"RepetitionTime":[2.0]}}}]}. Rule scopes use the existing filename ScanQuery, not metadata success or metadata filtering. Each matching rule's MetadataExpectations must pass. Define between 1 and 64 rules with unique ids (1–64 ASCII letters/digits/underscore/hyphen, no case collisions); rules serialize in ordinal id order. All nested controls retain their strict validation, exact values and limits. Unknown controls, duplicate keys, unsupported selectors, malformed rules and duplicate ids fail without dropping data.

    index.check_acquisition(rules) checks the entire indexed inventory once, applies every matching rule, and returns AcquisitionReport. Every indexed scan must have a rule, and every declared rule must match at least one scan. Empty inventories, uncovered scans, unmatched rules or resolution errors make the overall decision Indeterminate, taking priority over field violations. Otherwise any failed check yields Violations; only complete coverage with no errors/violations yields Pass. error_count() counts unresolved scans once even when multiple rules match; violation_count() counts failed rule/field pairs. uncovered_paths() and unmatched_rule_ids() identify coverage mistakes. Reports retain per-scan rule coverage, every rule result, the full original audit and manifest, and the normalized rule set. Existing single-expectation APIs and serialized audit output retain their behavior.

    The CLI exits0 only for Pass and exits1 for Violations/Indeterminate with the complete report; malformed input or IO failure emits a diagnostic on stderr and no partial stdout. Limits: 2,097,152 UTF-16 units per parsed/canonical rule set, 70 containers including the rule envelopes, existing per-query/per-expectation limits, and 8 MiB per CLI rules file. Reports repeat evidence and can exceed input size. No rule priorities, exemptions, automatic corrections, numeric tolerance, nested field predicates or full BIDS schema validation are implemented. Scans outside the supplied inventory remain outside the check.

    #Sources and license

    Project implementation and authored tests: Apache-2.0, copyright 2026 cn-cheems. External data retain their own license. PROVENANCE.md records specification, dependency, fixture and ecosystem sources. This project does not claim ecosystem uniqueness, clinical suitability or a performance advantage.

    #Atomic dataset transactions

    DatasetChanges::new(scans, sources, edits) combines inventory and metadata changes against one baseline. DatasetIndex::plan_changes validates only the final combined snapshot, so coordinated scan/source renames can succeed without invalid intermediate states. Sources cannot be edited and added/removed in the same transaction. The combined limit is 256 operations and 2,097,152 UTF-16 units. No files are written; unresolved inheritance remains explicit impact evidence.

    Run moon run cmd/main -- plan-changes examples/ds000001/manifest.json examples/ds000001/combined-changes.json to review a synthetic acquisition edit on the public CC0 fixture. Add --manifest-only to export the complete candidate. Controls require exactly scans, sources, and edits, using the existing scan/source changes and batch-edit schemas. Unknown controls fail.

    #Prove an override removal is safe

    DatasetIndex::plan_prune(source_path, fields) accepts 1–256 distinct existing field names and returns a PrunePlan only when all indexed scans resolve before/after and have exactly equal effective metadata, including unknown fields and numeric spellings. An unmatched source or any unresolved scan prevents proof. The source remains even if empty; provenance changes remain visible. This guarantee covers only the manifest's scan inventory. The baseline and disk files are unchanged. Use after(), patch() and impact() to export or compose the reviewed proposal.

    Run moon run cmd/main -- prune examples/ds000001/provenance-manifest.json sub-01/func/sub-01_task-balloonanalogrisktask_run-01_bold.json RepetitionTime. This removes a deliberately redundant synthetic override of the real fixture. The report includes the complete edit, both manifests and impact evidence; unsafe requests return a diagnostic and exit 1 without a partial report.

    #Check consistency within acquisition groups

    AuditReport::check_consistency(ConsistencyRules) compares exact whole field values within groups of filename entities, without hard-coding a correct acquisition value. ConsistencyRules::new(group_by, fields, require_present) supports the eleven filename entities supported by scan selection, 1–256 distinct fields, and explicit missing-field policy. Empty group_by checks the entire audit as one group. Missing entity values form a null group; decimal labels are normalized as in filename selection. Select a suffix scope first for mixed modalities.

    moon run cmd/main -- consistency examples/ds000001/manifest.json examples/ds000001/consistency-rules.json checks the real fixture's RT consistency by subject/task. Optional data paths restrict the audit; failed queries remain indeterminate. provenance-manifest.json also passes despite its different assignment history. Uniform wrong values can pass: consistency does not replace explicit acquisition expectations. Missing metadata differs from explicit null; exact numeric spellings and array ordering matter. Errors or an empty audit take priority over violations. Reports retain the full audit, groups, value buckets, original query indices and assignment histories; each inconsistent group-field pair counts once. CLI exits 1 for violations or indeterminate results.

    To reproduce a failing review, export plan-changes examples/ds000001/manifest.json examples/ds000001/consistency-changes.json --manifest-only to a new manifest, then run consistency on that manifest with consistency-rules.json. This authored synthetic change overrides only run 01 to 3.0 while runs 02/03 retain the fixture's 2.0, producing one group-field violation. scripts/verify.mbtx executes this complete workflow in a temporary directory and checks both the exit code and diagnostic report.

    #Explicit conflict reconciliation

    Blocked merges can now accept snapshot-bound source/scan decisions and exact effective-field assertions, revalidate all inherited scan outcomes, and export independently replayable merge review bundles. See the complete workflow.

    #Incremental impact for repeated reviews

    DatasetIndex::dependency_graph() caches full inheritance results and source dependencies; DependencyGraph::advance(after) resolves affected scans and returns a complete impact report plus the next immutable graph. Added sources, ambiguities, shadowed provenance and inventory edits are covered. The optional verify_full() compares the full report byte for byte with ordinary comparison. CLI: diff-incremental BEFORE.json AFTER.json --verify. See the API and offline examples and measured costs.

    TraceError

    pub(all) suberror TraceError {
    TraceError(Diagnostic)
    } derive(
    Debug
    )

    AcquisitionReport

    pub struct AcquisitionReport {
    // private fields
    }

    AcquisitionReport::decision

    AcquisitionReport::error_count

    fn AcquisitionReport::error_count(self : AcquisitionReport) -> Int

    Unresolved indexed scans, counted once irrespective of rule overlap.

    AcquisitionReport::to_json

    fn AcquisitionReport::to_json(self : AcquisitionReport) -> String

    AcquisitionReport::to_json_value

    fn AcquisitionReport::to_json_value(self : AcquisitionReport) -> Json

    AcquisitionReport::uncovered_paths

    fn AcquisitionReport::uncovered_paths(self : AcquisitionReport) -> Array[String]

    AcquisitionReport::unmatched_rule_ids

    fn AcquisitionReport::unmatched_rule_ids(self : AcquisitionReport) -> Array[String]

    AcquisitionReport::violation_count

    fn AcquisitionReport::violation_count(self : AcquisitionReport) -> Int

    Failed rule/field checks; a scan covered by multiple rules can contribute to each.

    AcquisitionRules

    pub struct AcquisitionRules {
    // private fields
    }

    Every declared rule must match scans; every indexed scan must be covered. Overlaps are conjunctive, never first/last-rule-wins.

    AcquisitionRules::from_json

    fn AcquisitionRules::from_json(text : String) -> AcquisitionRules raise TraceError

    AcquisitionRules::to_json

    fn AcquisitionRules::to_json(self : AcquisitionRules) -> String

    AuditEntry

    pub struct AuditEntry {
    // private fields
    }

    AuditEntry::diagnostic

    fn AuditEntry::diagnostic(self : AuditEntry) -> Diagnostic?

    AuditEntry::path

    fn AuditEntry::path(self : AuditEntry) -> String

    AuditEntry::result

    fn AuditEntry::result(self : AuditEntry) -> ResolvedMetadata?

    AuditReport

    pub struct AuditReport {
    // private fields
    }

    AuditReport::check_consistency

    fn AuditReport::check_consistency(self : AuditReport, rules : ConsistencyRules) -> ConsistencyReport

    AuditReport::check_expectations

    fn AuditReport::check_expectations(self : AuditReport, expectations : MetadataExpectations) -> ExpectationReport

    Errors take priority. An empty audit is indeterminate, never vacuous approval. Every requested occurrence retains complete effective/source/error evidence.

    AuditReport::entries

    fn AuditReport::entries(self : AuditReport) -> Array[AuditEntry]

    AuditReport::error_count

    fn AuditReport::error_count(self : AuditReport) -> Int

    AuditReport::filter_metadata

    fn AuditReport::filter_metadata(self : AuditReport, query : MetadataQuery) -> MetadataSelection

    Every input occurrence has a classification and complete resolution evidence. Failed queries remain in audit(); they are not guessed to be nonmatches.

    AuditReport::summarize_fields

    fn AuditReport::summarize_fields(self : AuditReport, keys : Array[String]) -> FieldSummaryReport raise TraceError

    Missing means resolved and absent; unresolved queries are reported separately. Group identity is exact canonical JSON, not numeric/scientific equivalence.

    AuditReport::to_json

    fn AuditReport::to_json(self : AuditReport) -> String

    AuditReport::to_review_csv

    fn AuditReport::to_review_csv(self : AuditReport, keys : Array[String]) -> String raise TraceError

    RFC 4180 quoted UTF-8 review projection; dynamic cells contain tagged JSON. Absence (false), null (true/json:null) and unresolved (empty presence) differ. Rejects output above 16 Mi UTF-16 units rather than returning partial rows.

    BatchEditPlan

    pub struct BatchEditPlan {
    // private fields
    }

    One immutable candidate snapshot, never a collection of partial plans.

    BatchEditPlan::after

    BatchEditPlan::before

    BatchEditPlan::impact

    BatchEditPlan::sidecar_paths

    fn BatchEditPlan::sidecar_paths(self : BatchEditPlan) -> Array[String]

    BatchEditPlan::to_json

    fn BatchEditPlan::to_json(self : BatchEditPlan) -> String

    Retain every patch, old/new source values, complete snapshots and impacts.

    BatchEditPlan::updated_sidecar_json

    fn BatchEditPlan::updated_sidecar_json(self : BatchEditPlan, path : String) -> String?

    Return complete replacement metadata only for an explicitly edited source.

    ConsistencyReport

    pub struct ConsistencyReport {
    // private fields
    }

    ConsistencyReport::decision

    ConsistencyReport::to_json

    fn ConsistencyReport::to_json(self : ConsistencyReport) -> String

    ConsistencyReport::violation_count

    fn ConsistencyReport::violation_count(self : ConsistencyReport) -> Int

    ConsistencyRules

    pub struct ConsistencyRules {
    // private fields
    }

    Exact metadata equality within filename-entity groups, without a fixed expected value. Missing grouping entities form an explicit null group.

    ConsistencyRules::from_json

    fn ConsistencyRules::from_json(text : String) -> ConsistencyRules raise TraceError

    ConsistencyRules::new

    fn ConsistencyRules::new(group_by : Array[String], fields : Array[String], require_present : Bool) -> ConsistencyRules raise TraceError

    ConsistencyRules::to_json

    fn ConsistencyRules::to_json(self : ConsistencyRules) -> String

    DatasetChangePlan

    pub struct DatasetChangePlan {
    // private fields
    }

    DatasetChangePlan::after

    DatasetChangePlan::before

    DatasetChangePlan::impact

    DatasetChangePlan::to_json

    fn DatasetChangePlan::to_json(self : DatasetChangePlan) -> String

    DatasetChanges

    pub struct DatasetChanges {
    // private fields
    }

    One final snapshot transaction. Every operation refers to the baseline.

    DatasetChanges::from_json

    fn DatasetChanges::from_json(text : String) -> DatasetChanges raise TraceError

    DatasetChanges::new

    fn DatasetChanges::new(scans : ScanChanges, sources : SourceChanges, edits : EditBatch) -> DatasetChanges raise TraceError

    DatasetChanges::to_json

    fn DatasetChanges::to_json(self : DatasetChanges) -> String

    DatasetIndex

    pub struct DatasetIndex {
    // private fields
    }

    An immutable construction-time dataset snapshot, with no filesystem access.

    DatasetIndex::audit

    fn DatasetIndex::audit(self : DatasetIndex, paths : Array[String]) -> AuditReport

    Every requested occurrence is retained in request order, including duplicates. Resolution errors are isolated; invalid manifests still fail at construction.

    DatasetIndex::audit_all

    fn DatasetIndex::audit_all(self : DatasetIndex) -> AuditReport

    DatasetIndex::check_acquisition

    fn DatasetIndex::check_acquisition(self : DatasetIndex, rules : AcquisitionRules) -> AcquisitionReport

    Apply all matching filename-scoped rules, account for uncovered scans and unmatched rules, and retain complete snapshot/audit/check evidence.

    DatasetIndex::compare

    fn DatasetIndex::compare(self : DatasetIndex, after : DatasetIndex) -> ImpactReport

    Compare the sorted union of indexed scans. Errors remain indeterminate; nothing is inferred about omitted sidecars or image content.

    DatasetIndex::data_paths

    fn DatasetIndex::data_paths(self : DatasetIndex) -> Array[String]

    DatasetIndex::dependency_graph

    fn DatasetIndex::dependency_graph(self : DatasetIndex) -> DependencyGraph raise TraceError

    Resolve once and retain immutable results/errors plus complete dependencies. Graph construction is explicit and can cost more than one full comparison.

    DatasetIndex::from_json

    fn DatasetIndex::from_json(text : String) -> DatasetIndex raise TraceError

    Read the strict v1 manifest: {"files":[paths],"sidecars":[{"path":path,"metadata":object}]}. Duplicate keys are checked throughout, before any data is consumed.

    DatasetIndex::from_manifest

    fn DatasetIndex::from_manifest(files : Array[String], sidecars : Array[SidecarInput]) -> DatasetIndex raise TraceError

    Build a snapshot from supported raw-MRI paths and complete JSON sources. Repeated paths, scope errors, duplicate JSON keys and unknown profiles fail.

    DatasetIndex::merge

    fn DatasetIndex::merge(self : DatasetIndex, left : DatasetIndex, right : DatasetIndex) -> MergeReport raise TraceError

    Three-way metadata merge over a shared ancestor, followed by per-scan inheritance validation. Values are atomic at the top-level field boundary. Conflicts block the entire candidate. More than 1,024 conflicts raises. This is a conservative review policy, not a BIDS conformance certificate.

    DatasetIndex::plan_changes

    fn DatasetIndex::plan_changes(self : DatasetIndex, changes : DatasetChanges) -> DatasetChangePlan raise TraceError

    DatasetIndex::plan_edit

    fn DatasetIndex::plan_edit(self : DatasetIndex, sidecar_path : String, patch : MetadataPatch) -> EditPlan raise TraceError

    Plan one existing sidecar edit, then revalidate the complete new snapshot. Removing an absent field fails. Scan ambiguity stays visible in the impact.

    DatasetIndex::plan_edits

    fn DatasetIndex::plan_edits(self : DatasetIndex, batch : EditBatch) -> BatchEditPlan raise TraceError

    Validate all operations against the baseline, rebuild once, then compare. Failures never mutate the baseline or return a partially updated snapshot.

    DatasetIndex::plan_prune

    fn DatasetIndex::plan_prune(self : DatasetIndex, source_path : String, fields : Array[String]) -> PrunePlan raise TraceError

    DatasetIndex::plan_scans

    fn DatasetIndex::plan_scans(self : DatasetIndex, changes : ScanChanges) -> ScanChangePlan raise TraceError

    Validate the final snapshot only, then expose impacts and source coverage. Removed scans keep before evidence; unmatched sources remain in the candidate.

    DatasetIndex::plan_sources

    fn DatasetIndex::plan_sources(self : DatasetIndex, changes : SourceChanges) -> SourceChangePlan raise TraceError

    Build/revalidate only the final inventory. Never writes/deletes disk files.

    DatasetIndex::resolve

    fn DatasetIndex::resolve(self : DatasetIndex, data_path : String) -> ResolvedMetadata raise TraceError

    Resolve JSON top-level assignments only. Same-level ambiguity is an error, even when one filename has more entities or the values happen to agree.

    DatasetIndex::select_metadata

    fn DatasetIndex::select_metadata(self : DatasetIndex, query : MetadataQuery) -> MetadataSelection

    DatasetIndex::select_scans

    fn DatasetIndex::select_scans(self : DatasetIndex, query : ScanQuery) -> ScanSelection

    Selection depends only on validated filename entities, never metadata success. The original index is retained so ancestor and unselected-source inheritance remain available when auditing the selected scans.

    DatasetIndex::source_coverage

    fn DatasetIndex::source_coverage(self : DatasetIndex) -> SourceCoverageReport

    Account for every supplied sidecar, including empty and ambiguous sources. Failed scans never produce speculative winning/shadowed assignments.

    DatasetIndex::to_manifest_json

    fn DatasetIndex::to_manifest_json(self : DatasetIndex) -> String

    DependencyGraph

    pub struct DependencyGraph {
    // private fields
    }

    Immutable resolution cache with all applicable source edges, including ambiguity participants and empty/shadowed sources. No filesystem state.

    DependencyGraph::advance

    Advance to arbitrary validated snapshot content. Changed/removed sources invalidate all old edges; added sources also search potential dependencies. Scan additions/removals invalidate their own paths. Untouched results/errors are reused without resolution. Never mutates this graph or either snapshot.

    DependencyGraph::audit

    Cached audit in the snapshot's sorted scan order, including errors.

    DependencyGraph::dependents

    fn DependencyGraph::dependents(self : DependencyGraph, source_path : String) -> Array[String] raise TraceError

    Current applicable dependents of an existing source, even if resolution is ambiguous. Unknown paths are errors rather than an empty graph answer.

    DependencyGraph::snapshot

    DependencyGraph::to_json

    fn DependencyGraph::to_json(self : DependencyGraph) -> String

    Deterministic dependency evidence with full snapshot and cached audit.

    Diagnostic

    pub(all) struct Diagnostic {
    code : String
    path : String
    message : String
    offset : Int?
    related : Array[String]
    } derive(Eq, ToJson,
    Debug
    )

    A stable machine-readable diagnostic. Offsets count UTF-16 code units.

    Diagnostic::equal

    fn Diagnostic::equal(Diagnostic, Diagnostic) -> Bool

    Diagnostic::not_equal

    fn Diagnostic::not_equal(x : Diagnostic, y : Diagnostic) -> Bool

    Diagnostic::to_json

    fn Diagnostic::to_json(Diagnostic) -> Json

    Diagnostic::to_report_json

    fn Diagnostic::to_report_json(self : Diagnostic) -> String

    Deterministic JSON for logs and CLI error streams.

    DiscoveryReport

    pub struct DiscoveryReport {
    // private fields
    }

    DiscoveryReport::auxiliary_paths

    fn DiscoveryReport::auxiliary_paths(self : DiscoveryReport) -> Array[String]

    DiscoveryReport::excluded_paths

    fn DiscoveryReport::excluded_paths(self : DiscoveryReport) -> Array[String]

    DiscoveryReport::from_inventory

    fn DiscoveryReport::from_inventory(paths : Array[String], sidecars : Array[SidecarInput]) -> DiscoveryReport raise TraceError

    The caller supplies a full inventory of regular files and explicit excluded subtree paths, plus text for every classified MRI sidecar. Image bytes unused.

    DiscoveryReport::index

    DiscoveryReport::to_json

    fn DiscoveryReport::to_json(self : DiscoveryReport) -> String

    DiscoveryRole

    pub(all) enum DiscoveryRole {
    Directory
    Data
    Sidecar
    Auxiliary
    Excluded
    } derive(Eq)

    DiscoveryRole::equal

    DiscoveryRole::not_equal

    fn DiscoveryRole::not_equal(x : DiscoveryRole, y : DiscoveryRole) -> Bool

    EditBatch

    pub struct EditBatch {
    // private fields
    }

    A bounded, unordered set of edits to distinct existing sidecars. Every patch is evaluated against the same original snapshot.

    EditBatch::from_json

    fn EditBatch::from_json(text : String) -> EditBatch raise TraceError

    Read {"edits":[{"path":relative_path,"set":object,"remove":[field_names]}]}. Unknown controls and duplicate paths/decoded JSON keys fail. Empty is a no-op.

    EditBatch::from_patches

    fn EditBatch::from_patches(patches : Array[(String, MetadataPatch)]) -> EditBatch raise TraceError

    Build a batch directly from typed patches. Copies/normalizes the input list. A successful batch always reloads through from_json(to_json()).

    EditBatch::sidecar_paths

    fn EditBatch::sidecar_paths(self : EditBatch) -> Array[String]

    EditBatch::to_json

    fn EditBatch::to_json(self : EditBatch) -> String

    Normalize sidecar/removal ordering while preserving exact metadata tokens.

    EditPlan

    pub struct EditPlan {
    // private fields
    }

    An immutable proposed edit with complete before/after snapshot evidence. Creating or inspecting a plan never changes any filesystem entry.

    EditPlan::after

    fn EditPlan::after(self : EditPlan) -> DatasetIndex

    EditPlan::before

    fn EditPlan::before(self : EditPlan) -> DatasetIndex

    EditPlan::impact

    fn EditPlan::impact(self : EditPlan) -> ImpactReport

    EditPlan::sidecar_path

    fn EditPlan::sidecar_path(self : EditPlan) -> String

    EditPlan::to_json

    fn EditPlan::to_json(self : EditPlan) -> String

    Full snapshots, explicit operations, sidecar values and per-scan provenance. An error in impact marks the plan indeterminate rather than safe to apply.

    EditPlan::updated_sidecar_json

    fn EditPlan::updated_sidecar_json(self : EditPlan) -> String

    Complete replacement metadata for this sidecar, retaining untouched fields.

    ExpectationDecision

    pub(all) enum ExpectationDecision {
    Pass
    Violations
    Indeterminate
    } derive(Eq)

    ExpectationDecision::equal

    ExpectationDecision::not_equal

    ExpectationReport

    pub struct ExpectationReport {
    // private fields
    }

    ExpectationReport::decision

    ExpectationReport::error_count

    fn ExpectationReport::error_count(self : ExpectationReport) -> Int

    ExpectationReport::to_json

    fn ExpectationReport::to_json(self : ExpectationReport) -> String

    ExpectationReport::to_json_value

    fn ExpectationReport::to_json_value(self : ExpectationReport) -> Json

    ExpectationReport::violation_count

    fn ExpectationReport::violation_count(self : ExpectationReport) -> Int

    Counts failed legacy fields plus numeric fields/relations, including repeats.

    FieldChange

    pub struct FieldChange {
    // private fields
    }

    FieldChange::after_json

    fn FieldChange::after_json(self : FieldChange) -> String?

    FieldChange::after_trace

    fn FieldChange::after_trace(self : FieldChange) -> Array[FieldOrigin]

    FieldChange::before_json

    fn FieldChange::before_json(self : FieldChange) -> String?

    None means absent; Some("null") is a present JSON null. Number tokens matter.

    FieldChange::before_trace

    fn FieldChange::before_trace(self : FieldChange) -> Array[FieldOrigin]

    FieldChange::key

    fn FieldChange::key(self : FieldChange) -> String

    FieldChange::kind

    FieldChangeKind

    pub(all) enum FieldChangeKind {
    Added
    Removed
    ValueChanged
    ProvenanceChanged
    } derive(Eq)

    FieldChangeKind::equal

    FieldChangeKind::not_equal

    fn FieldChangeKind::not_equal(x : FieldChangeKind, y : FieldChangeKind) -> Bool

    FieldOrigin

    pub struct FieldOrigin {
    // private fields
    }

    One assignment of a top-level metadata field, in root-to-leaf order.

    FieldOrigin::path

    fn FieldOrigin::path(self : FieldOrigin) -> String

    FieldOrigin::value

    fn FieldOrigin::value(self : FieldOrigin) -> Json

    Returns a defensive copy, preserving numeric source representations.

    FieldSummaryReport

    pub struct FieldSummaryReport {
    // private fields
    }

    Exact whole-value groups over an audit, retaining duplicate requests and errors.

    FieldSummaryReport::error_count

    fn FieldSummaryReport::error_count(self : FieldSummaryReport) -> Int

    FieldSummaryReport::to_json

    fn FieldSummaryReport::to_json(self : FieldSummaryReport) -> String

    FieldSummaryReport::to_json_value

    fn FieldSummaryReport::to_json_value(self : FieldSummaryReport) -> Json

    A defensive structured view; numeric source tokens remain intact.

    ImpactEntry

    pub struct ImpactEntry {
    // private fields
    }

    ImpactEntry::after

    fn ImpactEntry::after(self : ImpactEntry) -> AuditEntry?

    ImpactEntry::before

    fn ImpactEntry::before(self : ImpactEntry) -> AuditEntry?

    ImpactEntry::changes

    fn ImpactEntry::changes(self : ImpactEntry) -> Array[FieldChange]

    ImpactEntry::kind

    fn ImpactEntry::kind(self : ImpactEntry) -> ImpactKind

    ImpactEntry::path

    fn ImpactEntry::path(self : ImpactEntry) -> String

    ImpactEntry::sources_changed

    fn ImpactEntry::sources_changed(self : ImpactEntry) -> Bool

    Meaningful only when neither side has a resolution error.

    ImpactKind

    pub(all) enum ImpactKind {
    Unchanged
    Changed
    Added
    Removed
    Unresolved
    } derive(Eq)

    ImpactKind::equal

    fn ImpactKind::equal(ImpactKind, ImpactKind) -> Bool

    ImpactKind::not_equal

    fn ImpactKind::not_equal(x : ImpactKind, y : ImpactKind) -> Bool

    ImpactReport

    pub struct ImpactReport {
    // private fields
    }

    ImpactReport::changed_count

    fn ImpactReport::changed_count(self : ImpactReport) -> Int

    ImpactReport::entries

    fn ImpactReport::entries(self : ImpactReport) -> Array[ImpactEntry]

    ImpactReport::error_count

    fn ImpactReport::error_count(self : ImpactReport) -> Int

    Count indeterminate scan paths, not individual diagnostics.

    ImpactReport::release_check

    fn ImpactReport::release_check(self : ImpactReport, policy : ReleasePolicy) -> ReleaseCheck

    ImpactReport::to_json

    fn ImpactReport::to_json(self : ImpactReport) -> String

    IncrementalImpact

    pub struct IncrementalImpact {
    // private fields
    }

    A complete impact report and the next immutable cache. Invalidated means potentially affected, not necessarily changed. Counts describe actual work.

    IncrementalImpact::invalidated_paths

    fn IncrementalImpact::invalidated_paths(self : IncrementalImpact) -> Array[String]

    IncrementalImpact::next

    IncrementalImpact::report

    IncrementalImpact::resolved_count

    fn IncrementalImpact::resolved_count(self : IncrementalImpact) -> Int

    Actual after-side resolution attempts, including new and unresolved scans.

    IncrementalImpact::reused_count

    fn IncrementalImpact::reused_count(self : IncrementalImpact) -> Int

    Entries reused in the AFTER snapshot, including unchanged cached errors.

    IncrementalImpact::to_json

    fn IncrementalImpact::to_json(self : IncrementalImpact) -> String

    IncrementalImpact::verify_full

    fn IncrementalImpact::verify_full(self : IncrementalImpact) -> Unit raise TraceError

    Optional full-oracle verification. Compares the entire deterministic report, including all before/after metadata, histories and diagnostics, byte for byte.

    MergeConflict

    pub struct MergeConflict {
    // private fields
    }

    Exact JSON evidence; None is absence, Some("null") is a present null. For source conflicts evidence is a complete source object; for scan deletion it is a complete resolution report. diagnostics identify unresolved inputs.

    MergeConflict::base_json

    fn MergeConflict::base_json(self : MergeConflict) -> String?

    MergeConflict::branch

    fn MergeConflict::branch(self : MergeConflict) -> String?

    base, left, right or merged for resolution/validation errors; otherwise None.

    MergeConflict::diagnostics

    fn MergeConflict::diagnostics(self : MergeConflict) -> Array[Diagnostic]

    MergeConflict::key

    fn MergeConflict::key(self : MergeConflict) -> String?

    MergeConflict::kind

    MergeConflict::left_json

    fn MergeConflict::left_json(self : MergeConflict) -> String?

    MergeConflict::merged_json

    fn MergeConflict::merged_json(self : MergeConflict) -> String?

    MergeConflict::message

    fn MergeConflict::message(self : MergeConflict) -> String

    MergeConflict::path

    fn MergeConflict::path(self : MergeConflict) -> String

    MergeConflict::right_json

    fn MergeConflict::right_json(self : MergeConflict) -> String?

    MergeConflictKind

    pub(all) enum MergeConflictKind {
    FieldDiverged
    SourceAddAdd
    SourceDeleteEdit
    ScanDeleteEdit
    EffectiveDiverged
    InheritanceShadow
    UnresolvedMetadata
    InvalidSnapshot
    } derive(Eq,
    Debug
    )

    Conflicts are review outcomes, never an instruction to choose a branch.

    MergeConflictKind::equal

    MergeConflictKind::not_equal

    fn MergeConflictKind::not_equal(x : MergeConflictKind, y : MergeConflictKind) -> Bool

    MergeReport

    pub struct MergeReport {
    // private fields
    }

    An all-or-nothing proposal retaining all three immutable inputs. A candidate exists only after structural and inheritance checks both pass.

    MergeReport::candidate

    fn MergeReport::candidate(self : MergeReport) -> DatasetIndex?

    MergeReport::conflict_count

    fn MergeReport::conflict_count(self : MergeReport) -> Int

    MergeReport::conflicts

    fn MergeReport::conflicts(self : MergeReport) -> Array[MergeConflict]

    MergeReport::impact

    fn MergeReport::impact(self : MergeReport) -> ImpactReport?

    Full base-to-candidate impact, including provenance. None when blocked.

    MergeReport::reconcile

    fn MergeReport::reconcile(self : MergeReport, resolution : MergeResolution) -> ReconciledMerge raise TraceError

    MergeReport::resolution_template

    fn MergeReport::resolution_template(self : MergeReport) -> String

    A directly editable binding document; empty decisions do not waive conflicts.

    MergeReport::to_json

    fn MergeReport::to_json(self : MergeReport) -> String

    Deterministic complete evidence. No partially merged manifest is exposed when conflicts exist. Recompute with merge() from the three embedded inputs.

    MergeResolution

    pub struct MergeResolution {
    // private fields
    }

    Explicit decisions bound to three complete immutable snapshots. Complete source replacements (null removes a source) cannot affect unnamed sources. Effective assertions compare exact JSON tokens, including absent vs null.

    MergeResolution::from_json

    fn MergeResolution::from_json(text : String) -> MergeResolution raise TraceError

    Strict v1 control grammar. Up to 256 source/scan decisions combined and 1,024 effective field assertions. Snapshot limits remain independently enforced.

    MergeResolution::to_json

    fn MergeResolution::to_json(self : MergeResolution) -> String

    MergeReviewBundle

    pub struct MergeReviewBundle {
    // private fields
    }

    Self-contained reconciliation and release evidence. Replay checks agreement, not authorship, signatures, dataset authenticity or release authorization.

    MergeReviewBundle::from_json

    fn MergeReviewBundle::from_json(text : String) -> MergeReviewBundle raise TraceError

    Recompute both the initial merge and reconciled candidate, verify all recorded evidence and the release bundle's exact snapshots and policy.

    MergeReviewBundle::reconciliation

    MergeReviewBundle::report

    MergeReviewBundle::to_json

    fn MergeReviewBundle::to_json(self : MergeReviewBundle) -> String

    MetadataExpectations

    pub struct MetadataExpectations {
    // private fields
    }

    Project-specific whole-field expectations over effective inherited metadata. Optional numeric rules add exact decimal policy, without unit conversion.

    MetadataExpectations::from_json

    fn MetadataExpectations::from_json(text : String) -> MetadataExpectations raise TraceError

    Strict required/allowed controls, with optional numeric rules. Allowed-only fields may be absent; explicit null is a present value.

    MetadataExpectations::to_json

    fn MetadataExpectations::to_json(self : MetadataExpectations) -> String

    MetadataExpectations::with_numeric

    Attach/replace numeric policy, retaining existing exact-value expectations.

    MetadataPatch

    pub struct MetadataPatch {
    // private fields
    }

    Explicit whole-field assignments and removals. JSON null is an assignment. A patch never deletes nested values implicitly or merges nested objects.

    MetadataPatch::from_json

    fn MetadataPatch::from_json(text : String) -> MetadataPatch raise TraceError

    Parse {"set":object,"remove":[unique field names]}; both controls are required. Unknown controls, duplicate JSON keys and contradictory operations fail.

    MetadataPatch::to_json

    fn MetadataPatch::to_json(self : MetadataPatch) -> String

    Deterministic patch JSON; removal order is normalized, number tokens retained.

    MetadataQuery

    pub struct MetadataQuery {
    // private fields
    }

    Exact predicates over effective, inherited top-level fields.

    MetadataQuery::from_json

    fn MetadataQuery::from_json(text : String) -> MetadataQuery raise TraceError

    All controls mandatory; AND across fields, OR within exact value choices. Equality requires presence. Null is present; unknown controls never vanish.

    MetadataQuery::to_json

    fn MetadataQuery::to_json(self : MetadataQuery) -> String

    MetadataQuery::with_numeric

    fn MetadataQuery::with_numeric(self : MetadataQuery, rules : NumericRules) -> MetadataQuery raise TraceError

    Attach/replace numeric predicates. A field cannot be both absent and numeric.

    MetadataSelection

    pub struct MetadataSelection {
    // private fields
    }

    MetadataSelection::audit

    Matches plus unresolved entries, in input order, so downstream gates see errors.

    MetadataSelection::error_count

    fn MetadataSelection::error_count(self : MetadataSelection) -> Int

    MetadataSelection::paths

    fn MetadataSelection::paths(self : MetadataSelection) -> Array[String]

    Only proven matches; unresolved entries are accounted separately, never excluded.

    MetadataSelection::to_json

    fn MetadataSelection::to_json(self : MetadataSelection) -> String

    MetadataSelection::to_json_value

    fn MetadataSelection::to_json_value(self : MetadataSelection) -> Json

    NumericRules

    pub struct NumericRules {
    // private fields
    }

    Project policy over inherited top-level numeric fields, with exact decimal comparisons. This is not a JSON Schema implementation or unit converter.

    NumericRules::from_json

    fn NumericRules::from_json(text : String) -> NumericRules raise TraceError

    Strict {fields:{name:{type,...}},relations:[{left,op,right}]}. Every declared field is required. Relations reference declared scalar fields.

    NumericRules::to_json

    fn NumericRules::to_json(self : NumericRules) -> String

    PrunePlan

    pub struct PrunePlan {
    // private fields
    }

    A removal proposal whose effective metadata is proven unchanged for every indexed scan. This makes no statement about scans outside the manifest.

    PrunePlan::after

    fn PrunePlan::after(self : PrunePlan) -> DatasetIndex

    PrunePlan::impact

    fn PrunePlan::impact(self : PrunePlan) -> ImpactReport

    PrunePlan::patch

    fn PrunePlan::patch(self : PrunePlan) -> MetadataPatch

    PrunePlan::to_json

    fn PrunePlan::to_json(self : PrunePlan) -> String

    ReconciledMerge

    pub struct ReconciledMerge {
    // private fields
    }

    A proposal after replaying explicit decisions and full inheritance checks. Blocked proposals expose evidence, never a partial candidate or bundle.

    ReconciledMerge::candidate

    fn ReconciledMerge::candidate(self : ReconciledMerge) -> DatasetIndex?

    ReconciledMerge::conflict_count

    fn ReconciledMerge::conflict_count(self : ReconciledMerge) -> Int

    ReconciledMerge::conflicts

    ReconciledMerge::impact

    ReconciledMerge::review_bundle

    fn ReconciledMerge::review_bundle(self : ReconciledMerge, policy : ReleasePolicy) -> MergeReviewBundle raise TraceError

    ReconciledMerge::to_json

    fn ReconciledMerge::to_json(self : ReconciledMerge) -> String

    Original conflicts, all decisions/reasons, all three inputs and the complete revalidated base-to-candidate evidence. This is not a release approval.

    ReleaseCheck

    pub struct ReleaseCheck {
    // private fields
    }

    ReleaseCheck::affected_paths

    fn ReleaseCheck::affected_paths(self : ReleaseCheck) -> Array[String]

    Sorted scan paths meeting the selected change policy, even when errors exist.

    ReleaseCheck::decision

    fn ReleaseCheck::decision(self : ReleaseCheck) -> ReleaseDecision

    Errors take precedence over changes. A pass refers only to this policy and these explicit snapshots; it is not a BIDS compliance or completeness claim.

    ReleaseCheck::impact

    fn ReleaseCheck::impact(self : ReleaseCheck) -> ImpactReport

    Full evidence, including policy-ignored provenance changes and diagnostics.

    ReleaseCheck::policy

    ReleaseCheck::to_json

    fn ReleaseCheck::to_json(self : ReleaseCheck) -> String

    ReleaseCheck::unresolved_paths

    fn ReleaseCheck::unresolved_paths(self : ReleaseCheck) -> Array[String]

    ReleaseDecision

    pub(all) enum ReleaseDecision {
    Pass
    ChangesDetected
    Indeterminate
    } derive(Eq)

    ReleaseDecision::equal

    ReleaseDecision::not_equal

    fn ReleaseDecision::not_equal(x : ReleaseDecision, y : ReleaseDecision) -> Bool

    ReleasePolicy

    pub(all) enum ReleasePolicy {
    EffectiveValues
    AllChanges
    } derive(Eq)

    EffectiveValues includes scan additions/removals and exact value-token edits. AllChanges additionally includes assignment-history and source-chain edits.

    ReleasePolicy::equal

    ReleasePolicy::not_equal

    fn ReleasePolicy::not_equal(x : ReleasePolicy, y : ReleasePolicy) -> Bool

    ResolvedMetadata

    pub struct ResolvedMetadata {
    // private fields
    }

    ResolvedMetadata::applied_sidecars

    fn ResolvedMetadata::applied_sidecars(self : ResolvedMetadata) -> Array[String]

    ResolvedMetadata::get

    fn ResolvedMetadata::get(self : ResolvedMetadata, key : String) -> Json?

    ResolvedMetadata::keys

    fn ResolvedMetadata::keys(self : ResolvedMetadata) -> Array[String]

    ResolvedMetadata::path

    fn ResolvedMetadata::path(self : ResolvedMetadata) -> String

    ResolvedMetadata::to_json

    fn ResolvedMetadata::to_json(self : ResolvedMetadata) -> String

    Sorted object keys, preserved array order and numeric lexemes; no float math.

    ResolvedMetadata::to_report_json

    fn ResolvedMetadata::to_report_json(self : ResolvedMetadata) -> String

    ResolvedMetadata::trace

    fn ResolvedMetadata::trace(self : ResolvedMetadata, key : String) -> Array[FieldOrigin]

    ReviewBundle

    pub struct ReviewBundle {
    // private fields
    }

    A self-contained release review whose report can be recomputed from its complete immutable snapshots. Replay verifies content agreement, not who produced the bundle or whether its snapshots represent a real dataset.

    ReviewBundle::after

    ReviewBundle::before

    fn ReviewBundle::before(self : ReviewBundle) -> DatasetIndex

    ReviewBundle::create

    fn ReviewBundle::create(before : DatasetIndex, after : DatasetIndex, policy : ReleasePolicy) -> ReviewBundle raise TraceError

    Capture both complete snapshots and a policy-specific release check. An indeterminate check is retained with all diagnostics, never made into pass.

    ReviewBundle::from_json

    fn ReviewBundle::from_json(text : String) -> ReviewBundle raise TraceError

    Strictly decode a v1 bundle and recompute its complete expected report. Duplicate keys, unknown controls, unsupported versions, and stale evidence are errors. Numeric source tokens are compared exactly, without float math.

    ReviewBundle::report

    fn ReviewBundle::report(self : ReviewBundle) -> ReleaseCheck

    The recomputed check, including policy-ignored changes and diagnostics.

    ReviewBundle::to_json

    fn ReviewBundle::to_json(self : ReviewBundle) -> String

    Deterministic complete v1 document; creation has already checked its size.

    ScanChangePlan

    pub struct ScanChangePlan {
    // private fields
    }

    ScanChangePlan::after

    ScanChangePlan::after_coverage

    fn ScanChangePlan::after_coverage(self : ScanChangePlan) -> SourceCoverageReport

    ScanChangePlan::before

    ScanChangePlan::before_coverage

    fn ScanChangePlan::before_coverage(self : ScanChangePlan) -> SourceCoverageReport

    ScanChangePlan::impact

    ScanChangePlan::to_json

    fn ScanChangePlan::to_json(self : ScanChangePlan) -> String

    Complete evidence with already canonical components; never rounds numbers.

    ScanChanges

    pub struct ScanChanges {
    // private fields
    }

    Atomic changes to the indexed scan inventory; sources are never removed.

    ScanChanges::from_json

    fn ScanChanges::from_json(text : String) -> ScanChanges raise TraceError

    ScanChanges::new

    fn ScanChanges::new(additions : Array[String], removals : Array[String]) -> ScanChanges raise TraceError

    ScanChanges::to_json

    fn ScanChanges::to_json(self : ScanChanges) -> String

    ScanQuery

    pub struct ScanQuery {
    // private fields
    }

    AND across entity names, OR across a name's choices and selected suffixes.

    ScanQuery::from_json

    fn ScanQuery::from_json(text : String) -> ScanQuery raise TraceError

    Strict {suffixes:[strings],entities:{entity:[strings]}}; both mandatory.

    ScanQuery::new

    fn ScanQuery::new(suffixes : Array[String], entities : Map[String, Array[String]]) -> ScanQuery raise TraceError

    Copies inputs, normalizes decimal indices without conversion to machine Int. Empty choice lists, unsupported keys/values and normalized duplicates fail.

    ScanQuery::to_json

    fn ScanQuery::to_json(self : ScanQuery) -> String

    ScanSelection

    pub struct ScanSelection {
    // private fields
    }

    ScanSelection::audit

    Empty selection returns an empty audit, not audit_all(). Errors are retained.

    ScanSelection::paths

    fn ScanSelection::paths(self : ScanSelection) -> Array[String]

    ScanSelection::to_json

    fn ScanSelection::to_json(self : ScanSelection) -> String

    Inventory accounting, not a new manifest; no sources/data are deleted.

    SidecarInput

    pub struct SidecarInput {
    // private fields
    }

    Sidecar source text is retained; no caller-supplied Json can be mutated.

    SidecarInput::new

    fn SidecarInput::new(path : String, text : String) -> SidecarInput

    SourceChangePlan

    pub struct SourceChangePlan {
    // private fields
    }

    SourceChangePlan::after

    SourceChangePlan::before

    SourceChangePlan::impact

    SourceChangePlan::to_json

    fn SourceChangePlan::to_json(self : SourceChangePlan) -> String

    Complete snapshots retain removed data, including sources without scan targets.

    SourceChanges

    pub struct SourceChanges {
    // private fields
    }

    Atomic source inventory changes; additions and removals name distinct paths.

    SourceChanges::from_json

    fn SourceChanges::from_json(text : String) -> SourceChanges raise TraceError

    Both add and remove are mandatory; every unknown control is rejected.

    SourceChanges::new

    fn SourceChanges::new(additions : Array[SidecarInput], removals : Array[String]) -> SourceChanges raise TraceError

    Validate source paths/JSON, normalize order, retain exact unknown metadata.

    SourceChanges::to_json

    fn SourceChanges::to_json(self : SourceChanges) -> String

    SourceCoverageEntry

    pub struct SourceCoverageEntry {
    // private fields
    }

    Applicability and field contribution for one source within indexed scans.

    SourceCoverageEntry::applicable_paths

    fn SourceCoverageEntry::applicable_paths(self : SourceCoverageEntry) -> Array[String]

    SourceCoverageEntry::fully_shadowed_paths

    fn SourceCoverageEntry::fully_shadowed_paths(self : SourceCoverageEntry) -> Array[String]

    SourceCoverageEntry::metadata_keys

    fn SourceCoverageEntry::metadata_keys(self : SourceCoverageEntry) -> Array[String]

    SourceCoverageEntry::path

    fn SourceCoverageEntry::path(self : SourceCoverageEntry) -> String

    SourceCoverageEntry::unresolved_paths

    fn SourceCoverageEntry::unresolved_paths(self : SourceCoverageEntry) -> Array[String]

    SourceCoverageEntry::winning_paths

    fn SourceCoverageEntry::winning_paths(self : SourceCoverageEntry) -> Array[String]

    SourceCoverageReport

    pub struct SourceCoverageReport {
    // private fields
    }

    SourceCoverageReport::diagnostics

    SourceCoverageReport::entries

    SourceCoverageReport::error_count

    fn SourceCoverageReport::error_count(self : SourceCoverageReport) -> Int

    SourceCoverageReport::to_json

    fn SourceCoverageReport::to_json(self : SourceCoverageReport) -> String

    SourceCoverageReport::unmatched_paths

    fn SourceCoverageReport::unmatched_paths(self : SourceCoverageReport) -> Array[String]

    Sources without any applicable indexed scan; this is not a deletion verdict.

    classify_discovery_entry

    fn classify_discovery_entry(path : String, directory~ : Bool) -> DiscoveryRole raise TraceError

    Classify an inventory entry before reading it. Unsupported candidates fail; exclusions are explicit profile boundaries, never implicit .bidsignore rules.