Reference

release.yaml, and every rule it is checked against

One file in your repository describes the pipeline. Credentials, store accounts, distribution targets and approval policies are deliberately not in it — they live in the platform, which is what makes moving a project between store accounts possible.

A complete file

version: 1
framework: flutter
mode: native

apps:
  ios:
    bundle_id: com.example.app
  android:
    package: com.example.app

workflows:
  pr:
    triggers:
      pull_request:
        branches: [main]
    priority: pr-check
    jobs:
      test:
        steps:
          - uses: core/checkout
          - uses: flutter/bootstrap
          - uses: flutter/test
      build-ios:
        needs: [test]
        app: ios
        runs_on: { os: darwin }
        steps:
          - uses: core/checkout
          - uses: flutter/build
            with:
              platform: ios
              signed: false      # unsigned: no credential is resolved at all
              archive: false

That pull-request workflow is fork-safe, and not because anything says so. signed: false means the iOS build resolves no signing credential, no step in the job distributes, and so allowed_on_untrusted derives true. The step library shows that derivation for all forty-two steps.

28 errors

A file with any of these does not run.

  • E001

    The document does not match the schema

    Structural: a wrong type, an unknown key, a value outside an enum. Reported before any semantic rule runs, because running semantics over a malformed document produces cascading nonsense.

  • E010

    needs names a job that does not exist

    Usually a rename that missed one reference. The graph is built from these names, so an unresolvable one is a job that can never start.

  • E011

    The job graph has a cycle

    Two or more jobs waiting on each other. The engine would never schedule any of them, and it would look like a queue problem rather than a config one.

  • E012

    A job depends on itself

    The degenerate cycle, called out separately because the fix is obvious once named.

  • E013

    Two steps in one job share an id

    Step ids are how one step references another's output. Two steps with one id makes that reference ambiguous.

  • E014

    Retired: an expression referenced an unknown step id

    Never emitted. It described an error inside a `${{ }}` expression, and Conductor does not evaluate expressions — E017 refuses them outright. The code is kept so it is never reused for something else.

  • E015

    Retired: an expression referenced a job not in needs

    Never emitted, for the same reason as E014: there are no expressions for it to be about. Kept reserved rather than reused.

  • E016

    A field the schema accepts and Conductor does not implement

    `if:`, `outputs:`, `strategy.fail_fast: true` and `strategy.max_parallel` are part of the file format and nothing acts on them. Accepting them silently meant a job with `if: false` ran anyway; refusing them means nobody builds on a behaviour that is not there.

  • E017

    A `${{ }}` expression, which nothing evaluates

    Conductor has no expression language, deliberately: a job's credential set must be computable by reading the file. An expression would be passed along as literal text. Matrix values arrive as CONDUCTOR_MATRIX_<KEY> environment variables, the run's ref and SHA as CONDUCTOR_GIT_REF and CONDUCTOR_GIT_SHA, and a concurrency group is already scoped to its ref.

  • E020

    uses names a step that is not in the registry

    The registry is the whole step library, so this is a typo or a version of Conductor older than the step. The diagnostic suggests the nearest name.

  • E021

    The step exists; that major version does not

    Steps are versioned per major. Pinning one that was never published fails here rather than at dispatch.

  • E022

    `with` violates the step's input schema

    Each step declares a JSON Schema for its inputs, and it is checked here rather than by the step failing on the machine.

  • E023

    A required input is absent

    Separated from the schema error above because “you forgot one” and “this value is wrong” are different problems with different fixes.

  • E030

    A distribution step is in a pull-request workflow

    A pull request must not be able to ship anything. This is an error rather than a warning because the failure mode is a build reaching testers from an unreviewed branch.

  • E031

    core/shell is in a job that also distributes

    A shell step in a job holding store credentials is the shape of an incident: arbitrary commands beside standing, high-privilege material. Split the shell work into a job the distribution job depends on.

  • E032

    A step needing a sensitive credential is on the untrusted pool

    The untrusted pool is where a fork's pull request runs, and it never receives standing credentials. A clone token is not sensitive and does not trip this — a fork may check out the code it is testing.

  • E033

    A fastlane lane needing credentials is reachable from a fork

    An adopted Fastfile is arbitrary Ruby holding injected credentials. Running it credential-free on a fork fails in a way that looks like Conductor broke it, so it is refused instead.

  • E034

    A job resolves credentials and does not say which app it targets

    Which credential satisfies a purpose is resolved per app. Without `app: ios` or `app: android` there is nothing to resolve against, and the build would have to guess.

  • E035

    One job mixes runner steps with control-plane ones

    A job runs entirely on one side. Mixing them would mean a workspace that exists for half the steps in it.

  • E036

    A job selects a variant that is not declared

    `variant:` names one of apps.<app>.variants — another build of the app that is a separate app at the stores, with its own bundle id or package. It needs an `app:` to be a variant of, and the variant has to be declared there; otherwise there is no identifier to sign or ship it under, and the run would fail minutes in instead of here.

  • E037

    A pull-request workflow asks for a signed build

    Pull request builds are never signed or archived — Conductor forces both off for every pull_request run, so the build needs no credential and a fork's pull request can run at all. An explicit `signed: true` is a line claiming something that will not happen.

  • E039

    A pull-request workflow step needs signing material

    A pull request's run never receives a credential that signs — not by forcing a build unsigned, which covers ios/build, android/build and flutter/build, but at the credential boundary, so ios/sign and a fastlane lane listing ios_signing or android_signing cannot reach it either. Sign in a push or manual workflow.

  • E038

    ios/build or ios/test has no Xcode scheme

    xcodebuild's default scheme depends on which schemes are shared and how they sort, so the step refuses to guess. Name one on the step, or once under apps.ios.scheme; a shared scheme is a file in xcshareddata/xcschemes, which is also where Conductor's detection looks.

  • E040

    A store-account identifier appears in the config

    No Apple Team ID and no Play developer id may appear in release.yaml. That rule is the entire reason a project can be moved to a different store account as a configuration change rather than a rewrite.

  • E041

    A distribution step names no target

    `target` is how a step says where a build goes; the store account, credential and app behind the name are resolved at run time. The step library lists it as optional for compatibility, but no distribution can run without one, so leaving it out is refused here rather than at the last step of a pipeline.

  • E042

    The step is declared but cannot run on this version of Conductor

    A control-plane step whose executor is not implemented yet. It would validate, schedule and then fail at run time, so it is refused while the file is still being written.

  • E052

    The step is in the registry but this version of Conductor does not run it

    A step that was declared and turned out not to work. The registry only ever grows, so it stays listed; it is refused here, with the reason and what to do instead, rather than failing a run.

  • E050

    A fastlane step is used while mode is native

    Native mode means typed steps only. Set `mode: hybrid` if you want both, which is a deliberate choice rather than an accident.

