@adlc/rails-guard

中文文档
1.12.0发布于 2026/10/10MITES Module构建来源已验证近 30 天下载 8452

Rail-freeze enforcement and suppression-marker gate — blocks builder edits to frozen paths (C5).

原始使用文档
文档目录13 节
  1. ADLC Phase
  2. Usage
  3. Flags
  4. Rail source resolution
  5. allow-suppression declarations
  6. Examples
  7. Output
  8. Human-readable (default)
  9. JSON ( --json )
  10. Manifest record ( --record , clean pass only)
  11. Changing a trust root
  12. Sibling tools
  13. Core gaps

rails-guard

Rail-freeze enforcement and suppression-marker gate (ADLC C5).

Blocks builder edits to declared frozen rail paths during P4, and greps added diff lines for suppression markers that escape test/lint coverage. On a clean pass it can record a rails-diff-empty proof in the gate manifest.

ADLC Phase

C5 — P3/P4 enforcement. Sits in the CI gate between P3 (charter / planning) and P4 (builder execution). Guards two invariants simultaneously:

  1. Rail-freeze: declared rail paths (test files, contract types, CI config) must not be touched by the builder during P4.
  2. Suppression-marker gate: newly added .skip(, .only(, xfail, @ts-ignore, @ts-expect-error, eslint-disable, # noqa, #[ignore] lines are blocked unless the active ticket's body contains an explicit allow-suppression: <marker> declaration. Prose documentation files (.md, .markdown) are exempt from this scan — a marker in prose is never an executed suppression, and docs (including this README) legitimately name the markers when describing the rule. .mdx is not exempt: it compiles to JSX/TS and can carry operative suppressions, so it is scanned like code.

Usage

rails-guard [--base <ref>] [--ticket <id>] [--tickets <path>] \
            [--rails <glob>...] [--record] [--json]

Flags

FlagDefaultDescription
--base <ref>HEADGit ref to diff against
--ticket <id>—Load rails and allow-suppression declarations from this ticket
--tickets <path>.adlc/tickets.jsonPath to tickets file
--rails <glob>—One or more frozen-path globs (repeatable; overrides ticket.rails)
--recordoffOn a clean pass, append a manifest entry to .adlc/manifest.jsonl
--jsonoffEmit machine-readable JSON result
--help—Print this help and exit 0

Rail source resolution

  1. If --rails globs are supplied they are used directly.
  2. Otherwise --ticket must be provided; rails come from ticket.rails.
  3. If neither is available the tool exits 1 (operational error).

allow-suppression declarations

To permit a specific suppression marker, add this exact text to the ticket body:

allow-suppression: @ts-ignore
allow-suppression: .skip(

One declaration per line; case-sensitive; marker text must match exactly.

Examples

# Check that no frozen test files were touched, fail on any suppression
rails-guard --rails "test/**" --rails "schema/**"

# Use rails declared in ticket T42; allow .skip( for known broken test
rails-guard --ticket T42 --tickets .adlc/tickets.json

# Diff against a feature branch base commit
rails-guard --rails "test/**" --base origin/main

# CI gate with machine-readable output + manifest record on clean pass
rails-guard --rails "test/**" --json --record

Output

Human-readable (default)

Clean:

rails-guard: all checks passed

Violations:

rails-guard: 2 violation(s) found
  [rail-edit]   test/auth.test.ts  (matched globs: test/**)
  [suppression] src/foo.ts:12  marker: @ts-ignore
                  // @ts-ignore

JSON (--json)

{
  "tool": "rails-guard",
  "base": "HEAD",
  "ticket": "T42",
  "railGlobs": ["test/**"],
  "railGlobError": null,
  "railsDiffEmpty": true,
  "suppressionsClean": false,
  "sanctionedAdditions": [],
  "passed": false,
  "violations": [
    {
      "file": "src/foo.ts",
      "type": "suppression",
      "marker": "@ts-ignore",
      "lineNo": 12,
      "line": "// @ts-ignore"
    }
  ]
}

Manifest record (--record, clean pass only)

Appended to .adlc/manifest.jsonl:

{
  "ts": "2026-06-10T12:00:00.000Z",
  "type": "rails-check",
  "ticket": "T42",
  "base": "HEAD",
  "railsDiffEmpty": true,
  "suppressionsClean": true,
  "sanctionedAdditions": ["test/frozen/new.test.mjs"],
  "railFiles": {
    "test/auth.test.ts": "abc123...",
    "schema/types.ts": "def456..."
  }
}

sanctionedAdditions (--json and --record) names every rail-matching path that was exempted via the CI-plumbing --sanctioned-add flag for this run — empty when none were used. railsDiffEmpty's own meaning is unchanged: it is still true whenever there are zero rail-edit violations, sanctioned or #228 version-only exemptions included. A path in sanctionedAdditions was a pure ADDITION, never an edit — the exemption is never silent.


`railFiles` contains the SHA-256 of every repo file that matches a rail glob at
the time of the clean pass — a content-addressed snapshot usable for auditing.

## Exit codes

| Code | Meaning |
|------|---------|
| `0` | Gate passes — no violations |
| `1` | Operational error — not a git repo, bad input, no rails resolvable |
| `2` | Gate fails — violations found (list printed to stderr / JSON) |

## Violation types

| `type` | Meaning |
|--------|---------|
| `rail-edit` | A changed file matches a frozen rail glob |
| `suppression` | An added line contains a suppression marker not declared in the ticket |

## Immutable trust roots (CI gate)

The CI backstop `adlc rails-guard-ci` freezes a second set of paths on top of the
active ticket's rails: the **immutable trust roots** (#140). A trust root is a file
whose contents decide what the gate *enforces* — the config, the ticket-store
manifest, CODEOWNERS, the deployed workflow, and the gate's own sources. They are
frozen unconditionally, because a PR that can edit the enforcer can edit away its
own enforcement.

The default set lives in `DEFAULT_IMMUTABLE_TRUST_ROOTS`
(`lib/ci/trust-roots.mjs`) and applies to every repo with no configuration:

.adlc/config.json .adlc/admin.pub .adlc/tickets/.store.json .github/workflows/adlc-rails-guard.yml CODEOWNERS · .github/CODEOWNERS · docs/CODEOWNERS docs/ci/rails-guard.yml scripts/rails-guard-ci.mjs packages/rails-guard/lib/ci/** packages/rails-guard/bin/rails-guard-ci.mjs packages/rails-guard/bin/rails-guard.mjs packages/rails-guard/lib/trust-root-authorization.mjs


**`package.json` and `.npmrc` are NOT in the default set.** They were briefly added
during the 1.11.0 development window and removed again before release: every Node
repo has them and edits them constantly, so freezing them by default turns an
ordinary dependency bump into a denied PR that the consuming repo cannot opt out of.
A repo that *does* want them frozen — the ADLC repo itself does, because its
documented entry points resolve through `scripts` and npm execution flags — declares
them with `--trust-root`.

### Extending the set

`--trust-root <path>` (repeatable) adds a repo-specific trust root. It only ever
**extends** the defaults; nothing on the command line can shrink them. Supplying
them this way is safe because the caller that passes them is itself a default trust
root, so a PR cannot drop the arguments without editing a frozen file.

```bash
adlc rails-guard-ci --base origin/main \
  --trust-root .github/workflows/ci.yml \
  --trust-root package.json

Changing a trust root

A diff that modifies, deletes, or renames a trust root exits 2 unless the #141 ceremony authorizes it: apply the trust-root-change label and obtain an approving review from a non-author CODEOWNER of the changed path. The alternative is the protected-base admin path. Authorization lifts only the specific trust-root paths the ceremony approved — never a rail a ticket declared.

Version-only exemption. A change to a package manifest (package.json, plugin.json, marketplace.json) whose only differences are version fields and lockstep @adlc/* dependency repins is a mechanical release bump, not a trust-root change, and passes without the ceremony (ADR 0012). Any other edit — a scripts change, a third-party dependency bump — is a trust-root change. .npmrc is not a manifest and has no exemption.

Sibling tools

  • flail-detector (C6): uses --record manifest entries to verify no builder session slipped past the gate.
  • consensus-fix (C7): runs every candidate fix through rails-guard before accepting it as a survivor.
  • merge-forecast: reads manifest.jsonl to surface gate history alongside merge risk.

Core gaps

None. All required APIs (globMatch, hashFiles, appendEntry, changedFiles, gitDiff, loadTickets) are present in @adlc/core.

版本与发布记录

@adlc/rails-guard 共发布 18 个版本,首次发布于 2026/06/14;过去 12 个月发布了 18 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.12.0latest2026/10/1027 天前
1.11.12026/09/091 个月前
1.11.02026/08/252 个月前
1.10.02026/08/112 个月前
1.9.02026/08/092 个月前
1.8.02026/08/082 个月前

关于 @adlc/rails-guard 的常见问题

@adlc/rails-guard 是什么?

Rail-freeze enforcement and suppression-marker gate — blocks builder edits to frozen paths (C5).

如何安装 @adlc/rails-guard?

在项目目录运行 npm install @adlc/rails-guard,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 rails-guard、rails-guard-ci,可以用 npx rails-guard 直接运行。

@adlc/rails-guard 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/adlc__rails-guard 是否存在。

@adlc/rails-guard 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@adlc/rails-guard 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@adlc/rails-guard 可以商用吗?

@adlc/rails-guard 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@adlc/rails-guard 的下载量有多少?

根据 npm 官方下载统计,@adlc/rails-guard 在 2026/10/05 – 2026/11/03 这 30 天内被下载 8,452 次,日均约 282 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@adlc/rails-guard 还在维护吗?

最新版本 1.12.0 发布于 2026/10/10(27 天前);至今共发布 18 个版本,其中过去 12 个月发布了 18 个;首次发布于 2026/06/14。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。