TenantAtlas/specs/046-inventory-sync-button/plan.md
ahmido cf5b0027e3 046-inventory-sync-button (#47)
Zusammenfassung: Fügt im „Run Inventory Sync“-Modal einen include_dependencies-Toggle hinzu und persistiert die Auswahl in der InventorySyncRun.selection_payload. Tests, Quickstart und Tasks wurden entsprechend aktualisiert.

Files: InventoryLanding.php, InventorySyncButtonTest.php, quickstart.md, tasks.md
Motivation: Ermöglicht explizites Ein-/Ausschalten der Dependency-Extraktion pro Sync-Run (z. B. Assignments/Scope Tags/Foundations), statt starrer Defaults. Passt zur bestehenden selection_hash-Logik (InventorySelectionHasher) und zur deterministischen Selektionspersistenz.
Verhalten: include_dependencies ist im Modal standardmäßig true. Wird die Option gesetzt, landet der Wert als bool im selection_payload und beeinflusst selection_hash über die Normalisierung.
Tests: Neuer/angepasster Pest-Test stellt sicher, dass include_dependencies in selection_payload persistiert. Lokaler Testlauf:
./vendor/bin/sail artisan test tests/Feature/Inventory/InventorySyncButtonTest.php → alle Tests für diese Datei bestanden.
./vendor/bin/pint --dirty wurde ausgeführt (Formatting ok).
How to test (quick):
Start Sail + Queue:
Im Admin → Inventory: „Run Inventory Sync“ öffnen, Include dependencies umschalten, ausführen.
Prüfen: neu erstellter InventorySyncRun.selection_payload.include_dependencies ist der gesetzten Auswahl entsprechend. Oder laufen lassen:
Notes / Next steps:
Diese Änderung bereitet den Weg, später die Dependency-Extraction (042-inventory-dependencies-graph) optional tiefer zu integrieren.
Working tree ist sauber; es gibt ein nicht eingebundenes Verzeichnis 0800-future-features (unrelated).

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #47
2026-01-09 22:15:04 +00:00

6.3 KiB

Implementation Plan: Inventory Sync Button (046)

Branch: 046-inventory-sync-button | Date: 2026-01-08 | Spec: specs/046-inventory-sync-button/spec.md Input: Feature specification from specs/046-inventory-sync-button/spec.md

Summary

Add a “Run Inventory Sync” action in the Inventory area that dispatches a queued job, records a new InventorySyncRun attributed to the initiating user, and provides visibility via:

  • Filament database notifications
  • Existing bottom-right progress widget (powered by BulkOperationRun + BulkOperationProgress)

Technical Context

Language/Version: PHP 8.4.15
Primary Dependencies: Laravel 12, Filament v4, Livewire v3
Storage: PostgreSQL (JSONB used for run payload + error context)
Testing: Pest v4 (Laravel test runner)
Target Platform: Containerized (Laravel Sail locally), deployed via Dokploy
Project Type: Web application (Laravel + Filament admin panel)
Performance Goals: UI action should enqueue quickly (<2s perceived) and never block on Graph calls
Constraints: Tenant-isolated, idempotent start semantics, observable background execution, queue workers may be down
Scale/Scope: Single-tenant interactive action; one run per tenant/selection at a time (locks/concurrency)

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

  • Inventory-first: This feature refreshes Inventory (“last observed”) and does not touch snapshots/backups.
  • Read/write separation: No Graph writes. Only reads + local DB writes (run records + inventory items).
  • Graph contract path: Inventory sync already uses GraphClientInterface; this feature does not add new Graph endpoints.
  • Deterministic capabilities: Uses PolicyTypeResolver + config('tenantpilot.supported_policy_types') to derive default selection.
  • Tenant isolation: Uses Tenant::current() for all run creation and progress tracking.
  • Automation: Uses existing inventory locks/concurrency; runs are observable via InventorySyncRun and BulkOperationRun.
  • Data minimization: Inventory sync continues to store metadata + sanitized meta JSON; no secrets in notifications/logs.

Project Structure

Documentation (this feature)

specs/046-inventory-sync-button/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── internal-actions.md
└── tasks.md            # Phase 2 output (/speckit.tasks) - not created here

Source Code (repository root)

app/
├── Filament/
│   └── Pages/
│       └── InventoryLanding.php              # add header action “Run Inventory Sync”
├── Jobs/
│   └── RunInventorySyncJob.php               # new queued job
├── Models/
│   ├── InventorySyncRun.php                  # add user relationship (nullable)
│   └── BulkOperationRun.php                  # reused by progress widget
├── Services/
│   ├── BulkOperationService.php              # reused for progress + audit
│   ├── Intune/
│   │   └── AuditLogger.php                   # reused for inventory audit trail
│   └── Inventory/
│       └── InventorySyncService.php          # add entrypoint to run sync attributed to user

database/migrations/
└── xxxx_xx_xx_xxxxxx_add_user_id_to_inventory_sync_runs_table.php

resources/views/
└── filament/pages/inventory-landing.blade.php # may need to show action if not already in header

tests/Feature/
└── Inventory/InventorySyncButtonTest.php     # new Pest feature test

Structure Decision: Web application, implemented in Filament page actions + Laravel queued job.

Phase 0 Output (Research)

Completed in specs/046-inventory-sync-button/research.md.

Key decisions used in this plan:

  • Reuse existing bottom-right progress widget by creating a BulkOperationRun (resource=inventory, action=sync).
  • Authorize using existing tenant role capability: User::canSyncTenant(Tenant::current()).
  • Default selection = “full inventory” (all supported policy types, foundations + dependencies enabled).

Phase 1 Output (Design)

Completed in:

  • specs/046-inventory-sync-button/data-model.md
  • specs/046-inventory-sync-button/contracts/internal-actions.md

Phase 2 Plan (Implementation Outline)

1) Data model: store initiator on run record

  • Add nullable user_id foreign key to inventory_sync_runs.
  • Add InventorySyncRun::user() relationship.
  • Backfill is not required (existing rows can remain null).