9 warnings

The pipeline runs. Each one is something that will cost you later, said now.

  • E051

    mode is adopt and no fastlane step is used

    Adopt mode exists to run your existing lanes. If none are called, `mode: native` describes what is actually happening.

  • W060

    No current runner matches runs_on

    The job would queue indefinitely. A warning rather than an error because a fleet changes — enrolling the right machine fixes it without touching the file.

  • W061

    The matrix expands beyond what this fleet can run

    On a small fleet a wide matrix is not parallelism, it is a queue. Learning that from a warning beats learning it from a dashboard.

  • W062

    An `on:` key was parsed as the boolean true

    Under YAML 1.1 an unquoted `on` is a boolean. Conductor's parser is 1.2 and keeps it a string, but other tooling may not — `triggers:` is the spelling immune to it.

  • W063

    The declared framework differs from what was detected

    A warning, never a silent switch. A build that quietly changed how it was produced is worse than one that stopped.

  • W064

    paths and paths_ignore overlap

    The overlap is silently excluded, which is rarely what somebody meant to write.

  • W065

    An expensive darwin job has no path filter

    macOS capacity is the scarce resource. A job that rebuilds an iOS app because a README changed is spending the thing you have least of.

  • W066

    Retired: a pull-request workflow asked for a signed build

    Never emitted. It was a warning, and only for an explicit `signed: true`; pull request builds are now forced unsigned, and asking for a signed one is the error E037.

  • W067

    A concurrency group does not cancel in-progress runs

    A run that arrives while its group is busy is not started — Conductor does not queue runs behind a group the way GitHub Actions does. Sometimes that is the point; set cancel_in_progress: true if the newer run should replace the older one.

Codes are stable and never reused

A diagnostic code is an identifier you can search for, link a colleague to, and grep a build log for. So they are append-only: a rule that is removed leaves its number retired rather than handing it to the next rule, because a search result explaining E032 should not one day explain something else.