# Implementation Plan: Counted Progress Rollout v1 **Branch**: `271-counted-progress-rollout` | **Date**: 2026-05-05 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `/specs/271-counted-progress-rollout/spec.md` ## Summary This plan prepares one bounded writer-rollout slice on top of the existing shared progress contract from Spec 270. The implementation path is to reuse `OperationRunProgressContract` and `OperationRunService`, add truthful `total` plus `processed` writes only where the repo already exposes deterministic work units, and leave baseline capture/compare on their current phased path. The slice must not invent totals, widen into phase/composite work, add new `summary_counts` keys, or redesign current Ops-UX surfaces. ## Inherited Baseline / Explicit Delta ### Inherited baseline - `App\Support\OpsUx\OperationRunProgressContract` already centralizes `none`, `activity`, `counted`, `phased`, and `composite` progress modes. - `App\Services\OperationRunService` already owns `updateRun()`, `incrementSummaryCounts()`, and `maybeCompleteBulkRun()` as the authoritative summary-count mutation path. - The current tenant shell adopter already consumes the shared progress contract and can render determinate counted progress when trustworthy counts exist. - Inventory sync, review-pack generation, evidence-snapshot generation, `AddPoliciesToBackupSetJob`, `BulkBackupSetRestoreJob`, and `BackupSetRestoreWorkerJob` are already repo-real workflows with current tests. - Baseline capture and baseline compare already expose evidence-capture phase hints, which the current progress contract classifies as `phased` before `counted`. ### Explicit delta in this plan - roll out truthful counted inputs for selected stable-unit run families only - standardize parent and child count discipline across `AddPoliciesToBackupSetJob`, `BulkBackupSetRestoreJob`, and `BackupSetRestoreWorkerJob` - keep current shell and Operations detail surfaces unchanged except for consuming newly truthful count data - document the narrowed scope deviation from the original candidate wording: baseline capture/compare remain deferred because repo truth now places them on the phased/composite path ## Technical Context **Language/Version**: PHP 8.4, Laravel 12, Filament v5, Livewire v4 **Primary Dependencies**: current Ops-UX support classes, native Filament/Livewire shell feedback, Pest v4 **Storage**: PostgreSQL via existing `operation_runs`, review-pack, evidence-snapshot, backup-set, and restore tables **Testing**: Pest Unit + Feature **Validation Lanes**: fast-feedback, confidence **Target Platform**: existing Laravel monolith in `apps/platform` **Project Type**: web application (Laravel monolith with Filament) **Performance Goals**: no new query families, no new polling loops, and no slower-than-current active-operation feedback **Constraints**: no new `summary_counts` keys, no new persistence, no contract-precedence change, and no broad writer sweep **Scale/Scope**: one shared contract reused across 4 selected run families plus one standards update and focused regression coverage ## Likely Affected Repo Surfaces - `apps/platform/app/Support/OpsUx/OperationRunProgressContract.php` - `apps/platform/app/Support/OpsUx/SummaryCountsNormalizer.php` - `apps/platform/app/Support/OpsUx/OperationSummaryKeys.php` - `apps/platform/app/Services/OperationRunService.php` - `apps/platform/app/Jobs/RunInventorySyncJob.php` - `apps/platform/app/Services/Inventory/InventorySyncService.php` - `apps/platform/app/Jobs/GenerateReviewPackJob.php` - `apps/platform/app/Services/ReviewPackService.php` - `apps/platform/app/Jobs/GenerateEvidenceSnapshotJob.php` - `apps/platform/app/Services/Evidence/EvidenceSnapshotService.php` - `apps/platform/app/Jobs/AddPoliciesToBackupSetJob.php` - `apps/platform/app/Jobs/BulkBackupSetRestoreJob.php` - `apps/platform/app/Jobs/Operations/BackupSetRestoreWorkerJob.php` - `apps/platform/tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php` - `apps/platform/tests/Feature/Inventory/RunInventorySyncJobTest.php` - `apps/platform/tests/Feature/ReviewPack/ReviewPackGenerationTest.php` - `apps/platform/tests/Feature/Evidence/GenerateEvidenceSnapshotJobTest.php` - `apps/platform/tests/Feature/BackupSets/AddPoliciesToBackupSetJobTest.php` - `apps/platform/tests/Unit/BulkBackupSetRestoreJobTest.php` - `apps/platform/tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php` - `apps/platform/tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php` - `docs/ui/tenantpilot-enterprise-ui-standards.md` ## UI / Filament & Livewire Fit - The visible adopter remains the existing tenant-shell activity feedback surface. No new page, widget family, or dashboard card is introduced. - The shell stays decision-first. This slice changes only whether selected runs can legitimately enter the already-existing counted mode. - Operations collection/detail pages remain diagnostics-first drill-through targets. They inherit more truthful count data but no new layout or action model. - No panel registration, asset registration, or global-search changes are planned. ## RBAC / Policy Fit - Existing capability gates for the initiating surfaces remain unchanged. - Existing `OperationRun` policies remain the only progress-visibility gate. - No new mutation surface is introduced, so current server-side authorization and confirmation behavior remains on the existing launch and detail surfaces. ## Audit / Logging Fit - Existing queued toasts and terminal notifications remain authoritative and unchanged. - Existing run audit remains the only audit trail for the counted rollout. No new run-local audit channel is introduced. - Parent/child bulk completion still flows through existing `OperationRunService` helpers instead of feature-local completion code. ## Data & Query Fit - Progress truth remains fully derived from `operation_runs.summary_counts` plus existing contract logic. - Determinate progress stays limited to work with deterministic units known before or during processing. - Outcome counters remain summary truth and do not replace `processed`. - No migration, no new JSON schema, no cache layer, and no new persisted preference or progress mode are planned. ## UI / Surface Guardrail Plan - **Guardrail scope**: changed surfaces - **Native vs custom classification summary**: native Filament + existing Livewire/Blade shell surface - **Shared-family relevance**: Ops-UX activity feedback and run summaries - **State layers in scope**: shell - **Audience modes in scope**: operator-MSP - **Decision/diagnostic/raw hierarchy plan**: decision-first on shell, diagnostics-second on Operations detail - **Raw/support gating plan**: unchanged; raw/support detail remains on diagnostics surfaces only - **One-primary-action / duplicate-truth control**: keep one dominant `View operation` action and one progress mode derived from the shared contract - **Handling modes by drift class or surface**: review-mandatory - **Repository-signal treatment**: review-mandatory - **Special surface test profiles**: global-context-shell - **Required tests or manual smoke**: functional-core, state-contract - **Exception path and spread control**: none planned - **Active feature PR close-out entry**: Guardrail / Smoke Coverage ## Shared Pattern & System Fit - **Cross-cutting feature marker**: yes - **Systems touched**: Ops-UX progress contract, summary-count service helpers, inventory/review/evidence/backup writers, shell adopter - **Shared abstractions reused**: `OperationRunProgressContract`, `OperationRunService`, `SummaryCountsNormalizer`, `OperationSummaryKeys` - **New abstraction introduced? why?**: none planned - **Why the existing abstraction was sufficient or insufficient**: rendering semantics are already centralized; the missing piece is writer-side counted input for specific repo-real workflows - **Bounded deviation / spread control**: keep all count mutation on `OperationRunService`; any run family without deterministic units stays out of scope rather than adding a local exception ## OperationRun UX Impact - **Touches OperationRun start/completion/link UX?**: yes, for progress truth only - **Central contract reused**: existing OperationRun Start UX Contract plus `OperationRunProgressContract` - **Delegated UX behaviors**: queued toast, canonical run links, `run-enqueued` event, and terminal notification lifecycle remain delegated and unchanged - **Surface-owned behavior kept local**: launch inputs and current domain-specific validation 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**: `OperationRun` truth, progress contract, shell feedback - **Neutral platform terms / contracts preserved**: `Operation`, `progress`, `counted progress`, `activity`, `terminal outcome` - **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 only enriches execution feedback over current `OperationRun` truth. - Read/write separation: PASS. No new external write path is introduced; current domain jobs keep their existing behavior and only improve run-count truth. - Graph contract path: PASS. No new Graph/provider seam is introduced. - Deterministic capabilities: PASS. Authorization and progress eligibility stay deterministic and testable. - RBAC-UX: PASS. Visibility remains on existing tenant/admin boundaries and `OperationRun` policies. - Run observability: PASS. Long-running work still flows through current `OperationRun` ownership and current Ops-UX surfaces. - Ops-UX lifecycle: PASS. `status` and `outcome` ownership remains on `OperationRunService`; only count truth is enriched. - Ops-UX summary counts: PASS. The rollout stays on current whitelist semantics and numeric-only values. - Test governance: PASS. Proof remains bounded to Unit plus Feature. - Proportionality / no premature abstraction: PASS. No new abstraction or persistence is introduced. - Persisted truth / behavioral state: PASS. No new table, cache, or status family is added. - Shared pattern first / UI semantics / Filament-native UI: PASS. Existing Ops-UX path remains central and the visible adopter is unchanged. - Provider boundary: PASS. No provider/platform seam change. - Filament/Laravel panel safety: PASS. Filament v5 stays on Livewire v4, provider registration remains in `apps/platform/bootstrap/providers.php`, and no assets change. **Gate evaluation**: PASS. ## Test Governance Check - **Test purpose / classification by changed surface**: Unit for contract safeguards; Feature for selected writer rollouts and current shell adoption - **Affected validation lanes**: fast-feedback, confidence - **Why this lane mix is the narrowest sufficient proof**: domain feature suites already exist for the in-scope writer families, and the shared contract already has a focused unit suite. Browser proof remains owned by Spec 268 because this slice does not alter 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/Inventory/RunInventorySyncJobTest.php tests/Feature/ReviewPack/ReviewPackGenerationTest.php tests/Feature/Evidence/GenerateEvidenceSnapshotJobTest.php tests/Feature/BackupSets/AddPoliciesToBackupSetJobTest.php tests/Unit/BulkBackupSetRestoreJobTest.php tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php` - **Fixture / helper / factory / seed / context cost risks**: low to moderate; reuse current tenant context helpers and current job-specific fixtures instead of introducing new provider-heavy harnesses - **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**: rerun the two proving commands above and verify that selected families now emit truthful counts, excluded phased families remain excluded, and no writer bypasses `OperationRunService` - **Budget / baseline / trend follow-up**: none expected beyond a small feature-local increase - **Review-stop questions**: did any excluded run family sneak in, did any writer invent totals, did `processed` stay bounded by `total`, and did any new summary key or local progress helper appear? - **Escalation path**: `reject-or-split` for any baseline phased/composite widening, 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 writer-rollout follow-through on Spec 270; any remaining excluded families are already named as future follow-ups. ## Project Structure ### Documentation (this feature) ```text specs/271-counted-progress-rollout/ ├── spec.md ├── plan.md ├── tasks.md └── checklists/ └── requirements.md ``` ### Source Code (expected implementation surfaces) ```text apps/platform/app/Support/OpsUx/ apps/platform/app/Services/OperationRunService.php apps/platform/app/Jobs/RunInventorySyncJob.php apps/platform/app/Services/Inventory/InventorySyncService.php apps/platform/app/Jobs/GenerateReviewPackJob.php apps/platform/app/Services/ReviewPackService.php apps/platform/app/Jobs/GenerateEvidenceSnapshotJob.php apps/platform/app/Services/Evidence/EvidenceSnapshotService.php apps/platform/app/Jobs/AddPoliciesToBackupSetJob.php apps/platform/app/Jobs/BulkBackupSetRestoreJob.php apps/platform/app/Jobs/Operations/BackupSetRestoreWorkerJob.php apps/platform/tests/Unit/Support/OpsUx/ apps/platform/tests/Feature/Inventory/ apps/platform/tests/Feature/ReviewPack/ apps/platform/tests/Feature/Evidence/ apps/platform/tests/Feature/BackupSets/ apps/platform/tests/Feature/OpsUx/ docs/ui/tenantpilot-enterprise-ui-standards.md ``` **Structure Decision**: keep the rollout local to current jobs/services plus the existing Ops-UX support family. Do not introduce a new progress rollout framework or a second writer abstraction. ## Data / Migration Implications - No migration or schema change is planned. - No new persisted progress mode or preference is allowed. - No backfill is planned. Historical runs remain historical truth; the rollout affects future execution only. ## Rollout Considerations - Filament remains v5 on Livewire v4. Provider registration remains in `apps/platform/bootstrap/providers.php`. - No global search or asset change is required because the slice changes only run-count truth. - No destructive action or confirmation model changes are planned. - No deployment step beyond ordinary code deploy and current test validation is expected. ## Risk Controls - Reject any implementation that changes progress-contract precedence to force phased runs into counted mode. - Reject any implementation that adds new `summary_counts` keys or uses outcome counters as hidden progress substitutes. - Reject any implementation that sets totals from speculative estimates instead of deterministic current work sets. - Reject any implementation that initializes parent totals multiple times or allows child retries to double-increment `processed`. - Reject any implementation that broadens the rollout to unrelated run families not named in the spec and tasks. ## Implementation Phases ### Phase 0 - Confirm Selected Stable-Unit Writer Seams - Verify current counted and terminal-only seams in inventory sync, review-pack generation, evidence-snapshot generation, `AddPoliciesToBackupSetJob`, `BulkBackupSetRestoreJob`, and `BackupSetRestoreWorkerJob`. - Reconfirm that baseline capture/compare remain phased under the current contract and stay out of scope. ### Phase 1 - Roll Out Inventory And Artifact Writer Counts - Add or standardize `total` plus `processed` writes for inventory sync, review-pack generation, and evidence-snapshot generation. ### Phase 2 - Standardize Enumerated Backup/Restore Fan-Out Counts - Align `AddPoliciesToBackupSetJob`, `BulkBackupSetRestoreJob`, and `BackupSetRestoreWorkerJob` on one total/processed discipline and current `maybeCompleteBulkRun()` semantics. ### Phase 3 - Lock The Guardrail And Proof - Update the UI standards and focused tests so later run families do not re-open fake or partial counted rollout. ## Proportionality Review - **Current operator problem**: determinable long-running work still looks indeterminate because selected writers do not persist counts early enough or consistently enough. - **Existing structure is insufficient because**: the shared contract already exists and cannot invent counted progress from terminal summaries alone. - **Narrowest correct implementation**: update only repo-verified stable-unit writers and leave all other families on their current truthful modes. - **Ownership cost created**: targeted job/service tests and one standards update. - **Alternative intentionally rejected**: broad all-writers rollout or phased-precedence changes were rejected because they would widen into a second spec. - **Release truth**: current-release truth. The repo already contains the contract, the visible adopter, and the selected writers needed for this rollout.