# Feature Specification: OperationRun Progress Contract v1

**Feature Branch**: `270-operationrun-progress-contract`  
**Created**: 2026-05-04  
**Status**: Ready for implementation  
**Input**: Manual promotion from `docs/product/spec-candidates.md` after repo-based duplicate verification excluded candidate `269` because `specs/268-operationrun-activity-feedback/` already owns the terminal-success and terminal-follow-up shell slice.

## Spec Candidate Check *(mandatory - SPEC-GATE-001)*

- **Problem**: `OperationRun` progress rendering is still decided ad hoc in the shell activity view and raw `summary_counts` conventions. The repo sanitizes numeric counts and normalizes lifecycle status, but it does not yet have one shared product contract that answers whether a run should show no progress, indeterminate activity, determinate counted progress, or a future phase/composite fallback.
- **Today's failure**: `BulkOperationProgress` currently calculates progress inline from raw `summary_counts`, while other current or future surfaces can only see status, outcome, and flattened counters. That makes it too easy for a surface to invent fake percentages from status, duration, or outcome counters such as `failed`, `succeeded`, or `skipped`, and it leaves future `OperationRun` hosts without one truthful progress language.
- **User-visible improvement**: Operators see one consistent, honest progress language across the covered Ops-UX surfaces: queued work and active work without trustworthy counts show activity only, active running work with trustworthy `processed` and `total` counts shows determinate progress, and terminal outcomes never masquerade as progress.
- **Smallest enterprise-capable version**: introduce one shared Ops-UX progress contract or presenter over existing `OperationRun` truth, adopt it in the current shell activity feedback surface that already renders progress, and document the contract in the UI standards so later run hosts and later rollout specs extend the same rules instead of improvising them locally.
- **Explicit non-goals**: no broad counted-progress rollout across run writers, no new `OperationRun` type or lifecycle, no dashboard redesign, no new queue or notification policy, no new `summary_counts` keys, no persistence for progress modes, no AI summaries, no customer-facing review changes, and no reopening of `specs/268-operationrun-activity-feedback/` or `specs/266-tenant-dashboard-productization-v1/` as the primary scope.
- **Permanent complexity imported**: one bounded shared progress-semantics helper in `App\Support\OpsUx`, one derived progress-capability vocabulary (`none`, `activity`, `counted`, `phased`, `composite`) kept in code and docs only, focused unit plus feature coverage, and one UI standards update.
- **Why now**: `specs/268-operationrun-activity-feedback/` already captures shell terminal outcome semantics, which makes the remaining abstraction gap visible: progress meaning still lives inline in a Blade view. The candidate backlog explicitly sequences `270` ahead of `271` and `272`, and the repo already contains the concrete seams needed to prepare this contract without inventing a new foundation.
- **Why not local**: a local shell-only math cleanup would still leave future `OperationRun` surfaces, future counted rollouts, and future phase/composite work free to invent their own progress semantics from raw counters.
- **Approval class**: Core Enterprise
- **Red flags triggered**: new derived capability vocabulary, one shared Ops-UX presenter/contract seam, and one cross-surface truth contract. Defense: the vocabulary stays derived only, the contract is justified by multiple real consumers, and no new persistence or framework layer is introduced.
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 1 | Wiederverwendung: 2 | **Gesamt: 10/12**
- **Decision**: approve

## Spec Scope Fields *(mandatory)*

- **Scope**: tenant + canonical-view
- **Primary Routes**:
  - `/admin/t/{tenant}/...` tenant-scoped start surfaces that receive the shared shell activity hint
  - `/admin/operations` and `/admin/operations/{run}` remain the canonical operations collection/detail routes whose summaries must stay compatible with the shared progress contract even when this slice does not broaden their visible UI
  - `/admin/t/{tenant}` remains contextual-only for any existing recent-operation summary that already consumes the same Ops-UX family; no new dashboard card is introduced in this slice
- **Data Ownership**: existing `OperationRun.status`, `OperationRun.outcome`, `OperationRun.summary_counts`, and `OperationRun.context` remain the only persisted truth. The progress contract is derived and must not introduce a new table, cache, mirror entity, or persisted progress mode.
- **RBAC**: existing `OperationRun` policies remain authoritative. Out-of-scope tenants stay deny-as-not-found (`404` semantics through the current tenant/admin boundaries), and in-scope actors only see progress states for runs they can already view.

