> ## Documentation Index
> Fetch the complete documentation index at: https://tashfiq.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# AE-SUPPRESS-001

> Every suppression must carry a human-readable reason so that an accepted finding is auditable and a future reviewer understands why it was muted.

**Rule ID:** AE-SUPPRESS-001 · **Severity:** <Badge color="yellow">Medium</Badge> · **Category:** Governance · **Auto-fixable:** No

## Why this rule

A suppression without a reason is indistinguishable from accident. When a finding re-surfaces after a security review or team rotation, there is no record of why it was acceptable or who decided.

Suppressions are a legitimate tool for managing accepted risk — but only when the decision is documented. A reason-less suppression creates a silent blind spot that grows harder to audit over time. See [Suppression governance](/docs/concepts/suppression-governance) for the full model.

## What triggers it

Charter audits every suppression that was applied during the current scan:

* All entries in `.charter-suppress.yml`
* Every inline `# charter:ignore` comment that matched a finding

For each applied suppression, Charter checks whether a `reason` field is present and non-empty. Both of these fire the finding:

* A YAML entry with no `reason:` key
* An inline directive with no `reason="…"` segment

<Note>
  The suppression itself still takes effect — Charter continues to suppress the underlying finding. A missing reason is a governance hygiene problem, not grounds to un-suppress. The deduction is applied to the score, not the original finding.
</Note>

## Examples

<Tabs>
  <Tab title="Failing">
    A `.charter-suppress.yml` entry with no reason field:

    ```yaml .charter-suppress.yml theme={null}
    suppressions:
      - rule: AE-CC-001
        expires: "2026-09-07"
        # no reason field → flagged Medium
    ```

    An inline directive with no reason:

    ```go internal/scanner.go theme={null}
    // charter:ignore AE-MCP-001
    func scanMCP() { ... }
    ```
  </Tab>

  <Tab title="Passing">
    A YAML entry with a meaningful reason:

    ```yaml .charter-suppress.yml theme={null}
    suppressions:
      - rule: AE-CC-001
        reason: "Claude config lives in the infra repo — hook scanning not applicable here"
        expires: "2026-09-07"
        approver: alice
    ```

    An inline directive with a reason segment:

    ```go internal/scanner.go theme={null}
    // charter:ignore AE-MCP-001 reason="vendored test fixture, not a real MCP server"
    func scanMCP() { ... }
    ```
  </Tab>
</Tabs>

## How to fix

<Steps>
  <Step title="Find reason-less suppressions">
    Run [`charter explain`](/cli/explain) with `AE-SUPPRESS-001` — Charter names the suppressed rule and its source file (YAML entry or inline comment location) in the finding evidence.
  </Step>

  <Step title="Add a meaningful reason">
    For YAML entries, add a `reason:` field. For inline comments, add `reason="…"` after the rule ID. Use plain language — a sentence is enough.
  </Step>

  <Step title="Use charter suppress for new entries">
    The [`charter suppress`](/cli/suppress) command always writes the reason for you when you provide `--reason`. The rule ID is positional:

    ```bash theme={null}
    charter suppress AE-MCP-001 \
      --reason "vendored test fixture, not a real MCP server" \
      --expires 90d
    ```
  </Step>
</Steps>

<Tip>
  A good reason answers: "Why is this finding acceptable in this repo, specifically?" Generic reasons like "not applicable" or "known issue" are technically valid but provide no value to future reviewers.
</Tip>

## Score impact

`Medium` (−4 per finding). No hard cap — caps are reserved for raw-secret and Blocker findings. One finding per reason-less suppression entry. See [Scoring and caps](/docs/concepts/scoring-and-caps).

## Edge cases

<AccordionGroup>
  <Accordion title="Non-matched YAML entries are still audited">
    A suppression entry whose rule never matched any finding this scan — but which is still non-expired — is still audited for a reason field. Expired entries are inert and not evaluated.
  </Accordion>

  <Accordion title="Dangling inline directives are not audited">
    An inline `# charter:ignore` comment that matched no finding is not audited. Inline directives are discovered only at finding locations.
  </Accordion>

  <Accordion title="Whitespace-only reason counts as missing">
    A `reason:` field containing only spaces or tabs counts as missing and fires the finding.
  </Accordion>

  <Accordion title="Secret rule suppressions">
    Secrets are suppressible like any rule. A reason-less `AE-SEC-001` or `AE-SEC-002` suppression is audited exactly the same way as any other rule.
  </Accordion>
</AccordionGroup>

## Related rules

<CardGroup cols={2}>
  <Card title="AE-SUPPRESS-002" icon="shield-lock" href="/rules/AE-SUPPRESS-002">
    Requires an approver on permanent suppressions — the accountability companion to this rule.
  </Card>

  <Card title="AE-SUPPRESS-003" icon="chart-bar" href="/rules/AE-SUPPRESS-003">
    Informational — surfaces when the overall suppression rate is high.
  </Card>
</CardGroup>

## CLI

```bash theme={null}
charter explain AE-SUPPRESS-001
charter suppress <RULE> --reason "..." --expires 90d
```
