chore(#216): 提交信息工单号门禁(commit-msg hook + CI)+ 台账补录 #191~#197

R-P2-9 治理无号提交(不追溯历史,只管新增):

1. 判据单一来源 scripts/check-commit-issue.sh:
   - --message-file(本地 hook)/ --range(CI)/ --head-only(CI 回退)
   - 放行:信息含 #数字、显式 [skip-issue]、merge 提交;违规逐条列出后退出 1。
   - 临时 git 仓正/负/区间/空区间/skip/merge/--no-verify 全部实测。
2. scripts/install-commit-msg-hook.sh:安装 .git/hooks/commit-msg 调用判据脚本
   (与 gitleaks pre-commit 独立;判据随仓库更新无需重装)。
3. .gitea/workflows/test.yml 新增 commit-message-lint 轻量 job(纯 git 不依赖 docker):
   push 用 before..after、PR 用 base..head;两端不可达/新分支/force-push 回退仅查 HEAD,
   保证不误阻断主干。
4. docs/standards/issue-workflow.md §1.5 + README 补提交规范与钩子安装说明。
5. 台账新增 §十七,据 Gitea API + git log 考据补录 #191~#197:
   #192/#194/#195/#197 closed 且均有关闭凭证评论;#191/#193/#196 open(附 commit/文档依据)。
   R-P2-9 回填已完成。
This commit is contained in:
2026-10-07 22:39:03 +08:00
parent 5fef31e90c
commit 64086c5518
6 changed files with 194 additions and 2 deletions

View File

@@ -18,6 +18,49 @@ on:
branches: [main, master]
jobs:
# #216/R-P2-9: 提交信息必须携带 #工单号(与本地 commit-msg 钩子共用 scripts/check-commit-issue.sh)。
# 纯 git 轻量门禁, 不依赖 docker; 只校验「本次新增」提交, 不追溯历史。
commit-message-lint:
runs-on: ubuntu-latest
steps:
- name: 检出代码 (显式 clone)
run: |
rm -rf /workspace/ci-repo
git clone --quiet http://127.0.0.1:3000/tanzk/csctalon.git /workspace/ci-repo
cd /workspace/ci-repo && git log --oneline -1
- name: 校验新增提交信息携带工单号 (#216)
run: |
set -euo pipefail
cd /workspace/ci-repo
zero=0000000000000000000000000000000000000000
event="${{ github.event_name }}"
# 默认按 push 的 before..after;PR 改用 base..head。
before="${{ github.event.before }}"
after="${{ github.event.after }}"
[ -z "$after" ] && after="${{ github.sha }}"
if [ "$event" = "pull_request" ]; then
before="${{ github.event.pull_request.base.sha }}"
after="${{ github.event.pull_request.head.sha }}"
fi
echo "event=$event before=${before:-<empty>} after=$after"
# 两端提交都本地可达且 before 非全零(非新分支首推)→ 精确校验新增区间;
# 新分支首推 / force-push 旧基不可达 / fork PR 等无法确定可靠区间时,
# 回退为仅校验 HEAD(保守: 不追溯、不误阻断; 主干最新提交本就要求带号)。
if [ -n "${before:-}" ] && [ "$before" != "$zero" ] \
&& git cat-file -e "${before}^{commit}" 2>/dev/null \
&& git cat-file -e "${after}^{commit}" 2>/dev/null; then
range="$before..$after"
echo "== 校验新增提交区间 $range =="
git log --oneline "$range" || true
bash scripts/check-commit-issue.sh --range "$range"
else
echo "== 无法确定可靠区间(新分支/force-push/fork),回退仅校验 HEAD =="
git log --oneline -1
bash scripts/check-commit-issue.sh --head-only
fi
backend-lint-test:
runs-on: ubuntu-latest
steps:

View File

@@ -161,7 +161,10 @@ csctalon/
| 规范 | `docs/standards/` | 错误码 / 审查清单 / 任务派工单模板 / 工单驱动工作流 |
| 文档组织 | `docs/README.md` | docs 目录职责 / 文件命名规范 / 运维手册分发 |
| 测试 | `backend/tests/` | pytest 基建, 独立 `csctalon_test` 库, 隔离开发/生产 |
| CI | `.gitea/workflows/` | gitleaks 凭证扫描 + backend-test(ruff `app/`+`tests/` · **空库 alembic upgrade head** · pytest,强制) |
| CI | `.gitea/workflows/` | gitleaks 凭证扫描 + backend-test(**提交信息工单号校验** · ruff `app/`+`tests/` · **空库 alembic upgrade head** · pytest,强制) |
> **提交钩子(建议安装)**:`bash scripts/install-gitleaks-hook.sh`(pre-commit 扫凭证)+
> `bash scripts/install-commit-msg-hook.sh`(commit-msg 强制提交信息带 `#工单号`,#216)。
**协作流程**: 需求/事件 → **建 Gitea 工单**(见 [docs/standards/issue-workflow.md](docs/standards/issue-workflow.md),事故拆单:incident + governance)→ 架构师出设计 → 人工锁定(`docs/arch/` + git tag) → 开发者实现(提交/PR 关联 `#issue号`)→ 审查员审查 → 人工终审合并。测试与 lint 由 CI 机器验证, 不依赖 AI 口头声称。

View File

@@ -17,6 +17,11 @@
3. **一单一目标**。事故类必须拆成「止损工单」与「治理工单」两条,禁止混单。
4. **工单必须写「关闭条件」**。没有关闭条件的工单视为无效工单。
5. **代码变更必须关联工单**。提交信息、PR 描述须携带 `#issue号`,否则审查不通过。
本地 `commit-msg` 钩子与 CI(`commit-message-lint`)双重校验(#216):安装钩子执行
`bash scripts/install-commit-msg-hook.sh`(与 gitleaks 的 pre-commit 相互独立);
判据脚本 `scripts/check-commit-issue.sh` 两处同源。merge 提交自动放行;确无工单的纯维护/
版本号提交须在信息中显式写 `[skip-issue]`;紧急可用 `git commit --no-verify`(会同时绕过
gitleaks,慎用)。CI 只校验**本次新增**提交,不追溯历史。
6. **禁止无凭证关闭**。关闭工单前必须先追加 `## 关闭凭证核验` 评论,逐条对应关闭
条件并给出可复现的核验方式(测试名 / commit sha / 实测命令);批量关闭时每张
工单各写一条。详见 §四点一。

View File

@@ -390,7 +390,7 @@ sudo rm -f /opt/csctalon/backups/pre-org-20260914.dump # 108M (09
| R-P2-6 | P2 | L-3(2031 乱年份 3 行 ¥203)决策悬置 | ✅ 脚本已落地(#209 `7a7bc25`;前置缺陷 #220 `ad7ddf5`),生产随发布打标 | 报告 §4.4;#207 已拍板选项③(只打标、不改 register_time/口径):scripts/mark_manual_future_year_209.py 经 bootstrap+db_guard,默认 dry-run、行数安全门(--expected 3 不符拒绝)、写 dup_residual+note、幂等;5 单测 + test 库端到端(3 行¥203 打标/拒绝/幂等/清理)全绿。遗留:生产 3 行随发布执行 `--mode prod --apply --allow-real-db` 后回贴 #209/#207。实施中另修 bootstrap 导入时序致切库失效的缺陷(#220,影响所有非默认模式脚本,新增 2 子进程回归测试) |
| R-P2-7 | P2 | LLM 审计无 token/成本字段;无问答级缓存 | 待处理 | 报告 §3.2 INFO |
| R-P2-8 | P2 | API 文档端点数 ~374 vs 实际 520(+39%) | ✅ 已完成(#215,2026-10-07) | 报告 §3.6 W3;以 `app.openapi()` 实盘(较台账清点时已再增长):**449 个路径 / 547 个 method 操作**(448 个 `/api/v1` 业务路径 + `/health`);API 文档升 v1.2、README §6.3 同步校正,均附可复算命令;REF 明细以代码为准、不逐条重列(避免再失真) |
| R-P2-9 | P2 | 无号提交 ~50%;台账行与 #191~#197 登记缺失 | 待处理 | 报告 §3.6 W4/INFO;治理(hook/CI)+ 补录 |
| R-P2-9 | P2 | 无号提交 ~50%;台账行与 #191~#197 登记缺失 | ✅ 已完成(#216,2026-10-07) | 报告 §3.6 W4/INFO;① 判据单一脚本 `scripts/check-commit-issue.sh`(带号 / `[skip-issue]` / merge 放行),本地 commit-msg hook 安装器 `scripts/install-commit-msg-hook.sh`(与 gitleaks pre-commit 独立,临时仓正/负/跳过/--no-verify 全测);② CI test.yml 新增 `commit-message-lint` 轻量 job,校验本次新增提交(before..after / PR base..head,区间不可得回退仅查 HEAD,不误阻断、不追溯历史);③ 台账新增 §十七据 Gitea API+git log 考据补录 #191~#197(4 关闭均有凭证评论、3 open);④ issue-workflow.md §1.5 + README 补提交信息规范与钩子安装。守不做项:未改写历史提交 |
| R-P2-10 | P2 | `scripts/_bootstrap.py` 路径与实际不符;顶层 `scripts/` 0 处 bootstrap | 待处理 | 报告 §3.6 W2;涉 AGENTS 改动须人工审核 |
| R-P2-11 | P2 | 备份异地未配置、恢复演练未做 | 待处理 | 报告 §3.5 W7;配置异地存储 + 排期演练 |
| R-P2-12 | P2 | web 无 healthcheck;四服务无资源限额;nginx 宿主日志未见轮转 | 待处理 | 报告 §3.5 W8;逐项加固 |
@@ -441,3 +441,18 @@ sudo rm -f /opt/csctalon/backups/pre-org-20260914.dump # 108M (09
| S-P2-4 | P2 | 3 个 .ts 零类型(checkJs:false);api 层零覆盖 | 待处理 | 报告 §4.4 |
| S-P2-5 | P2 | 运营策略硬编码(sync_task_manager:386 等 4 处)须发版 | 待处理 | 报告 §4.1 |
| S-P2-6 | P2 | 发票/微信注册数据源未入待办 | 待处理 | 报告 §4.2 |
## 十七、工单补录:#191~#197(#216,R-P2-9)
> 2026-10-07 据 Gitea API + `git log` 考据补记(R 报告 §4.x「台账 header 失真、#191~#197 无登记行」)。
> 状态为 **Gitea 实查**(非转述报告——报告评审时无 API 凭证,曾注「#195~#197 关闭凭证无法线上核验」;本次持 token 核实:4 张已关闭工单**均有「关闭凭证核验」评论**)。仅补登记,不改工单状态、不追溯历史提交。
| 工单 | 标题(摘要) | Gitea 状态(2026-10-07 实查) | 考据依据(commit / 文档 / 凭证) |
|---|---|---|---|
| #191 | E 路定时采集恒 0 行:0 行当成功 + 无时效监控 + 低频档窗口写死昨天 | 🔵 open(4 评论,**含凭证评论**) | E 路时效闭环已落地:`5bf4325`/`6c369a7`(档位按源可配小时/窗口 + 0 行/时效告警 + AI T+1)、`f42fcb1`(低频回填看自身记录)、`1cd8f19`(基准日随采集小时消误报);只读核验脚本 `4e0a31a`。open 疑为持续观测/生产确认未闭环 |
| #192 | 命中链路把「缺维度空壳快照」当有效命中(74~88 字"暂无"回答) | ✅ closed 2026-09-24(1 评论,**有凭证**) | `a579138`(空壳快照门槛)、`d1f1162`(命中门槛收紧为"问什么必须有什么"+ 占比/字数双判据) |
| #193 | AI 报告刷新频率与命中量错配(日预生成 100+ vs 命中 2~10) | 🔵 open(5 评论,**无关闭凭证**) | 联动 **R-P0-3 / #204**:P1+P2 已实施处于 dry_run 影子期(`3630c30`),方案 `docs/design/20260924-#193-AI报告刷新策略-无新事件跳过-实现方案.md`;生产验收数据待补,**不重开、不静默关**(见 R-P0-3 行) |
| #194 | 本地遗留同名库 csctalon 处置:备份 + 根 .env 对齐 dev + 改名 | ✅ closed 2026-09-24(1 评论,**有凭证**) | 运维处置类(备份/改名/环境对齐),无代码 commit 属正常;凭证见工单评论 |
| #195 | 315 共享文档解析治理:脏值清洗增强 + 自由文本投诉日期补救 | ✅ closed 2026-09-25(1 评论,**有凭证**) | `90b48c1`(确定性恢复 + 批次传播 + 提示词沉淀);样本号码 PII 净化另见 #211 凭证遗留项 |
| #196 | 退款数据治理:采集偏差检测 + 数据异常修复 | 🔵 open(0 评论,**无凭证**) | 审计服务 `63498ef`(采集对账+日期跳跃+负数检测)、修复 `5f5a67a`(YY/MMDD 解析 + 空日期邻近填充);报告 `docs/reports/20260926-退款日期缺陷治理.md`;《仓库入库规范》§五 checklist 尚有未勾项(结论报告落档)——open 与该收尾未完成一致 |
| #197 | gen_system v2.1:派生字段系统侧注入 + 122b 降级修正 | ✅ closed 2026-09-27(2 评论,**有凭证**) | `a67bb76`(v2.1 落地)、`e175646`/`354c18f`(ruff/CI 兼容修复)、`99841af`(提示词迭代 4 份报告入库) |

86
scripts/check-commit-issue.sh Executable file
View File

@@ -0,0 +1,86 @@
#!/usr/bin/env bash
# 提交信息工单号校验(#216 / R-P2-9)
#
# 本地 commit-msg 钩子与 Gitea CI 共用本脚本,保证两处判据完全一致(单一事实来源):
# bash scripts/check-commit-issue.sh --message-file <file> # 校验单条信息(commit-msg hook)
# bash scripts/check-commit-issue.sh --range <revspec> # 校验区间内每条提交(CI,如 before..after)
# bash scripts/check-commit-issue.sh --head-only # 仅校验 HEAD(CI 无法确定区间时的回退)
#
# 判据(任一满足即放行该条提交):
# 1) merge 提交:subject 以 "Merge " 开头(git/github/gitea 自动合并产生,天然无工单);
# 2) 提交信息全文含显式跳过标记 [skip-issue](大小写不敏感)——仅限纯维护/版本号 bump 等
# 确无工单的场景,留下可审计的显式声明;
# 3) 提交信息含 "#" + 数字(#123)。
# 其余一律判为不合规;存在任一条不合规即退出码 1(空区间视为通过)。
set -euo pipefail
ISSUE_RE='#[0-9]+'
SKIP_RE='\[skip-issue\]'
# check_one <标识>,提交信息从 stdin 读入;合规返回 0,不合规打印一行并返回 1。
check_one() {
local ident="$1" msg subject
msg="$(cat)"
subject="$(printf '%s\n' "$msg" | sed -n '1p')"
# 1) merge 提交(英文 "Merge ...";中文合并极少出现在自动提交里,一并兼容大小写)
case "$subject" in
"Merge "*|"merge "*) return 0 ;;
esac
# 2) 显式跳过标记(全文、大小写不敏感)
if printf '%s\n' "$msg" | grep -Eiq "$SKIP_RE"; then
return 0
fi
# 3) 携带工单号
if printf '%s\n' "$msg" | grep -Eq "$ISSUE_RE"; then
return 0
fi
printf ' ✗ %s\n 首行: %s\n' "$ident" "$subject" >&2
return 1
}
check_sha() {
local sha="$1" rc=0 ident
ident="$(git log -1 --format='%h %s' "$sha")"
if ! git log -1 --format=%B "$sha" | check_one "$ident"; then
rc=1
fi
return "$rc"
}
main() {
local mode="${1:-}" arg="${2:-}" rc=0 sha
case "$mode" in
--message-file)
[ -n "$arg" ] || { echo "用法: $0 --message-file <file>" >&2; exit 2; }
if ! check_one "commit-msg" < "$arg"; then rc=1; fi
;;
--range)
[ -n "$arg" ] || { echo "用法: $0 --range <revspec>" >&2; exit 2; }
# 逐个提交校验;收集所有违规而非遇第一条即停,便于作者一次改全。
while IFS= read -r sha; do
[ -z "$sha" ] && continue
if ! check_sha "$sha"; then rc=1; fi
done < <(git rev-list "$arg")
;;
--head-only)
if ! check_sha "HEAD"; then rc=1; fi
;;
*)
echo "用法: $0 --message-file <file> | --range <revspec> | --head-only" >&2
exit 2
;;
esac
if [ "$rc" -ne 0 ]; then
{
echo ""
echo "❌ 上述提交信息缺少工单号(规范: 信息中写 #工单号,如 'fix(#123): ...')。"
echo " 确无工单的纯维护/合并提交,请在信息中显式加 [skip-issue];merge 提交自动放行。"
} >&2
fi
exit "$rc"
}
main "$@"

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# 安装 commit-msg 钩子:提交信息必须携带 #工单号(#216 / R-P2-9)
# 用法: bash scripts/install-commit-msg-hook.sh
#
# 与 pre-commit(gitleaks,见 scripts/install-gitleaks-hook.sh)是两个独立钩子文件,
# 互不影响、可分别安装;判据统一调用入库脚本 scripts/check-commit-issue.sh,
# 因此判据更新随仓库走,无需重装钩子。
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
echo "❌ 不在 git 仓库中" >&2
exit 1
}
HOOK_DIR="$REPO_ROOT/.git/hooks"
HOOK_FILE="$HOOK_DIR/commit-msg"
CHECK_SCRIPT="$REPO_ROOT/scripts/check-commit-issue.sh"
if [ ! -f "$CHECK_SCRIPT" ]; then
echo "❌ 找不到 $CHECK_SCRIPT(请在仓库根目录执行本脚本)" >&2
exit 1
fi
mkdir -p "$HOOK_DIR"
# 注意用字面量 heredoc('EOF'):hook 内的 $1 / $(...) 必须在触发时而非安装时展开。
cat > "$HOOK_FILE" <<'EOF'
#!/usr/bin/env bash
# commit-msg: 校验提交信息携带 #工单号(#216)
# 判据见 scripts/check-commit-issue.sh(本地与 CI 同源)。
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
bash "$REPO_ROOT/scripts/check-commit-issue.sh" --message-file "$1"
EOF
chmod +x "$HOOK_FILE"
echo "✅ commit-msg 工单号钩子已安装: ${HOOK_FILE}"
echo " 判据脚本: ${CHECK_SCRIPT}(随仓库更新,无需重装)"
echo " 跳过方式: 提交信息加 [skip-issue](显式声明),或紧急时 git commit --no-verify"
echo " (--no-verify 会同时绕过 gitleaks pre-commit,慎用)"