Migrated from the legacy mvmr_container wrapper in nodes-io. One
directory = one plugin family = one git-able unit. Panel-free family:
MVMR needs no reference data bundles, so the manifest declares no
[[panels]] (the loader documents mvmr as a legitimately panel-free
family).
manifest.toml— node kindmvmr: params, ports, image provenancescripts/mvmr.R— the execution script (R, run byRscript), referenced relatively and inlined by the loader at startupDockerfile— pinned official MVMR 0.4.8 image build (rocker/r-ver 4.5.1 base, upstream source tarball at revisionbceaa38)test_mvmr.sh— image baseline: builds the image and smoke-tests the official package againstfixtures/rawdat_mvmr.csvfixtures/— vendored test data for the image baseline
Development checkout at /mnt/projects/node-plugins/mvmr; the runtime
loads every subdirectory with a manifest.toml from the plugins root
(~/.autonomics/plugins, or AUTONOMICS_PLUGIN_ROOT, or the explicit
plugins_root argument of build_default_registry_with_container_execution).
The golden test (autonomics repo,
crates/container-plugin/tests/mvmr_migration.rs) compares the compiled
ContainerCommandSpec against the legacy Rust wrapper's contract:
image, outputs, timeout, and artifact prefix are asserted directly; the
script is asserted on semantic markers. Structural deltas, all
deliberate:
- Kind rename:
mvmr_container→mvmr(plugin convention drops the_containersuffix);artifact_prefixfollows the kind (/artifacts/mvmr, legacy default was/artifacts/mvmr_container). DAG specs referencing the old kind must be regenerated. - Params via env: the legacy wrapper baked every parameter into the
R program with Rust string-building (empty env); the plugin renders
MVMR_*env values and the script reads them withSys.getenv. String arrays (beta_xg,sebeta_xg) render space-joined and are rebuilt withstrsplit; booleans rendertrue/falseand convert withas.logical; optional params render as empty strings and test withnzchar. Column access usesdata[[name]](identical to the legacydata$namecodegen, robust to non-syntactic names). - Diagnostic dispatch:
strength/strhet/pleiotropy/qhetstay booleans; the legacy compile-time choice between the upstream call andNULLbecame a script-sideif (flag) MVMR::… else NULL. Result-slot semantics are unchanged (assigningNULLdrops the slot, exactly like the legacy emittedresult$x <- NULL). gencovis gone as a param: the legacy field was#[schemars(skip)], had to be zero, and the R code ignored its value (gencov <- 0orphenocov_mvmr). The plugin derives gencov frompcorpresence only, which is everything the legacy path supported.pcorserialization (DSL gap): the v0 param vocabulary has no number-matrix type, so the legacy nested-arraypcortravels as a row-major serialized string (rows;-separated, entries,- separated, e.g.1,0.25;0.25,1). The script rebuilds the exact legacymatrix(..., byrow = TRUE)construction and re-implements the legacy validation (square, finite, symmetric, unit diagonal, andqhet requires a pcor matrix). Replace with a structured param type once container-plugin grows one.- Validation moved into the container: legacy
validate()ran at DAG build time; its checks (non-empty columns, equal-length exposure vectors, pcor shape) now run at the top of the script with the same failure messages.beta_xg/sebeta_xgequal-length andqhet ⇒ pcorare cross-field rules the DSL cannot express (requiresgates bools only), so they live in the script. - Output port labels: legacy ports were unlabeled; manifest ports
always carry labels, and both file stems are
mvmr, so the outputs declare explicitresult/loglabels. - Everything numerical —
format_mvmrinput construction, the upstreamivw_mvmr/strength_mvmr/strhet_mvmr/pleiotropy_mvmr/qhet_mvmr/phenocov_mvmrcalls, thesink/printlog epilogue,saveRDSoutput, and the output file names (mvmr.RDS,mvmr.log) — is byte-faithful to the legacy program.