Added `ProviderResourceBinding` model, migrations, policies, and supporting framework for canonical resource identity mapping as defined in Spec 381. This provides the structural capability to resolve baseline and posture discrepancies by binding logical entities across source providers to canonical identities. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #452
348 lines
26 KiB
Markdown
348 lines
26 KiB
Markdown
# Implementation Plan: Spec 381 - Provider Resource Identity and Binding Foundation v1
|
|
|
|
**Branch**: `381-provider-resource-identity-binding` | **Date**: 2026-06-15 | **Spec**: `specs/381-provider-resource-identity-binding/spec.md`
|
|
**Input**: Feature specification from `specs/381-provider-resource-identity-binding/spec.md`
|
|
|
|
## Summary
|
|
|
|
Add the managed-environment-scoped provider resource identity and binding foundation needed before future baseline matching, resolution UI, and evidence/review integration. The plan introduces provider-neutral identity primitives, extends the existing `BaselineSubjectKey` path for canonical keys, persists auditable provider resource bindings, and proves isolation, uniqueness, RBAC, auditability, and no-op compare/evidence behavior. It does not add UI, Graph calls, OperationRun behavior, matching changes, or evidence/review readiness changes.
|
|
|
|
## Technical Context
|
|
|
|
**Language/Version**: PHP 8.4.15, Laravel 12.52.0
|
|
**Primary Dependencies**: Filament 5.2.1, Livewire 4.1.4, Pest 4.3.1, PostgreSQL, existing Laravel policies/Gates, existing `AuditLog`, existing baseline compare/resolution support
|
|
**Storage**: PostgreSQL table `provider_resource_bindings` with managed-environment scope and partial unique active-binding index
|
|
**Testing**: Pest 4 Unit, Feature, and PostgreSQL lane
|
|
**Validation Lanes**: fast-feedback, confidence, PostgreSQL; no browser lane because no UI surface changes
|
|
**Target Platform**: Laravel Sail locally; Dokploy-first Staging/Production containers
|
|
**Project Type**: Laravel monolith with Filament admin/system panels
|
|
**Performance Goals**: Active binding lookup must be indexed by workspace, managed environment, provider key, canonical subject key, and active status. No render-time provider calls are introduced.
|
|
**Constraints**: no UI, no Graph calls, no OperationRun creation, no matching pipeline rewrite, no evidence/review behavior change, no workspace/baseline-profile binding scopes in v1
|
|
**Scale/Scope**: One new scoped binding table, identity/descriptor support classes, one binding service, one policy/capability path, focused tests
|
|
|
|
## UI / Surface Guardrail Plan
|
|
|
|
- **Guardrail scope**: no operator-facing surface change.
|
|
- **Affected routes/pages/actions/states/navigation/panel/provider surfaces**: none in v1; existing baseline compare/evidence/review surfaces are regression targets only.
|
|
- **No-impact class, if applicable**: backend-only identity and persistence foundation.
|
|
- **Native vs custom classification summary**: N/A.
|
|
- **Shared-family relevance**: provider boundary vocabulary, baseline subject identity, RBAC, audit, and future evidence/review truth.
|
|
- **State layers in scope**: backend persistence and service state only; no shell/page/detail/URL-query state.
|
|
- **Audience modes in scope**: future operator/MSP and support/platform users are represented through audit and service semantics only. No customer-readable surface changes.
|
|
- **Decision/diagnostic/raw hierarchy plan**: N/A for UI. Audit metadata must remain safe and redacted.
|
|
- **Raw/support gating plan**: no raw provider payloads, secrets, tokens, signed URLs, or raw Graph data in binding records or audit metadata.
|
|
- **One-primary-action / duplicate-truth control**: `binding_status` is the single active/superseded/revoked truth. Do not add an `is_active` mirror.
|
|
- **Handling modes by drift class or surface**: hard-stop if implementation adds UI, matching behavior, workspace-level scope, Graph calls, or a new OperationRun type without updating the spec/plan first.
|
|
- **Repository-signal treatment**: review-mandatory for any separate capability family; follow-up-spec for workspace/baseline-profile scopes or automatic canonicalization.
|
|
- **Special surface test profiles**: N/A.
|
|
- **Required tests or manual smoke**: Unit, Feature, PostgreSQL, and targeted no-op regression tests.
|
|
- **Exception path and spread control**: none.
|
|
- **Active feature PR close-out entry**: Identity Binding Foundation / Provider Boundary.
|
|
- **UI/Productization coverage decision**: No UI surface impact.
|
|
- **Coverage artifacts to update**: none.
|
|
- **No-impact rationale**: The spec adds backend identity/persistence only and no route/page/action/navigation/presentation changes.
|
|
- **Navigation / Filament provider-panel handling**: N/A.
|
|
- **Screenshot or page-report need**: no.
|
|
|
|
## Shared Pattern & System Fit
|
|
|
|
- **Cross-cutting feature marker**: yes.
|
|
- **Systems touched**: baseline subject key support, provider resource descriptors, binding persistence, capability/policy enforcement, audit logging, PostgreSQL migration/index patterns.
|
|
- **Shared abstractions reused**: `App\Support\Baselines\BaselineSubjectKey`, `App\Support\Baselines\SubjectClass`, `App\Support\Baselines\ResolutionOutcome`, `App\Models\ProviderConnection`, `App\Models\ManagedEnvironment`, `App\Models\AuditLog`, `App\Support\Auth\Capabilities`.
|
|
- **New abstraction introduced? why?**: `ResourceIdentity` and `ProviderResourceDescriptor` are introduced because stable provider identity and descriptor serialization are not currently represented independently of compare runtime records. `ProviderResourceBindingService` is introduced because binding mutation requires scoped authorization, uniqueness, supersession, note validation, and audit in one transaction.
|
|
- **Why the existing abstraction was sufficient or insufficient**: Existing `BaselineSubjectKey` is sufficient to host canonical key generation. Existing compare/resolution classes are insufficient as durable binding truth because they are run/result semantics, not reusable operator decisions.
|
|
- **Bounded deviation / spread control**: New binding foundation must remain passive until Spec 382 or later explicitly consumes it.
|
|
|
|
## OperationRun UX Impact
|
|
|
|
- **Touches OperationRun start/completion/link UX?**: no.
|
|
- **Central contract reused**: N/A.
|
|
- **Delegated UX behaviors**: N/A.
|
|
- **Surface-owned behavior kept local**: N/A.
|
|
- **Queued DB-notification policy**: N/A.
|
|
- **Terminal notification path**: N/A.
|
|
- **Exception path**: none.
|
|
|
|
Binding records may reference a source `OperationRun`, but creating or revoking a binding is a short DB-only security-relevant action. It writes `AuditLog` and does not create a new run.
|
|
|
|
## Provider Boundary & Portability Fit
|
|
|
|
- **Shared provider/platform boundary touched?**: yes.
|
|
- **Provider-owned seams**: concrete provider resource IDs, external IDs, resource type names, discriminators, fingerprints, and future built-in canonicalization rules.
|
|
- **Platform-core seams**: `ResourceIdentity`, descriptor shape, canonical subject key generation, binding persistence, active uniqueness, audit/action semantics.
|
|
- **Neutral platform terms / contracts preserved**: provider, provider connection, managed environment, governed subject, canonical subject key, resource identity, descriptor, binding, resolution mode.
|
|
- **Retained provider-specific semantics and why**: provider fields are retained as opaque descriptors because exact provider identity must be represented. The core must not interpret Microsoft display labels.
|
|
- **Bounded extraction or follow-up path**: automatic Microsoft built-in mapping and matching behavior are follow-up specs.
|
|
|
|
## Constitution Check
|
|
|
|
- **Inventory-first, Snapshots-second**: PASS. Inventory remains last-observed provider state. Bindings are operator/provider identity decision truth, not a replacement for inventory or snapshots.
|
|
- **Read/Write Separation by Default**: PASS. No provider writes. Binding mutations affect TenantPilot only and require authorization plus audit.
|
|
- **Single Contract Path to Graph**: PASS. No Graph calls are introduced.
|
|
- **Deterministic Capabilities**: PASS with constraint. V1 reuses existing baseline capabilities unless spec/plan update authorizes dedicated capabilities.
|
|
- **Workspace isolation**: PASS. All rows include `workspace_id`, and services/policies must enforce workspace entitlement.
|
|
- **Tenant/Managed-environment isolation**: PASS. V1 rows include non-null `managed_environment_id`; no workspace-level binding scope is implemented.
|
|
- **RBAC-UX**: PASS. Non-member access is deny-as-not-found; member without capability is forbidden; policies/Gates enforce server-side.
|
|
- **Run observability**: PASS. No new long-running/remote/queued work. Security-relevant DB-only actions audit-log instead of creating an OperationRun.
|
|
- **Proportionality / No premature abstraction**: PASS as narrowed. New persistence is justified because bindings outlive runs and affect future governance truth. No provider adapter framework or UI is included.
|
|
- **Persisted truth**: PASS. Binding records have independent lifecycle and auditability.
|
|
- **Behavioral state**: PASS. `binding_status` affects active lookup and supersession/revocation; `resolution_mode` affects audit and future operator/matching behavior.
|
|
- **Provider boundary**: PASS with guardrails. Core stores provider identity but must not branch on Microsoft display names or Graph endpoint assumptions.
|
|
- **UI/Productization coverage**: PASS. `No UI surface impact` is recorded.
|
|
- **Test governance**: PASS. Unit/Feature/PostgreSQL lanes are named and narrow.
|
|
- **Release safety**: PASS. Migration is additive and reversible where practical. PostgreSQL partial unique behavior is test-covered before production promotion.
|
|
|
|
## Existing Repository Surfaces
|
|
|
|
Read-only context and likely implementation targets:
|
|
|
|
- `apps/platform/app/Support/Baselines/BaselineSubjectKey.php`
|
|
- `apps/platform/app/Support/Baselines/SubjectClass.php`
|
|
- `apps/platform/app/Support/Baselines/ResolutionOutcome.php`
|
|
- `apps/platform/app/Support/Baselines/Compare/CompareSubjectIdentity.php`
|
|
- `apps/platform/app/Support/Baselines/Compare/IntuneCompareStrategy.php`
|
|
- `apps/platform/app/Services/Baselines/BaselineCompareService.php`
|
|
- `apps/platform/app/Jobs/CompareBaselineToTenantJob.php`
|
|
- `apps/platform/app/Services/Baselines/BaselineCaptureService.php`
|
|
- `apps/platform/app/Jobs/CaptureBaselineSnapshotJob.php`
|
|
- `apps/platform/app/Models/ProviderConnection.php`
|
|
- `apps/platform/app/Models/ManagedEnvironment.php`
|
|
- `apps/platform/app/Models/InventoryItem.php`
|
|
- `apps/platform/app/Models/PolicyVersion.php`
|
|
- `apps/platform/app/Models/BaselineSnapshot.php`
|
|
- `apps/platform/app/Models/OperationRun.php`
|
|
- `apps/platform/app/Models/AuditLog.php`
|
|
- `apps/platform/app/Support/Auth/Capabilities.php`
|
|
- `apps/platform/app/Services/Auth/WorkspaceRoleCapabilityMap.php`
|
|
- `apps/platform/app/Services/Auth/RoleCapabilityMap.php`
|
|
- `apps/platform/database/migrations/2026_03_20_000000_create_environment_reviews_table.php`
|
|
- `apps/platform/database/migrations/2026_02_19_100003_create_baseline_snapshot_items_table.php`
|
|
- `apps/platform/database/migrations/2026_01_07_142720_create_inventory_items_table.php`
|
|
|
|
Expected write surfaces during later implementation:
|
|
|
|
- `apps/platform/database/migrations/<timestamp>_create_provider_resource_bindings_table.php`
|
|
- `apps/platform/app/Models/ProviderResourceBinding.php`
|
|
- `apps/platform/database/factories/ProviderResourceBindingFactory.php`
|
|
- `apps/platform/app/Policies/ProviderResourceBindingPolicy.php`
|
|
- `apps/platform/app/Support/Resources/ResourceIdentity.php`
|
|
- `apps/platform/app/Support/Resources/ProviderResourceDescriptor.php`
|
|
- `apps/platform/app/Support/Resources/ProviderResourceBindingStatus.php`
|
|
- `apps/platform/app/Support/Resources/ProviderResourceResolutionMode.php`
|
|
- `apps/platform/app/Services/Resources/ProviderResourceBindingService.php`
|
|
- `apps/platform/app/Support/Baselines/BaselineSubjectKey.php`
|
|
- `apps/platform/app/Support/Audit/AuditActionId.php`
|
|
- `apps/platform/tests/Unit/Support/Resources/ResourceIdentityTest.php`
|
|
- `apps/platform/tests/Unit/Support/Resources/ProviderResourceDescriptorTest.php`
|
|
- `apps/platform/tests/Unit/Support/Baselines/BaselineSubjectKeyCanonicalIdentityTest.php`
|
|
- `apps/platform/tests/Feature/ProviderResources/ProviderResourceBindingServiceTest.php`
|
|
- `apps/platform/tests/Feature/ProviderResources/ProviderResourceBindingAuthorizationTest.php`
|
|
- `apps/platform/tests/Feature/ProviderResources/ProviderResourceBindingPostgresTest.php`
|
|
- Targeted existing baseline/evidence/review tests listed in the validation section
|
|
|
|
No Filament resource/page/action, route, Livewire component, Blade view, job, Graph client, provider adapter, or customer output surface is planned.
|
|
|
|
## Technical Approach
|
|
|
|
1. Add deterministic provider-neutral identity and descriptor primitives.
|
|
2. Extend `BaselineSubjectKey` with canonical key methods that accept provider key, subject class, subject type key, resource type, and stable identity or canonical discriminator.
|
|
3. Add a managed-environment-scoped `provider_resource_bindings` table:
|
|
- `workspace_id` non-null foreign key
|
|
- `managed_environment_id` non-null foreign key
|
|
- composite foreign key from `(managed_environment_id, workspace_id)` to `managed_environments(id, workspace_id)`
|
|
- `provider_key`
|
|
- nullable `provider_connection_id`
|
|
- `subject_domain`, `subject_class`, `subject_type_key`
|
|
- nullable `legacy_subject_key`
|
|
- `canonical_subject_key`
|
|
- nullable provider resource descriptor columns
|
|
- `binding_status`
|
|
- `resolution_mode`
|
|
- nullable `resolution_reason`
|
|
- required `operator_note` for manual decisions
|
|
- nullable source references to `operation_runs`, `baseline_snapshots`, `inventory_items`, and `policy_versions`
|
|
- `decided_by_user_id`, `decided_at`, nullable `ended_at`
|
|
- timestamps
|
|
4. Use PostgreSQL partial unique index for active rows:
|
|
- `(workspace_id, managed_environment_id, provider_key, canonical_subject_key) WHERE binding_status = 'active'`
|
|
5. Add a model, factory, policy, and service:
|
|
- model uses `DerivesWorkspaceIdFromTenant` or an equivalent invariant to derive and protect `workspace_id`
|
|
- service methods create manual binding, create exclusion, create accepted limitation, mark unsupported, mark missing expected, supersede active binding, and revoke binding
|
|
- all mutations run in transactions
|
|
- all mutations enforce authorization, workspace/managed-environment scope, provider connection scope, source-reference scope, note requirements, active uniqueness, and audit
|
|
6. Add tests before or alongside implementation.
|
|
7. Add targeted regression proof that current compare/evidence/review behavior does not consume bindings yet.
|
|
|
|
## Domain / Model Implications
|
|
|
|
- `ProviderResourceBinding` is tenant-owned operational truth because it is bound to a managed environment and affects future tenant governance semantics.
|
|
- Binding scope is deliberately not a persisted axis in v1. All rows are managed-environment scoped.
|
|
- Provider defaults use `canonical_builtin` resolution mode in v1; do not introduce a separate default-specific resolution mode unless spec/plan are updated first.
|
|
- The model must reject or prevent workspace drift from the managed environment, matching the repo's tenant-owned model invariant.
|
|
- `binding_status` is the lifecycle truth:
|
|
- `active`
|
|
- `superseded`
|
|
- `revoked`
|
|
- `resolution_mode` is the decision meaning:
|
|
- `exact_provider_identity`
|
|
- `canonical_builtin`
|
|
- `canonical_virtual_target`
|
|
- `manual_binding`
|
|
- `excluded_non_governed`
|
|
- `accepted_limitation`
|
|
- `unsupported_coverage`
|
|
- `missing_expected`
|
|
- Do not add `is_active`, validity windows, workspace-level rows, baseline-profile rows, or subject-only rows in v1.
|
|
|
|
## UI / Filament Implications
|
|
|
|
- No Filament Resource, Page, RelationManager, action, table, form, navigation item, modal, drawer, wizard, or view is added or changed.
|
|
- Livewire v4.0+ compliance remains unchanged; installed Livewire is 4.1.4.
|
|
- Laravel 12 panel providers remain in `apps/platform/bootstrap/providers.php`.
|
|
- No globally searchable resource is added. `ProviderResourceBinding` must not be globally searchable because no Filament Resource/View/Edit page is introduced.
|
|
- No destructive Filament action is added. Service-level revocation and supersession are high-impact backend mutations and require server-side authorization plus audit; future UI confirmation belongs to Spec 384.
|
|
- No assets are registered. Normal deployment still runs `php artisan filament:assets` when the app deploys existing Filament assets, but Spec 381 adds none.
|
|
|
|
## OperationRun / Monitoring Implications
|
|
|
|
- No new OperationRun type or lifecycle transition.
|
|
- Binding mutations are DB-only and expected to complete under 2 seconds.
|
|
- Binding audit events must include safe source references where provided.
|
|
- Future matching specs may link compare OperationRuns to binding decisions, but v1 only stores optional source references.
|
|
|
|
## RBAC / Policy Implications
|
|
|
|
- Add a `ProviderResourceBindingPolicy` or an equivalent policy path consistent with existing model policy conventions.
|
|
- Reuse existing `workspace_baselines.view` for read authorization and `workspace_baselines.manage` for create/supersede/revoke decisions in v1.
|
|
- Do not introduce raw capability strings in feature code.
|
|
- Non-members or actors outside the workspace/managed environment receive 404 deny-as-not-found.
|
|
- Entitled members lacking baseline management capability receive 403 for mutations.
|
|
- Register the policy explicitly through the existing Laravel provider convention used by this app (`apps/platform/app/Providers/AuthServiceProvider.php` unless implementation finds a stronger adjacent precedent and updates this plan).
|
|
- Include positive and negative authorization tests.
|
|
|
|
## Audit / Evidence Implications
|
|
|
|
- Manual binding, exclusion, accepted limitation, unsupported coverage, missing expected, supersession, and revocation emit audit events.
|
|
- Audit metadata includes only safe identifiers:
|
|
- provider key
|
|
- canonical subject key
|
|
- subject class/type
|
|
- old/new binding IDs
|
|
- resolution mode
|
|
- source operation/snapshot/inventory/policy version IDs
|
|
- redacted, summarized, length-limited, or reference-only operator note/reason metadata
|
|
- Audit metadata must not include tokens, secrets, raw credential payloads, raw provider payloads, raw Graph response bodies, signed URLs, or customer-sensitive raw JSON.
|
|
- Raw operator notes must not be copied unchecked into audit metadata. The binding row may retain the required note; audit assertions must prove the audit payload remains safe.
|
|
|
|
## Data / Migration Implications
|
|
|
|
- Add one reversible migration for `provider_resource_bindings`.
|
|
- Add the repo-standard tenant-owned composite foreign key `(managed_environment_id, workspace_id)` -> `managed_environments(id, workspace_id)`.
|
|
- Add scoped foreign keys or service-enforced validation for `provider_connection_id` and source reference IDs so referenced records cannot point outside the binding workspace/managed environment. `provider_connection_id`, when present, must match `provider_key`.
|
|
- Use `jsonb` only if implementation needs a small descriptor payload; prefer explicit indexed columns listed above for lookup truth.
|
|
- Add indexes:
|
|
- `workspace_id`
|
|
- `managed_environment_id`
|
|
- `provider_key`
|
|
- `provider_connection_id`
|
|
- `canonical_subject_key`
|
|
- `binding_status`
|
|
- `resolution_mode`
|
|
- `source_operation_run_id`
|
|
- `source_baseline_snapshot_id`
|
|
- `source_inventory_item_id`
|
|
- `source_policy_version_id`
|
|
- Add partial unique active index in a PostgreSQL migration statement and drop it in `down()` before dropping the table if required by migration ordering.
|
|
- PostgreSQL lane is required because SQLite cannot prove partial unique index, composite foreign key, and tenant/workspace constraint behavior.
|
|
|
|
## Test Strategy
|
|
|
|
Required validation during implementation:
|
|
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Resources/ResourceIdentityTest.php tests/Unit/Support/Resources/ProviderResourceDescriptorTest.php tests/Unit/Support/Baselines/BaselineSubjectKeyCanonicalIdentityTest.php`
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderResources/ProviderResourceBindingServiceTest.php tests/Feature/ProviderResources/ProviderResourceBindingAuthorizationTest.php`
|
|
- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest -c phpunit.pgsql.xml tests/Feature/ProviderResources/ProviderResourceBindingPostgresTest.php`
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Baselines/BaselineCompareGapClassificationTest.php tests/Feature/Evidence/BaselineDriftPostureSourceTest.php tests/Feature/ReviewPack/Spec349ReviewPackResolutionGuidanceTest.php`
|
|
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
|
|
- `git diff --check`
|
|
|
|
If Sail is unavailable, use `./scripts/platform-sail ...` only if configured and record the blocker in the implementation close-out.
|
|
|
|
## Rollout Considerations
|
|
|
|
- Additive schema migration only.
|
|
- Staging must run the migration and PostgreSQL lane before Production.
|
|
- No env var, queue, scheduler, storage, or reverse-proxy changes are expected.
|
|
- No queue worker restart is specifically required beyond the normal deploy/reload process.
|
|
- Rollback is dropping the new table before any follow-up spec consumes it; after follow-up consumption exists, rollback must be redesigned.
|
|
- Since the product is pre-production, no historical backfill is required.
|
|
|
|
## Risk Controls
|
|
|
|
- Keep all binding records managed-environment scoped.
|
|
- Enforce workspace/environment alignment in the database and the model, not only in the service.
|
|
- Reject cross-scope provider connections and source references before persistence.
|
|
- Use partial unique index and service transaction to prevent duplicate active bindings.
|
|
- Require operator note for all governance-impacting decisions.
|
|
- Audit every mutation with redacted metadata and safe note handling.
|
|
- Add fake-provider tests to block Microsoft-only leakage through both identity primitives and service-level binding persistence.
|
|
- Add no-op regression tests so compare/evidence/review behavior remains unchanged.
|
|
- Stop and update spec/plan before adding UI, matching pipeline consumption, workspace-level scope, provider-specific built-in mappings, or evidence/review changes.
|
|
|
|
## Implementation Phases
|
|
|
|
### Phase 1 - Baseline And Guardrail Reconfirmation
|
|
|
|
Confirm repo state, completed-spec guardrails, current baseline/provider classes, capability patterns, audit patterns, and PostgreSQL migration conventions.
|
|
|
|
### Phase 2 - Identity And Canonical Key Foundation
|
|
|
|
Add identity/descriptor primitives and extend `BaselineSubjectKey` for canonical provider resource keys with unit coverage.
|
|
|
|
### Phase 3 - Binding Persistence And Integrity
|
|
|
|
Add migration, model, factory, enums, policy, service, PostgreSQL partial unique index, and tests.
|
|
|
|
### Phase 4 - Audit, RBAC, And Fake Provider Proof
|
|
|
|
Add service-level authorization, audit events, fake-provider fixtures/tests, and no-secret audit metadata assertions.
|
|
|
|
### Phase 5 - No-Op Runtime Regression
|
|
|
|
Run targeted compare, evidence, and review tests; ensure no current pipeline consumes bindings.
|
|
|
|
### Phase 6 - Final Validation And Hygiene
|
|
|
|
Run formatting, `git diff --check`, PostgreSQL lane, and final artifact close-out.
|
|
|
|
## Structure Decision
|
|
|
|
Use the existing Laravel monolith structure under `apps/platform`. Add small support classes under `app/Support/Resources`, extend the existing baseline support namespace only where current semantics already live, and keep all mutation behavior in a service under `app/Services/Resources`.
|
|
|
|
## Complexity Tracking
|
|
|
|
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
|
|---|---|---|
|
|
| New table/model `provider_resource_bindings` | Binding decisions must survive runs, be unique, be auditable, and affect future governance truth | Run-local JSON context or display-name normalization would not be durable, constrained, or independently auditable |
|
|
| New identity/descriptor classes | Provider resource identity must represent built-ins, virtuals, unsupported resources, and fake providers without display names | Extending compare result objects would couple persistent identity to one runtime pipeline |
|
|
| New status/mode enums | Active lookup, supersession, revocation, audit meaning, and future operator action differ by state/mode | String literals or presentation labels would spread state semantics through services/tests |
|
|
|
|
## Proportionality Review
|
|
|
|
- **Current operator problem**: Future operators need stable, auditable provider resource identity decisions instead of repeated ambiguity around mutable labels.
|
|
- **Existing structure is insufficient because**: Current subject/resolution support classifies run outcomes but does not persist binding decisions or provider resource identity truth.
|
|
- **Narrowest correct implementation**: Managed-environment-only binding table, reuse existing baseline capability and subject-key seams, no UI, no matching rewrite, no provider canonicalizer.
|
|
- **Ownership cost created**: Migration/index, model/policy/service, two support classes, two enum families, PostgreSQL tests, and provider-boundary review burden.
|
|
- **Alternative intentionally rejected**: Display-name normalization, run-context notes, and workspace-level/baseline-profile scope in v1.
|
|
- **Release truth**: Current-release foundation needed to unblock the immediately proposed follow-up specs without increasing current runtime behavior risk.
|
|
|
|
## Filament v5 Output Contract For Implementation Close-Out
|
|
|
|
- **Livewire v4.0+ compliance**: unchanged; Livewire 4.1.4 is installed and no Livewire component changes are planned.
|
|
- **Provider registration location**: unchanged; Laravel 12 panel providers remain in `apps/platform/bootstrap/providers.php`.
|
|
- **Global search**: no Filament Resource is added; `ProviderResourceBinding` is not globally searchable.
|
|
- **Destructive/high-impact actions**: no Filament action is added. Backend supersede/revoke decisions are high-impact and require policy authorization plus audit; future UI confirmation belongs to Spec 384.
|
|
- **Asset strategy**: no new assets; no Spec 381-specific `filament:assets` concern beyond normal deployment.
|
|
- **Testing plan**: Unit, Feature, PostgreSQL, and targeted no-op regression tests listed above.
|
|
- **Deployment impact**: additive migration only; no env vars, queues, scheduler, storage, or assets expected.
|