# Sections:
#   1. meta                — Artifact metadata and versioning
#   2. external_references — Pointers to authoritative sibling artifacts
#   3. platform_scope      — Which CI/CD platform this file targets
#   4. canonical_order     — Environment ordering definition (OTAP)
#   5. precedence          — Six-tier conflict resolution model
#   6. variables           — Variable sourcing rules
#   7. pipeline_types      — Infra and release pipeline stage structures
#   8. application_types   — .NET and Nuxt app-type-specific conventions
#   9. task_version_policy — Global task version selection rules
#  10. exceptions          — Documented deviations from convention rules

# ─── 1. Meta ────────────────────────────────────────────────────────────────────

meta:
  version: "v1"
  source: https://superpowers.frontis.nl/conventions/pipeline-convention.yaml
  snapshotDate: 2026-05-21
  description: |
    This file encodes the Frontis pipeline conventions in a machine-readable format.
    It defines how Azure DevOps Cloud multi-stage YAML pipelines are structured for
    .NET and Nuxt applications. AI agents and human reviewers use this file alongside
    naming-convention.yaml and project-context.yaml to generate or validate pipelines.

    Usage for AI:
    1. Read 'external_references' to locate sibling artifacts for naming rules and project context.
    2. Read 'canonical_order' to determine the environment ordering (OTAP).
    3. Read 'pipeline_types' to determine stage structure for infra or release pipelines.
    4. Read 'application_types' to apply .NET- or Nuxt-specific conventions.
    5. Read 'variables' to determine variable sourcing rules (Library vs. inline).
    6. Read 'task_version_policy' for task version selection rules.
    7. Read 'precedence' to resolve conflicts between rules at different scopes.
    8. Read 'exceptions' for documented deviations from convention rules.

# ─── 2. External References ─────────────────────────────────────────────────────

external_references:
  naming_convention: "https://superpowers.frontis.nl/conventions/naming-convention.yaml"
  project_context: "https://superpowers.frontis.nl/conventions/project-context.yaml"

# ─── 3. Platform Scope ──────────────────────────────────────────────────────────

platform_scope:
  target: "azure_devops_cloud"
  excluded_platforms:
    - "azure_devops_server"
    - "github_actions"
    - "gitlab_ci"
    - "jenkins"
  description: >
    This convention applies exclusively to Azure DevOps Services (cloud).
    On-premises Azure DevOps Server and other CI/CD platforms are explicitly excluded.

# ─── 4. Canonical Order ─────────────────────────────────────────────────────────

canonical_order:
  environments:
    - "dev"
    - "test"
    - "acc"
    - "prod"
  rules:
    - id: "order-001"
      description: "Stages MUST appear in canonical OTAP order, filtered to enabled environments."
      scope: "global"
      severity: "error"
      rule:
        ordering: ["dev", "test", "acc", "prod"]
        filter: "Only environments present in project-context.yaml environments array produce stages."
    - id: "order-002"
      description: "The prod stage MUST always be last when present."
      scope: "global"
      severity: "error"
      rule:
        constraint: "prod_always_last"
        applies_when: "prod is in the project's environments array"
    - id: "order-003"
      description: "Only enabled environments produce deployment stages."
      scope: "global"
      severity: "error"
      rule:
        constraint: "filter_to_enabled"
        source: "project-context.yaml#variables.environments"
    - id: "order-004"
      description: "Stage-to-environment mapping: each deployment stage targets exactly one environment from the canonical order."
      scope: "global"
      severity: "error"
      rule:
        mapping: "one_stage_per_environment"
        target: "azure_devops_environment_resource"
    - id: "order-005"
      description: "Agent OS defaults to the application_types entry; app-type-specific overrides take precedence over global default."
      scope: "global"
      severity: "warning"
      rule:
        agent_os_resolution: "app_type_specific overrides global"
        global_default: "windows"
        lookup: "application_types[app_type].agent_os.default_os"

# ─── 5. Precedence ──────────────────────────────────────────────────────────────

