开发规范
1. 核心原则
- 模块化与组件化: 遵循高内聚低耦合,公共逻辑迁移到
utils/或可复用模块。 - 降低耦合度: 纯函数与副作用代码分层;核心模块依赖方向单向、可注入。
- 提升复用率: 重复逻辑抽象为工具函数,删减样板代码。
- 类型安全: 全面使用 TypeScript。严禁使用
any,不确定类型时优先使用unknown+ 类型守卫。 - 显式假设原则: 需求、边界不清晰时,必须先暴露假设并澄清,禁止靠默认猜测推进实现。
- 搜索优先原则: 当需要外部信息或根因不明确时,优先搜索获取一手信息。详见 AI 协作规范。
- 最小变更原则: 聚焦目标本身,减少对无关代码的触动。
- 实用性优先: 避免过度设计。引入新功能前评估真实价值与成本。
- 决策梯子原则: 实现前按顺序判断 —
- 真的需要做吗?不需要就跳过(YAGNI)
- 代码库里已经有了?复用,别重写
- 已安装的依赖能解决?用现有依赖
- 能用 util 封装?封装复用
- 能一行搞定?一行
- 实在不行:写最少能工作的代码
2. 命名约定
| 类别 | 规则 | 示例 |
|---|---|---|
| 文件 | kebab-case.ts | app-error.ts、runtime-config.ts |
| Vue 组件 | kebab-case.vue | app-header.vue、dashboard-view.vue |
| 类型/接口 | PascalCase,优先 interface | NormalizedSecurityAlert、RuntimeConfig |
| 函数/变量 | camelCase | resolveRuntimeConfig、isValidRepoIdentifier |
| 常量 | UPPER_SNAKE_CASE | RUNTIME_MODES、SEVERITY_THRESHOLDS |
3. 注释规范
- 注释只解释关键点: 优先说明"为什么这样写""边界条件""隐含约束/副作用",不把代码表面行为复述一遍。
- 复杂逻辑必须补注释: 涉及复杂分支、状态切换、兼容性兜底、协议契约、性能或安全取舍的代码必须加注释。
- 导出函数默认应有 JSDoc: 简要说明用途、边界、返回语义与副作用。
- 禁止无效或过量注释: 不机械给每行、每个变量加注释。
- 注释必须随实现同步: 修改逻辑时同步更新或删除过时注释。
- 禁止开发流程编号标记: 注释与测试名中一律不得出现
C1:、T303、G2、M4+、R2、P0这类规划 / 任务 / 审计 / backlog 编号(含C1:xxx与it('C1: xxx')形式)。阶段与编号是规划文档(docs/plan/)中区分进度的概念,代码中无意义且无法反查;追溯用git blame/ 审计记录。例外:代码内真实存在的常量(如 HTTP 错误码E401),以及指向规划文档的导航说明(如"背景详见docs/plan/todo.md「已知缺口 G2」"、"见 todo.md G3"、"见 backlog B1")——导航指针内的规划编号属例外,因为它们提供真实可查的文档锚点,但必须同时写明文档路径或章节名,不得只写孤立编号。执行挂接:D 阶段自检(Full Stack Master (全栈大师) agent)与 A 阶段 Review Gate 必查项(Code Auditor (代码审计员) agent)均含本检查。违反案例见 经验归档 §十六。 - i18n locale 文件 insert anchor 必须用目标 locale 实际文本:locale 文件多段对称(
apps/platform/i18n/locales/zh-CN.json+en-US.json),edit 工具 insert anchor 必须用目标 locale 实际文本。自动检测:pnpm i18n:check:anchor(scripts/i18n/i18n-anchor-check.mjs)对比 zh-CN + en-US locale 文件,检测同一 key 在两边取值完全相等且 en-US locale 值含中文的错位污染(结构化本地化数据 + i18n 复合格式占位符 + 纯 ASCII 字符串视为合理相等,自动跳过)。CI test job 已添加该步骤作为 blocker。详见 经验归档 §五十六 M24.1 教训 2 +scripts/i18n/i18n-anchor-check.mjs注释。 - 同一解释只写一处: 相同背景说明(平台坑、口径、设计取舍)在仓库内只保留一处,通常放在首次出现或语义最贴近的位置;其他位置要么不写,要么用一句话指向文档。
- 详细解释放文档,代码只留短指针: 完整设计背景、复盘结论、口径变更写入
docs/design/、docs/research/或复盘文档;代码注释只保留一句"为什么"或文档指针,不展开长文。 - 简化标记约定: 主动选择简化实现时使用
// lean:标记:typescript// lean: global lock, per-account locks if throughput matters // lean: single query, batch if > 1000 items
4. 目录约束
packages/core/src/ # 核心域层,不依赖任何运行时环境
├── alerts/ # 告警标准化模型
├── errors/ # 错误模型(AppError)
├── filters/ # 告警过滤引擎
├── logger/ # 日志工具
├── planner/ # 修复规划模型
├── report/ # 报告模型
├── toolchain/ # 工具链策略
└── utils/ # 纯函数工具(不依赖外部服务)
packages/engine/src/ # 共享执行引擎(DependfixApp),cli / mcp / platform 共同依赖
├── ai/ # AI 研判(breaking change 分析、patch 生成)
├── alerts/ # 告警处理
├── app/ # DependfixApp 应用骨架
├── code-scanning/ # Code Scanning 集成
├── config/ # 配置层(多源合并、校验)
├── fixers/ # 修复器(dependency / pnpm / code-scanning)
├── github/ # GitHub API 集成
├── grouping/ # 依赖分组升级
├── helpers/ # 公共辅助
├── multirepo/ # 多仓库治理
├── report/ # 报告模型
├── runners/ # 执行器
└── verification/ # 验证链(install / lint / build)
packages/cli/src/ # CLI 入口(薄壳),编排依赖 engine
├── app/ # pipeline(本地执行编排)
├── cli/ # 参数解析与运行入口
└── skills/ # skill 编排(agents / doctor / installer / source)
packages/mcp/src/ # MCP Server,能力复用 engine
├── bin.ts # 进程入口
└── tools/ # MCP tools(run_scan / fix_dependency 等)
packages/skills/ # 产品 skill 权威源(dependfix-remediator,发布 npm)
├── dependfix-remediator/ # skill 内容(SKILL.md / REFERENCES.md)
└── test/ # 一致性测试
apps/platform/ # Nuxt 全栈平台
├── app/ # 前端(pages / components / composables / utils / layouts / middleware)
├── server/ # API 路由、数据库、服务
└── data/ # 运行时数据(不入库内容)依赖约束
packages/core/不依赖任何运行时环境(Node / 浏览器 API)与任何内部包packages/engine/承载共享执行能力(DependfixApp),内部包依赖仅@dependfix/corepackages/cli/为薄壳,依赖 engine 编排- 禁止 cli / mcp / platform 应用层之间互相依赖:mcp 曾依赖 cli(
dependfix包)导致应用层互相依赖 + 连带安装膨胀 + 版本耦合,engine 拆包解决(见 todo.md「已完成任务:@dependfix/engine 拆包」);packages/mcp/与apps/platform/均只依赖@dependfix/engine+@dependfix/core packages/skills/为资源包(无运行时依赖),仅被 cli 消费- 依赖方向单向:
core←engine←{cli, mcp, platform};禁止反向与循环引用 - 共享能力一律下沉 engine 后在应用层复用,禁止应用层复制实现或直连 core 内部模块
执行挂接:本依赖约束的合规核验由 A 阶段 Review Gate 必查项执行(见 code-quality-checklist 包依赖约束 与 Code Auditor (代码审计员) 必查项)。
5. TypeScript
- 严格模式逐步收紧(当前
noImplicitAny: false为过渡状态) tsc --noEmit必须通过- 禁止
any逃逸(逐步清零) - 优先使用
interface定义类型,需要联合类型时使用type
5.1 工程经验
5.1.1 错误路径 helper 自身不抛异常
- 统一用
toErrorMessage(value)提取错误消息(Error→message、string→原样、可序列化→JSON、其余→类型描述),禁止在 catch 块手写instanceof分支。 - 错误路径 helper 必须 try/catch 包裹
JSON.stringify(循环引用会 throw,掩盖原始错误),且必须有单测锚定。
5.1.2 日志输出人读/机读双模
- 所有输出考虑"人读 vs 机读"双路径:
process.stdout.isTTY检测 → TTY 输出格式化彩色文本,非 TTY(CI/管道)输出 JSON。
5.1.3 截断带固定前缀的 ID 先去除前缀
- 对
prefix-<唯一段>形式的 ID,禁止直接slice(0, N)(唯一部分会被丢光);先去掉固定前缀再截断,或取最后一个分隔段。 - 文件名/分支名采用
YYYYMMDD-HHmmss-{唯一尾段}(字典序==时间序且唯一)。
5.1.4 改名/迁移全局排查命名残留
- 命名前缀抽为统一常量 + 封装读取辅助,所有读取必须走它(防漏网)。
- 改名后全局搜索旧名(含 env 前缀、错误消息、注释、示例),不只看文件引用。
5.1.5 Node 脚本 main 入口守卫(必须)
scripts/*.mjs等可执行脚本必须用入口守卫包裹main()调用:javascriptif (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { main().catch(...) }守卫是入口副本中最容易被漏的一行——完成新脚本后 grep
process.argv[1]确认。详见 经验归档 §三十九。
5.1.7 容器拼装类代码注释必须准确区分 execFile 与 exec
- docker 拼装类代码注释禁止写"防 argv 回显"等不准确表述。
execFile不经过 shell,不会回显 argv(与exec不同)。 - 准确语义:
spec.env隔离,避免 cmd/test 日志、git URL、daemon config 可见 token(凭据走http.extraheader等带外通道,与 argv 无关)。 - 注释必须真实反映防御机制,错把"防 argv 回显"当成威胁模型会导致后续审计按错误方向找漏洞。
5.1.8 JSDoc 注释必须与可见性声明一致
private方法 + JSDoc 写"导出便于 snapshot 测试"自相矛盾。若实际不导出,改注释或改public/internal。- 拼装类函数(如
buildRunArgs、buildSpawnArgs)应在测试中 snapshot 验证——拼装 bug 在真起容器前难暴露,靠运行时回显只能发现一半问题。 - 注释与实现脱节会被 audit 作为 warning 处置;统一规范后减少文档维护成本。
5.1.9 测试 Spy 与生产实现同模块时必须 @internal 标注
SpyAdapter与生产Adapter同模块导出时,必须在 Spy 类上加@internalJSDoc + 文件级注释"生产代码禁止导入",避免业务模块误用 spy 路径导致测试覆盖率虚高、运行时行为错位。- 强约束:eslint
no-restricted-imports规则限制生产代码 import spy 路径是最稳护栏,新写 Spy 模块时同步配置。 - 已有项目内案例:
packages/engine/src/runners/*.ts中 Docker adapter 与 spy 同模块导出(commitb189aaa落地)。
5.1.10 删除"自动状态赋值"时必须搜遍所有被动接收路径
- 删除状态自动赋值逻辑(如
selectedRepos.value = ...filter(...))前,必须审视所有调用路径是否依赖该自动行为收敛。 - 被动接收态举例:成功提交后重新调用
loadImportable()时,已删的自动赋值语句留下的旧 selectedRepos 会让 checkbox 呈 disabled+checked 态、计数过期、按钮仍可点——误导用户。 - 修复范式:在
emit('success')后await reload()前主动selectedRepos.value = [],让"删除"与"主动重置"形成完整闭环。 - 原则:删"自动逻辑"必须搜遍所有"被动接收该状态的路径"——单点删除会留下隐式不一致。
5.1.11 调试临时代码必须在 commit 前清理
- 任何调试临时代码(
// DEBUG注释、console.log('[debug]', ...)、// TODO未跟踪项、alert(...)弹窗、debugger语句)必须在conventional-committer提交前手动清理。 - 不能依赖 lint(
no-console等规则仅限服务端日志场景,无法拦截浏览器端调试输出),code-auditor 会作为 blocker Reject。 - 调试完成后立即清理,不要等到 commit 前——
git diff --staged容易遗漏单行console.log,养成实时清理习惯。 - 范围扩展:ui-validator agent 视觉验证时自建的截图脚本(如
*-visual-verify.e2e.test.ts)属同类——git status不应有 untracked 临时文件。
5.1.6 测试不得依赖 git 忽略工作区文件的存在性
- 测试/脚本不得隐式依赖
.session/、temp/等 git 忽略目录下文件的存在性(本地有、CI 无 → 行为分叉,CI 挂、本地过)。 - 必须依赖时:把路径/内容作为参数注入,或模拟缺失场景(临时移走文件)验证两分支。
5.1.12 调试临时代码触发 TDZ(Temporal Dead Zone)陷阱
script setup顶部加临时调试console.log引用尚未声明的 ref/computed 会触发 TDZCannot access 'X' before initializationSSR 500 错误——即使console.log只是 debug 也会让整个 SSR 阶段失败(不是 hydration warning 而是真错误)。- 临时调试代码引用变量前必须确认其在执行前已声明,或放在
watchEffect/onMounted里。 - 调试完成后立刻清理不留痕(与 §5.1.11 调试临时代码清理规则配合)。
- 详见 经验归档 §四十二
5.1.13 已测试文件补测胜于新建(CI 覆盖率阈值回归修复模式)
- CI Coverage 阈值回归(差 7 分支)时,优先在已有测试文件加 case,而不是新建 test 文件或临时修改 vitest 阈值。
- 判断标准:diff 文件数 = 1 / 风险扩散 = 0 / 价值密度 = 高(覆盖核心聚合更新策略 / 失败终态保护 / 多状态流转等业务关键路径)。
- 实证见 经验归档 §四十二(M13.3 + 0c57211)。
5.1.14 OR 链触发条件精确追踪(if (a || b || c || d || e) 写回决策测试模式)
if (a || b || c || d || e)写回决策的 OR 链必须逐项追踪每个条件真假才能准确断言测试用例。- 常见错误:以为 "statusWriteBack=false 就完全不写回",但 counts 差异仍会触发 OR 链进入写回块(只跳过 status 赋值,counts/summaryJson 仍更新)。
- 修复模式:调试 case 时打开真实 SQL 数据看
batchRepo.save后的状态字段;不要"想当然"按 statusWriteBack 反推。 - 实证见 经验归档 §四十二(CI Coverage 修复批次 commit 0c57211)。
5.1.15 集成外部库前必须读 README 标准用法 + 落地真实路径 e2e 冒烟测试(hard requirement)
集成任何外部库(@octokit/*、Vue 插件、TypeORM、Playwright、better-auth 等)前必须先查 README 官方示例(installation / authentication / getting started 章节的契约代码)。
集成层测试不 mock 真实被集成库:mock 仅替换被测单元边界;mock 形态永远无法完整模拟真实 dispatch 行为。真实路径冒烟测试必须用真实 RSA / 真实私钥 / 真实 nock 拦截,不能仅凭单测通过即认为集成完成。
「单测全过 + typecheck 0 error」≠ 集成 Done:必须有「真实路径调用 + 断言关键行为」的可执行验证;A 阶段 code-auditor 主责边界已挂「集成外部库时验证 README 标准用法引用 + e2e 真实路径测试存在」必查项(code-auditor.agent.md 主责边界)。
详见 经验归档 §四十三(M18.4 audit round 1 Reject 实证)
5.1.16 v-model 修改嵌套字段必须用 reactive + deep watch(hard requirement)
Nuxt useAsyncData 内置 watch 默认浅监听(reference equality),对 nested field mutation(如 v-model 改 filters.includeSuperseded = true)不响应——任何 v-model 嵌套字段修改需要用 reactive 而非 ref,配合 deep: true watch。
错误模式:
const filters = ref<Filters>({...}) // ref + watch 浅监听不响应 nested field mutation
watch: [viewMode, filters], // 对 ref 浅比较正确模式:
const filters = reactive<Filters>({...}) // reactive 字段级修改
watch: [viewMode, () => filters, { deep: true }], // getter source + deep watch
// 或显式兜底
watch(filters, () => { void refreshAlerts() }, { deep: true })依赖 Nuxt useAsyncData 默认 dedupe: 'cancel' 抑制双触发:内置 watch + 显式 watch 都可能触发 refresh,但 abortController 会取消旧 execute;改 dedupe 策略前需重新评估。
调试技巧:用 page.on('request') 跟踪浏览器侧 /api/alerts 请求数(而不是 Vue devtools),直接判断 refetch 是否触发。
详见 经验归档 §四十六
5.1.17 一次性脚本 TypeScript 价值评估(避免 over-engineering)
不要为了"项目完整性"添加不必要的 dev 依赖:一次性脚本 + 永久 devDep 代价不匹配价值;评估价值 / 成本比。
Node 运行时支持矩阵(影响 .ts 脚本运行):
- Node 20 LTS:不支持 .ts 直接运行;需 tsx / ts-node / esbuild-register 等中间层
- Node 22.6+
--experimental-strip-types:只剥离类型注解,不处理装饰器 - Node 23.6+ / 24
--experimental-transform-types:转换 enum / namespace,仍不处理装饰器 - 装饰器依赖
emitDecoratorMetadata(TS 编译器专属能力),Node 内置 TS 支持均无法替代
TypeORM 装饰器需要 emitDecoratorMetadata:@Entity('table_name') + @Column({...}) 装饰器运行后必须 emit 元数据到 reflect-metadata,否则 DataSource 构造时找不到 entity metadata → EntityMetadataNotFoundError。
何时必须 TypeScript(一次性脚本场景):
- ✅ TypeORM / Prisma / Drizzle 等装饰器密集型 ORM
- ✅ 类型安全严格(DB schema → API 契约同步)
- ❌ 纯 SQL / 简单业务逻辑(改 JavaScript 即可)
CLI 端 entity metadata 必须显式 import 触发装饰器:tsx / vitest CLI 路径不走 Nitro auto-load,需在脚本入口处显式 import 触发 @Entity / @Column 装饰器注册。
helper 文件模式(避免 ESLint unused-vars warning):
// register-entities.ts 文件级 eslint-disable
/* eslint-disable @typescript-eslint/no-unused-vars -- TypeORM 装饰器注册用 side-effect import */
import { ScanResult } from '../../entities/scan-result'
import { Repository } from '../../entities/repository'
// ... 其他 entity imports
void ScanResult
void Repository
// ...
/* eslint-enable @typescript-eslint/no-unused-vars */engines 应该与 Node LTS 实际部署版本对齐:Node 20 已 EOL(2026-04-30),engines >=20 是历史遗留,实际部署是 Node 22+ 或 Node 24+。建议升级到 >=22(兼容 Node 22 LTS)+ 注释说明 Node 22.6+ 内置 strip-types 仍不处理装饰器(tsx 仍必须)。
详见 经验归档 §四十七
5.1.18 SQLite 数据库启动期自动备份(引用 security.md §2.1 + 开发角度差异化信息)
权威完整声明(备份路径 / fsync / 保留策略 / 命令式恢复 / 自检工具等)见 security.md §2.1。本节仅保留开发角度差异化信息(应用范围 / 禁止 / D 阶段自检 + A 阶段 Review Gate)。
应用范围:所有 better-sqlite3 部署形态(dev / e2e / prod / Docker 容器)。e2e.sqlite 与 dependfix.sqlite 各自独立(不交叉备份)。
禁止:
- 禁用
--no-verify-backup跳过备份(无备份时应用启动期打印醒目 WARN 但仍允许启动——这是 fail-open 而非 fail-closed) - 禁用备份目录走
.gitignore之外的位置(避免误提交) - 禁用备份过程阻塞启动超过 5 秒(超过视为备份实现有问题,需审计)
D 阶段自检(Full Stack Master (全栈大师) agent):必须验证 apps/platform/server/database/backup.ts 存在 + 含 backup-on-startup 调用 + 含 fsync + 含保留策略清理逻辑
A 阶段 Review Gate 必查项:apps/platform/server/database/backup.ts 文件存在 + 含 fsync 证据 + 含保留策略
实证(2026-09-01 dependfix.sqlite 数据清空事故)见 经验归档 §五十。
5.1.19 TypeORM 1.x synchronize 与 migrationsRun 反模式禁止(hard requirement)
apps/platform/server/database/index.ts 配置必须遵守以下约束:
禁止组合:
- ❌
synchronize: true同时migrationsRun: true(TypeORM 1.x 文档明文警告的反模式) - ❌ dev 模式下
synchronize硬编码自动开启(如|| isDev)
强制组合:
- ✅ dev 模式:
synchronize: DATABASE_SYNCHRONIZE === 'true'(显式 opt-in,不自动开启)+migrationsRun: false - ✅ prod 构建:
synchronize: DATABASE_SYNCHRONIZE === 'true'(默认关闭)+migrationsRun: DATABASE_MIGRATIONS_RUN === 'true' || false - ✅ e2e 测试:
synchronize: true(独立数据库,schema 同步可接受)+migrationsRun: false
启动期日志强制项:
- 必须打印当前生效的
synchronize值 +migrationsRun值 + 触发来源(环境变量或默认) - 例:
[database] synchronize=false (DATABASE_SYNCHRONIZE unset, NODE_ENV=production), migrationsRun=false - 便于排查"为什么数据库 schema 没更新"或"为什么数据库被自动改写"
NOT NULL 列无 default 时同步失败的恢复路径:
- 当 schema 升级需要给已有数据的表新增 NOT NULL 列且无 default value,TypeORM 1.x synchronize 在 SQLite 上会失败(
SqliteError: NOT NULL constraint failed) - 事务回滚保证数据不丢(
RdbmsSchemaBuilder.build()内嵌 startTransaction / commitTransaction / rollbackTransaction) - 启动期需打印明确错误:
[database] synchronize FAILED: ...请写 migration 而非改 entity - D 阶段自检必须验证:涉及 NOT NULL 列无 default 的 schema 变更必须走 migration 路径,不可仅靠 synchronize
5.1.21 zod .optional() 接受 undefined 为合法值(陷阱模式)
zod z.enum([...]).optional() 接受 undefined 为合法值(safeParse(undefined).success = true, data = undefined),但区分「未传字段」与「传 undefined」需显式 data !== undefined 判断,否则 data === 'some-value' 三元永远为 false(因 data 是 undefined)。
防御(防 future zod 0.x 升级或 .optional() 行为变更):
- 写 query 参数解析时,对
boolean字段必须保留data !== undefined区分(false是合法值) - 对
string字段可简化为单层safeParse.success ? data : undefined(下方if (conclusion)自动过滤 falsy)
zod-helpers helper:apps/platform/server/utils/zod-helpers.ts 提供 parseOptional<T>(schema, value): { success: boolean, value?: T, isProvided: boolean } helper,强制三态语义区分(success / value / isProvided),避免原 data !== undefined 死代码陷阱。
5.1.20 atomic commit 边界(重构支撑 vs 业务行为变更必须分 commit)
核心规则:
- 重构支撑 = 不改变行为,只改善代码结构(提取 const / 改命名 / 删除冗余分支)
- 业务行为变更 = 改变默认行为(默认值反转 / 逻辑反转 / 新增功能)——两者必须分 commit
安全做法:
- 启动日志要打印某变量时,临时用内联表达式(
console.log(\migrationsRun=${process.env.DATABASE_MIGRATIONS_RUN !== 'false'}`)`),不提取 const - 业务行为变更的 commit(如默认值反转)时再统一提取 + 改计算
规范支撑:AGENTS.md §提交规范 第 4 条"原子粒度——一个提交对应一个逻辑变更" + 规划规范 §1.1 任务粒度约束
详见 经验归档 §四十九(M22.4 教训沉淀) + §五十(2026-09-01 dependfix.sqlite 事故关联风险)
5.1.22 baseline lint 治理路径:删除占位符 vs 改写为 void X 的治本决策(M25.3 阶段实证)
ESLint 双重禁止规则:
@typescript-eslint/no-unused-expressions禁止未使用表达式(如someCondition && doSomething())@typescript-eslint/no-meaningless-void-operator禁止void X无意义用法
修复方向选择:
- 方向 A(删除占位符):删除冗余表达式(
void someValue→ 完全删除该行;condition && doSomething()→ 改为if (condition) { doSomething() })—— 本项目标准做法 - 方向 B(改写为
void X):保留表达式但用void前缀 —— 与no-meaningless-void-operator冲突,禁止
M25.3 实证:baseline 16 errors 全部走方向 A 治本(12 个 void X 直接删除,4 个 condition && doSomething() 改写为 if 块,0 个使用 eslint-disable 抑制)。max-warnings 临时方案同样禁止——CI 触发 ESLint 临界值是"信号"而非"阈值调整",治理方向是"清空 warnings"而非"提高阈值"。
M26.4b 延伸实证:22 warnings → 0 warnings 全部治本(不扩展 max-warnings 临时方案)。具体修复策略:
await-thenable(9 个)→ 去除冗余 awaitno-invalid-void-type(1 个)→voidunion 改undefinedmax-params(1 个)→ 合并相邻可选参数到 options 对象no-empty-function(1 个)→ 保留 + 加注释说明设计意图only-throw-error(1 个)→ 仅测试代码必须 throw 非 Error 覆盖 fallback 时加 eslint-disable 注释max-statements-per-line(4 个)→ 拆分多语句到多行no-unused-vars(3 个)→ 删除未使用 import / 冗余 importno-deprecated(2 个)→ TypeORM 1.xconnection改dataSourcerequire-await(1 个)→async function改function(返回Promise.resolve(...)显式包装 fetch API 契约)
规范支撑:规划规范 §4.4 治本 vs 临时 + D 阶段自检三向验证纪律(AI 协作规范 §2.0)
详见 经验归档 §六十 M25.3 baseline lint 修复方向 + §六十二 教训 2(M25 → 当前 25 commits 文档治理批次)
5.1.23 git config user identity 一致性 guard(M26 阶段 2026-09-09 实证)
git config 优先级 local > global > system,.git/config [user] 会静默覆盖 global user(无任何提示)。M26 阶段 33 commits 误用 dependfix[bot] 而非 CaoMeiYouRen 即此现象实证。修复模式:
- pre-commit guard 治本:检测
.git/config [user]与git config --global user.*一致性,不一致阻断 commit(项目内已落地.husky/pre-commit-identity-guard.sh+.husky/pre-commit第一步) - session 启动时第一件事:
.session/current-task.yaml段对齐git config --local user.*与git config --global user.* - 严禁批量改 commit author(除非用户显式同意 +
git rebase -i HEAD~N --exec 'git commit --amend --no-edit --author=...'+ 强制 push)——历史 commit 改 author 风险高
详见 经验归档 §六十三 M26 阶段 git config user 错位事故与防护
6. 样式规范(平台阶段适用)
- 纯 SCSS: 禁止 CSS-in-JS、Tailwind。所有样式以纯 SCSS 编写。
- SCSS 复用: 优先使用全局变量(Variables)和混合宏(Mixins)。
- BEM 命名: 组件样式遵循
block__element--modifier规范。 - 禁止
!important: 破坏 CSS 层级结构。 - 暗色模式: 通过
:global(.dark) .selector覆盖样式(注意:main.scss是全局 CSS 无 scope,原:global(.dark) &编译失败,正确写法.dark &,让 mixin 自动工作;详见 平台开发规范 §7)。 - 响应式基线(768px): dashboard / 列表 / 表格页都应默认支持 768px 响应式(不是 mobile-specific feature 而是响应式基线)——
@media (max-width: 768px)切换grid-template-columns: 1fr、表格水平滚动、侧栏折叠。V 阶段 ui-validator 自动检测 768px 适配遗漏,遗漏会被列为 Blocker。 - 跨 Dialog i18n label key 共享: 共享选项数据(mode / severity / batch-start 等)时,i18n label key 也应共享(如
repos.batchMode/repos.batchSeverity同时用于批量与单仓库 Dialog),避免冗余 key(如repos.scanConfigMode与批量 Dialog 相同 label 但不同 key)。仅在 Dialog 标题 / 目标信息等真正差异处新增 key。
7. 包命名规范
| 子包 | npm 名 | 类型 | 说明 |
|---|---|---|---|
packages/core | @dependfix/core | 内部库 | 核心领域模型,被其他包消费 |
packages/engine | @dependfix/engine | 内部库 | 共享执行引擎(DependfixApp),cli / mcp / platform 共同依赖 |
packages/skills | @dependfix/skills | 内部库 | 产品 skill 权威源(dependfix-remediator) |
packages/cli | dependfix | CLI 工具 | 用户通过 npx dependfix 调用 |
packages/mcp | @dependfix/mcp | MCP Server | MCP 协议服务 |
apps/platform | @dependfix/platform | 应用(Nuxt 全栈) | 管理平台,非库;归 apps/ 目录体系 |
可发布包清单单点权威声明见 packages.config.mjs;新增发布包须登记并同步 README / release.md / CI 引用(见 code-quality-checklist 新增发布包链路完整性)。
packages/github、packages/action为规划中未实现的包,按需添加(见 AGENTS.md 项目简介)。
- CLI / 可执行入口使用 unscoped
dependfix名称 - 内部库使用 scoped
@dependfix/*前缀 - 应用(
apps/*)使用 scoped@dependfix/*前缀,仅限工作区内部消费,不发布 npm
8. 提交规范
- 提交类型(
feat/fix/docs/refactor/test/ci/chore/perf/style/build/revert)、主题行与正文的完整编写规则见 Git 规范 §3.1 提交消息格式。
提交语言使用中文或用户使用的语言。单次提交对应一个逻辑变更,避免"大杂烩"提交。
9. 提交前检查
在 git commit 之前必须通过以下检查:
- Review Gate: 所有改动必须经过至少一轮 review,且 A 阶段(
Code Auditor (代码审计员))已放行。 - Lint:
pnpm lint零 error。 - Typecheck:
pnpm typecheck零 error。 - 测试: 定向测试通过;命中全量测试条件时执行
pnpm test。 - 提交执行: 必须通过
conventional-committerskill 提交(禁止裸git commit -m),详见 Git 规范 与 AGENTS.md 提交规范。
10. Code Scanning 告警处理流程
当 GitHub Code Scanning 报告安全告警时,按以下流程处理:
- 获取告警详情:使用
gh api repos/owner/repo/code-scanning/alerts获取告警类型、位置和描述 - 根因分析 + 搜索优先:使用搜索优先模式确认是否为误报,避免不必要的修复
- 制定修复方案:根据告警类型制定针对性修复方案(如命令注入 → 使用
execFileSync替代execSync) - 质量门验证:运行 lint + typecheck + test 确保修复不引入回归
- 深度审计:安全修复应使用
deep级别审计,确保全面覆盖 - 提交:使用
conventional-committerskill 提交,消息格式为fix(scope): 描述
11. 相关文档
本文档在 1.0.0 前参考 momei 项目的成熟做法完成继承与适配;1.0.0 后按项目自身实践持续演进,形成自有规范。