Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Standard GitHub Label Set and Labelling Policy

QEP-2: Standard GitHub Label Set and Labelling Policy

QEP2
TitleStandard GitHub Label Set and Labelling Policy
Authormmcky
StatusAccepted
Typestandard
Created2026-06-17
DiscussionQuantEcon/meta#324

Summary

This QEP defines a single standard set of GitHub labels for QuantEcon repositories, the meaning of each label, and the policy for how labels are applied. It standardises label names, colours, and descriptions so they mean the same thing everywhere, and it commits to one behavioural change: the status labels (in-work / ready / review / blocked) are retired in favour of native GitHub features (Draft PRs, “Ready for review”, review requests, issue dependencies). The set is defined as a universal core plus two small domain extensions: a lecture extension for lecture repos and a one-label software extension for software / tooling repos.

Motivation

QuantEcon’s repositories have drifted into inconsistent, overlapping label vocabularies: the same idea appears under different names (improve / content / teaching vs enhancement), priority is expressed several different ways, and bot-generated output is hard to tell from human triage. One standard set, defined once for the whole organisation, fixes three things:

Proposal

The label set

Labels are grouped by purpose. Colour carries meaning where it helps: priority is a heat scale (hot red → cool green), grey marks low-salience state — machine output or a closing outcome, rather than triage signal — with the automation and meta families distinguished by text, not hue, and type and community labels keep conventional GitHub colours. Every label is core — meaningful on any repository — except the two that form the lecture extension, applied only to lecture repos, and the one that forms the software extension, applied only to software / tooling repos.

Type — what kind of work is this? (one per issue, set at triage)

LabelColourDescriptionWhen to use
bug🟥 #d73a4aSomething is wrong or brokenThe content is incorrect: wrong maths, erroring code, broken rendering — in a lecture or a build
enhancement🟦 #a2eeefImprovement to existing content or functionalityBetter exposition, a new exercise, improved figures
documentation🟫 #b08968Repo docs and contributor metaREADMEs, CONTRIBUTING — about the repo. Product content is never documentation, even where the product is docs (lectures, the manual): wrong content is bug, routine sweeps are maintenance, gaps and improvements are enhancement
infrastructure🟦 #1d3c78Substantial CI / build / deploy / tooling / automation work, or behaviour-preserving restructuringEngineering a teammate should know shipped — it would appear in a release note; includes restructuring that changes no behaviour (software repos sharpen this to refactor). Use instead of maintenance
maintenance🟨 #fbca04Routine housekeeping: style, formatting, env & dependency upkeepInvisible churn that would never appear in a release note (“rendered lectures look identical after”)
question🟪 #d876e3Someone needs an answer or clarificationTerminal state: answered. Swap for a work type once it becomes agreed work
discuss🟪 #f904a0Open-ended team deliberation or a decision to be madeNo single right answer. Reuses meta’s existing discuss colour

Priority — a heat scale (unlabelled = normal priority)

LabelColourDescriptionWhen to use
high-priority🟧 #d93f0bAddress soonPublished content visibly broken; build blockers. Label only the outliers
low-priority🟩 #c2e0c6Nice to have, no time pressureAgreed work that’s fine to sit; “someday” ideas

There is deliberately no medium-priority — the unlabelled default is the middle of the scale.

Cross-cutting — combine with any Type (like priority)

LabelColourDescriptionWhen to use
security🟥 #ee0701Security implications — needs a security-aware review barCredentials / tokens, supply-chain surface, workflow permissions. Applied alongside the Type label (e.g. bug + security); makes org:QuantEcon label:security a standing query

Community (GitHub-canonical names — spaces are deliberate, see Alternatives)

LabelColourDescriptionWhen to use
good first issue🟪 #7057ffSelf-contained and friendly to newcomersOnly when genuinely self-contained with clear acceptance criteria
help wanted🟩 #008672Maintainers would welcome outside helpIncluding domain (econ/math) expertise — say what’s needed in a comment

Workflow

LabelColourDescriptionWhen to use
do-not-merge🟥 #b60205Approved-looking but must not be merged yetA voluntary hold on a mergeable PR: pins, experiments, cross-repo timing

Automation — applied by bots, not humans (quiet grey)

LabelColourDescriptionWhen to use
automated#edededOpened by a bot or scheduled workflowEvery bot issue or PR carries this, plus at most one diagnostic below
broken-links#ddddddLink checker found dead linksApplied by the link-checker action
build-failure#ccccccExecution, build, or warnings failureApplied by scheduled build / warnings checks to the issues they open. A failing check on a PR needs no label — the red ✗ is the signal
dependencies#bdbdbdDependency or environment update (pip, conda, actions)The single Dependabot label (replaces github_actions / conda)

Automation labels take a grey in the band #ededed#bdbdbd; a new automation label takes the next value in the band not already used by any label — the meta grey duplicate #cfd3d7 also sits inside it. In a repo with no label-applying automation yet, these four sit installed but dormant — a conformant state, not a misconfiguration: the labels are the org-wide vocabulary contract, and they activate when the automation arrives.

