> For the complete documentation index, see [llms.txt](/llms.txt)

# The published spec repo

Almost everything downstream of your API reads a spec document. A docs platform renders one, an
agent grounds itself in one, a partner writes against one, and your API's change history *is* the
sequence of them. The document that matters most is the one your SDKs were actually generated from —
and that one has usually lived inside a build, unaddressable, while the copy everyone reads drifts
away from it.

The `spec_repo` target turns that document into a **standing guarantee**: a git repository you own
that carries your specification, republished on every build, with a commit only when your API
actually changed. What it gives you is not a serialization — anything can write a YAML file. It is
that the URL a consumer polls is provably current, and provably the same document the SDKs were
built from, across every build and every forge, forever.

Enable it under `targets:` in `glotto.yml`:

**The document your SDKs were built from, published**

The target as a real demo declares it, and the index that one real generate run over that demo published. The README lists exactly the variants × formats the config selected — three variants in two serializations, six documents, named for what they are rather than when they were written.

Derived from `examples/endpoint-migration/inputs` — every byte below is sliced from that demo or from one real generate run over it.

**Your glotto.yml — the spec&#95;repo target, in full** (`examples/endpoint-migration/inputs/glotto.yml` `targets → spec_repo`)

```yaml
  # The specification itself, published to a git repo you own alongside the SDKs.
  # `repo` / `repo_provider` / `repo_path` are the same release-repo coordinates
  # every target takes; `variants` and `formats` choose which documents land there.
  spec_repo:
    repo: acme/api-spec
    repo_provider: github
    repo_path: spec
    variants: [base, with_transforms, with_code_samples]
    formats: [yaml, json]
```

**glotto generate**

**What Glotto emits — the published repo's index, listing exactly what it carries** (`spec_repo/README.md` `whole file`)

```md
# Acme Data API — API specification

The OpenAPI specification for Acme Data API (version 2.0.0), published and maintained by Glotto.

## Contents

- `spec.base.json`, `spec.base.yaml` — the spec exactly as supplied to Glotto, with excluded operations removed.
- `spec.with-transforms.json`, `spec.with-transforms.yaml` — the spec after every configured correction in `transforms:` — the document the SDKs were generated from.
- `spec.with-code-samples.json`, `spec.with-code-samples.yaml` — the spec with per-operation SDK code samples embedded, for a documentation platform to render.

## How this repository is maintained

Every file here is generated. Glotto regenerates the whole set from the source specification on
each build and publishes it as a single commit, so a hand-edit to any file will be overwritten
by the next build — send spec changes upstream to the source specification instead.

A build that changes nothing publishes nothing, so every commit in this history is a real change
to the API surface, and the commit message states what changed. The repository's git log is
therefore a changelog of the API itself.
```

sha256 `d7e955a9c175c690f1d4bcd5fd23e04c25570d84e1f6142aa57e9e886beae29f`

