C ABI¶
The ABI is designed around opaque host handles. Rust owns the control lifetime; the host owns the underlying object behind each handle.
When a C entry point fails with a DagMlError, error_out carries the ADR-11
descriptor JSON shape: category, code, severity, message,
remediation_hint and context. Null-pointer and malformed-input preflight
errors still use the legacy human string because callers often branch on the
status code before parsing a payload.
Current Scaffold¶
crates/dag-ml-capi/include/dag_ml.h exposes:
version and string-free helpers;
owned byte release helper for JSON outputs returned by Rust;
owned row-major
DagMlF64Tensorand column-majorDagMlF64ColumnarTensorrelease helpers for Rust-allocated prediction buffers returned to host bindings;dagml_graph_spec_contract_jsonanddagml_graph_validate_jsonfor GraphSpec contract discovery and graph validation before plan building;dagml_model_input_spec_contract_json,dagml_model_input_spec_validate_json,dagml_data_plan_contract_jsonanddagml_data_plan_validate_jsonfor non-Rust bindings that need to exchange neutral data/model compatibility requests and data-planner answers;dagml_controller_manifest_validate_jsonanddagml_controller_manifest_list_validate_jsonso bindings can preflight controller manifests and registry uniqueness before plan building;custom aggregation-controller task/result contract discovery and validation, including result-vs-task checks that preserve requested sample/unit order, fold scope, prediction level and target names before a host aggregation controller result can be trusted by the coordinator;
the generic controller vtable
invokepath is also used for runtime custom aggregation dispatch: bindings receive anAggregationControllerTaskJSON and return anAggregationControllerResultJSON, after which Rust validates the result against the exact task;dagml_data_output_provenance_contract_jsonanddagml_data_output_provenance_validate_jsonso bindings can discover the reservedDataProviderViewSpec.extrakey and validate propagated data-view provenance before using it;dagml_node_result_validate_for_task_jsonso host bindings can preflight a controller-producedNodeResultagainst the exactNodeTaskbefore handing the JSON back to the scheduler;dagml_pipeline_dsl_contract_jsonanddagml_pipeline_dsl_validate_jsonso bindings can discover and preflight the canonicalPipelineDslSpecand serialized nirs4all-compatible list/dict DSL input profiles;dagml_pipeline_dsl_compile_jsonfor pure compilation of that DSL input surface into canonicalGraphSpecJSON, plusdagml_pipeline_dsl_compile_artifact_jsonwhen bindings also need the extractedGenerationSpecincluding coordinated override dimensions, validated shape-plan fragments, validated data-binding fragments, a campaign template and search-space fingerprint, anddagml_pipeline_dsl_execution_plan_build_jsonwhen bindings want the Rust planner to build a validatedExecutionPlandirectly from DSL and controller manifests;dagml_graph_parallel_levels_jsonfor deterministic node batches that bindings can use to prepare parallel schedulers;dagml_execution_plan_build_jsonfor compiling graph/campaign/controller manifests into anExecutionPlanwhile Rust owns planner validation;dagml_execution_plan_schedule_jsonfor exporting deterministic phase/variant/fold node-level schedules from a compiledExecutionPlan;selection policy/decision validation and candidate selection JSON helpers;
sample-level and target/group aggregated prediction block conversion helpers that validate canonical JSON blocks and return contiguous row-major
doublebuffers with explicitrows,cols,lenandcapacity;prediction-cache payload tensor export that validates a cache payload set against its bundle before returning contiguous row-major F64 values plus JSON metadata for requirement key, prediction level, block offsets, folds and sample/unit ids. The metadata JSON is versioned by
DAG_ML_PREDICTION_CACHE_TENSOR_METADATA_SCHEMA_VERSIONand documented indocs/contracts/prediction_cache_tensor_metadata.schema.json;prediction-cache payload columnar tensor export that returns contiguous column-major F64 values with explicit
column_offsets, versioned byDAG_ML_PREDICTION_CACHE_COLUMNAR_TENSOR_METADATA_SCHEMA_VERSIONand documented indocs/contracts/prediction_cache_columnar_tensor_metadata.schema.json;execution bundle validation, replay-envelope validation, replay-request validation and prediction-cache payload validation helpers;
dagml_research_provenance_export_jsonfor building the standards-facingResearchProvenanceExportJSON over validated execution plans, bundles, optional lineage records, replay envelopes, prediction-cache manifests and artifact manifests; null pointer plus zero length denotes an omitted optional input;dagml_openlineage_run_event_jsonfor building the same validated provenance evidence as an OpenLineageRunEventJSON with explicit namespace and event time byte views;vtable replay execution helper that composes host controllers, host data provider, host artifact store and optional host prediction-cache store while Rust owns scheduling and validation;
replay-request validation can optionally include an OOF prediction-cache payload set, which is required for OOF-dependent
REFITreplay;mock replay execution helper that returns a JSON summary while exercising Rust-side data handle materialization, data view creation and artifact handle materialization;
Arrow C Data
ArrowArrayandArrowSchemastructs for controller predictions and data-provider identity/target/feature exports;DagMlControllerVTablefor host operator controllers, including genericinvokeoverNodeTask/NodeResultJSON and explicit returned-byte release. Controller vtable ABI v2 keeps borroweduser_datasemantics; v3 is opt-in owned semantics where Rust callsdestroy(user_data)after releasing controller-owned result handles. The planned multitask extension keeps the same ownership shape: an optionalinvoke_batchcarries a validatedTaskBatchRequestJSON and returns one logicalNodeResultper member task plus optional parent batch provenance;DagMlDataVTablefor host data providers, includingmaterialize,make_view,view_identity,target_arrowandfeature_arrow.feature_arrowremains ABI-compatible: hosts may receive either a plain feature-set id or a JSON fusion selector understood bydag-ml-dataproviders. The vtable uses the sharedDAG_ML_DATA_PROVIDER_VTABLE_ABI_VERSIONmacro and guardedDagMlDataVTabledefinition sodag_ml.handdag_ml_data.hcan be included together by bindings.DagMlArtifactStoreVTablefor host replay artifact stores, returning typedDagMlHandleRefvalues for model/artifact handles. Artifact references are JSON-level Rust contracts with optional typed backend, URI, content fingerprint and plugin/version metadata; the ABI still transports them inside owned JSON payloads so C structs do not freeze a storage layout too early. Artifact-store vtable ABI v1 is borrowed; v2 is opt-in owned lifecycle withdestroy(user_data)after materialized artifact handles are released.DagMlPredictionCacheVTablefor host prediction-cache stores, includingload_blocks,materializeand explicit returned-byte release.load_blocksis the single JSON load callback for replay prediction requirements: sample-level requirements returnPredictionBlock[], while target/group requirements returnAggregatedPredictionBlock[]keyed by typedPredictionUnitIdvalues. Rust selects and validates the expected block shape from the bundle requirement before materializing a prediction handle. Prediction-cache vtable ABI v1 is borrowed; v2 is opt-in owned lifecycle withdestroy(user_data)after materialized prediction handles are released.docs/contracts/process_adapter_description.schema.jsondocuments the required process-adapter--describeJSON used by CLI-managed host adapters. The C ABI exposes its schema version and schema id throughdagml_process_adapter_description_contract_json.docs/contracts/process_adapter_frame.schema.jsondocuments thecontrol_frames_v1JSONL protocol used by persistent process adapters: coordinatorinit/task/closerequest frames and adapterack/result/errorresponse frames wrapping the publishedNodeTask/NodeResultcontracts. The C ABI exposes the frame schema version and schema id throughdagml_process_adapter_frame_contract_json.docs/contracts/graph_spec.schema.jsondocuments the canonical graph JSON consumed by the planner and C ABI.docs/contracts/model_input_spec.schema.jsonanddocs/contracts/data_plan.schema.jsondocument the neutral data-shape contracts exchanged between controller descriptions, data planners and host bindings.
The vtables are intentionally small in this scaffold. They establish shape, ownership and naming before full execution is implemented.
DagMlStatusCode is a fixed uint32_t ABI value rather than a C/Rust enum
boundary type. Unknown host status codes are treated as runtime validation
errors instead of being decoded as Rust enum discriminants.
Vtable user_data lifetime is explicit per ABI surface. Controller vtable v2 is
borrowed for backwards compatibility, while controller vtable v3 opts into
Rust-owned lifecycle and calls destroy(user_data) after handle release on drop.
Artifact-store and prediction-cache vtable v1 are also borrowed; their v2
surfaces opt into Rust-owned lifecycle with destroy(user_data) after
materialized handles are released. Data-provider vtables remain borrowed in the
current replay API because that ABI is shared with dag-ml-data. Rust releases
controller-result, data/view, replay-artifact and prediction-cache handles that
it receives or materializes through the vtables.
Ownership Rules¶
Object |
Owner |
Release path |
|---|---|---|
Host data block |
Host |
|
Host controller result JSON |
Host allocation returned through controller vtable |
|
Host fitted model |
Host |
|
Host replay artifact handle |
Host |
|
Host prediction cache handle |
Host |
|
Rust error string |
Rust allocation returned through ABI |
|
Rust JSON byte output |
Rust allocation returned through ABI |
|
Rust row-major F64 tensor output |
Rust allocation returned through ABI |
|
Rust column-major F64 tensor output |
Rust allocation returned through ABI |
|
Host JSON byte output |
Host allocation returned through prediction-cache vtable |
|
Arrow arrays |
Producer of the Arrow array |
Arrow C Data Interface release callback |
JSON blobs |
Caller-provided view unless returned as owned bytes |
ABI-specific free function |
ABI Roadmap¶
Freeze
DagMlBytesView,DagMlOwnedBytes,DagMlF64Tensor,DagMlF64ColumnarTensor, handle and status conventions.Add schema coverage for the remaining execution-plan and bundle sub-blobs that are still Rust-implicit.
Add conformance tests that call the C ABI from a small C program.
Add C conformance tests that drive non-mock replay through the vtable surface.
Keep shared
dag-ml-dataheader inclusion and vtable ABI conformance in CI.Add host adapters for Python and native C++ controllers and artifact stores.