---
name: azldev-overlays
description: "Read this before adding, changing, or diagnosing any overlay; never edit a spec or rendered file from memory. Explains how to modify a component's RPM spec or loose source files with azldev overlays (semantic patches applied at render time) instead of forking the spec, covering overlay types, the render-and-inspect loop, common failures, pitfalls, and metadata. Triggers include overlay, overlay failed, no match, spec-add-tag, spec-remove-tag, patch-add, fix spec, backport, disable test, prune subpackage, edit spec."
---

# Working with overlays

Overlays are **semantic patches** applied to a component's RPM spec and loose
source files at render time. They let you make targeted changes to an upstream
spec without forking it. Prefer an overlay over hand-editing a rendered spec:
overlays are re-applied on every render, so a manual edit to a rendered spec is
overwritten.

## The inner loop

Overlays live in the component's TOML config — inline `[[components.<name>.overlays]]`
entries, or per-file overlay documents referenced by the component's `overlay-files`
glob. They apply **in order** and are **non-atomic**: if one fails part-way, the
overlays before it stay applied.

1. Add or edit the overlay in the component config.
2. Re-render and inspect the result:

   ```sh
   azldev comp render -p <name>
   ```

   Read the rendered spec (under `specs/`) to confirm the change
   landed where you intended, and iterate until it is correct.
3. Finalize the lock and changelog with the normal end-of-work refresh (see the
   `azldev-update-component` skill): update the lock, commit, then re-render and amend.

Config errors reference the offending overlay by its `description`, so give every
overlay a short, specific `description`.

## Diagnose common failures

Start with `azldev comp diff-sources -p <name>` to see the exact overlay effect.
Use separate pre/post `prep-sources` directories only when you need persistent trees
for deeper inspection.

| Symptom | Likely cause and fix |
| --- | --- |
| `spec-add-tag`: tag already exists | Upstream already has the tag. Use `spec-set-tag`, or `spec-update-tag` when its prior existence is an invariant. |
| `spec-search-replace`: no match | Inspect the current upstream line, check TOML regex quoting, and narrow the expression to the actual section/package. |
| Section or file not found | Inspect the upstream spec/source names; upstream may have renamed or removed the target. |
| Overlay applies but output/build is wrong | Inspect `diff-sources` for an over-broad match, malformed replacement, or a dependency/file change the overlay omitted. |

## Choosing an overlay type

Match the change to the narrowest overlay type. Required fields are enforced when
the config loads, so a missing field fails fast rather than at apply time.

### Spec overlays (structured `.spec` edits)

| Type | Use for | Required |
| --- | --- | --- |
| `spec-add-tag` | add a tag; fails if it already exists | `tag`, `value` |
| `spec-insert-tag` | add a tag next to its family (e.g. after the last `Source*`) | `tag`, `value` |
| `spec-set-tag` | set a tag, replacing it if present or adding it if not | `tag`, `value` |
| `spec-update-tag` | change an existing tag; fails if it is missing | `tag`, `value` |
| `spec-remove-tag` | delete tag instances; without `value`, deletes every instance | `tag` |
| `spec-prepend-lines` | insert lines at the top of a section (or the whole file) | `lines` |
| `spec-append-lines` | insert lines at the end of a section (or the whole file) | `lines` |
| `spec-search-replace` | regex replace within a section (or the whole spec) | `regex` |
| `spec-remove-section` | delete a whole section | `section` |
| `spec-remove-subpackage` | delete every section of a sub-package | `package` |
| `patch-add` | add a `.patch` file and register it in the spec | `source` |
| `patch-remove` | remove a patch and its spec references | `file` |

### File overlays (loose non-spec files; never `.spec`)

| Type | Use for | Required |
| --- | --- | --- |
| `file-prepend-lines` | prepend lines to a file | `file`, `lines` |
| `file-search-replace` | regex replace in a file | `file`, `regex` |
| `file-add` | copy in a new file; fails if it already exists | `file`, `source` |
| `file-remove` | delete a file | `file` |
| `file-rename` | rename a file in place | `file`, `replacement` |

## Rules that trip people up

- **`spec-remove-tag` without `value` removes every instance** of the named tag.
  To remove one dependency, set both `tag` and the exact `value` to match:

  ```toml
  [[components.mypackage.overlays]]
  description = "Remove an unavailable build dependency"
  type = "spec-remove-tag"
  tag = "BuildRequires"
  value = "unwanted-package"
  ```