`variants` and `formats` are optional — omit them and you get every variant your input can produce,
in YAML. `repo` is `owner/name` (Azure Repos: `org/project/repo`), `repo_provider` is one of
`github` / `gitlab` / `bitbucket` / `azure-repos`, and `repo_path` publishes into a subtree of a
monorepo. All three are the same release-repo coordinates every target takes, documented in the
[`targets` reference](/docs/glotto-yml#targets).

`glotto generate` then writes `<out>/spec_repo/` and makes **no network write of any kind**: the
emission is hermetic and deterministic, so a self-hosted build commits the tree with its own
credentials and the hosted platform publishes it for you (below).

## What it emits

| Path | What it is |
| --- | --- |
| `spec.base.yaml` / `spec.base.json` | Your spec exactly as you supplied it, with excluded operations removed. |
| `spec.with-transforms.yaml` / `.json` | The same spec after every correction in your [`transforms`](/docs/transforms) block — **the document your SDKs were generated from.** |
| `spec.with-code-samples.yaml` / `.json` | The spec with per-operation SDK code samples embedded as `x-codeSamples`, for a documentation platform to render. |
| `README.md` | A generated index: your API's title and version, one line per published variant, and a statement that the tree is regenerated and republished on every build — so a reader knows not to hand-edit it. |

One file per `variants` × `formats` pair, named for what it is rather than when it was written, so a
consumer can hard-code a raw URL and keep it forever.

The `with-transforms` variant is the one worth pointing your tooling at. It is not a reconstruction
of what the SDKs were built from — Glotto folds your transforms **once** and hands the same document
to the code generators and to this target, so "the published spec is what the SDKs implement" is an
equality rather than an intention.

## Which variants your input can publish

Availability follows what your pipeline actually produces, and a gap is always a loud refusal rather
than a missing file. That distinction matters more here than anywhere else Glotto emits: a published
spec repo is a public surface, so an absent document reads to a consumer as a fact about your API
("this API has no code samples") rather than as a Glotto refusal.

| Input | Publishable variants |
| --- | --- |
| **OpenAPI** | All of them. `with_code_samples` additionally requires [`openapi.code_samples.formats`](/docs/glotto-yml) to be set — there is nothing to embed otherwise. |
| **AsyncAPI** | `base` only. The transform engine is OpenAPI-shaped and never runs on an AsyncAPI document, so a `with-transforms` file there would be a copy of the base one published under a name asserting corrections were applied. The code-sample decorator is OpenAPI-only for the same reason. |
| **GraphQL** | None. A GraphQL input never becomes a canonical spec document, so there is nothing to publish in any variant or format — the same reason the spec changelog declines it. This is a boundary of the target, not work in progress: no artifact is being withheld from you. |

Requesting a variant your input cannot produce fails with
[`GLOTTO_SPEC_REPO_VARIANT_UNAVAILABLE`](/docs/diagnostics-sdk-generation#the-spec-repo-target), naming which
variant and why; selecting the target at all with a GraphQL input fails with
[`GLOTTO_SPEC_REPO_INPUT_UNSUPPORTED`](/docs/diagnostics-sdk-generation#the-spec-repo-target).

## Published to a repo you own

On the hosted platform, the tree is published on every build to the repo named by `repo` — a
repository **you** own and have connected, on **GitHub, GitLab, Bitbucket or Azure Repos**. Glotto
never creates the repository; it writes into one you already control, which is the right trust
boundary for a service pushing to your forge.

The publish lands as a commit directly on a branch rather than as a pull request you have to merge,
because the artifact's whole value is a branch a docs platform can poll without lag. It is
idempotent: the same generation published twice changes nothing. `repo_path` scopes the write to a
subtree, so a spec repo can share a monorepo with anything else you keep there.

If you later narrow `variants`, a previously published file stays on the branch — Glotto's publish
path adds and updates, and deliberately never deletes on your behalf. The publish result **reports**
every such leftover path rather than leaving you to notice it, so you can remove it deliberately.

## Every commit means something

Two properties of the git history are decisions, not implementation details:

- **A build that changes nothing makes no commit.** Publishing on every build means most builds have
  nothing to say, and an empty commit per build would turn the history — the point of the whole
  artifact — into noise you have to filter.
- **The commit message is derived from the spec diff.** The previously published document is read
  back from the branch and compared against the new one, and the rendered spec changelog becomes the
  message body; a first publish gets an initial-publish subject and no body. There is no model in
  the loop, so the message is reproducible and cannot describe a change that did not happen.

Together those make `git log` on the published repo a changelog of your API itself: every entry is a
real change, and it says what changed.

## Kept true, not just generated

Like every Glotto artifact, the spec repo is a deterministic emission — byte-stable across repeated
runs, YAML rendering pinned so a renderer upgrade cannot silently re-wrap your document, locked by
golden tests, and regenerated in lockstep with your SDKs, docs site, MCP server and CLI. The
[drift gate](/docs/drift-detection) proves the committed copy never lags your spec.

That is the guarantee on offer. Exporting a spec file once is trivial; keeping a published, polled,
externally-referenced copy true against every change, on whichever forge you use, is the part that
decays. Add an operation, correct a schema with a transform, enable code samples — the spec repo is
provably current on the next build.
