Pipeline Runner

This section covers the structured abacus.pipeline runner: how it loads a config and dataset, executes the retained stage sequence, and writes reproducible run artefacts to disk.

Pages

  • Runner Overview - How run_pipeline(...) works, which stages run, and when the optimisation stage is skipped.
  • YAML Configuration - Which YAML keys the runner consumes and how they map to model build, data loading, holidays, and optimisation.
  • CLI Reference - The thin python -m abacus.pipeline.runner interface and its supported flags.
  • Output Directory Schema - The run directory layout, manifest schema, stage statuses, and main artefacts.
  • Extending the Runner - How to add a stage or wire in reporting without bypassing the manifest and artifact helpers.

Subsections of Pipeline Runner

Runner Overview

Use the pipeline runner when you want a full disk-backed PanelMMM run instead of only an in-memory fit.

The runner loads a YAML config and a CSV dataset, builds the model, executes a fixed stage sequence, and writes each stage’s artefacts into a structured run directory. When validation is enabled, the runner performs a second train-window fit for the blocked holdout stage, so the run takes longer than a pure full-sample fit.

If you want a quick first run, start with Quickstart: Pipeline Runner.

Public entry points

The public Python API is:

  • abacus.pipeline.PipelineRunConfig
  • abacus.pipeline.run_pipeline
  • abacus.pipeline.PipelineRunResult

The thin CLI wraps the same code path:

python -m abacus.pipeline.runner --config path/to/config.yml

Basic Python example

from pathlib import Path

from abacus.pipeline import PipelineRunConfig, run_pipeline

result = run_pipeline(
    PipelineRunConfig(
        config_path=Path("data/demo/geo_panel/config.yml"),
        output_dir=Path("results"),
        run_name="geo_panel_baseline",
        prior_samples=10,
        draws=500,
        tune=500,
        chains=2,
        cores=2,
        random_seed=42,
        curve_samples=100,
        curve_points=100,
    )
)

print(result.run_dir)
print(result.manifest_path)

PipelineRunResult contains:

Field Meaning
run_dir The created run directory
manifest_path The path to run_manifest.json inside that directory

What the runner does

run_pipeline(...) performs these steps:

  1. Load the YAML config with load_yaml_config(...).
  2. Load X and y from CSV using load_pipeline_data(...).
  3. Merge CLI sampler overrides with YAML fit through build_model_kwargs(...).
  4. Create the output directory tree and initialise run_manifest.json.
  5. Run the retained stages in order, updating the manifest after every stage.

The model is built in Stage 00 by build_mmm_from_yaml(...), then stored in the shared PipelineContext for the remaining stages. Runner-only roots such as diagnostics and validation stay on the pipeline context and are stripped before the public MMM builder validates the model YAML.

Stage order

The runner uses a fixed stage list.

Stage key Directory Purpose Optional
metadata 00_run_metadata Build the model and write resolved config and dataset metadata No
preflight 10_pre_diagnostics Prior predictive draws and plot No
fit 20_model_fit Fit the model, save InferenceData, write trace and summary No
assessment 30_model_assessment In-sample posterior predictive checks, fitted values, residual outputs No
validation 35_holdout_validation Blocked holdout scoring on a train-window refit Yes
decomposition 40_decomposition Contribution tables and decomposition plots No
diagnostics 50_diagnostics Raw input screening, MCMC, predictive, and residual diagnostics No
curves 60_response_curves Saturation-only, forward-pass direct contribution, and adstock curve artefacts No
optimisation 70_optimisation Budget optimisation artefacts Yes

The validation stage is marked skipped when the YAML config does not contain validation or it is disabled. The optimisation stage is also optional; it returns None and is marked skipped when the YAML config does not contain an optimization block.

See Output Directory Schema for the stage folders and artefact layout.

Data and model assumptions

The retained runner is designed around PanelMMM.

  • The flow-oriented public YAML is expected to describe a PanelMMM.
  • The data loader reads CSV only.
  • Later stages call PanelMMM plotting, summary, diagnostics, and optimisation methods directly.

If you need the exact YAML keys, see YAML Configuration.

PipelineRunConfig

