feat: implement operation run queue truth foundation (spec 358)
This commit is contained in:
parent
b7907bd69d
commit
03514b9e5b
@ -38,6 +38,13 @@ public static function forRun(OperationRun $run): array
|
||||
$context = is_array($run->context) ? $run->context : [];
|
||||
$capability = self::capabilityForRun($run, $summaryCounts, $context);
|
||||
|
||||
if ($run->isCurrentlyActive() && $run->freshnessState()->isLikelyStale()) {
|
||||
return self::indeterminateModel(
|
||||
$capability,
|
||||
RunDurationInsights::stuckGuidance($run) ?? 'Past the lifecycle window. Review worker health and logs before retrying.',
|
||||
);
|
||||
}
|
||||
|
||||
return match ($capability) {
|
||||
self::COUNTED => self::countedModel($summaryCounts),
|
||||
self::PHASED => self::phasedModel($run, $context),
|
||||
@ -411,4 +418,4 @@ private static function intOrNull(mixed $value): ?int
|
||||
{
|
||||
return is_int($value) ? $value : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,7 +201,8 @@ private static function buildSurfaceGuidance(OperationRun $run): ?string
|
||||
$freshnessState = self::freshnessState($run);
|
||||
|
||||
if ($freshnessState->isLikelyStale()) {
|
||||
return 'This operation is past its lifecycle window. Review worker health and logs before retrying from the start surface.';
|
||||
return RunDurationInsights::stuckGuidance($run)
|
||||
?? 'Past the lifecycle window. Review worker health and logs before retrying.';
|
||||
}
|
||||
|
||||
if ($freshnessState->isReconciledFailed()) {
|
||||
|
||||
@ -13,8 +13,14 @@
|
||||
$operationsIndexUrl = $tenant ? \App\Support\OpsUx\OperationRunUrl::index($tenant) : null;
|
||||
$primaryActionLabel = $usesCollectivePrimaryAction ? 'Review operations' : 'View operation';
|
||||
$bannerTitle = $hasActiveVisibleRuns && ! $hasTerminalVisibleRuns ? 'Active operations' : 'Operation updates';
|
||||
$hasStaleActiveVisibleRuns = $runs->contains(
|
||||
fn ($run): bool => $run instanceof \App\Models\OperationRun
|
||||
&& $run->isCurrentlyActive()
|
||||
&& $run->problemClass() === \App\Models\OperationRun::PROBLEM_CLASS_ACTIVE_STALE_ATTENTION
|
||||
);
|
||||
$bannerHelper = match (true) {
|
||||
$hasActiveVisibleRuns && $hasTerminalFollowUpVisibleRuns => 'Active and recent operation updates that may need review.',
|
||||
$hasStaleActiveVisibleRuns => 'One or more active operations are past their lifecycle window and need review.',
|
||||
$hasActiveVisibleRuns => 'Queued and running work stays here until diagnostics are needed.',
|
||||
$hasTerminalFollowUpVisibleRuns => 'Recent operation updates that may need review.',
|
||||
$hasTerminalVisibleRuns => 'Successful operation updates stay briefly visible so you can confirm completion and keep working.',
|
||||
@ -111,8 +117,7 @@ class="inline-flex items-center justify-center rounded-lg border border-transpar
|
||||
$progress = \App\Support\OpsUx\OperationRunProgressContract::forRun($run);
|
||||
$hasDeterminateProgress = $progress['display'] === 'counted';
|
||||
$lifecycleAttention = \App\Support\OpsUx\OperationUxPresenter::lifecycleAttentionSummary($run);
|
||||
$showsLifecycleAttention = $lifecycleAttention !== null
|
||||
&& ($lifecycleAttention !== 'Likely stale' || $run->status === 'queued' || ! $hasDeterminateProgress);
|
||||
$showsLifecycleAttention = $lifecycleAttention !== null;
|
||||
$progressLabel = $progress['label'];
|
||||
$progressPercent = $progress['percent'];
|
||||
$showsIndeterminateProgress = $progress['display'] === 'indeterminate';
|
||||
|
||||
@ -243,6 +243,34 @@ function baselineCompareGapContext(array $overrides = []): array
|
||||
->and($bannerPosition)->toBeLessThan($decisionPosition);
|
||||
});
|
||||
|
||||
it('keeps stale canonical detail aligned with lifecycle guidance instead of ordinary queued copy', function (): void {
|
||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||
|
||||
setAdminPanelContext();
|
||||
|
||||
$run = OperationRun::factory()->create([
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'type' => 'inventory_sync',
|
||||
'status' => OperationRunStatus::Queued->value,
|
||||
'outcome' => OperationRunOutcome::Pending->value,
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||
->assertOk()
|
||||
->assertSee('Likely stale operation')
|
||||
->assertSee('Decision');
|
||||
|
||||
$pageText = visiblePageText($response);
|
||||
|
||||
expect($pageText)->toContain('past its lifecycle window')
|
||||
->not->toContain('No action needed yet. The operation is waiting for a worker.')
|
||||
->not->toContain('Waiting for worker.');
|
||||
});
|
||||
|
||||
it('renders explicit sparse-data fallbacks for operation runs', function (): void {
|
||||
$workspace = Workspace::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
@ -115,3 +115,29 @@
|
||||
->assertDontSee('Operation finished Unknown')
|
||||
->assertDontSee('Completed with follow-up Unknown');
|
||||
});
|
||||
|
||||
it('subordinates stale running progress to lifecycle guidance on the operations workbench', function (): void {
|
||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||
|
||||
OperationRun::factory()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'type' => 'inventory_sync',
|
||||
'status' => 'running',
|
||||
'outcome' => 'pending',
|
||||
'summary_counts' => [
|
||||
'total' => 10,
|
||||
'processed' => 4,
|
||||
],
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||
->get(\App\Support\OperationRunLinks::index())
|
||||
->assertOk()
|
||||
->assertSee('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->assertDontSee('4 / 10 processed (40%)')
|
||||
->assertDontSee('Progress details pending.');
|
||||
});
|
||||
|
||||
@ -45,6 +45,8 @@
|
||||
->get(\App\Support\OperationRunLinks::index())
|
||||
->assertOk()
|
||||
->assertSee('Likely stale')
|
||||
->assertSee('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->assertDontSee('Progress details pending.')
|
||||
->assertSee('belong in terminal follow-up');
|
||||
|
||||
$this->actingAs($user)
|
||||
@ -58,7 +60,9 @@
|
||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||
->get(\App\Support\OperationRunLinks::tenantlessView($staleRun))
|
||||
->assertOk()
|
||||
->assertSee('Likely stale operation');
|
||||
->assertSee('Likely stale operation')
|
||||
->assertDontSee('No action needed yet. The operation is currently in progress.')
|
||||
->assertDontSee('Progress details pending.');
|
||||
});
|
||||
|
||||
it('renders lifecycle outcome fallbacks when historical runs are missing stored outcomes', function (): void {
|
||||
|
||||
@ -186,3 +186,32 @@
|
||||
->toContain('data-shared-detail-family="verification-report"')
|
||||
->toContain('data-host-kind="operation_run_detail"');
|
||||
});
|
||||
|
||||
it('keeps workspace monitoring guidance stale-first when an active run is beyond its lifecycle window', function (): void {
|
||||
$tenant = ManagedEnvironment::factory()->create();
|
||||
[$user, $tenant] = createUserWithTenant($tenant, role: 'owner');
|
||||
|
||||
OperationRun::factory()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'type' => 'inventory_sync',
|
||||
'status' => 'running',
|
||||
'outcome' => 'pending',
|
||||
'summary_counts' => [
|
||||
'total' => 10,
|
||||
'processed' => 4,
|
||||
],
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
Filament::setTenant(null, true);
|
||||
|
||||
$this->actingAs($user)
|
||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||
->get(route('admin.operations.index', ['workspace' => $tenant->workspace]))
|
||||
->assertSuccessful()
|
||||
->assertSee('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->assertDontSee('4 / 10 processed (40%)')
|
||||
->assertDontSee('Progress details pending.');
|
||||
});
|
||||
|
||||
@ -272,6 +272,40 @@
|
||||
expect(mb_substr_count($pageText, 'Automatically reconciled'))->toBe(1);
|
||||
});
|
||||
|
||||
it('keeps stale active detail aligned with lifecycle guidance when determinate progress data exists', function (): void {
|
||||
$tenant = ManagedEnvironment::factory()->create();
|
||||
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
||||
|
||||
$run = OperationRun::factory()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'user_id' => (int) $user->getKey(),
|
||||
'type' => 'inventory_sync',
|
||||
'status' => OperationRunStatus::Running->value,
|
||||
'outcome' => OperationRunOutcome::Pending->value,
|
||||
'summary_counts' => [
|
||||
'total' => 10,
|
||||
'processed' => 4,
|
||||
],
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
Filament::setTenant(null, true);
|
||||
|
||||
$this->actingAs($user)
|
||||
->withSession([
|
||||
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
||||
])
|
||||
->get(OperationRunLinks::tenantlessView((int) $run->getKey()))
|
||||
->assertSuccessful()
|
||||
->assertSee('Likely stale operation')
|
||||
->assertSee('past its lifecycle window')
|
||||
->assertDontSee('No action needed yet. The operation is currently in progress.')
|
||||
->assertDontSee('4 / 10 processed (40%)')
|
||||
->assertDontSee('Progress details pending.');
|
||||
});
|
||||
|
||||
it('keeps a canonical run viewer accessible when the remembered environment differs from the run tenant', function (): void {
|
||||
$workspace = Workspace::factory()->create();
|
||||
$tenantA = ManagedEnvironment::factory()->for($workspace)->create();
|
||||
|
||||
@ -344,7 +344,7 @@
|
||||
'total' => 10,
|
||||
'processed' => 4,
|
||||
],
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'started_at' => now()->subMinute(),
|
||||
]);
|
||||
|
||||
$component = Livewire::actingAs($user)
|
||||
@ -364,6 +364,70 @@
|
||||
->and($html)->not->toContain('Likely stale');
|
||||
})->group('ops-ux');
|
||||
|
||||
it('renders stale queued activity with lifecycle guidance instead of ordinary waiting copy', function (): void {
|
||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||
|
||||
$this->actingAs($user);
|
||||
Filament::setTenant($tenant, true);
|
||||
|
||||
OperationRun::factory()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'user_id' => (int) $user->getKey(),
|
||||
'type' => 'inventory_sync',
|
||||
'status' => 'queued',
|
||||
'outcome' => 'pending',
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
$component = Livewire::actingAs($user)
|
||||
->test(BulkOperationProgress::class)
|
||||
->call('refreshRuns');
|
||||
|
||||
$html = html_entity_decode($component->html(), ENT_QUOTES | ENT_HTML5);
|
||||
$pageText = preg_replace('/\s+/', ' ', strip_tags($html));
|
||||
|
||||
expect($html)->toContain('Likely stale')
|
||||
->and($pageText)->toContain('One or more active operations are past their lifecycle window and need review.')
|
||||
->and($pageText)->toContain('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->and($pageText)->not->toContain('Waiting for worker.');
|
||||
})->group('ops-ux');
|
||||
|
||||
it('shows lifecycle attention for determinate stale-running shell cases without keeping the ordinary progress bar', function (): void {
|
||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||
|
||||
$this->actingAs($user);
|
||||
Filament::setTenant($tenant, true);
|
||||
|
||||
OperationRun::factory()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'user_id' => (int) $user->getKey(),
|
||||
'type' => 'inventory_sync',
|
||||
'status' => 'running',
|
||||
'outcome' => 'pending',
|
||||
'summary_counts' => [
|
||||
'total' => 10,
|
||||
'processed' => 4,
|
||||
],
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
$component = Livewire::actingAs($user)
|
||||
->test(BulkOperationProgress::class)
|
||||
->call('refreshRuns');
|
||||
|
||||
$html = html_entity_decode($component->html(), ENT_QUOTES | ENT_HTML5);
|
||||
$pageText = preg_replace('/\s+/', ' ', strip_tags($html));
|
||||
|
||||
expect($html)->toContain('Likely stale')
|
||||
->and($html)->toContain('data-testid="ops-ux-activity-feedback-indeterminate"')
|
||||
->and($pageText)->toContain('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->and($pageText)->not->toContain('4 / 10 processed (40%)')
|
||||
->and($html)->not->toContain('aria-valuenow="40"');
|
||||
})->group('ops-ux');
|
||||
|
||||
it('renders phased fallback progress without inventing a counted percentage', function (): void {
|
||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||
|
||||
|
||||
@ -215,9 +215,40 @@
|
||||
->test(BulkOperationProgress::class)
|
||||
->call('refreshRuns')
|
||||
->assertSet('hasActiveRuns', true)
|
||||
->assertSee('One or more active operations are past their lifecycle window and need review.')
|
||||
->assertSee('Inventory sync')
|
||||
->assertSee('Likely stale')
|
||||
->assertSee('Waiting for worker.');
|
||||
->assertSee('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->assertDontSee('Waiting for worker.');
|
||||
})->group('ops-ux');
|
||||
|
||||
it('subordinates determinate stale-running shell progress to lifecycle guidance', function () {
|
||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||
$this->actingAs($user);
|
||||
Filament::setTenant($tenant, true);
|
||||
|
||||
OperationRun::factory()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'type' => 'inventory_sync',
|
||||
'status' => 'running',
|
||||
'outcome' => 'pending',
|
||||
'summary_counts' => [
|
||||
'total' => 10,
|
||||
'processed' => 4,
|
||||
],
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
Livewire::actingAs($user)
|
||||
->test(BulkOperationProgress::class)
|
||||
->call('refreshRuns')
|
||||
->assertSet('hasActiveRuns', true)
|
||||
->assertSee('Likely stale')
|
||||
->assertSee('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->assertDontSee('4 / 10 processed (40%)')
|
||||
->assertDontSeeHtml('aria-valuenow="40"');
|
||||
})->group('ops-ux');
|
||||
|
||||
it('clamps counted progress at the shell host when processed exceeds total', function () {
|
||||
|
||||
@ -67,6 +67,22 @@
|
||||
->and($progress['percent'])->toBeNull();
|
||||
});
|
||||
|
||||
it('replaces queued waiting copy with stale lifecycle guidance once the lifecycle window is exceeded', function (): void {
|
||||
$run = OperationRun::factory()->create([
|
||||
'type' => 'inventory_sync',
|
||||
'status' => 'queued',
|
||||
'outcome' => 'pending',
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
$progress = OperationRunProgressContract::forRun($run);
|
||||
|
||||
expect($progress['capability'])->toBe('activity')
|
||||
->and($progress['display'])->toBe('indeterminate')
|
||||
->and($progress['label'])->toBe('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->and($progress['percent'])->toBeNull();
|
||||
});
|
||||
|
||||
it('returns no progress for terminal runs even when retained counts exist', function (): void {
|
||||
$run = OperationRun::factory()->create([
|
||||
'status' => 'completed',
|
||||
@ -87,6 +103,32 @@
|
||||
->and($progress['percent'])->toBeNull();
|
||||
});
|
||||
|
||||
it('returns no progress for reconciled terminal runs even when stale lineage is recorded', function (): void {
|
||||
$run = OperationRun::factory()->create([
|
||||
'status' => 'completed',
|
||||
'outcome' => 'failed',
|
||||
'context' => [
|
||||
'reconciliation' => [
|
||||
'reconciled_at' => now()->toIso8601String(),
|
||||
'source' => 'adapter_reconciler',
|
||||
],
|
||||
],
|
||||
'summary_counts' => [
|
||||
'total' => 10,
|
||||
'processed' => 10,
|
||||
],
|
||||
'started_at' => now()->subMinutes(2),
|
||||
'completed_at' => now()->subSecond(),
|
||||
]);
|
||||
|
||||
$progress = OperationRunProgressContract::forRun($run);
|
||||
|
||||
expect($progress['capability'])->toBe('none')
|
||||
->and($progress['display'])->toBe('none')
|
||||
->and($progress['label'])->toBeNull()
|
||||
->and($progress['percent'])->toBeNull();
|
||||
});
|
||||
|
||||
it('does not let outcome counters masquerade as counted progress', function (): void {
|
||||
$run = OperationRun::factory()->create([
|
||||
'status' => 'running',
|
||||
@ -107,6 +149,67 @@
|
||||
->and($progress['percent'])->toBeNull();
|
||||
});
|
||||
|
||||
it('replaces ordinary activity copy with stale lifecycle guidance for stale running runs', function (): void {
|
||||
$run = OperationRun::factory()->create([
|
||||
'type' => 'inventory_sync',
|
||||
'status' => 'running',
|
||||
'outcome' => 'pending',
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
$progress = OperationRunProgressContract::forRun($run);
|
||||
|
||||
expect($progress['capability'])->toBe('activity')
|
||||
->and($progress['display'])->toBe('indeterminate')
|
||||
->and($progress['label'])->toBe('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->and($progress['percent'])->toBeNull();
|
||||
});
|
||||
|
||||
it('subordinates determinate stale-running progress to lifecycle guidance', function (): void {
|
||||
$run = OperationRun::factory()->create([
|
||||
'type' => 'inventory_sync',
|
||||
'status' => 'running',
|
||||
'outcome' => 'pending',
|
||||
'summary_counts' => [
|
||||
'total' => 10,
|
||||
'processed' => 4,
|
||||
],
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
$progress = OperationRunProgressContract::forRun($run);
|
||||
|
||||
expect($progress['capability'])->toBe('counted')
|
||||
->and($progress['display'])->toBe('indeterminate')
|
||||
->and($progress['label'])->toBe('Past the lifecycle window. Review worker health and logs before retrying.')
|
||||
->and($progress['processed'])->toBeNull()
|
||||
->and($progress['total'])->toBeNull()
|
||||
->and($progress['percent'])->toBeNull();
|
||||
});
|
||||
|
||||
it('does not overclaim stale lifecycle guidance for unsupported running types', function (): void {
|
||||
$run = OperationRun::factory()->create([
|
||||
'type' => 'unsupported.operation',
|
||||
'status' => 'running',
|
||||
'outcome' => 'pending',
|
||||
'summary_counts' => [
|
||||
'total' => 10,
|
||||
'processed' => 4,
|
||||
],
|
||||
'started_at' => now()->subWeeks(2),
|
||||
'created_at' => now()->subWeeks(2),
|
||||
]);
|
||||
|
||||
$progress = OperationRunProgressContract::forRun($run);
|
||||
|
||||
expect($progress['capability'])->toBe('counted')
|
||||
->and($progress['display'])->toBe('counted')
|
||||
->and($progress['label'])->toBe('4 / 10 processed (40%)')
|
||||
->and($progress['percent'])->toBe(40);
|
||||
});
|
||||
|
||||
it('classifies repo-real baseline evidence capture runs as phased fallback', function (): void {
|
||||
$run = OperationRun::factory()->create([
|
||||
'type' => 'baseline_capture',
|
||||
|
||||
@ -0,0 +1,64 @@
|
||||
# Requirements Checklist: Spec 358 - OperationRun Queue Truth Foundation
|
||||
|
||||
**Purpose**: Preparation analysis for Spec 358 readiness
|
||||
**Created**: 2026-06-06
|
||||
**Feature**: `specs/358-operationrun-queue-truth-foundation/spec.md`
|
||||
|
||||
## Candidate Selection And Guardrails
|
||||
|
||||
- [x] CHK001 The candidate source is explicit: direct user-provided draft plus repo-verified current code truth.
|
||||
- [x] CHK002 No `specs/358-*` package existed before this prep.
|
||||
- [x] CHK003 Related historical specs are treated as context only: 149, 160, 233, 268, 270, and 272.
|
||||
- [x] CHK004 The selected slice is narrow and reviewable: generic queue-truth alignment only, with no new persistence or adapter framework.
|
||||
- [x] CHK005 Repo-truth deviations from the user draft are recorded in `spec.md`.
|
||||
|
||||
## Required Prep Artifacts
|
||||
|
||||
- [x] CHK006 `spec.md` exists and contains no template placeholders.
|
||||
- [x] CHK007 `plan.md` exists and is repo-aware.
|
||||
- [x] CHK008 `tasks.md` exists and is ordered, small, and verifiable.
|
||||
- [x] CHK009 This checklist exists.
|
||||
|
||||
## Spec Quality
|
||||
|
||||
- [x] CHK010 Spec Candidate Check is completed.
|
||||
- [x] CHK011 The spec keeps `OperationRun` truth derived-only and does not introduce new persisted lifecycle state.
|
||||
- [x] CHK012 The spec clearly separates current repo reconciliation truth from the user's broader future-framework wording.
|
||||
- [x] CHK013 The spec states clear goals, non-goals, requirements, risks, and acceptance criteria.
|
||||
- [x] CHK014 The proportionality review rejects a new queue-health subsystem or adapter registry by default.
|
||||
|
||||
## Plan / Task Alignment
|
||||
|
||||
- [x] CHK015 The plan identifies the actual repo surfaces likely to change.
|
||||
- [x] CHK016 The plan explicitly preserves existing scheduled and adapter reconciliation commands as out-of-scope context.
|
||||
- [x] CHK017 The plan keeps Filament v5 / Livewire v4 posture and provider-registration location visible.
|
||||
- [x] CHK018 The tasks include tests-first work and explicit runtime validation commands.
|
||||
- [x] CHK019 The task list keeps scope bounded and includes anti-creep guardrails against persistence or framework expansion.
|
||||
|
||||
## UI / Monitoring Coverage
|
||||
|
||||
- [x] CHK020 UI Surface Impact is completed and does not claim no-impact.
|
||||
- [x] CHK021 The changed surfaces are correctly classified as existing monitoring family follow-through, not a new strategic customer surface.
|
||||
- [x] CHK022 No new page-report identity or route-inventory expansion is required for this bounded monitoring-family wording fix.
|
||||
- [x] CHK023 Audience-aware disclosure and no-overclaim wording boundaries are explicit.
|
||||
|
||||
## Test Governance
|
||||
|
||||
- [x] CHK024 The declared test families are the narrowest honest proof: Unit plus focused Feature.
|
||||
- [x] CHK025 No broad new browser or heavy-governance family is introduced.
|
||||
- [x] CHK026 Planned validation commands are explicit and file-scoped.
|
||||
|
||||
## Readiness Gate Outcome
|
||||
|
||||
- [x] CHK027 Candidate Selection Gate passes.
|
||||
- [x] CHK028 Spec Readiness Gate passes.
|
||||
- [x] CHK029 Runtime implementation has not started in this preparation step.
|
||||
- [x] CHK030 Recommended next step is implementation, not more prep.
|
||||
|
||||
## Review Outcome
|
||||
|
||||
- [x] Outcome class: acceptable-special-case
|
||||
- [x] Workflow outcome: keep
|
||||
- [x] Final note location: active feature PR close-out entry `Guardrail / Smoke Coverage`
|
||||
- [x] Preparation analyze result: pass via repo-based artifact review checklist; no standalone local `speckit.analyze` command was available in this repo surface
|
||||
- [x] Tooling note: the repo-generated branch number advanced to `1000`, and local `setup-plan.sh` rejected that 4-digit prefix; the final prep artifacts were therefore authored manually under the user-requested `358` package without changing application runtime code
|
||||
209
specs/358-operationrun-queue-truth-foundation/plan.md
Normal file
209
specs/358-operationrun-queue-truth-foundation/plan.md
Normal file
@ -0,0 +1,209 @@
|
||||
# Implementation Plan: OperationRun Queue Truth Foundation
|
||||
|
||||
**Branch**: `358-operationrun-queue-truth-foundation` | **Date**: 2026-06-06 | **Spec**: `specs/358-operationrun-queue-truth-foundation/spec.md`
|
||||
**Input**: Feature specification from `specs/358-operationrun-queue-truth-foundation/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Implement a narrow follow-up that aligns generic `OperationRun` queue truth across the existing lifecycle, freshness, progress, and monitoring-detail helpers. The slice must reuse current `OperationRun` persistence, current scheduled and adapter reconciliation behavior, current run links, and current authorization boundaries while removing contradictory active-work wording from shell, list, and canonical detail surfaces.
|
||||
|
||||
The repo already contains the foundations from Specs 149, 160, 233, 268, 270, and 272. This plan must not reopen those packages or invent a new reconciliation framework. It only corrects the remaining generic truth gap in current runtime code.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: PHP 8.4.15, Laravel 12.52, Filament 5.2.1, Livewire 4.1.4
|
||||
**Primary Dependencies**: existing `OperationRun` monitoring helpers, `OperationRunProgressContract`, `RunDurationInsights`, `OperationUxPresenter`, current Filament Monitoring pages and shared `OperationRun` implementation seams, Pest 4.3
|
||||
**Storage**: PostgreSQL `operation_runs`; no schema change planned
|
||||
**Testing**: Pest Unit + Feature
|
||||
**Validation Lanes**: fast-feedback, confidence
|
||||
**Target Platform**: Laravel monolith in `apps/platform`
|
||||
**Project Type**: web application
|
||||
**Performance Goals**: keep current polling/query posture unchanged; no new queries or background work required for render-time truth
|
||||
**Constraints**: no new `OperationRun` status/outcome fields, no new adapter registry, no new queue/job commands, no Graph calls, no notification policy change
|
||||
**Scale/Scope**: one shared derived truth path plus current shell/list/detail monitoring surfaces and focused regression coverage
|
||||
|
||||
## Likely Affected Repo Surfaces
|
||||
|
||||
- `apps/platform/app/Support/OpsUx/OperationRunProgressContract.php`
|
||||
- `apps/platform/app/Support/OpsUx/RunDurationInsights.php`
|
||||
- `apps/platform/app/Support/OpsUx/OperationUxPresenter.php`
|
||||
- `apps/platform/app/Support/Operations/OperationRunFreshnessState.php` only if the current derivation needs a small bounded clarification
|
||||
- `apps/platform/resources/views/livewire/bulk-operation-progress.blade.php`
|
||||
- `apps/platform/app/Filament/Pages/Monitoring/Operations.php` as the current route-backed Operations hub surface
|
||||
- `apps/platform/resources/views/filament/pages/monitoring/operations.blade.php` only if current workbench/list wording needs a bounded adjustment
|
||||
- `apps/platform/app/Filament/Resources/OperationRunResource.php` only as the reused table/detail payload seam, not as a route-backed surface
|
||||
- `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php`
|
||||
- focused tests under:
|
||||
- `apps/platform/tests/Unit/Support/OpsUx/`
|
||||
- `apps/platform/tests/Feature/OpsUx/`
|
||||
- `apps/platform/tests/Feature/Monitoring/`
|
||||
- `apps/platform/tests/Feature/Filament/`
|
||||
- `apps/platform/tests/Feature/Operations/`
|
||||
|
||||
## UI / Surface Guardrail Plan
|
||||
|
||||
- **Guardrail scope**: existing operator-facing monitoring surfaces only
|
||||
- **Affected routes/pages/actions/states/navigation/panel/provider surfaces**:
|
||||
- tenant shell activity hint
|
||||
- `/admin/workspaces/{workspace}/operations` via `App\Filament\Pages\Monitoring\Operations`
|
||||
- `/admin/workspaces/{workspace}/operations/{run}` via `App\Filament\Pages\Operations\TenantlessOperationRunViewer`
|
||||
- **No-impact class, if applicable**: N/A
|
||||
- **Native vs custom classification summary**: native Filament surfaces plus one existing Livewire shell host
|
||||
- **Shared-family relevance**: monitoring-state messaging, queue guidance, active-run hinting
|
||||
- **State layers in scope**: shell, page, detail
|
||||
- **Audience modes in scope**: operator-MSP, support-platform
|
||||
- **Decision/diagnostic/raw hierarchy plan**: decision-first lifecycle truth before diagnostics; raw context remains secondary
|
||||
- **Raw/support gating plan**: unchanged; raw/support evidence stays on current detail-only surfaces
|
||||
- **One-primary-action / duplicate-truth control**: no new primary actions; existing row open and current shell open links remain authoritative
|
||||
- **Handling modes by drift class or surface**:
|
||||
- contradictory stale vs queue guidance is `hard-stop-candidate`
|
||||
- wording-only cleanup with no behavioral effect is `review-mandatory`
|
||||
- **Repository-signal treatment**: review-mandatory
|
||||
- **Special surface test profiles**: monitoring-state-page, shared-detail-family
|
||||
- **Required tests or manual smoke**: focused unit and feature proof only
|
||||
- **Exception path and spread control**: any move toward new persistence, new adapter abstractions, or new queue-health orchestration is `reject-or-split`
|
||||
- **Active feature PR close-out entry**: Guardrail / Smoke Coverage
|
||||
- **Navigation / Filament provider-panel handling**: unchanged; no provider registration or panel path work
|
||||
|
||||
## Shared Pattern & System Fit
|
||||
|
||||
- **Cross-cutting feature marker**: yes
|
||||
- **Systems touched**: lifecycle freshness, progress derivation, queue guidance, shell render, canonical monitoring detail
|
||||
- **Shared abstractions reused**:
|
||||
- `OperationRunFreshnessState`
|
||||
- `OperationRunProgressContract`
|
||||
- `RunDurationInsights`
|
||||
- `OperationUxPresenter`
|
||||
- existing `OperationRunLinks` and detail routes
|
||||
- **New abstraction introduced? why?**: none by default; if one tiny queue-truth helper is required, it must stay inside `App\Support\OpsUx` and remain a derivation helper rather than a strategy or adapter registry
|
||||
- **Why the existing abstraction was sufficient or insufficient**: the repo already centralizes the right ingredients, but the final operator wording remains split across helpers and render sites
|
||||
- **Bounded deviation / spread control**: do not create a new framework layer where extending the current presenter or progress contract is sufficient
|
||||
|
||||
## OperationRun UX Impact
|
||||
|
||||
- **Touches OperationRun start/completion/link UX?**: yes, reuse-only
|
||||
- **Central contract reused**: current shared run-link, queued-toast, terminal-notification, and detail-route contract
|
||||
- **Delegated UX behaviors**: queued toast wording, run links, browser events, and terminal notifications remain unchanged
|
||||
- **Surface-owned behavior kept local**: only density and render ordering for shell/list/detail
|
||||
- **Queued DB-notification policy**: unchanged
|
||||
- **Terminal notification path**: unchanged
|
||||
- **Exception path**: none
|
||||
|
||||
## Provider Boundary & Portability Fit
|
||||
|
||||
- **Shared provider/platform boundary touched?**: no
|
||||
- **Provider-owned seams**: N/A
|
||||
- **Platform-core seams**: platform-owned `OperationRun` monitoring truth only
|
||||
- **Neutral platform terms / contracts preserved**: queued, running, lifecycle window, active operation, review worker health
|
||||
- **Retained provider-specific semantics and why**: none
|
||||
- **Bounded extraction or follow-up path**: none
|
||||
|
||||
## Constitution Check
|
||||
|
||||
- Inventory-first: unchanged; no new source of truth.
|
||||
- Read/write separation: unchanged; this is read-only monitoring truth.
|
||||
- Deterministic capabilities: unchanged.
|
||||
- Proportionality / anti-bloat: pass only if the implementation extends current helpers instead of creating a new framework.
|
||||
- Persisted truth: no new tables, entities, or status families.
|
||||
- State discipline: derived-only; no new persisted lifecycle category.
|
||||
- Shared pattern first: extend existing Ops UX helpers and current monitoring surfaces.
|
||||
- Workspace / tenant isolation: unchanged and still mandatory.
|
||||
- RBAC-UX: no client-side authorization expansion; server-side boundaries remain the only access control.
|
||||
- Test governance: unit + feature remain the narrowest honest proof.
|
||||
- Filament v5 / Livewire v4 posture: unchanged; no provider-registration or asset change.
|
||||
|
||||
## Test Governance Check
|
||||
|
||||
- **Test purpose / classification by changed surface**:
|
||||
- Unit: generic queue-truth derivation
|
||||
- Feature: current shell/list/detail rendering and authorization-safe presentation
|
||||
- **Affected validation lanes**: fast-feedback, confidence
|
||||
- **Why this lane mix is the narrowest sufficient proof**: the slice corrects shared derivation and rendered copy on current surfaces without introducing JS-heavy or browser-only behavior
|
||||
- **Narrowest proving command(s)**:
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php`
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php tests/Feature/MonitoringOperationsTest.php tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php tests/Feature/Monitoring/MonitoringOperationsTest.php tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php`
|
||||
- `git diff --check`
|
||||
- **Fixture / helper / factory / seed / context cost risks**: existing `OperationRun` fixtures and workspace membership helpers are sufficient; avoid widening defaults
|
||||
- **Expensive defaults or shared helper growth introduced?**: none planned
|
||||
- **Heavy-family additions, promotions, or visibility changes**: none
|
||||
- **Surface-class relief / special coverage rule**: monitoring-state-page and shared-detail-family coverage is explicit
|
||||
- **Closing validation and reviewer handoff**: reviewers should compare stale-active, fresh-active, and reconciled-terminal cases across shell, list, and detail and confirm that stale-active wording does not coexist with ordinary queue/progress reassurance
|
||||
- **Budget / baseline / trend follow-up**: none expected
|
||||
- **Review-stop questions**: Did the change add persistence? Did it invent a new adapter framework? Does any stale-active surface still combine stale attention with ordinary `Waiting for worker.`, `Progress details pending.`, or equivalent calm queue/progress reassurance?
|
||||
- **Escalation path**: `reject-or-split` if scope widens into queue runtime redesign
|
||||
- **Active feature PR close-out entry**: Guardrail / Smoke Coverage
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 1 — Confirm the shared queue-truth inputs
|
||||
|
||||
- Inventory the exact contradictory paths in:
|
||||
- `OperationRunProgressContract`
|
||||
- `RunDurationInsights`
|
||||
- `OperationUxPresenter`
|
||||
- current shell/list/detail renderers
|
||||
- Freeze the current shared vocabulary for:
|
||||
- fresh queued/running
|
||||
- stale active
|
||||
- reconciled terminal
|
||||
- Confirm that existing scheduled and adapter reconciliation commands remain context only
|
||||
|
||||
### Phase 2 — Align the generic queue-truth derivation
|
||||
|
||||
- Extend existing helpers so freshness, queue guidance, and progress availability tell one story
|
||||
- Keep phase/composite/determinate progress derived-only and subordinate to stale lifecycle truth
|
||||
- Ensure unsupported or low-evidence cases remain cautious rather than overclaiming orphaned state
|
||||
|
||||
### Phase 3 — Retrofit current monitoring surfaces
|
||||
|
||||
- Update the current shell banner so stale active work does not read as ordinary queue wait
|
||||
- Update canonical list/detail guidance so they confirm the same lifecycle meaning
|
||||
- Keep existing links, filters, and diagnostics structure unchanged
|
||||
|
||||
### Phase 4 — Validate and lock the contract
|
||||
|
||||
- Add or update targeted unit + feature tests
|
||||
- Run the focused validation commands
|
||||
- Record any remaining bounded wording choices in the active feature close-out
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
- **Over-escalation of healthy work**: mitigate with explicit fresh-active negative assertions.
|
||||
- **Residual renderer drift**: mitigate by covering shell, list, and detail in the same test set.
|
||||
- **Accidental framework expansion**: mitigate by rejecting new registries, adapters, or persisted truth in review.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation
|
||||
|
||||
```text
|
||||
specs/358-operationrun-queue-truth-foundation/
|
||||
|-- spec.md
|
||||
|-- plan.md
|
||||
|-- tasks.md
|
||||
`-- checklists/
|
||||
`-- requirements.md
|
||||
```
|
||||
|
||||
### Runtime surfaces likely to change later
|
||||
|
||||
```text
|
||||
apps/platform/app/Support/OpsUx/
|
||||
|-- OperationRunProgressContract.php
|
||||
|-- OperationUxPresenter.php
|
||||
`-- RunDurationInsights.php
|
||||
|
||||
apps/platform/app/Support/Operations/
|
||||
`-- OperationRunFreshnessState.php
|
||||
|
||||
apps/platform/app/Filament/
|
||||
|-- Pages/Monitoring/Operations.php
|
||||
|-- Pages/Operations/TenantlessOperationRunViewer.php
|
||||
`-- Resources/OperationRunResource.php
|
||||
|
||||
apps/platform/resources/views/filament/pages/monitoring/
|
||||
`-- operations.blade.php
|
||||
|
||||
apps/platform/resources/views/livewire/
|
||||
`-- bulk-operation-progress.blade.php
|
||||
```
|
||||
298
specs/358-operationrun-queue-truth-foundation/spec.md
Normal file
298
specs/358-operationrun-queue-truth-foundation/spec.md
Normal file
@ -0,0 +1,298 @@
|
||||
# Feature Specification: OperationRun Queue Truth Foundation
|
||||
|
||||
**Feature Branch**: `358-operationrun-queue-truth-foundation`
|
||||
**Created**: 2026-06-06
|
||||
**Status**: Draft
|
||||
**Input**: User-provided OperationRun queue-truth draft, reconciled against current repo truth
|
||||
|
||||
## Spec Candidate Check *(mandatory — SPEC-GATE-001)*
|
||||
|
||||
- **Problem**: Current `OperationRun` UX still allows contradictory generic queue truth. The repo can show stale-attention semantics and ordinary queue/progress reassurance for the same active run, depending on which helper or surface renders first.
|
||||
- **Today's failure**: A run can surface `Likely stale` lifecycle attention while generic copy still says `Waiting for worker.` or `No action needed yet. The operation is waiting for a worker.` This creates a false-calm operator message on active monitoring surfaces.
|
||||
- **User-visible improvement**: Queued and running `OperationRun` records will render one honest generic lifecycle story across shell hints, monitoring rows, and canonical detail without overclaiming domain success or orphaned queue state.
|
||||
- **Smallest enterprise-capable version**: Align the existing generic lifecycle helpers (`OperationRunFreshnessState`, `OperationRunProgressContract`, `RunDurationInsights`, and `OperationUxPresenter`) and apply the resulting truth to current monitoring and shell surfaces only.
|
||||
- **Explicit non-goals**: No new persisted `OperationRun` statuses or outcomes, no queue schema redesign, no new worker-health subsystem, no new notification family, no new adapter framework, no new domain-success reconciliation, no restore/review/backup auto-completion logic, and no destructive action changes.
|
||||
- **Permanent complexity imported**: One bounded derived queue-truth path over existing `OperationRun` state, plus focused tests that lock the contract across shared monitoring surfaces.
|
||||
- **Why now**: Historical specs already improved stale visibility and shared progress, but current repo truth still contains contradictory generic wording. This is an active operator-trust gap in currently shipped runtime paths.
|
||||
- **Why not local**: Fixing only one Blade view, one banner, or one detail card would leave the contradiction between progress, freshness, and queue-guidance helpers intact.
|
||||
- **Approval class**: Core Enterprise
|
||||
- **Red flags triggered**: shared interaction family, monitoring-state semantics, and possible helper consolidation. Defense: the slice remains derived-only, persistence-free, and narrower than a new reconciliation framework.
|
||||
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexität: 1 | Produktnähe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12**
|
||||
- **Decision**: approve
|
||||
|
||||
## Repo Truth Reconciliation
|
||||
|
||||
The user draft is directionally correct, but current repo truth changes the exact framing:
|
||||
|
||||
1. The repo helper advanced to `1000` because `specs/999-seeder-external-id/` already exists, but this package is intentionally finalized as user-requested Spec `358`.
|
||||
2. Generic stale reconciliation already exists via `TenantpilotReconcileOperationRuns` and `OperationLifecycleReconciler`; adapter-backed reconciliation already exists via `OpsReconcileAdapterRuns` and `AdapterRunReconciler`.
|
||||
3. A shared progress helper already exists via `OperationRunProgressContract`, and Spec 272 already extended its active-progress precedence for phased/composite truth; this spec aligns stale/fresh queue guidance with that current shared contract instead of inventing a new one.
|
||||
4. The current canonical Monitoring routes are workspace-scoped: `/admin/workspaces/{workspace}/operations` and `/admin/workspaces/{workspace}/operations/{run}`. `OperationRunResource` remains an implementation seam, not a route-backed surface.
|
||||
5. No follow-up `Spec 359` promise is recorded here. Any later adapter or domain-success work must be promoted from fresh repo truth rather than pre-allocating a speculative framework sequence.
|
||||
|
||||
## Spec Scope Fields *(mandatory)*
|
||||
|
||||
- **Scope**: workspace, tenant, canonical-view
|
||||
- **Primary Routes**:
|
||||
- workspace-admin surfaces that host the shared shell activity hint while an entitled managed environment is active, including current `/admin/workspaces/{workspace}/environments/{environment}/...` starts
|
||||
- `/admin/workspaces/{workspace}/operations`
|
||||
- `/admin/workspaces/{workspace}/operations/{run}`
|
||||
- the current canonical Monitoring surfaces `App\Filament\Pages\Monitoring\Operations` and `App\Filament\Pages\Operations\TenantlessOperationRunViewer`
|
||||
- **Data Ownership**:
|
||||
- `operation_runs` remain the only lifecycle and freshness source of truth
|
||||
- `OperationRun` context remains the only place where legitimacy or reconciliation evidence may already exist
|
||||
- no new persisted queue-truth mirror, no new audit artifact, and no new worker-health record are introduced
|
||||
- **RBAC**:
|
||||
- existing workspace and managed-environment entitlement rules remain authoritative
|
||||
- non-members and out-of-scope actors remain `404`
|
||||
- this spec changes wording and derived presentation only; it does not widen access or add new capability strings
|
||||
|
||||
For canonical-view specs, the spec MUST define:
|
||||
|
||||
- **Default filter behavior when tenant-context is active**: Existing environment-prefilter and page-state behavior on `/admin/workspaces/{workspace}/operations` remain unchanged. This spec changes queue truth, not monitoring state ownership.
|
||||
- **Explicit entitlement checks preventing cross-tenant leakage**: Derived stale/queued guidance must use only runs the actor is already authorized to view. No new wording may reveal hidden tenant scope or hidden run existence.
|
||||
|
||||
## UI Surface Impact *(mandatory — UI-COV-001)*
|
||||
|
||||
- [ ] No UI surface impact
|
||||
- [x] Existing page changed
|
||||
- [ ] New page/route added
|
||||
- [ ] Navigation changed
|
||||
- [ ] Filament panel/provider surface changed
|
||||
- [ ] New modal/drawer/wizard/action added
|
||||
- [ ] New table/form/state added
|
||||
- [ ] Customer-facing surface changed
|
||||
- [ ] Dangerous action changed
|
||||
- [x] Status/evidence/review presentation changed
|
||||
- [x] Workspace/environment context presentation changed
|
||||
|
||||
## UI/Productization Coverage *(mandatory when UI Surface Impact is not "No UI surface impact")*
|
||||
|
||||
- **Route/page/surface**:
|
||||
- tenant shell activity hint (`BulkOperationProgress`)
|
||||
- workspace operations hub (`App\Filament\Pages\Monitoring\Operations`) at `/admin/workspaces/{workspace}/operations`
|
||||
- canonical operation detail (`App\Filament\Pages\Operations\TenantlessOperationRunViewer`) at `/admin/workspaces/{workspace}/operations/{run}`
|
||||
- shared implementation seam only: `App\Filament\Resources\OperationRunResource` for the reused table/detail payload contract
|
||||
- **Current or new page archetype**: existing monitoring/workbench family only
|
||||
- **Design depth**: Domain Pattern Surface
|
||||
- **Repo-truth level**: repo-verified
|
||||
- **Existing pattern reused**: current `OperationRun` monitoring family, current shell activity hint, current monitoring detail banners
|
||||
- **New pattern required**: none; this is a truth-alignment follow-up within the existing pattern family
|
||||
- **Screenshot required**: no; the slice corrects shared wording/derived truth inside existing monitoring surfaces
|
||||
- **Page audit required**: no new page-report identity; existing monitoring family coverage is sufficient
|
||||
- **Customer-safe review required**: no; operator-only monitoring surfaces
|
||||
- **Dangerous-action review required**: no; no action hierarchy or destructive behavior changes
|
||||
- **Coverage files updated or explicitly not needed**: no new coverage file is required because the existing monitoring-family anchors already cover the touched reachable surfaces: `docs/ui-ux-enterprise-audit/route-inventory.md` (`UI-016`, `UI-017`), `docs/ui-ux-enterprise-audit/page-reports/ui-003-operations.md`, `docs/ui-ux-enterprise-audit/strategic-surfaces.md`, and `specs/313-workspace-environment-context-browser-verification/surface-inventory.md`
|
||||
- **No-impact rationale when applicable**: N/A
|
||||
|
||||
## Cross-Cutting / Shared Pattern Reuse *(mandatory)*
|
||||
|
||||
- **Cross-cutting feature?**: yes
|
||||
- **Interaction class(es)**: status messaging, queue/progress guidance, monitoring detail guidance, shell active-work hint
|
||||
- **Systems touched**:
|
||||
- `OperationRunFreshnessState`
|
||||
- `OperationRunProgressContract`
|
||||
- `RunDurationInsights`
|
||||
- `OperationUxPresenter`
|
||||
- current shell banner and canonical monitoring/detail surfaces
|
||||
- **Existing pattern(s) to extend**: current lifecycle policy, freshness derivation, the Spec-270/272 shared progress contract, monitoring detail banner, and shared run-link/presenter paths
|
||||
- **Shared contract / presenter / builder / renderer to reuse**: `OperationRunProgressContract`, `OperationUxPresenter`, `OperationRunFreshnessState`, and current monitoring/view renderer paths
|
||||
- **Why the existing shared path is sufficient or insufficient**: The repo already has the right ownership boundaries, but the generic queue truth is split across multiple helpers that can disagree in wording and emphasis.
|
||||
- **Allowed deviation and why**: none by default; if one tiny helper is required to keep existing presenters reviewable, it must remain local to current Ops UX truth and must not become an adapter registry
|
||||
- **Consistency impact**: queued/running/stale wording, progress availability, lifecycle attention, and detail guidance must remain aligned across shell, list, and canonical detail
|
||||
- **Review focus**: no surface may reassure with ordinary queue copy after freshness has already escalated the same active run to stale attention
|
||||
|
||||
## OperationRun UX Impact *(mandatory)*
|
||||
|
||||
- **Touches OperationRun start/completion/link UX?**: yes, reuse-only
|
||||
- **Shared OperationRun UX contract/layer reused**: current `OperationRun` link, freshness, presenter, and progress helper paths
|
||||
- **Delegated start/completion UX behaviors**: existing queued toasts, canonical run links, browser events, and terminal notification paths remain unchanged
|
||||
- **Local surface-owned behavior that remains**: density and placement only
|
||||
- **Queued DB-notification policy**: 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`, `queued`, `running`, `lifecycle window`, `review worker health`
|
||||
- **Provider-specific semantics retained and why**: none
|
||||
- **Why this does not deepen provider coupling accidentally**: the slice works entirely on platform-owned `OperationRun` truth and existing shared monitoring helpers
|
||||
- **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 | yes | Native Filament + existing Livewire view | shared active-work hint family | shell, page | no | no new action or route |
|
||||
| Operations list / resource detail summary | yes | Native Filament resource/detail | shared monitoring family | page, detail | no | wording-only inside existing collection/detail |
|
||||
| Canonical tenantless run viewer banners | yes | Native Filament page | shared monitoring detail family | detail | no | no new diagnostics section family |
|
||||
|
||||
## 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 | Secondary Context Surface | Decide whether an active run needs inspection now | active-state truth, one open link, honest progress availability | full diagnostics stay on canonical monitoring/detail pages | secondary because it supports ongoing work rather than owning diagnostics | follows existing start-surface workflow | removes false calmness |
|
||||
| Operations list | Primary Decision Surface | Decide which active run needs inspection first | lifecycle attention, queue truth, scope, and run identity | full detail after drill-through | primary because it is the canonical monitoring queue | aligns with current monitoring triage | removes open-every-row guesswork |
|
||||
| Canonical run detail | Tertiary Evidence / Diagnostics Surface | Confirm what the stale or active state really means | one honest lifecycle explanation before deep diagnostics | raw context, history, evidence, and related links | tertiary because inspection already happened | preserves current detail role | removes banner/guidance contradiction |
|
||||
|
||||
## 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 | active-state summary plus one open action | minimal guidance only | raw/support data stays off-surface | `View operation` or current collective review action | raw detail stays on canonical monitoring surfaces | do not repeat stale explanation multiple ways |
|
||||
| Operations list | operator-MSP | row-level stale/queued truth and scope | detail remains secondary | raw payloads stay on detail | row open | raw and related evidence stay on detail | one row summary, not multiple competing summaries |
|
||||
| Canonical run detail | operator-MSP, support-platform | one honest lifecycle banner | diagnostics sections below the banner | raw context remains lower-priority | existing return/open actions | support/raw detail remains secondary to the top summary | lifecycle banner and queue guidance must not disagree |
|
||||
|
||||
## 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 active run if guidance escalates | explicit open link | forbidden | existing shell secondary actions only | none | `/admin/workspaces/{workspace}/operations` with current contextual prefilter rules | `/admin/workspaces/{workspace}/operations/{run}` | current tenant/workspace shell context | Operation | whether the run is ordinary active work or already stale | none |
|
||||
| Operations list | List / Table / Monitoring | Read-only monitoring registry | Open the run that needs follow-up | full-row open | required | existing table controls only | none | `/admin/workspaces/{workspace}/operations` | `/admin/workspaces/{workspace}/operations/{run}` | workspace scope and current filters | Operation run | honest queued/running lifecycle truth | none |
|
||||
| Canonical run detail | Record / Detail / Monitoring | Diagnostics-first detail surface | Inspect lifecycle truth before deeper diagnosis | canonical detail page | N/A | existing header/related links only | none | `/admin/workspaces/{workspace}/operations` | `/admin/workspaces/{workspace}/operations/{run}` | workspace scope plus entitled tenant context | Operation run | honest active-state explanation | 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 to inspect active work now | shell hint | Is this active work still ordinary, or is it already stale? | label, status, progress availability, open link | deep diagnostics remain elsewhere | lifecycle, freshness, progress availability | none | current open/review action | none |
|
||||
| Operations list | workspace operator | Prioritize which active run to inspect | monitoring registry | Which run is actually waiting, progressing, or already stale? | row summary, scope, lifecycle attention | raw payloads on detail | lifecycle, freshness, queue truth | none | open row | none |
|
||||
| Canonical run detail | workspace operator | Confirm generic lifecycle truth before diagnosing | detail surface | Why does this run read as stale or still-active? | one lifecycle banner and queue guidance | raw context, failure payloads, related artifacts | lifecycle, freshness, legitimacy evidence | none | existing navigation and related links | none |
|
||||
|
||||
## Proportionality Review *(mandatory when structural complexity is introduced)*
|
||||
|
||||
- **New source of truth?**: no
|
||||
- **New persisted entity/table/artifact?**: no
|
||||
- **New abstraction?**: no by default; prefer extending existing helpers
|
||||
- **New enum/state/reason family?**: no
|
||||
- **New cross-domain UI framework/taxonomy?**: no
|
||||
- **Current operator problem**: current generic queue truth can contradict itself across existing shared monitoring surfaces
|
||||
- **Existing structure is insufficient because**: lifecycle, freshness, progress, and queue guidance currently live in separate helpers that can drift in wording and emphasis
|
||||
- **Narrowest correct implementation**: align existing shared helpers and current monitoring renderers without adding a new framework, status family, or persistence layer
|
||||
- **Ownership cost**: bounded shared-helper review burden plus focused tests
|
||||
- **Alternative intentionally rejected**: a new reconciliation framework or queue-health persistence layer was rejected because the current issue is derived wording drift, not missing stored truth
|
||||
- **Release truth**: current-release operator-trust correction
|
||||
|
||||
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
|
||||
|
||||
- **Test purpose / classification**: Unit + Feature
|
||||
- **Validation lane(s)**: fast-feedback, confidence
|
||||
- **Why this classification and these lanes are sufficient**: the feature changes derived queue truth and current rendered monitoring output; focused unit and feature proof are sufficient without a new browser or heavy-governance lane
|
||||
- **New or expanded test families**:
|
||||
- `apps/platform/tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php`
|
||||
- `apps/platform/tests/Unit/Support/OpsUx/RunDurationInsightsTest.php` or adjacent focused unit coverage if the repo keeps these assertions in an existing file
|
||||
- `apps/platform/tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php`
|
||||
- `apps/platform/tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php`
|
||||
- `apps/platform/tests/Feature/MonitoringOperationsTest.php`
|
||||
- `apps/platform/tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php`
|
||||
- `apps/platform/tests/Feature/Monitoring/MonitoringOperationsTest.php`
|
||||
- `apps/platform/tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php`
|
||||
- `apps/platform/tests/Feature/Operations/TenantlessOperationRunViewerTest.php`
|
||||
- **Fixture / helper cost impact**: low to moderate; reuse existing `OperationRun` factories, workspace membership helpers, and monitoring fixtures only
|
||||
- **Heavy-family visibility / justification**: none
|
||||
- **Special surface test profile**: monitoring-state-page plus shared-detail-family
|
||||
- **Standard-native relief or required special coverage**: feature coverage is sufficient; no browser smoke is required unless implementation later proves a render-only regression that unit/feature coverage cannot catch
|
||||
- **Reviewer handoff**: reviewers must confirm the same stale run no longer mixes stale attention with ordinary queue/progress reassurance on shell, list, and detail surfaces
|
||||
- **Budget / baseline / trend impact**: none expected beyond small feature-local coverage growth
|
||||
- **Escalation needed**: `reject-or-split` if the slice expands into new persistence, adapter orchestration, or queue runtime redesign
|
||||
- **Active feature PR close-out entry**: Guardrail / Smoke Coverage
|
||||
- **Planned validation commands**:
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php`
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php tests/Feature/MonitoringOperationsTest.php tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php tests/Feature/Monitoring/MonitoringOperationsTest.php tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php`
|
||||
- `git diff --check`
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - See honest queued and stale truth on active monitoring surfaces (Priority: P1)
|
||||
|
||||
As an operator, I need active monitoring surfaces to distinguish normal queued/running work from stale lifecycle drift without mixing calm queue reassurance into the same state.
|
||||
|
||||
**Why this priority**: This is the direct trust gap visible in current runtime surfaces.
|
||||
|
||||
**Independent Test**: Seed fresh and stale queued/running runs, open the shell activity hint and monitoring list, and verify that stale-active runs do not mix stale attention with ordinary queue or progress reassurance.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a queued or running run is past its lifecycle window, **When** the shell or monitoring list renders it, **Then** the visible stale-active state does not also reassure with ordinary `Waiting for worker.`, `Progress details pending.`, or equivalent calm queue/progress copy.
|
||||
2. **Given** a fresh queued or running run is still within its lifecycle window, **When** the same surfaces render it, **Then** the copy remains calm and does not falsely escalate it as stale.
|
||||
|
||||
### User Story 2 - Keep canonical run detail aligned with generic queue truth (Priority: P1)
|
||||
|
||||
As an operator, I need canonical run detail to confirm the same generic lifecycle meaning that list and shell surfaces already communicate before I inspect deeper diagnostics.
|
||||
|
||||
**Why this priority**: The current contradiction is most damaging when detail and compact surfaces disagree.
|
||||
|
||||
**Independent Test**: Open canonical detail for fresh, stale, and reconciled runs and verify that lifecycle banners, queue guidance, and top summary agree with the generic monitoring truth.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a stale queued or stale running run, **When** the detail page opens, **Then** the top summary explains that the run is past its lifecycle window before deeper diagnostics render.
|
||||
2. **Given** a reconciled terminal run, **When** the detail page opens, **Then** it preserves the existing reconciled truth and does not regress to ordinary active-work guidance.
|
||||
|
||||
### User Story 3 - Keep proof-backed reconciliation separate from generic queue truth (Priority: P2)
|
||||
|
||||
As a product owner, I need generic queue truth to stay separate from domain-success or adapter reconciliation so that stale monitoring copy does not silently become a business-completion engine.
|
||||
|
||||
**Why this priority**: The slice must stay narrow and avoid speculative framework growth.
|
||||
|
||||
**Independent Test**: Compare a stale generic run and an adapter-reconciled run, then verify that generic stale wording remains cautious while existing reconciled evidence paths still drive terminal truth where already implemented.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a stale active run without proof-backed queue legitimacy evidence, **When** the UI renders it, **Then** the wording stays at `past lifecycle window` or equivalent cautious language and does not claim orphaned or domain-complete truth.
|
||||
2. **Given** a run already completed through scheduled or adapter reconciliation, **When** the UI renders it, **Then** the existing reconciled terminal semantics remain intact and are not rewritten as generic queue waiting.
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- A queued run is stale but has no trustworthy worker correlation or job identifier.
|
||||
- A running run has determinate counts but is still past the lifecycle window.
|
||||
- A run is unsupported by current lifecycle policy and therefore should not receive stale overclaiming.
|
||||
- A run became terminal through `scheduled_reconciler` or `adapter_reconciler`.
|
||||
- A system-initiated run has no human initiator but still needs honest lifecycle wording.
|
||||
- Existing phase or composite progress hints exist in context and must not override stale lifecycle truth with false reassurance.
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
- **FR-358-001**: The system MUST keep `OperationRun.status` and `OperationRun.outcome` as the only persisted lifecycle fields for this slice.
|
||||
- **FR-358-002**: Generic queue truth MUST be derived from current lifecycle, freshness, and trusted context only.
|
||||
- **FR-358-003**: A run that is `likely_stale` by current lifecycle policy MUST NOT render ordinary queue or ordinary progress reassurance as visible stale-active guidance on shell, list, or canonical detail surfaces, whether that reassurance appears alone or alongside a stale label, banner, or attention cue.
|
||||
- **FR-358-004**: Fresh queued and fresh running runs MUST remain visibly calm and MUST NOT inherit stale emphasis.
|
||||
- **FR-358-005**: Existing scheduled or adapter reconciliation paths MUST remain authoritative for terminal truth where they already exist.
|
||||
- **FR-358-006**: This feature MUST NOT introduce a new queue-orphaned, reconciled, or business-success persistence family.
|
||||
- **FR-358-007**: This feature MUST NOT claim hard orphaned queue state unless current repo truth already provides trustworthy legitimacy evidence for that specific run.
|
||||
- **FR-358-008**: Existing `OperationRun` links, queued toasts, browser events, and terminal notification paths MUST remain unchanged.
|
||||
- **FR-358-009**: Existing workspace and tenant authorization boundaries MUST remain unchanged.
|
||||
- **FR-358-010**: The canonical monitoring list, shell hint, and canonical run detail MUST use one aligned generic lifecycle vocabulary for fresh queued/running work, stale active work, and reconciled terminal work.
|
||||
- **FR-358-011**: The implementation MUST stay within current helper ownership unless one small local helper is required to avoid unreadable presenter logic.
|
||||
- **FR-358-012**: Focused unit and feature coverage MUST prove both positive and negative cases for fresh vs stale queue truth.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
- **SC-358-001**: In focused regression coverage, stale queued/running runs no longer mix stale attention with ordinary queue/progress reassurance on the affected surfaces.
|
||||
- **SC-358-002**: In focused regression coverage, fresh queued/running runs remain calm and do not falsely escalate to stale.
|
||||
- **SC-358-003**: Existing reconciled terminal behavior remains intact for runs already completed by scheduled or adapter truth.
|
||||
- **SC-358-004**: No new persisted lifecycle field, queue artifact, or adapter framework is introduced.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The current contradiction is a derived UX-truth problem, not a missing persistence problem.
|
||||
- Existing lifecycle policy thresholds remain valid for this slice.
|
||||
- Existing generic and adapter reconciliation commands remain out of scope except as historical context the UI must respect.
|
||||
|
||||
## Risks
|
||||
|
||||
- **Over-correction**: the slice could make all active work sound problematic. Mitigation: explicit fresh vs stale negative assertions.
|
||||
- **Framework creep**: the slice could drift into a new generic queue-health subsystem. Mitigation: no new persistence, no new adapter registry, and explicit out-of-scope boundaries.
|
||||
- **Detail/list drift survives**: one renderer could remain on old wording. Mitigation: focused list + detail + shell coverage in the same package.
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Queue worker health automation
|
||||
- New scheduler, command, or job families
|
||||
- New `OperationRun` statuses or outcomes
|
||||
- Domain-success reconciliation for review, restore, backup, sync, or export runs
|
||||
- Adapter framework expansion
|
||||
- New Filament resources, routes, or destructive actions
|
||||
160
specs/358-operationrun-queue-truth-foundation/tasks.md
Normal file
160
specs/358-operationrun-queue-truth-foundation/tasks.md
Normal file
@ -0,0 +1,160 @@
|
||||
# Tasks: OperationRun Queue Truth Foundation
|
||||
|
||||
**Input**: `specs/358-operationrun-queue-truth-foundation/spec.md`, `plan.md`, and `checklists/requirements.md`
|
||||
**Prerequisites**: `spec.md` and `plan.md`
|
||||
**Tests**: REQUIRED (Pest). Keep proof bounded to one unit family plus focused monitoring/detail feature tests.
|
||||
**Operations**: No new `OperationRun` type, no new queue family, no new notification path, no new reconciliation command, and no new persisted lifecycle state.
|
||||
**RBAC**: Reuse current workspace and managed-environment authorization rules; presentation must never reveal unauthorized runs.
|
||||
**Shared Pattern Reuse**: Reuse `OperationRunProgressContract`, `RunDurationInsights`, `OperationUxPresenter`, `OperationRunFreshnessState`, and existing monitoring renderers. Do not introduce a new adapter registry or queue-health framework.
|
||||
**Filament / Panel Guardrails**: Filament remains v5 on Livewire v4. Provider registration stays in `apps/platform/bootstrap/providers.php`. No new panel, route family, or asset strategy is allowed.
|
||||
**Organization**: Tasks are grouped by user story so the generic derivation, surface retrofit, and follow-up guardrails remain independently reviewable.
|
||||
|
||||
## Test Governance Checklist
|
||||
|
||||
- [x] Lane assignment is named and is the narrowest sufficient proof for the changed behavior.
|
||||
- [x] New or changed tests stay in the smallest honest family, and no hidden browser or heavy-governance proof is introduced.
|
||||
- [x] Shared helpers, fixtures, and context defaults stay cheap by default.
|
||||
- [x] Planned validation commands cover the change without widening into unrelated lanes.
|
||||
- [x] The declared monitoring/detail surface test profile is explicit.
|
||||
- [x] Any material budget, baseline, or escalation note is recorded in the active feature close-out.
|
||||
|
||||
## Phase 1: Setup (Shared Truth Inventory)
|
||||
|
||||
**Purpose**: confirm the current contradiction and the exact shared helper boundaries before runtime edits begin.
|
||||
|
||||
- [x] T001 Review `specs/358-operationrun-queue-truth-foundation/spec.md`, `plan.md`, `checklists/requirements.md`, `.specify/memory/constitution.md`, `docs/ai-coding-rules.md`, `specs/149-queued-execution-reauthorization/spec.md`, `specs/160-operation-lifecycle-guarantees/spec.md`, `specs/233-stale-run-visibility/spec.md`, `specs/268-operationrun-activity-feedback/spec.md`, `specs/270-operationrun-progress-contract/spec.md`, and `specs/272-operationrun-phase-composite-progress/spec.md` together so the implementation stays on current repo truth.
|
||||
- [x] T002 [P] Confirm the current generic derivation seams in `apps/platform/app/Support/OpsUx/OperationRunProgressContract.php`, `apps/platform/app/Support/OpsUx/RunDurationInsights.php`, `apps/platform/app/Support/OpsUx/OperationUxPresenter.php`, and `apps/platform/app/Support/Operations/OperationRunFreshnessState.php`.
|
||||
- [x] T003 [P] Confirm the current render seams in `apps/platform/resources/views/livewire/bulk-operation-progress.blade.php`, `apps/platform/app/Filament/Pages/Monitoring/Operations.php`, `apps/platform/resources/views/filament/pages/monitoring/operations.blade.php`, `apps/platform/app/Filament/Resources/OperationRunResource.php` as the shared table/detail seam, and `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php`.
|
||||
- [x] T004 [P] Confirm the focused proof owners in `apps/platform/tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php`, `apps/platform/tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php`, `apps/platform/tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php`, `apps/platform/tests/Feature/MonitoringOperationsTest.php`, `apps/platform/tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php`, `apps/platform/tests/Feature/Monitoring/MonitoringOperationsTest.php`, `apps/platform/tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php`, and `apps/platform/tests/Feature/Operations/TenantlessOperationRunViewerTest.php`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Queue-Truth Contract)
|
||||
|
||||
**Purpose**: settle one generic queue-truth contract before any individual surface is updated.
|
||||
|
||||
**Critical**: No user-story runtime work should begin until this phase is complete.
|
||||
|
||||
- [x] T005 [P] Add or extend failing unit coverage in `apps/platform/tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php` for fresh queued, stale queued, fresh running, stale running, determinate-progress stale running, unsupported lifecycle, and reconciled-terminal cases.
|
||||
- [x] T006 [P] Add or extend focused feature coverage in `apps/platform/tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php`, `apps/platform/tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php`, and `apps/platform/tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php` proving that stale-active runs do not mix stale attention with ordinary queue/progress reassurance.
|
||||
- [x] T007 Freeze the canonical derivation boundary in `apps/platform/app/Support/OpsUx/OperationRunProgressContract.php`, `apps/platform/app/Support/OpsUx/RunDurationInsights.php`, `apps/platform/app/Support/OpsUx/OperationUxPresenter.php`, and `apps/platform/app/Support/Operations/OperationRunFreshnessState.php` without introducing new persisted state or a new registry.
|
||||
|
||||
**Checkpoint**: Generic queue truth is derived from one aligned helper path before shell/list/detail surfaces are edited.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - See honest queued and stale truth on active monitoring surfaces (Priority: P1)
|
||||
|
||||
**Goal**: shell and list surfaces no longer mix stale-active attention with calm queue or progress reassurance.
|
||||
|
||||
**Independent Test**: seed fresh and stale queued/running runs, render the shell hint and operations list, and verify that only fresh active work keeps calm queue/progress copy.
|
||||
|
||||
### Tests for User Story 1
|
||||
|
||||
- [x] T008 [P] [US1] Extend `apps/platform/tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php` and `apps/platform/tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php` for stale queued, stale running, and determinate-progress stale-running shell cases.
|
||||
- [x] T009 [P] [US1] Extend `apps/platform/tests/Feature/Monitoring/MonitoringOperationsTest.php` and `apps/platform/tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php` for operations-list row truth and lifecycle-attention alignment.
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [x] T010 [US1] Align generic progress and queue guidance in `apps/platform/app/Support/OpsUx/OperationRunProgressContract.php`, `apps/platform/app/Support/OpsUx/RunDurationInsights.php`, and `apps/platform/app/Support/OpsUx/OperationUxPresenter.php` so stale-active work no longer presents ordinary queue/progress reassurance.
|
||||
- [x] T011 [US1] Update `apps/platform/resources/views/livewire/bulk-operation-progress.blade.php` so the shell activity hint consumes the aligned generic queue truth without local contradictory fallback copy.
|
||||
- [x] T012 [US1] Update `apps/platform/app/Filament/Pages/Monitoring/Operations.php` and `apps/platform/app/Filament/Resources/OperationRunResource.php` only as needed so the route-backed operations hub plus the shared table/detail summaries expose the same generic queue truth.
|
||||
|
||||
**Checkpoint**: User Story 1 is independently functional when stale-active work reads honestly on shell and list surfaces.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - Keep canonical run detail aligned with generic queue truth (Priority: P1)
|
||||
|
||||
**Goal**: canonical run detail confirms the same lifecycle meaning before deeper diagnostics render.
|
||||
|
||||
**Independent Test**: open canonical detail for fresh, stale, and reconciled runs and verify that top-level lifecycle guidance matches shell/list semantics.
|
||||
|
||||
### Tests for User Story 2
|
||||
|
||||
- [x] T013 [P] [US2] Extend `apps/platform/tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php` and `apps/platform/tests/Feature/Operations/TenantlessOperationRunViewerTest.php` for fresh, stale, and reconciled-terminal top-summary behavior.
|
||||
- [x] T014 [P] [US2] Extend `apps/platform/tests/Feature/Monitoring/MonitoringOperationsTest.php` for compact-to-detail continuity where the same run is opened from the list after stale-active presentation.
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [x] T015 [US2] Update `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php` so lifecycle banners and detail guidance reflect the aligned generic queue truth before raw diagnostics.
|
||||
- [x] T016 [US2] Update `apps/platform/app/Support/OpsUx/OperationUxPresenter.php` and any touched `OperationRunResource` summary helpers so canonical detail and resource detail stay aligned.
|
||||
|
||||
**Checkpoint**: User Story 2 is independently functional when canonical detail confirms, rather than contradicts, generic queue truth.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - Keep proof-backed reconciliation separate from generic queue truth (Priority: P2)
|
||||
|
||||
**Goal**: generic stale wording stays cautious, while existing scheduled/adapter reconciliation remains authoritative for terminal truth.
|
||||
|
||||
**Independent Test**: compare stale active runs and already-reconciled terminal runs to verify that cautious generic wording and existing reconciled semantics both survive.
|
||||
|
||||
### Tests for User Story 3
|
||||
|
||||
- [x] T017 [P] [US3] Extend unit or feature coverage, including `apps/platform/tests/Feature/MonitoringOperationsTest.php` where the operations aggregate wording is already asserted, so stale active runs without strong legitimacy evidence do not claim orphaned or domain-complete truth while existing scheduled/adapter-reconciled terminal runs keep their terminal semantics.
|
||||
- [x] T018 [P] [US3] Extend `apps/platform/tests/Feature/Operations/TenantlessOperationRunViewerTest.php` or the nearest focused monitoring suite for reconciled-terminal detail language regression protection.
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [x] T019 [US3] Tighten cautious stale-active wording in `apps/platform/app/Support/OpsUx/RunDurationInsights.php` and `apps/platform/app/Support/OpsUx/OperationUxPresenter.php` so the generic path says only what current lifecycle truth can prove.
|
||||
- [x] T020 [US3] Confirm `apps/platform/app/Console/Commands/TenantpilotReconcileOperationRuns.php` and `apps/platform/app/Console/Commands/OpsReconcileAdapterRuns.php` need no runtime change and document that no command-layer change was required.
|
||||
|
||||
**Checkpoint**: User Story 3 is independently functional when generic stale wording stays cautious and existing terminal reconciliation remains intact.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting Validation
|
||||
|
||||
**Purpose**: validate the bounded slice, stop drift, and hand off a clean implementation path.
|
||||
|
||||
- [x] T021 [P] Refresh `specs/358-operationrun-queue-truth-foundation/spec.md`, `plan.md`, and `checklists/requirements.md` only if implementation proves a thinner or broader touched-file boundary.
|
||||
- [x] T022 [P] Run `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php`.
|
||||
- [x] T023 [P] Run `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php tests/Feature/MonitoringOperationsTest.php tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php tests/Feature/Monitoring/MonitoringOperationsTest.php tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php`.
|
||||
- [x] T024 [P] Run `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` for touched platform files.
|
||||
- [x] T025 [P] Run `git diff --check`.
|
||||
- [x] T026 [P] Record the final queue-truth wording, proof boundaries, and any retained cautious-language decisions in the active feature close-out entry `Guardrail / Smoke Coverage`.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Setup (Phase 1)**: no dependencies
|
||||
- **Foundational (Phase 2)**: depends on Setup and blocks all story work
|
||||
- **US1 (Phase 3)**: depends on Foundational completion
|
||||
- **US2 (Phase 4)**: depends on Foundational completion and is easiest after US1 settles the shared wording
|
||||
- **US3 (Phase 5)**: depends on US1 and US2 because it confirms the final generic truth boundary
|
||||
- **Polish (Phase 6)**: depends on all desired user stories
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- `T002`, `T003`, and `T004` can run in parallel.
|
||||
- `T005` and `T006` can run in parallel after the current contradiction is confirmed.
|
||||
- `T008` and `T009` can run in parallel.
|
||||
- `T013` and `T014` can run in parallel.
|
||||
- `T022`, `T023`, `T024`, and `T025` can run in parallel after implementation is stable.
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
1. Freeze the shared queue-truth derivation first.
|
||||
2. Ship US1 to remove the stale-vs-queue contradiction on shell/list surfaces.
|
||||
3. Ship US2 so canonical detail confirms the same truth.
|
||||
4. Ship US3 to protect the narrow boundary against overclaiming or framework creep.
|
||||
5. Finish with the focused validation commands and close-out notes.
|
||||
|
||||
## Guardrail / Smoke Coverage
|
||||
|
||||
- Final queue-truth wording keeps stale-active guidance at `Past the lifecycle window. Review worker health and logs before retrying.` and removes competing calm queue/progress copy from shell, list, and top-level detail surfaces.
|
||||
- Proof boundary remains shared-first: `OperationRunProgressContract` owns stale-active progress truth, `OperationUxPresenter` owns aligned lifecycle/detail guidance, and the shell/list/detail surfaces consume those shared seams instead of local fallback wording.
|
||||
- Browser smoke path used the real local route `GET /admin/local/smoke-login` and verified `/admin/workspaces/38/operations` followed by the primary `Open operation` drilldown to `/admin/workspaces/38/operations/82`.
|
||||
- Browser smoke confirmed stale wording is visible on both hub and detail, while `Waiting for worker.`, `Progress details pending.`, and `4 / 10 processed (40%)` do not appear as primary stale-active guidance.
|
||||
- No runtime change was required in `apps/platform/app/Filament/Pages/Monitoring/Operations.php`, `apps/platform/app/Filament/Resources/OperationRunResource.php`, `apps/platform/app/Filament/Pages/Operations/TenantlessOperationRunViewer.php`, `apps/platform/app/Console/Commands/TenantpilotReconcileOperationRuns.php`, or `apps/platform/app/Console/Commands/OpsReconcileAdapterRuns.php`; the existing shared seams consumed the contract/presenter updates directly.
|
||||
- Validation evidence:
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/OpsUx/OperationRunProgressContractTest.php`
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/OpsUx/BulkOperationProgressDbOnlyTest.php tests/Feature/OpsUx/ActivityFeedbackSurfaceTest.php`
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/MonitoringOperationsTest.php tests/Feature/Monitoring/OperationLifecycleFreshnessPresentationTest.php tests/Feature/Monitoring/MonitoringOperationsTest.php`
|
||||
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/OperationRunEnterpriseDetailPageTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php`
|
||||
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
|
||||
- `git diff --check`
|
||||
Loading…
Reference in New Issue
Block a user