## Cross-Cutting / Shared Pattern Reuse *(mandatory)*

- **Cross-cutting feature?**: yes
- **Interaction class(es)**: status messaging, activity feedback, progress disclosure, canonical run links, and execution-truth summaries
- **Systems touched**: `BulkOperationProgress`, `ActiveRuns`, `OperationRunService`, `SummaryCountsNormalizer`, `OperationSummaryKeys`, `OperationStatusNormalizer`, `OperationUxPresenter`, current Ops-UX tests, and `docs/ui/tenantpilot-enterprise-ui-standards.md`
- **Existing pattern(s) to extend**: current numeric-only `summary_counts` normalization, current lifecycle normalization, current active shell feedback surface, and the existing Ops-UX 3-surface contract
- **Shared contract / presenter / builder / renderer to reuse**: `App\Support\OpsUx\SummaryCountsNormalizer`, `App\Support\OpsUx\OperationSummaryKeys`, `App\Support\OpsUx\ActiveRuns`, `App\Support\OpsUx\OperationStatusNormalizer`, `App\Support\OpsUx\OperationUxPresenter`, `App\Services\OperationRunService`, and `apps/platform/resources/views/livewire/bulk-operation-progress.blade.php`
- **Why the existing shared path is sufficient or insufficient**: the repo already sanitizes allowed counters and normalizes lifecycle state, but none of those helpers currently decides whether a surface should show progress, what kind of progress is allowed, or when progress must collapse back to terminal outcome or simple activity.
- **Allowed deviation and why**: none planned. The feature must remove shell-local progress inference instead of creating a second widget-local contract.
- **Consistency impact**: `Queued`, `In progress`, `Completed successfully`, indeterminate activity labels, determinate progress labels, and the rule that outcome counters never substitute for `processed` must keep one meaning across covered Ops-UX surfaces and docs.
- **Review focus**: reviewers must block any implementation that derives percentage from status, duration, stale-state heuristics, or outcome counters, or that leaves a second local progress calculator in Blade or Livewire code.

## OperationRun UX Impact *(mandatory)*

- **Touches OperationRun start/completion/link UX?**: yes
- **Shared OperationRun UX contract/layer reused**: the existing Ops-UX start contract through `OperationRunLinks`, `OperationRunUrl`, `ActiveRuns`, `OperationStatusNormalizer`, and `OperationUxPresenter`, extended with one shared progress-semantics helper
- **Delegated start/completion UX behaviors**: queued toasts, canonical `View operation` link generation, tenant-safe URL resolution, current `run-enqueued` browser events, and existing terminal database-notification behavior remain delegated to the current shared path and are unchanged in this slice
- **Local surface-owned behavior that remains**: shell layout, copy density, and bounded host placement stay local to the existing shell surface; progress semantics no longer remain view-local
- **Queued DB-notification policy**: `N/A` - unchanged
- **Terminal notification path**: unchanged central lifecycle mechanism
- **Exception required?**: none

## Provider Boundary / Platform Core Check *(mandatory)*

- **Shared provider/platform boundary touched?**: no
- **Boundary classification**: `N/A`
- **Seams affected**: `N/A`
- **Neutral platform terms preserved or introduced**: `Operation`, `activity`, `progress`, `counted progress`, `terminal outcome`
- **Provider-specific semantics retained and why**: none
- **Why this does not deepen provider coupling accidentally**: the feature only formalizes progress semantics over existing platform-owned `OperationRun` truth
- **Follow-up path**: none

## UI / Surface Guardrail Impact *(mandatory)*

| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note |
|---|---|---|---|---|---|---|
| Tenant shell activity hint progress treatment | yes | Native Filament + existing Livewire/Blade surface | Ops UX activity feedback, execution-truth summaries | shell, page | no | The shell remains the visible v1 adopter; this slice moves progress semantics out of inline Blade math and into one shared contract |

## Decision-First Surface Role *(mandatory)*

| Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction |
|---|---|---|---|---|---|---|---|
| Tenant shell activity hint | Primary Decision Surface | Decide whether current work simply needs time, needs inspection now, or is already terminal | operation label, lifecycle state, one truthful progress mode, and the canonical `View operation` action | full run detail, logs, evidence, and diagnostics stay in Operations detail | Primary because it is the current visible progress host and must not invent execution truth | Follows the existing start-surface workflow | Replaces ad hoc progress math with one trustworthy meaning |

## Audience-Aware Disclosure *(mandatory)*

| Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention |
|---|---|---|---|---|---|---|---|
| Tenant shell activity hint | operator-MSP | operation label, lifecycle state, either activity-only or counted-progress treatment, and canonical open link | one concise guidance line only when it changes the next decision | raw payloads, failure summaries, logs, and debug context | `View operation` | raw/support detail stays in Operations detail | progress mode is derived once from the shared contract instead of each surface inventing its own explanation |

## UI/UX Surface Classification *(mandatory)*

| Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Tenant shell activity hint | Monitoring hint | Activity shell hint | Open the most relevant operation if follow-up is needed | explicit `View operation` link | forbidden | overflow navigation only | none | `/admin/operations?tenant_id={currentTenant}` | `/admin/operations/{run}` | current tenant context from the shell | Operation | lifecycle state plus one truthful progress mode | none |

## Operator Surface Contract *(mandatory)*

| Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions |
|---|---|---|---|---|---|---|---|---|---|---|
| Tenant shell activity hint | Tenant operator | Decide whether active work is merely progressing, still waiting, or already terminal | Start-surface hint | What operation state do I need to react to right now? | operation label, lifecycle state, activity-only or counted-progress treatment, canonical open link | detailed run diagnostics and evidence on Operations pages | lifecycle, progress capability | none | `View operation`, `Show all operations` | none |

**UI Action Matrix**: `N/A - no Filament Resource, RelationManager, or Page action surface is being introduced or reclassified. The changed surface remains a shell/widget hint and shared Ops-UX semantics layer only.`

## Proportionality Review *(mandatory when structural complexity is introduced)*

- **New source of truth?**: no new persisted source of truth; one shared derived progress contract over existing `OperationRun` truth only
- **New persisted entity/table/artifact?**: no
- **New abstraction?**: yes - one bounded shared progress contract/helper inside `App\Support\OpsUx`
- **New enum/state/reason family?**: yes - one derived progress-capability vocabulary (`none`, `activity`, `counted`, `phased`, `composite`) used in code and docs only, not persisted
- **New cross-domain UI framework/taxonomy?**: no
- **Current operator problem**: operators currently rely on progress semantics that are computed locally inside a shell view rather than from one central rule set
- **Existing structure is insufficient because**: numeric-only `summary_counts` sanitization and lifecycle normalization do not answer whether a surface is allowed to show determinate progress or how terminal, queued, phased, or composite work must degrade safely
- **Narrowest correct implementation**: one shared progress-semantics helper plus current shell adoption and one standards-doc update, with no count rollout and no new persistence
- **Ownership cost**: one helper, focused unit plus feature tests, and one standards-doc update
- **Alternative intentionally rejected**: leaving the progress math in `bulk-operation-progress.blade.php` or persisting an explicit progress mode on `operation_runs` were both rejected as either too local or too heavy
- **Release truth**: current-release truth. The repo already has multiple real surfaces and writers using `summary_counts`; this slice keeps them from drifting into conflicting progress claims

### Compatibility posture

This feature assumes a pre-production environment.

Backward compatibility, legacy aliases, migration shims, and compatibility-specific tests are out of scope unless a later implementation slice proves they are required.

Canonical replacement of ad hoc progress inference is preferred over preserving duplicate logic.

## Testing / Lane / Runtime Impact *(mandatory)*

