#  Copyright (c) 2026 Cisco Systems, Inc. and its affiliates
#  SPDX-License-Identifier: Apache-2.0
$schema: "http://json-schema.org/draft-07/schema#"
$id: "mas-lab/pipeline/v1"
title: "mas-lab pipeline manifest (v1)"
description: >
  Schema for pipeline.yaml — standalone post-processing pipeline declaration.
  Top-level key must be 'pipeline:'.  All relative paths are resolved from the
  directory containing this file.

  **Artifacts and transport:** Steps pass outputs in-memory within a single pipeline
  run (Python dicts / dataframes). Steps that declare file paths in config
  (events_jsonl, trajectory_csv) serialize artifacts to disk. Experiment
  manifests declare typed artifacts (trace, metrics, dataframe, plot, …) at each
  level's ``artifacts:`` block; a step's ``in:``/``out:`` names which one it
  reads/writes, fanning in the level below's instances. See
  docs/manifests/pipeline.md and experiment.schema.yaml's artifacts block.

type: object
required: [pipeline]
additionalProperties: false

properties:
  pipeline:
    type: object
    required: [name, steps]
    additionalProperties: false
    description: "Standalone post-processing pipeline — ordered steps with DAG
      dependencies."

    properties:
      # ── Identity ──────────────────────────────────────────────────────────
      name:
        type: string
        minLength: 1
        description: "Pipeline identifier (used in output paths and logs)."

      description:
        type: string
        description: "Free-text description."

      max_active:
        type: integer
        minimum: 1
        default: 8
        description: >
          Global cap on in-flight scheduler nodes. Independent of per-step
          concurrency. Standalone CLI defaults to parallel=true (this cap);
          pass --no-parallel to force 1.

      # ── Output ────────────────────────────────────────────────────────────
      output:
        type: object
        additionalProperties: false
        description: "Pipeline output configuration."
        properties:
          base_dir:
            type: string
            default: "./output"
            description: "Base output directory (relative to this file)."

      # ── Steps ─────────────────────────────────────────────────────────────
      steps:
        type: array
        minItems: 1
        description:
          "Ordered list of pipeline steps.  Execute in DAG order respecting
          depends_on."
        items:
          type: object
          required: [type]
          additionalProperties: false
          properties:
            name:
              type: string
              minLength: 1
              description: >
                Step identifier within this pipeline. Optional — defaults to
                ``type`` when omitted (see PipelineStepSpec.name).
            type:
              type: string
              description: "Registered step type."
            depends_on:
              type: array
              items:
                type: string
              description: "Names of upstream steps that must finish before this step runs."
            inputs:
              type: object
              additionalProperties: true
              description: >
                Logical input bindings from upstream steps (in-memory artifact handles).
                Omitted when the step reads only from config file paths.
            outputs:
              type: object
              additionalProperties: true
              description: >
                Named outputs exposed to downstream steps. File serialization is
                configured inside step config (output_csv, output_dir, …).
            in:
              type: [string, array]
              items:
                type: string
              description: >
                (v2) Input artifact name(s) — fans in the named artifact from the
                level below (e.g. all child ``run`` instances into a ``test``-scope
                step). See experiment.schema.yaml's per-level ``artifacts:`` blocks.
            out:
              type: [string, array]
              items:
                type: string
              description: "(v2) Output artifact name(s) produced at this step's scope."
            scope:
              type: string
              enum: [run, item, scenario, experiment, test, application]
              description: >
                Execution scope. Normally inferred from which experiment-level
                block (run/item/scenario/experiment root) declared the step; only
                needed here for a standalone pipeline.yaml with no enclosing level
                block. ``item`` and ``experiment`` are canonical; ``test`` and
                ``application`` are deprecated aliases.
            phase:
              type: string
              enum: [pre, post]
              default: post
              description: "Execution phase relative to the benchmark run loop."
            concurrency:
              type: integer
              minimum: 1
              default: 1
              description: >
                Per-type semaphore: at most N instances of this step type run
                at once. Each instance is its own scheduler node (not a wrapper).
            per_scenario:
              type: boolean
              description: "(v1, legacy) Expand this step once per scenario. Superseded by scope: scenario."
            per_run:
              type: boolean
              description: "(v1, legacy) Expand this step once per benchmark run folder. Superseded by scope: run."
            role:
              type: string
              enum: [native, wrap, batch]
              description: "v2 step class. Default batch (C)."
            resource:
              type: string
              enum: [cpu, io, net, judge]
              default: cpu
              description: "v2 scheduler resource class."
            refresh:
              type: string
              enum: [progressive]
              description: "Higher-scope UI-only partial re-evaluation on child-terminal."
            batch:
              type: object
              additionalProperties: false
              properties:
                max_records:
                  type: integer
                  minimum: 1
                max_latency_ms:
                  type: number
                  minimum: 0
            shape:
              type: string
              enum: [value, records, multi]
              description: Output artifact shape.
            mode:
              type: string
              enum: [append, upsert, snapshot]
              description: How records accumulate on the output artifact.
            records:
              type: object
              additionalProperties: true
              description: Named record types for multi-shape artifacts (e.g. kg node/edge).
            invariants:
              type: array
              items:
                type: string
              description: Seal-time invariants (e.g. edges_reference_nodes).
            persist:
              type: [boolean, string]
              description: >
                Persist policy. Boolean true writes the v1 path; ``always`` /
                ``on-seal`` / ``never`` for the stream artefact store.
            config:
              type: object
              additionalProperties: true
              description: "Step-type-specific configuration (see § Step types in docs)."

# ── Step config sub-schemas (informative, not enforced at top level) ──────────
#
# plot_trajectory config:
#   log_path: str          (path to events.jsonl, relative to this pipeline.yaml)
#   format: html | mermaid | svg | table  (default: html)
#   include_prompts: bool  (default: true)
#   filename: str          (output filename without extension; default: trajectory)
#
# extract_trajectories config:
#   output_csv: str        (default: trajectories.csv)
#   scenarios: [str]       (optional filter)
#
# eval_mce config (one run per invocation — place at scope: run):
#   events_path / trace_path: str  (override; default resolved from run_dir)
#   metrics: [str]                 (default: all session-level MCE metrics)
#   metrics_filename: str          (default: metrics.json)
#   overwrite: bool                (default: false — skip if metrics.json exists)
#   validate: bool                 (default: true)
#   max_workers: int               (default: 2 — process-wide cap on judge calls)
#   model: str                     (judge; default: experiment.evaluation.model,
#                                    then experiment.models.judge, then
#                                    experiment.model / models.main, then
#                                    application spec.models[]; `any` uses
#                                    local config.yaml defaults.model)
#   response_agent: str            (agent_id whose last execution_end is the answer)
#
# metrics_to_dataframe config (one run's metrics.json → data.csv — scope: run):
#   metrics_filename: str  (default: metrics.json)
#   output: str            (default: data.csv, relative to run_dir)
#
# gather_level config (concatenate child dataframe artifacts at a folder):
#   artifact_paths: [str]  (from `in:` fan-in)
#   annotate: {str: str}   (constant columns on every gathered row)
#   output: str            (default: data.csv)
#   format: csv | parquet  (default: csv)
#
# embed_trajectories config:
#   trajectory_csv: str
#   model: str             (embed model id)
#   output_csv: str
#
