TenantAtlas/specs/270-operationrun-progress-contract/plan.md
ahmido ca61fa17dc 270-operationrun-progress-contract: OperationRun progress contract (#325)
Automated PR created by Copilot agent: commits workspace changes, adds specs and tests.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #325
2026-05-04 16:36:50 +00:00

18 KiB

Implementation Plan: OperationRun Progress Contract v1

Branch: 270-operationrun-progress-contract | Date: 2026-05-04 | Spec: spec.md Input: Feature specification from /specs/270-operationrun-progress-contract/spec.md

Summary

This plan prepares one bounded Ops-UX foundation slice over existing OperationRun truth. The implementation path is to introduce one shared progress-semantics contract in the current App\Support\OpsUx family, move progress-mode decisions out of bulk-operation-progress.blade.php, and document the contract in docs/ui/tenantpilot-enterprise-ui-standards.md. The slice must stay on existing OperationRun.status, OperationRun.outcome, summary_counts, and context truth; it must not widen into counted writer rollout, dashboard redesign, terminal-notification changes, or new persistence.

Filament remains on Livewire v4, no panel-provider registration changes are required (apps/platform/bootstrap/providers.php remains authoritative), no globally searchable resource is added, and no asset registration or deployment step is expected.

Inherited Baseline / Explicit Delta

Inherited baseline

  • SummaryCountsNormalizer and OperationSummaryKeys already sanitize and whitelist numeric summary_counts values.
  • OperationRunService already owns summary_counts writes through updateRun(), incrementSummaryCounts(), and maybeCompleteBulkRun().
  • ActiveRuns already owns shell-visible run selection and terminal-success grace-window filtering.
  • BulkOperationProgress and bulk-operation-progress.blade.php already render the current shell host, but the progress semantics are still decided inline in the Blade view.
  • specs/268-operationrun-activity-feedback/ already owns the shell terminal-success and terminal-follow-up slice.
  • Historical Ops-UX specs already require numeric-only summary_counts and preserve the three-surface lifecycle contract.

Explicit delta in this plan

  • formalize one shared OperationRun progress capability and render-model contract
  • centralize counted vs activity-only vs terminal no-progress semantics in one Ops-UX helper
  • move current shell progress logic off inline Blade math and onto that shared contract
  • document future-safe boundaries for phased and composite progress without rolling them out yet
  • leave run-writer rollout, dashboard follow-up work, and phase/composite truth to later specs

Technical Context

Language/Version: PHP 8.4, Laravel 12, Filament v5, Livewire v4
Primary Dependencies: current Ops-UX support classes, native Filament widgets/Blade, Pest v4
Storage: PostgreSQL via existing operation_runs; no new persistence
Testing: Pest Unit + Feature coverage
Validation Lanes: fast-feedback, confidence
Target Platform: existing Laravel monolith in apps/platform, admin/operator plane only
Project Type: Web application (Laravel monolith with Filament)
Performance Goals: no new query families, no extra polling loops, and no slower-than-current shell rendering for active-run feedback
Constraints: no new summary_counts keys, no new run lifecycle, no new persistence, and no browser-only proof requirement in this slice
Scale/Scope: one shared contract, one shell adopter, one standards update, and focused regression coverage

Likely Affected Repo Surfaces

  • apps/platform/app/Support/OpsUx/SummaryCountsNormalizer.php
  • apps/platform/app/Support/OpsUx/OperationSummaryKeys.php
  • apps/platform/app/Support/OpsUx/ActiveRuns.php
  • apps/platform/app/Support/OpsUx/OperationStatusNormalizer.php
  • apps/platform/app/Support/OpsUx/OperationUxPresenter.php
  • one new bounded helper under apps/platform/app/Support/OpsUx/ for progress capability/render semantics
  • apps/platform/app/Livewire/BulkOperationProgress.php
  • apps/platform/resources/views/livewire/bulk-operation-progress.blade.php
  • apps/platform/app/Services/OperationRunService.php
  • apps/platform/tests/Unit/Support/OpsUx/...
  • apps/platform/tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php
  • apps/platform/tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php
  • apps/platform/tests/Feature/OpsUx/SummaryCountsWhitelistTest.php
  • docs/ui/tenantpilot-enterprise-ui-standards.md

UI / Filament & Livewire Fit

  • Keep the changed surface Filament-native. The visible v1 adopter remains the existing Livewire shell host rather than a new page, widget family, or dashboard card.
  • The shell host stays decision-first. The new contract decides only whether a progress line or bar is truthful; it does not widen the shell into a diagnostics surface.
  • Monitoring collection/detail pages remain diagnostics-first drill-through targets. This slice prepares their future compatibility by naming one shared contract, not by redesigning their UI.
  • The current shell host may keep bounded progress text or bars, but it must no longer calculate their eligibility or percentages inline.
  • No new asset registration, panel configuration, or provider registration change is planned.

RBAC / Policy Fit

  • Existing OperationRun policies remain the first and only visibility gate.
  • The progress contract derives output only after the current actor is already entitled to see the run.
  • Tenant/admin plane behavior stays unchanged: no cross-plane expansion and no new authorization surface.
  • No new mutation or retry action is introduced, so current confirmation/authorization behavior stays on existing start surfaces and run detail pages.

Audit / Logging Fit

  • Existing queued toasts and terminal DB notifications remain authoritative and unchanged.
  • Existing run audit and Monitoring behavior remain the only audit trail. No new view-level or contract-level audit stream is introduced.
  • OperationRun.status and OperationRun.outcome remain service-owned and unchanged.

Data & Query Fit

  • The contract derives only from current OperationRun truth: status, outcome, sanitized summary_counts, and bounded current context where trustworthy phase/composite truth may later exist.
  • Determinate progress remains limited to current running work with trustworthy numeric processed and total counters.
  • Outcome counters remain summary truth only; they are not reinterpreted as progress inputs.
  • No migration, no new JSON schema, no backfill, and no cache layer are planned.

UI / Surface Guardrail Plan

  • Guardrail scope: changed surfaces
  • Native vs custom classification summary: native Filament plus a bounded local Ops-UX helper refactor
  • Shared-family relevance: Ops UX start feedback and execution-truth summaries
  • State layers in scope: shell, page
  • Audience modes in scope: operator-MSP
  • Decision/diagnostic/raw hierarchy plan: decision-first on the shell host, diagnostics-first on Operations collection/detail
  • Raw/support gating plan: raw/support evidence stays on the current diagnostics surfaces only
  • One-primary-action / duplicate-truth control: the shell keeps the current dominant View operation action and only swaps local progress math for the shared contract; it does not add duplicate progress explanations
  • Handling modes by drift class or surface: review-mandatory
  • Repository-signal treatment: review-mandatory
  • Special surface test profiles: global-context-shell
  • Required tests: functional-core, state-contract
  • Exception path and spread control: none planned; any attempt to add new writer semantics, a new browser family, or dashboard-specific progress logic resolves as reject-or-split
  • Active feature PR close-out entry: Guardrail / Smoke Coverage

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes
  • Systems touched: current shell host, summary-count sanitization, progress disclosure semantics, and UI standards
  • Shared abstractions reused: SummaryCountsNormalizer, OperationSummaryKeys, ActiveRuns, OperationStatusNormalizer, OperationUxPresenter, current Ops-UX shell host
  • New abstraction introduced? why?: yes, one bounded shared progress contract/helper because the repo already has multiple real consumers and the current progress truth gap cannot stay view-local without drift
  • Why the existing abstraction was sufficient or insufficient: the repo already owns lifecycle normalization and count sanitization, but it does not currently answer progress eligibility or progress mode once and centrally
  • Bounded deviation / spread control: do not create multiple host-specific helpers, a registry, or a persisted progress model

OperationRun UX Impact

  • Touches OperationRun start/completion/link UX?: yes, for active-surface progress disclosure only
  • Central contract reused: current Ops-UX start contract via OperationRunLinks, OperationRunUrl, ActiveRuns, OperationStatusNormalizer, and OperationUxPresenter
  • Delegated UX behaviors: queued toast wording, canonical view/collection links, current browser-event dispatch, and existing terminal DB notifications remain delegated to the shared contract and unchanged
  • Surface-owned behavior kept local: bounded shell layout and copy density only
  • Queued DB-notification policy: N/A - unchanged
  • Terminal notification path: unchanged central lifecycle mechanism
  • Exception path: none

Provider Boundary & Portability Fit

  • Shared provider/platform boundary touched?: no
  • Provider-owned seams: N/A
  • Platform-core seams: existing OperationRun truth, summary-count vocabulary, and operator-facing execution language only
  • Neutral platform terms / contracts preserved: Operation, activity, progress, terminal outcome, counted progress
  • Retained provider-specific semantics and why: none
  • Bounded extraction or follow-up path: none

Constitution Check

GATE: Must pass before implementation begins and again before merge.

  • Inventory-first: PASS. The slice is fully derived from existing OperationRun truth.
  • Read/write separation: PASS. No new write path or retry surface is introduced.
  • Graph contract path: PASS. No Graph/provider interaction is added.
  • Deterministic capabilities: PASS. Existing OperationRun policies remain authoritative.
  • RBAC-UX: PASS. No plane expansion; tenant/admin visibility stays on current guards and deny-as-not-found semantics.
  • Run observability: PASS. Existing start contract, terminal notifications, and Monitoring ownership remain unchanged while progress semantics are centralized.
  • Ops-UX lifecycle: PASS. No change to service-owned status/outcome transitions or summary_counts ownership.
  • Data minimization: PASS. Hosts stay compact and do not surface raw evidence by default.
  • Test governance: PASS. Proof stays bounded to unit plus feature coverage.
  • Proportionality / no premature abstraction: PASS. The helper is justified by multiple real consumers and avoids wider rollout or persistence.
  • Persisted truth / behavioral state: PASS. No new table, cache, or progress-mode persistence.
  • Shared pattern first / UI semantics / Filament-native UI: PASS. Existing helpers stay central, and the current shell moves closer to the Ops-UX contract.
  • Provider boundary: PASS. No provider/platform seam changes.
  • Filament/Laravel panel safety: PASS. Filament v5 stays on Livewire v4, provider registration remains in apps/platform/bootstrap/providers.php, and no new assets are planned.

Gate evaluation: PASS.

Test Governance Check

  • Test purpose / classification by changed surface: Unit for progress-capability/render-model truth; Feature for current shell adoption and shell-visible progress output
  • Affected validation lanes: fast-feedback, confidence
  • Why this lane mix is the narrowest sufficient proof: one unit suite proves the shared contract itself, while focused shell feature tests prove the visible adopter no longer calculates progress locally. Browser proof remains owned by specs/268-operationrun-activity-feedback/ because this slice does not change layout or clickability.
  • Narrowest proving command(s):
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php tests/Feature/OpsUx/SummaryCountsWhitelistTest.php
  • Fixture / helper / factory / seed / context cost risks: low to moderate; reuse current OperationRun factories and tenant helpers instead of introducing new provider-heavy defaults
  • Expensive defaults or shared helper growth introduced?: no
  • Heavy-family additions, promotions, or visibility changes: none
  • Surface-class relief / special coverage rule: global-context-shell
  • Closing validation and reviewer handoff: reviewers should rerun the focused commands above, then confirm the shell uses one shared progress contract, queued runs stay indeterminate, terminal runs stay terminal, and outcome counters never create a percentage
  • Budget / baseline / trend follow-up: none expected beyond a small feature-local increase
  • Review-stop questions: did the helper stay bounded, did the shell lose its inline progress math, did any new summary_counts keys or writer semantics appear, and were 269, 271, 272, and 273 kept out of scope?
  • Escalation path: reject-or-split for any writer rollout, dashboard redesign, or persisted progress model
  • Active feature PR close-out entry: Guardrail / Smoke Coverage
  • Why no dedicated follow-up spec is needed: this package is itself the bounded contract layer. Later counted rollout and phase/composite rollout remain explicit follow-up specs rather than hidden growth here.

Project Structure

Documentation (this feature)

specs/270-operationrun-progress-contract/
├── spec.md
├── plan.md
├── tasks.md
└── checklists/
    └── requirements.md

This preparation package intentionally stays on the core artifacts plus the readiness checklist. The repo already contains the relevant Ops-UX truth, current shell host, and adjacent tests, so no extra research, data-model, or contract package is required for a bounded implementation handoff.

Source Code (expected implementation surfaces)

apps/platform/app/Support/OpsUx/
apps/platform/app/Livewire/BulkOperationProgress.php
apps/platform/resources/views/livewire/bulk-operation-progress.blade.php
apps/platform/app/Services/OperationRunService.php
apps/platform/tests/Unit/Support/OpsUx/
apps/platform/tests/Feature/OpsUx/
docs/ui/tenantpilot-enterprise-ui-standards.md

Structure Decision: keep the implementation local to the existing Ops-UX support family and the current shell host. Do not introduce a new activity or progress framework outside App\Support\OpsUx.

Data / Migration Implications

  • No migration or new table is planned.
  • No new persisted user preference or progress-mode storage is allowed.
  • No new cache layer, backfill, or asset/deploy step should be required for v1.

Rollout Considerations

  • Filament remains v5 on Livewire v4. No panel-provider change is required, and provider registration remains in apps/platform/bootstrap/providers.php.
  • No global search change is required because the slice changes shared progress semantics, not resource discovery.
  • No destructive action is added. Existing start/retry/detail surfaces remain the only mutation owners.
  • No new asset registration is expected.

Risk Controls

  • Reject any implementation that reopens the shell terminal-outcome slice already owned by specs/268-operationrun-activity-feedback/ and the deferred 269 candidate.
  • Reject any implementation that introduces new summary_counts keys, a persisted progress mode, or a new OperationRun lifecycle.
  • Reject any implementation that derives percentages from status, duration, stale heuristics, or outcome counters.
  • Reject any implementation that widens the slice into dashboard-specific redesign, activity tray work, or counted writer rollout.
  • Reject any implementation that leaves a second progress calculator in Blade, Livewire, or another current host surface.

Implementation Phases

Phase 0 - Confirm Current Progress Truth And Drift Seams

  • Verify the current writer seams (OperationRunService, SummaryCountsNormalizer, OperationSummaryKeys) and the current visible adopter (BulkOperationProgress).

Phase 1 - Encode The Shared Progress Contract

  • Introduce one shared progress contract/helper that classifies capability and derives render-safe output from existing OperationRun truth.

Phase 2 - Adopt The Current Shell Host

  • Move shell progress eligibility and percentage math out of bulk-operation-progress.blade.php and onto the shared contract.

Phase 3 - Record The Guardrail And Future Boundaries

  • Update the UI standards and the focused tests so later specs inherit the same contract instead of re-explaining it locally.

Proportionality Review

  • Current operator problem: the repo has truthful counters and lifecycle state, but the current visible host still computes progress ad hoc.
  • Existing structure is insufficient because: sanitization and lifecycle normalization alone do not decide whether determinate progress is allowed.
  • Narrowest correct implementation: one shared progress-semantics helper plus shell adoption and one standards-doc update.
  • Ownership cost created: one helper, focused tests, and one standards update.
  • Alternative intentionally rejected: view-local math or persisted progress modes were rejected because they either preserve drift or add unjustified persistence.
  • Release truth: current-release truth. The repo already renders progress and already stores the counts needed to centralize the semantics now.