TenantAtlas/specs/055-ops-ux-rollout/research.md
ahmido bd6df1f343 055-ops-ux-rollout (#64)
Kurzbeschreibung

Implementiert Feature 055 — Ops‑UX Constitution Rollout v1.3.0.
Behebt: globales BulkOperationProgress-Widget benötigt keinen manuellen Refresh mehr; ETA/Elapsed aktualisieren korrekt; Widget verschwindet automatisch.
Verbesserungen: zuverlässiges polling (Alpine factory + Livewire fallback), sofortiger Enqueue‑Signal-Dispatch, Failure‑Message‑Sanitization, neue Guard‑ und Regressionstests, Specs/Tasks aktualisiert.
Was geändert wurde (Auszug)

InventoryLanding.php
bulk-operation-progress.blade.php
OperationUxPresenter.php
SyncRestoreRunToOperationRun.php
PolicyResource.php
PolicyVersionResource.php
RestoreRunResource.php
tests/Feature/OpsUx/* (PollerRegistration, TerminalNotificationFailureMessageTest, CanonicalViewRunLinksTest, OperationCatalogCoverageTest, UnknownOperationTypeLabelTest)
InventorySyncButtonTest.php
tasks.md
Tests

Neue Tests hinzugefügt; php artisan test --group=ops-ux lokal grün (alle relevanten Tests laufen).
How to verify manually

Auf Branch wechseln: 055-ops-ux-rollout
In Filament: Inventory → Sync (oder relevante Bulk‑Aktion) auslösen.
Beobachten: Progress‑Widget erscheint sofort, ETA/Elapsed aktualisiert, Widget verschwindet nach Fertigstellung ohne Browser‑Refresh.
Optional: ./vendor/bin/sail exec app php artisan test --filter=OpsUx oder php artisan test --group=ops-ux
Besonderheiten / Hinweise

Einzelne, synchrone Policy‑Actions (ignore/restore/PolicyVersion single archive/restore/forceDelete) sind absichtlich inline und erzeugen kein OperationRun. Bulk‑Aktionen und restore.execute werden als Runs modelliert. Wenn gewünscht, kann ich die inline‑Actions auf OperationRunService umstellen, damit sie in Monitoring → Operations sichtbar werden.
Remote: Branch ist bereits gepusht (origin/055-ops-ux-rollout). PR kann in Gitea erstellt werden.
Links

Specs & tasks: tasks.md
Monitoring page: Operations.php

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local>
Reviewed-on: #64
2026-01-18 14:50:15 +00:00

3.7 KiB
Raw Permalink Blame History

Phase 0 Research: Ops-UX Constitution Rollout (v1.3.0 Alignment) (055)

Date: 2026-01-18

Findings

Current operation-run storage and UX primitives

  • The repo has a tenant-scoped operation_runs table and an OperationRun model.
  • Structured “metrics” for this rollout are stored as operation_runs.summary_counts (JSONB).
  • Canonical Monitoring entrypoint exists via the Filament resource OperationRunResource.
  • Canonical route building already exists in App\Support\OperationRunLinks.

Existing progress widget patterns

  • A bottom-right Livewire widget exists today for bulk operations: App\Livewire\BulkOperationProgress.
  • It is injected globally via a Filament render hook in App\Providers\Filament\AdminPanelProvider.
  • Current behavior is not constitution-compliant:
    • It shows terminal states and renders terminal wording.
    • It renders percentages and per-run counts unconditionally.
    • It is user-scoped today (filters by user_id = auth()->id()).

Notifications

  • Filament database notifications are already used and are persistent by default.
  • Current code has OperationRun DB notifications (queued + completed), but this rollout requires:
    • no queued DB notifications
    • exactly one terminal notification per run
    • initiator-only audience

Decisions

Decision 1: Tenant-wide progress widget scope

  • Chosen: The progress widget shows all active runs for the current tenant (not only runs started by the current user), for users with access to Monitoring → Operations.
  • Rationale: Aligns with the constitutions tenant-scoped operations model and avoids “why dont I see whats running?” support loops.
  • Alternatives considered:
    • User-only widget: rejected (hides tenant work and defeats the monitoring intent).

Decision 2: Canonical metrics source

  • Chosen: Treat operation_runs.summary_counts as the canonical “metrics” field for this rollout.
  • Rationale: Matches existing schema; avoids adding a new metrics column during a UX migration.
  • Alternatives considered:
    • New operation_runs.metrics column: rejected (scope increase + migration risk).

Decision 3: Unknown operation types in UI

  • Chosen: Soft-fail at runtime with label Unknown operation, and fail-fast in CI for code-produced operation types.
  • Rationale: Keeps Monitoring usable for legacy/dirty data while enforcing discipline for new work.
  • Alternatives considered:
    • Throw exception at runtime: rejected (breaks Monitoring for historical data).
    • Render raw type string: rejected (leaks internal naming and encourages drift).

Decision 4: DB notification audience

  • Chosen: Initiator-only for terminal DB notifications.
  • Rationale: Prevents tenant-wide notification spam; Monitoring remains the tenant-wide audit surface.
  • Alternatives considered:
    • Tenant-wide fan-out: rejected (noisy; not necessary for monitoring).

Decision 5: No queued DB notifications

  • Chosen: Ban queued DB notifications repo-wide for OperationRuns.
  • Rationale: Simplifies dedupe; queued intent belongs to the toast surface.
  • Alternatives considered:
    • Allow queued DB notifications with dedupe: rejected (still noisy; adds edge cases).

Decision 6: Migration approach for progress widget

  • Chosen: Reuse the existing “global Livewire progress widget via render hook” pattern, but migrate it to query OperationRun and apply constitution rules.
  • Rationale: Low-risk way to ship a single widget surface across the app.
  • Alternatives considered:
    • Per-feature widgets/pages: rejected (violates the “three surfaces only” rule).

Open Questions

None (all clarifications captured in the feature spec).