- **Test purpose / classification**: Unit, Feature
- **Validation lane(s)**: fast-feedback, confidence
- **Why this classification and these lanes are sufficient**: one unit suite can prove the progress-capability mapping and render-model rules cheaply, while focused feature coverage can prove the current shell host consumes the shared contract and does not regress into fake percentage output. Browser proof is not required for this slice because layout and clickability are already owned by `specs/268-operationrun-activity-feedback/`.
- **New or expanded test families**: one focused `tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php` family plus focused extensions to the current `tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php`, `tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php`, and `tests/Feature/OpsUx/SummaryCountsWhitelistTest.php`
- **Fixture / helper cost impact**: low to moderate. Reuse current `OperationRun` factories, tenant context helpers, and current Ops-UX feature fixtures; do not add provider-heavy setup or a new browser family.
- **Heavy-family visibility / justification**: none
- **Special surface test profile**: global-context-shell
- **Standard-native relief or required special coverage**: standard unit plus feature coverage is sufficient; this spec deliberately does not create new browser obligations
- **Reviewer handoff**: reviewers must confirm that the shell consumes a single shared progress contract, queued runs never show determinate progress, terminal runs never keep progress UI, outcome counters do not masquerade as progress, and the feature does not introduce new `summary_counts` keys or a second local helper
- **Budget / baseline / trend impact**: small feature-local increase only
- **Escalation needed**: `reject-or-split` if implementation widens into run-writer rollout, dashboard redesign, or a second persisted progress model
- **Active feature PR close-out entry**: Guardrail / Smoke Coverage
- **Planned validation commands**:
  - `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`

## User Scenarios & Testing *(mandatory)*

### User Story 1 - Derive truthful progress modes from existing run truth (Priority: P1)

As an operator, I need one shared progress contract to decide whether a run shows no progress, activity-only feedback, or determinate counted progress, so the product does not invent conflicting execution claims across Ops-UX surfaces.

**Why this priority**: this is the core truth gap. Without a shared contract, later shell, dashboard, and rollout work will continue to duplicate or drift progress semantics.

**Independent Test**: create queued, running, completed-success, completed-failed, and follow-up runs with different `summary_counts`, then assert that the shared contract returns the correct capability and render model without depending on a specific UI surface.

**Acceptance Scenarios**:

1. **Given** a running run with numeric `summary_counts.total` and `summary_counts.processed`, **When** the shared contract evaluates that run, **Then** it returns counted progress and clamps the rendered values safely.
2. **Given** a queued run or a running run without trustworthy numeric `processed` and `total` counts, **When** the shared contract evaluates it, **Then** it returns activity-only progress and does not fabricate a percentage.
3. **Given** a terminal run, **When** the shared contract evaluates it, **Then** it returns no progress line or bar even if outcome counters are present.

---

### User Story 2 - Keep the current shell host on the shared contract (Priority: P1)

As a tenant operator, I need the current shell activity hint to consume the shared progress contract instead of local view math, so active work stays truthful and terminal work never keeps stale progress UI.

**Why this priority**: the current shell view is the real, repo-visible seam where ad hoc progress logic already exists.

**Independent Test**: render the current shell activity surface with queued, running, and terminal runs, then verify that it uses counted progress only when the shared contract allows it and otherwise falls back to activity-only or terminal semantics.

**Acceptance Scenarios**:

1. **Given** a running run with valid `processed` and `total` counts, **When** the shell hint renders it, **Then** the shell shows determinate counted progress derived through the shared contract rather than view-local math.
2. **Given** a queued run with a planned total, **When** the shell hint renders it, **Then** the shell shows waiting or activity state only and does not promote it to determinate progress.
3. **Given** a completed run with `succeeded`, `failed`, or `skipped` counters but no trustworthy active progress truth, **When** the shell hint renders it, **Then** the shell shows terminal outcome semantics and not a percentage.

---

### User Story 3 - Document future-safe progress boundaries (Priority: P2)

As a maintainer, I need the UI standards and developer guidance to define what counted, activity-only, phased, and composite progress mean, so later rollout specs extend one contract instead of inventing new progress languages.

**Why this priority**: the contract only stays useful if later surfaces and writer rollouts know exactly what they may and may not claim.

**Independent Test**: review the standards guidance and the focused validation suite together, then confirm the package explicitly defers counted rollout and future phase/composite implementation to named follow-up specs.

**Acceptance Scenarios**:

1. **Given** the UI standards are updated for this package, **When** a maintainer reads the progress rules, **Then** they can tell that `processed` and `total` are the only v1 determinate source and that outcome counters remain outcome-only.
2. **Given** future phased or composite work is still unspecced, **When** a maintainer reads the package, **Then** the package clearly states that those categories must not masquerade as counted percentages until trustworthy persisted truth exists.

### Edge Cases