2) Job orchestration: queued execution + progress tracking

  • Add RunInventorySyncJob that:
    • Loads the tenant + user + BulkOperationRun.
    • Marks the BulkOperationRun as running (BulkOperationService::start).
    • Runs inventory sync via InventorySyncService in a way that sets InventorySyncRun.user_id.
    • Records bulk progress as a single-item operation:
      • success: recordSuccess + complete
      • failure/skip: recordFailure/recordSkippedWithReason + complete (or fail for hard failures)
    • Sends Filament database notifications to the initiating user on completion/failure.

3) UI: Inventory “Run Inventory Sync” action

  • Add a header action on InventoryLanding:
    • Visible/authorized only if auth()->user() is a User and canSyncTenant(Tenant::current()).
    • On click:
      • Compute default selection payload.
      • Pre-flight check: if a matching InventorySyncRun is currently running (same tenant + selection hash), show an informational notification and do not dispatch.
      • Create a BulkOperationRun via BulkOperationService::createRun(...) with total_items=1.
      • Send a “started” Filament DB notification with the “check bottom right progress bar” copy.
      • Dispatch RunInventorySyncJob with identifiers.

4) Audit trail

  • Use AuditLogger to emit at minimum:
    • inventory.sync.dispatched
    • terminal event keyed by outcome: inventory.sync.completed|inventory.sync.failed|inventory.sync.skipped

5) Tests (Pest)

  • New feature test covering:
    • authorized user can dispatch a sync from the Inventory page and it creates a BulkOperationRun.
    • RunInventorySyncJob sets InventorySyncRun.user_id.
    • unauthorized user cannot see/execute the action.
    • concurrency case: when a run is already running, a second dispatch is prevented.