pr-to-lint-rule
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTurn a prose rule that no off-the-shelf linter rule matches into a custom
lint rule — the opt-in synthesis step. This is the hand-off target of the
custom rule (⚙) lane in a rule map: audit maps a rule there
when it looks enforceable but nothing off-the-shelf fits. You invoke this skill
explicitly; nothing here runs on its own, and installing vigiles never starts
synthesizing anything.
vigiles auditThe point of this skill is not "write a checker." A model can write a
plausible-looking checker in seconds. The point is the discipline that makes the
result trustworthy: synthesize the rule and an independent test that
encodes the rule's real intent, run the checker against adversarial cases it
didn't author, and abstain — hand it back as prose — if it leaks. A checker
that matches a rule's surface but not its intent gives false confidence
(the measured failure mode: most naively-synthesized checkers silently leak).
Shipping a green check nobody should trust is worse than shipping nothing.
将现有lint规则无法匹配的书面规则转换为自定义lint规则——这是可选的合成步骤。这是规则图谱中**自定义规则(⚙)**通道的交付目标:当审计发现某规则看似可强制执行但没有现成规则匹配时,会将该规则映射到这个通道。你需要显式调用此功能;本功能不会自动运行,安装vigiles也不会启动任何合成操作。
vigiles audit本功能的核心不是“编写检查器”。AI模型可以在几秒内写出一个看似合理的检查器。关键在于让结果具备可信度的流程:既要合成规则,也要编写一个独立测试来编码规则的真实意图,让检查器针对非其生成的对抗性用例运行,若检查器存在漏洞则放弃——将规则还原为书面形式。仅匹配规则表面内容而非真实意图的检查器会带来虚假的信心(实测失败模式:大多数朴素合成的检查器会悄悄出现漏洞)。发布一个无人可信的绿色检查标记,比什么都不发布更糟糕。
Arguments
参数
$ARGUMENTSvigiles audit- "we keep telling people not to import directly from antd — use our design-system barrel"
- "people forget to use our custom logger instead of console.log"
- "wrap outbound API calls in our helper"
withRetry - "route handlers must go through the wrapper"
withAuth
$ARGUMENTSvigiles audit- "我们反复提醒不要直接从antd导入——使用我们的设计系统桶文件"
- "人们忘记使用我们的自定义日志工具,而是用console.log"
- "将对外API调用包裹在我们的工具函数中"
withRetry - "路由处理程序必须通过包装器"
withAuth
The pipeline
流程
prose rule
→ detect language + linter (ask if ambiguous)
→ REUSE first: does an off-the-shelf rule already fit? → use it, STOP (no synthesis)
→ clarify intent + read the codebase (what exactly is the violation? the fix?)
→ SYNTHESIZE: the rule + an INDEPENDENT intent-encoding test (adversarial cases)
→ TRUST GATE: run the checker on that test; precision AND recall must be 1.0
pass → KEPT (safe to enforce; wire it in)
leak → ABSTAIN (hand back as prose; never ship a checker it can't prove sound)书面规则
→ 检测语言和linter(若有歧义则询问用户)
→ 优先复用:是否已有现成规则匹配?→ 使用该规则,停止流程(无需合成)
→ 明确意图并读取代码库(违规行为具体是什么?修复方式是什么?)
→ 合成:规则 + 独立的意图编码测试(对抗性用例)
→ 信任管控:在测试上运行检查器;精确率和召回率必须均为1.0
通过 → 保留(可安全强制执行;接入系统)
存在漏洞 → 放弃(还原为书面形式;绝不发布无法证明健全性的检查器)Instructions
步骤说明
Step 1 — Detect the language and toolchain
步骤1 — 检测语言和工具链
Read the repo to find the primary language, the linter in use (ESLint,
Ruff, Pylint, Clippy, RuboCop, Stylelint…), the test framework, and any
existing custom rules (to match conventions). If you cannot confidently pick
one — polyglot repo, no linter config, several candidates — ask the user
before generating anything.
读取代码库以确定主要语言、正在使用的linter(ESLint、Ruff、Pylint、Clippy、RuboCop、Stylelint…)、测试框架以及任何现有自定义规则(以匹配编码规范)。如果无法确定(多语言代码库、无linter配置、多个候选选项),在生成任何内容前询问用户。
Step 2 — REUSE before you synthesize (ADOPT > REUSE > SYNTHESIZE)
步骤2 — 合成前优先复用(采用 > 复用 > 合成)
Synthesis is the last resort, not the first. Before writing any rule:
- Check existing plugins / built-in rules. Read the linter reference doc
below (the plugin table + families) — most one-off prohibitions are a config line, not custom code.
no-restricted-* - Try a built-in construct rule. ESLint /
no-restricted-syntax, Ruff/Pylint selects, RuboCop cops, etc.no-restricted-imports - Only synthesize when the rule needs AST analysis, a fix, or options that no existing rule provides.
If an off-the-shelf rule fits, use it and stop — enable it (one config line)
and jump to Step 6. That is the outcome, and it's the better one;
don't synthesize a rule when a maintained one exists.
strengthen| Language | Linter | Reference doc |
|---|---|---|
| JavaScript/TypeScript | ESLint | |
| Python | Ruff | |
| Python | Pylint | |
| Ruby | RuboCop | |
| Rust | Clippy | |
| CSS | Stylelint | |
For Go, generate a analyzer with
tests. For any other language, use the most idiomatic linting
approach with test cases and integration notes.
golang.org/x/tools/go/analysisanalysistest合成是最后的选择,而非首选。在编写任何规则前:
- 检查现有插件/内置规则。阅读下方的linter参考文档(插件表 + 系列规则)——大多数一次性禁令只需一行配置,而非自定义代码。
no-restricted-* - 尝试内置构造规则。例如ESLint的/
no-restricted-syntax、Ruff/Pylint的选择规则、RuboCop cops等。no-restricted-imports - 仅当规则需要AST分析、自动修复或现有规则无法提供的选项时,才进行合成。
如果有现成规则匹配,直接使用并停止流程——启用该规则(一行配置)并跳至步骤6。这是的结果,也是更优方案;当存在维护中的规则时,不要合成新规则。
strengthen| 语言 | Linter | 参考文档 |
|---|---|---|
| JavaScript/TypeScript | ESLint | |
| Python | Ruff | |
| Python | Pylint | |
| Ruby | RuboCop | |
| Rust | Clippy | |
| CSS | Stylelint | |
对于Go语言,使用测试生成分析器。对于其他任何语言,使用最符合语言习惯的lint方法,并提供测试用例和集成说明。
analysistestgolang.org/x/tools/go/analysisStep 3 — Nail the intent, then read the codebase
步骤3 — 明确意图,然后读取代码库
Synthesis fails when the checker enforces what's easy to match instead of what
the rule means. Before writing anything, pin down:
- The exact violation. "No hardcoded secrets" is not "a variable named
" — it's
password,apiKey, an env-less literal too. "Wrap API calls in retry" needs the real signature of the calls and the wrapper.token - The fix. What does the compliant form look like, concretely, in this codebase? Read the real files — the helper's import path, its call shape.
- The clean lookalikes. What looks like a violation but isn't? (the pattern
inside a string, a comment, a JSDoc ; an aliased/namespaced form.)
@example
If any of this is ambiguous — and for anything like "wrap API calls in retry" it
usually is — ask the user rather than guessing. A rule built on a guessed
intent will fail the gate anyway; asking is cheaper.
当检查器强制执行的是易于匹配的内容而非规则真实含义时,合成就会失败。在编写任何内容前,需明确:
- 具体的违规行为。“禁止硬编码密钥”并非指“名为的变量”——还包括
password、apiKey、无环境变量的字面量等。“将API调用包裹在重试工具中”需要明确调用的真实签名和包装器。token - 修复方式。在当前代码库中,合规的写法具体是什么样的?读取真实文件——工具函数的导入路径、调用形式。
- 易混淆的合规情况。哪些内容看似违规但实际并非如此?(字符串中的模式、注释、JSDoc示例;包含关键字的驼峰式标识符如
@example;形式相近的合规调用。)consoleLog
如果其中任何一点存在歧义——比如“将API调用包裹在重试工具中”通常都会有歧义——询问用户而非猜测。基于猜测意图构建的规则终究会在管控环节失败;询问的成本更低。
Step 4 — Synthesize the rule AND an independent intent test
步骤4 — 合成规则及独立的意图测试
Write two artifacts:
-
The rule — prefer AST/keyword analysis over text/substring scanning (text-scan checkers false-positive on the pattern appearing in prose; a name-based checker false-negatives on synonyms). Follow the reference doc's anatomy, fix/suggest, and registration guidance.
-
An independent test that encodes the INTENT, not the checker's shortcut. This is the trust anchor — write it to the rule's meaning, then seed it with the adversarial cases that break naive synthesis:
- valid (must NOT flag): the pattern in a string literal, a line comment,
a JSDoc/docstring ; a camelCase identifier that merely contains the keyword (
@example); a compliant call that looks close.consoleLog - invalid (MUST flag): every real form of the violation — synonyms
(/
apiKey, not justtoken), the aliased/namespaced form (password), the wrapped-differently case.window.console.log
Do not make the test agree with a shortcut the checker took. If the rule is "no hardcoded secret," the test assertsis a violation even if your first checker only catchesapiKey— so the gate can catch the leak.password - valid (must NOT flag): the pattern in a string literal, a line comment,
a JSDoc/docstring
编写两个产物:
- 规则——优先选择AST/关键字分析,而非文本/子字符串扫描(文本扫描检查器会对出现在文本中的模式误报;基于名称的检查器会对同义词漏报)。遵循参考文档的结构、修复/建议以及注册指南。
- 独立的意图编码测试——而非检查器的快捷方式测试。这是信任的基石——根据规则的含义编写测试,然后加入会破坏朴素合成的对抗性用例:
- 合规(不得标记):字符串字面量中的模式、行注释、JSDoc/文档字符串的示例;仅包含关键字的驼峰式标识符(如
@example);形式相近的合规调用。consoleLog - 违规(必须标记):所有真实的违规形式——同义词(/
apiKey,而非仅token)、别名/命名空间形式(password)、不同包裹方式的情况。window.console.log
- 合规(不得标记):字符串字面量中的模式、行注释、JSDoc/文档字符串的
不要让测试迁就检查器采用的快捷方式。如果规则是“禁止硬编码密钥”,即使你的第一个检查器仅能捕获,测试仍需断言属于违规——这样管控环节才能发现漏洞。
passwordapiKeyStep 5 — The trust gate: precision AND recall must be 1.0
步骤5 — 信任管控:精确率和召回率必须均为1.0
Run the synthesized checker against the Step-4 test (ESLint , pytest,
, whatever fits the linter).
RuleTesteranalysistest- Pass (every invalid flagged, every valid clean → precision = recall = 1.0): the rule is KEPT — safe to enforce. Continue to Step 6.
- Leak (any false positive or false negative): the rule is ABSTAINED.
Do not wire it in. Report honestly why it leaked (e.g. "matches the
pattern in a comment," "misses ") and either (a) hand the rule back as prose, or (b) if you can fix the checker, do so and re-run the gate — but never ship an un-passed checker.
apiKey
The reference gate lives in (two-stage: self-test + a blind
adversarial gold set), the canonical adversarial cases in ,
and a runnable end-to-end demo ( — watch it keep the
sound rules and abstain the two leaky ones). Read them for the case shapes and the
abstain discipline, then mirror it here; don't reinvent the gate.
rule-enforcer/gate.jsrule-enforcer/gold/gold.jsoncd rule-enforcer && npm run demo在步骤4的测试上运行合成的检查器(ESLint的、pytest、或其他适配linter的工具)。
RuleTesteranalysistest- 通过(所有违规都被标记,所有合规都未被标记 → 精确率=召回率=1.0):规则被保留——可安全强制执行。继续步骤6。
- 存在漏洞(任何误报或漏报):规则被放弃。不得将其接入系统。如实报告漏洞原因(例如“匹配了注释中的模式”、“遗漏了”),然后要么(a)将规则还原为书面形式,要么(b)若能修复检查器则进行修复并重新运行管控环节——但绝不发布未通过管控的检查器。
apiKey
参考管控逻辑位于(两阶段:自测 + 盲态对抗性黄金用例集),标准对抗性用例位于,还有一个可运行的端到端演示(——观察它保留健全规则并放弃两个存在漏洞的规则)。阅读这些内容了解用例形式和放弃机制,然后在此处遵循该逻辑;不要重新发明管控环节。
rule-enforcer/gate.jsrule-enforcer/gold/gold.jsoncd rule-enforcer && npm run demoStep 6 — Wire a KEPT rule into the instruction file
步骤6 — 将保留的规则接入指令文件
Only for a rule that passed the gate (or an off-the-shelf rule reused in Step 2).
v2 specs (repo has ): add an line and
recompile —
CLAUDE.md.spec.tsenforce()typescript
"<rule-id>": enforce("<linter>/<rule-name>", "<why>"),then .
npx vigiles compilev1 (hand-written ): append —
CLAUDE.mdmarkdown
undefined仅适用于通过管控环节的规则(或步骤2中复用的现成规则)。
v2规范(代码库包含):添加一行并重新编译——
CLAUDE.md.spec.tsenforce()typescript
"<rule-id>": enforce("<linter>/<rule-name>", "<why>"),然后执行。
npx vigiles compilev1版本(手写的):追加内容——
CLAUDE.mdmarkdown
undefined<Rule title — imperative, concise>
<规则标题——祈使语气,简洁明了>
Enforced by:
Why: <one sentence — the architectural reason>
<linter>/<rule-name>undefined强制执行工具:
原因: <一句话说明——架构层面的理由>
<linter>/<rule-name>undefinedStep 7 — Present the outcome
步骤7 — 呈现结果
Show the user, honestly:
- Kept rules: the generated files, the gate evidence (the adversarial cases
it passed), integration steps, the spec/block, and how to verify (run the linter, watch it catch a real violation).
CLAUDE.md - Abstained rules: which rule, why it leaked, and that it stays prose — a refused checker is a correct outcome, not a failure to hide.
Then ask before writing any files or editing the spec/. Nothing is
written for the user automatically.
CLAUDE.md如实向用户展示:
- 保留的规则:生成的文件、管控环节的证据(通过的对抗性用例)、集成步骤、规范/代码块,以及验证方式(运行linter,观察它捕获真实违规)。
CLAUDE.md - 放弃的规则:哪个规则、漏洞原因,以及该规则将保持书面形式——拒绝检查器是正确的结果,而非需要隐瞒的失败。
然后在写入任何文件或编辑规范/前询问用户。不会自动为用户写入任何内容。
CLAUDE.md