Skip to content

Author WORKFLOW.md

You'll learn what the sections of a WORKFLOW.md do, what the shortest working file looks like, and where your own decisions go.

WORKFLOW.md is plain Markdown at the repository root, committed. It answers two questions in four sections: what runs — the skills this project loads and the values it overrides — and what binds — the domain skills that encode your standards, and the standards themselves. This page assumes the when triple and the exactly-one-match rule from Skills and the workflow contract.

The shortest working file

md
# my-project — WORKFLOW

This project **loads** the skills below. Everything is a skill; this file only loads them — it
sequences nothing. Each loaded skill self-declares, in its `when`, the `work_type`, `workflow` and
`step` it is taken for, and owns that step's whole flow. The runtime matches the ticket's current
`(work_type, workflow, step)` triple to **exactly one** loaded skill and runs it.

## Loaded skills

- @gaia/essential-skills
- @gaia/ddev-drupal-bug
- @gaia/ddev-drupal-feature
- @gaia/docs-authoring

## Required skills

A `.prompt` value is **binding**: load every skill it requires before doing the work it governs.
If a required skill cannot be resolved, stop and report the exact identifier that failed.

## Domain skills

- **`acme-drupal:drupal-developer`** — applies to `web/modules/custom/`, `web/themes/custom/`,
  `recipes/`, `config/`: module and theme work, hooks, services, entity definitions, update hooks.
- **`acme-qa:playwright-bdd`** — applies to `tests/playwright/`: feature files, step files, the
  fixture and selector conventions the suite binds to.

## Standards

- **`CLAUDE.md` is the primary source, and every rule in it binds.**
- **`phpcs.xml.dist`, `phpstan.neon.dist`, `eslint.config.js`** — run with `ddev phpcs`, `ddev phpstan`, `pnpm lint`.
- **Test conventions** — one step file per component; selectors bind to `data-component-id`.
- **Git and delivery** — imperative subject, `ref:<identifier>` trailer, squash-merge to `develop`.

## Loaded skills — what runs

One bullet per skill, in load order. This is the only load-bearing list, and the loader reads it until the next ## heading — keep example bullets out of it.

@gaia/essential-skills is a bundle. That one bullet loads the globally applicable workflow skills — qualification, the code spec, deployment, verification — from one canonical place, so every project inherits the same set.

The remaining bullets are the work-type skills, and this is your choice. Add or remove them to match the sub-works your project produces. Only work:code is universal; @gaia/docs-authoring (work:docs) is shipped but opt-in.

Override a value only to deviate from a default. Every skill declares its inputs with a description and a default, so bare bullets already run. When your commands differ, write the one key inline under that skill's bullet — the keys are the ones the skill's inputs frontmatter declares, dotted by role:

md
- @gaia/ddev-drupal-bug
    validate.command: pnpm test:e2e
    provision.command: docker compose up -d
- @gaia/ddev-drupal-feature
    validate.command: pnpm test:e2e
    provision.command: docker compose up -d
- @gaia/docs-authoring
    validate.command: pnpm docs:check

Block scalars are allowed, so a multi-line policy (build.prompt: |) is as valid a value as a one-line command. A skill's inputs frontmatter lists every key it accepts, what it means, and its default.

Load order matters in exactly one case. When a ticket carries several work:* sub-works, the matching skills run in the order listed and the last one performs the single confirmation and transition.

## Required skills — the binding rule

Some inputs are .prompt values that name skills (build.prompt, review.prompt). This section states, once, that such a value is binding: the agent loads every skill it names — reads its complete SKILL.md — before doing the governed work, and stops if one cannot be resolved rather than substituting its own method. Keep the section; a step's one-line reference to it is the whole enforcement.

## Domain skills — the skills that encode your standards

One bullet per skill, each with the one line that says when it applies — a path, a file type, a kind of change. This list exists because of a specific, silent failure mode: a subagent that a step dispatches performs no skill discovery. Your framework rules and test conventions are simply not applied unless the handoff names the skills that carry them — and nothing errors when one is missed. Every dispatching step reads this section for the names. Say so honestly when none applies to a change.

## Standards — the map

Pointers, never copies, so they cannot drift: the instruction file that is the primary source, the linter and static-analysis configs and the command that runs them, the test conventions, the enforced architectural rules and the script that asserts them, the commit and merge convention. Every step owner of spec, coding and review reads this loader anyway, so writing the standards here is all the wiring there is.

Outside the loaded list

## Domain skills and ## Standards sit after ## Loaded skills on purpose: their bullets would otherwise be parsed as loaded skills.

The customer-facing labels

The labels GAIA writes on origin issues need no bullet unless your customer's vocabulary differs — see GitLab: import and origin labels.

Where the template comes from

The GAIA skills ship templates/WORKFLOW.template.md. @gaia/initialize-project copies it into a repository that has none; @gaia/upgrade-project preserves a file you already wrote. Neither overwrites, and both are human-initiated. You can equally write the file by hand from the shape above.

Next