## 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
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, notbootstrap/app.php. - Global search coverage:
OperationRunResourcealready 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:assetsonly 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,OperationRunproblem-class helpers,OperationUxPresenter, centralized badge rendering,BulkOperationProgress,RecentOperationsSummary,RecentOperations,DashboardKpis,NeedsAttention,WorkspaceOverviewBuilder,WorkspaceRecentOperations,OperationRunResource, andTenantlessOperationRunViewer - 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 tohealthyActive()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:
Featurefor 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
OperationRunfreshness 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 agentexport 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.phpexport 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.phpexport 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
OperationRunfactories 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-pageproving 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
OperationRunstatuses 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(), andOperationLifecycleReconciler; the feature should consume them rather than create new thresholds. - Canonical monitoring surfaces already partially honor stale-active semantics:
OperationRunResource,Dashboard\RecentOperations, andWorkspaceOverviewBuilderall feed badge/presenter state withfreshness_stateor lifecycle summaries. - The clearest gap was tenant-local active-progress visibility:
BulkOperationProgressscoped tohealthyActive(), 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.mddefines the derived active-state presentation model over existingOperationRun, freshness state, problem class, and covered surface consumers.contracts/operation-run-active-state-visibility.logical.openapi.yamldocuments the internal logical contract for how covered surfaces derive and display active-state meaning from existing run truth.quickstart.mdgives the narrow validation path for fresh-versus-stale fixtures, compact-surface rendering, canonical drill-through, and regression checks.
Implementation Strategy
- Converge on one freshness-to-surface contract
- Reuse
OperationUxPresenter::decisionZoneTruth(),lifecycleAttentionSummary(), current badge helpers, andActiveRunsas the default convergence path. - Keep all thresholds and lifecycle windows owned by existing freshness truth.
- Fix the tenant-local active-progress blind spot
- Update
BulkOperationProgressso 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.
- 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.”
- Keep workspace and canonical monitoring surfaces authoritative
- Reuse existing freshness-aware row/detail rendering in
OperationRunResource,TenantlessOperationRunViewer, andWorkspaceOverviewBuilder, tightening copy and top-level summary semantics only where necessary. - Preserve canonical list/detail roles and existing filter continuity from tenant context.
- 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
statusonly. 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
BulkOperationProgresscould 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
OperationRunrecords, 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 staleplusOperationUxPresenter::surfaceGuidance()text about being past the lifecycle window. Canonical detail keeps the strongerLikely stale operationdiagnostic banner. - Test-governance disposition:
document-in-feature. Coverage stayed inside existing feature-test families and the focusedfast-feedback/confidencelanes; 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.