Skip to content

Service operations

The CLI and Python callers use the same functions in etl_craft.services.operations. Operations take a frozen OperationContext and a pipeline code, return frozen result dataclasses, and never print or dispose the caller's engine.

Calling from Python

Use an initialized, upgraded Engine DB. The caller owns its engine and supplies the acting identity:

import json
from datetime import date
from pathlib import Path

from etl_craft.config import load_config
from etl_craft.core.actor import resolve_actor
from etl_craft.engine.connection import check_reachable, engine_db
from etl_craft.engine.runlog import RunSelector
from etl_craft.services.operations import OperationContext, to_json
from etl_craft.services.operations import runs

config = load_config(Path("craft-connector.yml"))
engine = engine_db(config)
try:
    check_reachable(engine, config.engine.schema)
    ctx = OperationContext(engine, config, resolve_actor())
    done = runs.initialize_run(
        ctx,
        "SALES",
        run_date=date(2026, 10, 1),
        selector=RunSelector(run_key="python:2026-10-01"),
    )
    print(json.dumps(to_json(done), indent=2))
finally:
    engine.dispose()

initialize_run performs the CLI's initialization step. trigger_run runs the pipeline in the foreground; task execution, gates, leases, reconciliation and finalization use the same execution functions as the CLI. ctx.child controls the child process's logging and execution options. Project files remain relative to config.project_dir.

Area Operations
runs trigger_run, initialize_run, finalize_run, skip_run, mark, mark_run, stand_in_run, cancel_run, reconcile_runs
tasks run_task, force_task, rerun_task, mark_task
pipelines set_pause
backfills run_backfill
inspect list_pipelines, pipeline_graph, pipeline_steps, run_history, audit

Pass verb="pause" or verb="resume" to pipelines.set_pause(ctx, pipeline_code, reason, ...).

execute_run(ctx, RunRequest(...)) dispatches the same request used by etl-craft run. reason on tasks.run_task requests the dependency override and must be non-empty; without one the task follows its normal dependency checks. Force, rerun, backfill, pause and intervention restrictions remain the same as their CLI equivalents.

Domain refusals raise the existing core.errors classes. Read operations use one consistent database snapshot for related views and do not create action records.

Execution identities and outcomes

Every stored run view names its pipeline_id and pipeline_run_id. Every task and attempt view also names its task_run_id, with task_id for the task definition and attempt_id for an individual attempt. The service reads the identities returned by execution or selected explicitly; it never attaches a result to a different run by recency.

OperationResult.status and message describe the call's reported outcome. Its run, task and pipeline fields hold the relevant stored views. Marking a run FAILED can be a successful operation: the result reports SUCCESS while run.status reports FAILED.

A paused pipeline that starts nothing has no invented run or task view. A configured task that has never run has a null task_run_id in its step document. A skipped or stand-in task can have an empty attempts list. Backfill documents retain each date's outcome and the outcome that stopped the range.

The actor scopes request attribution and dispatched work, then is restored when the operation returns or raises. Automatic lifecycle transitions retain their system actor. Each mutating request inserts one complete REQUESTED action, including domain refusals after the Engine DB is available. Delegation from the CLI does not insert another copy. Flow outcomes remain in run and attempt history; action records are never updated.

JSON on the command line

These commands accept --format json: run, mark, cancel, pause, resume, reconcile, list, graph, steps, history and audit. Text remains the default.

etl-craft list --format json
etl-craft history --pipeline_code SALES --run-id 42 --format json
etl-craft history --pipeline_code SALES --task_code load --run-id 42 --format json
etl-craft run --pipeline_code SALES --init-only --run-key daily:2026-10-01 --format json

Standard output contains one JSON document produced by to_json, including empty lists. Errors and logs remain on standard error, and command exit behavior is unchanged. Audit text renders captured JSON consistently on SQLite and PostgreSQL.

Dates and timestamps use ISO 8601; typed Engine DB timestamps use UTC with an explicit offset. Enums use their values and tuples become arrays. Captured metadata JSON keeps its original values. The serializer returns a fresh dictionary.

Schema Document
etl-craft/operation/1 Call outcome with stored run, task or pipeline views
etl-craft/run/1 Stored pipeline run
etl-craft/task-run/1 Stored task summary and ordered attempts
etl-craft/attempt/1 Stored attempt outcome, ownership and execution counts
etl-craft/backfill/1 Date range, outcomes and stopping result
etl-craft/pipeline/1, etl-craft/pipeline-list/1 Pipeline definitions and pauses
etl-craft/graph/1, etl-craft/step/1, etl-craft/steps/1 Configuration and selected-run steps
etl-craft/history/1 Bounded run or task history and interventions
etl-craft/reconciliation/1 Lost attempt IDs and released pipeline-run IDs
etl-craft/audit/1, etl-craft/action/1, etl-craft/metadata-change/1 Immutable audit records

The Python API reference lists the functions, context, requests and result fields.

Execution inspection and diagnostics

operations.status.pipeline_status(ctx, code, selector=...) returns StatusView; explain_task(ctx, code, task_code, selector=...) returns Explanation. The pure explain(snapshot) function computes the same explanation from a captured TaskSnapshot. These reads never modify run or admission state.

operations.diagnostics.validate_metadata(ctx, code) returns the validation report; check_configuration(config) returns DoctorView, including unreachable-service findings; trace_lineage(ctx, table=..., column=..., upstream=True, downstream=True, depth=..., refresh=False) returns LineageView. Lineage retains its existing metadata cache behavior. All these documents use to_json and a versioned schema identifier, matching CLI --format json output.

Run results include waiting=True when a single-task command recorded nothing because its dependencies were not met. That call exits 23; unfinished executions exit 22. Initializing a run with --init-only retains exit 0 for orchestrator first steps.