Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions .github/labels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# The issue taxonomy, checked in.
#
# This file is the source of truth; `labels.yml` in .github/workflows applies it
# on a merge to `main`. Edit here, not in the UI — a description edited in the
# UI is silently reverted by the next sync, which is the point: a label whose
# meaning drifts is worse than one that is missing, because it keeps sorting
# issues by a rule nobody agreed to.
#
# **Removing a line does not delete the label.** Deletion strips the label from
# every issue that carried it and no history records what was lost, so it stays
# a deliberate act in the UI. Renaming is the same: `gh label edit <old> --name
# <new>` moves the issues; editing this file alone just creates a second label.
#
# Three prefixes, and the axes are independent — an issue takes one from each
# where it applies, not one label total:
#
# area:* which part of the system. The module map in docs/ARCHITECTURE.md
# is the same carve.
# engine:* which relational engine, when a finding is specific to one.
# bug:/trigger:/blocked: a qualifier on the kind, not an area.
#
# Colours are grouped on purpose: all `area:*` share one, so the eye reads the
# kind first and the area second.

# --- kind: what sort of work this is -----------------------------------------

- name: bug
color: "d73a4a"
description: "Fails loudly: exception or obviously wrong result"

- name: bug:silent
color: "a00000"
description: "Plausible wrong answer or unearned green light; the user gets no signal"

- name: feature
color: "a2eeef"
description: New capability

- name: perf
color: "0e8a16"
description: Wall time, memory, or I/O cost — not a wrong answer

- name: chore
color: "cfd3d7"
description: Packaging, CI, dependencies

- name: decision
color: "d876e3"
description: "A question to be answered, not work to be done; closes by resolution"

- name: roadmap
color: "5319e7"
description: "Decided yes: on the plan. Applied on resolution, replacing 'decision'"

# --- area: which part of the system ------------------------------------------

- name: area:language
color: "0075ca"
description: Schema, grammar, AST, macros, primitives

- name: area:engine
color: "0075ca"
description: Lowering, IR, relational executor, the sink contract

- name: area:solver
color: "0075ca"
description: Solver sink, options, statuses, duals, value read-back

- name: area:file-out
color: "0075ca"
description: "How a model leaves as text: LP/MPS output, formatting, stable bytes"

- name: area:api
color: "0075ca"
description: Native entry points and CLI

- name: area:compat
color: "0075ca"
description: Oracle lane and linopy interop

- name: area:docs
color: "0075ca"
description: SPEC, ARCHITECTURE, ROADMAP, GLOSSARY, README

- name: area:packaging
color: "0075ca"
description: Distribution, extras, dependency footprint

# --- engine: which implementation, when it matters ---------------------------

- name: engine:polars
color: "ffd93d"
description: Specific to the polars engine — the default

- name: engine:duckdb
color: "d4a017"
description: Specific to the duckdb engine — opt-in, the [duckdb] extra

# --- qualifiers ---------------------------------------------------------------

- name: breaking
color: "e99695"
description: "Changes the YAML surface; land before there are users"

- name: taxed
color: "1b9aaa"
description: "New closed-vocabulary construct: needs both an oracle and a streaming implementation"

- name: blocked:upstream
color: "000000"
description: Waiting on an upstream linopy change

- name: trigger:bench
color: "ffffff"
description: Run the benchmark comparison on this PR; removed when the run finishes

# --- machine-applied: release-please and dependabot own these -----------------

- name: "autorelease: pending"
color: "ededed"
description: Release-please owns this one — a release PR awaiting merge

- name: "autorelease: tagged"
color: "ededed"
description: Release-please owns this one — released, tag pushed

- name: dependencies
color: "ededed"
description: Dependabot version bumps
69 changes: 69 additions & 0 deletions .github/workflows/labels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Labels

# Applies .github/labels.yml to the repository. Cost: one ~15s job, and only
# when that file changes.
#
# `gh label create --force` is create-or-update, so the run is idempotent and
# re-running it after a UI edit puts the description back. It does **not**
# delete: a label absent from the file is left alone, because deleting one
# strips it from every issue that carried it. That asymmetry is deliberate and
# documented at the top of the file.
#
# Not a required check. It runs *after* the merge — a taxonomy that blocks a
# PR is a taxonomy people route around.

on:
push:
branches: [main]
paths: [".github/labels.yml"]
workflow_dispatch:

permissions:
contents: read
# labels are issue-scoped, and this is the only job that writes them
issues: write

concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

jobs:
apply:
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
with:
sparse-checkout: .github/labels.yml
sparse-checkout-cone-mode: false
persist-credentials: false

- uses: astral-sh/setup-uv@v7
with:
python-version: "3.11"

# TSV rather than a jq pipeline, because a description carries colons,
# em-dashes and brackets — pyyaml is the only thing here that parses the
# file the same way a reader does.
- name: Read the taxonomy
run: |
uv run --no-project --with pyyaml python - > labels.tsv <<'PY'
import yaml

with open(".github/labels.yml") as fh:
labels = yaml.safe_load(fh)

for label in labels:
print(label["name"], label["color"], label.get("description", ""), sep="\t")
PY
wc -l < labels.tsv

- name: Apply it
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
set -euo pipefail
while IFS=$'\t' read -r name color description; do
gh label create "$name" --color "$color" --description "$description" --force
done < labels.tsv
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ next section is why there is nothing for the version to announce.
`main` is protected: no force-push, no deletion, squash-only through a PR, and
the two required checks above. Approvals are not required, but the PR is.

**Labels are checked in.** [`.github/labels.yml`](.github/labels.yml) is the
taxonomy and a merge to `main` applies it, so edit the file rather than the UI —
a description changed in the UI is reverted by the next sync. The prefixes are
independent axes, not a single choice: an issue takes a kind, plus the `area:`
it touches, plus an `engine:` only when the finding is specific to one.

Versioning, the release PR, and how to force a specific version:
[RELEASING.md](RELEASING.md).

Expand Down
Loading