TenantAtlas/specs/196-hard-filament-nativity-cleanup/data-model.md
ahmido a2a42d4e5f Spec 196: finalize hard Filament nativity cleanup artifacts (#231)
## Summary
- add the complete Spec 196 artifact set for hard Filament nativity cleanup
- include spec, requirements checklist, plan, research, data model, logical contract, quickstart, and executable tasks
- update agent context after planning
- resolve all cross-artifact consistency issues so the feature package is implementation-ready

## Included artifacts
- specs/196-hard-filament-nativity-cleanup/spec.md
- specs/196-hard-filament-nativity-cleanup/checklists/requirements.md
- specs/196-hard-filament-nativity-cleanup/plan.md
- specs/196-hard-filament-nativity-cleanup/research.md
- specs/196-hard-filament-nativity-cleanup/data-model.md
- specs/196-hard-filament-nativity-cleanup/contracts/filament-nativity-cleanup.logical.openapi.yaml
- specs/196-hard-filament-nativity-cleanup/quickstart.md
- specs/196-hard-filament-nativity-cleanup/tasks.md

## Notes
- no runtime code paths were changed
- no application tests were run because this change set is spec and planning documentation only
- the artifact set was re-analyzed until no consistency issues remained

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #231
2026-04-13 10:26:27 +00:00

212 lines
10 KiB
Markdown

# Data Model: Hard Filament Nativity Cleanup
## Overview
This feature introduces no new persisted entity, table, enum, or product-domain source of truth. It refactors three existing UI surfaces by replacing pseudo-native interaction contracts with native page-owned or component-owned state.
The data model for planning is therefore a set of derived UI-state and row-projection models that answer four questions:
1. What state is authoritative for each cleaned surface?
2. Which source truths continue to produce the rows and summaries?
3. Which values may be seeded from deeplinks, and which values must remain route- or entitlement-authoritative?
4. Which invariants must remain true after the cleanup?
## Existing Source Truths Reused Without Change
The following truths remain authoritative and are not redefined by this feature:
- `InventoryItem`, `InventoryLink`, `DependencyQueryService`, and `DependencyTargetResolver` for dependency edges and rendered targets
- the current tenant-context inventory route and inventory-record scope rules
- `TenantRequiredPermissionsViewModelBuilder`, `TenantPermission`, permission configuration, and provider guidance links for required-permissions truth
- the route-scoped tenant on `/admin/tenants/{tenant:external_id}/required-permissions`
- `EvidenceSnapshot`, `TenantReview`, `ArtifactTruthPresenter`, and the current workspace-context entitlement rules for evidence overview rows
- existing capability registries, `WorkspaceContext`, tenant membership checks, and current deny-as-not-found boundaries
This feature changes how these truths are controlled and rendered, not what they mean.
## New Derived Planning Models
### DependencyEdgesTableState
**Type**: embedded detail-surface state
**Source**: Livewire component state on inventory item detail
| Field | Type | Notes |
|------|------|-------|
| `inventoryItemId` | int | Required current detail record key |
| `tenantId` | int | Required tenant-context key derived from the current panel or record scope |
| `direction` | string | Allowed values: `all`, `inbound`, `outbound`; default `all` |
| `relationshipType` | string or null | Null means all relationship types; otherwise one allowed relationship type key |
**Validation rules**
- `inventoryItemId` must resolve to the current authorized record.
- `tenantId` must match the current tenant-context scope.
- `direction` must stay inside the three allowed values.
- `relationshipType` must be null or a recognized relationship type value.
### DependencyEdgeRow
**Type**: derived row projection
**Source**: `DependencyQueryService` plus `DependencyTargetResolver`
| Field | Type | Notes |
|------|------|-------|
| `relationshipType` | string | Canonical relationship family for grouping or filter matching |
| `targetType` | string | Current target kind, including `missing` when unresolved |
| `targetId` | string or null | External or internal target identifier |
| `renderedTarget` | array | Existing rendered badge and link payload |
| `isMissing` | boolean | Derived from `targetType === missing` |
| `missingTitle` | string | Existing descriptive fallback text for unresolved targets |
**Invariants**
- Row membership must stay tenant-isolated.
- Missing-target rendering must preserve current operator hints.
- Render-time behavior must remain DB-only with no Graph access.
### RequiredPermissionsTableState
**Type**: page-owned derived table state
**Source**: native Filament table filters and search on `TenantRequiredPermissions`
| Field | Type | Notes |
|------|------|-------|
| `routeTenantExternalId` | string | Authoritative tenant scope from the route |
| `status` | string | Allowed values: `missing`, `present`, `error`, `all` |
| `type` | string | Allowed values: `all`, `application`, `delegated` |
| `features` | list<string> | Zero or more selected feature keys |
| `search` | string | Native table search text |
| `seededFromQuery` | boolean | True only during initial mount when deeplink values were present |
**Validation rules**
- The route tenant always wins over tenant-like query values.
- Query values may seed `status`, `type`, `features`, and `search` only at initial mount.
- `features` must be a normalized unique list of known feature keys.
### RequiredPermissionsSummaryProjection
**Type**: derived page summary model
**Source**: `TenantRequiredPermissionsViewModelBuilder` evaluated against the currently active normalized filter state
| Field | Type | Notes |
|------|------|-------|
| `counts` | object | Existing counts for missing application, missing delegated, present, and error rows |
| `overall` | string or null | Existing overall readiness state |
| `freshness` | object | Existing freshness payload including stale or not stale |
| `featureImpacts` | list<object> | Existing per-feature impact summary |
| `copyPayloads` | object | Existing application and delegated copy payloads |
| `issues` | list<object> | Existing derived guidance and next-step content |
**Invariants**
- Summary and table rows must be derived from the same active filter state.
- Copy payload semantics must remain consistent with current expectations.
- Tenant scope must not be mutable through filter state.
### PermissionReviewRow
**Type**: derived table row
**Source**: `TenantRequiredPermissionsViewModelBuilder`
| Field | Type | Notes |
|------|------|-------|
| `permissionKey` | string | Stable permission identifier |
| `type` | string | `application` or `delegated` |
| `status` | string | Current permission review status |
| `description` | string | Human-readable permission description |
| `features` | list<string> | Feature tags associated with the permission |
| `details` | object | Existing supporting metadata used for inline review only |
### EvidenceOverviewTableState
**Type**: workspace-context table state
**Source**: native Filament table search and optional query-seeded entitled tenant prefilter
| Field | Type | Notes |
|------|------|-------|
| `workspaceId` | int | Required current workspace context |
| `authorizedTenantIds` | list<int> | Entitled tenant ids available to the actor |
| `tenantFilter` | int or null | Current entitled tenant prefilter, nullable when not active |
| `search` | string | Native table search across tenant-facing row labels |
| `seededFromQuery` | boolean | True only when the initial request carried a prefilter |
**Validation rules**
- `tenantFilter` must be null or one of the actor's entitled tenant ids.
- Missing workspace membership continues to produce `404`.
- Non-entitled tenant ids must not leak through filter state, row counts, or drilldowns.
### EvidenceOverviewRow
**Type**: derived workspace report row
**Source**: current snapshot query plus `ArtifactTruthPresenter`
| Field | Type | Notes |
|------|------|-------|
| `tenantId` | int | Entitled tenant identifier |
| `tenantName` | string | Current display label |
| `snapshotId` | int | Current active snapshot id for drilldown |
| `artifactTruth` | object | Existing truth badge and explanation payload |
| `freshness` | object | Existing freshness badge payload |
| `generatedAt` | string or null | Timestamp label |
| `missingDimensions` | int | Existing burden metric |
| `staleDimensions` | int | Existing burden metric |
| `nextStep` | string | Existing next-step text |
| `viewUrl` | string | Current tenant evidence drilldown URL |
**Invariants**
- Row drilldowns must stay workspace-safe and tenant-entitlement-safe.
- Derived-state memoization must remain effective.
- Render-time behavior must remain DB-only.
### CleanupAdmissionCandidate
**Type**: planning-only admission check
**Source**: implementation audit only when a possible extra hit is discovered
| Field | Type | Notes |
|------|------|-------|
| `surfaceKey` | string | Stable human-readable identifier |
| `path` | string | File or route path for the potential extra surface |
| `matchesProblemClass` | boolean | Must be true to qualify |
| `opensArchitectureQuestion` | boolean | Must be false to qualify |
| `decision` | string | `include` or `defer` |
| `reason` | string | Explicit justification for the decision |
## State Transition Rules
### Rule 1 - Deeplink seed to native active state
- Initial request query values may seed filter state on `TenantRequiredPermissions` and `EvidenceOverview`.
- After initial mount, active state belongs to the native page table or component, not to `request()`.
### Rule 2 - Route scope remains authoritative
- `TenantRequiredPermissions` may never replace its route tenant from query values.
- Inventory dependency state may never replace the current detail record or tenant context.
- Evidence overview may never reveal non-entitled tenant rows through a prefilter.
### Rule 3 - No new persistence or mirrored helper truth
- Filter state stays session-backed or Livewire-backed only where Filament already provides that behavior.
- No new database table, JSON helper artifact, or persisted UI-state mirror is introduced.
## Safety Rules
- No cleaned surface may introduce a second wrapper contract that simply restyles the current non-native behavior.
- No cleaned surface may widen current workspace or tenant scope behavior.
- No cleaned surface may lose current empty-state meaning, next-step clarity, or inspect destination correctness.
- No page or component may call Graph or other remote APIs during render as part of this cleanup.
## Planned Test Mapping
| Model / Rule | Existing Coverage | Planned Additions |
|---|---|---|
| `DependencyEdgesTableState` | `tests/Feature/InventoryItemDependenciesTest.php`, dependency tenant-isolation and query-service tests | native component test for direction and relationship interaction |
| `RequiredPermissionsTableState` | `tests/Feature/Rbac/TenantRequiredPermissionsTrustedStateTest.php`, unit filter normalization tests | page-level native table test |
| `RequiredPermissionsSummaryProjection` | current unit tests for freshness, overall state, feature impacts, and copy payloads | page-level summary consistency assertions |
| `EvidenceOverviewTableState` | `tests/Feature/Evidence/EvidenceOverviewPageTest.php` | native table assertions and any new table-standard guard alignment |
| `EvidenceOverviewRow` DB-only invariant | `tests/Feature/Filament/EvidenceOverviewDerivedStateMemoizationTest.php` | update assertions to reflect native table rendering without losing memoization guarantees |