Skip to content
Fallow home
All docs pages

fallow guard

Know the architecture rules for a file before you edit it. Fallow guard reports the boundary zone, the zones the file can import from, the forbidden calls, and the rule-pack policies with their suppression tokens.

Before you write code in a file, fallow guard tells you what the file is allowed to do. You can then choose an allowed import from the start, and you do not have to fix a violation later. For each file, guard reports:

  • the boundary zone of the file
  • the zones that the file can import from
  • the calls that are forbidden in that zone
  • each rule-pack policy in scope, with the token that suppresses it

guard reads the configuration and runs no analysis. It is fast enough to call on every edit, and it works on files that do not exist yet.

fallow guard src/ui/App.ts
fallow guard src/ui/App.ts src/db/query.ts --format json

guard shows the rules before an edit. For violations that already exist in the code, run fallow architecture after the edit. fallow audit also reports boundary-violation and policy-violation findings in the changed files.

Output

src/ui/App.ts (zone: ui)
  note: Same-zone imports are always allowed.
  may import zones: shared, ui (same zone)   type-only: db
  forbidden calls in zone: child_process.*, eval
  policy rules:
    warn  house-rules/no-child-process  banned-call: child_process.*
           suppress: // fallow-ignore-next-line policy-violation:house-rules/no-child-process -- <reason>
  severities: boundary-violation=error  policy-violation=warn

Files that do not exist yet

guard matches the path against the zone patterns and does not read the file. You can call it for a file that you are about to create:

fallow guard src/ui/NewScreen.tsx

The JSON has "exists": false, and the human output adds an exists: false line. guard fills every rule field as usual.

Paths outside every zone

For a path that matches no configured zone, guard returns "zone": null and an empty rule set. This is a normal answer and not an error: import rules do not restrict files outside every zone.

src/orphan/thing.ts (zone: none)
  note: Files outside every zone are unrestricted for import and call checks.
  may import zones: none   type-only: none
  forbidden calls in zone: none
  policy rules:
    warn  house-rules/no-moment  banned-import: moment
           suppress: // fallow-ignore-next-line policy-violation:house-rules/no-moment -- <reason>
  severities: boundary-violation=error  policy-violation=warn

A file outside every zone has no import rules, but the policy rules still apply. A rule-pack rule that declares no zones applies everywhere, so it still appears in the output.

Some projects set boundaries.coverage.requireAllFiles. If the path does not match an allowUnmatched glob, the JSON has "coverage_required": true. An analysis run then reports the file as a boundary-coverage-violation because it belongs to no zone. The human output adds a second note that says boundaries.coverage.requireAllFiles is enabled. In scripts, read coverage_required from the JSON.

Options

FlagDefaultDescription
<FILES>...requiredOne or more files to report on. Paths can be root-relative or absolute. The output shows them root-relative.
--root <ROOT>current directoryProject root directory.
--config <CONFIG>discoveredPath to the Fallow config file.
--allow-remote-extendsoffAllow trusted config files to extend HTTPS URLs.
--format <FORMAT>humanhuman or json only.
--prettyoffIndent JSON output.
--quietoffSuppress progress output.

--help also lists the analysis flags. They have no effect on guard because it runs no analysis. This includes the scoping, baseline, and severity-gate flags. Any format other than human or json exits 2 with guard supports --format json or human.

Exit codes

CodeMeaning
0The rules were reported, including when a path matches no zone or does not exist.
2Invalid input or configuration, such as an unsupported --format or a rule pack that fails to parse.

guard reports no findings, so it never exits 1.

JSON output

{
  "files": [
    {
      "path": "src/ui/App.ts",
      "exists": true,
      "zone": {
        "name": "ui",
        "patterns": ["src/ui/**"]
      },
      "boundary": {
        "configured": true,
        "unrestricted": false,
        "allowed_zones": ["shared", "ui"],
        "allowed_type_only_zones": ["db"],
        "forbidden_calls": ["child_process.*", "eval"],
        "coverage_required": false
      },
      "policy_rules": [
        {
          "pack": "house-rules",
          "rule_id": "no-child-process",
          "kind": "banned-call",
          "patterns": ["child_process.*"],
          "message": null,
          "severity": "warn",
          "suppress_token": "policy-violation:house-rules/no-child-process"
        }
      ],
      "severities": {
        "boundary_violation": "error",
        "policy_violation": "warn"
      },
      "notes": ["Same-zone imports are always allowed."]
    }
  ],
  "kind": "guard"
}
  • allowed_zones always contains the zone of the file itself, because same-zone imports are never restricted.
  • unrestricted is true when no import rule applies to the file. This happens when boundaries are not configured, or when the path is outside every zone.
  • policy_rules contains only the rules in scope for that file. A rule that declares zones appears only for files in those zones. The array is empty when rules.policy-violation is off.
  • For a gdp-proof-producer policy, allowed_files lists the globs where proof factories are permitted. proof_kinds, when present, lists the exact label filter. An absent filter checks every recognized factory call, including dynamic labels. The human output also shows the allowed producers and proof kinds. These fields describe permission; the rule's scope still comes from files, exclude, and zones.

Suppression tokens

Each policy rule reports the exact token that suppresses it. You can copy the comment directly from the output:

// fallow-ignore-next-line policy-violation:house-rules/no-moment -- vendored, migrating in Q3
import moment from "moment";

The token is policy-violation:<pack>/<rule-id>. Rule ids are unique in a pack, so the pair identifies one rule in all places.

For agents

Call guard on the files you are about to change, while you can still choose what to write. An agent that reads the rules first can pick an allowed import. An agent that runs an analysis after the edit can only fix the violation it already wrote.

fallow guard --format json --quiet <files>

After the edit, run fallow architecture to find the violations in the code:

fallow architecture --format json --quiet

The guard MCP tool gives the same output as fallow guard. The MCP integration guide describes it.

See also