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

238 lines
23 KiB
Markdown

# Implementation Plan: Operation Run Active-State Visibility & Stale Escalation
**Branch**: `233-stale-run-visibility` | **Date**: 2026-04-23 | **Spec**: [spec.md](./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)
```text
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)
```text
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.
2. **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.
3. **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.”
4. **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.
5. **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`.