mechanical-enforcement
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMechanical Enforcement
机制化强制执行
Rules a reviewer would otherwise have to remember belong in a linter. This skill is the curated catalogue of rules, the linters that enforce them, and the rationale for each — so a new project can be hardened without re-deriving the set.
This is a content skill, not a tool. It provides rules and snippets. For wiring those rules into git hooks, see the skill.
hk原本需要评审人员牢记的规则都应该放到linter中。本skill是经过精心整理的规则目录,包含执行这些规则的linter工具以及每条规则的设计理由,这样你无需从头梳理规则集就能完成新项目的代码加固。
这是一个内容类skill,而非工具。它提供规则和代码片段,如果你需要将这些规则配置到git hooks中,请参考 skill。
hkPrinciples
原则
- Mechanical over social. If a rule relies on a reviewer remembering it, it will drift. Encode it in a linter, a type, or a test — never in a convention.
- Types first, lint second, tests third. Prefer TypeScript / Pydantic / clippy to a custom lint rule. Reach for a lint rule when the type system can't express it. Reach for a test only when neither can.
strict - Architectural boundaries are linter rules. Layers (domain ← infra, utilities ← server, UI ← schemas) are enforced with /
no-restricted-imports, not trusted to vigilance.no-restricted-syntax - Auto-fix where possible, gate where not. Formatters and whitespace fixers run with and re-stage. Correctness rules gate the commit.
fix = true - Prefer opinionated presets, override minimally. Ultracite for Biome, for commits,
@commitlint/config-conventionalfor Next. Only override with a comment explaining why.next/core-web-vitals - The why lives with the rule. Every non-obvious override has an inline comment saying what would break if it were removed.
- 机制优先于人为约定。如果一条规则需要靠评审人员牢记,迟早会出现执行偏移。将规则编码到linter、类型系统或测试中,永远不要只停留在口头约定层面。
- 类型优先,lint次之,测试最后。优先使用模式的TypeScript / Pydantic / clippy,而非自定义lint规则。当类型系统无法表达规则时再使用lint规则,只有前两者都无法实现时才用测试覆盖。
strict - 架构边界就是linter规则。层级约束(领域层 ← 基础设施层、工具层 ← 服务端、UI ← schema定义)通过/
no-restricted-imports规则强制执行,不能依赖开发人员的自觉性。no-restricted-syntax - 可自动修复的优先自动修复,不可修复的设置门禁。格式化工具和空格修复工具开启执行后自动重新暂存文件,正确性相关规则作为代码提交的门禁校验项。
fix = true - 优先使用有主见的预设配置,最小化自定义覆盖。Biome用Ultracite预设,提交信息用,Next项目用
@commitlint/config-conventional。仅当有明确理由时才覆盖默认配置,且必须添加注释说明原因。next/core-web-vitals - 规则的设计理由与规则共存。每一条非显而易见的覆盖配置都要有行内注释,说明如果移除该配置会导致什么问题。
When to use this skill
何时使用本skill
- Setting up linting in a new project → pick linters from the table below, copy snippets from , wire with the
references/skill.hk - Hardening an existing project → audit against the rules catalogue, add the missing ones.
- A bug just happened → ask "what rule would have caught this mechanically?" and add it here.
- Choosing a linter for an unfamiliar stack → see the picks table.
- 新项目配置lint规则 → 从下表中选择linter,从目录复制代码片段,通过
references/skill完成配置。hk - 加固现有项目 → 对照规则目录进行审计,补充缺失的规则。
- 刚出现bug时 → 思考"什么样的规则可以从机制上提前捕获这类问题?",并将对应规则添加到本目录中。
- 为不熟悉的技术栈选择linter → 参考选型表。
Linter picks by stack
按技术栈分类的linter选型
Use the tool in the Primary column first; reach for the Also column only when the primary can't express the rule.
| Stack | Formatter | Primary linter | Also | Type-check | Notes |
|---|---|---|---|---|---|
| TypeScript / React / Next | Biome (via Ultracite presets | Biome | ESLint flat config — only for | | Ultracite is the default for new projects. Raw Biome only if Ultracite doesn't support the framework. |
| TypeScript (library / node) | Biome | Biome | — | | Skip ESLint entirely unless you need boundary rules. |
| Python | ruff format | ruff | — | basedpyright strict (or pyright) | |
| Rust | rustfmt | clippy ( | — | | |
| Go | gofmt / gofumpt | golangci-lint | — | | Enable |
| Shell | shfmt | shellcheck | — | — | |
| Markdown | rumdl | rumdl | — | — | Handles frontmatter too. |
| Nix | nixfmt | deadnix + statix | — | — | |
| YAML | — | yamllint | — | — | |
| Commit messages | — | commitlint ( | — | — | One-line config. See |
| Secrets | — | gitleaks | — | — | Always add — cheap, high-signal. |
| Typos | — | typos | — | — | Fast, auto-fixes, tiny false-positive rate. |
优先使用首选列的工具;仅当首选工具无法实现对应规则时才使用备选列的工具。
| 技术栈 | 格式化工具 | 首选linter | 备选工具 | 类型检查 | 说明 |
|---|---|---|---|---|---|
| TypeScript / React / Next | Biome(通过Ultracite预设 | Biome | ESLint flat config — 仅用于 | | 新项目默认使用Ultracite,仅当Ultracite不支持对应框架时才直接使用原生Biome。 |
| TypeScript(类库/node) | Biome | Biome | — | | 除非需要边界规则,否则完全不需要ESLint。 |
| Python | ruff format | ruff | — | basedpyright strict(或pyright) | |
| Rust | rustfmt | clippy( | — | | 可选择性开启 |
| Go | gofmt / gofumpt | golangci-lint | — | | 开启 |
| Shell | shfmt | shellcheck | — | — | 仅当有注释说明时才添加 |
| Markdown | rumdl | rumdl | — | — | 同时支持frontmatter校验。 |
| Nix | nixfmt | deadnix + statix | — | — | |
| YAML | — | yamllint | — | — | |
| 提交信息 | — | commitlint( | — | — | 单行配置,参考 |
| 密钥 | — | gitleaks | — | — | 必须添加,成本低、检出准确率高。 |
| 拼写错误 | — | typos | — | — | 速度快、支持自动修复、误报率极低。 |
Rules catalogue
规则目录
Rules are organised by concern, not by linter. Each entry gives: what it prevents, how to encode it, and known exceptions.
规则按关注点而非linter分类,每个条目包含:预防的问题、实现方式、已知例外。
Type safety
类型安全
| Rule | Encode with | Prevents | Notes |
|---|---|---|---|
| Full strict mode | | Most null/undefined footguns | Non-negotiable. |
Indexed access returns | | | See |
| Dead code fails build | | Drifted imports, zombie variables | Prefix with |
| Only erasable TS syntax | | | Enables deno/bun/swc/esbuild interop without a TS runtime. Breaks existing code using |
No | Biome | Escape hatch from the type system | Use |
No | ESLint | Silent lies to the compiler | Allowed exceptions (document each with |
No | ESLint | Silent runtime crashes | Use a proper null check or throw a narrowed error. |
Prefer | Biome | Accidental runtime imports of type-only modules | Auto-fixable. |
| 规则 | 实现方式 | 预防的问题 | 说明 |
|---|---|---|---|
| 全量strict模式 | | 绝大多数null/undefined相关的坑 | 必须开启,无例外。 |
索引访问返回 | | 空数组场景下 | 参考 |
| 死代码导致构建失败 | | 漂移的导入、僵尸变量 | 故意保留未使用参数时加 |
| 仅使用可擦除的TS语法 | | | 无需TS运行时即可实现deno/bun/swc/esbuild兼容,会破坏现有使用 |
禁止使用 | Biome | 绕过类型系统 | 使用 |
禁止使用 | ESLint | 对编译器的静默欺骗 | 允许的例外(每个例外都需要加 |
禁止使用 | ESLint | 静默的运行时崩溃 | 使用规范的空值检查或者抛出收窄后的错误。 |
优先使用 | Biome | 意外导入仅类型模块的运行时代码 | 支持自动修复。 |
Error handling
错误处理
| Rule | Encode with | Prevents | Notes |
|---|---|---|---|
No bare | Biome | Errors disappearing into the void | Narrow in the catch ( |
| No catch-all re-throw without cause | Custom | Losing error context | Required pattern: |
| Prefer Result types at domain boundaries | Convention + review; no linter | Exception-driven control flow in pure code | Exceptions live at the imperative shell only. |
No | Biome | Logs leaking to user consoles | Use the project's logger. |
| 规则 | 实现方式 | 预防的问题 | 说明 |
|---|---|---|---|
禁止空 | Biome | 错误凭空消失 | 在catch中做类型收窄( |
| 禁止不带cause的全捕获重抛 | 自定义 | 丢失错误上下文 | 要求的写法: |
| 领域边界优先使用Result类型 | 约定 + 评审;无对应linter规则 | 纯代码中基于异常的控制流 | 异常仅允许出现在命令式外壳层。 |
生产代码禁止使用 | Biome | 日志泄漏到用户控制台 | 使用项目统一的日志工具。 |
Architectural boundaries
架构边界
Use and to make illegal graphs uncompilable. The catalogue of patterns:
no-restricted-importsno-restricted-syntax- Pure layer cannot import side-effectful layer. +
files: ["src/utilities/**"]banningno-restricted-imports,next/cache,next/headers, ORM runtime modules. Usenext/navigationfor types you still want visible. Exempt one or two intentionally coupled files (allowTypeImports: true,queries.ts) viarevalidate.ts.ignores - UI cannot import schemas directly. +
files: ["src/components/**"]banningno-restricted-imports patterns(or whichever path holds your DB schemas). UI should depend on generated types, not schema source — otherwise a UI tweak forces a migration.@/collections/* - Raw SQL only in the query layer. on
no-restricted-syntaxeverywhere exceptTaggedTemplateExpression[tag.name='sql']. Also ban raw driver imports (src/db/**) outside the same directory.ImportDeclaration[source.value='postgres'] - Dynamic only via named wrappers.
import()onno-restricted-syntaxoutsideImportExpression/next/dynamic. Prevents ad-hoc chunking that defeats SSR.React.lazy
Full working snippets live in .
references/eslint-boundaries.mjs使用和让非法的依赖图无法编译。模式目录如下:
no-restricted-importsno-restricted-syntax- 纯逻辑层不能导入有副作用的层。+
files: ["src/utilities/**"]禁止导入no-restricted-imports、next/cache、next/headers、ORM运行时模块。对仍需要可见的类型设置next/navigation。通过allowTypeImports: true豁免少数故意耦合的文件(ignores、queries.ts)。revalidate.ts - UI不能直接导入schema。+
files: ["src/components/**"]禁止导入no-restricted-imports patterns(或者你存放DB schema的路径)。UI应该依赖生成的类型,而非schema源码——否则UI修改会触发不必要的迁移。@/collections/* - 仅查询层允许写原生SQL。除了之外的所有位置通过
src/db/**禁止no-restricted-syntax语法。同时禁止同一目录外导入原生驱动(TaggedTemplateExpression[tag.name='sql'])。ImportDeclaration[source.value='postgres'] - 动态仅允许通过命名包装器使用。除了
import()/next/dynamic之外通过React.lazy禁止no-restricted-syntax语法。防止破坏SSR的临时代码分片。ImportExpression
完整可用的代码片段在中。
references/eslint-boundaries.mjsUI hygiene (React / Next)
UI卫生规范(React / Next)
| Rule | Encode with | Prevents | Notes |
|---|---|---|---|
No raw | | Drift from the design system | Exempt the UI library path ( |
| ESLint | Accessibility regressions | Turn off |
| No inline styles | Biome | Design-system bypass | Allow |
| default in Ultracite | Regex recompiled on every call; inline regex in test assertions | Prefer |
| 规则 | 实现方式 | 预防的问题 | 说明 |
|---|---|---|---|
组件库外禁止使用原生 | 在业务代码中通过 | 脱离设计系统的样式漂移 | 豁免UI库路径( |
开启 | 通过flat config开启ESLint | 可访问性退化 | 关闭 |
| 禁止行内样式 | Biome | 绕过设计系统 | 允许少数图表组件使用 |
| Ultracite默认开启 | 正则表达式每次调用都重新编译;测试断言中使用行内正则 | 优先使用 |
Import hygiene
导入卫生规范
| Rule | Encode with | Prevents |
|---|---|---|
| Sorted + grouped imports | Biome | Merge conflicts; inconsistency |
| No cycles | madge ( | Module init-order bugs |
| No default exports (optional) | Biome | Inconsistent naming at import sites; poor rename refactoring. Exempt Next.js pages/layouts where defaults are required. |
| Unique function names | | Duplicate helpers being written instead of discovered. Grep check catches the cross-file case ESLint can't. |
| 规则 | 实现方式 | 预防的问题 |
|---|---|---|
| 导入排序+分组 | 格式化时开启Biome | 合并冲突;风格不一致 |
| 禁止循环依赖 | pre-commit中执行madge( | 模块初始化顺序bug |
| 禁止默认导出(可选) | Biome | 导入位置命名不一致;重命名重构体验差。豁免Next.js要求默认导出的页面/布局文件。 |
| 唯一函数名 | 同一文件内通过 | 重复编写功能重合的辅助函数而没有复用已有的实现。Grep检查可以覆盖ESLint无法处理的跨文件场景。 |
Testing
测试
| Rule | Encode with | Prevents |
|---|---|---|
No | Biome | Accidentally skipping the rest of the suite in CI |
| No inline regex in assertions | Biome | Flaky matches and poor error messages |
| Coverage threshold enforced pre-commit | hk step running | Untested branches slipping in. Use |
| No mocks in unit tests | Convention + review | Tests that pass but mask integration bugs |
| 规则 | 实现方式 | 预防的问题 |
|---|---|---|
禁止提交 | Biome | CI中意外跳过其余测试用例 |
| 断言中禁止使用行内正则 | Biome | 匹配不稳定、错误信息不清晰 |
| pre-commit强制覆盖率阈值 | hk步骤执行 | 未测试的分支流入代码库。对无法到达的防御性代码使用 |
| 单元测试禁止使用mock | 约定 + 评审 | 测试通过但掩盖了集成bug |
Secrets & supply chain
密钥与供应链
| Rule | Encode with | Prevents |
|---|---|---|
| No committed secrets | gitleaks pre-commit step | Token leaks |
| Pinned dependencies with quarantine | pnpm | Compromised releases |
No | Documented in project CLAUDE.md / AGENTS.md; not technically preventable | Bypassing the whole gate. Cultural rule — reinforce in every project's agent docs. |
| 规则 | 实现方式 | 预防的问题 |
|---|---|---|
| 禁止提交密钥 | gitleaks pre-commit步骤 | Token泄漏 |
| 依赖版本固定+新包隔离 | pnpm | 被篡改的版本发布 |
禁止使用 | 在项目CLAUDE.md / AGENTS.md中说明;无法从技术上禁止 | 绕过所有门禁校验。属于文化规则——在每个项目的agent文档中强调。 |
Commit messages
提交信息
js
// commitlint.config.js
export default { extends: ["@commitlint/config-conventional"] };Wire via hk's hook (see ). Nothing else to configure.
commit-msgreferences/hk-steps.pkljs
// commitlint.config.js
export default { extends: ["@commitlint/config-conventional"] };通过hk的钩子配置(参考),无需其他配置。
commit-msgreferences/hk-steps.pklComposition with the hk
skill
hk与hk
skill配合使用
hkThis skill gives you what to enforce. The skill gives you how to wire it.
hkThe typical mapping:
tier 1 (format/fix) → trailing-whitespace, newlines, typos, rumdl, biome fix
tier 2 (lint/gate) → biome check, eslint, gitleaks, yamllint, check-merge-conflict
tier 3 (typecheck) → tsc --noEmit (or tsgo)
tier 4 (test) → vitest run --coverage
commit-msg → commitlintUse + on pre-commit so tier 1 auto-fixes and re-stages. See for a full worked example.
fix = truestash = "git"references/hk-steps.pkl本skill告诉你要强制执行什么, skill告诉你怎么配置这些规则。
hk典型的映射关系:
tier 1 (格式化/自动修复) → trailing-whitespace, newlines, typos, rumdl, biome fix
tier 2 (lint/门禁校验) → biome check, eslint, gitleaks, yamllint, check-merge-conflict
tier 3 (类型检查) → tsc --noEmit (或tsgo)
tier 4 (测试) → vitest run --coverage
commit-msg → commitlint在pre-commit中设置 + ,这样tier 1的规则自动修复后会重新暂存文件。完整示例参考。
fix = truestash = "git"references/hk-steps.pklAdding a new rule
添加新规则
When a bug escapes to review or production, the retro question is: what rule would have caught this mechanically?
- Identify the smallest AST pattern, import, or type flag that expresses the rule.
- Pick the linter that already owns that concern (see picks table).
- Add it, with an inline comment explaining the failure mode it prevents.
- Add an entry to the relevant rules-catalogue section above (in this SKILL.md) with the same rationale.
- If it's a new type of rule worth sharing, add a snippet to .
references/
当有bug流到评审阶段或生产环境时,复盘需要问的问题是:什么样的规则可以从机制上提前捕获这个问题?
- 找出可以表达该规则的最小AST模式、导入路径或者类型标记。
- 选择负责对应领域的linter(参考选型表)。
- 添加规则,同时加行内注释说明它预防的故障模式。
- 在上方对应规则目录板块添加条目,附上同样的设计理由(在本SKILL.md中)。
- 如果是值得分享的新类型规则,在目录添加对应的代码片段。
references/
References
参考资料
- — strict
references/typescript-strict.jsoncblock (drop-in)compilerOptions - — Biome config extending Ultracite with override pattern
references/biome-ultracite.jsonc - — layered
references/eslint-boundaries.mjs+no-restricted-importsexamplesno-restricted-syntax - — worked hk.pkl step graph
references/hk-steps.pkl - — one-line conventional-commits config
references/commitlint.config.js - Ultracite — Biome preset bundle
- hk — git hook manager
- — 严格模式
references/typescript-strict.jsonc配置块(可直接复用)compilerOptions - — 扩展Ultracite的Biome配置,包含覆盖规则的示例
references/biome-ultracite.jsonc - — 分层
references/eslint-boundaries.mjs+no-restricted-imports示例no-restricted-syntax - — 完整可用的hk.pkl步骤图
references/hk-steps.pkl - — 单行约定式提交配置
references/commitlint.config.js - Ultracite — Biome预设集合
- hk — git hook管理器