Meta — closing outcomes

LabelColourDescriptionWhen to use
duplicate#cfd3d7Already tracked elsewhereOn close; link the survivor
wontfix#ffffffDecided not to actOn close, with one sentence why

Lecture extension — lecture repos only

LabelColourDescriptionWhen to use
new-lecture🟦 #0537E9A new lecture (the marquee outcome)Brand-new lecture — proposed, in progress, or shipped. Use instead of enhancement
editor🟩 #0e8a16Requires editor review — final sign-off stageApply at handoff after team review; editor’s queue = org:QuantEcon label:editor. Remove on sign-off

Software extension — software / tooling repos only

LabelColourDescriptionWhen to use
refactor🟦 #2c5aa0Behaviour-preserving restructuring of product sourceExtract a seam, consolidate duplication, introduce a choke point — code moves, behaviour doesn’t. Use instead of infrastructure; the repo’s own CI / build / tooling stays infrastructure

This is 19 core labels + 2 lecture labels + 1 software label = 22. The org-level default set for new repositories is the core 19.

Labelling policy

The labels only help if they are applied consistently. The rules are deliberately few:

The one behavioural change: retire status labels

Pull-request lifecycle state moves entirely onto native GitHub features and off labels:

Old labelReplaced by
in-worka Draft pull request
readyclicking “Ready for review”
reviewa review request
blocked / on-holdnative issue dependencies (“Blocked by #N”) for issues; a Draft PR with a “Blocked by #N” note for PRs

Dependencies are for hard blocks only — the UI renders one as a blocker, which overstates “should ideally follow”; soft ordering belongs to sub-issue order or the parent’s body. A blocker that is not an issue — a pending decision, a named person’s availability — has no native representation: record it as a “Blocked by: …” first line in the issue body, so a deliberately parked issue reads as parked rather than neglected, and ages visibly.

Only do-not-merge survives as a workflow label, for the genuine case of a PR that looks mergeable (even approved) but must be held.

What we deliberately don’t label

medium-priority → no label (unlabelled is the middle) · project / grouping labels (a recurring program like reading-group-*, a one-shot campaign like a tech-debt audit) → Milestones · umbrella / tracking issues → native sub-issues (the parent carries no Type, see the policy above) · per-tool diagnostic labels (colab, …) → build-failure · testinginfrastructure or maintenance (test work is not its own Type) · PR lifecycle → native GitHub, as above.

Scope

Machine-readable appendix

The normative schema ships with this QEP as a companion file, qep-0002-labels.yml, co-located so that the standard and the file tooling consumes are versioned together: under QEP-1’s versioning rule, a change to a machine-readable appendix is a substantive amendment that bumps this QEP’s version, and version-hash (a commit hash) pins both files at once.

Alternatives considered

Adoption

Acceptance fixes the names, colours, descriptions, and policy above as the QuantEcon standard. Applying the set to a repo is done with tooling that reads the co-located qep-0002-labels.yml from this repository (see Machine-readable appendix) — qe gh labels sync in QuantEcon/cli — and is a migration, not an addition: known variants are renamed in place so issue and PR history is preserved (see Scope). Adoption is pilot-first: validate on a single lecture repo before widening to the remaining lecture repos and then the software / tooling repos. The org-level default label set for new repositories is the core 19 (a manual settings change — there is no public API for org defaults).

Syncing a repo emits a migration report: one row per non-standard label, its usage count, and its disposition — renamed to X · re-tagged (12 → infrastructure, 9 → maintenance), removed · replaced by a milestone · removed (unused) · kept, unmapped. The report is the repo’s audit trail and the standard’s evidence base. A label the standard does not cover is removed if unused and kept if it carries tags — removing it would strip triage signal from live issues with nothing to replace it. A kept-unmapped label is a quarantine, not an exception: it reappears on every subsequent report until it is resolved, by an amendment that covers it, by re-tagging its issues to a standard label, or by a decision that the tags are not worth keeping. The tool never removes a label that carries tags; only a human decision does. Carrying tags counts closed items too: GitHub strips a deleted label retroactively from closed issues and merged PRs, so deleting a label always costs history. A retired label whose remaining tags are all on closed items — the status label ready on merged PRs, say — is a decision to make knowingly: delete and accept the stripped history, or keep the label as deprecated.

A label recorded as unmapped across several repos is a gap in the set, and closing it is a normal in-place amendment under QEP-1, which bumps this QEP’s version. The bar is recurrence — one repo’s oddity is a local exception, the same label unmapped in four repos is a missing label. Tooling produces the evidence; proposing the amendment stays a human judgement.

The sequenced execution checklist — the CLI integration, the pilot target, widening order, org defaults, closing the earlier unification attempts (meta#178, meta#290) — belongs in a tracking issue (a sub-issue parent, per the labelling policy above), not in this document, so completing, reordering, or dropping a step never requires amending the standard.