PipelineRunConfig controls runtime settings that sit outside the YAML model specification.

Field Purpose
config_path YAML file to load
output_dir Root directory under which the run directory is created
run_name Optional run-name override; otherwise the config filename stem
dataset_path Optional combined dataset CSV override
x_path, y_path Optional feature and target CSV overrides
holidays_path Optional holiday CSV override
target_column Target column name used during CSV loading
prior_samples Number of prior predictive samples for Stage 10
draws, tune, chains, cores, random_seed Sampler overrides merged onto YAML fit
curve_samples, curve_points Curve sampling settings for Stage 60

Only sampler settings are merged into model construction. Other overrides are used by the runner itself during data loading, holiday resolution, diagnostics reporting, and output setup.

Run directory naming

The runner creates the run directory as:

<output_dir>/<effective_run_name>_<YYYYMMDD_HHMMSS>

The timestamp is generated in UTC.

All stage directories are created up front, even if a later stage is skipped or the run aborts.

Failure and skip behaviour

If a stage raises an exception:

  • the current stage is marked failed
  • the run manifest is marked failed
  • all still-pending later stages are marked not_reached
  • run_pipeline(...) re-raises the exception

If a stage returns None:

  • the stage is marked skipped
  • the manifest warning records that no configuration was supplied for that optional stage

Reporter hook

run_pipeline(...) accepts an optional reporter that implements the PipelineReporter protocol.

The reporter can observe:

  • pipeline start
  • stage start
  • stage end
  • pipeline end
  • pipeline failure

See Extending the Runner for the callback contract.

YAML Configuration

The pipeline runner reads the same YAML model specification used by build_mmm_from_yaml(...), then adds a small set of runner-specific conventions for data loading, optional blocked holdout validation, and Stage 70 optimisation.

This page documents the keys that the runner actually consumes.

Root keys

Key Required Used for
data Usually Resolve dataset paths when you do not pass dataset_path, x_path, or y_path through PipelineRunConfig
target Yes Define the target column and business target type
dimensions No Declare panel-dimension columns such as geo or brand
media Yes Define channel/control columns and transform types
scaling No Configure target/channel scaling rules
effects No Append additive effects in YAML order before build_model(...)
priors No Override model-level priors and prefixed transform priors
fit No Default sampler settings for Stage 20 fitting
holidays No Add holiday events before model build
original_scale_vars No Add original-scale contribution variables before fitting
inference_data No Attach existing InferenceData when the file exists
validation No Enable optional Stage 35 blocked holdout validation
optimization No Enable Stage 70 budget optimisation
diagnostics No Override Stage 50 runner diagnostics thresholds

Minimal runner config

data:
  dataset_path: dataset.csv
  date_column: date

target:
  column: revenue
  type: revenue

dimensions:
  panel: [geo]

media:
  channels: [channel_1, channel_2]
  adstock:
    type: geometric
    l_max: 4
  saturation:
    type: logistic

fit:
  draws: 1000
  tune: 1000
  chains: 4
  cores: 4
  random_seed: 42

Relative paths in YAML are resolved relative to the YAML file’s directory.

diagnostics is runner-only. The structured pipeline reads it, but build_mmm_from_yaml(...) still validates only the public MMM model schema.

validation is also runner-only. The structured pipeline reads it for Stage 35 blocked holdout scoring, but the public MMM YAML builder never sees it.

Core modeling blocks

The runner always builds a PanelMMM, so the public YAML no longer exposes a model.class field. Instead, it reads:

  • data.date_column
  • target.column
  • target.type
  • media.channels
  • media.controls, if any
  • dimensions.panel, if any
  • media.adstock
  • media.saturation
  • fit

data

The runner loads data before building the model. It supports two CSV layouts.

Combined dataset

data:
  dataset_path: "dataset.csv"

The runner reads the CSV, removes the target column from X, and uses that column as y.

Separate feature and target files

data:
  x_path: "X.csv"
  y_path: "y.csv"

When loading y_path:

  • if the configured target column exists, the runner uses that column
  • otherwise, if the file has exactly one column, the runner uses that column and renames it to the target name

