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 repo’s 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.
