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

73 lines
3.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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).