From 6b34310a28b5ca7f01b72de80edcb93092011e97 Mon Sep 17 00:00:00 2001 From: SeasonsChange <1204992313@qq.com> Date: Wed, 2 Sep 2026 11:30:01 +0800 Subject: [PATCH 1/6] docs: clarify governance check names --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c123b2..0be837a 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,6 @@ ## 使用边界 -开发 Agent 负责创建分支、提交代码和创建 PR。治理 Agent 只读取 PR 状态并发布 `git-governance` 检查结果。 +开发 Agent 负责创建分支、提交代码和创建 PR。仓库内 GitHub Actions 发布原生 `git-governance` 门禁;接入外部治理 Agent 后,由它只读观察 PR 并发布建议性的 `multica-governance` 检查。 从本模板创建仓库后,使用 Git 治理工具包中的 `bootstrap-repository.ps1` 配置分支保护。 From ccfdec33d992ffe8e58d004d9655aec4ce964018 Mon Sep 17 00:00:00 2001 From: SeasonsChange <1204992313@qq.com> Date: Fri, 4 Sep 2026 16:26:05 +0800 Subject: [PATCH 2/6] fix: enforce task ID in PR titles --- .git-management.json | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.git-management.json b/.git-management.json index facfd67..e13386a 100644 --- a/.git-management.json +++ b/.git-management.json @@ -1,11 +1,16 @@ { - "policy_version": 1, + "policy_version": 3, "default_branch": "main", "branch_pattern": "^(ai|feat|fix|refactor|chore)/.+$", "required_pr_metadata": ["task_id", "run_id"], + "task_id_pattern": "^[A-Z][A-Z0-9]*-[0-9]+$", + "require_task_id_in_pr_title": true, "required_checks": ["ci", "git-governance"], - "required_approvals": 1, - "require_resolved_conversations": true, + "review_policy": { + "enabled": false, + "required_approvals": 0, + "require_resolved_conversations": false + }, "require_up_to_date_branch": true, "merge_method": "squash" } From b928b76492720df0e99283d8096898336d2e8f06 Mon Sep 17 00:00:00 2001 From: SeasonsChange <1204992313@qq.com> Date: Fri, 4 Sep 2026 16:26:07 +0800 Subject: [PATCH 3/6] fix: enforce task ID in PR titles --- .github/PULL_REQUEST_TEMPLATE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4bdc94f..ee7fe2c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,6 +4,8 @@ run_id: <必填运行标识> change_id: <可选变更标识> --> + + ## 目标 ## 变更范围 From dcba604e71fe9b3850b29333edbaf554e3f26063 Mon Sep 17 00:00:00 2001 From: SeasonsChange <1204992313@qq.com> Date: Fri, 4 Sep 2026 16:26:09 +0800 Subject: [PATCH 4/6] fix: enforce task ID in PR titles --- .github/scripts/validate_pr.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/scripts/validate_pr.py b/.github/scripts/validate_pr.py index fe83f2f..9252e88 100644 --- a/.github/scripts/validate_pr.py +++ b/.github/scripts/validate_pr.py @@ -19,6 +19,7 @@ def parse_metadata(body): def validate(event, policy): pull_request = event.get("pull_request") or {} + title = pull_request.get("title") or "" body = pull_request.get("body") or "" head_ref = pull_request.get("head", {}).get("ref") or "" base_ref = pull_request.get("base", {}).get("ref") or "" @@ -43,6 +44,19 @@ def validate(event, policy): if not value or value.startswith("<"): failures.append(f"missing PR metadata: {field}") + task_id = metadata.get("task_id", "") + task_id_is_present = bool(task_id) and not task_id.startswith("<") + task_id_is_valid = task_id_is_present + task_id_pattern = policy.get("task_id_pattern") + if task_id_is_present and task_id_pattern and not re.fullmatch(task_id_pattern, task_id): + failures.append(f"invalid PR metadata: task_id: {task_id}") + task_id_is_valid = False + + if policy.get("require_task_id_in_pr_title") and task_id_is_valid: + exact_task_id = rf"(? Date: Fri, 4 Sep 2026 16:26:11 +0800 Subject: [PATCH 5/6] fix: enforce task ID in PR titles --- .github/scripts/test_validate_pr.py | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/.github/scripts/test_validate_pr.py b/.github/scripts/test_validate_pr.py index f804cc5..c2a2fe3 100644 --- a/.github/scripts/test_validate_pr.py +++ b/.github/scripts/test_validate_pr.py @@ -12,6 +12,8 @@ "default_branch": "main", "branch_pattern": r"^(ai|feat|fix|refactor|chore)/.+$", "required_pr_metadata": ["task_id", "run_id"], + "task_id_pattern": r"^[A-Z][A-Z0-9]*-[0-9]+$", + "require_task_id_in_pr_title": True, } @@ -19,6 +21,7 @@ class ValidatePullRequestTest(unittest.TestCase): def test_accepts_valid_pull_request(self): event = { "pull_request": { + "title": "feat(example): MT-1 add example", "body": "", "head": {"ref": "feat/example"}, "base": {"ref": "main"}, @@ -39,6 +42,42 @@ def test_rejects_missing_metadata_and_invalid_branch(self): self.assertIn("missing PR metadata: task_id", failures) self.assertIn("missing PR metadata: run_id", failures) + def test_rejects_title_without_task_id(self): + event = { + "pull_request": { + "title": "feat(home): add team homepage", + "body": "", + "head": {"ref": "feat/home"}, + "base": {"ref": "main"}, + } + } + failures = VALIDATE_PR.validate(event, POLICY) + self.assertIn("PR title must contain task_id: TML-741", failures) + + def test_rejects_title_with_different_task_id(self): + event = { + "pull_request": { + "title": "feat(home): TML-742 add team homepage", + "body": "", + "head": {"ref": "feat/home"}, + "base": {"ref": "main"}, + } + } + failures = VALIDATE_PR.validate(event, POLICY) + self.assertIn("PR title must contain task_id: TML-741", failures) + + def test_rejects_task_id_prefix_match(self): + event = { + "pull_request": { + "title": "feat(home): TML-7410 add team homepage", + "body": "", + "head": {"ref": "feat/home"}, + "base": {"ref": "main"}, + } + } + failures = VALIDATE_PR.validate(event, POLICY) + self.assertIn("PR title must contain task_id: TML-741", failures) + if __name__ == "__main__": unittest.main() From 980ebea0097224f649b7ef3a2cd890498955dfc9 Mon Sep 17 00:00:00 2001 From: SeasonsChange <1204992313@qq.com> Date: Fri, 4 Sep 2026 16:26:12 +0800 Subject: [PATCH 6/6] fix: enforce task ID in PR titles --- README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0be837a..c6f432d 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,21 @@ ## 文件说明 -- `.git-management.json`:仓库治理策略,定义允许的分支格式、必需检查、审批数量和合并方式。 +- `.git-management.json`:仓库治理策略,定义分支格式、必需检查、可选 Review 和合并方式。 - `.github/workflows/ci.yml`:通用项目检查,支持 Makefile、Go、Bun 和 npm 项目。 -- `.github/workflows/pr-policy.yml`:运行 `git-governance` 门禁。 -- `.github/scripts/validate_pr.py`:校验目标分支、分支名和 PR 元数据。 +- `.github/workflows/pr-policy.yml`:运行原生 `git-governance` 门禁。 +- `.github/scripts/validate_pr.py`:校验目标分支、分支名、PR 标题任务号和 PR 元数据。 - `.github/scripts/test_validate_pr.py`:治理校验器的单元测试。 - `.github/PULL_REQUEST_TEMPLATE.md`:开发 Agent 创建 PR 时使用的统一模板。 -- `.github/CODEOWNERS`:项目关键路径的 Review 所有者配置。 +- `.github/CODEOWNERS`:为以后启用 Review 保留;初期不要求 Code Owner Review。 -## 使用边界 +## 默认策略 -开发 Agent 负责创建分支、提交代码和创建 PR。仓库内 GitHub Actions 发布原生 `git-governance` 门禁;接入外部治理 Agent 后,由它只读观察 PR 并发布建议性的 `multica-governance` 检查。 +- Required Checks:`ci`、`git-governance`。 +- PR 标题:必须包含与正文元数据完全相同的完整 `task_id`。 +- Review:通过 `review_policy.enabled: false` 关闭。 +- 合并方式:Squash。 -从本模板创建仓库后,使用 Git 治理工具包中的 `bootstrap-repository.ps1` 配置分支保护。 +开发团队负责创建分支、Commit 和 PR。仓库内 GitHub Actions 负责原生门禁;接入外部 PR 检查 Agent 后,Agent 只检查状态、维护一条 PR 规范检查评论,并定向通知创建 PR 的开发 Agent。 + +从本模板创建仓库后,由 Git 治理管理员使用工具包中的 `bootstrap-repository.ps1` 配置分支保护。治理 Agent 不执行初始化。