Pre-commit 质量门禁手册¶
本仓库使用 pre-commit 框架,在 git commit / git commit-msg 两个阶段自动运行 15 项质量检查,覆盖后端(Python)与三前端(agent-system / admin-system / infinitecanvas)。本文档逐一说明:每条规则的作用、对应代码与配置位置、代码做了什么、带来的好处、失败时如何处理,并附技术债治理流程。
配置入口:
.pre-commit-config.yaml技术债追踪:docs/debt/quality-gates.md
一、总览:门禁流水线¶
提交一次代码会依次经过以下两层(顺序即执行顺序):
┌──────────────────────────── git commit 阶段(file 阶段)───────────────────────────┐
│ ① trailing-whitespace ② end-of-file-fixer ③ check-yaml ④ check-toml │
│ ⑤ check-merge-conflict ⑥ detect-private-key ⑦ ruff(--fix) ⑧ ruff-format │
│ ⑨ bandit ⑩ file-length ⑪ mypy ⑫ gitleaks │
│ ⑬ import-linter ⑭ alembic-migration-safety ⑮ frontend-check │
└───────────────────────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────── git commit-msg 阶段 ──────────────────────────────────┐
│ ⑯ commitlint(conventional commits 校验) │
└───────────────────────────────────────────────────────────────────────────────────┘
执行原则¶
| 原则 | 说明 |
|---|---|
| 零配置可复现 | 所有规则版本钉死在 .pre-commit-config.yaml(如 ruff v0.8.4),团队成员所见即所得 |
| 暂存区感知 | frontend-check 只对本次 git add 的前端目录运行,未改不跑,秒级反馈 |
| 自动修复优先 | trailing-whitespace / end-of-file-fixer / ruff --fix / ruff-format 会自动改写文件并重新暂存,无需手动处理 |
| 不阻塞即警告 | frontend-check 的 max-lines-per-function、no-console 为 warn 级,不阻断提交 |
| CI 兜底 | 本地未安装的工具(如 gitleaks)会跳过,但 GitHub Actions 的 gitleaks-action 仍会扫描 |
安装与首次运行¶
make install # 含 pre-commit install(钩子写入 .git/hooks/)
# 或手动
poetry install
poetry run pre-commit install
poetry run pre-commit install --hook-type commit-msg # 启用 commitlint
poetry run pre-commit run --all-files # 全量自检
重要:
commitlint挂在commit-msg阶段,需单独pre-commit install --hook-type commit-msg才生效。仅装pre-commit install不会校验提交信息。
二、基础卫生(pre-commit-hooks 官方包)¶
来源:pre-commit/pre-commit-hooks rev: v5.0.0。这是一组零依赖、跨语言的通用卫生检查。
① trailing-whitespace — 删除行尾空格¶
- 作用:删除所有文本文件行尾的多余空格与 Tab
- 行为:自动改写文件(
autofix),改完会重新git add - 好处:消除无意义的 diff 噪音;避免某些老式编辑器/终端因行尾空格产生奇怪渲染;防止 merge 冲突
- 失败处理:无需处理,已自动修复。提交即可
② end-of-file-fixer — 文件末尾确保一个换行¶
- 作用:每个文件以恰好一个换行符结尾
- 行为:自动改写文件
- 好处:POSIX 规范要求文本文件以换行结尾;部分工具(wc、cat、shell 循环)读到无换行的末行会拼接;git diff 末行更清晰
- 失败处理:自动修复
③ check-yaml — YAML 语法校验¶
- 配置:
args: [--allow-multiple-documents]允许---分隔的多文档 YAML(CI/Compose 场景常见) - 作用:解析所有
.yaml/.yml,语法错误即失败 - 好处:CI workflow、K8s manifest、docker-compose、MkDocs 配置在提交时就拦截语法错误,避免部署到 K8s 才发现
apiVersion拼错 - 失败处理:按报错行号修正缩进/冒号/引号
④ check-toml — TOML 语法校验¶
- 作用:解析所有
.toml(重点是pyproject.toml) - 好处:
pyproject.toml是 Poetry/Ruff/mypy/pytest/import-linter/bandit 的单一配置源,一个语法错误会让整条工具链瘫痪。提前拦截 - 失败处理:修正 TOML 语法
⑤ check-merge-conflict — 拦截残留冲突标记¶
- 作用:检测文件中是否含
<<<<<<</=======/>>>>>>>冲突标记 - 好处:防止开发者解决冲突时漏删标记,把
>>>>>>> HEAD提交进代码 - 失败处理:打开文件删除冲突标记,保留正确内容
⑥ detect-private-key — 拦截私钥提交¶
- 作用:检测 PEM 格式私钥文件头(RSA / OPENSSH / EC 等的
BEGIN行,下含 base64 密钥块),正则为BEGIN后跟PRIVATE KEY - 好处:第一道防线,防止 SSH/AWS/GPG 私钥误入仓库(与 gitleaks 形成纵深防御)
- 失败处理:私钥移出仓库,加入
.gitignore;若已提交需git filter-repo清理历史并轮换密钥
三、Python:Linter + Formatter(Ruff)¶
来源:astral-sh/ruff-pre-commit rev: v0.8.4。Ruff 用 Rust 实现,比 flake8 + isort + Black 快 10-100 倍,本项目用它一站式替代 Black / isort / flake8 / pyupgrade。
⑦ ruff(--fix)— Linter + 自动修复¶
- 配置:pyproject.toml
[tool.ruff.lint] - 启用规则集:
| 代码 | 规则集 | 检查内容 |
|---|---|---|
E W |
pycodestyle | PEP 8 风格(缩进、空格、行长 100) |
F |
Pyflakes | 未使用变量、未使用 import、重复定义、f-string 缺占位符 |
I |
isort | import 排序与分组(first-party: backend/messaging/storage) |
B |
bugbear | 常见陷阱(可变默认参数、except: 裸捕获、循环变量泄漏) |
C4 |
comprehensions | 推导式简化(list(x) → [x]、dict() → {}) |
UP |
pyupgrade | 升级到现代 Python 语法(dict() → {}、Optional[X] → X \| None) |
SIM |
simplify | 布尔简化、嵌套 if 合并 |
RUF |
ruff-specific | RUF010(显式转换标志)、RUF013(隐式 Optional)等 |
- 忽略:
B008(FastAPIDepends()在默认参数合法)、RUF001/002/003(中文日志/注释允许全角标点) --fix行为:能自动修的(如import排序、删未用 import)直接改写文件- 好处:统一代码风格、捕捉真实 bug(B 系列)、自动升级语法(UP 系列)、单工具替代多工具
- 失败处理:
- 看错误码(如
F401= 未用 import),多数可poetry run ruff check --fix <file>自动修 - 逻辑类(B/SIM)需手动改代码
- 误报可用
# noqa: <code>行内豁免并写明原因
⑧ ruff-format — 代码格式化¶
- 配置:
[tool.ruff.format]—quote-style = "double",indent-style = "space" - 作用:统一引号、缩进、换行(等价 Black,但与 Ruff lint 共享解析器,零冲突)
- 行为:自动改写文件
- 好处:消灭"格式之争",全团队代码长得一模一样,diff 只剩逻辑改动
- 失败处理:自动修复,无需介入
四、Python:安全扫描(Bandit)¶
⑨ bandit — Python 安全漏洞扫描¶
- 配置:
args: [-c, pyproject.toml, -r, src]+ pyproject.toml[tool.bandit] - 扫描范围:递归扫
src/(backend / messaging / storage / commons) - 排除:
tests(测试允许 assert)、.venv - 跳过:
B101(assert)——测试中合法 - 检测项(节选):
| 规则 | 含义 |
|---|---|
| B102 | exec() 使用 |
| B301/B302 | pickle / marshal 反序列化(远程代码执行风险) |
| B602/B603 | subprocess shell 注入 |
| B311 | random 用于密码学(应改用 secrets) |
| B324 | hashlib.md5 用于加密 |
pass_filenames: false:对全量src/扫描,而非仅暂存文件(避免漏扫依赖文件)- 好处:在提交时拦截低级安全错误,避免 secret 硬编码、命令注入、弱随机数进生产
- 失败处理:按报告修改;误报用
# nosec行内豁免并注释原因
五、Python:文件长度(本地 hook)¶
⑩ file-length — 单文件有效行数 ≤ 500¶
- 入口:
poetry run python scripts/check_file_length.py - 源码:scripts/check_file_length.py
- 算法:
- 扫描
.py/.ts/.tsx(跨语言统一,前后端同一把尺) count_effective_lines:不计空行、不计整行注释(与 ESLintskipBlankLines/skipComments对齐)- 超过
MAX_LINES = 500即 FAIL - 豁免:
SKIP_REL_PATHS(仅seed_data配置数据)+SKIP_DIRS(node_modules 等) - 技术债追踪子命令:
--list-debt输出豁免清单及各文件当前行数,供 docs/debt/quality-gates.md 核对 - 为什么 500:见
AGENTS.md「文件体量约定」——单文件 ≤ 400(推荐)/ 500(硬上限),单文件单职责,避免上帝对象 always_run: true+pass_filenames: false:全仓库扫描(不依赖暂存区),保证已提交的大文件也被持续监控- 好处:客观、跨语言的复杂度红线,强制拆分大文件 → 可读性、可测试性、可 review 性同步提升
- 失败处理:按职责拆分文件(barrel index / 子组件 / utils 抽取),参考 docs/debt/quality-gates.md 的拆分案例
六、Python:静态类型检查(mypy)¶
⑪ mypy — 强制类型标注¶
- 配置:pyproject.toml
[tool.mypy] - 全局:
python_version = "3.12",strict = true,plugins = ["pydantic.mypy"] - 检查范围:
packages = ["backend", "messaging", "storage"](含 Pydantic 模型字段类型校验) - 渐进式严格:
backend.*/storage.*:暂放宽(strict = false),关闭no-untyped-def等,待类型补齐再收紧messaging.*:全量 strict- 第三方无类型库(redis/testcontainers/qrcode 等):
ignore_missing_imports = true always_run: true:每次提交全量类型检查- 好处:类型即文档;IDE 补全更准;重构有编译期保障;Pydantic 模型字段类型错误提前暴露
- 失败处理:按报错补类型标注;第三方库缺类型用
# type: ignore[<code>]并注释原因
七、Python:密钥泄露扫描(gitleaks)¶
⑫ gitleaks — 暂存区密钥/Token 扫描¶
- 入口:
poetry run python scripts/gitleaks_precommit.py - 源码:scripts/gitleaks_precommit.py
- 行为:调用
gitleaks protect --verbose --redact --staged --staged:只扫暂存区(快)--redact:日志中密钥部分打码- 未安装降级:本地没装 gitleaks 时打印警告并跳过(返回 0),不阻塞开发;CI 的
gitleaks-action兜底 - 检测能力:AWS AK/SK、阿里云 AK/SK、GitHub Token、私钥、Stripe Key、Slack Token 等数百种(内置规则库 + 正则)
- 好处:纵深防御的第二道防线(配合
detect-private-key),防止.env、config.json、硬编码 token 进仓库 - 失败处理:
- 移除密钥,改用环境变量 /
.env(已 gitignore) - 误报在
.gitleaksignore登记规则 ID - 已泄露:
git filter-repo清历史 + 立即轮换密钥
八、Python:架构分层约束(import-linter)¶
⑬ import-linter — 模块依赖方向约束¶
- 配置:pyproject.toml
[tool.importlinter] - 约束(6 条契约):
| 契约 | 含义 | 违反示例 |
|---|---|---|
| backend API 不得依赖 db | API 层只能经 Service/Repository 访问数据 | from backend.db import X 出现在 backend/api/*.py |
| backend core 不得依赖 api | 业务层不应反向调用路由 | backend/core/*.py import backend.api |
| backend db 不得依赖 api/core | ORM/Repo 层是底层,不应知上层存在 | backend/db/models/*.py import backend.core |
| messaging 不得依赖 backend | 消息组件是独立可复用包 | messaging import backend |
| storage 不得依赖 backend | OSS 微服务独立部署,AK/SK 仅在 storage | storage import backend |
| storage core 不得依赖 api | storage 内部分层 | storage.core import storage.api |
root_packages:backend / messaging / storage / commons四个根包allow_indirect_imports = true:仅校验直接 import(避免误伤__init__re-export 链)always_run: true:每次提交全量校验依赖图- 好处:强制分层架构(API → core → db 单向),防止循环依赖、防止跨域走私、保证微服务独立性
- 失败处理:按违反方向重构——把共享逻辑下沉到
commons或正确层级,不要用# noqa绕过
九、Python:数据库迁移安全(Alembic)¶
⑭ alembic-migration-safety — 拦截高风险 DDL¶
- 入口:
poetry run python scripts/check_alembic_migrations.py - 源码:scripts/check_alembic_migrations.py
- 触发条件:
files: ^alembic(_backend)?/versions/.*\.py$(只在该改迁移时跑) - 扫描目录:
alembic/versions+alembic_backend/versions - 拦截的高危操作(在
upgrade()函数体内):
| 模式 | 风险 |
|---|---|
op.drop_column( |
直接删列会丢数据,须先停写、迁移、删列三步走 |
op.drop_table( |
同上 |
op.alter_column(..., type_=...) |
改列类型无数据迁移会截断/报错 |
- 豁免机制:行尾加
# alembic:allow-danger: 原因(必须写原因) - 配套规范:docs/standards/database-migrations.md
- 好处:PostgreSQL 大表在线 DDL 事故绝大多数源于直接 drop/alter,此门禁强制"安全三步走",保数据不丢
- 失败处理:
- 改为零停机方案(加新列 → 双写 → 迁移 → 切换 → 删旧列)
- 确需危险操作:行尾加
# alembic:allow-danger: <充分理由>,并在 PR 中说明
十、前端质量门禁(本地 hook)¶
⑮ frontend-check — 三前端统一门禁调度¶
- 入口:
poetry run python scripts/frontend_precommit.py - 源码:scripts/frontend_precommit.py
- 触发条件:
files: ^src/(agent-system|infinitecanvas|admin-system)/(仅当前端文件被暂存) - 声明式架构:用
FrontendTargetdataclass 统一描述,避免三份 if/elif
暂存区感知(关键优化)¶
读取 git diff --cached --name-only,只有暂存文件命中某前端前缀,该前端才执行。改后端只跑后端门禁,改 admin-system 只跑 admin-system,互不干扰,反馈秒级。
三前端步骤矩阵¶
| 前端 | runner | typecheck | lint | format-check | arch |
|---|---|---|---|---|---|
| agent-system | pnpm | ✅ | ✅ | ✅ | ✅ |
| admin-system | pnpm | ✅ | ✅ | ✅ | ✅ |
| infinitecanvas | npm | ✅ | — | — | — |
test因较慢(秒级→分钟级)不进 pre-commit,由make *-check与 CI 负责。
各步骤详解¶
typecheck(pnpm typecheck → tsc --noEmit):
- TypeScript 编译期类型检查,无输出文件
- 拦截类型错误、缺失 import、隐式 any
- 好处:前端也有"编译期"保障
lint(pnpm lint → ESLint flat config):
- 配置:src/admin-system/eslint.config.js(admin-system 示例)
- 核心规则:
| 规则 | 级别 | 作用 |
|---|---|---|
max-lines: 500 |
error | 单文件 ≤ 500 有效行(与后端 file-length 对齐,双保险) |
max-lines-per-function: 80 |
warn | 函数 ≤ 80 行(超长提示,不阻断) |
no-console |
warn | 生产代码不应留 console.log |
@typescript-eslint/no-unused-vars |
error | 未用变量(_ 前缀豁免) |
react-hooks/rules-of-hooks |
error | Hooks 使用规范 |
format-check(pnpm format-check → Prettier --check):
- 配置:各前端 .prettierrc.json
- 统一引号、缩进、尾逗号,与 Ruff format 对应
- 失败处理:pnpm exec prettier --write <file>
arch(pnpm arch → dependency-cruiser):
- 配置:src/admin-system/dependency-cruiser.config.mjs
- 三条契约:
| 规则 | 含义 |
|---|---|
features-no-cross-import |
features 之间禁止互相 import(同 feature 内子目录允许) |
shared-not-features |
shared 层不得依赖 features(避免反向依赖) |
no-direct-axios |
axios 仅允许在 shared/api/httpClient.ts / adminHttpClient.ts 中使用,强制 HTTP 请求走统一客户端 |
- 正则技巧:
features-no-cross-import用from: '^src/features/([^/]+)/'捕获 feature 名,to.pathNot: '^src/features/$1/'排除自身($1反向引用),实现"禁止跨 feature、允许同 feature 内部" -
好处:前端也有架构红线,防止 features 耦合、防止散落的 fetch/axios 绕过统一拦截器(鉴权、错误处理、日志)
-
失败处理:按步骤标签(如
admin-system lint)定位,进入对应前端目录运行pnpm <step>看详情
十一、提交信息规范(commitlint)¶
⑯ commitlint — Conventional Commits 强制¶
- 阶段:
commit-msg(与上面 15 个 hook 不同阶段,需pre-commit install --hook-type commit-msg) - 入口:
npx --no-install -- @commitlint/cli --edit - 配置:commitlint.config.cjs + package.json(根级 devDependencies)
- 继承:
@commitlint/config-conventional
格式¶
<type>(<scope>): <subject>
↑ ↑ ↑
必需 可选 必需
type 白名单¶
| type | 含义 |
|---|---|
feat |
新功能 |
fix |
修复 bug |
refactor |
重构(非新功能、非修 bug) |
perf |
性能优化 |
style |
格式调整(不改逻辑) |
test |
测试相关 |
docs |
文档 |
build |
构建系统/依赖 |
ci |
CI 配置 |
chore |
杂项(不修改 src 或 test) |
revert |
回滚提交 |
本仓库自定义放宽¶
subject-case: [0]:关闭首字母大小写限制(中文 subject 无大小写概念)header-max-length: 100:首行 ≤ 100 字符- 兼容
feat!:破坏性变更标记、feat(admin):scope
示例¶
git commit -m "feat(admin): 拆分 AiModelsPage 为 3 个子组件"
git commit -m "fix: 修复 OSS 批量删除空指针"
git commit -m "refactor(api): adminApi 改为 barrel index"
git commit -m "docs: 补充 pre-commit 门禁手册"
- 好处:
- 提交历史可机器解析 → 自动生成 changelog
- 配合 GitHub Actions(
auto-merge.yml)按 type 路由 - 强制写清"这次改了什么类别的什么"
- 历史迁移:仓库早期用
[FIX]/[FEAT]方括号风格,自 commitlint 引入起统一为 conventional,历史不回填 - 失败处理:按提示改首行前缀为合法 type
十二、门禁分层与 CI 关系¶
| 层级 | 触发 | 速度 | 覆盖 | 跳过项 |
|---|---|---|---|---|
| pre-commit(file) | 每次 git commit |
秒-十秒 | 全部门禁(不含 test) | — |
| pre-commit(commit-msg) | 每次提交信息 | 毫秒 | commitlint | — |
make check |
手动/CI | 分钟 | 后端全量(含 test) | — |
make *-check |
手动/CI | 分钟 | 各前端全量(含 test + build) | — |
| GitHub Actions | push/PR | 分钟 | 全量 + gitleaks-action + pip-audit | — |
设计意图:pre-commit 追求快与拦截低级错误,test/build/audit 等"重活"下沉到 make 与 CI,避免每次提交等数分钟。
十三、技术债治理流程¶
门禁不是"设了就完",豁免清单会随业务演进。本项目建立单一来源 + 文档追踪 + 定期核对三位一体机制。
13.1 单一来源原则¶
历史上文件长度豁免有两份清单(check_file_length.py 的 SKIP_REL_PATHS + eslint.config.js 的 max-lines overrides),易漂移。2026 年治理后业务豁免全部清空,仅留 seed_data 配置类。
| 场景 | 唯一来源 |
|---|---|
| Python + TS/TSX 文件长度 | scripts/check_file_length.py 的 SKIP_REL_PATHS |
| 前端 ESLint 额外规则 | eslint.config.js(不再用 overrides 豁免行数) |
13.2 技术债追踪文档¶
所有豁免必须登记在 docs/debt/quality-gates.md,含:文件、原因、负责人、预计消除时间。未登记的豁免视为违规。
13.3 自动核对¶
poetry run python scripts/check_file_length.py --list-debt
输出豁免清单 + 各文件当前有效行数 + 状态(OK / OVER)。建议每月核对一次,已达标项立即移除豁免。
13.4 新增豁免流程¶
- 评估必要性:能否拆分/重构消除,而非豁免?
- 登记:在
docs/debt/quality-gates.md对应章节写明文件、原因、负责人、预计消除时间 - 配置:在
SKIP_REL_PATHS或eslint.config.jsoverrides 添加条目 - PR 说明:在 PR 描述中说明豁免理由与消除计划
- 定期回顾:每月
--list-debt核对,达标即移除
13.5 消除豁免流程(拆分大文件)¶
参考 2026 年完成的 6 个大文件拆分案例(详见 docs/debt/quality-gates.md 第一节):
| 拆分手法 | 适用场景 | 案例 |
|---|---|---|
| barrel index | 聚合层 API 文件 | adminApi.ts → admin/ 下 15 域文件 + barrel re-export |
| 子组件抽取 | 单大组件 | AiModelsPage → 主页 + FormPanel + EditRow + KeyRow |
| Tab 拆分 | 多 Tab 页面 | SystemConfigPage → 3 个 Tab 组件 |
| utils 抽取 | 纯函数混在组件 | flattenContentTree / buildProductTree / maskKey |
| shared 常量 | 多组件共用常量 | panelShared.tsx(IconEdit + PANEL_TITLES) |
每步拆分后立即跑 typecheck + lint + format-check + arch 验证,确认无回归再继续下一步。
十四、常见问题¶
Q1:pre-commit 很慢怎么办?¶
- 后端文件改动只跑后端 hook(frontend-check 因暂存区不匹配会跳过)
- 前端改动同理
- 若首次
--all-files慢属正常(全量扫描),日常 commit 是增量的
Q2:如何临时跳过某个 hook?¶
git commit --no-verify # 跳过所有 hook(不推荐,CI 仍会检查)
SKIP=mypy git commit # 跳过指定 hook(仅本地)
警告:
--no-verify绕过的代码若 CI 失败仍会 block 合并。仅用于 WIP 快照提交。
Q3:gitleaks 报警告"未找到可执行文件"¶
本地未装 gitleaks,hook 自动跳过(返回 0)。CI 的 gitleaks-action 会兜底。安装见 gitleaks 官网。
Q4:commitlint 没生效?¶
需单独安装 commit-msg 钩子:
poetry run pre-commit install --hook-type commit-msg
仅 pre-commit install 不会启用 commitlint。
Q5:ruff 和 ruff-format 冲突?¶
不会冲突。两者共享 Ruff 的解析器,ruff-format 是 Black 兼容的格式化器,ruff --fix 只做 lint 修复(如删未用 import),分工明确。
Q6:如何添加新 hook?¶
- 在
.pre-commit-config.yaml添加repo/hooks - 本地 hook(
repo: local)需把脚本放scripts/并写entry - 运行
poetry run pre-commit run <hook-id>验证 - 更新本文档对应章节
十五、速查:失败退出码对照¶
| Hook | 自动修复 | 常见失败原因 | 修复命令 |
|---|---|---|---|
| trailing-whitespace | ✅ | — | 自动 |
| end-of-file-fixer | ✅ | — | 自动 |
| check-yaml | ❌ | YAML 语法 | 手动改 |
| check-toml | ❌ | TOML 语法 | 手动改 |
| check-merge-conflict | ❌ | 残留 <<<<<<< |
手动删 |
| detect-private-key | ❌ | 含私钥 | 移出仓库 |
| ruff (--fix) | ✅ 部分 | lint 规则 | ruff check --fix |
| ruff-format | ✅ | 格式 | 自动 |
| bandit | ❌ | 安全规则 | 改代码 / # nosec |
| file-length | ❌ | 超 500 行 | 拆分文件 |
| mypy | ❌ | 类型错误 | 补类型标注 |
| gitleaks | ❌ | 含密钥 | 移除密钥 |
| import-linter | ❌ | 跨层依赖 | 重构分层 |
| alembic-migration-safety | ❌ | 高危 DDL | 改三步走 / alembic:allow-danger |
| frontend-check | ❌ | typecheck/lint/format/arch | 进前端目录 pnpm <step> |
| commitlint | ❌ | 非 conventional | 改提交信息首行 |
十六、参考¶
- .pre-commit-config.yaml — 门禁总配置
- scripts/frontend_precommit.py — 前端门禁调度
- scripts/check_file_length.py — 文件长度 + 技术债清单
- scripts/gitleaks_precommit.py — 密钥扫描
- scripts/check_alembic_migrations.py — 迁移安全
- commitlint.config.cjs — 提交信息规范
- pyproject.toml — Ruff/mypy/bandit/import-linter/pytest 配置
- docs/debt/quality-gates.md — 技术债追踪
- AGENTS.md — 项目导航与约定