Target column resolution

The runner resolves the target column in this order:

  1. PipelineRunConfig.target_column or CLI --target-column
  2. target.column
  3. "y"

Use the CLI override only when you want to change how the runner reads the CSV. Keep it consistent with target.column in YAML.

fit

fit controls Stage 20 fitting because the fit stage calls:

context.model.fit(X=context.X, y=context.y, progressbar=False)

The runner merges these CLI or PipelineRunConfig overrides onto the YAML fit block when they are provided:

  • draws
  • tune
  • chains
  • cores
  • random_seed

The public YAML schema currently supports these fit keys:

  • draws
  • tune
  • chains
  • cores
  • random_seed
  • target_accept
  • progressbar
  • compute_convergence_checks

Unknown fit keys are rejected when the YAML is loaded.

effects

effects is an optional list of additive effect specifications:

effects:
  - type: linear_trend
    prefix: trend
    n_changepoints: 8
  - type: weekly_fourier
    order: 3

The builder appends each effect to model.mu_effects in YAML order before calling build_model(...).

holidays

The holidays block is optional.

Supported keys used by the builder include:

Key Meaning
path Holiday CSV path
enabled Set to false to disable holiday loading
prefix Prefix for generated holiday effect coordinates
countries Optional country filter for catalogue-style holiday CSV input

Example:

holidays:
  path: "holidays.csv"
  prefix: "holiday"

The CLI or PipelineRunConfig.holidays_path overrides holidays.path.

If you omit both path and the override but still configure holidays, Abacus falls back to the bundled abacus.data:holidays.csv.

original_scale_vars

Use original_scale_vars when you want specific contribution variables to be available on the original target scale:

original_scale_vars:
  - channel_contribution
  - y

The builder applies these through model.add_original_scale_contribution_variable(...) before fitting.

inference_data

inference_data.path is passed through to the YAML builder. If the file exists, Abacus attaches that InferenceData to the built model during Stage 00.

Important: the structured runner still executes Stage 20 and fits the model again. inference_data.path does not currently skip fitting.

optimization

Add an optimization block when you want Stage 70 to run. If this block is absent, Stage 70 is marked skipped.

The YAML builder validates this block when the config is loaded. The required scalar fields below must be present, and unknown top-level optimization keys are rejected.

Required keys:

optimization:
  start_date: "2024-11-11"
  end_date: "2025-01-27"
  total_budget: 1289000000.0

Optional keys read by Stage 70:

Key Default Meaning
response_variable total_media_contribution_original_scale Optimisation objective variable
budget_distribution_over_period None Time weights over the optimisation window
budget_bounds Derived or default Explicit spend bounds
spend_constraint_lower 0.3 when deriving bounds Relative lower bound around scaled reference spend
spend_constraint_upper 0.3 when deriving bounds Relative upper bound around scaled reference spend
default_constraints true Whether to add the default equality budget constraint
noise_level 0.001 Noise level for simulated response samples
include_last_observations false Whether posterior predictive sampling includes trailing observed rows
include_carryover true Whether simulated response sampling extends the window for carryover

Important budget-unit note

In the structured pipeline, optimization.total_budget is passed straight to PanelBudgetOptimizerWrapper.optimize_budget(...).

That means Stage 70 uses the wrapper’s per-period spend contract, not the scenario planner’s total-horizon spend contract.

See Budget Optimisation.

Xarray-like optimisation values in YAML

For panel bounds or time distributions, use the xarray-like mapping shape that Stage 70 expects:

optimization:
  start_date: "2025-02-03"
  end_date: "2025-02-24"
  total_budget: 100000.0
  budget_distribution_over_period:
    values:
      - [[0.25, 0.25], [0.25, 0.25]]
      - [[0.25, 0.25], [0.25, 0.25]]
      - [[0.25, 0.25], [0.25, 0.25]]
      - [[0.25, 0.25], [0.25, 0.25]]
    dims: ["date", "geo", "channel"]
    coords:
      date: [0, 1, 2, 3]
      geo: ["UK", "FR"]
      channel: ["channel_1", "channel_2"]

