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
- 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).
- 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.
- 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.
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 iswarn(non-blocking) therefore silently allows on an UNKNOWN leaf — it does not warn. Sowarn+fail_closeddoes 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_closedreasonably 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
fail_closedfires 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).unproven/indeterminatecount + list to the report (and JSON verdict) so unresolvable leaves are visible without warning-flood. Visibility without coupling to the fire path.warnrules 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.