forked from Gitlink/gitlink-cli
350 lines
11 KiB
Markdown
350 lines
11 KiB
Markdown
# gitlink-stale — 白名单豁免规则
|
||
|
||
> 本文档详述"哪些 Issue/PR 永不被 stale skill 处理"的完整规则。
|
||
> 豁免规则在 Stage B 执行,先于 AI 判断(节省 API 调用)。
|
||
|
||
## 1. 豁免原则
|
||
|
||
**核心思想**:宁可放过,不可误关。
|
||
|
||
任何满足"长期重要"或"主动声明保留"信号的 Issue 都应被豁免。
|
||
|
||
| 原则 | 说明 |
|
||
|------|------|
|
||
| **保守** | 不确定时,豁免(不处理) |
|
||
| **多信号** | 任一豁免信号触发即可 |
|
||
| **可追溯** | 豁免原因必须记录在报告中 |
|
||
| **可配置** | 用户可自定义豁免规则 |
|
||
|
||
---
|
||
|
||
## 2. 豁免信号全集
|
||
|
||
### 2.1 标签豁免(最强信号)
|
||
|
||
| 标签名(中/英) | 豁免原因 | 默认权重 |
|
||
|---------------|---------|---------|
|
||
| `pinned` / `置顶` | 显式标记永久保留 | 必豁免 |
|
||
| `security` / `安全` | 安全相关,永不自动关闭 | 必豁免 |
|
||
| `roadmap` / `路线图` | 长期规划 | 必豁免 |
|
||
| `epic` / `里程碑` | 大型任务父节点 | 必豁免 |
|
||
| `keep-open` / `保留` | 显式声明 | 必豁免 |
|
||
| `in-progress` / `进行中` | 正在处理 | 必豁免 |
|
||
| `under-review` / `审查中` | 等待审查 | 必豁免 |
|
||
| `help-wanted` | 等社区认领 | 必豁免 |
|
||
| `good-first-issue` | 等新手认领 | 必豁免 |
|
||
| `p0` / `p1` | 高优先级 | 必豁免 |
|
||
|
||
### 2.2 Tracker 类型豁免
|
||
|
||
GitLink 的 tracker_id 映射(参考 issue-triage skill):
|
||
|
||
| tracker_id | 名称 | 默认豁免 |
|
||
|-----------|------|---------|
|
||
| 1 | bug | ❌ 不豁免(仍可能 stale) |
|
||
| 2 | feature | ❌ 不豁免 |
|
||
| 3 | support | ❌ 不豁免(最易 stale) |
|
||
| 4 | doc | ❌ 不豁免 |
|
||
| 5 | test | ❌ 不豁免 |
|
||
| 6 | duplicate | ✅ 直接关闭(特殊处理) |
|
||
| 7 | question | ❌ 不豁免 |
|
||
| 自定义 | roadmap | ✅ 豁免 |
|
||
| 自定义 | epic | ✅ 豁免 |
|
||
|
||
### 2.3 优先级豁免
|
||
|
||
| priority_id | 等级 | 默认豁免 |
|
||
|------------|------|---------|
|
||
| 1 | low | ❌ 不豁免 |
|
||
| 2 | normal | ❌ 不豁免 |
|
||
| 3 | high | ⚠️ 仅在 confidence >= 0.9 时处理 |
|
||
| 4 | urgent | ✅ 豁免 |
|
||
|
||
### 2.4 标题模式豁免
|
||
|
||
匹配以下正则的标题豁免:
|
||
|
||
```yaml
|
||
keep_open_title_patterns:
|
||
- "\\[WIP\\]"
|
||
- "\\[Pinned\\]"
|
||
- "\\[Keep.?Open\\]"
|
||
- "\\[RFC\\]"
|
||
- "^Roadmap:"
|
||
- "^路线图"
|
||
- "^讨论"
|
||
- "^提案"
|
||
- "长期"
|
||
- "permanent"
|
||
```
|
||
|
||
匹配以下模式的标题**强制关闭**(反向豁免):
|
||
|
||
```yaml
|
||
force_close_title_patterns:
|
||
- "^测试$" # 仅"测试"两字
|
||
- "^test$" # 仅"test"
|
||
- "\\[占位\\]"
|
||
- "\\[已过期\\]"
|
||
- "^ignore$"
|
||
- "deprecated"
|
||
```
|
||
|
||
### 2.5 作者豁免
|
||
|
||
| 作者类型 | 豁免规则 |
|
||
|---------|---------|
|
||
| 仓库 owner | ✅ 豁免(信任 owner 会跟进) |
|
||
| 仓库 member | ✅ 豁免 |
|
||
| 资深贡献者(≥ 10 PR merged) | ⚠️ confidence >= 0.85 才处理 |
|
||
| 普通用户 | ❌ 不豁免 |
|
||
| 路过用户(仅 1 Issue) | ❌ 不豁免(更倾向清理) |
|
||
|
||
### 2.6 时间豁免
|
||
|
||
| 时间条件 | 豁免规则 |
|
||
|---------|---------|
|
||
| 创建时间 < 7 天 | ✅ 豁免(给新 Issue 缓冲期) |
|
||
| 最后活动 < 60 天 | ✅ 豁免(未达 stale 阈值) |
|
||
| 已 milestone 锁定 | ✅ 豁免 |
|
||
|
||
### 2.7 关联豁免
|
||
|
||
| 关联条件 | 豁免规则 |
|
||
|---------|---------|
|
||
| 有 linked PR(标题含"fixed in #N") | ✅ 豁免(等 PR 合并) |
|
||
| 有子任务(被 epic 引用) | ✅ 豁免 |
|
||
| duplicate of 已 closed | 直接关闭(特殊处理) |
|
||
|
||
---
|
||
|
||
## 3. 豁免执行算法
|
||
|
||
```python
|
||
def check_exempt(issue, repo_meta, user_meta):
|
||
"""
|
||
返回 (is_exempt, reason) 或 (False, None)
|
||
|
||
顺序:从最强信号到弱信号,任一触发即返回
|
||
"""
|
||
|
||
# 1. 标签豁免(最强)
|
||
EXEMPT_LABELS = {
|
||
"pinned", "置顶",
|
||
"security", "安全",
|
||
"roadmap", "路线图",
|
||
"epic", "里程碑",
|
||
"keep-open", "保留",
|
||
"in-progress", "进行中",
|
||
"under-review", "审查中",
|
||
"help-wanted",
|
||
"good-first-issue",
|
||
"p0", "p1",
|
||
}
|
||
|
||
for label in get_labels(issue):
|
||
if label.lower() in EXEMPT_LABELS:
|
||
return True, f"含豁免标签: {label}"
|
||
|
||
# 2. 标题强信号
|
||
import re
|
||
for pattern in KEEP_OPEN_TITLE_PATTERNS:
|
||
if re.search(pattern, issue.subject, re.IGNORECASE):
|
||
return True, f"标题匹配保留模式: {pattern}"
|
||
|
||
# 3. 优先级豁免
|
||
if issue.priority_id == 4: # urgent
|
||
return True, "urgent 优先级"
|
||
|
||
# 4. tracker 豁免
|
||
if issue.tracker_id in [ROADMAP, EPIC]:
|
||
return True, "tracker 是 roadmap/epic"
|
||
|
||
# 5. 作者豁免
|
||
if issue.author.login == repo_meta.owner:
|
||
return True, "作者是仓库 owner"
|
||
if issue.author.login in repo_meta.members:
|
||
return True, "作者是仓库 member"
|
||
|
||
# 6. 时间豁免(创建 < 7 天)
|
||
days_since_created = (now() - parse(issue.created_at)).days
|
||
if days_since_created < 7:
|
||
return True, f"创建仅 {days_since_created} 天,在缓冲期内"
|
||
|
||
# 7. 高优先级的特殊处理
|
||
if issue.priority_id == 3: # high
|
||
# 不直接豁免,但需要 confidence >= 0.9
|
||
return False, None # 走正常流程
|
||
|
||
return False, None
|
||
|
||
|
||
def check_force_close(issue):
|
||
"""
|
||
反向豁免:强制关闭
|
||
"""
|
||
import re
|
||
for pattern in FORCE_CLOSE_TITLE_PATTERNS:
|
||
if re.search(pattern, issue.subject, re.IGNORECASE):
|
||
return True, f"标题匹配强制关闭模式: {pattern}"
|
||
return False, None
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 豁免决策流程图
|
||
|
||
```
|
||
┌─────────────────────────────┐
|
||
│ Issue 候选(已通过时间过滤) │
|
||
└────────────┬────────────────┘
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ 1. 强制关闭模式匹配? │─── 是 ──→ force_close
|
||
└────────────┬────────────────┘
|
||
│ 否
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ 2. 含豁免标签? │─── 是 ──→ exempt
|
||
└────────────┬────────────────┘
|
||
│ 否
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ 3. 标题匹配保留模式? │─── 是 ──→ exempt
|
||
└────────────┬────────────────┘
|
||
│ 否
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ 4. priority == urgent? │─── 是 ──→ exempt
|
||
└────────────┬────────────────┘
|
||
│ 否
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ 5. tracker == roadmap/epic? │─── 是 ──→ exempt
|
||
└────────────┬────────────────┘
|
||
│ 否
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ 6. 作者是 owner/member? │─── 是 ──→ exempt
|
||
└────────────┬────────────────┘
|
||
│ 否
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ 7. 创建 < 7 天? │─── 是 ──→ exempt
|
||
└────────────┬────────────────┘
|
||
│ 否
|
||
▼
|
||
进入 AI 判断
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 自定义豁免规则
|
||
|
||
用户可在仓库根目录创建 `.gitlink-stale.yml` 自定义:
|
||
|
||
```yaml
|
||
# .gitlink-stale.yml
|
||
version: 1.0
|
||
|
||
# 阈值
|
||
thresholds:
|
||
stale_days: 60
|
||
close_days: 74
|
||
grace_days: 14
|
||
|
||
# 豁免标签(追加到默认列表)
|
||
exempt_labels:
|
||
- "客户合同"
|
||
- "VIP 用户反馈"
|
||
|
||
# 豁免标题模式(追加)
|
||
exempt_title_patterns:
|
||
- "^\\[长期讨论\\]"
|
||
|
||
# 强制关闭模式(追加)
|
||
force_close_title_patterns:
|
||
- "^spam"
|
||
|
||
# 豁免用户
|
||
exempt_authors:
|
||
- "trusted-contributor"
|
||
|
||
# 自定义 priority 豁免
|
||
exempt_priorities:
|
||
- 4 # urgent
|
||
- 3 # high(比默认更严格)
|
||
|
||
# 自定义 tracker 豁免
|
||
exempt_trackers:
|
||
- 8 # 自定义的"内部任务"
|
||
```
|
||
|
||
> 💡 Skill 在执行前自动加载此文件(如果存在),与默认规则合并。详见 [SKILL.md §4.3](../SKILL.md)。
|
||
|
||
---
|
||
|
||
## 6. 豁免审计
|
||
|
||
报告中必须列出所有被豁免的 Issue,便于人工复核:
|
||
|
||
```json
|
||
{
|
||
"summary": {
|
||
"exempt": 15,
|
||
"exempt_breakdown": {
|
||
"label_pinned": 3,
|
||
"label_security": 2,
|
||
"label_roadmap": 5,
|
||
"priority_urgent": 2,
|
||
"author_owner": 2,
|
||
"title_pattern": 1
|
||
}
|
||
},
|
||
"exempt_items": [
|
||
{
|
||
"number": 88,
|
||
"title": "[Pinned] 项目长期路线图",
|
||
"exempt_reason": "含豁免标签: pinned",
|
||
"exempt_signal": "label_pinned"
|
||
},
|
||
{
|
||
"number": 92,
|
||
"title": "线上数据库故障",
|
||
"exempt_reason": "urgent 优先级",
|
||
"exempt_signal": "priority_urgent"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 边界情况
|
||
|
||
| 情况 | 处理 |
|
||
|------|------|
|
||
| 同一 Issue 含豁免标签和强制关闭模式 | 豁免优先(保守原则) |
|
||
| 标签名大小写不同(`Pinned` vs `pinned`) | 大小写不敏感 |
|
||
| 标签名含空格(`keep open`) | 标准化(去空格、转小写)后比较 |
|
||
| 标签是 emoji(📌) | 当前不支持,建议搭配文字标签 |
|
||
| 作者 ID 已注销(`login == null`) | 不豁免(可能就是僵尸) |
|
||
| 用户自定义规则与默认冲突 | 用户规则优先(追加而非覆盖) |
|
||
|
||
---
|
||
|
||
## 8. 推荐的标签配置
|
||
|
||
为了让 Skill 发挥最佳效果,**强烈推荐**仓库具备以下标签:
|
||
|
||
| 标签名 | 用途 |
|
||
|-------|------|
|
||
| `pinned` | 显式标记永久保留的 Issue |
|
||
| `security` | 安全相关 |
|
||
| `roadmap` | 路线图 |
|
||
| `stale` | 已被本 Skill 标记 |
|
||
| `duplicate` | 重复 Issue |
|
||
| `wontfix` | 决定不修复(但保留记录) |
|
||
|
||
如果仓库缺少这些标签,Skill 在执行前会提示用户创建(不会自动创建,避免越权)。
|