TenantAtlas/specs/144-canonical-operation-viewer-context-decoupling/data-model.md
ahmido b0a724acef feat: harden canonical run viewer and onboarding draft state (#173)
## Summary
- harden the canonical operation run viewer so mismatched, missing, archived, onboarding, and selector-excluded tenant context no longer invalidates authorized canonical run viewing
- extend canonical route, header-context, deep-link, and presentation coverage for Spec 144 and add the full spec artifact set under `specs/144-canonical-operation-viewer-context-decoupling/`
- harden onboarding draft provider-connection resume logic so stale persisted provider connections fall back to the connect-provider step instead of resuming invalid state
- add architecture-audit follow-up candidate material and prompt assets for the next governance hardening wave

## Testing
- `vendor/bin/sail bin pint --dirty --format agent`
- `vendor/bin/sail artisan test --compact tests/Feature/144/CanonicalOperationViewerContextMismatchTest.php tests/Feature/144/CanonicalOperationViewerDeepLinkTrustTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php tests/Feature/OpsUx/OperateHubShellTest.php tests/Feature/Monitoring/OperationsTenantScopeTest.php tests/Feature/RunAuthorizationTenantIsolationTest.php tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php tests/Feature/Monitoring/HeaderContextBarTest.php tests/Feature/Monitoring/OperationRunResolvedReferencePresentationTest.php tests/Feature/Monitoring/OperationsCanonicalUrlsTest.php`
- `vendor/bin/sail artisan test --compact tests/Feature/ManagedTenantOnboardingWizardTest.php tests/Unit/Onboarding/OnboardingDraftStageResolverTest.php tests/Unit/Onboarding/OnboardingLifecycleServiceTest.php`

## Notes
- branch: `144-canonical-operation-viewer-context-decoupling`
- base: `dev`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #173
2026-03-15 18:32:04 +00:00

99 lines
4.2 KiB
Markdown

# 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