- **`section` is optional only** for `spec-prepend-lines`, `spec-append-lines`, and
  `spec-search-replace` (omit it to target the whole spec). It is **required** for
  `spec-remove-section`.
- **`package` needs `section`** on the whole-file-capable overlays — a sub-package is
  a sub-qualifier of a section. `spec-remove-subpackage` is the exception: it takes
  `package` and rejects `section`.
- **`replacement` is literal** — `$1`-style capture-group references are not expanded;
  omit it to delete matched text.
- **Quote `regex` as a TOML literal string** — write `regex = '\.so$'`, not
  `regex = "\.so$"`. A basic (double-quoted) TOML string interprets backslash escapes, so
  `\s`, `\.`, `\d` and friends are mangled before the regex engine ever sees them; single
  quotes keep the pattern verbatim.
- **Anchor regex overlays to whole lines, and prefer macro toggles.** When
  `spec-search-replace` is unavoidable, anchor the full line (for example,
  `regex = '^%setup -q$'`) instead of matching a fragment, and combine several
  near-identical patterns into one rather than stacking brittle overlays. If the
  upstream spec already exposes a conditional such as `%if 0%{?rhel}` /
  `%if 0%{?fedora}` or a definable macro, set that macro instead of rewriting the
  line with regex; the explicit toggle survives upstream changes more reliably.
- **`spec-search-replace` matches one line at a time** — the pattern is applied to each
  spec line independently, so it can never span a newline and `(?s)`/DOTALL does nothing.
  For a multi-line change use a structured spec overlay (`spec-remove-section`,
  `spec-prepend-lines`/`spec-append-lines`, etc.). `file-search-replace` is different: it
  matches against the whole file, so multi-line patterns (and `(?s)`) work there.
- **`file` is a glob** (`**` supported) for the multi-file file overlays; for `file-add`
  and `file-rename` it is a single name, and `file-rename`'s `replacement` is a
  filename only (not a path).
- **`source` paths are relative** to the config that declares the overlay — the overlay
  file when loaded via `overlay-files`, otherwise the component config.
- **`file-add` lands beside the spec**, in the dist-git sources root — not inside the
  extracted upstream tree. Adding a file there does not make the build use it; wire it in
  with a `SourceN` tag plus `%prep`/`%install` steps, or use `patch-add` to change tracked
  sources.
- **Don't rename the `Name:` tag** with `spec-update-tag`/`spec-set-tag`. `%{name}` feeds
  `Source*` URLs, `%setup -n`, and `%files` paths, so renaming it silently breaks those
  references. Keep the spec `Name` aligned with the component instead.
- To add a real `.patch` file (rather than an inline edit), use `patch-add`; it copies
  the `source` into the component sources and registers a `PatchN` tag or `%patchlist`
  entry.

## Document intent with `metadata`

Give non-trivial overlays a `metadata` table. It is documentation only — excluded from
the component fingerprint, so editing it never invalidates the build cache — but it
records *why* the overlay exists and *when* it can be dropped. Every metadata block
requires `category`; pick the narrowest of:

`upstream-backport`, `azl-pruning`, `azl-compatibility`, `azl-temp-workaround`,
`azl-branding-policy`, `azl-disable-flaky-tests`, `azl-disable-unsupported-tests`,
`azl-security-compliance`, `azl-release-management`, `azl-platform-adaptation`.

It also requires `upstream-status`: `upstreamed`, `upstreamable`,
`needs-upstream-hook`, `inapplicable`, or `unknown`. Add `commits` and `bugs` as
`{ url = "https://..." }` entries where they apply. `commits` is required for
`upstream-backport`, whose status must be `upstreamed` or `upstreamable`. When several
overlays share one provenance, put them in a per-file overlay document (`overlay-files`)
with a single file-level `[metadata]`.

For how to choose the right `category` and `upstream-status`, disambiguation tips, and
the TOML forms, read the `azldev-overlay-metadata` skill.

## Full reference

The tables above are the working subset. For the exhaustive field rules, metadata
constraints, and the per-file overlay format, generate the machine-readable schema
with `azldev config generate-schema` (see the `ComponentOverlay` definition), or read
azldev's overlays configuration reference.

Generated by `azldev docs agent`; do not hand-edit.
