Skip to content

fail_closed on an UNKNOWN leaf does not fire for non-blocking (warn) rules #124

Description

@Fszta

Summary

Under on_missing_meta: fail_closed, an UNKNOWN (unresolvable) leaf fires the rule only when the rule is blocking (_is_blocking, parrant/lineage/policy.py ~L924-950). A rule whose action is warn (non-blocking) therefore silently allows on an UNKNOWN leaf — it does not warn. So warn + fail_closed does not actually fail closed.

Why it matters

It's a footgun for compliance policies authored as advisory (warn-only) during rollout: an author writing on_missing_meta: fail_closed reasonably expects "warn me when you can't prove it's safe," but gets silence. A real PII-exposure policy hit exactly this (see #123): once lineage was unresolvable, the fail-closed PII rule returned ALLOW instead of surfacing anything.

Options to consider

  1. Uniform severity: an UNKNOWN under fail_closed fires the rule at its declared action severity (warn→warn, block→block) — most intuitive reading of "fail closed", but changes behavior for existing warn rules (possible noise in meta-poor repos).
  2. Explicit 'unproven' surface: instead of firing, add an explicit unproven/indeterminate count + list to the report (and JSON verdict) so unresolvable leaves are visible without warning-flood. Visibility without coupling to the fire path.
  3. Status quo + docs: keep block-only firing; document that warn rules do not fail closed on UNKNOWN, and that fail-closed enforcement engages at block-promotion.

Leaning toward (2) as the default with clear docs; (1) behind a knob if wanted.

Repro

The deferred-build fixture + runners from the #123 investigation reproduce an UNKNOWN leaf under a warn+fail_closed rule returning ALLOW.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions