# Data Model: RBAC Role Definition Diff UX Upgrade

## Overview

This feature introduces no database schema changes. The data model is a presentation-layer model that adapts existing `Finding.evidence_jsonb.rbac_role_definition` content into shared diff presentation objects.

## Existing Input Model

### RbacRoleDefinitionEvidence

- **Source**: `Finding.evidence_jsonb.rbac_role_definition`
- **Purpose**: authoritative comparison payload already produced by the baseline comparison engine
- **Core fields**:
  - `diff_kind`: string such as `metadata_only`, `missing`, or `unexpected`
  - `diff_fingerprint`: stable change fingerprint for recurrence logic
  - `changed_keys`: array of operator-facing diff keys that materially changed
  - `metadata_keys`: array of metadata-related changed keys
  - `permission_keys`: array of permission-related changed keys
  - `baseline`: `RbacRoleDefinitionSide`
  - `current`: `RbacRoleDefinitionSide`

### RbacRoleDefinitionSide

- **Source**: `rbac_role_definition.baseline` and `rbac_role_definition.current`
- **Purpose**: stores one side of the RBAC comparison
- **Core fields**:
  - `normalized`: map of `field_key => value`
  - `is_built_in`: nullable boolean used for role source display fallback
  - `role_permission_count`: nullable integer used for permission-block summary fallback

## New Presentation Model

### RbacRoleDefinitionDiffBuilder

- **Type**: new consumer-local adapter class
- **Purpose**: translate `RbacRoleDefinitionEvidence` into shared `DiffPresentation`
- **Responsibilities**:
  - merge baseline and current normalized rows
  - apply RBAC label mapping
  - apply deterministic field ordering
  - mark Allowed Actions and other approved list-like fields for inline list rendering
  - pass `changed_keys` through to shared state classification
  - preserve no-change and sparse-data behavior

### DiffPresentation

- **Type**: existing shared DTO from Spec 141
- **Fields**:
  - `summary`: `DiffSummary`
  - `rows`: ordered array of `DiffRow`
- **Purpose**: single render-ready result consumed by the RBAC Blade entry

### DiffSummary

- **Type**: existing shared DTO
- **Fields**:
  - `changedCount`
  - `addedCount`
  - `removedCount`
  - `unchangedCount`
  - `hasRows`
  - `message`
- **Purpose**: drive summary badge counts and no-change/no-data messaging

### DiffRow

- **Type**: existing shared DTO
- **Fields**:
  - `key`: stable field key
  - `label`: operator-facing label
  - `status`: `changed | unchanged | added | removed`
  - `oldValue`
  - `newValue`
  - `isListLike`
  - `addedItems`
  - `removedItems`
  - `unchangedItems`
  - `meta`
- **Purpose**: represent one renderable field row in the RBAC diff region

## Consumer Configuration Model

### RbacFieldLabelMap

- **Type**: internal associative map in the builder
- **Purpose**: keep operator-facing labels stable and concise when internal keys need normalization or fallback handling
- **Examples**:
  - `Role definition > Display name`
  - `Role definition > Description`
  - `Role definition > Role source`
  - `Role definition > Permission blocks`
  - `Permission block 1 > Allowed actions`

### RbacFieldOrderRules

- **Type**: internal ordering strategy in the builder
- **Purpose**: preserve predictable review order
- **Desired order**:
  1. top-level role metadata
  2. role source and permission-block summary
  3. permission-block details
  4. alphabetical fallback for unknown but valid keys

### RbacListFieldRules

- **Type**: internal field designation rules
- **Purpose**: identify which RBAC fields should intentionally use inline list diff rendering
- **Initial designated fields**:
  - any `Permission block * > Allowed actions`
  - optionally other simple list-valued RBAC keys that fit the same operator model, such as denied actions or conditions, if present and still readable

## State Derivation Rules

### Row status

- **Added**: key exists only on current side
- **Removed**: key exists only on baseline side
- **Changed**: key exists on both sides and either appears in `changed_keys` or has unequal values
- **Unchanged**: key exists on both sides and values are equal without change designation

### List fragments

- **Added items**: values present only in current list
- **Removed items**: values present only in baseline list
- **Unchanged items**: values present in both lists in shared order-preserving form

## Validation Rules

- Ignore empty or non-string field keys.
- Treat absent `baseline.normalized` and `current.normalized` maps as empty arrays.
- Preserve explicit `null`, boolean, scalar, and list values so `ValueStringifier` can render them consistently.
- Treat unknown metadata as optional and never required for row rendering.

## No Schema Change Statement

- No new tables, columns, indexes, or migrations are required.
- `findings.evidence_jsonb` remains the authoritative source for the RBAC consumer.