- Queued runs may already know `total`, but they still render as activity-only rather than counted progress.
- Running runs with `processed > total`, negative counters, or non-numeric counter values must clamp or degrade safely rather than showing impossible percentages.
- Terminal runs that retain `processed` and `total` counts for summary truth must still drop progress UI and show terminal outcome only.
- Runs that only have `succeeded`, `failed`, or `skipped` counts must not silently use those outcome counters as progress substitutes.
- Phase/composite categories may be declared in the shared contract for future use, but until trustworthy phase or child-run truth exists they must degrade to non-counted display and not invent percentages.

## Requirements *(mandatory)*

**Constitution alignment summary**: This feature adds no new Graph calls, no new write path, no new `OperationRun` lifecycle, no new `summary_counts` key, and no new persistence. It reuses the current Ops-UX 3-surface contract, the current `summary_counts` whitelist, and current shell activity host while centralizing progress semantics into one shared rule set.

### Functional Requirements

- **FR-001**: The implementation MUST provide one shared Ops-UX progress contract or presenter that derives progress capability and render model from existing `OperationRun` truth instead of Blade- or widget-local progress math.
- **FR-002**: The shared contract MUST define one derived vocabulary for `none`, `activity`, `counted`, `phased`, and `composite` progress capability, and that vocabulary MUST live in code and docs only rather than as persisted `OperationRun` state.
- **FR-003**: Determinate counted progress MUST be allowed only when the run is actively running and `summary_counts.total` plus `summary_counts.processed` are trustworthy numeric values with `total > 0`.
- **FR-004**: Queued runs and active runs without trustworthy `processed` and `total` counts MUST render as activity-only or indeterminate and MUST NOT fabricate a percentage from status, duration, stale-state heuristics, or outcome counters.
- **FR-005**: Terminal runs MUST render no progress bar, no percentage, and no `processed / total` progress line, even when retained counters are present for audit or summary truth.
- **FR-006**: `summary_counts.succeeded`, `summary_counts.failed`, and `summary_counts.skipped` remain outcome counters. They MUST NOT silently replace `summary_counts.processed` as the determinate progress source.
- **FR-007**: When counted progress is allowed, the shared contract MUST sanitize or clamp `processed`, `total`, and percentage output so rendered values stay within a truthful `0-100` percent range and `processed` never exceeds `total` visibly.
- **FR-008**: The shared contract MUST declare safe future `phased` and `composite` capabilities, and those categories MUST NOT masquerade as counted percentages until trustworthy persisted phase or child-progress truth exists.
- **FR-009**: The current shell activity feedback surface MUST consume the shared progress contract and MUST NOT keep inline percentage calculation or progress-mode inference in `bulk-operation-progress.blade.php`.
- **FR-010**: The feature MUST update `docs/ui/tenantpilot-enterprise-ui-standards.md` and the relevant Spec Kit artifacts so the progress contract, anti-patterns, and follow-up ownership are documented once.
- **FR-011**: This slice MUST NOT add new `OperationRun` status values, outcome values, notification surfaces, `summary_counts` keys, or persisted progress-mode flags.

### Authorization and Safety Requirements

- **AR-001**: Tenant/admin-plane authorization semantics remain unchanged: out-of-scope access remains deny-as-not-found (`404` semantics through current tenant/admin boundaries), while in-scope visibility continues to reuse server-side `OperationRun` policies.
- **AR-002**: No covered surface may reveal progress state or progress-derived copy for a run the current actor cannot already view.
- **AR-003**: No destructive or mutating action is introduced. Existing run start, retry, and detail actions remain responsible for their current confirmation and authorization rules.

### Non-Functional Requirements

- **NFR-001**: The slice MUST stay Filament-native and Livewire v4-compatible. No panel-provider registration change is allowed; `apps/platform/bootstrap/providers.php` remains authoritative.
- **NFR-002**: No new panel, no new globally searchable resource, and no new asset registration strategy are allowed.
- **NFR-003**: Polling remains intentional and bounded. The feature may reuse existing poller families, but it MUST NOT introduce new parallel polling loops.
- **NFR-004**: `OperationSummaryKeys` and `SummaryCountsNormalizer` remain the only allowed summary-count whitelist and sanitization owners. The progress contract must build on them rather than bypass them.

## Deferred Follow-Ups / Explicit Non-Goals

