TenantAtlas/specs/237-provider-boundary-hardening/research.md
ahmido bd26e209de
Some checks failed
Main Confidence / confidence (push) Failing after 57s
feat: harden provider boundaries (#273)
## Summary
- add the provider boundary catalog, boundary support types, and guardrails for platform-core versus provider-owned seams
- harden provider gateway, identity resolution, operation registry, and start-gate behavior to require explicit provider bindings
- add unit and feature coverage for boundary classification, runtime preservation, unsupported paths, and platform-core leakage guards
- add the full Spec Kit artifact set for spec 237 and update roadmap/spec-candidate tracking

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Providers/ProviderBoundaryClassificationTest.php tests/Unit/Providers/ProviderBoundaryGuardrailTest.php tests/Feature/Providers/ProviderBoundaryHardeningTest.php tests/Feature/Providers/UnsupportedProviderBoundaryPathTest.php tests/Feature/Guards/ProviderBoundaryPlatformCoreGuardTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Providers/ProviderGatewayTest.php tests/Unit/Providers/ProviderIdentityResolverTest.php tests/Unit/Providers/ProviderOperationStartGateTest.php`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- browser smoke: `http://localhost/admin/provider-connections?tenant_id=18000000-0000-4000-8000-000000000180` loaded with the local smoke user, the empty-state CTA reached the canonical create route, and cancel returned to the scoped list

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #273
2026-04-24 21:05:37 +00:00

4.2 KiB

Research: Provider Boundary Hardening

Decision 1: Use a small config-backed seam catalog instead of a provider framework

  • Decision: Model first-slice provider-boundary ownership in one repository config catalog plus a small boundary helper layer, not as a speculative multi-provider framework.
  • Rationale: The current release needs explicit ownership and guardrails across multiple real seams more than it needs connector plugins, provider registries, or generic runtime extension points. A config-backed catalog is reviewable, deterministic, and easy to enforce in tests.
  • Boundary model note: The catalog keeps seam ownership binary as provider_owned or platform_core. Any retained Microsoft-first behavior is recorded as seam metadata with an explicit follow-up action, not as a third ownership state.
  • Alternatives considered:
    • Prose-only documentation and comments: rejected because reviewers cannot enforce it mechanically and the same drift can reappear on the next seam.
    • Full provider-plugin architecture: rejected because there is still only one shipped provider runtime.

Decision 2: Keep Graph request shaping inside provider-owned seams

  • Decision: Remove Graph request-option shaping from ProviderIdentityResolution and keep it inside provider-owned seams such as ProviderGateway and MicrosoftGraphOptionsResolver.
  • Rationale: A shared identity-resolution object currently knows Microsoft Graph request-option keys and request-id generation details. That is provider-owned behavior and should not live on a platform-core result type.
  • Alternatives considered:
    • Leave graphOptions() on ProviderIdentityResolution: rejected because it preserves Graph semantics in a shared runtime type.
    • Introduce a broad provider request-context framework: rejected because the narrower extraction into existing provider-owned seams is sufficient.

Decision 3: Split shared operation definition from provider binding

  • Decision: Keep platform-core operation metadata separate from provider binding metadata in ProviderOperationRegistry and the ProviderOperationStartGate path.
  • Rationale: Operation type, module, label, and capability are shared orchestration truth. The fact that the current runtime binds those operations to microsoft is provider-owned current-release behavior and should be explicit rather than silent default truth.
  • Alternatives considered:
    • Keep a single registry array with provider => microsoft on every entry: rejected because it makes the current first provider look like a permanent platform default.
    • Fold this work into operation-type canonicalization: rejected because this spec is about ownership boundaries, not renaming operation codes.

Decision 4: Treat target-scope and platform app identity details as bounded current-release exceptions

  • Decision: Keep entra_tenant_id, platform app credential config, and callback-specific details as explicit current-release exceptions in this slice instead of widening into schema and UI neutrality.
  • Rationale: These are real hotspots, but the next candidate Provider Identity & Target Scope Neutrality exists specifically to clean up the deeper persistence and operator-vocabulary consequences. This slice should harden the boundary before it rewrites identity truth.
  • Alternatives considered:
    • Rename storage and UI semantics now: rejected because it would widen the slice into a second spec.
    • Ignore the hotspot entirely: rejected because the plan needs one documented exception boundary rather than pretending the issue is solved.

Decision 5: Enforce the boundary with focused unit and feature guardrails

  • Decision: Prove the hardening with narrow unit and feature tests that exercise seam classification, provider-binding behavior, unsupported-path behavior, and Microsoft runtime preservation.
  • Rationale: The risk is architectural drift inside shared services, not browser behavior. Focused code-level tests are the narrowest proof that the boundary is explicit and enforceable.
  • Alternatives considered:
    • Browser or UI smoke coverage: rejected because the slice adds no new operator-facing surface.
    • Manual review only: rejected because the feature exists specifically to remove dependence on reviewer memory.