All six L2 hygiene checks unified into one row-per-violation view. KPI = total open violations; bar chart breaks down by check_type so you see which check kind dominates today; the detail table sorts by count (descending) so the worst offenders surface first. Each check_type captures a 'declaration vs runtime' mismatch the L1 dashboard doesn't catch — Chain Orphans, Unmatched Transfer Type, Dead Rails, Dead Bundles Activity, Dead Metadata Declarations, Dead Limit Schedules.
Open L2 Violations
Total count across all six L2 hygiene checks.
L2 Violations by Check Type
Count per L2 hygiene check. A spike in one kind points at a recurring class of declaration-vs-runtime drift to investigate first.
L2 Violation Detail
Every row is one detected L2 violation. Sorted by count (largest first). Right-click any row to drill into Rails (entity_a → Rail filter) or Chains (entity_a → Chain filter). Read entity_a / entity_b / detail in the context of the row's check_type — see the sheet description above for which fields each check populates.
**L2 Hygiene Exceptions — what each check surfaces**
Each row in the table above is a piece of L2 declaration that doesn't match the live runtime. None of these break the ledger; they break the L2-to-runtime correspondence the integrator's ETL is supposed to maintain. The six check kinds:
- **Chain Orphans.** Either fix the ETL so the child rail fires when the parent does (the L2 says it should), or retire the chain edge from the L2 YAML if the parent-child causality no longer holds. Each row names the parent + child rail — drill to the L2FT Chains sheet for the firing-count history.
- **Unmatched Rail Name.** Either add the rail to the L2 YAML (`rails:` block) with the right `source_role` / `destination_role`, or fix the ETL to stop emitting rows with that `rail_name`. A row here means the L2 doesn't even know about a money path the bank is running — the L1 limit-breach + drift checks can't fire against undeclared rails, so this is a silent-blind-spot indicator.
- **Dead Rails.** Either the declared rail is genuinely unused (retire it from the L2 YAML), or the ETL is misrouting postings against it (check `rail_name` casing + spelling against the L2). A long-dead rail is L2 noise — it shows up in dropdowns + handbook prose but never has data.
- **Dead Bundles Activity.** Either the aggregating rail's bundling actually includes the named target rail (check the ETL's `bundle_id` writes — each authorization that should roll into the aggregating settlement must carry `bundle_id`), or the L2's `bundles_activity` over-claims what the rail bundles. Drop the false claim from the L2.
- **Dead Metadata Declarations.** Either the ETL needs to start writing the key into `transactions.metadata` (the L2 says this rail should carry it), or the L2 declares a key that's not actually used — drop it from the rail's `metadata_keys` block. Live metadata is what the L2FT cascade dropdowns drive off; a dead declaration shows an empty dropdown to operators who expected it to narrow.
- **Dead Limit Schedules.** Either the rail genuinely doesn't carry outbound debit flow from this role (retire the LimitSchedule entry), or the ETL routes the flow through a different (`role`, `rail`) cell than the L2 expects (verify the `account_parent_role` denormalization on every Debit row). A dead LimitSchedule means the L1 limit-breach check has nothing to catch — silent for that combination.