# Feature Specification: Inventory Sync Button

**Feature Branch**: `046-inventory-sync-button`  
**Created**: 2026-01-09  
**Status**: Draft  
**Input**: User description: "Add an Inventory Sync button"

## User Scenarios & Testing *(mandatory)*

<!--
  IMPORTANT: User stories should be PRIORITIZED as user journeys ordered by importance.
  Each user story/journey must be INDEPENDENTLY TESTABLE - meaning if you implement just ONE of them,
  you should still have a viable MVP (Minimum Viable Product) that delivers value.
  
  Assign priorities (P1, P2, P3, etc.) to each story, where P1 is the most critical.
  Think of each story as a standalone slice of functionality that can be:
  - Developed independently
  - Tested independently
  - Deployed independently
  - Demonstrated to users independently
-->

### User Story 1 - Run Inventory Sync from UI (Priority: P1)

As an admin, I want a button in the admin UI to start an Inventory Sync for the currently selected tenant, so I can refresh inventory data without needing CLI.

**Why this priority**: Inventory is a core admin surface; the ability to refresh on-demand reduces troubleshooting time and improves trust in the data.

**Independent Test**: Can be fully tested by clicking a "Run Inventory Sync" button and verifying a new Sync Run record appears and progresses to a terminal state.

**Acceptance Scenarios**:

1. **Given** I am viewing Inventory for a tenant, **When** I click "Run Inventory Sync", **Then** a new Inventory Sync Run is created for that tenant and work is started asynchronously.
2. **Given** a sync run is started, **When** I view in-app notifications and the progress widget, **Then** I can see the sync progress and final outcome.
3. **Given** a sync run finishes (success/partial/failed), **When** I open the Inventory Sync Runs list, **Then** the run is visible with its terminal status and error summary (if any).
4. **Given** I am viewing Inventory for tenant A, **When** a request/payload/state attempts to start a sync for tenant B, **Then** the request is rejected (403/AccessDenied), I see “Not allowed”, and no run/job is created for tenant B.

---

### User Story 2 - Safe feedback when sync cannot start (Priority: P2)

As an admin, I want clear feedback when an Inventory Sync cannot be started (for example due to locks or concurrency), so I understand what to do next.

**Why this priority**: Inventory sync runs are lock- and concurrency-gated; unclear failures lead to repeated clicks and confusion.

**Independent Test**: Can be tested by attempting to start a sync while another sync is already running and verifying the UI shows a clear message and does not create duplicate runs.

**Acceptance Scenarios**:

1. **Given** a sync for the same tenant/selection is already running, **When** I click "Run Inventory Sync", **Then** the system does not start a second overlapping run and provides a clear explanation.

---

### User Story 3 - Permissioned access (Priority: P3)

As a tenant-scoped admin, I want only authorized users to be able to start Inventory Sync runs, so tenant data and Graph access are not triggered by unauthorized users.

**Why this priority**: Starting a sync triggers tenant-scoped Graph reads and creates operational records.

**Independent Test**: Can be tested by using a user without the required permission and verifying the action is not available or is denied.

**Acceptance Scenarios**:

1. **Given** I do not have permission to run inventory sync, **When** I view the Inventory area, **Then** I cannot trigger a sync and I receive an access denied message if I attempt it.

---

[Add more user stories as needed, each with an assigned priority]

### Edge Cases

- If a sync is already `running` for the same tenant + selection, the UI MUST not dispatch another run and MUST show an informational message.
- If tenant credentials are missing/invalid, the run MUST end in a terminal failure state and the initiating user MUST be notified.
- If the user clicks multiple times quickly, only one run MUST be dispatched; subsequent attempts MUST be blocked with a clear message.
- If the UI is closed after dispatch, the run MUST still finish and remain observable later via runs list + notifications.
- If queue workers are down, the run MUST remain observable as “queued/pending” and the UI MUST not pretend completion.
- If a request/payload/state attempts cross-tenant initiation, the system MUST reject the attempt server-side (403/AccessDenied) and MUST NOT create any run/job for another tenant.

## Requirements *(mandatory)*

**Constitution alignment (required):** If this feature introduces any Microsoft Graph calls or any write/change behavior,
the spec MUST describe contract registry updates, safety gates (preview/confirmation/audit), tenant isolation, and tests.

<!--
  ACTION REQUIRED: The content in this section represents placeholders.
  Fill them out with the right functional requirements.
-->

### Functional Requirements

- **FR-001**: System MUST provide an admin UI action to start an Inventory Sync for the currently selected tenant.
- **FR-002**: System MUST run Inventory Sync exclusively in the context of the current tenant (`Tenant::current()`). If any request/payload/state attempts to target a different tenant (e.g. provides `tenant_id` that does not match `Tenant::current()->id`), the system MUST reject the attempt server-side with 403/AccessDenied, MUST show a user-visible “Not allowed” message, and MUST NOT create any `InventorySyncRun`, `BulkOperationRun`, or queued job for the foreign tenant.
- **FR-003**: System MUST create an observable Inventory Sync Run record when a sync is started.
- **FR-004**: System MUST start the Inventory Sync asynchronously (queued/background execution), not inline in the request.
- **FR-005**: System MUST enforce existing concurrency/lock rules and MUST NOT start overlapping runs for the same tenant/selection.
- **FR-006**: System MUST show a clear success message when a run is started and a clear informational message when a run cannot be started due to locks/concurrency.
- **FR-007**: System MUST enforce authorization for starting sync runs.
- **FR-008**: System MUST record who initiated the sync run (user identity) on the run record AND in the audit trail for auditability.
- **FR-009**: System MUST provide in-app visibility into sync progress and outcome via a DB-backed notifications panel.
- **FR-010**: System MUST provide in-app visibility into sync progress and outcome via a bottom-right progress widget, consistent with the Policy Sync experience.

### Assumptions

- The Inventory Sync capability already exists and can be triggered by the application.
- An Inventory Sync Runs list/detail view already exists (or will exist) where users can observe runs.

### Dependencies

- Tenant context switching is available so the action always targets the current tenant.
- Existing concurrency/lock rules are enforced consistently for all sync starts (UI-triggered or otherwise).
- The application has an existing mechanism for DB-backed in-app notifications and a progress widget (used by Policy Sync).

*Example of marking unclear requirements:*

<!-- no additional clarifications -->

### Key Entities *(include if feature involves data)*

- **Inventory Sync Run**: A record representing a single inventory sync execution for a specific tenant and selection; includes status, timestamps, and error summary.
- **Tenant**: The tenant context the user is currently operating on; determines credentials/scope for inventory sync.
- **User (Initiator)**: The authenticated user who triggered the sync run; used for auditability and access control.

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: An authorized admin can start an Inventory Sync from the UI in under 10 seconds (time to find + click action).
- **SC-002**: In a normal tenant, 95% of sync start attempts either create a run or provide a clear lock/concurrency explanation within 2 seconds.
- **SC-003**: In usability testing, at least 90% of admins can successfully start a sync and locate the resulting run and its progress without guidance.
- **SC-004**: Support/debug time for "inventory looks stale" issues is reduced (measured as fewer repeated sync/troubleshooting requests), after rollout.