The same shape works for budget_bounds, but with an additional "bound" dimension containing "lower" and "upper".

diagnostics

Use the optional diagnostics block when you want Stage 50 to use different warn/fail thresholds than the retained defaults.

diagnostics:
  thresholds:
    design_max_vif:
      warn: 8.0
      fail: 12.0
    mcmc_max_rhat:
      warn: 1.02
      fail: 1.08

Supported threshold keys:

  • design_max_vif
  • design_condition_number
  • mcmc_divergence_count
  • mcmc_max_rhat
  • mcmc_min_ess_bulk
  • mcmc_bfmi_min
  • predictive_nrmse
  • residual_ljung_box_p
  • residual_max_abs_acf

Validation rules:

  • upper-bound checks require warn <= fail
  • lower-bound checks require warn >= fail
  • omit the block entirely to use the built-in defaults

This block affects only the structured runner. It is stripped before Stage 00 model build so the public MMM YAML schema remains unchanged.

validation

Use the optional validation block when you want Stage 35 blocked holdout scoring.

validation:
  enabled: true
  holdout_observations: 8
  include_last_observations: true
  coverage_levels: [0.5, 0.8, 0.94]
  sampler:
    draws: 500
    tune: 500
    chains: 2
    cores: 2
    random_seed: 42

Supported keys:

Key Meaning
enabled Set to false to skip Stage 35 while keeping the stage in the manifest
holdout_observations Number of unique dates to reserve for the blocked holdout window
include_last_observations Keep lag history for carryover-sensitive holdout scoring
coverage_levels Coverage levels reported in Phase 10; use the fixed 50, 80, and 94 percent defaults
sampler Optional validation-only sampler overrides for the train-window refit

Phase 10 reports coverage as coverage_50, coverage_80, and coverage_94. Keep those defaults unless the implementation and tests are updated together.

The validation stage builds a clean train-window model for holdout scoring and ignores inference_data.path so the refit does not inherit attached posterior state from Stage 00.

Override precedence

For the runner, precedence is:

Setting Higher precedence Lower precedence
Combined dataset path dataset_path / --dataset-path data.dataset_path
Split CSV paths x_path, y_path / --x-path, --y-path data.x_path, data.y_path
Holiday CSV path holidays_path / --holidays-path holidays.path
Sampler settings PipelineRunConfig or CLI overrides fit
Target column for CSV loading target_column / --target-column target.column, then "y"
Diagnostics thresholds diagnostics.thresholds retained Stage 50 defaults

Common pitfalls

  • Using Parquet paths in the pipeline data block. The runner data loader reads CSV only.
  • Providing only one of data.x_path or data.y_path.
  • Treating optimization.total_budget as total horizon spend instead of per-period spend.
  • Assuming diagnostics is part of the public MMM builder schema. It is a runner-only block.
  • Assuming inference_data.path skips Stage 20 fitting. It does not.
  • Forgetting that relative paths are resolved from the YAML file directory, not from the shell working directory.

Output Directory Schema

Each pipeline run creates a timestamped directory under the configured output_dir:

<output_dir>/<run_name>_<YYYYMMDD_HHMMSS>

The timestamp is generated in UTC. The runner creates every stage directory up front, then updates run_manifest.json as stages start, complete, skip, or fail.

Directory tree

results/
  geo_panel_baseline_20260308_153000/
    run_manifest.json
    00_run_metadata/
    10_pre_diagnostics/
    20_model_fit/
    30_model_assessment/
    35_holdout_validation/
    40_decomposition/
    50_diagnostics/
    60_response_curves/
    70_optimisation/

Stage directories

Stage Directory Typical artefacts
metadata 00_run_metadata config.resolved.yaml, model_metadata.json, spec_summary.csv
preflight 10_pre_diagnostics prior_predictive.nc, prior_predictive.png
fit 20_model_fit model.nc, trace.png, posterior_summary.csv
assessment 30_model_assessment in-sample posterior predictive checks and residual outputs
validation 35_holdout_validation blocked holdout scoring, uncertainty-aware metrics, and residual diagnostics
decomposition 40_decomposition contribution CSVs and decomposition plots
diagnostics 50_diagnostics raw input screening, MCMC, predictive, and residual diagnostic reports
curves 60_response_curves saturation-only, forward-pass direct contribution, and adstock NetCDF, summaries, and plots
optimisation 70_optimisation allocation, response, optimisation summary, and bounds audit artefacts