precedence:
  tiers:
    - tier: 1
      name: "explicit_exceptions"
      source: "pipeline-convention.yaml#exceptions"
      description: >
        Documented deviations override all other rules. Each exception identifies
        the rule it overrides, conditions, and justification.
    - tier: 2
      name: "cross_file_constraints"
      source: "project-context.yaml#constraints"
      description: >
        Cross-variable constraints from project-context.yaml override pipeline
        convention defaults. Example: shared hosting restricts environments to [prod].
    - tier: 3
      name: "app_type_specific"
      source: "pipeline-convention.yaml#application_types"
      description: >
        Application-type-specific rules (.NET or Nuxt) override global pipeline
        convention defaults.
    - tier: 4
      name: "environment_specific"
      source: "pipeline-convention.yaml (rule overrides with scope: environment_specific)"
      description: >
        Per-environment overrides (e.g., prod requires approval gates) override
        global conventions.
    - tier: 5
      name: "global_defaults"
      source: "pipeline-convention.yaml (rules with scope: global)"
      description: >
        Baseline rules defined in this artifact. Apply when no higher-tier rule
        matches.
    - tier: 6
      name: "naming_convention"
      source: "naming-convention.yaml"
      description: >
        For naming concerns (file names, stage names, variable names), the naming
        convention is authoritative. Pipeline convention defers to naming-convention.yaml
        for all naming decisions.

# ─── 6. Variables ────────────────────────────────────────────────────────────────

variables:
  library_required:
    - category: "secret"
      source: "library"
      description: >
        All secret values (connection strings, API keys, passwords, tokens).
        Secrets MUST be stored in Azure DevOps Library variable groups with
        the secret flag enabled.
      constraints:
        - "MUST NOT appear as plaintext in pipeline YAML or repository files."
        - "MUST use the secret variable flag in the Library variable group."

    - category: "shared"
      source: "library"
      description: >
        Non-secret values shared across multiple pipelines or stages (e.g.,
        subscription IDs, resource group names, App Service plan names).
        Stored in Library variable groups for single-source-of-truth.
      constraints:
        - "MUST be defined in a Library variable group, not duplicated inline."

    - category: "environment_specific"
      source: "library"
      description: >
        Values that differ per environment (e.g., connection strings per env,
        App Service names per env). Stored in environment-scoped Library
        variable groups.
      constraints:
        - "MUST use one Library variable group per environment."
        - "Variable group naming follows naming-convention.yaml rules."

  pipeline_local_allowed:
    - category: "pipeline_local"
      source: "inline"
      description: >
        Values that are specific to the pipeline itself and do not contain
        secrets or shared configuration. Examples: build configuration (Release),
        solution file path, test filter expressions.
      constraints:
        - "MUST NOT contain secrets or sensitive data."
        - "SHOULD be used only for pipeline-internal configuration values."

  rules:
    - id: "var-001"
      description: "Secrets MUST be sourced from Azure DevOps Library variable groups, never inline."
      scope: "global"
      severity: "error"
      rule:
        category: "secret"
        must_source_from: "library"
        forbidden_in: ["pipeline_yaml", "repository_files"]
    - id: "var-002"
      description: "Shared and environment-specific variables MUST be sourced from Library variable groups."
      scope: "global"
      severity: "error"
      rule:
        categories: ["shared", "environment_specific"]
        must_source_from: "library"
    - id: "var-003"
      description: "Pipeline-local variables MAY be defined inline when they contain no secrets and are pipeline-internal."
      scope: "global"
      severity: "warning"
      rule:
        category: "pipeline_local"
        allowed_source: "inline"
        condition: "no secrets, pipeline-internal configuration only"

# ─── 7. Pipeline Types ──────────────────────────────────────────────────────────

