Skip to content
Arxo Arxo

Effect Violations

effect_violations

effect_violations enforces effect-boundary policy using layer rules and call-graph propagation.

It tracks:

  • Direct denied effects in files
  • Transitive denied effects reachable through call paths
  • Capability leaks at entity level (no direct denied effects, but denied effects exposed transitively)
  • Effect hubs (high blast-radius effectful orchestrators)
  • Effect coverage gaps (unknown/unscanned files in scope)

Use this metric when you want architectural guarantees such as: “domain code must stay pure” and “effectful boundaries are explicit and controlled.”

  • Call graph
  • Effect index
  • Entity graph
KeyMeaning
effect_violations.violation_countdirect_violations + transitive_violations + capability_leaks
effect_violations.direct_violationsCount of direct effect-policy violations
effect_violations.transitive_violationsCount of transitive effect-policy violations
effect_violations.capability_leaksEntity-level leaks of denied transitive effects
effect_violations.effect_hub_countFiles with outgoing effectful calls above configured threshold
effect_violations.effect_hub_scoreAverage outgoing effectful-call count across hubs
effect_violations.effectful_file_countFiles detected as effectful
effect_violations.pure_file_countFiles detected as pure
effect_violations.unknown_file_countIn-scope files with unknown/unscanned effect profile
effect_violations.coverage_ratioShare of in-scope files with known effect scan
effect_violations.healthWeighted health score in [0,1] (higher is better)

Notes:

  • violation_count does not include hubs or coverage gaps.
  • Hubs and coverage are modeled through dedicated keys plus findings.

For each candidate effect in scope:

  1. Applicable rules are selected from architecture.effect_rules (from_layers / from_paths).
  2. For transitive checks, only rules with check_transitive: true are considered.
  3. deny_effects wins first.
  4. Then allow_effects can allow.
  5. If no rule decides and file belongs to a layer, layer.allowed_effects is applied.
  6. If a rule matched scope but no allow matched and no layer allow exists, effect is denied.

Definitions:

  • direct_ratio = direct_violations / scoped_file_count
  • transitive_ratio = transitive_violations / scoped_file_count
  • capability_ratio = capability_leaks / scoped_file_count
  • hub_ratio = effect_hub_count / scoped_file_count
  • unknown_penalty = (unknown_file_count / scoped_file_count) * unknown_penalty_weight

Then:

  • weighted_debt = 0.35*direct_ratio + 0.35*transitive_ratio + 0.15*capability_ratio + 0.10*hub_ratio + 0.05*unknown_penalty
  • effect_violations.health = 1 - clamp(weighted_debt, 0, 1)

Coverage:

  • coverage_ratio = (scoped_file_count - unknown_file_count) / scoped_file_count (or 1.0 when scope is empty)

effect_violations emits deterministic findings with these rule_id values:

  • arxo/effect-direct-violation
  • arxo/effect-transitive-violation
  • arxo/effect-capability-leak
  • arxo/effect-coverage-gap
  • arxo/effect-hub

These are returned in MetricResult.findings and appear in JSON/SARIF/HTML reports.

effect_violations behavior is configured under architecture:

architecture:
layers:
- name: domain
paths: ["src/domain/**"]
allowed_effects: []
can_depend_on: []
- name: infra
paths: ["src/infra/**"]
allowed_effects: ["io", "network", "storage", "log"]
can_depend_on: ["domain"]
effect_rules:
- name: domain-deny-impure-effects
from_layers: ["domain"]
deny_effects: ["io", "network", "storage"]
check_transitive: true
max_depth: 6
- name: domain-allow-logging-subtree
from_paths: ["src/domain/observability/**"]
allow_effects: ["log"]
check_transitive: false
effect_violations:
hub_threshold: 5
max_witness_paths: 10
unknown_penalty_weight: 0.05
strict_confidence_min: 0.8
metrics:
- id: effect_violations
enabled: true
OptionDefaultPurpose
hub_threshold5Outgoing effectful-call count needed to classify a file as an effect hub
max_witness_paths10Max witness paths/hubs retained for UI and findings
unknown_penalty_weight0.05Multiplier for unknown coverage debt in health score
strict_confidence_min0.0Ignore effect occurrences below this confidence

Effect kinds are lowercase strings (for example: io, network, storage, log, time, random, mutation, llm), and can be extended with custom kinds.

metrics:
- id: effect_violations
enabled: true
policy:
invariants:
- metric: effect_violations.violation_count
op: "=="
value: 0
message: "No effect boundary violations allowed"
- metric: effect_violations.coverage_ratio
op: ">="
value: 0.95
message: "Keep effect scan coverage high"
policy:
invariants:
- metric: effect_violations.violation_count
op: "<="
value: 10
- metric: effect_violations.effect_hub_count
op: "<="
value: 5
- metric: effect_violations.health
op: ">="
value: 0.80
Terminal window
# Run only effect boundary analysis
arxo analyze --metric effect_violations --format json
# Explain metric in CLI
arxo metrics explain effect_violations