See Runner Overview for the stage order and optionality.

Main artefacts by stage

00_run_metadata

Main files:

  • a copy of the original config under its source filename
  • config.original.yaml
  • config.resolved.yaml
  • session_info.txt
  • dataset_metadata.json
  • model_metadata.json
  • data_dictionary.csv
  • design_matrix_manifest.csv
  • spec_summary.csv
  • holiday_feature_manifest.csv when holidays are configured

config.resolved.yaml normalises configured data and holiday paths to absolute paths and records the effective sampler configuration on the model.

10_pre_diagnostics

Main files:

  • prior_predictive.nc
  • prior_predictive.png

20_model_fit

Main files:

  • model.nc
  • trace.png
  • posterior_summary.csv

30_model_assessment

Main files:

  • posterior_predictive.nc
  • posterior_predictive.png
  • posterior_predictive_summary.csv
  • observed.csv
  • fitted.csv
  • fit_timeseries.png
  • fit_scatter.png
  • residuals.csv
  • residuals_timeseries.png
  • residuals_hist.png
  • residuals_vs_fitted.png

This stage is the in-sample or training-fit assessment. It uses the same data the model was fit on and should not be read as the pipeline’s out-of-sample validation layer.

35_holdout_validation

Main files:

  • validation_metadata.json
  • holdout_posterior_predictive.nc
  • holdout_predictive_summary.csv
  • holdout_predictive_report.json
  • holdout_observed.csv
  • holdout_fitted.csv
  • holdout_residuals.csv
  • holdout_timeseries.png
  • holdout_residuals_acf.png

The holdout summary and report include uncertainty-aware metrics such as crps, bias, and fixed coverage columns for coverage_50, coverage_80, and coverage_94.

This stage is optional. When validation is absent or disabled in YAML, the directory still exists and the stage is marked skipped.

40_decomposition

Main files:

  • waterfall_components_decomposition.png
  • weekly_media_contribution.png
  • channel_contributions.csv
  • baseline_contributions.csv
  • mean_contributions_over_time.csv

50_diagnostics

Main files:

  • design_summary.csv
  • design_report.json
  • vif_report.csv
  • mcmc_summary.csv
  • mcmc_report.json
  • predictive_summary.csv
  • predictive_report.json
  • residual_diagnostics.csv
  • residuals_acf.png
  • diagnostics_report.csv
  • diagnostics_summary.txt
  • chain_diagnostics.txt

The design-oriented files are raw input screening outputs. In particular, diagnostics_report.csv labels the corresponding phase as raw_input_screening rather than design.

60_response_curves

Main files:

  • saturation_curve.nc
  • saturation_curve_summary.csv
  • saturation_curve.png
  • forward_pass_contribution_curve.nc
  • forward_pass_contribution_curve_summary.csv
  • forward_pass_contribution_curve.png
  • adstock_curve.nc
  • adstock_curve_summary.csv
  • adstock_curve.png

These artefacts are intentionally different:

  • saturation_curve.* is the sampled saturation transformation on the scaled channel axis, exported with original-scale contribution values for easier reading. The PNG overlays that saturation-only curve against posterior mean realised contributions.
  • forward_pass_contribution_curve.* is a full-model direct contribution artefact. It rescales the observed historical spend path from 0% to 200%, runs that spend through the fitted adstock and saturation path, and records the resulting total channel contribution in original target units.
  • adstock_curve.* is the sampled carryover-weight profile for one impulse.

70_optimisation

This directory is present for every run, but the stage is skipped unless the YAML config contains an optimization block.

Main files when the stage runs:

  • optimized_allocation.nc
  • optimized_allocation.csv
  • response_distribution.nc
  • optimize_result.json
  • budget_summary.csv
  • budget_response_points.csv
  • budget_impact.csv
  • budget_bounds_audit.csv
  • budget_roi_cpa.csv
  • budget_response_curves.csv
  • budget_mroi.csv
  • budget_optimisation.json
  • several PNG plots for allocation, contribution over time, response curves, impact, bounds audit, and ROI or CPA