pipeline_types:
  # ── 7a. Infra Pipeline ──────────────────────────────────────────────────────
  - id: "infra"
    description: >
      Infrastructure pipeline — deploys Azure resources (ARM/Bicep) per enabled
      environment. Runs independently of the release pipeline with its own trigger.
    stage_templates:
      - id: "build"
        type: "build"
        generation: "singleton"
        naming_pattern: "Build"
        job_type: "job"
        rules:
          - id: "infra-build-001"
            description: "Infra build stage validates ARM/Bicep templates before deployment."
            scope: "global"
            severity: "error"
            rule:
              required: true
              purpose: "template_validation"
              outputs: ["validated_templates"]

      - id: "deploy_infra_env"
        type: "deploy_infra"
        generation: "per_environment"
        naming_pattern: "Deploy_Infra-{env}"
        depends_on: ["build"]
        job_type: "deployment"
        deployment_strategy: "runOnce"
        rules:
          - id: "infra-deploy-001"
            description: "Each infra deploy stage targets one Azure DevOps Environment matching the canonical environment name."
            scope: "global"
            severity: "error"
            rule:
              required: true
              environment_target: "{env}"
              one_stage_per_environment: true
          - id: "infra-deploy-002"
            description: "Infra deploy stages execute in canonical order with sequential dependencies."
            scope: "global"
            severity: "error"
            rule:
              depends_on_previous: true
              ordering: "canonical_order"

    rules:
      - id: "infra-pipeline-001"
        description: "Infra and release pipelines are separate YAML files with independent triggers."
        scope: "global"
        severity: "error"
        rule:
          separate_pipeline_file: true
          independent_trigger: true
      - id: "infra-pipeline-002"
        description: "Infra pipeline deploys Azure resources required before application deployment."
        scope: "global"
        severity: "warning"
        rule:
          executes_before: "release"
          relationship: "infra provides resources consumed by release deployments"
      - id: "infra-pipeline-003"
        description: "One infra deployment stage per enabled environment — infra stages map 1:1 to environments."
        scope: "global"
        severity: "error"
        rule:
          one_stage_per_environment: true
          stage_count_equals: "length of project-context.yaml#variables.environments"
      - id: "infra-pipeline-004"
        description: "For shared hosting, the infra pipeline is optional or reduced — shared infrastructure is managed centrally."
        scope: "environment_specific"
        severity: "warning"
        rule:
          hosting_model: "shared"
          infra_pipeline_required: false
          rationale: "Shared hosting uses centrally managed infrastructure; per-project infra deployment may be unnecessary."
        overrides:
          - scope: "environment_specific"
            when:
              hosting: "shared"
            rule:
              infra_pipeline_required: false

  # ── 7b. Release Pipeline ────────────────────────────────────────────────────
  - id: "release"
    description: >
      Release pipeline — builds the application artifact and deploys it to each
      enabled environment. Covers build, test, publish, and deployment stages.
    stage_templates:
      - id: "build"
        type: "build"
        generation: "singleton"
        naming_pattern: "Build"
        job_type: "job"
        required_tasks:
          - task_name: "NuGetAuthenticate"
            major_version: 1
            required: true
            version_policy: "pinned"
            mcp_lookup_allowed: false
            conditions:
              when: "Azure Artifacts NuGet feeds are used"
              app_type: "dotnet"
          - task_name: "DotNetCoreCLI"
            major_version: 2
            required: true
            version_policy: "latest_stable"
            mcp_lookup_allowed: true
            conditions:
              app_type: "dotnet"
          - task_name: "NodeTool"
            major_version: 0
            required: true
            version_policy: "latest_stable"
            mcp_lookup_allowed: true
            conditions:
              app_type: "nuxt"
          - task_name: "Npm"
            required: true
            version_policy: "latest_stable"
            mcp_lookup_allowed: true
            conditions:
              app_type: "nuxt"
        rules:
          - id: "release-build-001"
            description: "Release build stage compiles, tests, and publishes the application artifact."
            scope: "global"
            severity: "error"
            rule:
              required: true
              purpose: "compile_test_publish"
              outputs: ["application_artifact"]

      - id: "deploy_app_env"
        type: "deploy_app"
        generation: "per_environment"
        naming_pattern: "Deploy-{env}"
        depends_on: ["build"]
        job_type: "deployment"
        deployment_strategy: "runOnce"
        rules:
          - id: "release-deploy-001"
            description: "Each release deploy stage targets one Azure DevOps Environment matching the canonical environment name."
            scope: "global"
            severity: "error"
            rule:
              required: true
              environment_target: "{env}"
              one_stage_per_environment: true
          - id: "release-deploy-002"
            description: "Release deploy stages execute in canonical order with sequential dependencies."
            scope: "global"
            severity: "error"
            rule:
              depends_on_previous: true
              ordering: "canonical_order"
          - id: "release-deploy-003"
            description: "Approval gates for acc and prod environments are configured on the Azure DevOps Environment resource, not in pipeline YAML."
            scope: "environment_specific"
            severity: "warning"
            rule:
              approval_mechanism: "azure_devops_environment_checks"
              applies_to_environments: ["acc", "prod"]
              not_in_yaml: true

    rules:
      - id: "release-pipeline-001"
        description: "Release pipeline produces and deploys application artifacts."
        scope: "global"
        severity: "error"
        rule:
          separate_pipeline_file: true
          independent_trigger: true