- `269 — OperationRun Terminal Outcome Feedback`, because the repo-based duplicate check shows that `specs/268-operationrun-activity-feedback/` already owns that shell slice
- `271 — Counted Progress Rollout v1`
- `272 — OperationRun Phase & Composite Progress v1`
- `273 — Tenant Dashboard Active Operations Summary Card`
- any run-writer rollout that adds or changes `summary_counts.total` / `summary_counts.processed`
- any dashboard, tray, or customer-facing redesign beyond consuming the shared contract later
- any new persisted progress model or telemetry system

## Key Entities

- **OperationRun Progress Capability**: the derived classification that answers whether a run currently supports no progress, activity-only feedback, determinate counted progress, or a future phased/composite mode.
- **OperationRun Progress Render Model**: the derived payload that a host surface consumes to show activity-only copy, counted progress values, or terminal no-progress behavior without doing its own math.
- **Counted Progress Truth**: the specific case where active running work has trustworthy numeric `processed` and `total` counters suitable for determinate rendering.
- **Outcome Counters**: retained numeric counts such as `succeeded`, `failed`, and `skipped` that remain outcome summary truth and do not become determinate progress implicitly.

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: In focused unit coverage, the shared progress contract classifies the covered fixtures into `none`, `activity`, `counted`, and safe future `phased/composite` fallbacks without relying on a specific view.
- **SC-002**: In focused shell feature coverage, queued and terminal runs show no determinate percentage in 100% of covered scenarios.
- **SC-003**: In focused shell feature coverage, determinate progress appears only for active running runs with trustworthy numeric `processed` and `total` values.
- **SC-004**: In regression coverage, outcome counters such as `succeeded`, `failed`, and `skipped` never produce counted progress unless `processed` and `total` also satisfy the shared contract.
- **SC-005**: The UI standards and Spec Kit artifacts document one canonical progress contract and explicitly assign counted rollout and phase/composite rollout to later follow-up specs.

## Candidate Selection Rationale

- **Selected candidate**: OperationRun Progress Contract v1
- **Source locations**:
  - `docs/product/spec-candidates.md`
  - `docs/product/roadmap.md`
  - `specs/268-operationrun-activity-feedback/spec.md`
  - `apps/platform/app/Support/OpsUx/ActiveRuns.php`
  - `apps/platform/app/Support/OpsUx/SummaryCountsNormalizer.php`
  - `apps/platform/app/Support/OpsUx/OperationSummaryKeys.php`
  - `apps/platform/resources/views/livewire/bulk-operation-progress.blade.php`
- **Why selected**: the active auto-prep queue is intentionally empty, candidate `269` fails the duplicate check because `specs/268-operationrun-activity-feedback/` already covers shell terminal-success and terminal-follow-up semantics, and the current repo still shows inline progress logic inside the shell view with no shared contract. Candidate `270` is the next bounded backlog item that closes a real truth gap without widening into dashboard work or run-writer rollout.
- **Why this is the smallest viable implementation slice**: v1 stays on one shared progress contract plus current shell adoption and one standards update. It explicitly excludes counted writer rollout, dashboard productization, and future phase/composite execution truth.
- **Why close alternatives were deferred**:
  - `269 — OperationRun Terminal Outcome Feedback` is already covered by `specs/268-operationrun-activity-feedback/` and is therefore not a safe prep target.
  - `273 — Tenant Dashboard Active Operations Summary Card` remains conditional on post-`268` dashboard drift and should not be promoted before that drift is visible.
  - `271 — Counted Progress Rollout v1` depends on the shared contract from this package.
  - `272 — OperationRun Phase & Composite Progress v1` depends on the shared contract from this package and likely on later trustworthy phase/composite truth.

## Related-Spec Guardrail Check

- `specs/268-operationrun-activity-feedback/`: prep package, not completed, and not safe to refresh through this skill because it already owns the shell terminal outcome slice.
- `specs/266-tenant-dashboard-productization-v1/`: different dashboard surface with its own implementation history; used only as context for why `273` remains conditional.
- `specs/055-ops-ux-rollout/`, `specs/160-operation-lifecycle-guarantees/`, and `specs/134-audit-log-foundation/`: implemented foundation specs that already define the numeric-only `summary_counts` and three-surface lifecycle rules; used as inherited truth only.