TenantAtlas/specs/061-provider-foundation/research.md
ahmido a0ed9e24c5 feat: unify provider connection actions and notifications (#73)
## Summary
- introduce the Provider Connection Filament resource (list/create/edit) with DB-only controls, grouped action dropdowns, and badge-driven status/health rendering
- wire up the provider foundation stack (migrations, models, policies, providers, operations, badges, and audits) plus the required spec docs/checklists
- standardize Inventory Sync notifications so the job no longer writes its own DB rows; terminal notifications now flow exclusively through OperationRunCompleted while the start surface still shows the queued toast

## Testing
- ./vendor/bin/sail php ./vendor/bin/pint --dirty
- ./vendor/bin/sail artisan test tests/Unit/Badges/ProviderConnectionBadgesTest.php
- ./vendor/bin/sail artisan test tests/Feature/ProviderConnections tests/Feature/Filament/ProviderConnectionsDbOnlyTest.php
- ./vendor/bin/sail artisan test tests/Feature/Inventory/RunInventorySyncJobTest.php tests/Feature/Inventory/InventorySyncStartSurfaceTest.php

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #73
2026-01-25 01:01:37 +00:00

92 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

y# Research: Provider Foundation v1 (Microsoft-first, Security-first)
**Branch**: `061-provider-foundation`
**Date**: 2026-01-24
**Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/spec.md`
**Plan**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/061-provider-foundation/plan.md`
## Decisions
### D-001 — Keep the Graph contract path as the only Microsoft Graph entry
**Decision**: All Microsoft Graph calls remain behind `GraphClientInterface` and are modeled in `config/graph_contracts.php`; provider modules/services must not introduce ad-hoc HTTP clients or hardcoded Graph paths.
**Rationale**: This preserves the repos constitution (“Single Contract Path to Graph”) and prevents endpoint sprawl. It also reuses the existing retry/backoff/logging behavior already centralized in the Graph client.
**Alternatives considered**:
- Build a new provider-specific HTTP gateway: rejected because it risks bypassing the existing Graph contract + logging path and reintroduces “two Graph clients”.
- Allow `GraphClientInterface::request()` with hardcoded paths in feature code: rejected because it undermines contract registry governance.
---
### D-002 — Canonical Microsoft target scope identifier
**Decision**: The canonical target scope identifier for Microsoft provider connections is the Entra tenant ID (GUID). Domains may be stored as display labels only.
**Rationale**: Tenant IDs are stable and unambiguous; domains can change and multiple domains can exist.
**Alternatives considered**:
- Domain as canonical identifier: rejected due to ambiguity and change risk.
- Allow either: rejected for v1 to avoid inconsistent locking/routing keys.
---
### D-003 — Multiple Microsoft connections per Suite Tenant with a required default
**Decision**: A Suite Tenant may have multiple Microsoft provider connections (distinct Entra tenant IDs). Exactly one connection is marked as the default; operations use the default unless a connection is explicitly selected.
**Rationale**: Supports real-world multi-tenant management while keeping day-to-day usage predictable.
**Alternatives considered**:
- Exactly one Microsoft connection per Suite Tenant: rejected as too restrictive for long-term roadmap (M365/security suite expansion).
---
### D-004 — OperationRun identity, dedupe, and “scope busy” behavior
**Decision**: Provider operations are tracked as `OperationRun`s with two concurrency rules:
1) Re-starting the same operation type for the same scope returns the active run (dedupe; no new run).
2) Starting a different operation type while any run is active for that scope is blocked (“scope busy”) and links to the active run.
**Rationale**: Prevents accidental overlap, avoids run spam, and keeps user expectations simple and consistent.
**Alternatives considered**:
- Queue the second operation to run later: rejected for v1 because it creates “silent delays” and unexpected ordering.
- Always reuse an active run even for a different operation: rejected because it would attach unrelated work to the wrong run.
---
### D-005 — Authorization aligns with existing tenant roles
**Decision**:
- Owner/Manager can manage provider connections and credentials.
- Owner/Manager/Operator can start provider operations (health check, inventory, compliance).
- Readonly is view-only.
**Rationale**: Credential/connection management is security-sensitive; operations are operational work that Operators need for daily workflow.
**Alternatives considered**:
- Restrict operations to Owner/Manager only: rejected because it blocks day-to-day operations.
---
### D-006 — Failure reason codes and redaction reuse existing run sanitization
**Decision**: Provider operation failures use stable reason codes and short sanitized messages via the existing run failure sanitization utilities; extend reason codes only when needed for clear operator feedback (e.g., authentication misconfiguration).
**Rationale**: Keeps error handling consistent suite-wide and prevents token/secret/PII leakage.
**Alternatives considered**:
- Persist raw Graph error payloads for debugging: rejected due to secret/PII leak risk and constitution requirements.
---
### D-007 — DB-only render is enforced by design and tests
**Decision**: Provider Connections and Monitoring/Operations surfaces must be DB-only at render/poll time; outbound provider calls occur only in queued jobs started via explicit user actions.
**Rationale**: Prevents accidental “poll storms” against Graph and preserves predictable UI performance.
**Alternatives considered**:
- Inline health checks during page load: rejected as a render-side effect and operationally unsafe.