# Phase 0 Research: Inventory Sync Button (046)

**Date**: 2026-01-09

## Findings

### Existing patterns to reuse

#### DB-backed notifications
- Filament DB notifications are already used in multiple places.
- Example: Policy Sync action calls `Filament\Notifications\Notification::make()->sendToDatabase(auth()->user())->send()`.

#### Bottom-right progress widget
- The bottom-right progress widget is implemented by `App\Livewire\BulkOperationProgress` and renders `resources/views/livewire/bulk-operation-progress.blade.php`.
- It polls `BulkOperationRun` filtered by `tenant_id = Tenant::current()->id` and `user_id = auth()->id()`.
- It is injected globally into Filament via a render hook in `App\Providers\Filament\AdminPanelProvider`.

#### Inventory Sync run records
- Inventory sync runs are already persisted in `inventory_sync_runs` with counts and status.
- Current `InventorySyncService::syncNow(...)` runs inline and uses locks/concurrency to create/update `InventorySyncRun`.

#### Authorization
- The app already uses tenant-role based authorization for sync operations (e.g. `User::canSyncTenant($tenant)` in `TenantResource`).

#### Inventory selection payload
- Inventory Sync requires a selection payload with shape: `{policy_types: list<string>, categories: list<string>, include_foundations: bool, include_dependencies: bool}`.
- There is no existing UI picker for inventory selection.

## Decisions

### Decision: Start Inventory Sync as a queued job
- **Chosen**: Dispatch an Inventory Sync job from the UI action.
- **Rationale**: Aligns with existing background operation UX and avoids blocking Livewire requests.
- **Alternatives considered**:
  - Run inline (current `syncNow`) — rejected due to UX (slow request) and mismatch with existing “progress widget” expectations.

### Decision: Use DB notifications + progress widget UX consistent with Policy/Bulk operations
- **Chosen**: Create a `BulkOperationRun` (resource `inventory`, action `sync`) so the existing bottom-right widget shows progress; also send DB notifications at start and completion/failure.
- **Rationale**: Matches established UX language and avoids inventing new UI surfaces.
- **Alternatives considered**:
  - Only show toast notifications — rejected; user explicitly requires DB notification panel + progress widget.

### Decision: Authorize via tenant role sync permission
- **Chosen**: Gate the UI action using `auth()->user()->canSyncTenant(Tenant::current())`.
- **Rationale**: Aligns with existing “sync” authorization patterns already used for tenant/policy operations.
- **Alternatives considered**:
  - Introduce new permission strings/roles — rejected for MVP; adds RBAC surface area.

### Decision: Default selection = “full inventory”
- **Chosen**: Dispatch inventory sync with policy types set to `PolicyTypeResolver::supportedPolicyTypes()`, empty categories, and `include_foundations=true`, `include_dependencies=true`.
- **Rationale**: Simplest interpretation of “Run Inventory Sync” without inventing a new picker UX.
- **Alternatives considered**:
  - Reuse backup policy picker UI — rejected; different domain (backup selection), more UX than requested.

### Decision: Attribute initiator on run record and audit trail
- **Chosen**: Store initiator identity on `InventorySyncRun` and also emit an audit record.
- **Rationale**: Improves traceability and aligns with constitution principle “Operations / Run Observability Standard”.
- **Alternatives considered**:
  - Audit log only — rejected (you chose C).

## Open Questions (for Phase 1 design)

- None remaining for planning; implementation will add a dedicated queued job.