run_manifest.json

The manifest is the machine-readable index for the whole run.

Top-level fields include:

Field Meaning
run_name Effective run name
timestamp UTC run timestamp
config_path Original config path
output_dir Run directory path
status Overall run status
model_class Set after Stage 00 builds the model
data Basic dataset metadata
stages Per-stage manifest records
warnings Run-level warnings
error Run-level failure payload when the pipeline aborts

data includes:

  • x_shape
  • y_length
  • target_column
  • x_columns

Stage records

Each stage record contains:

Field Meaning
directory Stage directory name
status Current stage status
started_at ISO timestamp when the stage started
finished_at ISO timestamp when the stage finished
artifacts Mapping of artefact labels to root-relative paths
warnings Stage warnings
error Error string when the stage fails

The artifacts mapping uses root-relative paths such as 20_model_fit/model.nc.

Stage statuses

Status Meaning
pending Stage has not started yet
running Stage is currently running
completed Stage finished successfully
skipped Stage returned None intentionally
failed Stage raised an exception
not_reached A previous stage failed before this one ran

Common cases:

  • Stage 35 is skipped when validation is missing or disabled from YAML.
  • Stage 70 is skipped when optimization is missing from YAML.
  • Later stages become not_reached after the first failure.

Practical use

Use the run directory when you want:

  • a stable folder for downstream reporting
  • a machine-readable audit trail through run_manifest.json
  • stage-level links to artefacts without hard-coding filenames

If you want to add new artefact types or stages, see Extending the Runner.

CLI Reference

The pipeline exposes a thin CLI through abacus.pipeline.runner.

Entry point

python -m abacus.pipeline.runner --config path/to/config.yml

On success, the CLI prints the final run directory:

Structured pipeline completed: results/my_run_20260308_153000

Arguments

Flag Required Default Meaning
--config Yes None YAML config path
--output-dir No results Root directory for pipeline runs
--run-name No Config filename stem Optional run-name override
--dataset-path No None Combined dataset CSV override
--x-path No None Feature CSV override when not using --dataset-path
--y-path No None Target CSV override when not using --dataset-path
--holidays-path No None Holiday CSV override
--target-column No None Target column used when reading CSV input
--prior-samples No 20 Prior predictive samples for Stage 10
--draws No None Posterior draws override
--tune No None Posterior tuning steps override
--chains No None Posterior chains override
--cores No None Posterior cores override
--random-seed No 42 Shared random seed
--curve-samples No 100 Posterior samples for Stage 60 curves
--curve-points No 100 Number of x-values for saturation curves

Common command patterns

Use the dataset path from YAML

python -m abacus.pipeline.runner \
  --config data/demo/geo_panel/config.yml

Override the combined dataset path

python -m abacus.pipeline.runner \
  --config configs/geo_panel.yml \
  --dataset-path /data/geo_panel_latest.csv \
  --run-name geo_panel_latest

Use separate feature and target files

python -m abacus.pipeline.runner \
  --config configs/panel.yml \
  --x-path /data/X.csv \
  --y-path /data/y.csv \
  --target-column revenue

Override sampler settings for one run

python -m abacus.pipeline.runner \
  --config configs/panel.yml \
  --draws 1000 \
  --tune 1000 \
  --chains 4 \
  --cores 4 \
  --random-seed 42

Override the holiday CSV

python -m abacus.pipeline.runner \
  --config configs/panel.yml \
  --holidays-path /data/holidays_uk_fr.csv

How CLI overrides interact with YAML

The CLI does not replace the full YAML config. It only overrides the runtime fields exposed through PipelineRunConfig.

Important behaviours:

  • --dataset-path takes precedence over data.dataset_path.
  • --x-path and --y-path take precedence over data.x_path and data.y_path.
  • --holidays-path takes precedence over holidays.path.
  • --draws, --tune, --chains, --cores, and --random-seed are merged onto YAML fit.
  • --target-column affects CSV loading. Keep it consistent with target.column in YAML.

