The mirror section told contributors a label added on Gitea "must be
created on the mirror too -- no automation does it for you." That's no
longer true, and a stale instruction to do something by hand is worse than
none, since it invites a manual edit that the next scheduled sync would
overwrite anyway.
Describe what the sync actually does: what it creates, updates, and prunes,
that an in-use label is never deleted, the --dry-run and --self-test flags,
and the rename caveat that follows from matching labels by name. Also
record the GH_MIRROR_TOKEN secret the workflow needs and its exact scopes,
since that's the one part of this that can't be automated.
The exclusive-labels caveat below it is unaffected and stays as written.
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.
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.
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.
Gitea treats a `WIP:` title prefix as a draft marker and refuses to merge
while it's present. Verified empirically against this instance by
temporarily retitling PR #123: the API flipped `draft` to true and
`mergeable` to false, and both reverted when the prefix was removed.
The distinction from the verification merge gate is the point, so it's
stated explicitly in both files: `WIP:` means more code changes are
coming, NOT "finished but unverified". A complete branch with unticked
`## Verification` boxes is an ordinary PR — that case is already covered
by the merge gate and does not want the prefix. The two signals are
independent and can occur in any combination.
Documented as a merge rule under Branching & Pull Requests, next to the
verification gate, and in the template's title guidance where an author
picking a title will actually encounter it.
The old name implied the list was only for by-hand steps, which sat badly
with the fact that most entries in practice are programmatic — the test
suite, `fish_indent`, `docs/verify-manual.py`, CI. Dropping "Manual" lets
one list carry both kinds, and gives the checkbox state real meaning:
- **Checked** — verified, whether it ran programmatically or the author
performed it by hand.
- **Unchecked** — an outstanding manual check the reviewer still has to
perform. Left visible rather than dropped.
- **The list is the merge gate.** A PR isn't merged until every box is
checked; added as an explicit rule under Branching & Pull Requests
alongside the other merge rules.
Because unchecked boxes now block the merge, the guidance also states that
only resolvable checks belong here — one nobody can run would block the PR
indefinitely. Genuinely unverifiable caveats and assumptions go in
`## Notes`, which already existed in the optional-sections list.
Incidentally realigns with PRs 121-122, which had already shortened the
heading to `## Verification`; that part of the recent drift was right.
The repo has no issue-tracking history yet — a grep across all 122 prior
PRs found zero `Closes #N` references — so this is forward-looking rather
than derived from precedent. Establishing it now means the first PR that
does close an issue has a rule to follow instead of inventing one.
- Placement is a trailing line at the end of `## Summary`, not the bottom
of the body, because `## Manual Verification` is always last.
- `Fixes`/`Resolves` noted as equivalent; `Refs #N` for a related issue
that should stay open.
- Guidance calls out that the keyword must be repeated per issue, since a
bare `#43` after a comma links without closing.
- Lives in the template's HTML comments, so a PR with no associated issue
leaves no stray `Closes #` behind.
Also drops an unverified claim that the GitHub mirror pre-loads the
template; PRs are opened on Gitea, and the mirror is push-only.
Codifies the PR body format the repo has converged on across its first
122 pull requests, which until now lived only as an implicit pattern
agents and contributors had to reverse-engineer from prior PRs.
- **`.github/PULL_REQUEST_TEMPLATE.md`** — `## Summary`, optional `##`
sections, then `## Manual Verification` as a checkbox list. Gitea falls
back to `.github/` when `.gitea/` is absent, and the GitHub mirror reads
the same path, so one file covers both.
- **`CONTRIBUTING.md`** — new "Pull request descriptions" subsection under
Branching & Pull Requests, documenting the same three-part structure.
Section names were chosen by frequency across all 122 PRs: `## Summary`
(86) and `## Manual Verification` (63) are the clear majority. The later
`## Manual Verification Checklist` (12, PRs 90-113), `## Test plan` (11)
and `## Verification` (3, PRs 121-122) variants are drift away from that
baseline, not a newer standard, so the template restores the dominant
form. Checkboxes ship unchecked but are meant to be checked before the
PR opens, matching the 585-to-12 ratio of `[x]` to `[ ]` in merged PRs.
config-help pipes the manual through bat, which dims the backticks and
leaves the span content the same colour as the prose around it -- so a
delimiter carried no information and every span cost the reader two
literal characters. 1236 of them across the document.
Each span is now rendered bold and the delimiters dropped, in every
branch of the viewer chain:
- ov + bat, and ov alone, style the spans (bat's output wraps each
backtick in its own SGR sequence; raw Markdown is matched directly)
- bat alone flattens them on the way in instead, because bat escapes
any SGR sequence handed to it as input
- less and cat style them, less gaining -R to render the result
- man -l needs nothing; pandoc consumed the backticks at build time
Both substitutions are line-preserving, so the tail-slice that opens
the pager on a requested section still lands on it.
Function doc-headers are authored as plain text -- `config-help`,
`funcsave` and anyone opening the `.fish` file read them as-is -- so they
carry no backticks. The site inherited that and rendered `-a/--all` and
`__fish_config_op_aliases` as ordinary prose.
docs/codespans.py adds the spans at render time, as the last step of
prettify(), so only the site sees them; build_concat() (man page,
config-help) is byte-for-byte unchanged.
Recognised shapes: flags and flag pairs, `$vars`, SCREAMING_SNAKE env
vars, snake_case identifiers, paths and filenames, key chords, command
shadow chains (`ls->eza`), runs of tool names, whole command lines in a
table column of command lines, and known command names -- drawn from the
`_fdc_*` catalog in functions/_fish_deps_catalog.fish, the functions/
listing, and a standard-command list, minus the names that also read as
English.
Fenced blocks, existing code spans, headings, link targets, URLs,
component markup and <FileTree> bodies are passed through untouched, and
every rule bails out rather than guess.
Documents the branching/PR workflow, commit conventions, fish function
doc-header and colored --help conventions, the docs generation
pipeline, testing, and the secrets/machine-config placement rule, so
these practices live somewhere durable instead of only in commit
history and conversation memory.