| 1 | # 0024 — Tags live in git, releases live in rows |
| 2 | |
| 3 | **Status:** adopted |
| 4 | |
| 5 | ## Decision |
| 6 | |
| 7 | A tag is a git ref, `refs/tags/<name>`, and nothing else. There is no tag table: `GET /repositories/:owner/:repo/tags` reads |
| 8 | |
| 9 | A release is a `release` row keyed by `(repositoryId, tagName)`. It holds the notes, the draft and prerelease flags and the author. It stores no commit: each read looks the tag up in git, so a release whose tag was moved by a push follows it, and one whose tag was deleted reports |
| 10 | |
| 11 | Creating a release for a tag that does not exist yet creates an annotated tag at the requested branch or sha, or the default branch, with the requester as tagger and the release title as its message. A lightweight tag would be dated by its commit, so a tag made today on an old commit would list as months old. The tag goes through `commitPush` like any push: one transition from the zero oid, and a pack holding only the tag object, since the commit is already in the log. A concurrent push of the same tag loses the compare-and-swap and the request fails with a conflict. |
| 12 | |
| 13 | "Latest" is computed, never stored: the most recently published release that is neither a draft nor a prerelease. |
| 14 | |
| 15 | Deleting a release deletes the row and keeps the tag. |
| 16 | |
| 17 | ## Why |
| 18 | |
| 19 | The log is the only source of truth for refs ([0004](0004-object-storage-is-the-truth.md |
| 20 | |
| 21 | Writing API-created tags through `commitPush` keeps the rule from [0016] |
| 22 | |
| 23 | ## Consequences |
| 24 | |
| 25 | - Tag names are validated with `git check-ref-format`, and a leading `-` is refused, before anything is written. |
| 26 | - `tree/<tag>` and `blob/<tag>` resolve a tag to its commit and treat it as detached, so tags are never path-indexed. |
| 27 | - A draft creates its tag straight away. GitHub waits until publishing, which would need the target stored on the row; the tag of an unpublished draft is visible to anyone who can read the repository. |
| 28 | - Release assets are not built yet. When they are, they belong in object storage next to avatars, keyed by release id. |