Exit behaviour

The CLI exits with status 0 on success. On failure, the process exits non-zero with the underlying exception.

The pipeline stops at the first stage failure. It does not provide flags to:

  • run only a subset of stages
  • continue after a failed stage
  • disable individual built-in stages other than omitting the optional optimization block from YAML

See Runner Overview and YAML Configuration for the execution model and config surface.

Extending the Runner

The retained runner is static, not plugin-based. To add a stage or integrate custom status reporting, extend the existing runner surfaces instead of bypassing them.

Stage contract

A stage function has this contract:

def run_some_stage(context: PipelineContext) -> dict[str, str] | None:
    ...

Return values:

  • return a dict[str, str] of artefact labels to root-relative paths when the stage succeeds
  • return None when the stage is intentionally skipped
  • raise an exception when the stage fails and should abort the run

The runner handles manifest updates around the stage call. Do not update context.manifest directly from a normal stage implementation unless you are changing core runner behaviour.

What is available in PipelineContext

PipelineContext gives each stage access to:

Field Use it for
run_config Runtime settings such as output root, seeds, and curve sample counts
raw_cfg The loaded YAML config as a mutable mapping
X, y Loaded dataset inputs
paths Stage directories and manifest path
manifest Current run manifest
model_kwargs Effective sampler overrides passed into model build
model Built PanelMMM, available after Stage 00

Artifact helpers

Use the helpers in abacus/pipeline/artifacts.py:

  • write_json(...)
  • write_dataframe(...)
  • write_dataset(...)
  • write_idata(...)
  • write_text(...)
  • save_figure(...)
  • copy_file(...)

Use context.paths.relative(path) when building the artefact mapping that the stage returns. The manifest expects root-relative paths, not absolute paths.

Adding a new stage

To add a new built-in stage, update these places:

  1. abacus/pipeline/artifacts.py Add the stage directory name to STAGE_DIRECTORIES.
  2. abacus/pipeline/runner.py Add a PipelineStageSpec to PIPELINE_STAGE_SPECS.
  3. abacus/pipeline/runner.py Add the stage function to the stage_functions mapping inside run_pipeline(...).
  4. abacus/pipeline/stages/__init__.py Export the new stage helper if you want it available from the stage package.

Minimal stage example

from abacus.pipeline.artifacts import write_dataframe


def run_custom_stage(context):
    if context.model is None:
        raise ValueError("Model has not been initialized before the custom stage.")

    stage_dir = context.paths.stage_dirs["custom"]
    output_path = stage_dir / "custom_summary.csv"

    frame = context.model.summary.total_contribution(output_format="pandas")
    write_dataframe(output_path, frame)

    return {
        "custom_summary": context.paths.relative(output_path),
    }

Optional stage pattern

If a stage should only run when a config block is present, follow the same pattern as Stage 70:

def run_optional_stage(context):
    cfg = context.raw_cfg.get("my_optional_block")
    if cfg is None:
        return None
    ...

Returning None is what marks the stage as skipped in the manifest.

Failure semantics

If your stage raises an exception:

  • the stage is marked failed
  • the run is marked failed
  • later pending stages are marked not_reached
  • run_pipeline(...) re-raises the exception

That means stage code should only catch exceptions when it can recover locally and still produce a valid artefact set.

Adding structured reporting

If you want progress callbacks without changing the core stage code, implement a PipelineReporter and pass it to run_pipeline(...).

The reporter protocol methods are:

  • on_pipeline_start(...)
  • on_stage_start(...)
  • on_stage_end(...)
  • on_pipeline_end(...)
  • on_pipeline_error(...)

This is the right extension point for:

  • notebooks or dashboards that want progress updates
  • lightweight orchestration wrappers
  • structured logging around pipeline runs

Consuming the manifest programmatically

The manifest is written after every stage transition, so external tools can poll run_manifest.json during execution.

Typical uses:

  • check whether the optimisation stage was skipped
  • discover stage artefact paths without hard-coding filenames
  • detect the first failed stage and its error message

See Output Directory Schema for the manifest fields and status values.