# Data Model: Canonical Operation Viewer Context Decoupling ## Overview This feature introduces no database schema changes. The design work is centered on clarifying how existing records and session state combine to produce canonical run-view behavior. ## Core Entities ### OperationRun - **Ownership**: Workspace-owned canonical monitoring record - **Existing fields used by this feature**: - `id` - `workspace_id` - `tenant_id` nullable - `type` - `status` - `outcome` - `context` - `summary_counts` - `initiator_name` - **Relationships**: - Belongs to `workspace` - Optionally belongs to `tenant` - Optionally belongs to `user` - **Feature rule**: Route legitimacy is derived from the run itself plus direct entitlement checks, not from remembered tenant context. ### Tenant - **Ownership**: Workspace-owned durable record - **Existing fields used by this feature**: - `id` - `workspace_id` - `name` - `external_id` - lifecycle state as derived by the tenant operability and lifecycle presentation layer - **Feature rule**: A tenant linked to a run may be active, onboarding, archived, or otherwise non-selectable as current context and still remain a valid tenant reference for the canonical run viewer. ### RememberedTenantContext - **Ownership**: Session-backed operator preference state - **Existing sources used by this feature**: - Current Filament tenant when present and entitled - Workspace remembered tenant when no entitled Filament tenant is present - **Feature rule**: This state may influence labels, back links, and optional information banners, but it never decides whether the canonical run exists or is viewable. ## Derived Viewer State The implementation should model a lightweight derived view state rather than adding persistence. ### CanonicalRunViewerState - **Inputs**: - `OperationRun $run` - current workspace membership - direct tenant entitlement for `$run->tenant_id` when present - current remembered or selected header tenant context - **Derived outputs**: - `authorization_outcome`: allowed, deny-as-not-found, forbidden - `run_tenant_state`: tenantless, active, onboarding, archived, other non-selectable - `header_context_state`: no selected tenant, selected tenant matches run tenant, selected tenant differs from run tenant - `banner_message`: null or an informational message explaining mismatch or lifecycle framing - `follow_up_affordance_state`: available, partially available, unavailable because of lifecycle or entitlement ## Authorization State Matrix | Run Exists | Workspace Member | Tenant Entitled | Capability Granted | Result | |------------|------------------|-----------------|--------------------|--------| | No | N/A | N/A | N/A | 404 not found | | Yes | No | N/A | N/A | 404 deny-as-not-found | | Yes | Yes | No for linked tenant | N/A | 404 deny-as-not-found | | Yes | Yes | Yes or tenantless | No when capability is required | 403 forbidden | | Yes | Yes | Yes or tenantless | Yes or no capability required | Render viewer | ## Presentation State Matrix | Run Tenant | Header Tenant Context | Viewer Valid | Expected Messaging | |------------|-----------------------|--------------|--------------------| | Tenantless | None | Yes | Workspace-level framing only | | Tenantless | Selected tenant present | Yes | Optional note that run is workspace-level and not tied to the selected tenant | | Active tenant | Matching selected tenant | Yes | No mismatch banner required | | Active tenant | Different selected tenant | Yes | Non-blocking mismatch message | | Onboarding or archived tenant | None | Yes | Lifecycle-aware canonical workspace framing | | Onboarding or archived tenant | Different selected tenant | Yes | Lifecycle-aware mismatch message | ## Validation Rules - A run with `workspace_id <= 0` remains invalid and is denied as not found. - A tenant-linked run must never reveal tenant-linked details to an actor who lacks entitlement to that tenant. - A tenantless run must remain viewable based on workspace access and any resolved capability requirement for the run type. - Remembered tenant context must never be mutated as a side effect of viewing a canonical run. ## No Schema Change Confirmation - No new tables - No new columns - No new indexes - No data backfill - No migration work required