Native V1 migration and compatibility guide
Warning
Version 1.0.0 is published and is the compatibility baseline. Corrective
V1 qualification is in progress; this page distinguishes the general Python
API from the explicitly selected portable profile. A supported portable
subset alone does not establish general Python or Studio feature parity.
Runtime boundary
The R3/R4 product architecture is unchanged:
the general Python
run()API chooses a DAG-ML execution profile before execution: portable requests usenative; other requests usedag-ml;an explicit
engine=orN4A_ENGINEselector is respected unchanged;the explicit
engine="native"portable profile fails closed; no execution failure is retried through another engine;engine="legacy"remains available only when a direct Python caller selects the rollback profile explicitly, through the end of R4;Studio owns HTTP, WebSocket, store, jobs and scheduling in Rust;
Studio may package CPython only as a bounded JSON-stdio host for an explicit library or plugin after capability preflight. CPython owns no listener, scheduler, store, job lifecycle or fallback;
Studio, Web and strict Python product paths cannot select the legacy engine.
Installing Python is therefore the normal way to use the Python library and its optional ML integrations. It is not an instruction to install a Python backend for Studio.
Migrate a workspace
Migration is an explicit, one-way operation in the separately published
nirs4all-tools 0.0.7 package. The runtime never starts it automatically and
never writes a legacy source.
python -m venv n4a-migration
n4a-migration/bin/python -m pip install \
"nirs4all-tools[duckdb,parquet]==0.0.7"
n4a-migration/bin/nirs4all-tools workspace inspect /data/workspace
n4a-migration/bin/nirs4all-tools workspace convert /data/workspace \
--output /data/workspace-v2 --verify
The source path, inode and bytes remain unchanged. Keep the source and output
at different paths. Exit code 0 means a clean verified conversion; 10
means that unsupported content was preserved opaque and needs review; 20
means unsupported input or strict refusal. There is no native-to-legacy reverse
conversion.
workspace convert does not create a fitted predictor from historical result
metadata or prediction arrays. Those outputs can be inspected after conversion,
but they are not a model archive: load_session() expects a qualified .n4a
bundle, and new-sample predict() requires a fitted model artifact. A conversion
report showing preserved predictions must not be read as proof that prediction
without retraining is available for that source profile.
Passing the converted workspace directory to nirs4all.predict(model=..., ...)
now returns an explicit converted_workspace_has_no_fitted_model capability
error for both the default DAG-ML engine and explicit legacy engine. Provide a
separately qualified fitted .n4a archive, or retrain before predicting new
samples. This refusal is qualified on the synthetic legacy save fixture; opaque
preserved bundles need their own qualification.
The authoritative read/write/migrate dispositions and retention window are in the Tools support matrix and support SLA.
Public API examples
An ordinary general Python run does not need an engine selector. The actual
engine is available as result.execution_engine and in result.per_dataset.
For a strict portable request, select engine="native" explicitly. Its current
training contract requires a list pipeline, a splitter, a supported Methods
model and dataset={"X": X, "y": y, "sample_ids": ids}. Chart generation,
workspace/project/cache integration and broader host operators are separate
general-profile capabilities, not portable archive guarantees.
An omitted save_charts uses the selected profile’s default (false for
portable, true for general DAG-ML/explicit legacy). Explicit options are never
silently discarded. Both profiles accept verbose=0..3.
The general profile restores concrete pipelines without a splitter through a
single native REFIT phase: all training rows are fitted once, and test rows
are never fitted. There is no cross-validation score (cv_best_score is NaN).
When a test partition exists, the historical validation alias points to this
test partition, with a warning and persisted evaluation metadata; it is not
an independent model-selection holdout. Generator/HPO variants of this
no-splitter profile remain under qualification.
Concrete pipeline chart commands are presentation-only. Spectra after a
transform are generated from the captured full-training refit transform,
without a new fit; they are explicitly not out-of-fold features. Saved charts
include an adjacent HTML description, exact numeric CSV inputs, and scored
fold membership JSON under workspace/charts. Visible-only charts print a
text alternative. Branch/source/augmentation stage snapshots not yet captured
are refused explicitly rather than rendered from a misleading raw substitute;
this remains an open general-profile parity gap.
Captured general-profile .n4a models replay through a native DAG PREDICT
phase with a Python scientific host. Public predict() and trained/loaded
Session.predict() do not train again; their metadata records the selected
engine, input cohort, source artifact fingerprint and training_performed=False.
Inference does not manufacture validation scores. An explicit engine="native"
still requires a portable Core archive and never silently loads a host model.
General archives contain trusted Python/joblib objects: load them only from a
trusted producer. Recorded SHA-256 digests detect corruption, not malicious
producers. Older DAG exports without a recorded digest retain a visible warning
and do not claim verified integrity. Loaded Sessions are bound to their source
archive fingerprint and refuse replacement after loading.
General retrain(mode="full") also accepts a recorded workspace prediction
dictionary, or a captured-winner archive without train_pipeline.json. It
clones the selected estimator, its preprocessing pipeline and target transform,
then fits them on all new training rows once through DAG-ML. It does not repeat
the original generator/HPO search or reuse learned estimator objects; frozen
clones retaining source estimators are refused. There is no new CV score for
this full-training operation. Lineage records the source fingerprint, integrity
status and training contract. Existing concrete bundles with a training
specification retain their published specification-replay behavior, including
its explicit splitter. Prediction-only fusion wrappers and fine-tuning remain
separate unqualified retrain paths.
General retrain(mode="transfer") accepts those captured workspace predictors
and host archives, including archives with a training specification. It freezes
the captured X/y preprocessing in private copies and trains only a fresh final
model on the new training rows, optionally replacing it with new_model.
It never replays the original search, refits the captured transforms or invents
a CV score. Export/replay retain that frozen preprocessing, and lineage marks
its deliberate state reuse separately from the fresh model. A subsequent full
retrain removes these transfer freezes and trains all steps afresh. The explicit
portable native selector and the historical opt-in transfer plugin keep their
separate contracts; neither is an implicit fallback from this DAG host path.
When Studio supplies store_run_id, the library attaches its pipeline results
to that existing running workspace run without completing or failing the parent
run; Studio retains that lifecycle. should_stop is cooperative cancellation:
checked before execution, between native scientific tasks, and before publishing
results. It does not interrupt an individual BLAS/estimator fit mid-call.
General sklearn-compatible model steps support train_params estimator
overrides and refit_params overrides on top of them during REFIT only.
These follow the historical controller’s set_params contract, not arbitrary
fit(**kwargs). Controller verbose emits training diagnostics without
overwriting the estimator’s verbosity setting. Unknown keys are diagnosed;
the historical controller’s silent ignoring of them is not reproduced.
Overrides participate in every inner HPO evaluation as well as the final fit;
provenance distinguishes proposed parameters from effective parameters when an
override replaces a searched value. CV preprocessing remains fold-local.
For {"model": ..., "na_policy": "replace", "fill_value": v} and
{"preprocessing": ..., "na_policy": "replace", "fill_value": v},
the general DAG-ML profile inserts a stateless transform immediately before
the declared step. It replaces NaNs in both training and later prediction
data, and the transform is retained in exported .n4a predictors. This
step-local replacement is equivalent to filling the input at that point in the
pipeline; other preprocessing modifiers still require separate qualification.
Simple CV/full-training, export/replay, HPO, and native nested stacking controls
are qualified. A closed SGD CV-weight initialization profile
supports an explicitly named native fold and compatible full-train REFIT through
public sklearn coefficient/intercept initializers. It resets optimization
counters and does not enable general estimator or deep-learning warm starts.
Specialized GPU reset, fit-influence/AOM policies, and the old by-source
prediction-stacking route still require separate scientific validation.
General model-local finetune_params with approach="single", "grouped",
or "individual" can use the
host Optuna profile (engine="optuna" inside finetune_params, or inferred
from historical sampler/trial controls). Optuna proposes parameters; DAG owns
the bounded trial loop, native candidate scores and selection. Each outer
training scope gets its own inner parameter search, and the final
REFIT gets a separate search on the full training partition. Single/individual
searches use a training-only 80/20 holdout, with separate studies per outer fold.
Grouped search clones the configured splitter inside each outer-training scope,
including its explicit group constraints; its candidate parameters are shared
across those inner folds. Without a configured splitter, historical grouped
search uses the same training-only holdout. Preprocessing and
target transforms are fitted inside each inner training split. This fixes the
earlier global-preprocessing/global-parameter leakage, so selected parameters
and CV values need not equal that historical implementation.
Development main supports the same scoped host search inside global
multimodal tuning for direct models and source-bound base models. A local
subcampaign reconstructs unfitted upstream declarations with the current
global candidate parameters; it never uses an outer fitted transform to
select local parameters. Direct source blocks and branch source bindings stay
typed and identity-aligned. Overlapping global/local parameter owners refuse
before opening an optimizer, and global checkpoints bind the local search
configuration and graph recipe. Meta-model local HPO remains refused: selecting
parameters from a precomputed OOF matrix would leak inner selection targets.
This development feature does not expand the closed portable native profiles
or enable CV-weight warm starts.
Trial counts, seeds, the installed optimizer’s sampler grammar and verbosity
are honored. Its historical Grid-to-TPE conversion for noncategorical ranges
remains visible; provenance records the effective sampler. General exports
retain the searches and the selected fitted predictor, not a resumable Optuna
study or a Methods checkpoint. They remain trusted Python artifacts, not
portable Core packages. Grouped best, mean (alias avg) and robust_best
reduce the native per-fold scores explicitly: mean RMSE is not pooled OOF RMSE.
Best honors the metric direction; robust_best never suppresses a failed trial
or invents a penalty score. Source rows and the inner splitter remain in the
search provenance. Progressive pruning, parallel trials, persistent studies and
multi-phase host searches still require
qualification; deterministic DAG parameter grids keep their separate contract.
Classification repetition voting preserves the native raw/CV selection scores.
Each fitted CV/REFIT task captures its real class probabilities, when the
classifier provides them, with explicit class and sample identities. The
library’s existing ensemble and repetition aggregators produce the additional
train/test/aggregate presentation rows without fitting again. These rows carry
classification_evidence or aggregate_evidence metadata; their display scores
are not new native selection reports. A classifier without predict_proba
uses its real hard predictions, never fabricated one-hot probabilities.
In subprocess execution the same evidence travels in a host-only capture
sidecar, separate from the coordinator’s native result protocol.
Directory-backed Python models such as AutoGluon use a host-only archive
extension. Native result artifacts and general .n4a bundles keep a small
joblib model reference and store each predictor file under host_artifacts/.
The manifest records every relative filename, byte length and SHA-256 digest.
The reader verifies all declared files before deserializing the model and
rejects missing, extra or changed archive sidecars. Files are copied and hashed
in bounded chunks; the 512 MiB inline joblib limit does not apply to their
combined size. Earlier archives with an inline predictor directory still load.
These files remain trusted Python host artifacts, not portable Core packages.
General generate() and its convenience methods select the installed
nirs4all.python.synthesis.v1 library adapter when no engine/plugin selector
is supplied or engine="dag-ml" is requested. This reuses the scientific synthesis builder, without a legacy
ML runner, HTTP backend or retry. generate.preflight() exposes that host
contract; explicit engine="native" remains strict and unsupported for
synthesis. An explicitly requested unknown plugin is not substituted.
General explain() similarly selects nirs4all.python.shap.v1 for the
default or explicit engine="dag-ml" route. It accepts a
captured general .n4a, a trained DAG result, or a persisted result.best
selection and explains the full REFIT predictor without training it again.
Preprocessing and inverse target transforms remain inside that predictor.
output_index selects one target or class-probability column (default 0);
the returned two-dimensional SHAP values describe that output, not all classes.
Kernel SHAP operates on the complete input predictor and can be expensive on
wide spectra: n_samples limits background rows, while sample_indices chooses
the rows to explain. Recorded headers and relation aggregation levels are
preserved rather than labelling an aggregate as a raw observation.
CSV values, provenance and an HTML table are written before optional figures;
visualizations=[] disables figures. This does not claim portable Core SHAP
or saved individual CV-fold models. Explicit engine="native" remains strict.
For a qualified request:
import nirs4all
result = nirs4all.run(pipeline=pipeline, dataset=dataset)
result.export("model.n4a")
prediction = nirs4all.predict("model.n4a", new_data)
with nirs4all.session() as session:
second = nirs4all.run(pipeline=other_pipeline, dataset=dataset, session=session)
Unsupported native shapes are rejected during preflight; they are not retried through legacy. The rollback path is deliberately different and visible:
rollback_result = nirs4all.run(
pipeline=pipeline,
dataset=dataset,
engine="legacy",
)
Do not put that selector in Studio or Web configuration. See the native capability preflight and public interfaces references for the operation-by-operation capability contract.
ABI and schema identities
Surface |
V1 candidate identity |
Compatibility rule |
|---|---|---|
Methods C ABI |
|
Call |
Methods model payload |
N4MM format |
A fitted-model payload, not a complete |
Portable model/run archive |
Core Archive V2 |
Validate format, bounds, Methods identity and fingerprint before replay; no refit or legacy fallback on import. |
Workspace |
|
SQLite metadata plus Parquet array sidecars; legacy stores require explicit Tools conversion. |
Runtime envelopes |
|
Producers and consumers use the frozen JSON schemas; no product-specific reinterpretation. |
Studio product contract |
Studio V1 HTTP/OpenAPI/WS snapshots |
Rust is the sole control-plane owner; plugin-host responses cross bounded JSON stdio only. |
The Methods ABI reference and the ecosystem runtime schemas are the lower-level authorities. This guide does not redefine them.
Capability summary
Request |
Strict product behavior |
|---|---|
|
Native for the qualified V1 matrix; general DAG-ML sessions and captured host archives support training and replay. Unsupported shapes refuse before significant work. |
Full retrain |
DAG-ML for concrete archive specs and captured trainable winners (workspace prediction or archive); other modes remain under qualification. |
Transfer |
DAG-ML for captured trainable host winners; the explicit historical Python-library plugin retains its separate preflight. |
Model-local |
Public DAG-ML execution supports deterministic generation and scoped Optuna/n4m host search, with training/refit controls. The closed Archive V2 native profile has narrower capabilities; unsupported requests refuse before execution. See pipeline keywords. |
Continuing from trained weights, unavailable explain/generate shapes |
Explicit refusal for unsupported requests; no implicit legacy execution. |
Existing Python workflow during rollback |
Direct Python call with explicit |
FAQ
Does Studio need Python?
Not as a backend. Studio’s HTTP, WebSocket, store, jobs and scheduler are Rust. A packaged CPython may exist only for an explicitly selected, preflighted library/plugin call over bounded stdio.
Can Studio fall back to the old backend?
No. An unavailable native or plugin capability returns a typed refusal. The explicit legacy rollback profile is a direct Python-library feature only.
Can I open an old workspace in place?
No. Inspect it with Tools, convert to a new disjoint directory, verify the output, and retain the original for rollback.
Is exit code 10 a clean conversion?
No. It means the command completed but preserved unsupported content opaque. Read the migration and unsupported-content reports before using the output.
How long are legacy readers supported?
The Tools 0.x reader line is guaranteed through both complete releases after
the R2 flip: R3 and R4. Retirement is post-V1 only, after an announced
governance decision and migration notice.
Does this page mean V1 is available?
No. R4 remains held until the product lock and remaining release gates are green. Published component packages and passing candidate tests do not amount to a stable V1 release.