docs: define unified GitHub label taxonomy

This commit is contained in:
Tianyi Cui
2026-08-08 19:06:41 +08:00
parent 22609ea425
commit b0eb7f8f35
4 changed files with 53 additions and 83 deletions
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-25-semantic-pr-label-taxonomy.md
2026-07-25-semantic-pr-label-taxonomy.md: 3217b405e968d4d2c1eba1f1a5a08008b18ba514
2026-07-25-semantic-pr-label-taxonomy.zh.md: 978f11af9402f248679e2087ff7fb513321b69b9
2026-07-25-semantic-pr-label-taxonomy.md: 6aef5ecbb80005d1fcd90b4ee673bbbebb92cba6
2026-07-25-semantic-pr-label-taxonomy.zh.md: 0bd3fbcaeca37fa894e92ce5a4e78c4125056ac9
@@ -6,66 +6,51 @@ English | [中文](2026-07-25-semantic-pr-label-taxonomy.zh.md)
## Problem
Pull requests need two different signals: what kind of change they make and which repository domains they affect. A flat or broadly named label set conflates those questions, hides work in distinct areas such as `session` and `llm`, and gives reviewers and automation weak inputs.
Pull request labels answer two independent questions: what kind of change the work makes and which durable repository domains it materially affects. Mixing those dimensions or keeping synonymous labels makes queries ambiguous, while a closed area inventory forces new domains into inaccurate categories.
The repository also gains new domains over time. Treating today's area labels as a closed set would force future work into inaccurate labels or a generic catch-all.
Issues already have a native Type and a separate source taxonomy. Reusing pull request kind or source labels across both object types duplicates metadata and weakens the meaning of each label family.
## Decision
Every open or merged pull request carries exactly one kind and every materially affected area. Closed pull requests that were never merged are outside the maintained historical corpus. Other operational labels may coexist, but they do not satisfy either dimension.
Every open or merged pull request carries exactly one canonical `kind/*` label and at least one materially affected `area/*` label. Closed pull requests that were never merged retain migrated historical assignments but do not receive invented missing classification. Operational labels may coexist without satisfying either dimension.
### Kinds
The kind set is closed and mutually exclusive:
| Kind | Meaning |
|---|---|
| `feature` | Adds or intentionally changes behavior. |
| `bug-fix` | Corrects incorrect behavior. |
| `doc` | Makes documentation the dominant intent. |
| `testing` | Changes tests or testing infrastructure without changing product behavior. |
| `cleanup` | Preserves behavior while maintaining or simplifying the implementation or repository process. |
| `kind/feature` | Adds or intentionally changes behavior. |
| `kind/bug-fix` | Corrects incorrect behavior. |
| `kind/doc` | Makes documentation the dominant intent. |
| `kind/testing` | Changes tests or testing infrastructure without changing product behavior. |
| `kind/cleanup` | Preserves behavior while maintaining or simplifying implementation or repository process. |
| `kind/dependency` | Updates dependencies without another dominant intent. |
The kind records the change's dominant intent: accompanying tests and documentation do not turn a feature or bug fix into a testing or documentation change.
The kind records the dominant intent. Accompanying tests, documentation, cleanup, or dependency movement do not override a feature or bug fix.
Areas record semantic repository domains rather than temporary initiatives, ownership, or every path touched incidentally. Area labels are not a hierarchy: a pull request may carry several when it changes distinct contracts, but an umbrella and a narrower label do not both describe the same work.
### Areas
### Current areas
Areas name durable semantic domains rather than temporary initiatives, ownership, or every path touched incidentally. A pull request carries multiple areas when it changes distinct contracts, but it does not combine an umbrella and a narrower label for the same contract. GitHub's live `area/*` names and descriptions own the current inventory.
The 46 current areas are listed below. The group names organize the list for readability; they are not labels or another taxonomy level.
The area set is intentionally extensible. When no existing description honestly covers a durable and reusable repository domain, an agent is empowered to create a concise `area/<lowercase-kebab-case>` label without separate approval. The agent must not create an area for one pull request, an incidental path, a temporary project, a status, or a person or team, and must report the new label after applying it. Reusing an inaccurate area merely to avoid a justified addition is not acceptable.
| Group | Areas |
|---|---|
| Agent and model | `agent`, `agent-loop`, `session`, `llm`, `model-context`, `compaction`, `tools`, `persistence` |
| Orchestration | `subagent`, `workflow`, `planning`, `tasks`, `schedule`, `telemetry`, `storage`, `workspace` |
| Capabilities | `bash`, `subprocess`, `pty`, `filesystem`, `lsp`, `skills`, `web-search`, `code-mode`, `artifact`, `attachment`, `sandbox`, `mcp`, `hooks`, `cordis` |
| Interfaces | `ui`, `gui`, `tui`, `acp`, `json-rpc`, `cli`, `python-sdk`, `vscode`, `website` |
| Repository and release | `dev-infra`, `ci`, `build`, `dependencies`, `platform`, `i18n`, `release` |
Kinds are not extended this way. A new kind changes the mutually exclusive classification contract and requires an explicit taxonomy change with corresponding policy enforcement.
`gui` covers browser and Electron graphical applications, including standalone graphical developer tools; `vscode` remains the editor extension integration. `ui` covers shared cross-interface commands, approval interaction, presentation, and app boot; it coexists with `gui`, `tui`, or a protocol area only when the pull request also changes that shared contract.
### Issues and operational labels
`tasks` owns background work tied to a running process, while `schedule` owns durable time-triggered jobs. `tools` owns generic registry, schema, and execution contracts; a concrete capability receives `tools` only when it changes one of those contracts. `attachment` owns durable media references and multimodal input delivery, while `artifact` owns model-declared deliverable identity and preview lifecycle; neither borrows `tools` or `ui` for its implementation parts.
Issues use native Issue Type instead of `kind/*`; their `area/*` labels remain optional. `source/*` labels record Issue provenance and do not apply to pull requests. Priority, GitHub defaults, and workflow triggers remain independent operational metadata.
Names follow semantic ownership rather than lexical resemblance. `hooks` means the Claude Code and Codex agent bridges, not local Git hooks; `platform` means product portability, not CI runner selection; and `build` means compilation, bundling, and built package artifacts, not documentation generators.
### Extensibility
The area set is intentionally extensible. Add an area when a recurring, meaningful repository domain is missing; do not add a label for one pull request, a temporary project, a status, or a person or team. Rename, split, or retire an area when the domain model changes, and update this list and the affected open and merged pull requests together.
The kind set stays narrow because kinds are mutually exclusive. A new kind requires a distinct change intent that cannot be represented by the current five; it is not a substitute for an area.
Label migrations preserve meaning before removing aliases: add the canonical replacement, verify the labelable, then remove the obsolete assignment. A label is deleted only after no pull request or Issue still uses it, and unrelated labels are never replaced as a set.
## Alternatives considered
- **One undifferentiated label set.** Rejected because kind and area answer different questions; mixing them makes the presence of one label say nothing about whether the other dimension was considered.
- **A fixed, closed area set.** Rejected because repository domains evolve. A closed set would preserve spelling at the cost of semantic accuracy.
- **One broad `core` area or package-derived labels.** Rejected because domains such as `session`, `llm`, and `agent` remain independently meaningful across package boundaries, while incidental file paths are not the scope reviewers or automation need.
- **Separate browser and desktop areas.** Rejected because browser delivery and Electron packaging expose one graphical client domain; splitting them classifies the delivery shell rather than the semantic work.
- **Broad implementation areas in place of a domain.** Rejected because a durable scheduled job is not a background task, an attachment is not merely its source interface or filesystem implementation, and an artifact is not merely its declaring tool or preview interface.
- **Umbrella and leaf areas for the same contract.** Rejected because duplicate labels inflate scope without adding information. Multiple areas remain correct when a pull request changes genuinely distinct contracts.
- **Exactly one area per pull request.** Rejected because coherent changes can legitimately span several domains, and dropping secondary areas hides affected contracts.
- **Unprefixed labels.** Rejected because a flat name does not identify whether it classifies intent, domain, source, priority, or automation, and synonymous plain and prefixed labels caused ambiguous queries.
- **A fixed area allowlist in repository policy.** Rejected because durable repository domains evolve. The `area/*` namespace remains mechanically recognizable while live descriptions carry the extensible inventory.
- **Kinds on Issues.** Rejected because native Issue Type already owns that classification; duplicating it as a label creates drift.
- **Automatic areas from paths.** Rejected because areas describe semantic impact across package boundaries, while changed paths include incidental tests, documentation, and support files.
- **Exactly one area per pull request.** Rejected because coherent changes can materially affect several independent contracts.
## Consequences
- Reviewers and automation receive one stable intent signal plus a complete semantic scope.
- `gui` queries cover browser and desktop delivery together, while `ui` queries retain only shared cross-interface contracts.
- `schedule`, `attachment`, and `artifact` queries identify those domains directly instead of approximating them through implementation dependencies.
- Selecting labels remains a judgment call: paths and title prefixes can suggest areas, but they cannot replace reading the change.
- Taxonomy changes carry maintenance work. Area additions, renames, splits, and removals update this decision record and backfill open and merged pull requests so historical queries keep their meaning.
Reviewers and automation can query intent, semantic scope, provenance, priority, and operational triggers independently. Maintainers must read the change and live label descriptions rather than infer classification from title prefixes or paths, and taxonomy migrations carry an explicit historical backfill and verification cost.
@@ -6,66 +6,51 @@ Status: implemented
## 问题
PRPull Request需要传达两个不同的信号:它带来哪一类变更,以及会影响仓库中的哪些领域。一套扁平或命名宽泛的标签会混淆这两个问题,掩盖 `session``llm` 等不同领域的工作,也让评审人和自动化流程得到的输入缺乏有效信息
PRPull Request标签回答两个相互独立的问题:工作带来哪一类变更,以及会对哪些持久的仓库领域产生实质影响。混用这两个维度或保留同义标签会使查询含义模糊,而封闭的领域清单会迫使新领域被归入不准确的类别
仓库还会随时间发展出新的领域。如果把当前的领域标签视为封闭集合,未来的工作就只能归入不准确的标签或通用兜底标签
Issue 已有原生 Issue Type 和独立的来源分类体系。在这两类对象上复用 PR 类型或来源标签会产生重复元数据,并削弱每个标签族的含义
## 决策
每项开放或已合并的 PR 都带有恰好一个类型标签,以及所有受到实质影响的领域标签。未合并即关闭的 PR 不属于持续维护的历史记录集合。其他管理用途的标签可以并存,但不能满足这两个维度中的任一个。
每项开放或已合并的 PR 都带有恰好一个规范的 `kind/*` 标签,以及至少一个受到实质影响的 `area/*` 标签。未合并即关闭的 PR 保留经迁移的历史标签关系,但不会凭空补充缺失分类。管理用途的标签可以并存,但不能满足这两个维度中的任一个。
### 类型
类型集合封闭且互斥:
| 类型 | 含义 |
|---|---|
| `feature` | 新增行为或有意改变行为。 |
| `bug-fix` | 修正错误行为。 |
| `doc` | 以文档变更为主要意图。 |
| `testing` | 修改测试或测试基础设施,但不改变产品行为。 |
| `cleanup` | 在保持行为不变的前提下,维护或简化实现或仓库流程。 |
| `kind/feature` | 新增行为或有意改变行为。 |
| `kind/bug-fix` | 修正错误行为。 |
| `kind/doc` | 以文档变更为主要意图。 |
| `kind/testing` | 修改测试或测试基础设施,但不改变产品行为。 |
| `kind/cleanup` | 在保持行为不变的前提下,维护或简化实现或仓库流程。 |
| `kind/dependency` | 在没有其他主要意图时更新依赖。 |
类型记录变更的主要意图配套测试文档并不会把一项功能或缺陷修复变成测试或文档变更
类型记录主要意图配套测试文档、清理或依赖调整不会覆盖一项功能或缺陷修复的主要意图
领域记录仓库中的语义领域,而不是临时项目、归属关系或偶然触及的每条路径。领域标签不构成层级:一项 PR 修改不同契约时可以带有多个领域标签,但不能用一个总括标签和一个较窄标签重复描述同一项工作。
### 领域
### 当前领域
领域表示持久的语义领域,而不是临时项目、归属关系或偶然触及的每条路径。一项 PR 修改不同契约时带有多个领域标签,但不会用一个总括标签和一个较窄标签重复描述同一项契约。当前分类清单以 GitHub 上实时的 `area/*` 名称和描述为准。
当前的 46 个领域如下。分组名称仅用于提高列表的可读性;它们既不是标签,也不是分类体系中的另一个层级
领域集合有意保持可扩展。当现有描述都无法准确涵盖一个持久且可复用的仓库领域时,agent(智能体)有权无须另行批准,直接创建简洁的 `area/<lowercase-kebab-case>` 标签。agent 不得为单个 PR、偶然涉及的路径、临时项目、状态、个人或团队创建领域,并且必须在应用新标签后报告。仅为避免新增一个确有必要的标签而复用不准确的领域,不可接受
| 分组 | 领域 |
|---|---|
| agent(智能体)与模型 | `agent`, `agent-loop`, `session`, `llm`, `model-context`, `compaction`, `tools`, `persistence` |
| 编排 | `subagent`, `workflow`, `planning`, `tasks`, `schedule`, `telemetry`, `storage`, `workspace` |
| 能力 | `bash`, `subprocess`, `pty`, `filesystem`, `lsp`, `skills`, `web-search`, `code-mode`, `artifact`, `attachment`, `sandbox`, `mcp`, `hooks`, `cordis` |
| 接口 | `ui`, `gui`, `tui`, `acp`, `json-rpc`, `cli`, `python-sdk`, `vscode`, `website` |
| 仓库与发布 | `dev-infra`, `ci`, `build`, `dependencies`, `platform`, `i18n`, `release` |
类型不以这种方式扩展。新增类型会改变互斥的分类契约,必须明确修改分类体系并同步调整策略强制校验。
`gui` 涵盖浏览器和 Electron 图形应用,包括独立的图形化开发者工具;`vscode` 仍表示编辑器扩展集成。`ui` 涵盖共享的跨接口命令、审批交互、呈现和应用启动;只有当 PR 还修改这项共享契约时,它才与 `gui``tui` 或某个协议领域并用。
### Issue 与管理用途标签
`tasks` 负责与运行中进程绑定的后台工作,`schedule` 则负责持久化的定时作业。`tools` 负责通用的注册表契约、schema 契约和执行契约;具体能力只有在修改其中一项契约时才带有 `tools``attachment` 负责持久化的媒体引用和多模态输入传递,`artifact` 则负责模型声明的交付物标识和预览生命周期;二者都不会因实现包含工具或界面部分而借用 `tools``ui`
Issue 使用原生 Issue Type,而不是 `kind/*`;其 `area/*` 标签仍为可选。`source/*` 标签记录 Issue 来源,不适用于 PR。优先级、GitHub 默认标签和工作流触发器仍是相互独立的管理元数据
标签名称以语义归属为准,而不是词面相似性。`hooks` 指 Claude Code 和 Codex 的 agent 桥接,而不是本地 Git 钩子;`platform` 指产品可移植性,而不是 CI 运行器选择;`build` 指编译、打包和已构建的包产物,而不是文档生成器
### 可扩展性
领域集合有意保持可扩展。当分类体系缺少一个会反复涉及且具有实际意义的仓库领域时,就新增领域;不要仅为一项 PR、临时项目、状态、个人或团队新增标签。当领域模型发生变化时,重命名、拆分或退役相应领域,同时更新本列表以及所有受影响的开放和已合并 PR。
类型集合保持精简,因为各类型互斥。新增类型的前提是存在一种当前五类无法表达的独立变更意图;类型不能用来替代领域。
迁移标签时,须先保留语义,再移除别名:先添加规范替代标签并验证对象,再移除废弃的标签关系。只有在所有 PR 和 Issue 都不再使用某个标签后才能将其删除,且绝不整组替换无关标签
## 曾考虑的替代方案
- **一套不区分维度的标签。** 不予采纳,因为类型与领域回答的是不同问题;两者混在一起时,存在一个维度的标签并不表示另一个维度也经过了考虑
- **一套固定、封闭的领域集合。** 不予采纳,因为仓库领域会持续演变。封闭集合会以牺牲语义准确性为代价来维持拼写不变
- **一个宽泛的 `core` 领域,或从包结构派生的标签。** 不予采纳,因为 `session``llm``agent` 等领域在跨越包边界时仍各自具有意义,而偶然涉及的文件路径并不是评审人或自动化流程所需的范围信息
- **为浏览器和桌面端分别设置领域。** 不予采纳,因为浏览器交付和 Electron 打包共同呈现同一个图形客户端领域;拆开二者将按交付形态而非工作的语义进行分类
- **以宽泛的实现领域替代语义领域。** 不予采纳,因为持久化的定时作业不是后台任务,附件不只是其来源接口或文件系统实现,产物也不只是声明它的工具或预览接口
- **同一项契约同时使用总括领域与细分领域。** 不予采纳,因为重复标签只会虚增范围,不会增加信息。一项 PR 确实修改不同契约时,多个领域标签仍然合理。
- **每项 PR 恰好一个领域。** 不予采纳,因为一项内聚的变更可以合理地跨越多个领域;省略次要领域会隐藏受影响的契约。
- **无前缀标签。** 不予采纳,因为扁平名称无法表明其表示的是意图、领域、来源、优先级还是自动化用途,而同义的无前缀和带前缀标签会使查询含义模糊
- **在仓库策略中使用固定的领域允许列表。** 不予采纳,因为持久的仓库领域会持续演变。`area/*` 命名空间仍可由机制识别,而实时描述负责承载可扩展的分类清单
- **在 Issue 上使用类型标签。** 不予采纳,因为原生 Issue Type 已负责这项分类;再用标签重复表示会产生漂移
- **从路径自动生成领域。** 不予采纳,因为领域描述跨越包边界的语义影响,而变更路径还会包含偶然涉及的测试、文档和支持文件
- **每项 PR 恰好一个领域。** 不予采纳,因为一项内聚的变更可以对多个独立契约产生实质影响
## 后果
- 评审人和自动化流程获得一个稳定的意图信号,以及完整的语义范围
- `gui` 查询会同时覆盖浏览器与桌面端交付,`ui` 查询则只涵盖共享的跨接口契约。
- `schedule``attachment``artifact` 查询直接对应各自领域,无需通过实现依赖近似归类。
- 选择标签仍然需要判断:路径和标题前缀可以提示领域,但不能替代阅读变更内容。
- 变更分类体系会产生维护工作。新增、重命名、拆分或移除领域时,需要更新本决策记录,并回填开放和已合并的 PR,使历史查询保持原有含义。
评审人和自动化流程可以分别查询意图、语义范围、来源、优先级和工作流触发器。维护者必须阅读变更内容和实时标签描述,而不能根据标题前缀或路径推断分类;迁移分类体系还会产生明确的历史回填与验证成本
+1 -1
View File
@@ -118,7 +118,7 @@ Real-API tests and demos read `DEEPSEEK_API_KEY`, optional `DEEPSEEK_BASE_URL`,
- **A tool's UI render intent is part of its design**, decided up front (`generic`/`terminal`/`diff`, `locations`); presentation methods are pure functions of `args` ([cookbook](docs/cookbook/adding-a-tool.md)).
- **Plan unit, e2e, and snapshot coverage** for new seams, lifecycle shapes, and transcript surfaces; missing snapshot-harness support is part of the implementation, not deferred follow-up.
- **Choose PR history deliberately.** Split independent changes; fix the introducing PR before propagation. Standalone PRs and official stacks may merge-forward or rebase after review. Rewrites use `--force-with-lease`, abort on remote movement, never raw `--force`; an in-progress merge-forward preserves its checkpoint before taking a newer base ([rationale](.agents/notes/implemented/process/2026-08-02-native-github-stacks-and-optional-rebases.md)).
- **Label PRs:** one kind (`feature`/`bug-fix`/`doc`/`testing`/`cleanup`), each matching area; the [taxonomy](.agents/notes/implemented/process/2026-07-25-semantic-pr-label-taxonomy.md) is extensible.
- **Labels:** one PR `kind/*`, all material `area/*`, and native Issue Type ([workflow](.agents/skills/dsh-labeling/SKILL.md)).
- TODO markers: `FIXME`/`TODO`/`XXX` by urgency ([semantics](docs/development.md)).
- Files end with exactly one trailing newline; `git diff --cached --check` (pre-commit) gates it.