# Agent & contributor guide
Guidance for humans and AI agents working in this repository.
## Pull request titles
**PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/).**
This is not cosmetic: PRs are squash-merged, so the **PR title becomes the commit
message on `main`**, and [release-plz](https://release-plz.dev) parses those commit
messages to decide version bumps and to generate each crate's changelog. A sloppy
title produces a wrong version bump and a bad changelog entry.
### Format
```
<type>[optional (scope)][optional !]: <description>
```
- Use the imperative mood, lower-case, no trailing period.
- Add `!` (or a `BREAKING CHANGE:` footer) for breaking changes.
### Types and how release-plz treats them
| `feat` | New user-facing capability | minor bump |
| `fix` | Bug fix | patch bump |
| `perf` | Performance improvement | patch bump |
| `refactor` | Behaviour-preserving code change | no release, in changelog |
| `docs` | Documentation only | no release, in changelog |
| `test` | Tests only | no release |
| `ci` | CI / workflow changes | no release |
| `build` | Build system or dependency changes | no release |
| `chore` | Maintenance not covered above | no release |
A `feat!:` / `fix!:` (or `BREAKING CHANGE:` footer) triggers a breaking bump.
This table is enforced by `.release-plz.toml`, not merely a convention:
`release_commits` restricts releases to `feat`/`fix`/`perf`, and `commit_parsers`
keeps the purely internal types out of changelogs. Without that config
release-plz bumps a package for *any* commit touching its files — and because
the root package `oci-zero` owns the repository root, a CI-only change would
publish a new `oci-zero`.
### Scopes
Prefer a crate name as the scope so changelogs attribute correctly:
`oci-zero`, `gzip-zero`, `zstd-zero`, `oci-zero-web`, `oci-zero-no-std-extract`.
Example: `fix(gzip-zero): reject trailer with wrong ISIZE`.
### Examples
```
feat(oci-zero): stream blob digests without buffering
fix(gzip-zero): reject members with a corrupt header CRC
perf(zstd-zero): avoid recomputing the FSE table per block
docs: document the release-plz workflow in AGENTS.md
chore: bump miniz_oxide to 0.9.1
feat(oci-zero)!: rename Decoder::decode to Decoder::step
```
PR titles are enforced in CI by `.github/workflows/pr-title.yml`.
## Releasing
Releases are automated by release-plz (`.github/workflows/release-plz.yml`):
1. Merges to `main` cause release-plz to open/update a **Release PR** that bumps
versions and updates changelogs based on the merged commit messages.
2. Merging that Release PR publishes any bumped crates to crates.io via
[trusted publishing](https://crates.io/docs/trusted-publishing) (OIDC — no
stored registry token) and creates git tags and GitHub Releases.
Do not run `cargo publish` by hand; let the pipeline do it.
## Crates
- `oci-zero` — no-std, allocation-free streaming core for OCI registries (published)
- `gzip-zero` — no-std streaming gzip decoder (published)
- `zstd-zero` — no-std streaming Zstandard decoder (published)
- `oci-zero-no-std-extract`, `oci-zero-web` — examples/tools, `publish = false`
`tools/` and `web/` are not crates, but they sit inside the root package's
directory, so release-plz counts changes there as changes to `oci-zero`. The
`release_commits` filter in `.release-plz.toml` is what stops a `ci:`, `build:` or
`chore:` commit touching them from publishing a new `oci-zero`; use those types
for changes that are not user-visible library changes.