TenantAtlas/specs/233-stale-run-visibility/plan.md
ahmido 6fdd45fb02
Some checks failed
Main Confidence / confidence (push) Failing after 53s
feat: surface stale active operation runs (#269)
## Summary
- keep stale active operation runs visible in the tenant progress overlay and polling state
- align tenant and canonical operation surfaces around the shared stale-active presentation contract
- add Spec 233 artifacts and clean the promoted-candidate backlog entries

## Validation
- browser smoke: `/admin/t/18000000-0000-4000-8000-000000000180` -> stale dashboard CTA -> `/admin/operations?tenant_id=7&activeTab=active_stale_attention&problemClass=active_stale_attention` -> `/admin/operations/15`
- verified healthy vs likely-stale tenant cards, canonical stale list row, and canonical run detail consistency

## Notes
- local smoke fixture seeded with one fresh and one stale running `baseline_compare` operation for browser validation
- Pest suite was not re-run in this session before opening this PR

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #269
2026-04-23 15:10:06 +00:00

23 KiB

Implementation Plan: Operation Run Active-State Visibility & Stale Escalation

Branch: 233-stale-run-visibility | Date: 2026-04-23 | Spec: spec.md Input: Feature specification from /specs/233-stale-run-visibility/spec.md

Summary

Complete one shared active-state presentation contract on top of the already-existing OperationRun lifecycle freshness truth by converging tenant dashboard activity signals, tenant-local active-run progress cards, workspace recent-operations summaries, the canonical operations list, and canonical run detail on the same fresh-versus-past-expected-versus-likely-stale semantics without introducing new persisted run state, new status values, or page-local heuristics.

Technical Context

Language/Version: PHP 8.4.15, Laravel 12, Filament v5, Livewire v4
Primary Dependencies: Filament widgets/resources/pages, Pest v4, App\Models\OperationRun, App\Support\Operations\OperationRunFreshnessState, App\Services\Operations\OperationLifecycleReconciler, App\Support\OpsUx\OperationUxPresenter, App\Support\OpsUx\ActiveRuns, App\Support\Badges\BadgeCatalog / BadgeRenderer, App\Support\Workspaces\WorkspaceOverviewBuilder, App\Support\OperationRunLinks
Storage: Existing PostgreSQL operation_runs records and current session/query-backed monitoring navigation state; no new persistence
Testing: Focused Pest feature tests over tenant dashboard widgets, tenant active-run progress surfaces, workspace overview operations summaries, canonical operations list/detail pages, and stale-reconciliation semantics
Validation Lanes: fast-feedback, confidence
Target Platform: Laravel admin web application in Sail containers with workspace routes under /admin and tenant routes under /admin/t/{tenant}
Project Type: Monorepo with one Laravel runtime in apps/platform and spec artifacts at repository root
Performance Goals: Preserve existing query shape and request-local presenter work; no additional remote calls, no background-process changes, and no new persisted summary projections
Constraints: No new OperationRun.status or OperationRun.outcome values, no retry/cancel/reconcile-now UX, no new notification channel, no page-local stale heuristics, no cross-tenant leakage, and no second presentation framework beyond the existing badge/presenter path
Scale/Scope: Existing tenant dashboard and tenant resource widgets, one Livewire active-progress slice, workspace overview builders/widgets, canonical operations list/detail pages, and their focused feature-test families

Filament v5 Implementation Contract

  • Livewire v4.0+ compliance: Preserved. The feature extends existing Filament v5 pages/widgets/resources and Livewire components without introducing legacy Livewire v3 patterns.
  • Provider registration location: Unchanged. Panel providers remain registered in bootstrap/providers.php, not bootstrap/app.php.
  • Global search coverage: OperationRunResource already keeps global search disabled via $isGloballySearchable = false, so this feature adds no new global-search exposure and does not depend on Edit/View global-search rules.
  • Destructive actions: No destructive actions are introduced. Existing monitoring/detail actions remain read-only, and this feature must not add retry, cancel, or force-fail controls.
  • Asset strategy: No new Filament assets are planned. Deployment expectations remain unchanged, including cd apps/platform && php artisan filament:assets only if a future implementation adds registered assets.
  • Testing plan: Prove semantics with focused feature tests for tenant dashboard activity, tenant progress summaries, workspace recent operations, canonical operations list/detail consistency, and stale-versus-fresh boundary cases. No browser or heavy-governance lane is required for this slice.

UI / Surface Guardrail Plan

  • Guardrail scope: Changed surfaces across tenant dashboard activity, tenant-local active-run progress, workspace recent operations summaries, canonical monitoring list rows, and canonical run detail summary
  • Native vs custom classification summary: Mixed shared-family change using native Filament widgets/resources/pages plus one existing Livewire progress component
  • Shared-family relevance: Status messaging, dashboard signals/cards, monitoring list presentation, canonical drill-through, and run-detail summary semantics
  • State layers in scope: page, detail, and one request-scoped/Livewire compact-progress slice; no new URL-state layer beyond existing monitoring continuity
  • Handling modes by drift class or surface: Review-mandatory because meaning must stay aligned across multiple existing surfaces and one existing hidden gap (healthyActive()-only progress) must be closed without widening scope
  • Repository-signal treatment: Review-mandatory; the feature changes operator-visible semantics but does not need a hard-stop repo guard
  • Special surface test profiles: standard-native-filament, monitoring-state-page, shared-detail-family
  • Required tests or manual smoke: functional-core, state-contract
  • Exception path and spread control: None planned. All covered surfaces should consume existing freshness truth and shared presenter/badge paths rather than diverging locally.
  • Active feature PR close-out entry: Guardrail

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes
  • Systems touched: OperationRunFreshnessState, OperationLifecycleReconciler, OperationRun problem-class helpers, OperationUxPresenter, centralized badge rendering, BulkOperationProgress, RecentOperationsSummary, RecentOperations, DashboardKpis, NeedsAttention, WorkspaceOverviewBuilder, WorkspaceRecentOperations, OperationRunResource, and TenantlessOperationRunViewer
  • Shared abstractions reused: OperationRun::freshnessState(), OperationRun::problemClass(), OperationRunFreshnessState, OperationUxPresenter::decisionZoneTruth(), OperationUxPresenter::lifecycleAttentionSummary(), OperationUxPresenter::surfaceGuidance(), ActiveRuns, BadgeCatalog / BadgeRenderer, OperationRunLinks, and existing workspace/tenant authorization helpers
  • New abstraction introduced? why?: One bounded derived active-state presentation contract is intentionally made explicit through the existing presenter and active-run helpers so compact and canonical surfaces can stay aligned without introducing a standalone semantic framework.
  • Why the existing abstraction was sufficient or insufficient: Existing lifecycle truth is sufficient and authoritative. Existing compact-surface adoption is insufficient because some slices already honor freshness (OperationRunResource, RecentOperations, WorkspaceOverviewBuilder) while others still filter to healthyActive() or under-communicate stale active work.
  • Bounded deviation / spread control: Same meaning, different density only. Surface-specific copy may vary by density, but all covered surfaces must consume the same freshness/problem-class truth and must not invent local stale logic.

Constitution Check

GATE: Passed before Phase 0 research. Re-checked after Phase 1 design: still passed with one bounded derived presentation contract and no new persisted truth.

Gate Status Plan Notes
Inventory-first / read-write separation PASS The feature is read-only presentation hardening over existing operation_runs; no restore, preview, or write-path change is introduced.
RBAC, workspace isolation, tenant isolation PASS Existing tenant-scoped widgets and canonical workspace monitoring routes remain on current entitlement checks; no new visibility surface is added.
Run observability / Ops-UX lifecycle PASS OperationRunService, lifecycle reconciliation, queued/running/terminal notifications, and run ownership remain unchanged; the plan only changes interpretation and visibility of existing run truth.
Shared pattern first PASS The plan explicitly reuses existing freshness, problem-class, presenter, and badge paths rather than adding a second semantic layer or local mapping family.
Proportionality / no premature abstraction PASS The narrowest credible change is one derived presentation contract over current truth plus convergence of existing surfaces. No new persistence, registry, or workflow framework is planned.
Badge semantics / Filament-native discipline PASS Status-like emphasis stays on centralized badge rendering and existing Filament widgets/resources/pages; no ad-hoc surface-local color system is introduced.
Decision-first / operator surfaces PASS The operations list remains the primary triage surface, tenant widgets stay secondary context, and canonical run detail stays diagnostic-first.
Test governance PASS Proof stays in focused feature lanes and existing surface families, with no browser-lane promotion and no heavy shared test infrastructure growth.

Test Governance Check

  • Test purpose / classification by changed surface: Feature for tenant dashboard activity, tenant progress surfaces, workspace recent operations summaries, canonical operations list/detail consistency, and stale boundary semantics
  • Affected validation lanes: fast-feedback, confidence
  • Why this lane mix is the narrowest sufficient proof: The business truth is cross-surface semantic consistency over existing OperationRun freshness state. That is fully provable with focused feature tests against the touched widgets/pages and existing reconciliation truth; browser coverage would add cost without validating additional domain behavior.
  • Narrowest proving command(s):
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php tests/Feature/OpsUx/ProgressWidgetFiltersTest.php tests/Feature/OpsUx/ProgressWidgetOverflowTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/RecentOperationsSummaryWidgetTest.php tests/Feature/Filament/DashboardKpisWidgetTest.php tests/Feature/Filament/NeedsAttentionWidgetTest.php tests/Feature/Filament/WorkspaceOverviewOperationsTest.php tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php tests/Feature/Monitoring/MonitoringOperationsTest.php tests/Feature/Monitoring/OperationsDashboardDrillthroughTest.php tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/RunAuthorizationTenantIsolationTest.php tests/Feature/OpsUx/NonLeakageWorkspaceOperationsTest.php
  • Fixture / helper / factory / seed / context cost risks: Moderate. Tests need representative fresh queued/running runs, likely stale runs, reconciled terminal runs, tenant membership context, workspace overview payloads, and hidden-tenant/non-member isolation boundaries, but existing factories and operation-run helpers already cover most of that setup.
  • Expensive defaults or shared helper growth introduced?: No. Existing OperationRun factories and workspace/tenant test helpers should stay opt-in and sufficient.
  • Heavy-family additions, promotions, or visibility changes: none
  • Surface-class relief / special coverage rule: Standard native-Filament relief plus the existing monitoring-state-page proving profile for canonical monitoring pages and summaries
  • Closing validation and reviewer handoff: Re-run pint, then the focused feature command above. Reviewers should verify that stale active work is visible on every covered compact surface, healthy active work is not falsely escalated, and drill-through into canonical detail preserves the same active-state meaning.
  • Budget / baseline / trend follow-up: none
  • Review-stop questions: Did any surface invent its own stale threshold? Did healthyActive() filtering remain in a surface that should show stale-active attention? Did any test rely on status strings alone instead of freshness truth? Did any change accidentally widen visibility beyond entitled tenant/workspace scope?
  • Escalation path: document-in-feature
  • Active feature PR close-out entry: Guardrail
  • Why no dedicated follow-up spec is needed: This is bounded current-release convergence of an existing truth family. A separate follow-up spec is only needed if later work tries to add intervention actions or a broader operations workbench.

Project Structure

Documentation (this feature)

specs/233-stale-run-visibility/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   └── operation-run-active-state-visibility.logical.openapi.yaml
└── tasks.md

Source Code (repository root)

apps/platform/
├── app/
│   ├── Filament/
│   │   ├── Pages/
│   │   │   └── Operations/TenantlessOperationRunViewer.php
│   │   ├── Resources/
│   │   │   └── OperationRunResource.php
│   │   └── Widgets/
│   │       ├── Dashboard/
│   │       │   ├── DashboardKpis.php
│   │       │   ├── NeedsAttention.php
│   │       │   └── RecentOperations.php
│   │       ├── Tenant/
│   │       │   └── RecentOperationsSummary.php
│   │       └── Workspace/
│   │           └── WorkspaceRecentOperations.php
│   ├── Livewire/
│   │   └── BulkOperationProgress.php
│   ├── Models/
│   │   └── OperationRun.php
│   ├── Services/Operations/
│   │   └── OperationLifecycleReconciler.php
│   └── Support/
│       ├── Badges/Domains/OperationRunStatusBadge.php
│       ├── OperationRunLinks.php
│       ├── OpsUx/
│       │   ├── ActiveRuns.php
│       │   └── OperationUxPresenter.php
│       ├── Operations/OperationRunFreshnessState.php
│       └── Workspaces/WorkspaceOverviewBuilder.php
├── resources/views/
│   ├── filament/widgets/
│   │   ├── tenant/recent-operations-summary.blade.php
│   │   └── workspace/workspace-recent-operations.blade.php
│   └── livewire/
│       ├── bulk-operation-progress.blade.php
│       └── bulk-operation-progress-wrapper.blade.php
└── tests/
  └── Feature/
    ├── Filament/
    │   ├── DashboardKpisWidgetTest.php
    │   ├── NeedsAttentionWidgetTest.php
    │   ├── OperationRunEnterpriseDetailPageTest.php
    │   ├── RecentOperationsSummaryWidgetTest.php
    │   └── WorkspaceOverviewOperationsTest.php
    ├── Monitoring/
    │   ├── MonitoringOperationsTest.php
    │   ├── OperationLifecycleFreshnessPresentationTest.php
    │   └── OperationsDashboardDrillthroughTest.php
    └── Operations/
      └── TenantlessOperationRunViewerTest.php
    ├── OpsUx/
    │   ├── BulkOperationProgressDbOnlyTest.php
    │   ├── NonLeakageWorkspaceOperationsTest.php
    │   ├── ProgressWidgetFiltersTest.php
    │   └── ProgressWidgetOverflowTest.php
    └── RunAuthorizationTenantIsolationTest.php

Structure Decision: Single Laravel application inside apps/platform. Runtime work stays in existing monitoring widgets/resources/pages and one existing Livewire progress slice; planning artifacts stay under specs/233-stale-run-visibility.

Complexity Tracking

No constitutional violation is planned. One bounded derived presentation contract is intentionally tracked because the spec introduces a small new semantic family over existing truth.

Violation Why Needed Simpler Alternative Rejected Because
BLOAT-001 derived category family Compact surfaces currently disagree in practice about whether stale active work is still ordinary progress. One small derived contract keeps surface meaning aligned without changing persisted run state. Leaving each widget to infer meaning from raw status or local heuristics would preserve drift and make future regressions likely.

Proportionality Review

  • Current operator problem: Compact operator surfaces can still hide or understate that active work is already past its expected lifecycle, so operators get false reassurance until they drill into monitoring detail.
  • Existing structure is insufficient because: Existing freshness truth and presenter helpers already exist, but they are not applied consistently across tenant progress and summary surfaces, and one slice (BulkOperationProgress) still intentionally filters stale active work out.
  • Narrowest correct implementation: Reuse current freshness/problem-class truth, introduce at most one small derived active-state presentation contract, and retrofit only the existing tenant/workspace/canonical monitoring surfaces that already summarize active work.
  • Ownership cost created: Small ongoing maintenance of one derived category family, shared copy alignment, and focused regression tests across covered surfaces.
  • Alternative intentionally rejected: Adding new persisted OperationRun statuses or separate page-local stale heuristics. Both would widen lifecycle scope or create contradictory truth.
  • Release truth: Current-release truth and operator-trust hardening.

Phase 0 Research Summary

  • Existing lifecycle and freshness truth already live in OperationRunFreshnessState, OperationRun::problemClass(), and OperationLifecycleReconciler; the feature should consume them rather than create new thresholds.
  • Canonical monitoring surfaces already partially honor stale-active semantics: OperationRunResource, Dashboard\RecentOperations, and WorkspaceOverviewBuilder all feed badge/presenter state with freshness_state or lifecycle summaries.
  • The clearest gap was tenant-local active-progress visibility: BulkOperationProgress scoped to healthyActive(), which hid stale active work from a high-frequency tenant surface and created exactly the cross-surface contradiction the spec describes.
  • OperationUxPresenter::surfaceGuidance() already differentiates likely stale, reconciled failed, and ordinary queued/running work, so Phase 1 should extend adoption before inventing new presentation machinery.
  • Existing focused tests already cover parts of the semantics (OperationLifecycleFreshnessPresentationTest, MonitoringOperationsTest, RecentOperationsSummaryWidgetTest, WorkspaceOverviewOperationsTest, OperationRunEnterpriseDetailPageTest, TenantlessOperationRunViewerTest), so implementation should prefer extending those families over introducing new broad suites.

Phase 1 Design Summary

  • data-model.md defines the derived active-state presentation model over existing OperationRun, freshness state, problem class, and covered surface consumers.
  • contracts/operation-run-active-state-visibility.logical.openapi.yaml documents the internal logical contract for how covered surfaces derive and display active-state meaning from existing run truth.
  • quickstart.md gives the narrow validation path for fresh-versus-stale fixtures, compact-surface rendering, canonical drill-through, and regression checks.

Implementation Strategy

  1. Converge on one freshness-to-surface contract
  • Reuse OperationUxPresenter::decisionZoneTruth(), lifecycleAttentionSummary(), current badge helpers, and ActiveRuns as the default convergence path.
  • Keep all thresholds and lifecycle windows owned by existing freshness truth.
  1. Fix the tenant-local active-progress blind spot
  • Update BulkOperationProgress so stale active runs are not silently excluded from tenant-local progress visibility.
  • Preserve calm presentation for healthy active work while allowing stale/late work to escalate visibly.
  1. Align tenant dashboard and tenant-summary surfaces
  • Reconcile DashboardKpis, NeedsAttention, RecentOperationsSummary, and any shared tenant activity slices so they expose the same active-state meaning and drill-through expectations.
  • Ensure mixed tenant activity does not over-generalize one stale run into “all activity is stale.”
  1. Keep workspace and canonical monitoring surfaces authoritative
  • Reuse existing freshness-aware row/detail rendering in OperationRunResource, TenantlessOperationRunViewer, and WorkspaceOverviewBuilder, tightening copy and top-level summary semantics only where necessary.
  • Preserve canonical list/detail roles and existing filter continuity from tenant context.
  1. Regression-protect fresh versus stale boundaries
  • Extend the existing monitoring and Filament feature tests to prove fresh active, likely stale, reconciled terminal, and terminal-transition cases across covered surfaces.
  • Explicitly assert that healthy queued/running runs do not inherit stale emphasis and that terminal runs disappear from active-only compact surfaces after refresh.

Risks and Mitigations

  • Surface drift survives in one slice: A compact surface may continue to rely on status only. Mitigation: inventory and update every covered surface in this plan, with tests tied to each family.
  • Over-escalation of healthy active work: Copy or badge reuse could make all queued/running work feel unhealthy. Mitigation: keep the proving fixtures split between fresh and stale runs and assert negative cases explicitly.
  • Tenant progress regression: Broadening BulkOperationProgress could accidentally turn a calm progress bar into a noisy problem board. Mitigation: keep one bounded active-state distinction and preserve existing density expectations.
  • New semantic layer grows too far: It would be easy to invent a broader taxonomy. Mitigation: constrain the plan to one derived presentation contract backed entirely by existing freshness/problem-class truth.

Implementation Close-Out

  • Finalized affected surfaces: Tenant active progress overlay and polling now include all active OperationRun records, including stale-active runs. Tenant summary, dashboard KPI/attention, workspace overview, canonical operations list, and canonical detail surfaces already consume shared freshness, badge, and presenter paths and were validated without widening the runtime change.
  • Density-specific copy retained: Compact surfaces use shared badge copy such as Likely stale plus OperationUxPresenter::surfaceGuidance() text about being past the lifecycle window. Canonical detail keeps the stronger Likely stale operation diagnostic banner.
  • Test-governance disposition: document-in-feature. Coverage stayed inside existing feature-test families and the focused fast-feedback / confidence lanes; no browser lane, heavy-governance family, shared fixture widening, or new test infrastructure was introduced.

Post-Design Re-check

Phase 0 and Phase 1 outputs keep the feature within existing OperationRun lifecycle truth, existing Filament/Livewire surfaces, and focused feature-test families. The plan remains constitution-compliant, Livewire v4 / Filament v5 compliant, and ready for /speckit.tasks.