Reusable workflows take inputs and not everything

A callable workflow is invoked as a job rather than a step, which constrains what can be passed into it and what can come back out.

# .github/workflows/php-ci.yml, in a shared repo
on:
  workflow_call:
    inputs:
      php-version: { type: string, default: '8.0' }
    secrets:
      COMPOSER_AUTH: { required: false }

# and the caller
jobs:
  ci:
    uses: org/workflows/.github/workflows/php-ci.yml@v3
    with:
      php-version: '8.0'
    secrets: inherit

Inputs are typed and limited to strings, numbers and booleans, so anything structured has to be JSON in a string and parsed on the other side. The called workflow runs on its own runners and cannot see the caller’s checkout, which is the constraint people hit first — it must check out the repository itself. secrets: inherit is convenient and passes everything, which is the wrong default for a workflow in another organisation.