# ─── 8. Application Types ───────────────────────────────────────────────────────

application_types:
  # ── 8a. .NET Application Type ───────────────────────────────────────────────
  - id: "dotnet"
    description: >
      Conventions for .NET applications deployed to Azure App Service.
      Covers NuGet authentication, build/test/publish tasks, appsettings
      transformation, and Windows agent OS default.

    agent_os:
      default_os: "windows"
      rules:
        - id: "dotnet-os-001"
          description: ".NET applications default to Windows-based build agents."
          scope: "app_type_specific"
          severity: "warning"
          rule:
            default_os: "windows"
            rationale: "Full .NET Framework compatibility and tooling availability."
            overridable: true

    package_sources:
      - id: "dotnet-nuget-001"
        description: "A nuget.config file MUST exist in the repository root when private NuGet feeds are used."
        scope: "app_type_specific"
        severity: "error"
        rule:
          required_file: "nuget.config"
          location: "repository_root"
          condition: "private NuGet feeds are referenced"
      - id: "dotnet-nuget-002"
        description: "NuGetAuthenticate@1 task MUST be present before any NuGet restore step when using Azure Artifacts feeds."
        scope: "app_type_specific"
        severity: "error"
        rule:
          required_task: "NuGetAuthenticate"
          major_version: 1
          must_precede: "NuGet restore"
          condition: "Azure Artifacts NuGet feeds are used"

    build_conventions:
      - id: "dotnet-build-001"
        description: ".NET build stage MUST use DotNetCoreCLI tasks for restore, build, test, and publish."
        scope: "app_type_specific"
        severity: "error"
        rule:
          required_task: "DotNetCoreCLI"
          major_version: 2
          commands: ["restore", "build", "test", "publish"]
      - id: "dotnet-build-002"
        description: ".NET build artifacts MUST be published using PublishPipelineArtifact for consumption by deploy stages."
        scope: "app_type_specific"
        severity: "error"
        rule:
          required_task: "PublishPipelineArtifact"
          major_version: 1
          purpose: "make build outputs available to deploy stages"

    deployment_conventions:
      - id: "dotnet-deploy-appsettings-001"
        description: "appsettings.json transformation MUST use Library-sourced variables for environment-specific values."
        scope: "app_type_specific"
        severity: "error"
        rule:
          transformation_target: "appsettings.json"
          variable_source: "library"
          mechanism: "FileTransform task or Azure App Service deployment task built-in substitution"
      - id: "dotnet-deploy-appsettings-002"
        description: "Each environment's appsettings values MUST come from the corresponding environment-scoped Library variable group."
        scope: "environment_specific"
        severity: "error"
        rule:
          variable_group_scope: "per_environment"
          substitution_target: "appsettings.json"
      - id: "dotnet-deploy-001"
        description: ".NET app deployment MUST use AzureWebApp or AzureRmWebAppDeployment task to deploy to Azure App Service."
        scope: "app_type_specific"
        severity: "error"
        rule:
          allowed_tasks: ["AzureWebApp", "AzureRmWebAppDeployment"]
          target: "azure_app_service"

  # ── 8b. Nuxt Application Type ──────────────────────────────────────────────
  - id: "nuxt"
    description: >
      Conventions for Nuxt (Vue.js SSR) applications deployed to Azure App Service.
      Covers Node.js tooling, npm/yarn/pnpm package management, and Linux agent
      OS default. .NET-specific conventions (NuGet, appsettings) do NOT apply.

    agent_os:
      default_os: "linux"
      rules:
        - id: "nuxt-os-001"
          description: "Nuxt applications default to Linux-based build agents."
          scope: "app_type_specific"
          severity: "warning"
          rule:
            default_os: "linux"
            rationale: "Node.js tooling is optimized for Linux; faster build times."
            overridable: true

    package_sources:
      - id: "nuxt-npm-001"
        description: "Nuxt projects MUST use a package manager lockfile (package-lock.json, yarn.lock, or pnpm-lock.yaml)."
        scope: "app_type_specific"
        severity: "error"
        rule:
          required_file_one_of:
            ["package-lock.json", "yarn.lock", "pnpm-lock.yaml"]
          location: "repository_root"
          rationale: "Lockfile ensures deterministic dependency resolution."
      - id: "nuxt-npm-002"
        description: "Nuxt builds MUST NOT use NuGetAuthenticate — NuGet is a .NET-only concern."
        scope: "app_type_specific"
        severity: "error"
        rule:
          forbidden_task: "NuGetAuthenticate"
          rationale: "NuGetAuthenticate is irrelevant for Node.js/Nuxt projects."

    build_conventions:
      - id: "nuxt-build-001"
        description: "Nuxt build stage MUST install the correct Node.js version using NodeTool task."
        scope: "app_type_specific"
        severity: "error"
        rule:
          required_task: "NodeTool"
          major_version: 0
          purpose: "install project-required Node.js version"
      - id: "nuxt-build-002"
        description: "Nuxt build stage MUST run package install and build via npm/yarn/pnpm scripts."
        scope: "app_type_specific"
        severity: "error"
        rule:
          required_steps: ["install_dependencies", "build"]
          allowed_tasks: ["Npm", "CmdLine"]
          purpose: "install dependencies and produce build output"
      - id: "nuxt-build-003"
        description: "Nuxt build artifacts MUST be published using PublishPipelineArtifact for consumption by deploy stages."
        scope: "app_type_specific"
        severity: "error"
        rule:
          required_task: "PublishPipelineArtifact"
          major_version: 1
          purpose: "make build outputs available to deploy stages"

    deployment_conventions:
      - id: "nuxt-deploy-001"
        description: "Nuxt app deployment MUST use AzureWebApp task to deploy to Azure App Service."
        scope: "app_type_specific"
        severity: "error"
        rule:
          allowed_tasks: ["AzureWebApp"]
          target: "azure_app_service"
      - id: "nuxt-deploy-002"
        description: "Nuxt deployments do NOT require appsettings.json transformation — environment configuration uses runtime environment variables or .env files."
        scope: "app_type_specific"
        severity: "warning"
        rule:
          appsettings_transformation: false
          env_config_mechanism: "runtime environment variables or App Service configuration"

