From af764903e77e8e39dd51a208628a11f00e1cfb29 Mon Sep 17 00:00:00 2001 From: Rootiest Date: Mon, 31 Aug 2026 23:07:44 -0400 Subject: [PATCH 1/3] docs(contributing): add issue templates for bugs, features, and docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issues had no template at all, so a report arrived in whatever shape the reporter chose — most often without a fish version, a reproduction, or the full error text, which is what actually stalls a bug. Add three templates under .github/ISSUE_TEMPLATE/, beside the PR template so the GitHub mirror offers the same set: - **bug.yaml** — a Gitea issue form rather than markdown. Version, OS, area, reproduction, expected and actual behavior are required fields, so an unactionable report can't be submitted in the first place. The Area dropdown exists because contributors without push access can't set an Area/ label themselves. - **feature.md** and **docs.md** — comment-guided markdown in the same house style as PULL_REQUEST_TEMPLATE.md, since what they ask for is open-ended prose. feature.md carries `## Acceptance criteria`, the issue-side counterpart to a PR's `## Verification`. docs.md insists on the docs/manual/** source rather than the generated page, which the next CI run would overwrite. - **config.yaml** — keeps blank issues enabled for what the three don't cover, and links the contributing guide and the customization docs. Each template pre-applies its Kind/ label. Document the set, the plain- description title convention (an issue states a problem; the conventional subject belongs on the PR that closes it), and the triage split in a new CONTRIBUTING.md § Issues. --- .github/ISSUE_TEMPLATE/bug.yaml | 143 +++++++++++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yaml | 13 +++ .github/ISSUE_TEMPLATE/docs.md | 80 ++++++++++++++++ .github/ISSUE_TEMPLATE/feature.md | 111 ++++++++++++++++++++++ CONTRIBUTING.md | 67 ++++++++++++++ 5 files changed, 414 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug.yaml create mode 100644 .github/ISSUE_TEMPLATE/config.yaml create mode 100644 .github/ISSUE_TEMPLATE/docs.md create mode 100644 .github/ISSUE_TEMPLATE/feature.md diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yaml new file mode 100644 index 0000000..fe1fdde --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yaml @@ -0,0 +1,143 @@ +name: Bug report +about: Something in the config is broken or behaves unexpectedly +labels: + - Kind/Bug +body: + - type: markdown + attributes: + value: | + Thanks for filing a bug. + + **Title it as a plain description of the problem**, not as a + conventional-commit subject — `mv clobbers a symlink when the target + exists`, not `fix(mv): ...`. The commit format belongs on the PR that + fixes this; the `Kind/` and `Area/` labels carry type and scope here. + + Before filing, please confirm the problem survives a fresh shell + (`exec fish`) — a stale function definition in a long-lived session is + the single most common false alarm. + + - type: input + id: fish-version + attributes: + label: fish version + description: Output of `fish --version`. This config targets fish 4.x. + placeholder: fish, version 4.0.2 + validations: + required: true + + - type: input + id: os + attributes: + label: Operating system + description: Distribution and version, or macOS release. + placeholder: Arch Linux (CachyOS), kernel 6.12.4 + validations: + required: true + + - type: input + id: terminal + attributes: + label: Terminal emulator + description: >- + Only matters for rendering, key bindings, and color problems. Leave it + blank if the bug has nothing to do with those. + placeholder: kitty 0.42.1 + validations: + required: false + + - type: dropdown + id: area + attributes: + label: Area + description: >- + Which part of the config is affected? Pick the closest match — a + maintainer translates this into the matching `Area/` label at triage, + since contributors without push access can't set labels themselves. + Choose "Not sure" rather than guessing. + options: + - Not sure + - Functions (functions/) + - Completions (completions/) + - Config and startup (config.fish, conf.d/) + - Docs (docs/manual/, man page, docs site) + - Tests (tests/) + - CI (.github/workflows/) + - Integrations (integrations/) + - Prompt and theme (themes/) + - Opinionated components (C1-C6 toggles) + - Scripts (scripts/) + validations: + required: true + + - type: textarea + id: summary + attributes: + label: What's broken + description: One or two sentences. Name the function or file if you know it. + placeholder: >- + `mv` replaces an existing symlink instead of prompting, so the link + target is lost with no confirmation. + validations: + required: true + + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: >- + Exact commands, starting from a fresh shell, that someone else can + paste and run. Include any setup needed to reach the broken state. + render: fish + placeholder: | + exec fish + mkdir -p /tmp/repro; cd /tmp/repro + touch real; ln -s real link + mv real link + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What you thought those commands would do. + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual behavior + description: >- + What happened instead. Paste the complete output, including any error + text and stack traces — truncated errors are the usual reason a bug + report stalls in Status/Need More Info. + render: text + validations: + required: true + + - type: checkboxes + id: preflight + attributes: + label: Pre-flight + options: + - label: I searched the existing issues and this isn't already reported. + required: true + - label: I reproduced this in a fresh shell (`exec fish`), not a long-lived session. + required: true + - label: I ran `fish tests/run-tests.fish` and noted the result below (or in the output above). + required: false + + - type: textarea + id: context + attributes: + label: Additional context + description: >- + Anything else worth knowing: a private overlay in + `~/.config/.user-dots/fish/` that may be involved, opinionated + components you've disabled, the last commit where it worked. Never + paste credentials, tokens, or machine-specific paths you'd rather not + publish. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 0000000..de6c3ee --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1,13 @@ +# Gitea reads this alongside the templates in this directory. +# Blank issues stay enabled deliberately: the three templates cover bugs, +# features, and docs, and anything else (a chore, a refactor, a question) +# is better served by an empty box than by a template that doesn't fit. +blank_issues_enabled: true + +contact_links: + - name: Contributing guide + url: https://git.rootiest.dev/rootiest/fish-config/src/branch/main/CONTRIBUTING.md + about: Branch naming, commit conventions, coding standards, and the label taxonomy. + - name: Customization and personal overrides + url: https://git.rootiest.dev/rootiest/fish-config/src/branch/main/docs/manual/07-customization.md + about: Want to change behavior on just your machine? Use your private overlay — no issue needed. diff --git a/.github/ISSUE_TEMPLATE/docs.md b/.github/ISSUE_TEMPLATE/docs.md new file mode 100644 index 0000000..e121953 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs.md @@ -0,0 +1,80 @@ +--- +name: Documentation issue +about: Something in the manual, man page, config-help, or docs site is wrong, missing, or unclear +labels: + - Kind/Documentation +--- + + + +## Location + + + +## Problem + + + +## Suggested fix + + + +## Notes + + diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md new file mode 100644 index 0000000..7fe99ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -0,0 +1,111 @@ +--- +name: Feature or enhancement request +about: Propose new functionality, or an improvement to something that already exists +labels: + - Kind/Feature +--- + + + +## Summary + + + +## Problem + + + +## Proposed behavior + + + +## Alternatives considered + + + +## Scope + + + +## Acceptance criteria + + + +- [ ] +- [ ] + +## Notes + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e36ddea..90a93e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ treat it as a living document, not a final word. ## Table of Contents - [Getting Started](#getting-started) +- [Issues](#issues) - [Branching & Pull Requests](#branching--pull-requests) - [Commit Conventions](#commit-conventions) - [Fish Coding Standards](#fish-coding-standards) @@ -34,6 +35,72 @@ If you're touching anything under `docs/manual/`, you'll also want `pandoc`, (see [Documentation Pipeline](#documentation-pipeline)) — otherwise CI will catch problems on push. +## Issues + +Issues live on the Gitea repo. Three templates cover the common cases, each +pre-applying its `Kind/` label; blank issues stay enabled for everything else +— a chore, a refactor, a question, a tracking issue. + +| Template | Format | Use it for | Applies | +|---|---|---|---| +| **Bug report** | web form | Something is broken or behaves unexpectedly | `Kind/Bug` | +| **Feature or enhancement request** | markdown | New functionality, or an improvement to what exists | `Kind/Feature` | +| **Documentation issue** | markdown | The manual, man page, `config-help`, or docs site is wrong, missing, or unclear | `Kind/Documentation` | + +They live in `.github/ISSUE_TEMPLATE/`, next to the PR template, so the +GitHub mirror offers the same set. The bug report is a Gitea *issue form* — +a real web form with required fields — because a bug report missing its +version, reproduction, or full error text can't be acted on, and a form +refuses to submit without them. The other two are markdown templates in the +same comment-guided style as `.github/PULL_REQUEST_TEMPLATE.md`, since what +they ask for is open-ended prose that structure would only get in the way of. + +### Issue titles + +**Issue titles are plain descriptions of the problem, not Conventional +Commits subjects.** + +```text +mv clobbers a symlink when the target exists ← yes +fix(mv): prompt before replacing an existing symlink ← no +``` + +An issue states a problem; a commit states a change. The type and scope that +`fix(mv):` would carry are already on the issue as its `Kind/` and `Area/` +labels, and the conventional subject belongs on the PR that closes it, where +it becomes the commit message. Writing the fix into the title also presumes +one, which is the wrong end to start from for anything still being diagnosed. + +### What an issue owes + +- **A bug** needs a reproduction someone else can paste and run, starting + from a fresh shell, plus the complete error output. A stale function + definition in a long-lived session is the most common false alarm, so + confirm it survives `exec fish` first. `Status/Need More Info` is where + reports without a reproduction end up. +- **A feature** needs `## Acceptance criteria` — the checkbox list of what + must be true for the issue to close. It is the issue-side counterpart to a + PR's `## Verification`: a definition of done agreed before the work starts + rather than argued about after, and the PR's checks usually grow out of it. +- **A docs issue** needs to name the `docs/manual/**` source, not just the + page where the problem showed up. `docs/fish-config.md` and + `docs/fish-config.1` are generated, and a fix applied there is overwritten + by the next CI run — see [Documentation + Pipeline](#documentation-pipeline). + +### Triage + +Reporters aren't expected to label anything. Contributors without push access +can't, and the templates apply the `Kind/` label by themselves; the rest is +the maintainer's job when the issue is triaged — add the `Area/` label (the +bug form's **Area** dropdown is how a reporter tells you, since no forge can +map a form field to a label), set a `Priority/` if it isn't ordinary, and +apply `Reviewed/Confirmed` once a bug actually reproduces. See +[Labels](#labels). + +When a PR resolves an issue it closes it with a trailing `Closes #N` line — +see [Pull request descriptions](#pull-request-descriptions). + ## Branching & Pull Requests **If you don't have push access to this repo**, fork it and open your PR -- 2.54.0 From ce3c44a053ce307a1319b3e6563d51b7eff83e5a Mon Sep 17 00:00:00 2001 From: Rootiest Date: Mon, 31 Aug 2026 23:09:01 -0400 Subject: [PATCH 2/3] docs(contributing): define the issue and PR labeling standard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The repo carried Gitea's stock label set with no written rule for applying it, so labels were effectively unused. Establish the standard: every issue and PR carries exactly one Kind/ and at least one Area/, and document what each group means, which are exclusive, and who applies them. Extend the taxonomy to make that workable: - **Kind/Refactor, Kind/Chore, Kind/Performance** — the stock Kind/ group couldn't describe a refactor, a chore, or a perf change, which is much of this repo's history. The group now maps one-to-one onto the Conventional Commits types already in use, so a PR's label and title agree. - **A new non-exclusive Area/ group** over the ten subsystems (functions, completions, config, docs, tests, CI, integrations, prompt & theme, components, scripts). This is what makes the tracker searchable by subsystem; Kind/ alone can't answer "what's outstanding in the docs pipeline?". - **good first issue and help wanted**, deliberately unscoped rather than under Status/. Status/ is exclusive, and an issue is often both blocked and open for someone to pick up; keeping these outside the group lets them coexist with a real status. Priority/, Reviewed/, and Status/ are documented as exclusive and maintainer-applied, with the rules that keep them meaningful — no Priority/ label means ordinary priority, Reviewed/Confirmed means actually reproduced, and a Status/ label is removed once it stops being true. Also add labeling to the standing PR rules in Branching & Pull Requests, so it's visible where the rest of the PR checklist lives. --- CONTRIBUTING.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 90a93e5..8382bf7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,7 @@ treat it as a living document, not a final word. - [Getting Started](#getting-started) - [Issues](#issues) - [Branching & Pull Requests](#branching--pull-requests) +- [Labels](#labels) - [Commit Conventions](#commit-conventions) - [Fish Coding Standards](#fish-coding-standards) - [Opinionated Components](#opinionated-components) @@ -115,6 +116,9 @@ assumes you *do* have push access (maintainers, regular contributors). branching doesn't touch the working tree.) - **Merge target is `main`, via PR.** Contributors open the PR; the repo owner merges it. Don't merge your own PR. +- **Label every PR.** At minimum one `Kind/` and one `Area/`, same as an + issue — see [Labels](#labels). If you can't set labels, say what the + change is in the description and a maintainer applies them. - **Don't merge until the `## Verification` checklist is fully checked.** Unchecked boxes are outstanding manual checks, not decoration. See [Pull request descriptions](#pull-request-descriptions) below. @@ -179,6 +183,106 @@ bare `#43` is only a link and won't close anything. To point at a related issue that should stay open, drop the keyword and use `Refs #42`. Leave the line out entirely when no issue is involved. +## Labels + +**Every issue and every pull request carries exactly one `Kind/` label and at +least one `Area/` label.** Everything else is optional, and most of it is +applied by a maintainer at triage rather than by whoever opened the thing. + +Labels are scoped: the `Group/Name` form renders as a two-tone chip in Gitea, +and for the three *exclusive* groups below Gitea enforces one-at-a-time by +swapping the old label out when you apply a new one. + +### `Kind/` — what this is + +Required, and by convention exactly one. Gitea doesn't enforce one-of here, +so pick the dominant character of the change instead of stacking two. + +| Label | For | +|---|---| +| `Kind/Bug` | Something is not working | +| `Kind/Feature` | New functionality | +| `Kind/Enhancement` | Improves functionality that already exists | +| `Kind/Documentation` | Documentation changes | +| `Kind/Testing` | The test suite itself | +| `Kind/Refactor` | Restructures code without changing behavior | +| `Kind/Chore` | Tooling, dependencies, housekeeping | +| `Kind/Performance` | Makes existing behavior faster or lighter | +| `Kind/Security` | A security issue | + +These deliberately mirror the Conventional Commits types in [Commit +Conventions](#commit-conventions), so a PR's label and its title agree: +`fix` → `Kind/Bug`, `feat` → `Kind/Feature` or `Kind/Enhancement`, `docs` → +`Kind/Documentation`, `test` → `Kind/Testing`, `refactor` → +`Kind/Refactor`, `chore` → `Kind/Chore`, `perf` → `Kind/Performance`. + +### `Area/` — what it touches + +Required, and non-exclusive on purpose: a change that adds a function, its +completions, and a manual entry gets all three. + +| Label | Covers | +|---|---| +| `Area/Functions` | `functions/` | +| `Area/Completions` | `completions/` | +| `Area/Config` | `config.fish`, `conf.d/` — startup and environment | +| `Area/Docs` | `docs/manual/` and the generated manual, man page, and site | +| `Area/Tests` | `tests/` | +| `Area/CI` | `.github/workflows/` and repository automation | +| `Area/Integrations` | `integrations/` | +| `Area/Prompt & Theme` | `themes/` and prompt appearance | +| `Area/Components` | The opinionated-component system (C1-C6) | +| `Area/Scripts` | `scripts/` | + +`Area/` is what makes the tracker searchable: it answers "what's still +outstanding in the docs pipeline?" in a way `Kind/` never can. Two edges +worth naming — `Area/Docs` covers the documentation *and its pipeline*, so +`README.md` and this file count even though they sit outside `docs/`; and +`Area/Components` is for the C1-C6 machinery itself, not for every function +that happens to carry a `# COMPONENT` header. + +### `Compat/Breaking` + +Applied to **any PR whose title carries `!` before the colon**, and to any +issue proposing a change that would. It travels with the `## ⚠️ Breaking +Change` section that such a PR must already include — see [Pull request +descriptions](#pull-request-descriptions). + +### `Priority/` — exclusive, maintainer-applied + +`Priority/Critical`, `Priority/High`, `Priority/Medium`, `Priority/Low`. + +**No priority label means ordinary priority.** Labeling everything defeats +the point, so leave it off unless the item is genuinely more or less urgent +than the rest of the queue. + +### `Reviewed/` — exclusive, maintainer-applied + +`Reviewed/Confirmed` goes on a bug that has actually been reproduced — +that's the signal separating a report from a known defect. +`Reviewed/Duplicate`, `Reviewed/Invalid`, and `Reviewed/Won't Fix` accompany +closing an issue, always with a comment saying why; a close with only a +label on it is not an explanation. + +### `Status/` — exclusive, maintainer-applied + +`Status/Blocked`, `Status/Need More Info`, `Status/Abandoned`. These describe +the item's current state, so remove one as soon as it stops being true — a +stale `Status/Need More Info` on an issue that got its answer is worse than +no label, because it reads as still waiting. + +### `good first issue` and `help wanted` + +Invitations to contributors, applied by a maintainer. Both are deliberately +**unscoped**: they'd be a natural fit under `Status/`, but that group is +exclusive, and an issue is quite often both blocked on something *and* open +for someone to pick up. Keeping them outside the group lets them coexist +with a real status. + +Use `good first issue` for work that is genuinely self-contained — a clear +acceptance criterion, one or two files, no need to understand the +opinionated-component system first. + ## Commit Conventions Commit subjects follow [Conventional Commits](https://www.conventionalcommits.org/): -- 2.54.0 From 877c973e87328ba945b55aa07fdeb8ce51f68dad Mon Sep 17 00:00:00 2001 From: Rootiest Date: Mon, 31 Aug 2026 23:16:41 -0400 Subject: [PATCH 3/3] docs(contributing): make the issue templates work on the GitHub mirror too The templates were written against Gitea's schema alone, but the mirror serves the same .github/ISSUE_TEMPLATE/ files to GitHub, where two of them would have silently failed: - **config.yaml -> config.yml.** Gitea accepts either spelling (modules/structs/issue.go: `base == "config.yaml" || base == "config.yml"`), GitHub only recognizes config.yml. Under the old name the mirror's template chooser would have shown neither the contact links nor the blank-issue setting. - **bug.yaml `about:` -> bug.yml `description:`.** GitHub requires `description` on a YAML issue form; Gitea's IssueTemplate.About carries the comment "Using description in a template file is compatible" and falls back to it at modules/issue/template/unmarshal.go:126. So `description` is the one spelling both forges accept. The markdown templates keep `about:`, which is correct for their format on both. Both files now carry a comment explaining the constraint, so neither gets "tidied" back into a broken state. Also add a contact link pointing at the canonical Gitea tracker, so someone arriving from the mirror is steered to the right place before they file, and document mirror parity in CONTRIBUTING.md: the labels must be created on both sides by hand, since mirroring copies files rather than repository settings and GitHub drops a labels: entry naming a label it doesn't have. Note too that GitHub has no exclusive labels, so the one-of rule on Priority/, Reviewed/, and Status/ holds only by convention there. --- .github/ISSUE_TEMPLATE/{bug.yaml => bug.yml} | 10 ++++++- .../{config.yaml => config.yml} | 10 ++++++- CONTRIBUTING.md | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) rename .github/ISSUE_TEMPLATE/{bug.yaml => bug.yml} (89%) rename .github/ISSUE_TEMPLATE/{config.yaml => config.yml} (55%) diff --git a/.github/ISSUE_TEMPLATE/bug.yaml b/.github/ISSUE_TEMPLATE/bug.yml similarity index 89% rename from .github/ISSUE_TEMPLATE/bug.yaml rename to .github/ISSUE_TEMPLATE/bug.yml index fe1fdde..c3cff4c 100644 --- a/.github/ISSUE_TEMPLATE/bug.yaml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -1,5 +1,13 @@ +# The key below is `description`, NOT `about`. +# +# GitHub requires `description` on a YAML issue form and rejects the +# template without it; Gitea wants `about` but explicitly accepts +# `description` as a compatible alias. `description` is therefore the +# only spelling that works on both the canonical repo and the mirror. +# The markdown templates beside this one still use `about`, which is +# correct for their format on both forges. name: Bug report -about: Something in the config is broken or behaves unexpectedly +description: Something in the config is broken or behaves unexpectedly labels: - Kind/Bug body: diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yml similarity index 55% rename from .github/ISSUE_TEMPLATE/config.yaml rename to .github/ISSUE_TEMPLATE/config.yml index de6c3ee..c041d6e 100644 --- a/.github/ISSUE_TEMPLATE/config.yaml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,10 +1,18 @@ -# Gitea reads this alongside the templates in this directory. +# Read by both Gitea (the canonical repo) and GitHub (the mirror). +# +# The .yml extension is required: Gitea accepts config.yaml or config.yml, +# but GitHub only recognizes config.yml and silently ignores config.yaml. +# Don't "tidy" this back to .yaml — the chooser on the mirror stops working. +# # Blank issues stay enabled deliberately: the three templates cover bugs, # features, and docs, and anything else (a chore, a refactor, a question) # is better served by an empty box than by a template that doesn't fit. blank_issues_enabled: true contact_links: + - name: Canonical repository and issue tracker + url: https://git.rootiest.dev/rootiest/fish-config/issues + about: fish-config is developed on Gitea. If you came from the GitHub mirror, please file here instead — the mirror is read-only and issues opened there are easy to miss. - name: Contributing guide url: https://git.rootiest.dev/rootiest/fish-config/src/branch/main/CONTRIBUTING.md about: Branch naming, commit conventions, coding standards, and the label taxonomy. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8382bf7..7dd34fa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,13 @@ refuses to submit without them. The other two are markdown templates in the same comment-guided style as `.github/PULL_REQUEST_TEMPLATE.md`, since what they ask for is open-ended prose that structure would only get in the way of. +GitHub reads these same files on the mirror, and its schema differs from +Gitea's in two places, so both are pinned to the spelling that works on both +and each file says so in a comment: the chooser config must be `config.yml` +(GitHub ignores `config.yaml`), and `bug.yml` declares `description:` rather +than `about:` (GitHub requires it; Gitea accepts it as an alias). The two +markdown templates keep `about:`, which is correct for their format on both. + ### Issue titles **Issue titles are plain descriptions of the problem, not Conventional @@ -283,6 +290,28 @@ Use `good first issue` for work that is genuinely self-contained — a clear acceptance criterion, one or two files, no need to understand the opinionated-component system first. +### The GitHub mirror + +The repo is mirrored to +[github.com/rootiest/fish-config](https://github.com/rootiest/fish-config), +and **the mirror carries the same labels, by the same names**. That isn't +cosmetic: GitHub reads the same `.github/ISSUE_TEMPLATE/` files, and a +`labels:` entry naming a label that doesn't exist on that side is silently +dropped rather than reported. Mirroring copies files, not repository +settings, so **a label added here must be created on the mirror too** — no +automation does it for you. + +One behavioral difference to keep in mind: **GitHub has no exclusive +labels.** Gitea enforces one-at-a-time on `Priority/`, `Reviewed/`, and +`Status/` by swapping the old label out; on the mirror those are ordinary +labels and nothing stops two of a group coexisting, so there the one-of rule +holds by convention alone. + +Issues and pull requests belong on the canonical Gitea repo — the template +chooser links there first, on both sides. The mirror's tracker stays open so +that a report which lands there anyway isn't lost, not because it's a second +supported front door. + ## Commit Conventions Commit subjects follow [Conventional Commits](https://www.conventionalcommits.org/): -- 2.54.0