TenantAtlas/specs/055-ops-ux-rollout/research.md
2026-01-18 14:44:16 +01:00

3.7 KiB
Raw 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).