# ─── 9. Task Version Policy ─────────────────────────────────────────────────────

task_version_policy:
  rules:
    - id: "task-version-001"
      description: "AI agents SHOULD use the latest stable major version of each Azure DevOps task."
      scope: "global"
      severity: "warning"
      rule:
        default_policy: "latest_stable"
        rationale: "Latest stable versions include bug fixes and security patches."
    - id: "task-version-002"
      description: "Preview and deprecated task versions MUST NOT be used in production pipelines."
      scope: "global"
      severity: "error"
      rule:
        forbidden_versions: ["preview", "deprecated"]
        applies_to: "all tasks"
    - id: "task-version-003"
      description: "AI agents MAY use MCP-assisted lookup to discover the current latest stable version of a task."
      scope: "global"
      severity: "warning"
      rule:
        mcp_lookup_allowed: true
        purpose: "discover current latest stable major version at generation time"
    - id: "task-version-004"
      description: "NuGetAuthenticate MUST be pinned to major version 1."
      scope: "app_type_specific"
      severity: "error"
      rule:
        task_name: "NuGetAuthenticate"
        pinned_major_version: 1
        version_policy: "pinned"
        rationale: "NuGetAuthenticate@1 is the only stable version; @0 is deprecated."

# ─── 10. Exceptions ─────────────────────────────────────────────────────────────
# Each exception MUST include: id, overrides_rule, conditions, justification.
# Example:
#   - id: "exc-001"
#     overrides_rule: "some-rule-id"
#     conditions: "When project uses legacy Node 18 vendor dependency"
#     justification: "Vendor SDK requires Node 18; upgrade blocked until Q3 2026"

exceptions:
  - id: "exc-sample-001"
    overrides_rule: "nuxt-os-001"
    conditions: "When Nuxt project requires Windows-specific native Node modules (e.g., node-gyp with MSVC dependency)"
    justification: "Some native Node.js addons require a Windows build toolchain. Override Linux default for affected Nuxt projects."
