> ## 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-002

> An explicit permanent waiver (expires: permanent) must name an approver who owns the accepted risk; an unapproved permanent waiver is a governance hole, so Charter both flags it (High) and refuses to honor it — the finding re-surfaces until an approver is recorded or a finite expires is set.

**Rule ID:** AE-SUPPRESS-002 · **Severity:** <Badge color="orange">High</Badge> · **Category:** Governance · **Auto-fixable:** No

## Why this rule

A permanent suppression with no expiry and no approver is an unbounded waiver. It silently removes a finding from the score indefinitely with no human accountability attached.

Unlike a time-bounded suppression — which expires and forces a re-evaluation — a permanent waiver with no approver can survive security reviews, team rotations, and architectural changes without ever being questioned. Charter refuses to honor it until a named approver accepts the risk. See [Suppression governance](/docs/concepts/suppression-governance) for how waivers are evaluated.

## What triggers it

Charter audits every active suppression — non-expired entries in `.charter-suppress.yml` and inline `# charter:ignore` directives matched to a finding — and flags any entry that is an explicit permanent waiver without a named approver.

A suppression is a **permanent waiver** when its `expires` field is set to the literal value `permanent`. If that entry has no `approver:` field (or the approver is blank), Charter:

1. Flags the entry at **High** severity
2. **Does not honor the suppression** — the underlying finding remains active and is included in scoring

<Warning>
  An unapproved permanent waiver is not honored. The original finding continues to affect the score until either an `approver:` is added or the suppression is converted to a time-bounded entry.
</Warning>

Suppressions with a future `expires` date, or with no `expires` at all (which applies a default TTL), are not permanent waivers and do not require an approver.

## Examples

<Tabs>
  <Tab title="Failing">
    Permanent waiver with no approver — flagged High, suppression not honored:

    ```yaml .charter-suppress.yml theme={null}
    suppressions:
      - rule: AE-TEST-001
        reason: "Backend-only repo"
        expires: permanent
        # no approver field → flagged High, finding stays active
    ```

    An entry that fires both AE-SUPPRESS-001 and AE-SUPPRESS-002:

    ```yaml .charter-suppress.yml theme={null}
    suppressions:
      - rule: AE-CC-002
        expires: permanent
        # no reason AND no approver → both rules fire
    ```
  </Tab>

  <Tab title="Passing">
    Permanent waiver with a named approver:

    ```yaml .charter-suppress.yml theme={null}
    suppressions:
      - rule: AE-TEST-001
        reason: "Backend-only repo — no application code, only data pipeline scripts"
        expires: permanent
        approver: "@tashfiqul"
    ```

    A time-bounded suppression — no approver required:

    ```yaml .charter-suppress.yml theme={null}
    suppressions:
      - rule: AE-MCP-001
        reason: "Temporary during migration to new MCP server config"
        expires: "2026-09-01"
    ```

    An entry with no `expires` field — default TTL applies, not a permanent waiver:

    ```yaml .charter-suppress.yml theme={null}
    suppressions:
      - rule: AE-CI-002
        reason: "CI bootstrap in progress"
    ```
  </Tab>
</Tabs>

## How to fix

<Steps>
  <Step title="Identify unapproved permanent waivers">
    Run [`charter explain`](/cli/explain) with `AE-SUPPRESS-002` — Charter lists every permanent waiver missing an approver, including the file location and the rule being suppressed.
  </Step>

  <Step title="Add an approver or convert to time-bounded">
    Choose one of two paths:

    **Option A — Add an approver** (keep the permanent waiver, add accountability):

    ```yaml theme={null}
    - rule: AE-TEST-001
      reason: "Backend-only repo"
      expires: permanent
      approver: "@tashfiqul"   # ← add this
    ```

    **Option B — Convert to time-bounded** (eliminate the permanent waiver). The rule ID is positional on [`charter suppress`](/cli/suppress):

    ```bash theme={null}
    charter suppress AE-TEST-001 \
      --reason "Backend-only repo" \
      --expires 90d
    ```
  </Step>

  <Step title="Confirm the finding clears">
    Run `charter doctor` to verify the finding no longer appears and the suppression is now honored.
  </Step>
</Steps>

<Tip>
  AE-SUPPRESS-003 (Informational) surfaces when the overall suppression rate is high across the repo. AE-SUPPRESS-002 (High) is about accountability on permanent waivers specifically — the two rules are complementary, not redundant.
</Tip>

## Score impact

`High` (−10 per finding). No hard cap — caps are reserved for raw-secret and Blocker findings. The underlying finding also remains active and contributes its own deduction, since the unapproved permanent waiver is not honored. See [Scoring and caps](/docs/concepts/scoring-and-caps).

## Edge cases

<AccordionGroup>
  <Accordion title="Malformed expires field">
    An `expires` value that is not `YYYY-MM-DD` or `permanent` fails closed — the engine treats the entry as inert, suppresses nothing, and the finding stays active. The malformed entry is not audited by AE-SUPPRESS-002 (only non-expired file entries and matched inline directives are audited).
  </Accordion>

  <Accordion title="Blank approver counts as missing">
    A `approver:` field containing only whitespace counts as missing and fires the finding.
  </Accordion>

  <Accordion title="Secret rule suppressions">
    Secrets are suppressible like any rule. An unapproved permanent `AE-SEC-001` or `AE-SEC-002` suppression fires this rule and is not honored — the ≤49 raw-secret score cap remains in effect. An approved permanent secret suppression lifts the cap.
  </Accordion>

  <Accordion title="Both AE-SUPPRESS-001 and AE-SUPPRESS-002 can fire together">
    An entry that is both reason-less and a permanent waiver without an approver fires both AE-SUPPRESS-001 (Medium, −4) and AE-SUPPRESS-002 (High, −10) independently.
  </Accordion>
</AccordionGroup>

## Related rules

<CardGroup cols={2}>
  <Card title="AE-SUPPRESS-001" icon="file-pencil" href="/rules/AE-SUPPRESS-001">
    Requires a reason on every suppression — the documentation 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 across the repo is high.
  </Card>
</CardGroup>

## CLI

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